Languages and translations#
CrabbyTab's interface can be shown in 26 languages: English and every language Tabbycat v2.12.1 offers. Each person picks their own language. You can also set a default language for your tournament's public pages and participant portals.
Every language except English is machine-drafted. No fluent speaker has reviewed those translations yet. The language menu marks each one "Machine-drafted translation". If you run a tournament in one of these languages, check the pages your participants will see first, and consider reviewing the catalogue.
Full developer reference: docs/internationalization.md.
Supported languages#
| Code | Language | Native name | Direction | Status |
|---|---|---|---|---|
en |
English | English | Left to right | Source language |
ar |
Arabic | العربية | Right to left | Machine-drafted |
ast |
Asturian | Asturianu | Left to right | Machine-drafted |
bn |
Bengali | বাংলা | Left to right | Machine-drafted |
bg |
Bulgarian | Български | Left to right | Machine-drafted |
ca |
Catalan | Català | Left to right | Machine-drafted |
cs |
Czech | Čeština | Left to right | Machine-drafted |
de |
German | Deutsch | Left to right | Machine-drafted |
el |
Greek | Ελληνικά | Left to right | Machine-drafted |
es |
Spanish | Español | Left to right | Machine-drafted |
fr |
French | Français | Left to right | Machine-drafted |
he |
Hebrew | עברית | Right to left | Machine-drafted |
hi |
Hindi | हिन्दी | Left to right | Machine-drafted |
id |
Indonesian | Bahasa Indonesia | Left to right | Machine-drafted |
it |
Italian | Italiano | Left to right | Machine-drafted |
ja |
Japanese | 日本語 | Left to right | Machine-drafted |
kk |
Kazakh | Қазақ тілі | Left to right | Machine-drafted |
ms |
Malay | Bahasa Melayu | Left to right | Machine-drafted |
pt |
Portuguese | Português | Left to right | Machine-drafted |
ro |
Romanian | Română | Left to right | Machine-drafted |
ru |
Russian | Русский | Left to right | Machine-drafted |
sk |
Slovak | Slovenčina | Left to right | Machine-drafted |
tr |
Turkish | Türkçe | Left to right | Machine-drafted |
uk |
Ukrainian | Українська | Left to right | Machine-drafted |
vi |
Vietnamese | Tiếng Việt | Left to right | Machine-drafted |
zh-Hans |
Simplified Chinese | 简体中文 | Left to right | Machine-drafted |
Notes:
- Debate terms follow Tabbycat's own translation for that language where it has one. Where Tabbycat's term was missing or clearly wrong (for example Italian "Bye" translated as "Ciao"), the drafter chose a term.
- Portuguese uses the Brazilian wording of Tabbycat's
ptcatalogue. - Arabic, Bengali and Hindi show scores, ranks and other numbers with Latin digits (0–9), so they match paper ballots. Month and day names are still localised.
- There is no Traditional Chinese. A browser set to Traditional Chinese (
zh-TW,zh-HK,zh-Hant) gets English rather than Simplified characters.
Choosing a language#
For yourself#
- Staff pages: open the account menu (top right of the navbar). The Language section lists every language plus Automatic.
- Public pages, portals, public registration, the home page, the tournament list and the setup wizard: use the language button in the page header. (The sign-in page has no button; it uses your saved choice or browser language.) It shows the current language's own name (for example "Deutsch"), so a reader who cannot read the current language can still find it.
Your choice is saved in this browser only (localStorage), not in your account. It does not
follow you to another device. Choose Automatic to clear it.
For your tournament's participants#
Participants often leave their browser in English even when the tournament runs in another language. To set a default for them:
- In the sidebar, go to Admin → Configuration → Public visibility.
- Set Public pages and portals language. "Each viewer's browser language" (the default) means no tournament language.
- Save tournament settings.
The tournament language applies to:
- the public tab and its sub-pages (records, schedule, check-ins, audit log, public ballot and feedback forms),
- the team and adjudicator private-URL portals,
- the projector display (
/{slug}/display), - printed ballots and feedback forms (see below).
It does not apply to staff pages, which always use each staff member's own choice. It also
does not apply to public registration (/register/{slug}), which uses the viewer's own choice or
browser language.
Printed ballots and feedback sheets are printed in the tournament's public language, whatever language your own screen is in. The print options show "Sheets print in {language}, the tournament's public language." If no tournament language is set, they print in your own language.
Which language wins#
CrabbyTab uses the first of these that matches a supported language:
- The viewer's own choice in the language menu.
- The tournament's public language (public pages, portals and projector only).
- The browser's preferred languages, in order. Regional tags are matched to the base language:
bn-BD→ Bengali,pt-BR→ Portuguese,zh-CN→ Simplified Chinese. - English.
The language is never in the URL and is not stored in a cookie. Private URLs, QR codes and printed links work the same in every language.
Right-to-left languages#
Arabic and Hebrew pages are laid out right to left: the page mirrors, and directional icons (arrows, chevrons) flip. The direction is set before the page first appears, so it never flashes left to right. Any right-to-left language added later (Persian, Urdu, and so on) gets this automatically.
What is translated#
Translated:
- Public pages, public forms and public registration.
- Team and adjudicator portals, including check-in, feedback and ballot entry.
- Almost all staff pages: draw, allocation, results, standings, breaks, feedback, motions, settings, imports and exports, backups, staff access, email setup, private URLs, schedule and the projector.
- Home, sign-in, the tournament list, the setup wizard and the API reference text.
- Long side names (for example "Opening Government"). Short side codes (OG, OO, CG, CO, AFF, NEG) stay as codes in every language.
Never translated — anything you or participants type in, or anything saved into records:
- team, speaker, institution, adjudicator, venue and round names,
- motions and info slides,
- feedback questions and answers,
- audit log summaries,
- CSV imports and exports (headers and values).
Still English (not yet translated):
- Warnings and validation messages produced by shared logic: draw-check issues, allocator warnings, venue and clash reasons, rule violations, score validation, backup validation, sync and upload status, and why a public release is blocked.
- Error messages from the server and the REST API, and the OpenAPI document.
- Emails (campaigns and automatic notifications).
- Browser tab titles and page metadata.
Gotchas#
- Offline: a language you have never opened before cannot load while offline. The page stays in English until you are back online. A language you have opened once keeps working offline.
- First moment of a page load: the page renders in English and switches as soon as the language loads. Most pages show a loading state at that time, so this is rarely visible.
- Machine-drafted wording: treat unusual debate terms as a translation issue, not a rule difference. Report them (see below).
Reviewing a translation#
If you are fluent in one of the languages, a review is the most useful contribution you can make.
- Find the catalogue:
src/lib/i18n/messages/locales/<code>.json(Bengali is split acrosssrc/lib/i18n/messages/bn.tsand thesrc/lib/i18n/messages/areas/bn-*files). Each key's English text is insrc/lib/i18n/messages/en.tsandsrc/lib/i18n/messages/areas/. - Switch CrabbyTab to the language and walk through the public tab, both portals and the pages you use most. Look for wrong debate terms, unnatural wording, and text too long for buttons or table headers.
- Edit the values only. Keep the following exactly as in English:
{placeholders}such as{round}or{count},<tag>…</tag>markup such as<b>this portal</b>,- plural keys (
_one,_few,_many,_other, …) — the language needs every category it has, - product names (CrabbyTab, Tabbycat, CSV, API).
- Run
npm run i18n:check(see below). - After the whole file has been reviewed, remove
machineDrafted: truefrom that language's entry inLOCALES(src/lib/i18n/config.ts) in the same pull request. The "machine-drafted" labels then disappear. For Bengali, also remove the machine-drafted warning comment at the top ofbn.ts.
Partial reviews are welcome too; leave the flag on and say what you covered in the pull request. See Contributing.
Details for contributors#
How it works#
CrabbyTab uses a small in-house typed dictionary in src/lib/i18n/ rather than an i18n library.
Translated pages are client components; a client-side provider (I18nProvider) chooses the
language with resolveLocale in src/lib/i18n/config.ts.
- English is bundled with every page and is the fallback: a missing key in any catalogue shows the English text.
- Other catalogues are lazy-loaded. Each is its own code-split chunk, fetched the first time
that language is chosen and cached by the service worker afterwards. See
loadCatalogueinsrc/lib/i18n/messages/index.tsand Architecture. - Keys are type-checked.
t("some.key")with a key not in the English catalogue fails the build. - Plurals use
Intl.PluralRules; numbers, dates and lists use theIntlformatters fromuseI18n(). Each language has anintlLocaletag (for examplear-u-nu-latnkeeps Latin digits).
Adding a language#
- Create the catalogue from the English source:
This writesnpm run i18n:new -- swsrc/lib/i18n/messages/locales/sw.jsonwith every English key (English text as the value to translate) and every plural category the language needs, worked out withIntl.PluralRules(extra categories start from the English_othertext). The code must be a BCP 47 tag (sw,pt-BR). It refuses to overwrite an existing file unlessI18N_OVERWRITE=1is set. It then prints the remaining steps. - Translate the values, following the rules in Reviewing a translation.
- Add an entry to
LOCALESinsrc/lib/i18n/config.ts:
Leave{ code: "sw", nativeName: "Kiswahili", englishName: "Swahili", intlLocale: "sw", machineDrafted: true },machineDrafted: trueuntil a fluent speaker has reviewed it. Use anintlLocalesuch asxx-u-nu-latnif the language's default digits are not Latin. Right-to-left is detected from the code; nothing else to set. - Add a loader line in
src/lib/i18n/messages/index.ts(a literalimport()path, so the catalogue becomes its own chunk):sw: () => import("@/lib/i18n/messages/locales/sw.json").then(json), - Run
npm run i18n:check. - Check the public tab, both portals and a few staff pages in the new language, including the right-to-left layout if it applies.
The language then appears in the language menu and the tournament public-language setting, and is matched against browser preferences.
Checking catalogues#
npm run i18n:check runs the i18n tests (vitest run src/lib/i18n). It checks every
catalogue, not just one; a language code after it is ignored. It reports:
- missing and unknown keys,
- missing plural categories for the language,
{placeholder}and<tag>markup that differs from English,- locale matching, text direction and the pre-paint script (
config.test.ts), - lookup, fallback, plurals and formatting (
translate.test.ts).
The same tests run in npm test, so a pull request that adds an English string must add the key
to every catalogue (copy the English text if you cannot translate it, and say so in the pull
request). See Testing.
The two scripts live in tools/i18n/ (run.mjs, template.ts, newLanguage.test.ts).
Adding a new string#
Add the key to the English catalogue for that area (src/lib/i18n/messages/areas/… or en.ts),
use it with t(), plural() or rich() from useI18n(), and add it to every other catalogue.
Never translate strings that are saved into records or compared in code. Full rules:
docs/internationalization.md.
For right-to-left support in new components, use logical Tailwind utilities (ms-*/me-*,
ps-*/pe-*, start-*/end-*, text-start/text-end), gap-* instead of space-x-*, and
rtl:-scale-x-100 on directional icons.