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,87 @@
"use client";
import Link from "next/link";
import { ChangeEvent, useEffect, useState } from "react";
import {
DEFAULT_LOCALE,
I18nKey,
LOCALE_LABELS,
Locale,
SUPPORTED_LOCALES,
translateText,
} from "@/shared/config/i18n";
import {
resolveInitialLocale,
saveManualLocale,
} from "@/features/i18n/model/resolveInitialLocale";
import { I18nProvider } from "@/features/i18n/model/useI18n";
export function I18nLayoutShell({
children,
}: {
children: React.ReactNode;
}) {
const [locale, setLocale] = useState<Locale>(DEFAULT_LOCALE);
useEffect(() => {
const initialLocale = resolveInitialLocale();
setLocale(initialLocale);
document.documentElement.lang = initialLocale;
}, []);
const handleSetLocale = (nextLocale: Locale) => {
if (!SUPPORTED_LOCALES.includes(nextLocale)) return;
setLocale(nextLocale);
saveManualLocale(nextLocale);
document.documentElement.lang = nextLocale;
};
const handleLocaleChange = (event: ChangeEvent<HTMLSelectElement>) => {
const nextLocale = event.target.value as Locale;
handleSetLocale(nextLocale);
};
const t = (key: I18nKey) => translateText(locale, key);
return (
<I18nProvider locale={locale} setLocale={handleSetLocale}>
<div className="relative z-10 mx-auto flex min-h-screen max-w-6xl flex-col">
<header className="flex items-center justify-between px-6 py-4">
<Link
href="/"
className="z-50 text-lg font-bold tracking-wider text-indigo-400 transition-colors hover:text-indigo-300"
>
FOCUSTELLA
</Link>
<nav className="z-50 flex items-center gap-4 text-sm font-medium text-slate-400">
<Link href="/log" className="transition-colors hover:text-slate-200">
{t("layout.nav.log")}
</Link>
<Link
href="/settings"
className="transition-colors hover:text-slate-200"
>
{t("layout.nav.settings")}
</Link>
<label className="flex items-center gap-2 text-xs text-slate-400">
<span>{t("layout.nav.language")}</span>
<select
value={locale}
onChange={handleLocaleChange}
className="rounded border border-slate-700 bg-slate-900/70 px-2 py-1 text-xs text-slate-200 outline-none transition-colors focus:border-indigo-400"
>
{SUPPORTED_LOCALES.map((item) => (
<option key={item} value={item}>
{LOCALE_LABELS[item]}
</option>
))}
</select>
</label>
</nav>
</header>
<main className="relative flex w-full flex-1 flex-col">{children}</main>
</div>
</I18nProvider>
);
}