mirror of
https://github.com/bookorbit/bookorbit.git
synced 2026-11-03 00:11:37 -06:00
feat(client): add nine interface languages and a searchable language picker
Traditional Chinese cannot reuse the `%two_letters_code%` filename because it would collide with Simplified under `zh.json`, so `crowdin.yml` maps `zh-TW` to a `zh-Hant` catalog and locale matching resolves by script subtag before falling back to the base language. Every other new locale keeps its bare two-letter ID. Twenty-five entries outgrew the old menus. The mobile submenu inherited `overflow-hidden` with no max-height, so the last nine languages were unreachable on a phone, and the list was ordered by ISO code while displaying endonyms, which reads as unsorted. Both header menus now render one LanguagePicker that searches the native name, the English name and the locale code, groups the browser's own languages as suggestions, and orders the rest with a collator on the string it actually shows. Mobile opens a sheet instead of a nested flyout, since a flyout is the wrong shape for touch regardless of height. Settings gains the Appearance language section its copy has anticipated since the keys were added. Tighten the Crowdin progress chart, which the longer language list had stretched to 810px tall.
This commit is contained in:
+29
-20
@@ -4,30 +4,39 @@ BookOrbit uses Vue I18n catalogs under `client/src/locales/`. English is the sou
|
||||
|
||||
## Supported Catalogs
|
||||
|
||||
| Application locale | Crowdin language ID | Catalog |
|
||||
| -------------------- | ------------------- | --------- |
|
||||
| English | Source language | `en.json` |
|
||||
| Czech | `cs` | `cs.json` |
|
||||
| Danish | `da` | `da.json` |
|
||||
| German | `de` | `de.json` |
|
||||
| Spanish | `es-ES` | `es.json` |
|
||||
| Finnish | `fi` | `fi.json` |
|
||||
| French | `fr` | `fr.json` |
|
||||
| Italian | `it` | `it.json` |
|
||||
| Dutch | `nl` | `nl.json` |
|
||||
| Polish | `pl` | `pl.json` |
|
||||
| Brazilian Portuguese | `pt-BR` | `pt.json` |
|
||||
| Russian | `ru` | `ru.json` |
|
||||
| Slovenian | `sl` | `sl.json` |
|
||||
| Swedish | `sv-SE` | `sv.json` |
|
||||
| Ukrainian | `uk` | `uk.json` |
|
||||
| Simplified Chinese | `zh-CN` | `zh.json` |
|
||||
| Application locale | Crowdin language ID | Catalog |
|
||||
| -------------------- | ------------------- | -------------- |
|
||||
| English | Source language | `en.json` |
|
||||
| Czech | `cs` | `cs.json` |
|
||||
| Danish | `da` | `da.json` |
|
||||
| German | `de` | `de.json` |
|
||||
| Greek | `el` | `el.json` |
|
||||
| Spanish | `es-ES` | `es.json` |
|
||||
| Finnish | `fi` | `fi.json` |
|
||||
| French | `fr` | `fr.json` |
|
||||
| Hungarian | `hu` | `hu.json` |
|
||||
| Indonesian | `id` | `id.json` |
|
||||
| Italian | `it` | `it.json` |
|
||||
| Japanese | `ja` | `ja.json` |
|
||||
| Korean | `ko` | `ko.json` |
|
||||
| Dutch | `nl` | `nl.json` |
|
||||
| Polish | `pl` | `pl.json` |
|
||||
| Brazilian Portuguese | `pt-BR` | `pt.json` |
|
||||
| Romanian | `ro` | `ro.json` |
|
||||
| Russian | `ru` | `ru.json` |
|
||||
| Slovak | `sk` | `sk.json` |
|
||||
| Slovenian | `sl` | `sl.json` |
|
||||
| Swedish | `sv-SE` | `sv.json` |
|
||||
| Turkish | `tr` | `tr.json` |
|
||||
| Ukrainian | `uk` | `uk.json` |
|
||||
| Simplified Chinese | `zh-CN` | `zh.json` |
|
||||
| Traditional Chinese | `zh-TW` | `zh-Hant.json` |
|
||||
|
||||
Crowdin's `%two_letters_code%` placeholder reduces the regional IDs above to BookOrbit's application locale and filename: `es-ES` to `es`, `pt-BR` to `pt`, `sv-SE` to `sv`, and `zh-CN` to `zh`.
|
||||
|
||||
Verify every ID against Crowdin's language list rather than assuming the two-letter form exists. Several languages have no bare two-letter ID at all: Swedish is `sv-SE`, and `sv-FI` reduces to the same `sv.json`, so enabling both would collide.
|
||||
|
||||
Chinese ships as Simplified only under the bare `zh` locale, so every `zh-*` browser resolves to it, including Traditional readers. Adding Traditional later means a new `zh-Hant` catalog with a `languages_mapping` entry for `zh-TW`; `zh` keeps its Simplified meaning, so no rename or stored-preference migration is needed. That addition also requires matching by script subtag in `client/src/stores/locale.ts`, because the current base-language fallback would send `zh-Hant` to `zh`.
|
||||
Chinese uses separate application locales for each script. Simplified Chinese remains under `zh`, while Traditional Chinese uses `zh-Hant`. The `languages_mapping` entry in `crowdin.yml` maps Crowdin's `zh-TW` target to `zh-Hant.json` so it cannot collide with the Simplified catalog. Browser matching uses explicit or inferred script subtags: `zh-CN` and `zh-SG` resolve to `zh`, while `zh-TW`, `zh-HK`, and `zh-MO` resolve to `zh-Hant`.
|
||||
|
||||
## Adding User-Facing Copy
|
||||
|
||||
@@ -92,7 +101,7 @@ After an English source change reaches `main`:
|
||||
4. The workflow requests only translated strings, removes the empty values Crowdin emits for untranslated nested JSON entries, and validates the resulting sparse catalogs.
|
||||
5. Before writing, the workflow rejects exports that omit an existing translation or replace one with English source text.
|
||||
6. Only after validation and retention checks pass, the workflow updates `l10n_main` and opens a pull request to `main`.
|
||||
7. CI verifies that the pull request changes only the fifteen target catalogs and runs the normal client checks.
|
||||
7. CI verifies that the pull request changes only the twenty-four target catalogs and runs the normal client checks.
|
||||
8. A maintainer reviews and squash-merges the pull request.
|
||||
9. The `.github/workflows/crowdin-branch-cleanup.yml` workflow deletes the `l10n_main` service branch. The next export recreates it from the current `main` when translations change.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user