Passed
Push — 6.0 ( 4ac4e1...87e1d7 )
by Olivier
01:56
created

Locale::format_currency()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 6
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 3
1
<?php
2
3
namespace ICanBoogie\CLDR\Core;
4
5
use Closure;
6
use ICanBoogie\Accessor\AccessorTrait;
7
use ICanBoogie\CLDR\AbstractSectionCollection;
8
use ICanBoogie\CLDR\Dates\Calendar;
9
use ICanBoogie\CLDR\Dates\CalendarId;
10
use ICanBoogie\CLDR\General\Lists\ListFormatterLocalized;
11
use ICanBoogie\CLDR\General\Lists\ListType;
12
use ICanBoogie\CLDR\General\Transforms\ContextTransforms;
13
use ICanBoogie\CLDR\General\Transforms\HasContextTransforms;
14
use ICanBoogie\CLDR\Numbers\Currency;
15
use ICanBoogie\CLDR\Numbers\CurrencyFormatterLocalized;
16
use ICanBoogie\CLDR\Numbers\NumberFormatterLocalized;
17
use ICanBoogie\CLDR\Numbers\Numbers;
18
use ICanBoogie\CLDR\Repository;
19
use ICanBoogie\CLDR\Supplemental\Units\Units;
20
use ICanBoogie\CLDR\Warmable;
21
use WeakMap;
22
23
use function str_replace;
24
25
/**
26
 * Representation of a locale.
27
 *
28
 * @property-read string $language Unicode language.
29
 * @uses self::get_language()
30
 * @property-read Calendar $calendar The preferred calendar for this locale.
31
 * @uses self::get_calendar()
32
 * @property-read Numbers $numbers
33
 * @uses self::get_numbers()
34
 * @property-read NumberFormatterLocalized $number_formatter
35
 * @uses self::get_number_formatter()
36
 * @property-read CurrencyFormatterLocalized $currency_formatter
37
 * @uses self::get_currency_formatter()
38
 * @property-read ListFormatterLocalized $list_formatter
39
 * @uses self::get_list_formatter()
40
 * @property-read ContextTransforms $context_transforms
41
 * @uses self::get_context_transforms()
42
 * @property-read Units $units
43
 * @uses self::get_units()
44
 *
45
 * @extends AbstractSectionCollection<string>
46
 * @implements Localizable<Locale, LocaleLocalized>
47
 */
48
class Locale extends AbstractSectionCollection implements Localizable, Warmable
49
{
50
    /**
51
     * @uses get_context_transforms
52
     * @uses get_currency_formatter
53
     * @uses get_calendar
54
     * @uses get_language
55
     * @uses get_list_formatter
56
     * @uses get_number_formatter
57
     * @uses get_units
58
     */
59
    use AccessorTrait;
60
61
    /**
62
     * Where _key_ is an offset and _value_ and array where `0` is a pattern for the path and `1` the data path.
63
     */
64
    private const OFFSET_MAPPING = [
65
66
        'ca-buddhist' => [ 'cal-buddhist/{locale}/ca-buddhist', 'dates/calendars/buddhist' ],
67
        'ca-chinese' => [ 'cal-chinese/{locale}/ca-chinese', 'dates/calendars/chinese' ],
68
        'ca-coptic' => [ 'cal-coptic/{locale}/ca-coptic', 'dates/calendars/coptic' ],
69
        'ca-dangi' => [ 'cal-dangi/{locale}/ca-dangi', 'dates/calendars/dangi' ],
70
        'ca-ethiopic' => [ 'cal-ethiopic/{locale}/ca-ethiopic', 'dates/calendars/ethiopic' ],
71
        'ca-hebrew' => [ 'cal-hebrew/{locale}/ca-hebrew', 'dates/calendars/hebrew' ],
72
        'ca-indian' => [ 'cal-indian/{locale}/ca-indian', 'dates/calendars/indian' ],
73
        'ca-islamic' => [ 'cal-islamic/{locale}/ca-islamic', 'dates/calendars/islamic' ],
74
        'ca-japanese' => [ 'cal-japanese/{locale}/ca-japanese', 'dates/calendars/japanese' ],
75
        'ca-persian' => [ 'cal-persian/{locale}/ca-persian', 'dates/calendars/persian' ],
76
        'ca-roc' => [ 'cal-roc/{locale}/ca-roc', 'dates/calendars/roc' ],
77
        'ca-generic' => [ 'dates/{locale}/ca-generic', 'dates/calendars/generic' ],
78
        'ca-gregorian' => [ 'dates/{locale}/ca-gregorian', 'dates/calendars/gregorian' ],
79
        'dateFields' => [ 'dates/{locale}/dateFields', 'dates/fields' ],
80
        'timeZoneNames' => [ 'dates/{locale}/timeZoneNames', 'dates/timeZoneNames' ],
81
        'languages' => [ 'localenames/{locale}/languages', 'localeDisplayNames/languages' ],
82
        'localeDisplayNames' => [ 'localenames/{locale}/localeDisplayNames', 'localeDisplayNames' ],
83
        'scripts' => [ 'localenames/{locale}/scripts', 'localeDisplayNames/scripts' ],
84
        'territories' => [ 'localenames/{locale}/territories', 'localeDisplayNames/territories' ],
85
        'variants' => [ 'localenames/{locale}/variants', 'localeDisplayNames/variants' ],
86
        'characters' => [ 'misc/{locale}/characters', 'characters' ],
87
        'contextTransforms' => [ 'misc/{locale}/contextTransforms', 'contextTransforms' ],
88
        'delimiters' => [ 'misc/{locale}/delimiters', 'delimiters' ],
89
        'layout' => [ 'misc/{locale}/layout', 'layout' ],
90
        'listPatterns' => [ 'misc/{locale}/listPatterns', 'listPatterns' ],
91
        'posix' => [ 'misc/{locale}/posix', 'posix' ],
92
        'currencies' => [ 'numbers/{locale}/currencies', 'numbers/currencies' ],
93
        'numbers' => [ 'numbers/{locale}/numbers', 'numbers' ],
94
        'measurementSystemNames' => [
95
            'units/{locale}/measurementSystemNames',
96
            'localeDisplayNames/measurementSystemNames'
97
        ],
98
        'units' => [ 'units/{locale}/units', 'units' ],
99
100
    ];
101
102
    public function __construct(
103
        Repository $repository,
104
        public readonly LocaleId $id
105
    ) {
106
        $this->calendars = new WeakMap();
107
108
        parent::__construct($repository);
109
    }
110
111
    public function offsetGet($offset)
112
    {
113
        // Not all locales have context transforms
114
        if ($offset === 'contextTransforms' && !HasContextTransforms::for_locale($this->id)) {
115
            return [];
116
        }
117
118
        return parent::offsetGet($offset);
119
    }
120
121
    public function offsetExists(mixed $offset): bool
122
    {
123
        return isset(self::OFFSET_MAPPING[$offset]);
124
    }
125
126
    /**
127
     * Warm up with locale relevant data.
128
     */
129
    public function warm_up(Closure $progress): void
130
    {
131
        $progress("Warming up locale '{$this->id->value}':");
132
133
        foreach (array_keys(self::OFFSET_MAPPING) as $offset) {
134
            $progress("- $offset");
135
            $this->offsetGet($offset);
136
        }
137
    }
138
139
    protected function path_for(string $offset): string
140
    {
141
        return str_replace('{locale}', $this->id->value, self::OFFSET_MAPPING[$offset][0]);
142
    }
143
144
    protected function data_path_for(string $offset): string
145
    {
146
        return "main/{$this->id->value}/" . self::OFFSET_MAPPING[$offset][1];
147
    }
148
149
    private function get_language(): string
0 ignored issues
show
Unused Code introduced by
The method get_language() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
150
    {
151
        [ $language ] = explode('-', $this->id->value, 2);
152
153
        return $language;
154
    }
155
156
    private Calendar $calendar;
157
158
    private function get_calendar(): Calendar
0 ignored issues
show
Unused Code introduced by
The method get_calendar() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
159
    {
160
        /**
161
         * @TODO-20131101: use preferred data
162
         *
163
         * @see https://github.com/unicode-org/cldr-json/blob/45.0.0/cldr-json/cldr-core/supplemental/calendarPreferenceData.json
164
         */
165
        return $this->calendar ??= $this->calendar_for(CalendarId::GREGORIAN);
166
    }
167
168
    /**
169
     * @var WeakMap<CalendarId, Calendar>
170
     */
171
    private WeakMap $calendars;
172
173
    public function calendar_for(CalendarId $id): Calendar
174
    {
175
        return $this->calendars[$id] ??= new Calendar($this, $this["ca-" . $id->value]);
176
    }
177
178
    private Numbers $numbers;
179
180
    private function get_numbers(): Numbers
181
    {
182
        return $this->numbers ??= new Numbers($this, $this['numbers']);
183
    }
184
185
    private NumberFormatterLocalized $number_formatter;
186
187
    private function get_number_formatter(): NumberFormatterLocalized
188
    {
189
        return $this->number_formatter ??= $this->repository->number_formatter->localized($this);
190
    }
191
192
    private CurrencyFormatterLocalized $currency_formatter;
193
194
    private function get_currency_formatter(): CurrencyFormatterLocalized
195
    {
196
        return $this->currency_formatter ??= $this->repository->currency_formatter->localized($this);
197
    }
198
199
    private ListFormatterLocalized $list_formatter;
200
201
    private function get_list_formatter(): ListFormatterLocalized
202
    {
203
        return $this->list_formatter ??= $this->repository->list_formatter->localized($this);
204
    }
205
206
    private ContextTransforms $context_transforms;
207
208
    private function get_context_transforms(): ContextTransforms
209
    {
210
        return $this->context_transforms ??= new ContextTransforms($this['contextTransforms']);
211
    }
212
213
    private Units $units;
214
215
    private function get_units(): Units
0 ignored issues
show
Unused Code introduced by
The method get_units() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
216
    {
217
        return $this->units ??= new Units($this);
218
    }
219
220
    public function localized(Locale|LocaleId|string $locale): LocaleLocalized
221
    {
222
        if (!$locale instanceof self) {
223
            $locale = $this->repository->locale_for($locale);
224
        }
225
226
        return new LocaleLocalized($this, $locale);
227
    }
228
229
    /**
230
     * Formats a number using {@see $number_formatter}.
231
     *
232
     * @param float|int|numeric-string $number
0 ignored issues
show
Documentation Bug introduced by
The doc comment float|int|numeric-string at position 4 could not be parsed: Unknown type name 'numeric-string' at position 4 in float|int|numeric-string.
Loading history...
233
     *
234
     * @see NumberFormatterLocalized::format
235
     */
236
    public function format_number(float|int|string $number, string $pattern = null): string
237
    {
238
        return $this->get_number_formatter()->format($number, $pattern);
239
    }
240
241
    /**
242
     * @param float|int|numeric-string $number
0 ignored issues
show
Documentation Bug introduced by
The doc comment float|int|numeric-string at position 4 could not be parsed: Unknown type name 'numeric-string' at position 4 in float|int|numeric-string.
Loading history...
243
     *
244
     * @see NumberFormatterLocalized::format
245
     */
246
    public function format_percent(float|int|string $number, string $pattern = null): string
247
    {
248
        return $this->get_number_formatter()->format(
249
            $number,
250
            $pattern ?? $this->get_numbers()->percent_formats['standard']
251
        );
252
    }
253
254
    /**
255
     * Formats currency using localized conventions.
256
     *
257
     * @param float|int|numeric-string $number
0 ignored issues
show
Documentation Bug introduced by
The doc comment float|int|numeric-string at position 4 could not be parsed: Unknown type name 'numeric-string' at position 4 in float|int|numeric-string.
Loading history...
258
     */
259
    public function format_currency(
260
        float|int|string $number,
261
        Currency|string $currency,
262
        string $pattern = CurrencyFormatterLocalized::PATTERN_STANDARD
263
    ): string {
264
        return $this->get_currency_formatter()->format($number, $currency, $pattern);
265
    }
266
267
    /**
268
     * Formats variable-length lists of scalars.
269
     *
270
     * @param scalar[] $list
271
     *
272
     * @see ListFormatterLocalized::format()
273
     */
274
    public function format_list(array $list, ListType $type = ListType::STANDARD): string
275
    {
276
        return $this->get_list_formatter()->format($list, $type);
277
    }
278
279
    /**
280
     * Transforms a string depending on the context and the locale rules.
281
     *
282
     * @param ContextTransforms::USAGE_* $usage
283
     * @param ContextTransforms::TYPE_* $type
284
     */
285
    public function context_transform(string $str, string $usage, string $type): string
286
    {
287
        return $this->get_context_transforms()->transform($str, $usage, $type);
288
    }
289
}
290