Skip to content

Locales

Every word a card draws itself — a title, a label, an empty state, and the description a screen reader gets — comes from a translation table. locale picks which one:

?username=octocat&locale=es

An unknown code is an error reading Locale not found, not a silent fallback to English: a card drawn in the wrong language is worse than a card that says why it could not be.

en, ar, az, bg, bn, ca, cn, zh-tw, cs, de, sw, ur, es, fa, fi, fr, hi, sa, hu, it, ja, kr, nl, pt-pt, pt-br, np, el, ro, ru, uk-ua, id, ml, my, ta, sk, tr, pl, uz, vi, se, he, fil, th, sr, sr-latn, no, be.

A string added to a card is written in English first and translated afterwards, so a locale can be missing a key that en has. When that happens the card draws the English string for that one line and renders normally — it does not fail, and it does not fall back to English for the whole card.

Two things are deliberately never translated:

  • Error cards. An error is often thrown before the locale has been read, and sometimes because it could not be. They are English.
  • Your own text. custom_title is drawn exactly as you passed it.

Each card owns its table, in packages/core/src/cards/<card>/locales.ts, one entry per key per locale. Adding a language means adding your code to each key you can translate; anything you leave out keeps reading in English until someone fills it in.

A wording can carry the values the card draws into it, as {name} placeholders:

'statcard.title': {
en: "{name}'{apostrophe} GitHub Stats",
fr: 'Statistiques GitHub de {name}',
},

Word order is therefore the translation’s own, and so is which values it uses — French has no use for the possessive {apostrophe} above. A placeholder the English wording does not supply is the one thing that will not work.

Where the wording depends on a number, write a form per plural category instead. The card passes a count and the category is picked by the locale’s own rules, so a language with more than two forms is free to write them all:

'contributedtocard.footer-all': {
en: { one: '{count} repository', other: '{count} repositories' },
ru: { one: '{count} репозиторий', few: '{count} репозитория', other: '{count} репозиториев' },
},

other is the form every locale needs, and the one a category you leave out falls back to.