Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | 76x 76x 76x 76x 76x 76x 76x 76x 32x 76x 76x | import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
import * as Localization from 'expo-localization';
import * as SecureStore from 'expo-secure-store';
import i18n, { isSupportedLocale, resolveLocale, type Locale } from '@/i18n';
const LOCALE_STORAGE_KEY = 'preferred_locale';
interface LocaleContextValue {
locale: Locale;
isHydrating: boolean;
setLocale: (next: Locale) => Promise<void>;
}
const LocaleContext = createContext<LocaleContextValue | null>(null);
/**
* Singleton mirror of the active locale so non-React code (e.g. the api layer)
* can read it for the Accept-Language header without a provider.
*/
let currentLocale: Locale = 'en';
export function getCurrentLocale(): Locale {
return currentLocale;
}
export function LocaleProvider({ children }: { children: React.ReactNode }) {
const [locale, setLocaleState] = useState<Locale>('en');
const [isHydrating, setIsHydrating] = useState(true);
// Load persisted locale on mount, falling back to device locale.
useEffect(() => {
let cancelled = false;
(async () => {
try {
const stored = await SecureStore.getItemAsync(LOCALE_STORAGE_KEY);
const initial = isSupportedLocale(stored)
? stored
: resolveLocale(Localization.getLocales()[0]?.languageTag);
Iif (cancelled) return;
currentLocale = initial;
await i18n.changeLanguage(initial);
setLocaleState(initial);
} finally {
Iif (!cancelled) setIsHydrating(false);
}
})();
return () => {
cancelled = true;
};
}, []);
const setLocale = useCallback(async (next: Locale) => {
currentLocale = next;
await i18n.changeLanguage(next);
setLocaleState(next);
try {
await SecureStore.setItemAsync(LOCALE_STORAGE_KEY, next);
} catch {
// Persistence is best-effort; the in-memory locale is already updated.
}
}, []);
const value = useMemo<LocaleContextValue>(
() => ({ locale, isHydrating, setLocale }),
[locale, isHydrating, setLocale],
);
return <LocaleContext.Provider value={value}>{children}</LocaleContext.Provider>;
}
export function useLocale(): LocaleContextValue {
const ctx = useContext(LocaleContext);
Iif (!ctx) {
throw new Error('useLocale must be used within a LocaleProvider');
}
return ctx;
}
|