mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 02:44:02 +00:00
i18n(de): complete and ship German
- Translate the remaining 1,868 strings in a neutral infinitive style (no Sie/du), with German compounds, decimal commas and CLDR one/other plurals. - Review the #908 entries: knots are kn everywhere, the avionics codes BRG, CRS and DEST stay English like HDG and SPD, expand/collapse is ausklappen/einklappen, the radio toggle reads EIN/AUS, density SPARSE is DÜNN, the Thermal style is Wärmebild, German quotes in the shot prompt. - Register de: catalog, LOCALES, LOCALE_NAMES (Deutsch), package boundaries, same-as-english (codes, product names, music genres, units). - Tests: picker order and keyboard model with Deutsch, de/de-AT/de_CH normalization, browser de-DE resolves to German. - docs/translations/de.md: style, plurals, units and glossary; TRANSLATORS.md and CURRENT-STATE.md list German. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
7d7c535dc7
commit
dfa7ac98a7
10 files changed
+2409
-30
No files matched your search
+13
-2
@@ -34,6 +34,9 @@ test('normalizeLocale folds regional variants and rejects unshipped tags', () =>
|
||||
assert.equal(normalizeLocale('ru-RU'), 'ru');
|
||||
assert.equal(normalizeLocale(' RU '), 'ru');
|
||||
assert.equal(normalizeLocale('en_GB'), 'en');
|
||||
assert.equal(normalizeLocale('de'), 'de');
|
||||
assert.equal(normalizeLocale('de-AT'), 'de');
|
||||
assert.equal(normalizeLocale('de_CH'), 'de');
|
||||
assert.equal(normalizeLocale('xx'), null);
|
||||
// Explicit script tags take precedence over regional defaults.
|
||||
assert.equal(normalizeLocale('zh'), 'zh-CN');
|
||||
@@ -68,7 +71,7 @@ test('resolveLocale: ?lang= wins, then the stored choice, then browser languages
|
||||
resolveLocale({
|
||||
location: { search: '?lang=xx' },
|
||||
storage: memoryStorage(),
|
||||
languages: ['de-DE', 'ru-RU'],
|
||||
languages: ['ko-KR', 'ru-RU'],
|
||||
}),
|
||||
'ru',
|
||||
);
|
||||
@@ -76,7 +79,15 @@ test('resolveLocale: ?lang= wins, then the stored choice, then browser languages
|
||||
resolveLocale({
|
||||
location: { search: '' },
|
||||
storage: memoryStorage(),
|
||||
languages: ['de'],
|
||||
languages: ['de-DE', 'ru-RU'],
|
||||
}),
|
||||
'de',
|
||||
);
|
||||
assert.equal(
|
||||
resolveLocale({
|
||||
location: { search: '' },
|
||||
storage: memoryStorage(),
|
||||
languages: ['ko'],
|
||||
}),
|
||||
'en',
|
||||
);
|
||||
|
||||
+8
-1
@@ -16,12 +16,19 @@ import {
|
||||
normalizeLocale,
|
||||
writeStoredLocale,
|
||||
} from './locale.js';
|
||||
import de from './locales/de.json' with { type: 'json' };
|
||||
import es from './locales/es.json' with { type: 'json' };
|
||||
import ru from './locales/ru.json' with { type: 'json' };
|
||||
import zhCN from './locales/zh-CN.json' with { type: 'json' };
|
||||
import zhHant from './locales/zh-Hant.json' with { type: 'json' };
|
||||
|
||||
const CATALOGS = Object.freeze({ es, ru, 'zh-CN': zhCN, 'zh-Hant': zhHant });
|
||||
const CATALOGS = Object.freeze({
|
||||
de,
|
||||
es,
|
||||
ru,
|
||||
'zh-CN': zhCN,
|
||||
'zh-Hant': zhHant,
|
||||
});
|
||||
|
||||
let currentLocale = DEFAULT_LOCALE;
|
||||
|
||||
|
||||
+10
-1
@@ -8,11 +8,19 @@ export const LOCALE_STORAGE_KEY = 'gev:locale:v1';
|
||||
export const DEFAULT_LOCALE = 'en';
|
||||
|
||||
/** Shipped locales; order does not matter. Adding one: docs/TRANSLATORS.md. */
|
||||
export const LOCALES = Object.freeze(['en', 'es', 'ru', 'zh-CN', 'zh-Hant']);
|
||||
export const LOCALES = Object.freeze([
|
||||
'en',
|
||||
'de',
|
||||
'es',
|
||||
'ru',
|
||||
'zh-CN',
|
||||
'zh-Hant',
|
||||
]);
|
||||
|
||||
/** Each locale named in its own language, for the selector. */
|
||||
export const LOCALE_NAMES = Object.freeze({
|
||||
en: 'English',
|
||||
de: 'Deutsch',
|
||||
es: 'Español',
|
||||
ru: 'Русский',
|
||||
'zh-CN': '中文(简体)',
|
||||
@@ -24,6 +32,7 @@ const RTL_LOCALES = Object.freeze([]);
|
||||
|
||||
/**
|
||||
* Map any language tag to a shipped locale: 'ru-RU' and 'RU' → 'ru',
|
||||
* 'de-AT' and 'de_CH' → 'de',
|
||||
* 'zh', 'zh-CN' and 'zh-Hans-SG' → 'zh-CN';
|
||||
* 'zh-Hant', 'zh-TW', 'zh-HK' and 'zh-MO' → 'zh-Hant'.
|
||||
* @returns {string|null}
|
||||
|
||||
+2072
-18
File diff suppressed because it is too large.
Load diff
@@ -1,4 +1,168 @@
|
||||
{
|
||||
"de": [
|
||||
"1090 MHz",
|
||||
"978 MHz UAT",
|
||||
"AIS: --",
|
||||
"AIS: {name}",
|
||||
"ANIME",
|
||||
"AUTO",
|
||||
"Alternative",
|
||||
"Ambient",
|
||||
"Anime",
|
||||
"Austin",
|
||||
"BRG {value}°",
|
||||
"BRG —",
|
||||
"Bloom",
|
||||
"Blues",
|
||||
"Bus",
|
||||
"CCTV",
|
||||
"COCKPIT",
|
||||
"CRS {value}°",
|
||||
"CRT",
|
||||
"Country",
|
||||
"Cyan",
|
||||
"Cyber",
|
||||
"DEST ---°",
|
||||
"DEST {bearing}",
|
||||
"Dance",
|
||||
"Details",
|
||||
"Dubai",
|
||||
"FALLBACK",
|
||||
"FILTER",
|
||||
"FLIR",
|
||||
"FOV",
|
||||
"FOV {value}°",
|
||||
"FT",
|
||||
"Folk",
|
||||
"Funk",
|
||||
"GEO",
|
||||
"GOOGLE STREET VIEW",
|
||||
"Global",
|
||||
"Global · 60°S–60°N",
|
||||
"Google Street View",
|
||||
"HDG",
|
||||
"HDG {value}°",
|
||||
"Hip-Hop",
|
||||
"House",
|
||||
"Hybrid",
|
||||
"Indie",
|
||||
"Ironbow",
|
||||
"Jazz",
|
||||
"LIVE",
|
||||
"Landsat 8/9",
|
||||
"Latin",
|
||||
"Layout",
|
||||
"London",
|
||||
"Los Angeles",
|
||||
"MIL",
|
||||
"MINI",
|
||||
"MISSION",
|
||||
"MM",
|
||||
"MONITOR",
|
||||
"Metal",
|
||||
"Minimal",
|
||||
"NAME",
|
||||
"NASA FIRMS · LIVE",
|
||||
"NAV",
|
||||
"NEWS",
|
||||
"NNW",
|
||||
"NOIR",
|
||||
"NORMAL",
|
||||
"NVG",
|
||||
"NW",
|
||||
"New York",
|
||||
"Noir",
|
||||
"Normal",
|
||||
"OK",
|
||||
"ORBIT",
|
||||
"ORBIT · ",
|
||||
"Oldies",
|
||||
"OpenStreetMap + optional Google Maps Places",
|
||||
"Operator",
|
||||
"PAUSE",
|
||||
"PLAY",
|
||||
"POS: LIVE",
|
||||
"POS: {time}Z",
|
||||
"POSITION {age}",
|
||||
"PROJ MONITOR",
|
||||
"Paris",
|
||||
"Pause",
|
||||
"Pin",
|
||||
"Plan",
|
||||
"Pop",
|
||||
"Punk",
|
||||
"RADIO",
|
||||
"REC",
|
||||
"REGION",
|
||||
"RESET",
|
||||
"Radio",
|
||||
"Reggae",
|
||||
"Rock",
|
||||
"S2 GhostMaps",
|
||||
"SAR",
|
||||
"SIG",
|
||||
"SNAPSHOT",
|
||||
"SQUAWK {code} · {status}",
|
||||
"SSW",
|
||||
"STANDARD",
|
||||
"START",
|
||||
"STATION",
|
||||
"STATUS",
|
||||
"STATUS · ",
|
||||
"STD",
|
||||
"SW",
|
||||
"SYNC",
|
||||
"San Francisco",
|
||||
"Scanlines",
|
||||
"Scope",
|
||||
"Sentinel-2",
|
||||
"Sonar",
|
||||
"Soul",
|
||||
"Station",
|
||||
"Station {id}",
|
||||
"T minus {seconds}",
|
||||
"TAGS · {tags}",
|
||||
"TANKER",
|
||||
"TEMP",
|
||||
"Talk",
|
||||
"Tallinn",
|
||||
"Techno",
|
||||
"Trance",
|
||||
"Turboprop",
|
||||
"UPSTREAM",
|
||||
"Upstream HTTP {status}",
|
||||
"Vignette",
|
||||
"WHOT/BHOT",
|
||||
"WIND",
|
||||
"WNW",
|
||||
"WSW",
|
||||
"Washington DC",
|
||||
"Wind",
|
||||
"dB",
|
||||
"feed status|live",
|
||||
"fire behavior|Minimal",
|
||||
"ft",
|
||||
"hPa",
|
||||
"km",
|
||||
"km/h",
|
||||
"min",
|
||||
"mph",
|
||||
"radio|NEWS",
|
||||
"radio|TALK",
|
||||
"view band|GLOBAL",
|
||||
"{city} CCTV",
|
||||
"{county} County",
|
||||
"{source} HTTP {status}",
|
||||
"{speed} kn",
|
||||
"{type} SPD: {speed} HDG: {heading}",
|
||||
"{value} KM",
|
||||
"{value} KM DOWNRANGE",
|
||||
"{value} KM/H",
|
||||
"{value} KM/S",
|
||||
"ΔN",
|
||||
"Ⅱ PAUSE",
|
||||
"▲ {frp} MW"
|
||||
],
|
||||
"es": [
|
||||
"1090 MHz",
|
||||
"978 MHz UAT",
|
||||
|
||||
@@ -40,7 +40,7 @@ test('the current language comes first, the rest by their own names', () => {
|
||||
nativeNames: LOCALE_NAMES,
|
||||
}),
|
||||
),
|
||||
['ru', 'en', 'es', 'zh-CN', 'zh-Hant'],
|
||||
['ru', 'de', 'en', 'es', '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', 'zh-CN', 'zh-Hant'],
|
||||
['es', 'de', 'en', 'ru', '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', 'zh-CN', 'zh-Hant'],
|
||||
['en', 'de', 'es', 'ru', 'zh-CN', 'zh-Hant'],
|
||||
);
|
||||
const rows = buildLocaleOptions({
|
||||
locales: MANY,
|
||||
@@ -95,6 +95,7 @@ test('each row gets the interface-language name for search, unless it only repea
|
||||
const byCode = Object.fromEntries(rows.map((row) => [row.code, row]));
|
||||
assert.equal(byCode.en.localName, 'английский');
|
||||
assert.equal(byCode.es.localName, 'испанский');
|
||||
assert.equal(byCode.de.localName, 'немецкий');
|
||||
assert.equal(byCode.ru.localName, '', 'русский only repeats Русский');
|
||||
const english = buildLocaleOptions({
|
||||
locales: LOCALES,
|
||||
@@ -261,7 +262,7 @@ test('keyboard model: open on the current language, move, choose, close', () =>
|
||||
model.openList();
|
||||
assert.equal(model.activeOption.code, 'ru');
|
||||
assert.deepEqual(model.keydown({ key: 'ArrowDown' }), { handled: true });
|
||||
assert.equal(model.activeOption.code, 'en');
|
||||
assert.equal(model.activeOption.code, 'de');
|
||||
model.keydown({ key: 'End' });
|
||||
assert.equal(model.activeOption.code, 'zh-Hant');
|
||||
model.keydown({ key: 'Home' });
|
||||
@@ -275,7 +276,7 @@ test('keyboard model: open on the current language, move, choose, close', () =>
|
||||
model.keydown({ key: 'ArrowDown' });
|
||||
assert.deepEqual(model.keydown({ key: ' ' }), {
|
||||
handled: true,
|
||||
choose: 'en',
|
||||
choose: 'de',
|
||||
});
|
||||
assert.deepEqual(model.keydown({ key: 'Escape' }), {
|
||||
handled: true,
|
||||
|
||||
Reference in new issue
Block a user