mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 10:24:02 +00:00
70 lines
4.6 KiB
Markdown
70 lines
4.6 KiB
Markdown
# UI text and translation
|
|
|
|
Every piece of text a user can see goes through `t()`, `tn()` or `msg()` in code, or a `data-i18n*` attribute in markup. The English text itself is the key. English needs no catalog; other languages live in `src/i18n/locales/<code>.json`. Translators work from [TRANSLATORS.md](./TRANSLATORS.md).
|
|
|
|
## Which one to use
|
|
|
|
| | Translates | Use for |
|
|
| ------------------------------- | --------------------------------- | --------------------------------------------- |
|
|
| `t(text, values)` | now | any text shown right where it is written |
|
|
| `tn(count, one, other, values)` | now, picking the form for `count` | text that changes with a number |
|
|
| `msg(text)` | never: returns `text` unchanged | text stored in data and passed to `t()` later |
|
|
|
|
```js
|
|
import { t, tn, msg } from '../i18n/index.js';
|
|
|
|
status.textContent = t('Save keys');
|
|
status.textContent = t('Saved to {store}', { store: storeLabel() });
|
|
|
|
chip.textContent = tn(missing, '{count} KEY WAITING', '{count} KEYS WAITING');
|
|
// en: 1 KEY / 3 KEYS ru: 1 КЛЮЧ / 3 КЛЮЧА / 5 КЛЮЧЕЙ
|
|
|
|
const FEED_STATE_LABELS = { loading: msg('LOADING'), stale: msg('STALE') };
|
|
label.textContent = t(FEED_STATE_LABELS[state]);
|
|
```
|
|
|
|
`msg()` exists for two reasons. `npm run i18n` finds messages only as string literals inside `t(…)`, `tn(…)` and `msg(…)`, so `t(FEED_STATE_LABELS[state])` alone tells it nothing. And a `t()` call at module load runs before the page has picked its language, so it would freeze the English.
|
|
|
|
## Markup
|
|
|
|
```html
|
|
<span data-i18n>Normal</span>
|
|
<button title="Pin visual presets" data-i18n-title>…</button>
|
|
<input placeholder="Search any location..." data-i18n-placeholder />
|
|
```
|
|
|
|
`data-i18n` replaces the element's text, so put it on the element that directly holds the text, never on a parent with child elements. `data-i18n-title`, `data-i18n-aria-label` and `data-i18n-placeholder` translate that attribute. Markup is translated once at boot, and switching language reloads the page.
|
|
|
|
## Rules
|
|
|
|
- **Pass literals.** `t('Close')`, not `t(label)` (unless `label` holds a `msg()` value) and never a template literal with `${…}`.
|
|
- **Use placeholders, not concatenation.** `t('Flying to {place}…', { place })`, not `t('Flying to') + ' ' + place`. Word order differs between languages.
|
|
- **Use `tn()` for counts**, not `count === 1 ? … : …`. Russian has three forms.
|
|
- **Translate the label, not the value.** Layer ids, enums, share-link parameters, API fields, voice tool schemas and feed data (callsigns, vessel and place names) stay as they are; translate only the text shown for them.
|
|
- **No `t()` at module top level.** Keep the English in `msg()` and call `t()` when rendering.
|
|
- **Numbers and dates** go through `formatNumber`, `formatDate` and `formatRelativeTime` from `src/i18n/index.js`, which follow the active language.
|
|
- **Identical English means identical translation.** If two places need different words, make the English differ.
|
|
- **Text that should stay English** (a brand, a code, a unit symbol) is simply not wrapped.
|
|
|
|
## After you add or change text
|
|
|
|
```sh
|
|
npm run i18n # shows what each language now lacks or no longer uses
|
|
npm run i18n -- --missing ru # the missing ru entries as JSON
|
|
npm run i18n -- --prune # drop entries whose English no longer exists
|
|
```
|
|
|
|
Changing the English changes the key, so the old translation goes unused and the new text shows as missing. Add the translation to `src/i18n/locales/ru.json` (or ask a translator), prune, and run `npm test`, which runs the same check and fails until every language is complete.
|
|
|
|
To see a language, open the app with `?lang=ru`, or use the EN/RU switch in the Visual Presets dock.
|
|
|
|
## Where things live
|
|
|
|
| File | What it holds |
|
|
| ------------------------------ | ----------------------------------------------------------------------------- |
|
|
| `src/i18n/index.js` | `t`, `tn`, `msg`, the Intl formatters, markup translation, switching language |
|
|
| `src/i18n/locale.js` | shipped languages, their names, choosing the language at boot |
|
|
| `src/i18n/locales/<code>.json` | one catalog per language, keyed by English text |
|
|
| `scripts/i18n.mjs` | `npm run i18n`: extracts messages and checks the catalogs |
|
|
| `src/i18n/catalog.test.mjs` | runs that check under `npm test` |
|