Khởi tạo ban đầu
This commit is contained in:
119
hooks/use-i18n.ts
Normal file
119
hooks/use-i18n.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
import i18n, { LOCALE_STORAGE_KEY } from "@/config/localization/i18n";
|
||||
import { getStorageItem, setStorageItem } from "@/utils/storage";
|
||||
import { getLocales } from "expo-localization";
|
||||
import {
|
||||
PropsWithChildren,
|
||||
createContext,
|
||||
createElement,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from "react";
|
||||
|
||||
type SupportedLocale = "en" | "vi";
|
||||
|
||||
type I18nContextValue = {
|
||||
t: typeof i18n.t;
|
||||
locale: SupportedLocale;
|
||||
setLocale: (locale: SupportedLocale) => Promise<void>;
|
||||
isLoaded: boolean;
|
||||
};
|
||||
|
||||
const I18nContext = createContext<I18nContextValue | undefined>(undefined);
|
||||
|
||||
const SUPPORTED_LOCALES: SupportedLocale[] = ["en", "vi"];
|
||||
|
||||
const resolveSupportedLocale = (
|
||||
locale: string | null | undefined
|
||||
): SupportedLocale => {
|
||||
if (!locale) {
|
||||
return "en";
|
||||
}
|
||||
|
||||
const normalized = locale.split("-")[0]?.toLowerCase() as SupportedLocale;
|
||||
if (normalized && SUPPORTED_LOCALES.includes(normalized)) {
|
||||
return normalized;
|
||||
}
|
||||
|
||||
return "en";
|
||||
};
|
||||
|
||||
export const I18nProvider = ({ children }: PropsWithChildren<unknown>) => {
|
||||
const [locale, setLocaleState] = useState<SupportedLocale>(
|
||||
resolveSupportedLocale(i18n.locale)
|
||||
);
|
||||
const [isLoaded, setIsLoaded] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const loadLocale = async () => {
|
||||
try {
|
||||
const savedLocale = await getStorageItem(LOCALE_STORAGE_KEY);
|
||||
const deviceLocale = getLocales()[0]?.languageCode;
|
||||
const localeToUse = resolveSupportedLocale(savedLocale ?? deviceLocale);
|
||||
|
||||
if (localeToUse !== i18n.locale) {
|
||||
i18n.locale = localeToUse;
|
||||
}
|
||||
setLocaleState(localeToUse);
|
||||
} catch (error) {
|
||||
console.error("Error loading locale preference:", error);
|
||||
} finally {
|
||||
setIsLoaded(true);
|
||||
}
|
||||
};
|
||||
|
||||
void loadLocale();
|
||||
}, []);
|
||||
|
||||
const updateLocale = useCallback((nextLocale: SupportedLocale) => {
|
||||
if (i18n.locale !== nextLocale) {
|
||||
i18n.locale = nextLocale;
|
||||
}
|
||||
setLocaleState(nextLocale);
|
||||
}, []);
|
||||
|
||||
const setLocale = useCallback(
|
||||
async (nextLocale: SupportedLocale) => {
|
||||
if (!SUPPORTED_LOCALES.includes(nextLocale)) {
|
||||
console.warn(`Unsupported locale: ${nextLocale}`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
updateLocale(nextLocale);
|
||||
await setStorageItem(LOCALE_STORAGE_KEY, nextLocale);
|
||||
} catch (error) {
|
||||
console.error("Error setting locale:", error);
|
||||
}
|
||||
},
|
||||
[updateLocale]
|
||||
);
|
||||
|
||||
const translate = useCallback(
|
||||
(...args: Parameters<typeof i18n.t>) => i18n.t(...args),
|
||||
[locale]
|
||||
);
|
||||
|
||||
const value = useMemo<I18nContextValue>(
|
||||
() => ({
|
||||
t: translate,
|
||||
locale,
|
||||
setLocale,
|
||||
isLoaded,
|
||||
}),
|
||||
[locale, setLocale, translate, isLoaded]
|
||||
);
|
||||
|
||||
return createElement(I18nContext.Provider, { value }, children);
|
||||
};
|
||||
|
||||
export const useI18n = () => {
|
||||
const context = useContext(I18nContext);
|
||||
if (!context) {
|
||||
throw new Error("useI18n must be used within an I18nProvider");
|
||||
}
|
||||
|
||||
return context;
|
||||
};
|
||||
Reference in New Issue
Block a user