Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,6 @@ Autostart + close-to-tray + scan-on-start ([`commands/preferences.rs`](src-tauri

## Language

The README is in English. The app ships UI copy in **17 locales** via i18next — `fr` (source of truth), `en`, `es`, `de`, `it`, `nl`, `pt`, `pt-BR`, `ru`, `tr`, `id`, `ja`, `kr` (registered as `ko` + `kr` alias), `zh-CN`, `zh-TW`, `ar`, `hi`. Strings in `src/i18n/locales/<code>.json`. `index.ts` sets `document.documentElement.dir` per language so Arabic renders RTL automatically.
The README is in English. The app ships UI copy in **17 locales** via i18next — `fr` (source of truth), `en`, `es`, `de`, `it`, `nl`, `pt`, `pt-BR`, `ru`, `tr`, `id`, `ja`, `ko`, `zh-CN`, `zh-TW`, `ar`, `hi`. Strings in `src/i18n/locales/<code>.json`. `index.ts` sets `document.documentElement.dir` per language so Arabic renders RTL automatically. The legacy `kr` code is still accepted as an alias for back-compat (early builds shipped `kr` instead of the ISO 639-1 `ko`); a one-time migration rewrites stored `kr` preferences to `ko` at startup.

`fallbackLng: "en"` is set, but the project convention is **every locale carries every key** so the experience stays coherent without language-mixing. When you add a new key, propagate it to all 17 locales (a small Python script using `json.load`/`dump` with `ensure_ascii=False, indent=2` keeps the existing formatting intact). Brand tokens (`WaveFlow`, `Last.fm`, `Deezer`, `ReplayGain`, `LRCLIB`, `BPM`) stay verbatim across locales. Preserve i18next `{{placeholder}}` interpolation tokens unchanged.
23 changes: 20 additions & 3 deletions src/i18n/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,21 @@ export const SUPPORTED_LANGUAGES: readonly SupportedLanguage[] = [

const LOCAL_STORAGE_KEY = "waveflow-language";
const SUPPORTED_LANGUAGE_CODES = SUPPORTED_LANGUAGES.map((lang) => lang.code);

// Rewrite the historical "kr" preference to "ko" once, before i18next
// reads localStorage. `convertDetectedLanguage` already normalises at
// read time, but persisting the canonical value keeps DevTools / future
// migrations honest.
try {
if (typeof localStorage !== "undefined") {
if (localStorage.getItem(LOCAL_STORAGE_KEY) === "kr") {
localStorage.setItem(LOCAL_STORAGE_KEY, "ko");
}
}
} catch {
// Storage unavailable (private browsing, embed sandbox) — non-fatal,
// the in-memory alias still maps "kr" → "ko" for the session.
}
const localeLoaders: Record<
string,
() => Promise<{ default: ResourceLanguage }>
Expand All @@ -61,8 +76,7 @@ const localeLoaders: Record<
pt: () => import("./locales/pt.json"),
"pt-BR": () => import("./locales/pt-BR.json"),
ja: () => import("./locales/ja.json"),
ko: () => import("./locales/kr.json"),
kr: () => import("./locales/kr.json"),
ko: () => import("./locales/ko.json"),
nl: () => import("./locales/nl.json"),
ar: () => import("./locales/ar.json"),
hi: () => import("./locales/hi.json"),
Expand All @@ -76,7 +90,10 @@ const localeLoaders: Record<
// OS reporting `fr-FR` or `en-US` lands on `fr` / `en` instead of
// falling all the way to the fallback language.
const LANGUAGE_ALIASES: Record<string, string> = {
// Korean — historical "kr" code (the file is still kr.json)
// Korean — historical "kr" code that shipped before we normalised to
// the correct ISO 639-1 "ko". Old profiles may still have "kr" in
// localStorage; the alias keeps them working until they pick a
// language explicitly.
kr: "ko",
"ko-KR": "ko",
// Chinese — pick simplified by default for ambiguous codes; preserve
Expand Down
File renamed without changes.
Loading