@amazon-devices/expo-localization 2.0.9000000001 → 3.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CMakeLists.txt +4 -10
- package/build/ExpoLocalization.kepler.d.ts +5 -5
- package/build/ExpoLocalization.kepler.d.ts.map +1 -1
- package/build/ExpoLocalization.kepler.js +237 -6
- package/build/ExpoLocalization.kepler.js.map +1 -1
- package/kepler/ExpoLocalizationEvents.cpp +108 -0
- package/kepler/{ExpoLocalization.h → ExpoLocalizationEvents.h} +14 -5
- package/kepler/KeplerInit.cpp +2 -2
- package/lib/aarch64-release/libexpo-localization-3.so +0 -0
- package/lib/armv7-release/libexpo-localization-3.so +0 -0
- package/lib/x86_64-release/libexpo-localization-3.so +0 -0
- package/package.json +1 -1
- package/plugin/tsconfig.tsbuildinfo +1 -0
- package/react-native.config.js +5 -4
- package/src/ExpoLocalization.kepler.ts +267 -23
- package/kepler/ExpoLocalization.cpp +0 -214
- package/kepler/test/ExpoLocalization.cpp +0 -78
- package/kepler-compatibility.json +0 -7
package/CMakeLists.txt
CHANGED
|
@@ -12,11 +12,11 @@
|
|
|
12
12
|
# FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
|
|
13
13
|
#
|
|
14
14
|
set(PACKAGE_NAME "expo-localization")
|
|
15
|
-
|
|
16
|
-
set(
|
|
15
|
+
# The npm major versions the app-deployed artifact so it cannot collide with the frozen system lib
|
|
16
|
+
set(AUTO_LINK_PACKAGE_NAME "${PACKAGE_NAME}-3")
|
|
17
17
|
|
|
18
|
-
#
|
|
19
|
-
|
|
18
|
+
# Events-only module linking just the IDL packages it subscribes through so DT_NEEDED stays on the platform allowlist
|
|
19
|
+
set(SOURCES_KEPLER kepler/ExpoLocalizationEvents.cpp)
|
|
20
20
|
|
|
21
21
|
add_library(${PACKAGE_NAME} STATIC ${SOURCES_KEPLER})
|
|
22
22
|
|
|
@@ -24,7 +24,6 @@ kepler_add_idl_import_library(
|
|
|
24
24
|
expo_localization_idl_pkg
|
|
25
25
|
PACKAGES
|
|
26
26
|
com.amazon.kepler.env
|
|
27
|
-
com.amazon.kepler.device_config
|
|
28
27
|
com.amazon.kepler.experimental_mr33.messaging
|
|
29
28
|
)
|
|
30
29
|
|
|
@@ -32,15 +31,11 @@ target_link_libraries(
|
|
|
32
31
|
${PACKAGE_NAME}
|
|
33
32
|
PUBLIC
|
|
34
33
|
turbomoduleAPI::turbomoduleAPI
|
|
35
|
-
ICU::i18n
|
|
36
|
-
ICU::uc
|
|
37
34
|
expo_localization_idl_pkg
|
|
38
35
|
)
|
|
39
36
|
|
|
40
37
|
add_library(${AUTO_LINK_PACKAGE_NAME} SHARED kepler/KeplerInit.cpp)
|
|
41
38
|
|
|
42
|
-
set_target_properties(${AUTO_LINK_PACKAGE_NAME} PROPERTIES SOVERSION ${KEPLER_REACT_NATIVE_MAJOR_VERSION}.${KEPLER_REACT_NATIVE_MINOR_VERSION})
|
|
43
|
-
|
|
44
39
|
target_link_libraries(
|
|
45
40
|
${AUTO_LINK_PACKAGE_NAME}
|
|
46
41
|
PRIVATE
|
|
@@ -53,7 +48,6 @@ target_link_libraries(${AUTO_LINK_PACKAGE_NAME}_exe
|
|
|
53
48
|
PRIVATE
|
|
54
49
|
${AUTO_LINK_PACKAGE_NAME}
|
|
55
50
|
turbomoduleAPI::turbomoduleAPI
|
|
56
|
-
ICU::i18n
|
|
57
51
|
expo_localization_idl_pkg
|
|
58
52
|
)
|
|
59
53
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { Subscription } from 'expo-modules-core';
|
|
2
2
|
import { Calendar, Locale } from './Localization.types';
|
|
3
|
-
export declare function addLocaleListener(listener: () => void): Subscription;
|
|
4
|
-
export declare function addCalendarListener(listener: () => void): Subscription;
|
|
3
|
+
export declare function addLocaleListener(listener: (event: any) => void): Subscription;
|
|
4
|
+
export declare function addCalendarListener(listener: (event: any) => void): Subscription;
|
|
5
5
|
export declare function removeSubscription(subscription: Subscription): void;
|
|
6
6
|
declare const module: {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
readonly locale: string;
|
|
8
|
+
getLocales(): Locale[];
|
|
9
|
+
getCalendars(): Calendar[];
|
|
10
10
|
};
|
|
11
11
|
export default module;
|
|
12
12
|
//# sourceMappingURL=ExpoLocalization.kepler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoLocalization.kepler.d.ts","sourceRoot":"","sources":["../src/ExpoLocalization.kepler.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ExpoLocalization.kepler.d.ts","sourceRoot":"","sources":["../src/ExpoLocalization.kepler.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGtD,OAAO,EAAE,QAAQ,EAAsB,MAAM,EAAE,MAAM,sBAAsB,CAAC;AA6I5E,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,CAAC,KAAK,KAAA,KAAK,IAAI,GAAG,YAAY,CAEzE;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,CAAC,KAAK,KAAA,KAAK,IAAI,GAAG,YAAY,CAU3E;AAED,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,YAAY,QAE5D;AAsCD,QAAA,MAAM,MAAM;qBACI,MAAM;kBAIN,MAAM,EAAE;oBAuCN,QAAQ,EAAE;CAmC3B,CAAC;AAEF,eAAe,MAAM,CAAC"}
|
|
@@ -5,13 +5,139 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Use is subject to license terms.
|
|
7
7
|
*/
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
import * as rtlDetect from 'rtl-detect';
|
|
9
|
+
// RNLocalize looks up its TM at module load so guard the require and fall back to Intl where the library is absent
|
|
10
|
+
let RNLocalize;
|
|
11
|
+
try {
|
|
12
|
+
RNLocalize = require('@amazon-devices/react-native-localize');
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
RNLocalize = {};
|
|
16
|
+
}
|
|
17
|
+
// CLDR territory currency data so each entry carries its own region's tender with the user currency as fallback
|
|
18
|
+
const REGION_CURRENCIES = {
|
|
19
|
+
// euro area and territories
|
|
20
|
+
AD: 'EUR', AT: 'EUR', AX: 'EUR', BE: 'EUR', BL: 'EUR', CY: 'EUR', DE: 'EUR', EE: 'EUR',
|
|
21
|
+
ES: 'EUR', FI: 'EUR', FR: 'EUR', GF: 'EUR', GP: 'EUR', GR: 'EUR', HR: 'EUR', IE: 'EUR',
|
|
22
|
+
IT: 'EUR', LT: 'EUR', LU: 'EUR', LV: 'EUR', MC: 'EUR', ME: 'EUR', MF: 'EUR', MQ: 'EUR',
|
|
23
|
+
MT: 'EUR', NL: 'EUR', PM: 'EUR', PT: 'EUR', RE: 'EUR', SI: 'EUR', SK: 'EUR', SM: 'EUR',
|
|
24
|
+
TF: 'EUR', VA: 'EUR', XK: 'EUR', YT: 'EUR',
|
|
25
|
+
// US dollar territories
|
|
26
|
+
US: 'USD', AS: 'USD', BQ: 'USD', EC: 'USD', FM: 'USD', GU: 'USD', IO: 'USD', MH: 'USD',
|
|
27
|
+
MP: 'USD', PR: 'USD', PW: 'USD', SV: 'USD', TC: 'USD', TL: 'USD', UM: 'USD', VG: 'USD', VI: 'USD',
|
|
28
|
+
// pound and crown dependencies
|
|
29
|
+
GB: 'GBP', GG: 'GBP', GS: 'GBP', IM: 'GBP', JE: 'GBP',
|
|
30
|
+
// Australian and New Zealand dollar territories
|
|
31
|
+
AU: 'AUD', CC: 'AUD', CX: 'AUD', HM: 'AUD', KI: 'AUD', NF: 'AUD', NR: 'AUD', TV: 'AUD',
|
|
32
|
+
NZ: 'NZD', CK: 'NZD', NU: 'NZD', PN: 'NZD', TK: 'NZD',
|
|
33
|
+
// CFA and CFP francs and East Caribbean dollar
|
|
34
|
+
BF: 'XOF', BJ: 'XOF', CI: 'XOF', GW: 'XOF', ML: 'XOF', NE: 'XOF', SN: 'XOF', TG: 'XOF',
|
|
35
|
+
CF: 'XAF', CG: 'XAF', CM: 'XAF', GA: 'XAF', GQ: 'XAF', TD: 'XAF',
|
|
36
|
+
NC: 'XPF', PF: 'XPF', WF: 'XPF',
|
|
37
|
+
AG: 'XCD', AI: 'XCD', DM: 'XCD', GD: 'XCD', KN: 'XCD', LC: 'XCD', MS: 'XCD', VC: 'XCD',
|
|
38
|
+
// nordics and remaining single tenders
|
|
39
|
+
DK: 'DKK', FO: 'DKK', GL: 'DKK', NO: 'NOK', BV: 'NOK', SJ: 'NOK', SE: 'SEK', IS: 'ISK',
|
|
40
|
+
CH: 'CHF', LI: 'CHF',
|
|
41
|
+
AE: 'AED', AF: 'AFN', AL: 'ALL', AM: 'AMD', AO: 'AOA', AR: 'ARS', AW: 'AWG', AZ: 'AZN',
|
|
42
|
+
BA: 'BAM', BB: 'BBD', BD: 'BDT', BG: 'BGN', BH: 'BHD', BI: 'BIF', BM: 'BMD', BN: 'BND',
|
|
43
|
+
BO: 'BOB', BR: 'BRL', BS: 'BSD', BT: 'BTN', BW: 'BWP', BY: 'BYN', BZ: 'BZD', CA: 'CAD',
|
|
44
|
+
CD: 'CDF', CL: 'CLP', CN: 'CNY', CO: 'COP', CR: 'CRC', CU: 'CUP', CV: 'CVE', CW: 'ANG',
|
|
45
|
+
CZ: 'CZK', DJ: 'DJF', DO: 'DOP', DZ: 'DZD', EG: 'EGP', EH: 'MAD', ER: 'ERN', ET: 'ETB',
|
|
46
|
+
FJ: 'FJD', FK: 'FKP', GE: 'GEL', GH: 'GHS', GI: 'GIP', GM: 'GMD', GN: 'GNF', GT: 'GTQ',
|
|
47
|
+
GY: 'GYD', HK: 'HKD', HN: 'HNL', HT: 'HTG', HU: 'HUF', ID: 'IDR', IL: 'ILS', IN: 'INR',
|
|
48
|
+
IQ: 'IQD', IR: 'IRR', JM: 'JMD', JO: 'JOD', JP: 'JPY', KE: 'KES', KG: 'KGS', KH: 'KHR',
|
|
49
|
+
KM: 'KMF', KP: 'KPW', KR: 'KRW', KW: 'KWD', KY: 'KYD', KZ: 'KZT', LA: 'LAK', LB: 'LBP',
|
|
50
|
+
LK: 'LKR', LR: 'LRD', LS: 'LSL', LY: 'LYD', MA: 'MAD', MD: 'MDL', MG: 'MGA', MK: 'MKD',
|
|
51
|
+
MM: 'MMK', MN: 'MNT', MO: 'MOP', MR: 'MRU', MU: 'MUR', MV: 'MVR', MW: 'MWK', MX: 'MXN',
|
|
52
|
+
MY: 'MYR', MZ: 'MZN', NA: 'NAD', NG: 'NGN', NI: 'NIO', NP: 'NPR', OM: 'OMR', PA: 'PAB',
|
|
53
|
+
PE: 'PEN', PG: 'PGK', PH: 'PHP', PK: 'PKR', PL: 'PLN', PS: 'ILS', PY: 'PYG', QA: 'QAR',
|
|
54
|
+
RO: 'RON', RS: 'RSD', RU: 'RUB', RW: 'RWF', SA: 'SAR', SB: 'SBD', SC: 'SCR', SD: 'SDG',
|
|
55
|
+
SG: 'SGD', SH: 'SHP', SL: 'SLE', SO: 'SOS', SR: 'SRD', SS: 'SSP', ST: 'STN', SY: 'SYP',
|
|
56
|
+
SZ: 'SZL', TH: 'THB', TJ: 'TJS', TM: 'TMT', TN: 'TND', TO: 'TOP', TR: 'TRY', TT: 'TTD',
|
|
57
|
+
TW: 'TWD', TZ: 'TZS', UA: 'UAH', UG: 'UGX', UY: 'UYU', UZ: 'UZS', VE: 'VES', VN: 'VND',
|
|
58
|
+
VU: 'VUV', WS: 'WST', YE: 'YER', ZA: 'ZAR', ZM: 'ZMW', ZW: 'ZWG',
|
|
59
|
+
};
|
|
60
|
+
// https://wisevoter.com/country-rankings/countries-that-use-fahrenheit/
|
|
61
|
+
const USES_FAHRENHEIT = [
|
|
62
|
+
'AG', 'BZ', 'VG', 'FM', 'MH', 'MS', 'KN', 'BS', 'TC', 'US', 'LR', 'PW', 'KY',
|
|
63
|
+
];
|
|
64
|
+
// CLDR weekData firstDay per region with Sunday as 1 and Saturday as 7 and
|
|
65
|
+
// Monday as the default for regions in neither list
|
|
66
|
+
const WEEK_STARTS_SUNDAY = [
|
|
67
|
+
'AG', 'AS', 'BD', 'BR', 'BS', 'BT', 'BW', 'BZ', 'CA', 'CO', 'DM', 'DO', 'ET',
|
|
68
|
+
'GT', 'GU', 'HK', 'HN', 'ID', 'IL', 'IN', 'JM', 'JP', 'KE', 'KH', 'KR', 'LA',
|
|
69
|
+
'MH', 'MM', 'MO', 'MT', 'MX', 'MZ', 'NI', 'NP', 'PA', 'PE', 'PH', 'PK', 'PR',
|
|
70
|
+
'PY', 'SA', 'SG', 'SV', 'TH', 'TT', 'TW', 'UM', 'US', 'VE', 'VI', 'WS', 'YE',
|
|
71
|
+
'ZA', 'ZW',
|
|
72
|
+
];
|
|
73
|
+
const WEEK_STARTS_SATURDAY = ['AE', 'AF', 'BH', 'DJ', 'DZ', 'EG', 'IQ', 'IR', 'JO', 'KW', 'LY', 'OM', 'QA', 'SD', 'SY'];
|
|
74
|
+
function firstWeekdayForRegion(region) {
|
|
75
|
+
if (region == null) {
|
|
76
|
+
return 2;
|
|
77
|
+
}
|
|
78
|
+
if (WEEK_STARTS_SUNDAY.includes(region)) {
|
|
79
|
+
return 1;
|
|
80
|
+
}
|
|
81
|
+
if (WEEK_STARTS_SATURDAY.includes(region)) {
|
|
82
|
+
return 7;
|
|
83
|
+
}
|
|
84
|
+
return 2;
|
|
85
|
+
}
|
|
86
|
+
function currentLanguageTags() {
|
|
87
|
+
try {
|
|
88
|
+
const locales = RNLocalize.getLocales();
|
|
89
|
+
if (Array.isArray(locales) && locales.length > 0) {
|
|
90
|
+
return locales.map((l) => l.languageTag);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch { }
|
|
94
|
+
if (typeof Intl !== 'undefined') {
|
|
95
|
+
const tag = Intl.DateTimeFormat().resolvedOptions().locale;
|
|
96
|
+
if (tag) {
|
|
97
|
+
return [tag];
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return ['en-US'];
|
|
101
|
+
}
|
|
102
|
+
function currencyForCurrentLocale() {
|
|
103
|
+
try {
|
|
104
|
+
const currencies = RNLocalize.getCurrencies();
|
|
105
|
+
if (Array.isArray(currencies) && currencies.length > 0) {
|
|
106
|
+
return currencies[0];
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
catch { }
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
function currencySymbolFor(languageTag, currencyCode) {
|
|
113
|
+
if (!currencyCode || typeof Intl === 'undefined') {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
const parts = new Intl.NumberFormat(languageTag, {
|
|
118
|
+
style: 'currency',
|
|
119
|
+
currency: currencyCode,
|
|
120
|
+
}).formatToParts(0);
|
|
121
|
+
return parts.find((p) => p.type === 'currency')?.value ?? null;
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// Best-effort start of the events turbomodule which forwards OS locale and timezone changes to DeviceEventEmitter
|
|
128
|
+
try {
|
|
129
|
+
const { TurboModuleRegistry } = require('@amazon-devices/keplerscript-turbomodule-api');
|
|
130
|
+
TurboModuleRegistry.getEnforcing('ExpoLocalizationAppTM');
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
// without it the listeners never fire which was the previous behavior
|
|
134
|
+
}
|
|
135
|
+
const DeviceEventEmitter = require('react-native').DeviceEventEmitter;
|
|
11
136
|
export function addLocaleListener(listener) {
|
|
12
137
|
return DeviceEventEmitter.addListener('onLocaleSettingsChanged', listener);
|
|
13
138
|
}
|
|
14
139
|
export function addCalendarListener(listener) {
|
|
140
|
+
// Calendar values include the timezone so both signals invalidate them
|
|
15
141
|
const localeSubscription = DeviceEventEmitter.addListener('onLocaleSettingsChanged', listener);
|
|
16
142
|
const timezoneSubscription = DeviceEventEmitter.addListener('onCalendarSettingsChanged', listener);
|
|
17
143
|
return {
|
|
@@ -24,10 +150,115 @@ export function addCalendarListener(listener) {
|
|
|
24
150
|
export function removeSubscription(subscription) {
|
|
25
151
|
subscription.remove();
|
|
26
152
|
}
|
|
153
|
+
// Wire exactly the surface the 2.x Vega module exposed which is locale and getLocales and getCalendars
|
|
154
|
+
function currentLocaleTag() {
|
|
155
|
+
return currentLanguageTags()[0] || 'en-US';
|
|
156
|
+
}
|
|
157
|
+
function regionOfTag(tag) {
|
|
158
|
+
const [, ...suffixes] = tag.split('-');
|
|
159
|
+
for (const suffix of suffixes) {
|
|
160
|
+
if (suffix.length === 2) {
|
|
161
|
+
return suffix.toUpperCase();
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
function currentTimezone() {
|
|
167
|
+
try {
|
|
168
|
+
const tz = RNLocalize.getTimeZone();
|
|
169
|
+
if (tz) {
|
|
170
|
+
return tz;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
catch { }
|
|
174
|
+
if (typeof Intl !== 'undefined') {
|
|
175
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone || 'Etc/UTC';
|
|
176
|
+
}
|
|
177
|
+
return 'Etc/UTC';
|
|
178
|
+
}
|
|
179
|
+
function usesMetric() {
|
|
180
|
+
try {
|
|
181
|
+
return RNLocalize.usesMetricSystem();
|
|
182
|
+
}
|
|
183
|
+
catch { }
|
|
184
|
+
const region = regionOfTag(currentLocaleTag());
|
|
185
|
+
return region == null || !['US', 'LR', 'MM'].includes(region);
|
|
186
|
+
}
|
|
27
187
|
const module = {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
188
|
+
get locale() {
|
|
189
|
+
return currentLocaleTag();
|
|
190
|
+
},
|
|
191
|
+
getLocales() {
|
|
192
|
+
const userCurrency = currencyForCurrentLocale();
|
|
193
|
+
return currentLanguageTags().map((languageTag) => {
|
|
194
|
+
const locale = typeof Intl !== 'undefined'
|
|
195
|
+
? new Intl.Locale(languageTag)
|
|
196
|
+
: { region: undefined, textInfo: undefined, language: undefined };
|
|
197
|
+
const region = locale.region ?? null;
|
|
198
|
+
const languageCode = locale.language || languageTag.split('-')[0] || 'en';
|
|
199
|
+
const textDirection = locale.textInfo?.direction ?? (rtlDetect.isRtlLang(languageTag) ? 'rtl' : 'ltr');
|
|
200
|
+
const digitGroupingSeparator = Array.from((10000).toLocaleString(languageTag)).filter((c) => c > '9' || c < '0')[0] || null;
|
|
201
|
+
const decimalSeparator = (1.1).toLocaleString(languageTag).substring(1, 2);
|
|
202
|
+
const currencyCode = (region && REGION_CURRENCIES[region]) || userCurrency;
|
|
203
|
+
return {
|
|
204
|
+
languageTag,
|
|
205
|
+
languageCode,
|
|
206
|
+
textDirection,
|
|
207
|
+
digitGroupingSeparator,
|
|
208
|
+
decimalSeparator,
|
|
209
|
+
measurementSystem: (region === 'GB'
|
|
210
|
+
? 'uk'
|
|
211
|
+
: region
|
|
212
|
+
? (['US', 'LR', 'MM'].includes(region) ? 'us' : 'metric')
|
|
213
|
+
: usesMetric()
|
|
214
|
+
? 'metric'
|
|
215
|
+
: 'us'),
|
|
216
|
+
currencyCode,
|
|
217
|
+
currencySymbol: currencySymbolFor(languageTag, currencyCode),
|
|
218
|
+
regionCode: region,
|
|
219
|
+
temperatureUnit: region
|
|
220
|
+
? (USES_FAHRENHEIT.includes(region) ? 'fahrenheit' : 'celsius')
|
|
221
|
+
: null,
|
|
222
|
+
};
|
|
223
|
+
});
|
|
224
|
+
},
|
|
225
|
+
getCalendars() {
|
|
226
|
+
const resolved = typeof Intl !== 'undefined' ? Intl.DateTimeFormat().resolvedOptions() : undefined;
|
|
227
|
+
let calendar = resolved?.calendar ?? null;
|
|
228
|
+
try {
|
|
229
|
+
calendar = RNLocalize.getCalendar() ?? calendar;
|
|
230
|
+
}
|
|
231
|
+
catch { }
|
|
232
|
+
let uses24hourClock = null;
|
|
233
|
+
try {
|
|
234
|
+
uses24hourClock = RNLocalize.uses24HourClock();
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
const hourCycle = resolved?.hourCycle;
|
|
238
|
+
uses24hourClock = hourCycle ? hourCycle.startsWith('h2') : null;
|
|
239
|
+
}
|
|
240
|
+
// weekInfo converted from ISO numbering when the engine has it and the CLDR region table otherwise
|
|
241
|
+
let firstWeekday = null;
|
|
242
|
+
if (typeof Intl !== 'undefined') {
|
|
243
|
+
try {
|
|
244
|
+
const locale = new Intl.Locale(currentLocaleTag());
|
|
245
|
+
const isoFirstDay = locale.weekInfo?.firstDay;
|
|
246
|
+
firstWeekday = isoFirstDay != null ? (isoFirstDay % 7) + 1 : null;
|
|
247
|
+
}
|
|
248
|
+
catch { }
|
|
249
|
+
}
|
|
250
|
+
if (firstWeekday == null) {
|
|
251
|
+
firstWeekday = firstWeekdayForRegion(regionOfTag(currentLocaleTag()));
|
|
252
|
+
}
|
|
253
|
+
return [
|
|
254
|
+
{
|
|
255
|
+
calendar,
|
|
256
|
+
timeZone: currentTimezone(),
|
|
257
|
+
uses24hourClock,
|
|
258
|
+
firstWeekday,
|
|
259
|
+
},
|
|
260
|
+
];
|
|
261
|
+
},
|
|
31
262
|
};
|
|
32
263
|
export default module;
|
|
33
264
|
//# sourceMappingURL=ExpoLocalization.kepler.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoLocalization.kepler.js","sourceRoot":"","sources":["../src/ExpoLocalization.kepler.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAqB,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AAE5F,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAUlD,MAAM,YAAY,GAAG,mBAAmB,CAAC,YAAY,CAAO,kBAAkB,CAAC,CAAC;AAEhF,MAAM,UAAU,iBAAiB,CAAC,QAAoB;IAClD,OAAO,kBAAkB,CAAC,WAAW,CAAC,yBAAyB,EAAE,QAAQ,CAAC,CAAC;AAC/E,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,QAAoB;IACpD,MAAM,kBAAkB,GAAG,kBAAkB,CAAC,WAAW,CAAC,yBAAyB,EAAE,QAAQ,CAAC,CAAC;IAC/F,MAAM,oBAAoB,GAAG,kBAAkB,CAAC,WAAW,CAAC,2BAA2B,EAAE,QAAQ,CAAC,CAAC;IACnG,OAAO;QACH,MAAM,EAAE,GAAG,EAAE;YACT,kBAAkB,CAAC,MAAM,EAAE,CAAC;YAC5B,oBAAoB,CAAC,MAAM,EAAE,CAAC;QAClC,CAAC;KACY,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,YAA0B;IACzD,YAAY,CAAC,MAAM,EAAE,CAAC;AAC1B,CAAC;AAED,MAAM,MAAM,GAAG;IACX,UAAU,EAAE,YAAY,CAAC,UAAU;IACnC,YAAY,EAAE,YAAY,CAAC,YAAY;IACvC,MAAM,EAAE,YAAY,CAAC,SAAS,EAAE;CACnC,CAAC;AAEF,eAAe,MAAM,CAAC","sourcesContent":["/*\n * Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * PROPRIETARY/CONFIDENTIAL\n *\n * Use is subject to license terms.\n */\n\nimport { KeplerTurboModule, TurboModuleRegistry } from '@amazon-devices/keplerscript-turbomodule-api';\nimport type { Subscription } from 'expo-modules-core';\nimport { DeviceEventEmitter } from 'react-native';\n\nimport { Calendar, Locale } from './Localization.types';\n\ninterface Spec extends KeplerTurboModule {\n getLocales(): Locale[];\n getCalendars(): Calendar[];\n getLocale(): string;\n}\n\nconst nativeModule = TurboModuleRegistry.getEnforcing<Spec>('ExpoLocalization');\n\nexport function addLocaleListener(listener: () => void): Subscription {\n return DeviceEventEmitter.addListener('onLocaleSettingsChanged', listener);\n}\n\nexport function addCalendarListener(listener: () => void): Subscription {\n const localeSubscription = DeviceEventEmitter.addListener('onLocaleSettingsChanged', listener);\n const timezoneSubscription = DeviceEventEmitter.addListener('onCalendarSettingsChanged', listener);\n return {\n remove: () => {\n localeSubscription.remove();\n timezoneSubscription.remove();\n },\n } as Subscription;\n}\n\nexport function removeSubscription(subscription: Subscription) {\n subscription.remove();\n}\n\nconst module = {\n getLocales: nativeModule.getLocales,\n getCalendars: nativeModule.getCalendars,\n locale: nativeModule.getLocale(),\n};\n\nexport default module;\n"]}
|
|
1
|
+
{"version":3,"file":"ExpoLocalization.kepler.js","sourceRoot":"","sources":["../src/ExpoLocalization.kepler.ts"],"names":[],"mappings":"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AE,EAAE,KAAK;IACtF,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK;IACpB,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK;IACtF,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK;IACtF,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK;IACtF,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK;IACtF,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK;IACtF,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK;IACtF,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK;IACtF,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK;IACtF,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK;IACtF,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK;IACtF,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK;IACtF,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK;IACtF,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK;IACtF,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK;IACtF,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,E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","sourcesContent":["/*\n * Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * PROPRIETARY/CONFIDENTIAL\n *\n * Use is subject to license terms.\n */\n\n// Pure JS on Hermes Intl plus the react-native-localize turbomodule so the\n// package ships app-deployed with no native library and no embedded ICU.\nimport type { Subscription } from 'expo-modules-core';\nimport * as rtlDetect from 'rtl-detect';\n\nimport { Calendar, CalendarIdentifier, Locale } from './Localization.types';\n\n// RNLocalize looks up its TM at module load so guard the require and fall back to Intl where the library is absent\nlet RNLocalize: any;\ntry {\n RNLocalize = require('@amazon-devices/react-native-localize');\n} catch {\n RNLocalize = {};\n}\n\n// textInfo and weekInfo are recent Intl additions so both are feature detected\ntype ExtendedIntlLocale = Intl.Locale &\n Partial<{\n textInfo: { direction: 'ltr' | 'rtl' };\n weekInfo: { firstDay: number };\n }>;\n\n// CLDR territory currency data so each entry carries its own region's tender with the user currency as fallback\nconst REGION_CURRENCIES: Record<string, string> = {\n // euro area and territories\n AD: 'EUR', AT: 'EUR', AX: 'EUR', BE: 'EUR', BL: 'EUR', CY: 'EUR', DE: 'EUR', EE: 'EUR',\n ES: 'EUR', FI: 'EUR', FR: 'EUR', GF: 'EUR', GP: 'EUR', GR: 'EUR', HR: 'EUR', IE: 'EUR',\n IT: 'EUR', LT: 'EUR', LU: 'EUR', LV: 'EUR', MC: 'EUR', ME: 'EUR', MF: 'EUR', MQ: 'EUR',\n MT: 'EUR', NL: 'EUR', PM: 'EUR', PT: 'EUR', RE: 'EUR', SI: 'EUR', SK: 'EUR', SM: 'EUR',\n TF: 'EUR', VA: 'EUR', XK: 'EUR', YT: 'EUR',\n // US dollar territories\n US: 'USD', AS: 'USD', BQ: 'USD', EC: 'USD', FM: 'USD', GU: 'USD', IO: 'USD', MH: 'USD',\n MP: 'USD', PR: 'USD', PW: 'USD', SV: 'USD', TC: 'USD', TL: 'USD', UM: 'USD', VG: 'USD', VI: 'USD',\n // pound and crown dependencies\n GB: 'GBP', GG: 'GBP', GS: 'GBP', IM: 'GBP', JE: 'GBP',\n // Australian and New Zealand dollar territories\n AU: 'AUD', CC: 'AUD', CX: 'AUD', HM: 'AUD', KI: 'AUD', NF: 'AUD', NR: 'AUD', TV: 'AUD',\n NZ: 'NZD', CK: 'NZD', NU: 'NZD', PN: 'NZD', TK: 'NZD',\n // CFA and CFP francs and East Caribbean dollar\n BF: 'XOF', BJ: 'XOF', CI: 'XOF', GW: 'XOF', ML: 'XOF', NE: 'XOF', SN: 'XOF', TG: 'XOF',\n CF: 'XAF', CG: 'XAF', CM: 'XAF', GA: 'XAF', GQ: 'XAF', TD: 'XAF',\n NC: 'XPF', PF: 'XPF', WF: 'XPF',\n AG: 'XCD', AI: 'XCD', DM: 'XCD', GD: 'XCD', KN: 'XCD', LC: 'XCD', MS: 'XCD', VC: 'XCD',\n // nordics and remaining single tenders\n DK: 'DKK', FO: 'DKK', GL: 'DKK', NO: 'NOK', BV: 'NOK', SJ: 'NOK', SE: 'SEK', IS: 'ISK',\n CH: 'CHF', LI: 'CHF',\n AE: 'AED', AF: 'AFN', AL: 'ALL', AM: 'AMD', AO: 'AOA', AR: 'ARS', AW: 'AWG', AZ: 'AZN',\n BA: 'BAM', BB: 'BBD', BD: 'BDT', BG: 'BGN', BH: 'BHD', BI: 'BIF', BM: 'BMD', BN: 'BND',\n BO: 'BOB', BR: 'BRL', BS: 'BSD', BT: 'BTN', BW: 'BWP', BY: 'BYN', BZ: 'BZD', CA: 'CAD',\n CD: 'CDF', CL: 'CLP', CN: 'CNY', CO: 'COP', CR: 'CRC', CU: 'CUP', CV: 'CVE', CW: 'ANG',\n CZ: 'CZK', DJ: 'DJF', DO: 'DOP', DZ: 'DZD', EG: 'EGP', EH: 'MAD', ER: 'ERN', ET: 'ETB',\n FJ: 'FJD', FK: 'FKP', GE: 'GEL', GH: 'GHS', GI: 'GIP', GM: 'GMD', GN: 'GNF', GT: 'GTQ',\n GY: 'GYD', HK: 'HKD', HN: 'HNL', HT: 'HTG', HU: 'HUF', ID: 'IDR', IL: 'ILS', IN: 'INR',\n IQ: 'IQD', IR: 'IRR', JM: 'JMD', JO: 'JOD', JP: 'JPY', KE: 'KES', KG: 'KGS', KH: 'KHR',\n KM: 'KMF', KP: 'KPW', KR: 'KRW', KW: 'KWD', KY: 'KYD', KZ: 'KZT', LA: 'LAK', LB: 'LBP',\n LK: 'LKR', LR: 'LRD', LS: 'LSL', LY: 'LYD', MA: 'MAD', MD: 'MDL', MG: 'MGA', MK: 'MKD',\n MM: 'MMK', MN: 'MNT', MO: 'MOP', MR: 'MRU', MU: 'MUR', MV: 'MVR', MW: 'MWK', MX: 'MXN',\n MY: 'MYR', MZ: 'MZN', NA: 'NAD', NG: 'NGN', NI: 'NIO', NP: 'NPR', OM: 'OMR', PA: 'PAB',\n PE: 'PEN', PG: 'PGK', PH: 'PHP', PK: 'PKR', PL: 'PLN', PS: 'ILS', PY: 'PYG', QA: 'QAR',\n RO: 'RON', RS: 'RSD', RU: 'RUB', RW: 'RWF', SA: 'SAR', SB: 'SBD', SC: 'SCR', SD: 'SDG',\n SG: 'SGD', SH: 'SHP', SL: 'SLE', SO: 'SOS', SR: 'SRD', SS: 'SSP', ST: 'STN', SY: 'SYP',\n SZ: 'SZL', TH: 'THB', TJ: 'TJS', TM: 'TMT', TN: 'TND', TO: 'TOP', TR: 'TRY', TT: 'TTD',\n TW: 'TWD', TZ: 'TZS', UA: 'UAH', UG: 'UGX', UY: 'UYU', UZ: 'UZS', VE: 'VES', VN: 'VND',\n VU: 'VUV', WS: 'WST', YE: 'YER', ZA: 'ZAR', ZM: 'ZMW', ZW: 'ZWG',\n};\n\n// https://wisevoter.com/country-rankings/countries-that-use-fahrenheit/\nconst USES_FAHRENHEIT = [\n 'AG', 'BZ', 'VG', 'FM', 'MH', 'MS', 'KN', 'BS', 'TC', 'US', 'LR', 'PW', 'KY',\n];\n\n// CLDR weekData firstDay per region with Sunday as 1 and Saturday as 7 and\n// Monday as the default for regions in neither list\nconst WEEK_STARTS_SUNDAY = [\n 'AG', 'AS', 'BD', 'BR', 'BS', 'BT', 'BW', 'BZ', 'CA', 'CO', 'DM', 'DO', 'ET',\n 'GT', 'GU', 'HK', 'HN', 'ID', 'IL', 'IN', 'JM', 'JP', 'KE', 'KH', 'KR', 'LA',\n 'MH', 'MM', 'MO', 'MT', 'MX', 'MZ', 'NI', 'NP', 'PA', 'PE', 'PH', 'PK', 'PR',\n 'PY', 'SA', 'SG', 'SV', 'TH', 'TT', 'TW', 'UM', 'US', 'VE', 'VI', 'WS', 'YE',\n 'ZA', 'ZW',\n];\nconst WEEK_STARTS_SATURDAY = ['AE', 'AF', 'BH', 'DJ', 'DZ', 'EG', 'IQ', 'IR', 'JO', 'KW', 'LY', 'OM', 'QA', 'SD', 'SY'];\n\nfunction firstWeekdayForRegion(region: string | null): number {\n if (region == null) {\n return 2;\n }\n if (WEEK_STARTS_SUNDAY.includes(region)) {\n return 1;\n }\n if (WEEK_STARTS_SATURDAY.includes(region)) {\n return 7;\n }\n return 2;\n}\n\nfunction currentLanguageTags(): string[] {\n try {\n const locales = RNLocalize.getLocales();\n if (Array.isArray(locales) && locales.length > 0) {\n return locales.map((l: { languageTag: string }) => l.languageTag);\n }\n } catch {}\n if (typeof Intl !== 'undefined') {\n const tag = Intl.DateTimeFormat().resolvedOptions().locale;\n if (tag) {\n return [tag];\n }\n }\n return ['en-US'];\n}\n\nfunction currencyForCurrentLocale(): string | null {\n try {\n const currencies = RNLocalize.getCurrencies();\n if (Array.isArray(currencies) && currencies.length > 0) {\n return currencies[0];\n }\n } catch {}\n return null;\n}\n\nfunction currencySymbolFor(languageTag: string, currencyCode: string | null): string | null {\n if (!currencyCode || typeof Intl === 'undefined') {\n return null;\n }\n try {\n const parts = new Intl.NumberFormat(languageTag, {\n style: 'currency',\n currency: currencyCode,\n }).formatToParts(0);\n return parts.find((p) => p.type === 'currency')?.value ?? null;\n } catch {\n return null;\n }\n}\n\n// Best-effort start of the events turbomodule which forwards OS locale and timezone changes to DeviceEventEmitter\ntry {\n const { TurboModuleRegistry } = require('@amazon-devices/keplerscript-turbomodule-api');\n TurboModuleRegistry.getEnforcing('ExpoLocalizationAppTM');\n} catch {\n // without it the listeners never fire which was the previous behavior\n}\n\nconst DeviceEventEmitter = require('react-native').DeviceEventEmitter;\n\nexport function addLocaleListener(listener: (event) => void): Subscription {\n return DeviceEventEmitter.addListener('onLocaleSettingsChanged', listener) as Subscription;\n}\n\nexport function addCalendarListener(listener: (event) => void): Subscription {\n // Calendar values include the timezone so both signals invalidate them\n const localeSubscription = DeviceEventEmitter.addListener('onLocaleSettingsChanged', listener);\n const timezoneSubscription = DeviceEventEmitter.addListener('onCalendarSettingsChanged', listener);\n return {\n remove: () => {\n localeSubscription.remove();\n timezoneSubscription.remove();\n },\n } as Subscription;\n}\n\nexport function removeSubscription(subscription: Subscription) {\n subscription.remove();\n}\n\n// Wire exactly the surface the 2.x Vega module exposed which is locale and getLocales and getCalendars\nfunction currentLocaleTag(): string {\n return currentLanguageTags()[0] || 'en-US';\n}\n\nfunction regionOfTag(tag: string): string | null {\n const [, ...suffixes] = tag.split('-');\n for (const suffix of suffixes) {\n if (suffix.length === 2) {\n return suffix.toUpperCase();\n }\n }\n return null;\n}\n\nfunction currentTimezone(): string {\n try {\n const tz = RNLocalize.getTimeZone();\n if (tz) {\n return tz;\n }\n } catch {}\n if (typeof Intl !== 'undefined') {\n return Intl.DateTimeFormat().resolvedOptions().timeZone || 'Etc/UTC';\n }\n return 'Etc/UTC';\n}\n\nfunction usesMetric(): boolean {\n try {\n return RNLocalize.usesMetricSystem();\n } catch {}\n const region = regionOfTag(currentLocaleTag());\n return region == null || !['US', 'LR', 'MM'].includes(region);\n}\n\nconst module = {\n get locale(): string {\n return currentLocaleTag();\n },\n\n getLocales(): Locale[] {\n const userCurrency = currencyForCurrentLocale();\n return currentLanguageTags().map((languageTag) => {\n const locale =\n typeof Intl !== 'undefined'\n ? (new Intl.Locale(languageTag) as unknown as ExtendedIntlLocale)\n : ({ region: undefined, textInfo: undefined, language: undefined } as unknown as ExtendedIntlLocale);\n const region = locale.region ?? null;\n const languageCode = locale.language || languageTag.split('-')[0] || 'en';\n const textDirection: 'ltr' | 'rtl' =\n locale.textInfo?.direction ?? (rtlDetect.isRtlLang(languageTag) ? 'rtl' : 'ltr');\n const digitGroupingSeparator =\n Array.from((10000).toLocaleString(languageTag)).filter((c) => c > '9' || c < '0')[0] || null;\n const decimalSeparator = (1.1).toLocaleString(languageTag).substring(1, 2);\n const currencyCode = (region && REGION_CURRENCIES[region]) || userCurrency;\n\n return {\n languageTag,\n languageCode,\n textDirection,\n digitGroupingSeparator,\n decimalSeparator,\n measurementSystem: (region === 'GB'\n ? 'uk'\n : region\n ? (['US', 'LR', 'MM'].includes(region) ? 'us' : 'metric')\n : usesMetric()\n ? 'metric'\n : 'us') as Locale['measurementSystem'],\n currencyCode,\n currencySymbol: currencySymbolFor(languageTag, currencyCode),\n regionCode: region,\n temperatureUnit: region\n ? ((USES_FAHRENHEIT.includes(region) ? 'fahrenheit' : 'celsius') as Locale['temperatureUnit'])\n : null,\n };\n });\n },\n\n getCalendars(): Calendar[] {\n const resolved =\n typeof Intl !== 'undefined' ? Intl.DateTimeFormat().resolvedOptions() : undefined;\n let calendar: CalendarIdentifier | null = (resolved?.calendar as CalendarIdentifier) ?? null;\n try {\n calendar = (RNLocalize.getCalendar() as CalendarIdentifier) ?? calendar;\n } catch {}\n let uses24hourClock: boolean | null = null;\n try {\n uses24hourClock = RNLocalize.uses24HourClock();\n } catch {\n const hourCycle = (resolved as { hourCycle?: string } | undefined)?.hourCycle;\n uses24hourClock = hourCycle ? hourCycle.startsWith('h2') : null;\n }\n // weekInfo converted from ISO numbering when the engine has it and the CLDR region table otherwise\n let firstWeekday: number | null = null;\n if (typeof Intl !== 'undefined') {\n try {\n const locale = new Intl.Locale(currentLocaleTag()) as ExtendedIntlLocale;\n const isoFirstDay = locale.weekInfo?.firstDay;\n firstWeekday = isoFirstDay != null ? (isoFirstDay % 7) + 1 : null;\n } catch {}\n }\n if (firstWeekday == null) {\n firstWeekday = firstWeekdayForRegion(regionOfTag(currentLocaleTag()));\n }\n return [\n {\n calendar,\n timeZone: currentTimezone(),\n uses24hourClock,\n firstWeekday,\n },\n ];\n },\n};\n\nexport default module;\n"]}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
*
|
|
4
|
+
* PROPRIETARY/CONFIDENTIAL
|
|
5
|
+
*
|
|
6
|
+
* Use is subject to license terms.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
#include "ExpoLocalizationEvents.h"
|
|
10
|
+
|
|
11
|
+
#include <Kepler/turbomodule/TMLog.h>
|
|
12
|
+
#include <apmf/apmf_base.h>
|
|
13
|
+
#include <apmf/fn.h>
|
|
14
|
+
#include <apmf/iface/com/amazon/kepler/env/IEnvModule.h>
|
|
15
|
+
#include <apmf/iface/com/amazon/kepler/env/ILocaleConfig.h>
|
|
16
|
+
#include <apmf/iface/com/amazon/kepler/env/IResolvedLocalesListener.h>
|
|
17
|
+
#include <apmf/iface/com/amazon/kepler/experimental_mr33/messaging/IKeplerMessagingModule.h>
|
|
18
|
+
#include <apmf/iface/com/amazon/kepler/experimental_mr33/messaging/IMessageFilterBuilder.h>
|
|
19
|
+
#include <apmf/iface/com/amazon/kepler/experimental_mr33/messaging/IMessageListener.h>
|
|
20
|
+
|
|
21
|
+
#include <memory>
|
|
22
|
+
#include <string>
|
|
23
|
+
|
|
24
|
+
namespace env = apmf::iface::com::amazon::kepler::env;
|
|
25
|
+
namespace messaging = apmf::iface::com::amazon::kepler::experimental_mr33::messaging;
|
|
26
|
+
|
|
27
|
+
namespace {
|
|
28
|
+
|
|
29
|
+
apmf::Ptr<env::IEnvModule> getEnvModule() {
|
|
30
|
+
return apmf::detail::GetProcessObject()
|
|
31
|
+
->getComponent("/com.amazon.kepler.env")
|
|
32
|
+
.TryQueryInterface<env::IEnvModule>();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
} // namespace
|
|
36
|
+
|
|
37
|
+
ExpoLocalizationEvents::ExpoLocalizationEvents() : KeplerTurboModule("ExpoLocalizationEvents") {
|
|
38
|
+
TMDEBUG(std::string("ExpoLocalizationEvents constructor ") + name());
|
|
39
|
+
|
|
40
|
+
// Guarded because this runs in a noexcept autolink entry and the env lookup throws on hosts without the module
|
|
41
|
+
try {
|
|
42
|
+
getEnvModule()->getLocaleConfig()->addResolvedLocalesListener(
|
|
43
|
+
nullptr, apmf::Fn1([this, alive = m_alive](auto locales) {
|
|
44
|
+
if (alive->load()) {
|
|
45
|
+
this->emit("onLocaleSettingsChanged", nullptr);
|
|
46
|
+
}
|
|
47
|
+
}));
|
|
48
|
+
} catch (const std::exception& e) {
|
|
49
|
+
TMWARN(std::string("env locale config unavailable so locale change events are disabled: ") + e.what());
|
|
50
|
+
} catch (...) {
|
|
51
|
+
TMWARN("env locale config unavailable so locale change events are disabled");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Timezone changes via MR33 messaging with the same degradation where it is absent
|
|
55
|
+
apmf::detail::Ptr<messaging::IKeplerMessagingModule> messagingModule;
|
|
56
|
+
try {
|
|
57
|
+
auto proc = apmf::detail::GetProcessObject();
|
|
58
|
+
messagingModule = proc->getComponent("/com.amazon.kepler.experimental_mr33.messaging")
|
|
59
|
+
.TryQueryInterface<messaging::IKeplerMessagingModule>();
|
|
60
|
+
} catch (const std::exception& e) {
|
|
61
|
+
TMWARN(std::string("MR33 messaging unavailable so timezone change events are disabled: ") + e.what());
|
|
62
|
+
} catch (...) {
|
|
63
|
+
TMWARN("MR33 messaging unavailable so timezone change events are disabled");
|
|
64
|
+
}
|
|
65
|
+
if (messagingModule) {
|
|
66
|
+
struct TZListener : apmf::ApmfBase<TZListener, messaging::IMessageListener> {
|
|
67
|
+
ExpoLocalizationEvents* owner;
|
|
68
|
+
std::shared_ptr<std::atomic<bool>> alive;
|
|
69
|
+
TZListener(ExpoLocalizationEvents* o, std::shared_ptr<std::atomic<bool>> a)
|
|
70
|
+
: owner(o), alive(std::move(a)) {}
|
|
71
|
+
void onMessage(apmf::View<messaging::IMessage> /*message*/) {
|
|
72
|
+
if (alive->load()) {
|
|
73
|
+
owner->emit("onCalendarSettingsChanged", nullptr);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
void onError(apmf::View<apmf::IError> /*error*/) {}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
auto channel = messagingModule->makeMessageChannel(
|
|
80
|
+
"ExpoLocalization_TZ",
|
|
81
|
+
apmf::detail::Ptr<TZListener>::Make(this, m_alive));
|
|
82
|
+
|
|
83
|
+
apmf::detail::Array<apmf::detail::Ptr<messaging::IMessageFilter>> filters;
|
|
84
|
+
filters.push_back(
|
|
85
|
+
messagingModule->makeMessageFilterBuilder()
|
|
86
|
+
->baseUri("broadcast://*/com.amazon.devconf/system/configuration/time-zone")
|
|
87
|
+
->build());
|
|
88
|
+
channel->registerMessageFilter(filters);
|
|
89
|
+
m_timezoneChannel = std::move(channel);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
ExpoLocalizationEvents::~ExpoLocalizationEvents() {
|
|
94
|
+
m_alive->store(false);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// No-ops because subscriptions live in JS via DeviceEventEmitter and emits do not track counts
|
|
98
|
+
void ExpoLocalizationEvents::addListener(std::string /*eventName*/) {}
|
|
99
|
+
|
|
100
|
+
void ExpoLocalizationEvents::removeListeners(double /*count*/) {}
|
|
101
|
+
|
|
102
|
+
void ExpoLocalizationEvents::aggregateMethods(
|
|
103
|
+
tbm::MethodAggregator<KeplerTurboModule>& methodAggregator) const noexcept {
|
|
104
|
+
// The standard RN event module methods keep the napi method table non empty
|
|
105
|
+
// because NapiTurboModule::defineProperties rejects a zero method count
|
|
106
|
+
methodAggregator.addMethod("addListener", 0, &ExpoLocalizationEvents::addListener);
|
|
107
|
+
methodAggregator.addMethod("removeListeners", 0, &ExpoLocalizationEvents::removeListeners);
|
|
108
|
+
}
|
|
@@ -12,20 +12,29 @@
|
|
|
12
12
|
#include <apmf/iface/com/amazon/kepler/experimental_mr33/messaging/IMessageChannel.h>
|
|
13
13
|
#include <apmf/ptr.h>
|
|
14
14
|
|
|
15
|
+
#include <atomic>
|
|
16
|
+
#include <memory>
|
|
17
|
+
#include <string>
|
|
18
|
+
|
|
15
19
|
#include "expo-export.h"
|
|
16
20
|
|
|
17
21
|
namespace tbm = com::amazon::kepler::turbomodule;
|
|
18
22
|
|
|
19
|
-
|
|
23
|
+
// Events-only turbomodule that forwards the two OS change signals to JS since the read path is pure JS
|
|
24
|
+
class EXPO_EXPORT ExpoLocalizationEvents : public tbm::KeplerTurboModule {
|
|
20
25
|
public:
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
tbm::JSArray getLocales();
|
|
24
|
-
tbm::JSArray getCalendars();
|
|
26
|
+
ExpoLocalizationEvents();
|
|
27
|
+
~ExpoLocalizationEvents() override;
|
|
25
28
|
|
|
26
29
|
void aggregateMethods(tbm::MethodAggregator<tbm::KeplerTurboModule>& methodAggregator) const noexcept override;
|
|
27
30
|
|
|
31
|
+
// Standard RN event module surface so the napi method table is never empty
|
|
32
|
+
void addListener(std::string eventName);
|
|
33
|
+
void removeListeners(double count);
|
|
34
|
+
|
|
28
35
|
private:
|
|
36
|
+
// Liveness flag so a callback delivered after destruction cannot touch the freed instance
|
|
37
|
+
std::shared_ptr<std::atomic<bool>> m_alive{std::make_shared<std::atomic<bool>>(true)};
|
|
29
38
|
apmf::detail::Ptr<apmf::iface::com::amazon::kepler::experimental_mr33::messaging::IMessageChannel>
|
|
30
39
|
m_timezoneChannel;
|
|
31
40
|
};
|
package/kepler/KeplerInit.cpp
CHANGED
|
@@ -9,12 +9,12 @@
|
|
|
9
9
|
#include <Kepler/turbomodule/KeplerTurboModuleRegistration.h>
|
|
10
10
|
#include <Kepler/turbomodule/TMLog.h>
|
|
11
11
|
|
|
12
|
-
#include "
|
|
12
|
+
#include "ExpoLocalizationEvents.h"
|
|
13
13
|
|
|
14
14
|
extern "C" {
|
|
15
15
|
__attribute__((visibility("default"))) void autoLinkKeplerTurboModulesV1() noexcept {
|
|
16
16
|
TMDEBUG("autoLinkKeplerTurboModulesV1 for expo-localization");
|
|
17
|
-
KEPLER_REGISTER_TURBO_MODULE_WITH_NAME("
|
|
17
|
+
KEPLER_REGISTER_TURBO_MODULE_WITH_NAME("ExpoLocalizationAppTM", ExpoLocalizationEvents);
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name": "@amazon-devices/expo-localization", "version": "
|
|
1
|
+
{"name": "@amazon-devices/expo-localization", "version": "3.0.2+14.6.0", "description": "Provides an interface for native user localization information.", "main": "build/Localization.js", "types": "build/Localization.d.ts", "sideEffects": false, "scripts": {"build": "expo-module build", "clean": "expo-module clean", "lint": "expo-module lint", "test": "expo-module test", "prepublishOnly": "expo-module prepublishOnly", "expo-module": "expo-module"}, "kepler": {"temporary-alias-for-package": {"name": "expo-localization", "version": "14.6.0"}}, "keywords": ["react-native", "expo", "localization", "locales", "l10n"], "repository": {"type": "git", "url": "https://github.com/expo/expo.git", "directory": "packages/expo-localization"}, "bugs": {"url": "https://github.com/expo/expo/issues"}, "contributors": ["Evan Bacon <bacon@expo.io> (https://github.com/evanbacon)"], "author": "650 Industries, Inc.", "license": "See license in LICENSE", "homepage": "https://docs.expo.dev/versions/latest/sdk/localization/", "jest": {"preset": "expo-module-scripts"}, "dependencies": {"rtl-detect": "^1.0.2"}, "devDependencies": {"expo-module-scripts": "^3.0.0", "i18n-js": "^3.1.0", "@types/minimatch": "^3.0.5", "@amazon-devices/react-native-localize": "^2.0.0"}, "peerDependencies": {"expo": "^50.0.0-alpha.1", "@amazon-devices/react-native-localize": "*"}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"root":["./src/withExpoLocalization.ts"],"version":"5.9.3"}
|
package/react-native.config.js
CHANGED
|
@@ -10,10 +10,11 @@ module.exports = {
|
|
|
10
10
|
platforms: {
|
|
11
11
|
kepler: {
|
|
12
12
|
autolink: {
|
|
13
|
-
|
|
14
|
-
provider: '
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
ExpoLocalizationAppLib: {
|
|
14
|
+
provider: 'application',
|
|
15
|
+
linkDynamic: true,
|
|
16
|
+
libraryName: 'libexpo-localization-3.so',
|
|
17
|
+
turbomodules: ['ExpoLocalizationAppTM'],
|
|
17
18
|
},
|
|
18
19
|
},
|
|
19
20
|
},
|
|
@@ -6,43 +6,287 @@
|
|
|
6
6
|
* Use is subject to license terms.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
// Pure JS on Hermes Intl plus the react-native-localize turbomodule so the
|
|
10
|
+
// package ships app-deployed with no native library and no embedded ICU.
|
|
10
11
|
import type { Subscription } from 'expo-modules-core';
|
|
11
|
-
import
|
|
12
|
+
import * as rtlDetect from 'rtl-detect';
|
|
12
13
|
|
|
13
|
-
import { Calendar, Locale } from './Localization.types';
|
|
14
|
+
import { Calendar, CalendarIdentifier, Locale } from './Localization.types';
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
// RNLocalize looks up its TM at module load so guard the require and fall back to Intl where the library is absent
|
|
17
|
+
let RNLocalize: any;
|
|
18
|
+
try {
|
|
19
|
+
RNLocalize = require('@amazon-devices/react-native-localize');
|
|
20
|
+
} catch {
|
|
21
|
+
RNLocalize = {};
|
|
19
22
|
}
|
|
20
23
|
|
|
21
|
-
|
|
24
|
+
// textInfo and weekInfo are recent Intl additions so both are feature detected
|
|
25
|
+
type ExtendedIntlLocale = Intl.Locale &
|
|
26
|
+
Partial<{
|
|
27
|
+
textInfo: { direction: 'ltr' | 'rtl' };
|
|
28
|
+
weekInfo: { firstDay: number };
|
|
29
|
+
}>;
|
|
22
30
|
|
|
23
|
-
|
|
24
|
-
|
|
31
|
+
// CLDR territory currency data so each entry carries its own region's tender with the user currency as fallback
|
|
32
|
+
const REGION_CURRENCIES: Record<string, string> = {
|
|
33
|
+
// euro area and territories
|
|
34
|
+
AD: 'EUR', AT: 'EUR', AX: 'EUR', BE: 'EUR', BL: 'EUR', CY: 'EUR', DE: 'EUR', EE: 'EUR',
|
|
35
|
+
ES: 'EUR', FI: 'EUR', FR: 'EUR', GF: 'EUR', GP: 'EUR', GR: 'EUR', HR: 'EUR', IE: 'EUR',
|
|
36
|
+
IT: 'EUR', LT: 'EUR', LU: 'EUR', LV: 'EUR', MC: 'EUR', ME: 'EUR', MF: 'EUR', MQ: 'EUR',
|
|
37
|
+
MT: 'EUR', NL: 'EUR', PM: 'EUR', PT: 'EUR', RE: 'EUR', SI: 'EUR', SK: 'EUR', SM: 'EUR',
|
|
38
|
+
TF: 'EUR', VA: 'EUR', XK: 'EUR', YT: 'EUR',
|
|
39
|
+
// US dollar territories
|
|
40
|
+
US: 'USD', AS: 'USD', BQ: 'USD', EC: 'USD', FM: 'USD', GU: 'USD', IO: 'USD', MH: 'USD',
|
|
41
|
+
MP: 'USD', PR: 'USD', PW: 'USD', SV: 'USD', TC: 'USD', TL: 'USD', UM: 'USD', VG: 'USD', VI: 'USD',
|
|
42
|
+
// pound and crown dependencies
|
|
43
|
+
GB: 'GBP', GG: 'GBP', GS: 'GBP', IM: 'GBP', JE: 'GBP',
|
|
44
|
+
// Australian and New Zealand dollar territories
|
|
45
|
+
AU: 'AUD', CC: 'AUD', CX: 'AUD', HM: 'AUD', KI: 'AUD', NF: 'AUD', NR: 'AUD', TV: 'AUD',
|
|
46
|
+
NZ: 'NZD', CK: 'NZD', NU: 'NZD', PN: 'NZD', TK: 'NZD',
|
|
47
|
+
// CFA and CFP francs and East Caribbean dollar
|
|
48
|
+
BF: 'XOF', BJ: 'XOF', CI: 'XOF', GW: 'XOF', ML: 'XOF', NE: 'XOF', SN: 'XOF', TG: 'XOF',
|
|
49
|
+
CF: 'XAF', CG: 'XAF', CM: 'XAF', GA: 'XAF', GQ: 'XAF', TD: 'XAF',
|
|
50
|
+
NC: 'XPF', PF: 'XPF', WF: 'XPF',
|
|
51
|
+
AG: 'XCD', AI: 'XCD', DM: 'XCD', GD: 'XCD', KN: 'XCD', LC: 'XCD', MS: 'XCD', VC: 'XCD',
|
|
52
|
+
// nordics and remaining single tenders
|
|
53
|
+
DK: 'DKK', FO: 'DKK', GL: 'DKK', NO: 'NOK', BV: 'NOK', SJ: 'NOK', SE: 'SEK', IS: 'ISK',
|
|
54
|
+
CH: 'CHF', LI: 'CHF',
|
|
55
|
+
AE: 'AED', AF: 'AFN', AL: 'ALL', AM: 'AMD', AO: 'AOA', AR: 'ARS', AW: 'AWG', AZ: 'AZN',
|
|
56
|
+
BA: 'BAM', BB: 'BBD', BD: 'BDT', BG: 'BGN', BH: 'BHD', BI: 'BIF', BM: 'BMD', BN: 'BND',
|
|
57
|
+
BO: 'BOB', BR: 'BRL', BS: 'BSD', BT: 'BTN', BW: 'BWP', BY: 'BYN', BZ: 'BZD', CA: 'CAD',
|
|
58
|
+
CD: 'CDF', CL: 'CLP', CN: 'CNY', CO: 'COP', CR: 'CRC', CU: 'CUP', CV: 'CVE', CW: 'ANG',
|
|
59
|
+
CZ: 'CZK', DJ: 'DJF', DO: 'DOP', DZ: 'DZD', EG: 'EGP', EH: 'MAD', ER: 'ERN', ET: 'ETB',
|
|
60
|
+
FJ: 'FJD', FK: 'FKP', GE: 'GEL', GH: 'GHS', GI: 'GIP', GM: 'GMD', GN: 'GNF', GT: 'GTQ',
|
|
61
|
+
GY: 'GYD', HK: 'HKD', HN: 'HNL', HT: 'HTG', HU: 'HUF', ID: 'IDR', IL: 'ILS', IN: 'INR',
|
|
62
|
+
IQ: 'IQD', IR: 'IRR', JM: 'JMD', JO: 'JOD', JP: 'JPY', KE: 'KES', KG: 'KGS', KH: 'KHR',
|
|
63
|
+
KM: 'KMF', KP: 'KPW', KR: 'KRW', KW: 'KWD', KY: 'KYD', KZ: 'KZT', LA: 'LAK', LB: 'LBP',
|
|
64
|
+
LK: 'LKR', LR: 'LRD', LS: 'LSL', LY: 'LYD', MA: 'MAD', MD: 'MDL', MG: 'MGA', MK: 'MKD',
|
|
65
|
+
MM: 'MMK', MN: 'MNT', MO: 'MOP', MR: 'MRU', MU: 'MUR', MV: 'MVR', MW: 'MWK', MX: 'MXN',
|
|
66
|
+
MY: 'MYR', MZ: 'MZN', NA: 'NAD', NG: 'NGN', NI: 'NIO', NP: 'NPR', OM: 'OMR', PA: 'PAB',
|
|
67
|
+
PE: 'PEN', PG: 'PGK', PH: 'PHP', PK: 'PKR', PL: 'PLN', PS: 'ILS', PY: 'PYG', QA: 'QAR',
|
|
68
|
+
RO: 'RON', RS: 'RSD', RU: 'RUB', RW: 'RWF', SA: 'SAR', SB: 'SBD', SC: 'SCR', SD: 'SDG',
|
|
69
|
+
SG: 'SGD', SH: 'SHP', SL: 'SLE', SO: 'SOS', SR: 'SRD', SS: 'SSP', ST: 'STN', SY: 'SYP',
|
|
70
|
+
SZ: 'SZL', TH: 'THB', TJ: 'TJS', TM: 'TMT', TN: 'TND', TO: 'TOP', TR: 'TRY', TT: 'TTD',
|
|
71
|
+
TW: 'TWD', TZ: 'TZS', UA: 'UAH', UG: 'UGX', UY: 'UYU', UZ: 'UZS', VE: 'VES', VN: 'VND',
|
|
72
|
+
VU: 'VUV', WS: 'WST', YE: 'YER', ZA: 'ZAR', ZM: 'ZMW', ZW: 'ZWG',
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// https://wisevoter.com/country-rankings/countries-that-use-fahrenheit/
|
|
76
|
+
const USES_FAHRENHEIT = [
|
|
77
|
+
'AG', 'BZ', 'VG', 'FM', 'MH', 'MS', 'KN', 'BS', 'TC', 'US', 'LR', 'PW', 'KY',
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
// CLDR weekData firstDay per region with Sunday as 1 and Saturday as 7 and
|
|
81
|
+
// Monday as the default for regions in neither list
|
|
82
|
+
const WEEK_STARTS_SUNDAY = [
|
|
83
|
+
'AG', 'AS', 'BD', 'BR', 'BS', 'BT', 'BW', 'BZ', 'CA', 'CO', 'DM', 'DO', 'ET',
|
|
84
|
+
'GT', 'GU', 'HK', 'HN', 'ID', 'IL', 'IN', 'JM', 'JP', 'KE', 'KH', 'KR', 'LA',
|
|
85
|
+
'MH', 'MM', 'MO', 'MT', 'MX', 'MZ', 'NI', 'NP', 'PA', 'PE', 'PH', 'PK', 'PR',
|
|
86
|
+
'PY', 'SA', 'SG', 'SV', 'TH', 'TT', 'TW', 'UM', 'US', 'VE', 'VI', 'WS', 'YE',
|
|
87
|
+
'ZA', 'ZW',
|
|
88
|
+
];
|
|
89
|
+
const WEEK_STARTS_SATURDAY = ['AE', 'AF', 'BH', 'DJ', 'DZ', 'EG', 'IQ', 'IR', 'JO', 'KW', 'LY', 'OM', 'QA', 'SD', 'SY'];
|
|
90
|
+
|
|
91
|
+
function firstWeekdayForRegion(region: string | null): number {
|
|
92
|
+
if (region == null) {
|
|
93
|
+
return 2;
|
|
94
|
+
}
|
|
95
|
+
if (WEEK_STARTS_SUNDAY.includes(region)) {
|
|
96
|
+
return 1;
|
|
97
|
+
}
|
|
98
|
+
if (WEEK_STARTS_SATURDAY.includes(region)) {
|
|
99
|
+
return 7;
|
|
100
|
+
}
|
|
101
|
+
return 2;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function currentLanguageTags(): string[] {
|
|
105
|
+
try {
|
|
106
|
+
const locales = RNLocalize.getLocales();
|
|
107
|
+
if (Array.isArray(locales) && locales.length > 0) {
|
|
108
|
+
return locales.map((l: { languageTag: string }) => l.languageTag);
|
|
109
|
+
}
|
|
110
|
+
} catch {}
|
|
111
|
+
if (typeof Intl !== 'undefined') {
|
|
112
|
+
const tag = Intl.DateTimeFormat().resolvedOptions().locale;
|
|
113
|
+
if (tag) {
|
|
114
|
+
return [tag];
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return ['en-US'];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function currencyForCurrentLocale(): string | null {
|
|
121
|
+
try {
|
|
122
|
+
const currencies = RNLocalize.getCurrencies();
|
|
123
|
+
if (Array.isArray(currencies) && currencies.length > 0) {
|
|
124
|
+
return currencies[0];
|
|
125
|
+
}
|
|
126
|
+
} catch {}
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function currencySymbolFor(languageTag: string, currencyCode: string | null): string | null {
|
|
131
|
+
if (!currencyCode || typeof Intl === 'undefined') {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
const parts = new Intl.NumberFormat(languageTag, {
|
|
136
|
+
style: 'currency',
|
|
137
|
+
currency: currencyCode,
|
|
138
|
+
}).formatToParts(0);
|
|
139
|
+
return parts.find((p) => p.type === 'currency')?.value ?? null;
|
|
140
|
+
} catch {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Best-effort start of the events turbomodule which forwards OS locale and timezone changes to DeviceEventEmitter
|
|
146
|
+
try {
|
|
147
|
+
const { TurboModuleRegistry } = require('@amazon-devices/keplerscript-turbomodule-api');
|
|
148
|
+
TurboModuleRegistry.getEnforcing('ExpoLocalizationAppTM');
|
|
149
|
+
} catch {
|
|
150
|
+
// without it the listeners never fire which was the previous behavior
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const DeviceEventEmitter = require('react-native').DeviceEventEmitter;
|
|
154
|
+
|
|
155
|
+
export function addLocaleListener(listener: (event) => void): Subscription {
|
|
156
|
+
return DeviceEventEmitter.addListener('onLocaleSettingsChanged', listener) as Subscription;
|
|
25
157
|
}
|
|
26
158
|
|
|
27
|
-
export function addCalendarListener(listener: () => void): Subscription {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
159
|
+
export function addCalendarListener(listener: (event) => void): Subscription {
|
|
160
|
+
// Calendar values include the timezone so both signals invalidate them
|
|
161
|
+
const localeSubscription = DeviceEventEmitter.addListener('onLocaleSettingsChanged', listener);
|
|
162
|
+
const timezoneSubscription = DeviceEventEmitter.addListener('onCalendarSettingsChanged', listener);
|
|
163
|
+
return {
|
|
164
|
+
remove: () => {
|
|
165
|
+
localeSubscription.remove();
|
|
166
|
+
timezoneSubscription.remove();
|
|
167
|
+
},
|
|
168
|
+
} as Subscription;
|
|
36
169
|
}
|
|
37
170
|
|
|
38
171
|
export function removeSubscription(subscription: Subscription) {
|
|
39
|
-
|
|
172
|
+
subscription.remove();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Wire exactly the surface the 2.x Vega module exposed which is locale and getLocales and getCalendars
|
|
176
|
+
function currentLocaleTag(): string {
|
|
177
|
+
return currentLanguageTags()[0] || 'en-US';
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function regionOfTag(tag: string): string | null {
|
|
181
|
+
const [, ...suffixes] = tag.split('-');
|
|
182
|
+
for (const suffix of suffixes) {
|
|
183
|
+
if (suffix.length === 2) {
|
|
184
|
+
return suffix.toUpperCase();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function currentTimezone(): string {
|
|
191
|
+
try {
|
|
192
|
+
const tz = RNLocalize.getTimeZone();
|
|
193
|
+
if (tz) {
|
|
194
|
+
return tz;
|
|
195
|
+
}
|
|
196
|
+
} catch {}
|
|
197
|
+
if (typeof Intl !== 'undefined') {
|
|
198
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone || 'Etc/UTC';
|
|
199
|
+
}
|
|
200
|
+
return 'Etc/UTC';
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function usesMetric(): boolean {
|
|
204
|
+
try {
|
|
205
|
+
return RNLocalize.usesMetricSystem();
|
|
206
|
+
} catch {}
|
|
207
|
+
const region = regionOfTag(currentLocaleTag());
|
|
208
|
+
return region == null || !['US', 'LR', 'MM'].includes(region);
|
|
40
209
|
}
|
|
41
210
|
|
|
42
211
|
const module = {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
212
|
+
get locale(): string {
|
|
213
|
+
return currentLocaleTag();
|
|
214
|
+
},
|
|
215
|
+
|
|
216
|
+
getLocales(): Locale[] {
|
|
217
|
+
const userCurrency = currencyForCurrentLocale();
|
|
218
|
+
return currentLanguageTags().map((languageTag) => {
|
|
219
|
+
const locale =
|
|
220
|
+
typeof Intl !== 'undefined'
|
|
221
|
+
? (new Intl.Locale(languageTag) as unknown as ExtendedIntlLocale)
|
|
222
|
+
: ({ region: undefined, textInfo: undefined, language: undefined } as unknown as ExtendedIntlLocale);
|
|
223
|
+
const region = locale.region ?? null;
|
|
224
|
+
const languageCode = locale.language || languageTag.split('-')[0] || 'en';
|
|
225
|
+
const textDirection: 'ltr' | 'rtl' =
|
|
226
|
+
locale.textInfo?.direction ?? (rtlDetect.isRtlLang(languageTag) ? 'rtl' : 'ltr');
|
|
227
|
+
const digitGroupingSeparator =
|
|
228
|
+
Array.from((10000).toLocaleString(languageTag)).filter((c) => c > '9' || c < '0')[0] || null;
|
|
229
|
+
const decimalSeparator = (1.1).toLocaleString(languageTag).substring(1, 2);
|
|
230
|
+
const currencyCode = (region && REGION_CURRENCIES[region]) || userCurrency;
|
|
231
|
+
|
|
232
|
+
return {
|
|
233
|
+
languageTag,
|
|
234
|
+
languageCode,
|
|
235
|
+
textDirection,
|
|
236
|
+
digitGroupingSeparator,
|
|
237
|
+
decimalSeparator,
|
|
238
|
+
measurementSystem: (region === 'GB'
|
|
239
|
+
? 'uk'
|
|
240
|
+
: region
|
|
241
|
+
? (['US', 'LR', 'MM'].includes(region) ? 'us' : 'metric')
|
|
242
|
+
: usesMetric()
|
|
243
|
+
? 'metric'
|
|
244
|
+
: 'us') as Locale['measurementSystem'],
|
|
245
|
+
currencyCode,
|
|
246
|
+
currencySymbol: currencySymbolFor(languageTag, currencyCode),
|
|
247
|
+
regionCode: region,
|
|
248
|
+
temperatureUnit: region
|
|
249
|
+
? ((USES_FAHRENHEIT.includes(region) ? 'fahrenheit' : 'celsius') as Locale['temperatureUnit'])
|
|
250
|
+
: null,
|
|
251
|
+
};
|
|
252
|
+
});
|
|
253
|
+
},
|
|
254
|
+
|
|
255
|
+
getCalendars(): Calendar[] {
|
|
256
|
+
const resolved =
|
|
257
|
+
typeof Intl !== 'undefined' ? Intl.DateTimeFormat().resolvedOptions() : undefined;
|
|
258
|
+
let calendar: CalendarIdentifier | null = (resolved?.calendar as CalendarIdentifier) ?? null;
|
|
259
|
+
try {
|
|
260
|
+
calendar = (RNLocalize.getCalendar() as CalendarIdentifier) ?? calendar;
|
|
261
|
+
} catch {}
|
|
262
|
+
let uses24hourClock: boolean | null = null;
|
|
263
|
+
try {
|
|
264
|
+
uses24hourClock = RNLocalize.uses24HourClock();
|
|
265
|
+
} catch {
|
|
266
|
+
const hourCycle = (resolved as { hourCycle?: string } | undefined)?.hourCycle;
|
|
267
|
+
uses24hourClock = hourCycle ? hourCycle.startsWith('h2') : null;
|
|
268
|
+
}
|
|
269
|
+
// weekInfo converted from ISO numbering when the engine has it and the CLDR region table otherwise
|
|
270
|
+
let firstWeekday: number | null = null;
|
|
271
|
+
if (typeof Intl !== 'undefined') {
|
|
272
|
+
try {
|
|
273
|
+
const locale = new Intl.Locale(currentLocaleTag()) as ExtendedIntlLocale;
|
|
274
|
+
const isoFirstDay = locale.weekInfo?.firstDay;
|
|
275
|
+
firstWeekday = isoFirstDay != null ? (isoFirstDay % 7) + 1 : null;
|
|
276
|
+
} catch {}
|
|
277
|
+
}
|
|
278
|
+
if (firstWeekday == null) {
|
|
279
|
+
firstWeekday = firstWeekdayForRegion(regionOfTag(currentLocaleTag()));
|
|
280
|
+
}
|
|
281
|
+
return [
|
|
282
|
+
{
|
|
283
|
+
calendar,
|
|
284
|
+
timeZone: currentTimezone(),
|
|
285
|
+
uses24hourClock,
|
|
286
|
+
firstWeekday,
|
|
287
|
+
},
|
|
288
|
+
];
|
|
289
|
+
},
|
|
46
290
|
};
|
|
47
291
|
|
|
48
292
|
export default module;
|
|
@@ -1,214 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
-
*
|
|
4
|
-
* PROPRIETARY/CONFIDENTIAL
|
|
5
|
-
*
|
|
6
|
-
* Use is subject to license terms.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
#include "ExpoLocalization.h"
|
|
10
|
-
|
|
11
|
-
#include <Kepler/turbomodule/NativeObject.h>
|
|
12
|
-
#include <Kepler/turbomodule/TMLog.h>
|
|
13
|
-
#include <apmf/fn.h>
|
|
14
|
-
#include <apmf/iface/com/amazon/kepler/device_config/DeviceConfigNotFoundError.h>
|
|
15
|
-
#include <apmf/iface/com/amazon/kepler/device_config/DeviceConfigResult.h>
|
|
16
|
-
#include <apmf/iface/com/amazon/kepler/device_config/IDeviceConfig.h>
|
|
17
|
-
#include <apmf/iface/com/amazon/kepler/device_config/IDeviceConfigModule.h>
|
|
18
|
-
#include <apmf/iface/com/amazon/kepler/env/IEnvModule.h>
|
|
19
|
-
#include <apmf/iface/com/amazon/kepler/env/ILocaleConfig.h>
|
|
20
|
-
#include <apmf/iface/com/amazon/kepler/env/IResolvedLocalesListener.h>
|
|
21
|
-
#include <apmf/iface/com/amazon/kepler/experimental_mr33/messaging/IKeplerMessagingModule.h>
|
|
22
|
-
#include <apmf/iface/com/amazon/kepler/experimental_mr33/messaging/IMessageFilterBuilder.h>
|
|
23
|
-
#include <apmf/iface/com/amazon/kepler/experimental_mr33/messaging/IMessageListener.h>
|
|
24
|
-
#include <apmf/apmf_base.h>
|
|
25
|
-
#include <unicode/currunit.h>
|
|
26
|
-
#include <unicode/dcfmtsym.h>
|
|
27
|
-
#include <unicode/dtptngen.h>
|
|
28
|
-
#include <unicode/numfmt.h>
|
|
29
|
-
#include <unicode/ulocdata.h>
|
|
30
|
-
|
|
31
|
-
#include <memory>
|
|
32
|
-
#include <string>
|
|
33
|
-
#include <fmt/core.h>
|
|
34
|
-
|
|
35
|
-
namespace env = apmf::iface::com::amazon::kepler::env;
|
|
36
|
-
namespace devConf = apmf::iface::com::amazon::kepler::device_config;
|
|
37
|
-
namespace messaging = apmf::iface::com::amazon::kepler::experimental_mr33::messaging;
|
|
38
|
-
|
|
39
|
-
static const std::string g_timeZoneConfig{"com.amazon.devconf/system/configuration/time-zone"};
|
|
40
|
-
|
|
41
|
-
apmf::Ptr<devConf::IDeviceConfig> getDeviceConfig() {
|
|
42
|
-
return apmf::detail::GetProcessObject()
|
|
43
|
-
->getComponent("/com.amazon.kepler.device_config")
|
|
44
|
-
.TryQueryInterface<devConf::IDeviceConfigModule>()
|
|
45
|
-
->getDeviceConfig();
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
std::string toUTF8String(const icu::UnicodeString& from) {
|
|
49
|
-
std::string result;
|
|
50
|
-
from.toUTF8String(result);
|
|
51
|
-
return result;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
apmf::Ptr<env::IEnvModule> getEnvModule() {
|
|
55
|
-
return apmf::detail::GetProcessObject()
|
|
56
|
-
->getComponent("/com.amazon.kepler.env")
|
|
57
|
-
.TryQueryInterface<env::IEnvModule>();
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
ExpoLocalization::ExpoLocalization() : KeplerTurboModule("ExpoLocalization") {
|
|
61
|
-
TMDEBUG(fmt::format("ExpoLocalization constructor {}", name()));
|
|
62
|
-
|
|
63
|
-
getEnvModule()->getLocaleConfig()->addResolvedLocalesListener(nullptr, apmf::Fn1([this](auto locales) {
|
|
64
|
-
this->emit("onLocaleSettingsChanged", nullptr);
|
|
65
|
-
}));
|
|
66
|
-
|
|
67
|
-
// Subscribe to timezone config changes via MR33 messaging
|
|
68
|
-
auto proc = apmf::detail::GetProcessObject();
|
|
69
|
-
auto messagingModule = proc->getComponent("/com.amazon.kepler.experimental_mr33.messaging")
|
|
70
|
-
.TryQueryInterface<messaging::IKeplerMessagingModule>();
|
|
71
|
-
if (messagingModule) {
|
|
72
|
-
struct TZListener : apmf::ApmfBase<TZListener, messaging::IMessageListener> {
|
|
73
|
-
ExpoLocalization* owner;
|
|
74
|
-
explicit TZListener(ExpoLocalization* o) : owner(o) {}
|
|
75
|
-
void onMessage(apmf::View<messaging::IMessage> /*message*/) {
|
|
76
|
-
owner->emit("onCalendarSettingsChanged", nullptr);
|
|
77
|
-
}
|
|
78
|
-
void onError(apmf::View<apmf::IError> /*error*/) {}
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
auto channel = messagingModule->makeMessageChannel(
|
|
82
|
-
"ExpoLocalization_TZ",
|
|
83
|
-
apmf::detail::Ptr<TZListener>::Make(this));
|
|
84
|
-
|
|
85
|
-
apmf::detail::Array<apmf::detail::Ptr<messaging::IMessageFilter>> filters;
|
|
86
|
-
filters.push_back(
|
|
87
|
-
messagingModule->makeMessageFilterBuilder()
|
|
88
|
-
->baseUri("broadcast://*/com.amazon.devconf/system/configuration/time-zone")
|
|
89
|
-
->build());
|
|
90
|
-
channel->registerMessageFilter(filters);
|
|
91
|
-
m_timezoneChannel = std::move(channel);
|
|
92
|
-
}
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
std::string ExpoLocalization::getLocale() {
|
|
96
|
-
auto locales = getEnvModule()->getLocaleConfig()->getResolvedLocales(nullptr);
|
|
97
|
-
if (locales.empty()) {
|
|
98
|
-
return "";
|
|
99
|
-
}
|
|
100
|
-
return locales[0].identifier;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
tbm::NativeJSArray ExpoLocalization::getLocales() {
|
|
104
|
-
auto locales = getEnvModule()->getLocaleConfig()->getResolvedLocales(nullptr);
|
|
105
|
-
tbm::NativeJSArray resultArray(locales.size());
|
|
106
|
-
|
|
107
|
-
for (std::size_t i = 0; i < locales.size(); i++) {
|
|
108
|
-
tbm::NativeJSObject result;
|
|
109
|
-
const std::string& identifier = locales[i].identifier;
|
|
110
|
-
|
|
111
|
-
const icu::Locale loc(identifier.c_str());
|
|
112
|
-
result["languageCode"] = std::string(loc.getLanguage());
|
|
113
|
-
result["languageTag"] = identifier;
|
|
114
|
-
|
|
115
|
-
UErrorCode status = U_ZERO_ERROR;
|
|
116
|
-
std::unique_ptr<icu::NumberFormat> nf(icu::NumberFormat::createInstance(loc, status));
|
|
117
|
-
if (U_SUCCESS(status) != 0) {
|
|
118
|
-
result["currencyCode"] = toUTF8String(nf->getCurrency());
|
|
119
|
-
} else {
|
|
120
|
-
result["currencyCode"] = nullptr;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
status = U_ZERO_ERROR;
|
|
124
|
-
const icu::DecimalFormatSymbols dfs(loc, status);
|
|
125
|
-
if (U_SUCCESS(status) != 0) {
|
|
126
|
-
result["currencySymbol"] = toUTF8String(dfs.getSymbol(icu::DecimalFormatSymbols::kCurrencySymbol));
|
|
127
|
-
result["decimalSeparator"] =
|
|
128
|
-
toUTF8String(dfs.getSymbol(icu::DecimalFormatSymbols::kDecimalSeparatorSymbol));
|
|
129
|
-
result["digitGroupingSeparator"] =
|
|
130
|
-
toUTF8String(dfs.getSymbol(icu::DecimalFormatSymbols::kGroupingSeparatorSymbol));
|
|
131
|
-
} else {
|
|
132
|
-
result["currencySymbol"] = nullptr;
|
|
133
|
-
result["decimalSeparator"] = nullptr;
|
|
134
|
-
result["digitGroupingSeparator"] = nullptr;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
status = U_ZERO_ERROR;
|
|
138
|
-
auto measurementSystem = ulocdata_getMeasurementSystem(loc.getLanguage(), &status);
|
|
139
|
-
if (measurementSystem == UMS_SI) {
|
|
140
|
-
result["measurementSystem"] = "metric";
|
|
141
|
-
} else if (measurementSystem == UMS_US) {
|
|
142
|
-
result["measurementSystem"] = "imperial";
|
|
143
|
-
} else if (measurementSystem == UMS_UK) {
|
|
144
|
-
result["measurementSystem"] = "uk";
|
|
145
|
-
} else {
|
|
146
|
-
result["measurementSystem"] = nullptr;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
result["textDirection"] = (loc.isRightToLeft() != 0) ? "rtl" : "ltr";
|
|
150
|
-
|
|
151
|
-
result["regionCode"] = nullptr;
|
|
152
|
-
|
|
153
|
-
resultArray[i] = result;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
return resultArray;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
tbm::NativeJSArray ExpoLocalization::getCalendars() {
|
|
160
|
-
const auto& locales = getEnvModule()->getLocaleConfig()->getResolvedLocales(nullptr);
|
|
161
|
-
tbm::NativeJSArray calendarArray(locales.size());
|
|
162
|
-
|
|
163
|
-
for (std::size_t i = 0; i < locales.size(); i++) {
|
|
164
|
-
tbm::NativeJSObject calendar;
|
|
165
|
-
const std::string& identifier = locales[i].identifier;
|
|
166
|
-
|
|
167
|
-
const icu::Locale loc(identifier.c_str());
|
|
168
|
-
|
|
169
|
-
UErrorCode status = U_ZERO_ERROR;
|
|
170
|
-
std::unique_ptr<icu::Calendar> c(icu::Calendar::createInstance(loc, status));
|
|
171
|
-
if (U_SUCCESS(status) != 0) {
|
|
172
|
-
calendar["calendar"] = std::string(c->getType());
|
|
173
|
-
} else {
|
|
174
|
-
calendar["calendar"] = nullptr;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
status = U_ZERO_ERROR;
|
|
178
|
-
const int firstWeekday = static_cast<int>(c->getFirstDayOfWeek(status));
|
|
179
|
-
if (U_SUCCESS(status) != 0) {
|
|
180
|
-
calendar["firstWeekday"] = firstWeekday;
|
|
181
|
-
} else {
|
|
182
|
-
calendar["firstWeekday"] = nullptr;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
calendar["uses24hourClock"] = nullptr;
|
|
186
|
-
status = U_ZERO_ERROR;
|
|
187
|
-
std::unique_ptr<icu::DateTimePatternGenerator> dtp(icu::DateTimePatternGenerator::createInstance(loc, status));
|
|
188
|
-
if (U_SUCCESS(status) != 0) {
|
|
189
|
-
status = U_ZERO_ERROR;
|
|
190
|
-
auto cycle = dtp->getDefaultHourCycle(status);
|
|
191
|
-
if (U_SUCCESS(status) != 0) {
|
|
192
|
-
calendar["uses24hourClock"] = (cycle == UDAT_HOUR_CYCLE_23 || cycle == UDAT_HOUR_CYCLE_24);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
apmf::Ptr<devConf::IDeviceConfig> component = getDeviceConfig();
|
|
197
|
-
try {
|
|
198
|
-
calendar["timezone"] = component->get(g_timeZoneConfig);
|
|
199
|
-
} catch (const apmf::BaseError& e) {
|
|
200
|
-
TMERROR(fmt::format("Error while processing time zone data: {}", e.what()));
|
|
201
|
-
calendar["timezone"] = nullptr;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
calendarArray[i] = calendar;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
return calendarArray;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
void ExpoLocalization::aggregateMethods(tbm::MethodAggregator<KeplerTurboModule>& methodAggregator) const noexcept {
|
|
211
|
-
methodAggregator.addMethod("getLocale", 0, &ExpoLocalization::getLocale);
|
|
212
|
-
methodAggregator.addMethod("getLocales", 0, &ExpoLocalization::getLocales);
|
|
213
|
-
methodAggregator.addMethod("getCalendars", 0, &ExpoLocalization::getCalendars);
|
|
214
|
-
}
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
-
*
|
|
4
|
-
* PROPRIETARY/CONFIDENTIAL
|
|
5
|
-
*
|
|
6
|
-
* Use is subject to license terms.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
#include "../ExpoLocalization.h"
|
|
10
|
-
|
|
11
|
-
#include <gmock/gmock-actions.h>
|
|
12
|
-
#include <gmock/gmock.h>
|
|
13
|
-
#include <gtest/gtest.h>
|
|
14
|
-
|
|
15
|
-
#include "catch.hpp"
|
|
16
|
-
|
|
17
|
-
namespace {
|
|
18
|
-
template<typename T>
|
|
19
|
-
void assertKey(const volta::react::NativeJSObject& object, const std::string& key, const T& expectedValue) {
|
|
20
|
-
EXPECT_TRUE(object.find(key) != std::end(object)) << "Expected key: " << key;
|
|
21
|
-
EXPECT_EQ(std::get<T>(object.at(key)), expectedValue);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
void assertKey(const volta::react::NativeJSObject& object, const std::string& key) {
|
|
25
|
-
EXPECT_TRUE(object.find(key) != std::end(object)) << "Expected key: " << key;
|
|
26
|
-
}
|
|
27
|
-
} // namespace
|
|
28
|
-
|
|
29
|
-
namespace volta::react {
|
|
30
|
-
|
|
31
|
-
TEST_CASE("ExpoLocalization") {
|
|
32
|
-
SECTION("getLocales") {
|
|
33
|
-
ExpoLocalization module;
|
|
34
|
-
|
|
35
|
-
auto locales = module.getLocales();
|
|
36
|
-
EXPECT_EQ(locales.size(), 1);
|
|
37
|
-
|
|
38
|
-
auto locale = std::get<NativeJSObject>(locales[0]);
|
|
39
|
-
|
|
40
|
-
assertKey(locale, "languageCode");
|
|
41
|
-
assertKey(locale, "languageTag");
|
|
42
|
-
|
|
43
|
-
assertKey(locale, "currencyCode");
|
|
44
|
-
assertKey(locale, "currencySymbol");
|
|
45
|
-
assertKey(locale, "digitGroupingSeparator");
|
|
46
|
-
assertKey(locale, "measurementSystem");
|
|
47
|
-
|
|
48
|
-
assertKey<std::string>(locale, "textDirection", "ltr");
|
|
49
|
-
assertKey<std::nullptr_t>(locale, "regionCode", nullptr);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
SECTION("getCalendars") {
|
|
53
|
-
ExpoLocalization module;
|
|
54
|
-
|
|
55
|
-
auto calendars = module.getCalendars();
|
|
56
|
-
EXPECT_EQ(calendars.size(), 1);
|
|
57
|
-
|
|
58
|
-
auto calendar = std::get<NativeJSObject>(calendars[0]);
|
|
59
|
-
|
|
60
|
-
assertKey<std::string>(calendar, "calendar", "gregorian");
|
|
61
|
-
assertKey(calendar, "firstWeekday");
|
|
62
|
-
assertKey(calendar, "uses24hourClock");
|
|
63
|
-
assertKey<std::nullptr_t>(calendar, "timezone", nullptr);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
SECTION("aggregateMethods") {
|
|
67
|
-
ExpoLocalization module;
|
|
68
|
-
|
|
69
|
-
std::weak_ptr<facebook::react::Instance> instance;
|
|
70
|
-
|
|
71
|
-
module.triggerMethodAggregation(*module.jsQueue_, instance);
|
|
72
|
-
|
|
73
|
-
auto end = module.methodContainerMap_.end();
|
|
74
|
-
REQUIRE(module.methodContainerMap_.find("getLocales") != end);
|
|
75
|
-
REQUIRE(module.methodContainerMap_.find("getCalendars") != end);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
} // namespace volta::react
|