- next-intl 5.x 도입
- src/i18n/config.ts: LOCALES 상수, detectBrowserLocale, LOCALE_LABELS(국기/네이티브명)
- src/i18n/LocaleProvider.tsx: NextIntlClientProvider wrap + localStorage tasteby_locale 저장
- src/messages/{ko,en,ja,es}.json: 초기 30개 키 (header/actions/filter/restaurant/review 5 카테고리)
- src/components/LanguageSwitcher.tsx: 헤더용 드롭다운 (국기 + native, ARIA listbox, 44px 터치)
- providers.tsx: LocaleProvider로 AuthProvider 감싸기
- page.tsx 헤더에 LanguageSwitcher 배치
설계서: docs/design/352-i18n-skeleton/README.md (Approved)
언어 선택 근거:
- ko: 기본
- en: 글로벌 1순위
- ja: 일본 사용자 + 한국 음식 관광
- es: 5억 화자, 라틴아메리카 + 스페인 확장
미번역 키는 ko fallback. URL 라우팅(/en/)/SEO meta/사용자 콘텐츠 번역은 후속.
Refs: #352
29 lines
1.1 KiB
TypeScript
29 lines
1.1 KiB
TypeScript
// #352 i18n 뼈대 — 로케일 목록/기본값
|
|
|
|
export const LOCALES = ["ko", "en", "ja", "es"] as const;
|
|
export type Locale = (typeof LOCALES)[number];
|
|
|
|
export const DEFAULT_LOCALE: Locale = "ko";
|
|
export const LOCALE_STORAGE_KEY = "tasteby_locale";
|
|
|
|
export const LOCALE_LABELS: Record<Locale, { flag: string; label: string; native: string }> = {
|
|
ko: { flag: "🇰🇷", label: "Korean", native: "한국어" },
|
|
en: { flag: "🇺🇸", label: "English", native: "English" },
|
|
ja: { flag: "🇯🇵", label: "Japanese", native: "日本語" },
|
|
es: { flag: "🇪🇸", label: "Spanish", native: "Español" },
|
|
};
|
|
|
|
export function isLocale(value: string | null | undefined): value is Locale {
|
|
return value != null && (LOCALES as readonly string[]).includes(value);
|
|
}
|
|
|
|
/**
|
|
* 브라우저 언어 감지 → 지원 로케일이면 그것, 아니면 기본값.
|
|
* SSR-safe (typeof window 체크 호출자).
|
|
*/
|
|
export function detectBrowserLocale(): Locale {
|
|
if (typeof navigator === "undefined") return DEFAULT_LOCALE;
|
|
const code = navigator.language.split("-")[0].toLowerCase();
|
|
return isLocale(code) ? code : DEFAULT_LOCALE;
|
|
}
|