mirror of
https://github.com/bulwarkmail/native.git
synced 2026-10-31 08:06:36 +00:00
fix: keep device sync free of expo by registering device locales from i18n #75
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
import { I18nManager } from 'react-native';
|
||||
import { getLocales } from 'expo-localization';
|
||||
import { formatMessage, type MessageParams } from './format';
|
||||
import { setDeviceLocales } from '../lib/intl-locale';
|
||||
|
||||
// English is the fallback for every lookup, so it is loaded eagerly. The other
|
||||
// catalogs are loaded on first use; see CATALOG_LOADERS below.
|
||||
@@ -212,6 +213,20 @@ export function detectDeviceLocale(): LocaleCode {
|
||||
return 'en';
|
||||
}
|
||||
|
||||
/**
|
||||
* Hand the device locales to the date formatters (lib/intl-locale), so an
|
||||
* en-AU phone formats dates as en-AU while reading the English catalog.
|
||||
*/
|
||||
export function registerDeviceLocales(): void {
|
||||
try {
|
||||
setDeviceLocales(getLocales().map((l) => ({ appLocale: resolveLocaleTag(l), languageTag: l.languageTag })));
|
||||
} catch {
|
||||
setDeviceLocales([]);
|
||||
}
|
||||
}
|
||||
|
||||
registerDeviceLocales();
|
||||
|
||||
function lookup(dict: Dictionary, key: string): string | undefined {
|
||||
let current: unknown = dict;
|
||||
for (const part of key.split('.')) {
|
||||
|
||||
@@ -7,13 +7,14 @@ vi.mock('expo-localization', () => ({
|
||||
getCalendars: () => [{ timeZone: 'UTC', firstWeekday: 1 }],
|
||||
}));
|
||||
|
||||
import { intlLocale, resetIntlLocaleCache } from '../intl-locale';
|
||||
import { intlLocale } from '../intl-locale';
|
||||
import { registerDeviceLocales } from '../../i18n';
|
||||
import { formatCalendarDate, formatListDate } from '../date-format';
|
||||
import { formatHeaderDate } from '../email-date';
|
||||
|
||||
function setDevice(...tags: string[]) {
|
||||
device.locales = tags.map((t) => ({ languageTag: t, languageCode: t.split('-')[0] }));
|
||||
resetIntlLocaleCache();
|
||||
registerDeviceLocales();
|
||||
}
|
||||
|
||||
describe('intlLocale', () => {
|
||||
|
||||
+18
-15
@@ -3,11 +3,25 @@
|
||||
// order: en-AU writes 15/10/2026, en-US 10/15/2026. intlLocale() hands the
|
||||
// date formatters the device's full tag when its language matches the app
|
||||
// language, so an Australian or British phone gets its own dates (#75).
|
||||
import { getLocales } from 'expo-localization';
|
||||
import { resolveLocaleTag } from '../i18n';
|
||||
//
|
||||
// This module stays free of react-native and expo because the device sync
|
||||
// engine reaches it through quote-header (see device-sync purity test). The
|
||||
// i18n module reads the device locales and registers them here on load.
|
||||
|
||||
export interface DeviceLocale {
|
||||
/** The app language this device locale selects, from resolveLocaleTag. */
|
||||
appLocale: string | null;
|
||||
languageTag: string;
|
||||
}
|
||||
|
||||
let deviceLocales: DeviceLocale[] = [];
|
||||
const cache = new Map<string, string>();
|
||||
|
||||
export function setDeviceLocales(locales: DeviceLocale[]): void {
|
||||
deviceLocales = locales;
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
// Android 14 appends regional preferences such as `-u-fw-mon-mu-celsius`.
|
||||
// The app has its own time-format and week-start settings, so keep only the
|
||||
// language, script and region.
|
||||
@@ -25,14 +39,8 @@ function isUsable(tag: string): boolean {
|
||||
}
|
||||
|
||||
function deviceTagFor(locale: string): string | null {
|
||||
let devices: ReturnType<typeof getLocales>;
|
||||
try {
|
||||
devices = getLocales();
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
for (const l of devices) {
|
||||
if (!l.languageTag || resolveLocaleTag(l) !== locale) continue;
|
||||
for (const l of deviceLocales) {
|
||||
if (!l.languageTag || l.appLocale !== locale) continue;
|
||||
const tag = baseTag(l.languageTag);
|
||||
// A tag without a region adds nothing over the language subtag.
|
||||
if (tag.includes('-') && isUsable(tag)) return tag;
|
||||
@@ -52,8 +60,3 @@ export function intlLocale(locale: string | null | undefined): string {
|
||||
cache.set(lang, resolved);
|
||||
return resolved;
|
||||
}
|
||||
|
||||
/** Test hook: forget the resolved tags so a changed device locale is seen. */
|
||||
export function resetIntlLocaleCache(): void {
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user