Internationalization (apback)
Backend notification and email strings use i18next with locale files under locales/.
Layout
| Path | Content |
|---|---|
locales/en/apback.json |
English source |
locales/{de,fr,it,es-US}/apback.json |
Translations only (Weblate + bin/translate.mjs) |
locale-src/{de,fr,it,es-US}/apback.json |
English snapshot for machine-translation diffing (CI/local only) |
Community locales (es, pt, ta, etc.) have no sidecars. Only en, fr, de, it, pt, and ta are registered at runtime (lib/apbackI18n.ts via lib/api/i18n.ts / initi18n, and the same module for crons).
Game titles in notification bodies use @abstractplay/gameslib apgames:names.{uid} bundles (imported from gameslib/locales/*/apgames.json in initi18n). localizedGameName() in lib/gameDisplayName.ts resolves the title after changeLanguageForPlayer.
Scripts
npm run prune-locales # Drop stale keys from de/fr/it/es-US + locale-src
node bin/translate.mjs locales/en/apback.json # Gemini translate de/fr/it/es-US
node bin/check-locale-readiness.mjs # Audit vs English
node bin/split-locale-src.mjs # One-time _src_* extraction (reference)
CI (develop deploy)
On develop only, the deploy workflow prunes stale keys, runs bin/translate.mjs with GEMINI_API_KEY, auto-commits updated locales/{de,fr,it,es-US}/ and locale-src/ files, and verifies locale-src was committed. main deploy does not call Gemini; it ships whatever locales are already on the branch.
Weblate
Weblate syncs locales/ only. locale-src/ is never in scope, so PRs cannot strip translation-tracking metadata. Close any in-flight Weblate PRs that remove _src_* keys before merging the locale-src migration.