i18n(he): complete and ship Hebrew

- Add the full catalog (2940 entries keyed by the English text; 75 plural
  entries carry one/two/other per Intl.PluralRules('he')), reviewed for
  meaning, terms, separators and label length.
- Register he: he-IL and he map to it; it joins RTL_LOCALES so <html
  dir=rtl> mirrors the chrome through logical properties and rtl.css.
- rtl.css: Noto Sans Hebrew (loaded with the other Google fonts) leads the
  mono and sans stacks, tracking released like Arabic, HUDs keep JetBrains
  Mono for Latin telemetry.
- same-as-english: instrument codes and HUD readouts stay as printed
  (HDG/BRG/CRS/FOV/CRT/NVG/FLIR/CAL/SRC, MW units, Sentinel-2, km/h...).
- docs/translations/he.md: style, plurals and glossary.
- docs: TRANSLATORS.md and CURRENT-STATE.md updated; normalizeLocale test
  covers he-IL.
This commit is contained in:
Meir Michanie committed 2026-11-09 14:41:36 -06:00
1 parent e5da073d33
commit 584732c3ec
12 files changed
+3509 -15

No files matched your search

+3
View File
@@ -41,6 +41,9 @@ test('normalizeLocale folds regional variants and rejects unshipped tags', () =>
assert.equal(normalizeLocale('de'), 'de');
assert.equal(normalizeLocale('de-AT'), 'de');
assert.equal(normalizeLocale('de_CH'), 'de');
// Hebrew keeps Western digits like its catalog; regional tags fold to he.
assert.equal(normalizeLocale('he'), 'he');
assert.equal(normalizeLocale('he-IL'), 'he');
assert.equal(normalizeLocale('fr'), 'fr');
assert.equal(normalizeLocale('fr-CA'), 'fr');
assert.equal(normalizeLocale('FR_be'), 'fr');
+2
View File
@@ -22,6 +22,7 @@ import ar from './locales/ar.json' with { type: 'json' };
import de from './locales/de.json' with { type: 'json' };
import es from './locales/es.json' with { type: 'json' };
import fr from './locales/fr.json' with { type: 'json' };
import he from './locales/he.json' with { type: 'json' };
import it from './locales/it.json' with { type: 'json' };
import ja from './locales/ja.json' with { type: 'json' };
import pt from './locales/pt.json' with { type: 'json' };
@@ -35,6 +36,7 @@ const CATALOGS = Object.freeze({
de,
es,
fr,
he,
it,
ja,
pt,
+4 -2
View File
@@ -14,6 +14,7 @@ export const LOCALES = Object.freeze([
'de',
'es',
'fr',
'he',
'it',
'ja',
'pt',
@@ -30,6 +31,7 @@ export const LOCALE_NAMES = Object.freeze({
de: 'Deutsch',
es: 'Español',
fr: 'Français',
he: 'עברית',
it: 'Italiano',
ja: '日本語',
pt: 'Português (Brasil)',
@@ -40,7 +42,7 @@ export const LOCALE_NAMES = Object.freeze({
});
/** Right-to-left locales. */
const RTL_LOCALES = Object.freeze(['ar']);
const RTL_LOCALES = Object.freeze(['ar', 'he']);
/** Whether a shipped locale reads right to left. */
export function isRtlLocale(locale) {
@@ -62,7 +64,7 @@ export function intlLocale(locale) {
/**
* Map any language tag to a shipped locale: 'ru-RU' and 'RU' → 'ru',
* 'ar-EG' and 'ar-SA' → 'ar',
* 'ar-EG' and 'ar-SA' → 'ar', 'he-IL' and 'he' → 'he',
* 'de-AT' and 'de_CH' → 'de', 'ja-JP' → 'ja',
* 'pt-BR' and 'pt-PT' → 'pt' (Brazilian Portuguese),
* 'zh', 'zh-CN' and 'zh-Hans-SG' → 'zh-CN';
File diff suppressed because it is too large. Load diff
+89
View File
@@ -1189,5 +1189,94 @@
"acartia.io ↗",
"iNaturalist ↗",
"iNaturalist"
],
"he": [
"{speed} kn",
"GEO",
"SAR",
"EARTH WATCH",
"CRT",
"NVG",
"FLIR",
"AIS: --",
"REC",
"HDG {value}°",
"BRG {value}°",
"BRG —",
"CRS {value}°",
"FOV {value}°",
"CAL {value}",
"SRC {value}",
"▲ {frp} MW",
"≥ {value} MW",
"Intel Picture",
"Kinetic 30d",
"Border Crisis",
"Iran Picture",
"Iran Kinetic",
"Yemen FLOT",
"Latest Drop",
"1090 MHz",
"978 MHz UAT",
"ALT {altitude} FT · GS {speed} KT · TRK {track}",
"V/S {rate} FPM",
"Sentinel-2",
"Landsat 8/9",
"Google Street View",
"GOOGLE STREET VIEW",
"AIS: {name}",
"{type} SPD: {speed} HDG: {heading}",
"POS: {time}Z",
"{source} HTTP {status}",
"NNE",
"NE",
"ENE",
"ESE",
"SE",
"SSE",
"SSW",
"SW",
"WSW",
"WNW",
"NW",
"NNW",
"WHOT/BHOT",
"HDG",
"FOV",
"HGT",
"ΔN",
"ΔE",
"CAL · {badge}",
"DEST ---°",
"SQUAWK {code} · {status}",
"DEST {bearing}",
"Space Missions",
"CCTV",
"S2 GhostMaps",
"Street Level",
"STREET LEVEL",
"km/h",
"mph",
"hPa",
"kt",
"kts",
"ft",
"km",
"min",
"dB",
"LEVEL",
"KTS",
"FT",
"FLT",
"MIL",
"WX",
"MSG/S",
"FM MHZ",
"Acartia",
"CC BY 4.0",
"Salish Sea",
"iNaturalist",
"Be Whale Wise",
"MIC"
]
}
+4
View File
@@ -52,6 +52,7 @@ test('the current language comes first, the rest by their own names', () => {
'fr',
'it',
'pt',
'he',
'ar',
'zh-CN',
'zh-Hant',
@@ -75,6 +76,7 @@ test('the current language comes first, the rest by their own names', () => {
'pt',
'ru',
'uk',
'he',
'ar',
'zh-CN',
'zh-Hant',
@@ -98,6 +100,7 @@ test('the current language comes first, the rest by their own names', () => {
'pt',
'ru',
'uk',
'he',
'ar',
'zh-CN',
'zh-Hant',
@@ -122,6 +125,7 @@ test('the current language comes first, the rest by their own names', () => {
'pt',
'ru',
'uk',
'he',
'zh-CN',
'zh-Hant',
'ja',
+37
View File
@@ -60,6 +60,43 @@ html:lang(ar) :is(#intel-hud, #cockpit-hud) {
Tahoma, monospace;
}
/* Hebrew: JetBrains Mono and Inter also lack Hebrew glyphs. Noto Sans Hebrew
(loaded with the other Google fonts; its files download only when the page
shows Hebrew) leads the stack so the body text keeps one proportional
Hebrew face across platforms. Hebrew letters do not join like Arabic, but
tracking still reads as gaps inside a word, so the body is released from it
the same way; Latin marked translate="no" keeps its tracking. The intel and
cockpit HUDs keep JetBrains Mono for their Latin telemetry. */
html:lang(he) {
--font-mono:
'Noto Sans Hebrew', 'JetBrains Mono', 'SF Mono', 'Fira Code', Tahoma,
monospace;
--font-sans:
'Inter', 'Noto Sans Hebrew', -apple-system, BlinkMacSystemFont, Tahoma,
sans-serif;
}
html:lang(he)
:is(
#military-awareness-panel,
.military-awareness-controls button,
.military-awareness-standby button,
.military-awareness-compass-label,
.military-awareness-compass-heading,
.military-awareness-direction-label,
.tr3b-toggle
) {
font-family: var(--font-mono);
}
html:lang(he) :is(#intel-hud, #cockpit-hud) {
--font-mono:
'JetBrains Mono', 'SF Mono', 'Fira Code', 'Noto Sans Hebrew', Tahoma,
monospace;
}
html:lang(he) body
:not(#intel-hud, #intel-hud *, [translate='no'], [translate='no'] *) {
letter-spacing: 0 !important;
}
/* The Display/Context rail moves to the left edge. It keeps a physical
`right` in layers.css because the attribution-clearance model
(creditAttribution.test.mjs) reads that rule; the swap only applies above