feat(i18n): offer every ISO 639-1 language

The table was thirty languages typed by hand. It is now generated:
utils/generate_languages.py takes the 184 alpha-2 codes from pycountry,
the display names from CLDR through babel, and the writing direction from
CLDR character order. 159 languages carry their endonym; the remaining 25
have no CLDR entry and carry their English ISO name.

That corrects the right-to-left set, which had four entries and needs ten
— dv, ks, ps, sd, ug and yi were simply missed.

Only 29 languages ship an interface catalogue, so the other 155 render in
English until one is filled. make i18n-ui fills app/i18n/ui/ for them, and
make i18n now covers the interface strings as well; neither asks for a
string a shipped catalogue already answers, so hand-written entries stay.

184 entries do not fit on a screen, so the language menu scrolls inside
itself. overscroll-behavior keeps the page behind it from moving once the
list reaches its end.

babel and pycountry are dev dependencies: the generator needs them, the
application does not.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-22 01:08:28 +02:00
parent 0c67f999f6
commit ef1c8ff09a
12 changed files with 413 additions and 76 deletions

View File

@@ -11,46 +11,17 @@ from pathlib import Path
import yaml
try:
from app.utils.languages import LANGUAGES, RTL_LANGUAGES
except ImportError: # pragma: no cover - supports running from the app/ directory.
from utils.languages import LANGUAGES, RTL_LANGUAGES
I18N_DIR = Path(__file__).resolve().parent.parent / "i18n"
UI_DIR = I18N_DIR / "ui"
CONTENT_DIR = I18N_DIR / "content"
SOURCE_LANGUAGE = "en"
LANGUAGES = {
"en": "English",
"zh": "中文",
"hi": "हिन्दी",
"es": "Español",
"fr": "Français",
"ar": "العربية",
"bn": "বাংলা",
"pt": "Português",
"ru": "Русский",
"ur": "اردو",
"id": "Bahasa Indonesia",
"de": "Deutsch",
"ja": "日本語",
"tr": "Türkçe",
"ko": "한국어",
"vi": "Tiếng Việt",
"it": "Italiano",
"th": "ไทย",
"pl": "Polski",
"nl": "Nederlands",
"uk": "Українська",
"fa": "فارسی",
"ro": "Română",
"el": "Ελληνικά",
"cs": "Čeština",
"sv": "Svenska",
"hu": "Magyar",
"he": "עברית",
"da": "Dansk",
"fi": "Suomi",
}
RTL_LANGUAGES = frozenset({"ar", "fa", "he", "ur"})
AUTOFILL_KEYS = frozenset({"description", "text", "warning", "info", "subtitel"})