feat: 다국어 화
This commit is contained in:
42
src/features/i18n/model/resolveInitialLocale.ts
Normal file
42
src/features/i18n/model/resolveInitialLocale.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import {
|
||||
DEFAULT_LOCALE,
|
||||
Locale,
|
||||
MANUAL_LOCALE_STORAGE_KEY,
|
||||
SUPPORTED_LOCALES,
|
||||
} from "@/shared/config/i18n";
|
||||
|
||||
const normalizeLocale = (raw: string | null | undefined): Locale | null => {
|
||||
if (!raw) return null;
|
||||
const base = raw.trim().toLowerCase().split("-")[0];
|
||||
|
||||
return SUPPORTED_LOCALES.includes(base as Locale) ? (base as Locale) : null;
|
||||
};
|
||||
|
||||
export const getManualLocale = (): Locale | null => {
|
||||
if (typeof window === "undefined") return null;
|
||||
return normalizeLocale(localStorage.getItem(MANUAL_LOCALE_STORAGE_KEY));
|
||||
};
|
||||
|
||||
const getBrowserLocale = (): Locale | null => {
|
||||
if (typeof navigator === "undefined") return null;
|
||||
const candidates = [...(navigator.languages ?? []), navigator.language];
|
||||
|
||||
for (const candidate of candidates) {
|
||||
const locale = normalizeLocale(candidate);
|
||||
if (locale) return locale;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export const resolveInitialLocale = (): Locale => {
|
||||
const manualLocale = getManualLocale();
|
||||
if (manualLocale) return manualLocale;
|
||||
|
||||
return getBrowserLocale() ?? DEFAULT_LOCALE;
|
||||
};
|
||||
|
||||
export const saveManualLocale = (locale: Locale) => {
|
||||
if (typeof window === "undefined") return;
|
||||
localStorage.setItem(MANUAL_LOCALE_STORAGE_KEY, locale);
|
||||
};
|
||||
45
src/features/i18n/model/useI18n.tsx
Normal file
45
src/features/i18n/model/useI18n.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
"use client";
|
||||
|
||||
import { ReactNode, createContext, useContext } from "react";
|
||||
|
||||
import {
|
||||
DEFAULT_LOCALE,
|
||||
I18nKey,
|
||||
Locale,
|
||||
TranslationParams,
|
||||
translateText,
|
||||
} from "@/shared/config/i18n";
|
||||
|
||||
type I18nContextValue = {
|
||||
locale: Locale;
|
||||
setLocale: (locale: Locale) => void;
|
||||
t: (key: I18nKey | string, params?: TranslationParams, fallback?: string) => string;
|
||||
};
|
||||
|
||||
const I18nContext = createContext<I18nContextValue>({
|
||||
locale: DEFAULT_LOCALE,
|
||||
setLocale: () => {},
|
||||
t: (key, params, fallback) =>
|
||||
translateText(DEFAULT_LOCALE, key, params, fallback),
|
||||
});
|
||||
|
||||
export function I18nProvider({
|
||||
children,
|
||||
locale,
|
||||
setLocale,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
locale: Locale;
|
||||
setLocale: (locale: Locale) => void;
|
||||
}) {
|
||||
const t = (key: I18nKey | string, params?: TranslationParams, fallback?: string) =>
|
||||
translateText(locale, key, params, fallback);
|
||||
|
||||
return (
|
||||
<I18nContext.Provider value={{ locale, setLocale, t }}>
|
||||
{children}
|
||||
</I18nContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export const useI18n = () => useContext(I18nContext);
|
||||
Reference in New Issue
Block a user