mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 02:14:01 +00:00
Merge pull request #1473 from bilawalsidhu/german/hal-641-it
Italian in the language picker: 2730 strings, Italian plural forms, labels shortened to fit the panels, and the it style guide. Based on the translation by @abiyeenzo (#912). Co-authored-by: Enzo Abyie <[email protected]>
This commit is contained in:
commit
15da935b8f
15 files changed
+3433
-15
No files matched your search
@@ -0,0 +1,3 @@
|
||||
- Add Italian (it) to the language picker: the whole interface in Italian,
|
||||
with Italian plural forms and labels shortened to fit the panels. Based on
|
||||
the translation by @abiyeenzo (#912) (German, @gekh, #1473).
|
||||
@@ -3782,7 +3782,7 @@ test:track` 43 tracking invariants · headless QA harnesses under
|
||||
|
||||
### Interface localization (October 2026)
|
||||
|
||||
Shipped locales: **en** (source and fallback), **es**, **ru**, **uk**, **zh-CN** and **zh-Hant**. The code lives in
|
||||
Shipped locales: **en** (source and fallback), **es**, **it**, **ru**, **uk**, **zh-CN** and **zh-Hant**. The code lives in
|
||||
`src/i18n/`. How to mark UI text: [`docs/I18N.md`](./I18N.md); translator workflow:
|
||||
[`docs/TRANSLATORS.md`](./TRANSLATORS.md).
|
||||
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
# Translating God's Eye View
|
||||
|
||||
Shipped languages: **English** (source and fallback), **Spanish** (`es`), **Russian** (`ru`), **Ukrainian** (`uk`), **Simplified Chinese** (`zh-CN`) and **Traditional Chinese** (`zh-Hant`).
|
||||
Shipped languages: **English** (source and fallback), **Spanish** (`es`), **Italian** (`it`), **Russian** (`ru`), **Ukrainian** (`uk`), **Simplified Chinese** (`zh-CN`) and **Traditional Chinese** (`zh-Hant`).
|
||||
|
||||
## How it works
|
||||
|
||||
@@ -63,4 +63,4 @@ These are contracts, not copy: layer ids and registry keys, status enums and oth
|
||||
|
||||
## Language notes
|
||||
|
||||
Each language keeps its glossary, plural forms, units and style in its own file, `docs/translations/<code>.md`: [Spanish](./translations/es.md), [Russian](./translations/ru.md), [Ukrainian](./translations/uk.md), [Simplified Chinese](./translations/zh-CN.md), [Traditional Chinese](./translations/zh-Hant.md). Read it before translating, and add a term there when you settle on one.
|
||||
Each language keeps its glossary, plural forms, units and style in its own file, `docs/translations/<code>.md`: [Spanish](./translations/es.md), [Italian](./translations/it.md), [Russian](./translations/ru.md), [Ukrainian](./translations/uk.md), [Simplified Chinese](./translations/zh-CN.md), [Traditional Chinese](./translations/zh-Hant.md). Read it before translating, and add a term there when you settle on one.
|
||||
@@ -0,0 +1,108 @@
|
||||
# Italian (`it`)
|
||||
|
||||
Catalog: `src/i18n/locales/it.json`. General rules: [TRANSLATORS.md](../TRANSLATORS.md).
|
||||
|
||||
Translation source: abiyeenzo (#912).
|
||||
|
||||
## Style
|
||||
|
||||
- Standard Italian as in Italian software UIs. Address the user with informal "tu" imperatives: «Aggiungi», «Attiva», «Scegli», «Incolla», «Riprova». Tooltips that name an action use the same imperative: «Espandi il pannello», «Ripristina la camera».
|
||||
- Sentence case, not Title Case: "Live Flights" → «Voli live», "Space Missions" → «Missioni spaziali». UPPERCASE English stays UPPERCASE: «LIVE», «NON DISPONIBILE».
|
||||
- Quotes inside Italian text are straight double quotes: «Eliminare lo scatto "{shot}"?». Use the accented capitals (È, À) in UPPERCASE text: «VELOCITÀ», «CITTÀ».
|
||||
- Errors read as a statement, not a past participle on its own: "Search failed" → «Ricerca non riuscita»; "Could not …" → «Impossibile …». A dash that joins a cause and a hint becomes a colon: «Chiave API rifiutata: controlla AISSTREAM_API_KEY». The same goes for a label and its hint in a tooltip: «Direzione (bussola °): clic per digitare».
|
||||
- Short labels, chips and HUD readouts stay short. A standard abbreviation with a period beats a wrapped line: REGOLAZ., RILEV., INCL., ALT., VEL., STIM., PROIEZ., ESP.; PREC / SUCC for PREV / NEXT and PREVIOUS / NEXT.
|
||||
- The HUD codes HDG, BRG, CRS, FOV and DEST stay in English, as in cockpit avionics. RANGE is «PORTATA». Words used in a sentence are translated: heading → «prua» (aircraft, ships) or «direzione» (camera, buses, trams, trains), course → «rotta».
|
||||
- Keyboard keys stay in English: Space, Enter, Esc, Tab, Shift, Backspace, H, 1–9 («Tieni premuto Space per parlare», «Premi Enter per riprovare»).
|
||||
- Decimal comma in numbers written in the text, magnitudes included: «28,2710° N», «M5,2». Numbers formatted by the code follow `Intl` and need nothing.
|
||||
- Amounts: thousands, millions and billions are written out or abbreviated in Italian, not with K/M/B: «{amount} mila $», «{amount} mln $», «{amount} mld $».
|
||||
- Units: km, m, km/h, m/s, kn (knots), ft (feet), h, min, s; in UPPERCASE text KM, KM/S, KT, FT, MIN, H. "m" is always metres, so minutes are «min» («5 min fa»). MW stays MW. `°` and `%` stay.
|
||||
- "ago" goes after the time: «{count} min fa». When a placeholder already holds a localized "ago" text (`{age}`, `{ago}`), do not add «fa» again.
|
||||
- A feature name used as a sentence subject gets a head noun so the verb agrees: «La modalità Missioni spaziali isola…», «Il pannello Impostazioni provider è disattivato…», «Il livello {layer} è attivo…», «Lo strumento {name} è attivo…». A short card title with a placeholder name puts the state after a colon: «{label}: attivato». The same goes for a placeholder list of names: «Attivazione recente: {layers}». A count in a plain `t()` entry (no plural forms) that can be 1 goes after a label, so nothing has to agree with it: «Record accettati: {accepted} su {raw}».
|
||||
- English loanwords that Italian UIs use as they are stay in English: feed, live, cockpit, briefing, preset, overlay, layout, snapshot, tile, provider, timeout, payload, bloom, scope.
|
||||
|
||||
## Plurals
|
||||
|
||||
A `tn()` entry needs three forms. `Intl.PluralRules('it')` picks:
|
||||
|
||||
| Form | Counts | Example |
|
||||
| ------- | -------------------------------------- | ----------------------- |
|
||||
| `one` | 1 | 1 telecamera |
|
||||
| `many` | 1 000 000, 2 000 000… (exact millions) | 1 000 000 di telecamere |
|
||||
| `other` | everything else: 0, 2–999 999, 1,5… | 3 telecamere |
|
||||
|
||||
The `many` form is the `other` text with «di» after the number when a noun follows: «{count} di telecamere caricate», «{count} DI CHIAVI IN ATTESA». When no noun follows the number («{count} vicine», «{count} RICEVUTI»), `many` repeats `other`. Nouns that do not change in the plural keep one form for all three (`{count} file`, `{count} data center`), with «di» in `many`.
|
||||
|
||||
## Glossary
|
||||
|
||||
| English | Italiano |
|
||||
| --------------------------------------------- | ------------------------------------------------------------ |
|
||||
| layer / data layer | livello / livello dati |
|
||||
| feed / source / data source | feed / fonte / fonte dati |
|
||||
| LIVE (status) / live data / live (feed state) | LIVE / dati live / live |
|
||||
| SIMULATED | SIMULATO |
|
||||
| stale / degraded / unavailable | obsoleto / degradato / non disponibile |
|
||||
| retry / retrying | riprova / nuovo tentativo |
|
||||
| rate limited | limite di richieste (raggiunto) |
|
||||
| key (API key) / POWER UP / POWERED UP | chiave / ACCENDI / ACCESO |
|
||||
| Provider Settings | Impostazioni provider |
|
||||
| ON / OFF (state chips) | ON / OFF |
|
||||
| enable / disable | attivare / disattivare |
|
||||
| globe / map / map source / map stack | globo / mappa / fonte mappa / stack mappa |
|
||||
| basemap / box (Recent Imagery area) | mappa di base / riquadro |
|
||||
| aircraft, plane / flight / military flights | aereo / volo / voli militari |
|
||||
| vessel, ship | nave |
|
||||
| satellite / shell (Starlink) / DENSE | satellite / shell / DENSO |
|
||||
| SPARSE / BALANCED / DENSE (detection modes) | RADO / BILANCIATO / DENSO (agree with «overlay») |
|
||||
| broadband (Starlink shell) | a banda larga |
|
||||
| VISUAL (CelesTrak group) | VISIBILI |
|
||||
| launch (rocket) / launch site / space mission | lancio / sito di lancio / missione spaziale |
|
||||
| ascent / replay / payload / stage | salita / replay / payload / stadio |
|
||||
| contact / tracking | contatto / tracciamento |
|
||||
| installation / site | installazione / sito |
|
||||
| mapped / observed | mappato / osservato |
|
||||
| camera (view) / camera, CCTV camera | camera / telecamera, telecamera CCTV |
|
||||
| earthquake / active fire / fire perimeter | terremoto / incendio attivo / perimetro dell'incendio |
|
||||
| lightning strike / cloud (cover, %) | fulmine / nuvole («non singoli fulmini», «20% di nuvole») |
|
||||
| incident (wildfire, Type N) / scene incident | evento (Evento di tipo N, Incendio senza nome) / evento |
|
||||
| trigger record (USGS card) / OPEN SOURCE link | scheda / APRI LA FONTE |
|
||||
| advisory (NHC/CPHC cyclone) / cone | bollettino / cono di incertezza |
|
||||
| forecast lead hours (cyclone points) | scadenze di previsione in ore |
|
||||
| AIR / SEA (radio category) / ground station | ARIA / MARE / stazione di terra |
|
||||
| radio station / broadcaster | stazione radio / emittente |
|
||||
| CH (radio channel readout) | CH (unchanged) |
|
||||
| road / flow (traffic) | strada / flusso |
|
||||
| transit / bike share | trasporto pubblico / bike sharing |
|
||||
| last report (vehicle position) | ultima segnalazione |
|
||||
| scene / shot / scene recipe | scena / scatto / ricetta della scena |
|
||||
| anchor / data pack / bundle (scene) | ancora / pacchetto dati / pacchetto |
|
||||
| story beat | tappa del racconto |
|
||||
| briefing / panel / dock | briefing / pannello / dock |
|
||||
| cockpit | cockpit |
|
||||
| preset / visual preset | preset / preset visivo |
|
||||
| Tactical / Operator / Minimal / Cyber | Tattico / Operatore / Minimale / Cyber |
|
||||
| Scope (circular viewport mask) / keyhole | scope / foro |
|
||||
| feather / fade | sfumatura / dissolvenza |
|
||||
| detection overlay | overlay di rilevamento |
|
||||
| pin (Draw tool) / mark | segnaposto / segno |
|
||||
| pin (Recent Imagery) | fissare |
|
||||
| Draw / Directions (tools) | Disegno / Indicazioni |
|
||||
| Street Level (layer) / Street View (Google) | Vista stradale / Street View |
|
||||
| Recent Imagery | Immagini recenti |
|
||||
| Global Context / Context (panel) | Contesto globale / Contesto |
|
||||
| heading, course / altitude / ground speed | prua, rotta / altitudine / velocità al suolo (VEL. AL SUOLO) |
|
||||
| altitude in short HUD captions / Alt unknown | QUOTA (QUOTA · FT) / Quota sconosciuta |
|
||||
| fallback (FALLBACK, Street View fallback) | riserva / di riserva (SUPERFICIE DI RISERVA) |
|
||||
| forecast track / advisory center (cyclones) | traccia prevista / centro del ciclone da bollettino |
|
||||
| witness source / authored (scene) | fonte testimoniale / predefinito, definito dall'autore |
|
||||
| explore manually | esplora manualmente |
|
||||
| HDG / BRG / CRS / FOV / DEST | HDG / BRG / CRS / FOV / DEST (unchanged) |
|
||||
| RANGE / PITCH / HGT (CCTV calibration) | PORTATA / INCL. / ALT. |
|
||||
| PREV / NEXT | PREC / SUCC |
|
||||
| Previous / Next (button text) | Precedente / Successivo |
|
||||
| Focus {target} (action, aria label) | Inquadra {target} (the FOCUS button stays FOCUS) |
|
||||
| L / R (relative bearing) | S / D |
|
||||
| SIG / NEWS / LOCAL (briefing tabs) | SIG / NOTIZIE / LOCALE |
|
||||
| compass points (N, NNE, …, W, NW) | N, NNE, …, O, NO (west is «O», ovest) |
|
||||
| NOT AN ALL-CLEAR | NON È UN VIA LIBERA |
|
||||
| weather field / model / valid / issued | campo / modello / valida / emessa |
|
||||
| click / click to … | fai clic / clic per … |
|
||||
@@ -440,6 +440,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -481,6 +482,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -512,6 +514,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -535,6 +538,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -553,6 +557,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -574,6 +579,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -602,6 +608,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -638,6 +645,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -656,6 +664,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -816,6 +825,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -939,6 +949,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -1008,6 +1019,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -1058,6 +1070,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -1122,6 +1135,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -1153,6 +1167,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -1185,6 +1200,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -1217,6 +1233,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -1252,6 +1269,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -1296,6 +1314,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -1334,6 +1353,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -1386,6 +1406,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -1417,6 +1438,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -1467,6 +1489,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -1532,6 +1555,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -1563,6 +1587,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -1605,6 +1630,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -1629,6 +1655,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -1687,6 +1714,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -1751,6 +1779,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -2636,6 +2665,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -3222,6 +3252,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -3334,6 +3365,7 @@
|
||||
"src/data/cameraArrival.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -3359,6 +3391,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -3397,6 +3430,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -3425,6 +3459,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -3518,6 +3553,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -3575,6 +3611,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -3831,6 +3868,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -3876,6 +3914,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -3931,6 +3970,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -3949,6 +3989,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -4010,6 +4051,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
@@ -4208,6 +4250,7 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/it.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
|
||||
@@ -37,6 +37,8 @@ test('normalizeLocale folds regional variants and rejects unshipped tags', () =>
|
||||
assert.equal(normalizeLocale('uk-UA'), 'uk');
|
||||
assert.equal(normalizeLocale('en_GB'), 'en');
|
||||
assert.equal(normalizeLocale('xx'), null);
|
||||
assert.equal(normalizeLocale('it-IT'), 'it');
|
||||
assert.equal(normalizeLocale('it_CH'), 'it');
|
||||
// Explicit script tags take precedence over regional defaults.
|
||||
assert.equal(normalizeLocale('zh'), 'zh-CN');
|
||||
assert.equal(normalizeLocale('zh-CN'), 'zh-CN');
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
writeStoredLocale,
|
||||
} from './locale.js';
|
||||
import es from './locales/es.json' with { type: 'json' };
|
||||
import it from './locales/it.json' with { type: 'json' };
|
||||
import ru from './locales/ru.json' with { type: 'json' };
|
||||
import uk from './locales/uk.json' with { type: 'json' };
|
||||
import zhCN from './locales/zh-CN.json' with { type: 'json' };
|
||||
@@ -24,6 +25,7 @@ import zhHant from './locales/zh-Hant.json' with { type: 'json' };
|
||||
|
||||
const CATALOGS = Object.freeze({
|
||||
es,
|
||||
it,
|
||||
ru,
|
||||
uk,
|
||||
'zh-CN': zhCN,
|
||||
|
||||
@@ -11,6 +11,7 @@ export const DEFAULT_LOCALE = 'en';
|
||||
export const LOCALES = Object.freeze([
|
||||
'en',
|
||||
'es',
|
||||
'it',
|
||||
'ru',
|
||||
'uk',
|
||||
'zh-CN',
|
||||
@@ -21,6 +22,7 @@ export const LOCALES = Object.freeze([
|
||||
export const LOCALE_NAMES = Object.freeze({
|
||||
en: 'English',
|
||||
es: 'Español',
|
||||
it: 'Italiano',
|
||||
ru: 'Русский',
|
||||
uk: 'Українська',
|
||||
'zh-CN': '中文(简体)',
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -124,6 +124,175 @@
|
||||
"ΔN",
|
||||
"▲ {frp} MW"
|
||||
],
|
||||
"it": [
|
||||
"1090 MHz",
|
||||
"978 MHz UAT",
|
||||
"AFRICA",
|
||||
"AIS: --",
|
||||
"AIS: {name}",
|
||||
"ALT {altitude} FT · GS {speed} KT · TRK {track}",
|
||||
"ANIME",
|
||||
"ASIA",
|
||||
"AUTO",
|
||||
"AUTO HOP OFF",
|
||||
"AUTO HOP ON",
|
||||
"Alternative",
|
||||
"Ambient",
|
||||
"Anime",
|
||||
"Area",
|
||||
"Austin",
|
||||
"BRG {value}°",
|
||||
"BRG —",
|
||||
"Bloom",
|
||||
"Blues",
|
||||
"Business jet",
|
||||
"CAL {value}",
|
||||
"CAL · {badge}",
|
||||
"CARGO",
|
||||
"CCTV",
|
||||
"CCTV OFF",
|
||||
"CCTV ON",
|
||||
"CH {index} / {total}",
|
||||
"COCKPIT",
|
||||
"CRS {value}°",
|
||||
"CRT",
|
||||
"Country",
|
||||
"Cyber",
|
||||
"DEST ---°",
|
||||
"DEST {bearing}",
|
||||
"DISPLAY",
|
||||
"Dance",
|
||||
"Drone",
|
||||
"Dubai",
|
||||
"ENE",
|
||||
"ESE",
|
||||
"FLIR",
|
||||
"FM MHZ",
|
||||
"FOCUS",
|
||||
"FOV",
|
||||
"FOV {value}°",
|
||||
"FT",
|
||||
"Folk",
|
||||
"Funk",
|
||||
"GEO",
|
||||
"GOOGLE STREET VIEW",
|
||||
"Google Street View",
|
||||
"HDG",
|
||||
"HDG {value}°",
|
||||
"Hip-Hop",
|
||||
"House",
|
||||
"Indie",
|
||||
"Ironbow",
|
||||
"Jazz",
|
||||
"KTS",
|
||||
"LIVE",
|
||||
"Landsat 8/9",
|
||||
"Layout",
|
||||
"Los Angeles",
|
||||
"MIC",
|
||||
"MIL",
|
||||
"MINI",
|
||||
"MM",
|
||||
"MONITOR",
|
||||
"MSG/S",
|
||||
"Metal",
|
||||
"NASA FIRMS · LIVE",
|
||||
"NAV",
|
||||
"NE",
|
||||
"NNE",
|
||||
"NOIR",
|
||||
"NVG",
|
||||
"New York",
|
||||
"Noir",
|
||||
"OCEANIA",
|
||||
"OFF",
|
||||
"OK",
|
||||
"ON",
|
||||
"ON/OFF",
|
||||
"Oldies",
|
||||
"PAYLOAD",
|
||||
"PLAY",
|
||||
"POS: LIVE",
|
||||
"POS: {time}Z",
|
||||
"PRECIP",
|
||||
"Pop",
|
||||
"Punk",
|
||||
"RADIO",
|
||||
"REC",
|
||||
"RESET",
|
||||
"RESET CAL",
|
||||
"Radio",
|
||||
"Reggae",
|
||||
"Rock",
|
||||
"S2 GhostMaps",
|
||||
"SAR",
|
||||
"SE",
|
||||
"SIG",
|
||||
"SNAPSHOT",
|
||||
"SQUAWK {code} · {status}",
|
||||
"SSE",
|
||||
"STANDARD",
|
||||
"STD",
|
||||
"STOP",
|
||||
"SYNC",
|
||||
"San Francisco",
|
||||
"Scope",
|
||||
"Sentinel-2",
|
||||
"Sonar",
|
||||
"Soul",
|
||||
"TEMP",
|
||||
"TIMEOUT",
|
||||
"Talk",
|
||||
"Tallinn",
|
||||
"Techno",
|
||||
"Tokyo",
|
||||
"Tram",
|
||||
"Trance",
|
||||
"UPSTREAM",
|
||||
"USB {rate} msg/s",
|
||||
"Upstream HTTP {status}",
|
||||
"V/S {rate} FPM",
|
||||
"VIEWSHED",
|
||||
"VIEWSHED ON",
|
||||
"VOLUME",
|
||||
"VS",
|
||||
"WHOT/BHOT",
|
||||
"Washington DC",
|
||||
"World",
|
||||
"audio {state}",
|
||||
"dB",
|
||||
"feed status|live",
|
||||
"ft",
|
||||
"hPa",
|
||||
"installation class|BASE",
|
||||
"km",
|
||||
"km/h",
|
||||
"max {value} MW",
|
||||
"min",
|
||||
"mini",
|
||||
"mph",
|
||||
"radio|TALK",
|
||||
"satellite",
|
||||
"standard",
|
||||
"view band|METRO",
|
||||
"voice|OFF",
|
||||
"{count} MSGS",
|
||||
"{count} feeds live",
|
||||
"{count} scenes",
|
||||
"{rate} msg/s",
|
||||
"{scene} / {pack}: {status}. File: {path}. {attribution} · {license}",
|
||||
"{source} HTTP {status}",
|
||||
"{speed} kn",
|
||||
"{speed}KT",
|
||||
"{value} KM",
|
||||
"{value} KM DOWNRANGE",
|
||||
"{value} KM/H",
|
||||
"{value} KM/S",
|
||||
"{value} conf",
|
||||
"ΔE",
|
||||
"ΔN",
|
||||
"▲ {frp} MW"
|
||||
],
|
||||
"ru": [
|
||||
"AIS: --",
|
||||
"AIS: {name}",
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { SCENE_RECIPES, displayShotTitle } from './recipes.js';
|
||||
import { setLocale } from '../i18n/index.js';
|
||||
|
||||
test('an operator-typed title in the built-in Nepal scene stays as typed', () => {
|
||||
const scene = SCENE_RECIPES.find((r) => r.id === 'bhote-koshi-nepal-scene');
|
||||
assert.ok(scene, 'built-in bootstrap scene exists');
|
||||
for (const locale of ['ru', 'es', 'it', 'zh-CN', 'zh-Hant']) {
|
||||
setLocale(locale);
|
||||
const title = 'Global Incident Context';
|
||||
// A shot captured by the operator: no sourcePackId, renamed to this title.
|
||||
const shown = displayShotTitle(
|
||||
title,
|
||||
{ id: 'shot-user', title },
|
||||
{ id: scene.id },
|
||||
);
|
||||
assert.equal(shown, title, `${locale}: got ${shown}`);
|
||||
}
|
||||
});
|
||||
@@ -712,6 +712,10 @@ test('a legacy three-shot Nepal browser project bootstraps to the current 25-sho
|
||||
assert.deepEqual(scene.shots.slice(0, 3).map(({ camera }) => camera), originalCameras);
|
||||
assert.equal(scene.appliedShotPacks[0].id, 'bhote-koshi-nepal-evidence-pack');
|
||||
assert.equal(scene.appliedShotPacks[0].version, 18);
|
||||
// displayShotTitle trusts these bindings to localize the bootstrap shots.
|
||||
for (const shot of scene.shots.slice(0, 8)) {
|
||||
assert.equal(scene.appliedShotPacks[0].shotBindings[shot.title], shot.id);
|
||||
}
|
||||
assert.equal(director._selectedSceneId, scene.id);
|
||||
assert.equal(director._selectedShotId, scene.shots[0].id);
|
||||
} finally {
|
||||
|
||||
+28
-9
@@ -1061,25 +1061,44 @@ export function displaySceneTitle(scene) {
|
||||
* (`sourcePackId`), so a typed title that matches a built-in one stays as
|
||||
* typed. Without `shot`, the title alone decides.
|
||||
* @param {string} title
|
||||
* @param {{sourcePackId?: string}} [shot]
|
||||
* @param {{id?: string}} [scene] The shot's scene, for projects saved before
|
||||
* shots recorded their source pack.
|
||||
* @param {{id?: string, sourcePackId?: string}} [shot]
|
||||
* @param {{id?: string, appliedShotPacks?: Array<{id?: string, shotBindings?: Object<string, string>}>}} [scene]
|
||||
* The shot's scene, for projects saved before shots recorded their source
|
||||
* pack.
|
||||
* @returns {string}
|
||||
*/
|
||||
export function displayShotTitle(title, shot = undefined, scene = undefined) {
|
||||
const text = typeof title === 'string' ? title : '';
|
||||
const fromRecipe = shot
|
||||
? Boolean(shot.sourcePackId) || recipeDeclaresShotTitle(scene?.id, text)
|
||||
? Boolean(shot.sourcePackId) || recipeDeclaresShotTitle(scene, shot, text)
|
||||
: true;
|
||||
if (fromRecipe && BUILTIN_SHOT_TITLES.has(text)) return t(text);
|
||||
const numbered = /^Shot ([1-9]\d*)$/.exec(text);
|
||||
return numbered ? t('Shot {n}', { n: numbered[1] }) : text;
|
||||
}
|
||||
|
||||
/** Whether the built-in recipe behind a scene declares a shot with this title. */
|
||||
function recipeDeclaresShotTitle(sceneId, title) {
|
||||
const recipe = sceneId ? getSceneRecipeById(sceneId) : null;
|
||||
return Boolean(
|
||||
recipe?.cameraPath?.some((keyframe) => keyframe?.title === title),
|
||||
/** Whether the built-in recipe behind a scene declares this shot's title. */
|
||||
function recipeDeclaresShotTitle(scene, shot, title) {
|
||||
const recipe = scene?.id ? getSceneRecipeById(scene.id) : null;
|
||||
if (!recipe) return false;
|
||||
const declares = (path) =>
|
||||
Array.isArray(path) && path.some((keyframe) => keyframe?.title === title);
|
||||
if (declares(recipe.cameraPath)) return true;
|
||||
// A bootstrap recipe seeds legacy "Shot N" keyframes; the upgrade replaces
|
||||
// them with the append recipe's bootstrap shots, which carry no source pack.
|
||||
// The pack marker binds each of those titles to its shot id, so a shot the
|
||||
// operator later gave the same title stays as typed.
|
||||
return SCENE_APPEND_RECIPES.some(
|
||||
(append) =>
|
||||
append.legacySceneBootstrap?.targetSceneTitle === recipe.title &&
|
||||
declares(append.legacySceneBootstrap.cameraPath) &&
|
||||
Boolean(shot?.id) &&
|
||||
(Array.isArray(scene.appliedShotPacks)
|
||||
? scene.appliedShotPacks
|
||||
: []
|
||||
).some(
|
||||
(marker) =>
|
||||
marker?.id === append.id && marker.shotBindings?.[title] === shot.id,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import {
|
||||
SCENE_APPEND_RECIPES,
|
||||
SCENE_RECIPES,
|
||||
displaySceneTitle,
|
||||
displayShotTitle,
|
||||
@@ -141,3 +142,40 @@ test('the scene context of a shot carries its display title by provenance', asyn
|
||||
title,
|
||||
);
|
||||
});
|
||||
|
||||
test('bootstrap shots of a built-in scene localize without a source pack', () => {
|
||||
setLocale('ru');
|
||||
const append = SCENE_APPEND_RECIPES.find(
|
||||
(recipe) => recipe.legacySceneBootstrap?.cameraPath?.length,
|
||||
);
|
||||
if (!append) return;
|
||||
const bootstrap = append.legacySceneBootstrap;
|
||||
const scene = SCENE_RECIPES.find(
|
||||
(recipe) => recipe.title === bootstrap.targetSceneTitle,
|
||||
);
|
||||
if (!scene) return;
|
||||
for (const { title } of bootstrap.cameraPath) {
|
||||
// The upgrade copies these keyframes into the scene without sourcePackId
|
||||
// and binds each title to its shot in the pack marker.
|
||||
const upgraded = {
|
||||
id: scene.id,
|
||||
appliedShotPacks: [
|
||||
{ id: append.id, version: 1, shotBindings: { [title]: 'shot-old' } },
|
||||
],
|
||||
};
|
||||
assert.equal(
|
||||
displayShotTitle(title, { id: 'shot-old', title }, upgraded),
|
||||
displayShotTitle(title),
|
||||
);
|
||||
// Another shot the operator gave the same title stays as typed.
|
||||
assert.equal(
|
||||
displayShotTitle(title, { id: 'shot-user', title }, upgraded),
|
||||
title,
|
||||
);
|
||||
// The same title in an operator's own scene stays as typed.
|
||||
assert.equal(
|
||||
displayShotTitle(title, { id: 'shot-user', title }, { id: 'scene-user' }),
|
||||
title,
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -40,7 +40,7 @@ test('the current language comes first, the rest by their own names', () => {
|
||||
nativeNames: LOCALE_NAMES,
|
||||
}),
|
||||
),
|
||||
['ru', 'uk', 'en', 'es', 'zh-CN', 'zh-Hant'],
|
||||
['ru', 'uk', 'en', 'es', 'it', 'zh-CN', 'zh-Hant'],
|
||||
);
|
||||
assert.deepEqual(
|
||||
codes(
|
||||
@@ -50,7 +50,7 @@ test('the current language comes first, the rest by their own names', () => {
|
||||
nativeNames: LOCALE_NAMES,
|
||||
}),
|
||||
),
|
||||
['es', 'en', 'ru', 'uk', 'zh-CN', 'zh-Hant'],
|
||||
['es', 'en', 'it', 'ru', 'uk', 'zh-CN', 'zh-Hant'],
|
||||
);
|
||||
assert.deepEqual(
|
||||
codes(
|
||||
@@ -60,7 +60,7 @@ test('the current language comes first, the rest by their own names', () => {
|
||||
nativeNames: LOCALE_NAMES,
|
||||
}),
|
||||
),
|
||||
['en', 'es', 'ru', 'uk', 'zh-CN', 'zh-Hant'],
|
||||
['en', 'es', 'it', 'ru', 'uk', 'zh-CN', 'zh-Hant'],
|
||||
);
|
||||
const rows = buildLocaleOptions({
|
||||
locales: MANY,
|
||||
|
||||
Reference in new issue
Block a user