feat: 다국어 화

This commit is contained in:
2026-02-14 03:56:03 +09:00
parent efdec596b2
commit 166d04384f
17 changed files with 691 additions and 160 deletions

View 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);
};

View 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);