Translation guide
Every text JEC shows, in the web interface, error messages, issuance logs and notifications, is stored in one JSON file per language under i18n/locales/. The server embeds these files and the web interface loads the same ones, so a language added there takes effect everywhere at once. American English (en-US.json) is the base language: it defines every key, and stands in for any message another language leaves out.
Adding a language
- Copy
i18n/locales/en-US.jsonto a file named after the language's BCP 47 tag, such asde-DE.jsonorpt-BR.json. - Fill in
_meta:nameis the language's name in the language itself (such asDeutsch);dirisltr, orrtlfor right-to-left scripts such as Arabic and Hebrew.completenessis maintained by the tools. - Translate the values; keep the keys, and the argument names in braces, unchanged.
- Run
go run ./tools/i18n fixandgo generate ./i18n(see below). The latter adds the language to the web interface's list, after which it can be chosen in the language menu of the Settings page and for notification channels.
A translation need not be complete: untranslated messages are shown in English, and completeness records the progress. Only en-US and zh-CN must contain every key, so partial translations are welcome as a start.
Tools
go run ./tools/i18n fix # sort the files and write completeness
go run ./tools/i18n check # the checks CI runs
go generate ./i18n # after adding or removing keyscheck reports keys missing from the base language or from zh-CN, keys absent from the base language, empty texts, the TODO: placeholders of new modules, messages that do not parse, and messages whose arguments, plural branches or select branches differ from the English ones. Run fix after every edit: CI fails on a file that is not in canonical form.
Message syntax
Messages use the subset of ICU MessageFormat that the server and the web interface (FormatJS) render identically:
| Form | Example |
|---|---|
| Argument | Renew {domain} now |
| Plural | {count, plural, one {# certificate} other {# certificates}} |
| Exact count | {count, plural, =0 {no certificates} other {# certificates}} |
| Select | {kind, select, dns {DNS record} http {file} other {check}} |
- Keep every argument of the English message, with the same name and of the same kind. Arguments may be moved, and a language without plural forms need not use them. The check compares these.
- In a plural branch,
#stands for the number, written as the language writes numbers. Provide the plural categories the language has (zero,one,two,few,many,other; see CLDR);otheris always required. - An apostrophe quotes a following
{,},<,>or#up to the next apostrophe, so'{{.domains}}'stands for the text{{.domains}}; two consecutive apostrophes stand for one. Any other apostrophe is plain text:don'tneeds no escaping. - Number, date and time arguments are formatted before they reach the message; there is no
{n, number}or{d, date}.
Style
- Write plain, short sentences. The interface is dense, especially on phones: the pseudo-locale test lengthens every text to 140% of the English and fails any page that overflows, so prefer the shorter of two equally accurate wordings.
- Keep names unchanged: product and service names (Let's Encrypt, Alibaba Cloud ESA, DNSPod), protocol and field names (ACME, CNAME,
_acme-challenge), commands, paths and environment variables. - Use the same term for a concept throughout. The English terms are: certificate source, validation method, deploy target, notification channel, issuance, renewal, deployment.
- Error messages state what happened and what to do next, without blaming the reader.
Checking a translation
pnpm -C web build && go build -o build/jec ./cmd/jec
JEC_E2E_BIN=../build/jec pnpm -C web e2e # the pseudo-locale test, through every pageRun JEC and switch the language on the Settings page, or let it follow the browser's. Check a phone-sized window as well: the bottom tab bar, the certificate list and the forms are where overlong text shows first.
Plugins
A plugin provides the texts of its types in each language it supports (see Plugins); for any other language, it falls back to English, as JEC itself does.