feat: 다국어 화
This commit is contained in:
87
src/features/i18n/ui/I18nLayoutShell.tsx
Normal file
87
src/features/i18n/ui/I18nLayoutShell.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user