i18n
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
import { setupI18n, type I18n, type Messages } from "@lingui/core";
|
||||
import { messages as enMessages } from "../../locales/en/messages.po";
|
||||
|
||||
export const SUPPORTED_LOCALES = ["en", "es", "de", "ja"] as const;
|
||||
export type AppLocale = (typeof SUPPORTED_LOCALES)[number];
|
||||
export const DEFAULT_LOCALE: AppLocale = "en";
|
||||
|
||||
// es is Mexican Spanish (translations + dayjs es-mx).
|
||||
export const LOCALE_LABELS: Record<AppLocale, string> = {
|
||||
en: "English",
|
||||
es: "Español",
|
||||
de: "Deutsch",
|
||||
ja: "日本語",
|
||||
};
|
||||
|
||||
// Adding a language: add it to SUPPORTED_LOCALES + LOCALE_LABELS, add the
|
||||
// locale to lingui.config.ts, run `bunx lingui extract`, and translate the
|
||||
// generated src/locales/<locale>/messages.po.
|
||||
const catalogLoaders = import.meta.glob<{ messages: Messages }>(
|
||||
"/src/locales/*/messages.po"
|
||||
);
|
||||
|
||||
const messageCache: Partial<Record<AppLocale, Messages>> = {
|
||||
[DEFAULT_LOCALE]: enMessages,
|
||||
};
|
||||
|
||||
export function resolveLocale(candidate: string | null | undefined): AppLocale {
|
||||
if (!candidate) return DEFAULT_LOCALE;
|
||||
const base = candidate.toLowerCase().split("-")[0];
|
||||
return (SUPPORTED_LOCALES as readonly string[]).includes(base)
|
||||
? (base as AppLocale)
|
||||
: DEFAULT_LOCALE;
|
||||
}
|
||||
|
||||
export async function ensureMessages(locale: AppLocale): Promise<Messages> {
|
||||
const cached = messageCache[locale];
|
||||
if (cached) return cached;
|
||||
|
||||
const loader = catalogLoaders[`/src/locales/${locale}/messages.po`];
|
||||
if (!loader) return messageCache[DEFAULT_LOCALE]!;
|
||||
|
||||
const { messages } = await loader();
|
||||
messageCache[locale] = messages;
|
||||
return messages;
|
||||
}
|
||||
|
||||
export function getCachedMessages(locale: AppLocale): Messages {
|
||||
return messageCache[locale] ?? messageCache[DEFAULT_LOCALE]!;
|
||||
}
|
||||
|
||||
export function hasCachedMessages(locale: AppLocale): boolean {
|
||||
return messageCache[locale] !== undefined;
|
||||
}
|
||||
|
||||
export function createI18n(locale: AppLocale): I18n {
|
||||
const i18n = setupI18n();
|
||||
i18n.load(locale, getCachedMessages(locale));
|
||||
i18n.activate(locale);
|
||||
return i18n;
|
||||
}
|
||||
|
||||
export function parseAcceptLanguage(header: string | null): AppLocale {
|
||||
if (!header) return DEFAULT_LOCALE;
|
||||
for (const part of header.split(",")) {
|
||||
const tag = part.split(";")[0]?.trim();
|
||||
if (!tag) continue;
|
||||
const base = tag.toLowerCase().split("-")[0];
|
||||
if ((SUPPORTED_LOCALES as readonly string[]).includes(base)) {
|
||||
return base as AppLocale;
|
||||
}
|
||||
}
|
||||
return DEFAULT_LOCALE;
|
||||
}
|
||||
Reference in New Issue
Block a user