// semantic key
t("checkout.payButton"); // en.json: "Pay now"
// source text as the key (gettext style)
t("Pay now");
// key + default message + description (react-intl)
intl.formatMessage({
id: "checkout.payButton",
defaultMessage: "Pay now",
description: "Button that submits the payment form",
});
A good workflow keeps strings out of the code, sends them to translators with context, and checks in CI that nothing is missing.
Keys vs source text:
- Semantic keys (
checkout.payButton) stay stable when the English text changes. The same word can have different keys for different contexts, like "Open" as a verb or as a status. But the code is harder to read, and you must keep the English file in sync. - Source text as the key (
t("Pay now")) is easy to read and write. But fixing a typo in English creates a new key and loses existing translations. The same English word in two places can't be translated differently. - A common middle way (react-intl): a stable id plus a
defaultMessageand adescriptionin the code. Tools extract all three.
The workflow:
- Extract: a CLI scans the code and writes the messages to a source file, like
en.json. - Translate: the file goes to a translation management system (TMS) such as Crowdin, Lokalise or Phrase. Translators see the description and often a screenshot.
- Import: translated files come back into the repo or are downloaded at build time.
Missing translations:
- Fall back along a chain:
fr-CA, thenfr, then the default language. Never show the raw key to users. - Log missing keys in development, and fail CI when a locale is missing keys or has extra ones.
- Use typed keys (generated TypeScript types) so a typo in a key is a compile error.
- Semantic keys stay stable when the English text changes, and allow different translations by context.
- Source-text keys are readable, but editing the English loses the existing translations.
- Adding a description and a default message gives translators context.
- Extraction tools collect messages from code into a file for the translation system.
- Missing translations fall back through a chain like
fr-CA,fr, then the default. - CI should check that every locale has every key.