xref: /expo/apps/test-suite/tests/Localization.js (revision bb5069cd)
1import * as Localization from 'expo-localization';
2import i18n from 'i18n-js';
3import { Platform } from 'react-native';
4
5const en = {
6  good: 'good',
7  morning: 'morning',
8};
9
10const fr = {
11  good: 'bien',
12  morning: 'matin',
13};
14
15const pl = {
16  good: 'dobry',
17  morning: 'rano',
18};
19
20export const name = 'Localization';
21
22export function test(t) {
23  function validateString(result) {
24    t.expect(result).toBeDefined();
25    t.expect(typeof result).toBe('string');
26    t.expect(result.length > 0).toBe(true);
27  }
28
29  function validateStringArray(result) {
30    t.expect(result).toBeDefined();
31    t.expect(Array.isArray(result)).toBe(true);
32  }
33
34  t.describe(`Localization methods`, () => {
35    t.it('expect to getLocales return preferred locales', () => {
36      const locales = Localization.getLocales();
37      t.expect(locales.length).toBeGreaterThanOrEqual(1);
38      const {
39        languageTag,
40        languageCode,
41        regionCode,
42        currencyCode,
43        currencySymbol,
44        decimalSeparator,
45        digitGroupingSeparator,
46        textDirection,
47        measurementSystem,
48      } = locales[0];
49      validateString(languageTag);
50      validateString(languageCode);
51      // following properties can be nullish if the locale does not provide/override them
52      t.expect(regionCode).toBeDefined();
53      t.expect(currencyCode).toBeDefined();
54      t.expect(currencySymbol).toBeDefined();
55      t.expect(decimalSeparator).toBeDefined();
56      t.expect(digitGroupingSeparator).toBeDefined();
57      t.expect(textDirection).toBeDefined();
58      if (textDirection) {
59        t.expect(['rtl', 'ltr'].includes(textDirection)).toBe(true);
60      }
61      t.expect(measurementSystem).toBeDefined();
62      if (measurementSystem) {
63        t.expect(['metric', 'us', 'uk'].includes(measurementSystem)).toBe(true);
64      }
65    });
66
67    t.it('expect getCalendars to return at least a single calendar', () => {
68      const calendars = Localization.getCalendars();
69      t.expect(calendars.length).toBeGreaterThanOrEqual(1);
70      const { calendar, uses24hourClock, firstWeekday, timeZone } = calendars[0];
71      t.expect(calendar).toBeDefined();
72      t.expect(timeZone).toBeDefined();
73      // following properties can be nullish if the locale does not provide/override them
74      t.expect(uses24hourClock).toBeDefined();
75      if (uses24hourClock !== null) {
76        t.expect(typeof uses24hourClock).toBe('boolean');
77      }
78      t.expect(firstWeekday).toBeDefined();
79      if (firstWeekday !== null) {
80        t.expect(typeof firstWeekday).toBe('number');
81      }
82    });
83
84    t.it('expect async to return locale', async () => {
85      const {
86        currency,
87        decimalSeparator,
88        digitGroupingSeparator,
89        isoCurrencyCodes,
90        isMetric,
91        isRTL,
92        locale,
93        locales,
94        timezone,
95        region,
96      } = await Localization.getLocalizationAsync();
97
98      validateString(locale);
99      validateString(timezone);
100      if (Platform.OS !== 'web' || region) {
101        validateString(region);
102      }
103      validateStringArray(isoCurrencyCodes);
104      validateStringArray(locales);
105      t.expect(locales[0]).toBe(Localization.locale);
106      t.expect(typeof isRTL).toBe('boolean');
107      t.expect(typeof isMetric).toBe('boolean');
108      validateString(decimalSeparator);
109      validateString(digitGroupingSeparator);
110      if (Platform.OS !== 'web' || currency) {
111        validateString(currency);
112      }
113    });
114  });
115
116  t.describe(`Localization defines constants`, () => {
117    t.it('Gets the region', async () => {
118      const result = Localization.region;
119      if (Platform.OS !== 'web' || result) {
120        validateString(result);
121      }
122    });
123    t.it('Gets the locale', async () => {
124      validateString(Localization.locale);
125    });
126    t.it('Gets the preferred locales', async () => {
127      const result = Localization.locales;
128      validateStringArray(result);
129      t.expect(result.length > 0).toBe(true);
130      t.expect(result[0]).toBe(Localization.locale);
131    });
132    t.it('Gets ISO currency codes', async () => {
133      const result = Localization.isoCurrencyCodes;
134      validateStringArray(result);
135      result.map(validateString);
136    });
137    t.it('Gets the timezone', async () => {
138      const result = Localization.timezone;
139      if (result || Platform.OS !== 'web') {
140        validateString(Localization.timezone);
141      }
142    });
143    t.it('Gets the layout direction (ltr only)', async () => {
144      const result = Localization.isRTL;
145      t.expect(typeof result).toBe('boolean');
146      t.expect(result).toBe(false);
147    });
148    t.it('Gets the measurement system (metric)', async () => {
149      const result = Localization.isMetric;
150      t.expect(typeof result).toBe('boolean');
151    });
152    t.it('Gets the decimal separator', async () => {
153      validateString(Localization.decimalSeparator);
154    });
155    t.it('Gets the grouping separator', async () => {
156      const result = Localization.decimalSeparator;
157      t.expect(result).toBeDefined();
158      t.expect(typeof result).toBe('string');
159    });
160    if (Platform.OS !== 'web') {
161      t.it('Gets the currency', async () => {
162        validateString(Localization.currency);
163      });
164    }
165  });
166
167  t.describe(`Localization works with i18n-js`, () => {
168    i18n.locale = Localization.locale;
169    i18n.translations = { en, fr, pl };
170    i18n.missingTranslationPrefix = 'EE: ';
171    i18n.fallbacks = true;
172
173    t.it('expect language to match strings (en, pl, fr supported)', async () => {
174      const target = 'good';
175
176      i18n.locale = Localization.locale;
177
178      const expoPredictedLangTag = Localization.locale.split('-')[0];
179      const translation = i18n.translations[expoPredictedLangTag];
180
181      t.expect(translation[target]).toBe(i18n.t(target));
182    });
183  });
184}
185