mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 19:04:02 +00:00
i18n: fix the final pre-merge review findings
Fixes the P1 and the confirmed P2 findings of the final review of the
i18n branch.
- CI: the air-quality model no longer imports i18n (portable module),
so check:boundaries passes again.
- Air quality layer: named in the layer panel, HUD and voice; its
Weather card, bands and errors translate. A test keeps the layer-name
table in step with the layer registry.
- Layer statuses that still reached ru/es in English: GNSS, CelesTrak,
Transit loading and feed errors, launches, perimeters, ALPR, ECCC,
wind "field unavailable", Local ADS-B streaming lines.
- Voice and HUD: preset ids show city names ("Fly to San Francisco"),
the HUD city translates, cockpit refusals cover frame, aircraft and
satellite; empty Transit notes translate on the analyst card.
- tn() keeps a caller's formatted count ("12,345 map objects").
- Cyber HUD: the language button no longer covers the bottom-right
card or the cockpit position readout.
- CCTV buttons wrap only in non-English locales; English layout matches
main again.
- Switching language asks first when Provider Settings holds unsaved
keys.
Catalogs: es/ru 2611 messages each, complete.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
fada8b161b
commit
3adce33cc6
43 files changed
+1350
-97
No files matched your search
@@ -234,3 +234,15 @@ test('persistLocaleAndReload stores the choice, keeps the hash, strips ?lang', (
|
||||
['reload'],
|
||||
]);
|
||||
});
|
||||
|
||||
test('tn() shows a caller-formatted count and still picks the form by number', () => {
|
||||
const label = (n) =>
|
||||
tn(n, '{count} map object', '{count} map objects', {
|
||||
count: formatNumber(n),
|
||||
});
|
||||
assert.equal(label(12345), '12,345 map objects');
|
||||
assert.equal(tn(12345, '{count} file', '{count} files'), '12345 files');
|
||||
setLocale('ru');
|
||||
assert.equal(label(12345), '12 345 объектов на карте');
|
||||
assert.equal(label(1), '1 объект на карте');
|
||||
});
|
||||
+5
-3
@@ -94,11 +94,13 @@ export function t(text, values) {
|
||||
/**
|
||||
* Translate a count-dependent message. The catalog entry for `other` holds
|
||||
* one form per Intl.PluralRules category of the locale ({ one, few, many, other }
|
||||
* for Russian). `{count}` is always available to the pattern.
|
||||
* for Russian). `{count}` is always available to the pattern: the raw number,
|
||||
* unless `values.count` gives its display text, e.g. `formatNumber(n)`.
|
||||
* @param {number} count
|
||||
* @param {string} one English singular, e.g. '{count} camera'
|
||||
* @param {string} other English plural, e.g. '{count} cameras'
|
||||
* @param {object} [values] Extra interpolation values
|
||||
* @param {object} [values] Extra interpolation values; a `count` here is
|
||||
* shown in place of the number (the plural form still follows `count`)
|
||||
* @returns {string}
|
||||
*/
|
||||
export function tn(count, one, other, values) {
|
||||
@@ -111,7 +113,7 @@ export function tn(count, one, other, values) {
|
||||
} else {
|
||||
pattern = pluralRules(DEFAULT_LOCALE).select(count) === 'one' ? one : other;
|
||||
}
|
||||
return interpolate(pattern, { ...values, count });
|
||||
return interpolate(pattern, { count, ...values });
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2796,5 +2796,86 @@
|
||||
},
|
||||
"FOV {value}° (DATASHEET)": "FOV {value}° (FICHA TÉCNICA)",
|
||||
"SPEC FOV {span}° (CAPABILITY)": "FOV DE FICHA {span}° (CAPACIDAD)",
|
||||
"{spec} · SPECS: CCTV-DATABASE.COM": "{spec} · ESPECIFICACIONES: CCTV-DATABASE.COM"
|
||||
"{spec} · SPECS: CCTV-DATABASE.COM": "{spec} · ESPECIFICACIONES: CCTV-DATABASE.COM",
|
||||
"Canada only · ECCC AQHI network": "Solo Canadá · red AQHI de ECCC",
|
||||
"{count} stations reporting": {
|
||||
"one": "{count} estación con datos",
|
||||
"many": "{count} de estaciones con datos",
|
||||
"other": "{count} estaciones con datos"
|
||||
},
|
||||
"No readings available": "No hay lecturas disponibles",
|
||||
"No current readings": "Sin lecturas actuales",
|
||||
"Low health risk": "Riesgo bajo para la salud",
|
||||
"Moderate health risk": "Riesgo moderado para la salud",
|
||||
"High health risk": "Riesgo alto para la salud",
|
||||
"Very High health risk": "Riesgo muy alto para la salud",
|
||||
"Air quality request failed": "Falló la petición de calidad del aire",
|
||||
"ECCC request timed out": "La petición a ECCC agotó el tiempo de espera",
|
||||
"Malformed AQHI station response": "Respuesta de estaciones AQHI mal formada",
|
||||
"Malformed AQHI observation response": "Respuesta de observaciones AQHI mal formada",
|
||||
"No AQHI stations available": "No hay estaciones AQHI disponibles",
|
||||
"{n} air quality": {
|
||||
"one": "{n} estación de calidad del aire",
|
||||
"many": "{n} de estaciones de calidad del aire",
|
||||
"other": "{n} estaciones de calidad del aire"
|
||||
},
|
||||
"GNSS feed rate limited (HTTP 429)": "Fuente GNSS: límite de peticiones superado (HTTP 429)",
|
||||
"GNSS feed unavailable (HTTP {status})": "Fuente GNSS no disponible (HTTP {status})",
|
||||
"GNSS snapshot needs a view anchor": "La instantánea GNSS necesita un punto de referencia de la vista",
|
||||
"Malformed GNSS integrity snapshot": "Instantánea de integridad GNSS mal formada",
|
||||
"GNSS integrity snapshot has no observation time": "La instantánea de integridad GNSS no tiene hora de observación",
|
||||
"{feed} feed unavailable": "Fuente {feed} no disponible",
|
||||
"Loading {feeds}": "Cargando {feeds}",
|
||||
"{count} CelesTrak groups unavailable": {
|
||||
"one": "{count} grupo de CelesTrak no disponible",
|
||||
"many": "{count} de grupos de CelesTrak no disponibles",
|
||||
"other": "{count} grupos de CelesTrak no disponibles"
|
||||
},
|
||||
"Malformed launch snapshot": "Instantánea de lanzamientos mal formada",
|
||||
"Malformed perimeter snapshot": "Instantánea de perímetros mal formada",
|
||||
"Overpass returned an incomplete camera response": "Overpass devolvió una respuesta de cámaras incompleta",
|
||||
"Fly below {km} km to a covered region": "Desciende por debajo de {km} km sobre una región con cobertura",
|
||||
"No feed here yet · {n} regions available": {
|
||||
"one": "Aún no hay fuente aquí · {n} región disponible",
|
||||
"many": "Aún no hay fuente aquí · {n} de regiones disponibles",
|
||||
"other": "Aún no hay fuente aquí · {n} regiones disponibles"
|
||||
},
|
||||
"Wind speed field unavailable": "Campo de velocidad del viento no disponible",
|
||||
"Decoder feeds": "Fuentes del decodificador",
|
||||
"WebUSB + decoder feeds": "WebUSB + fuentes del decodificador",
|
||||
"opening receiver": "abriendo el receptor",
|
||||
"checking decoder feeds": "comprobando las fuentes del decodificador",
|
||||
"listening": "escuchando",
|
||||
"USB error": "error de USB",
|
||||
"{count} heard": {
|
||||
"one": "{count} aeronave captada",
|
||||
"many": "{count} de aeronaves captadas",
|
||||
"other": "{count} aeronaves captadas"
|
||||
},
|
||||
"{rate} msg/s": "{rate} msg/s",
|
||||
"USB {rate} msg/s": "USB {rate} msg/s",
|
||||
"{count} feeds live": {
|
||||
"one": "{count} fuente en vivo",
|
||||
"many": "{count} de fuentes en vivo",
|
||||
"other": "{count} fuentes en vivo"
|
||||
},
|
||||
"feeds {names} stale": {
|
||||
"one": "fuente {names} desactualizada",
|
||||
"many": "fuentes {names} desactualizadas",
|
||||
"other": "fuentes {names} desactualizadas"
|
||||
},
|
||||
"feeds {names} unreachable": {
|
||||
"one": "fuente {names} inaccesible",
|
||||
"many": "fuentes {names} inaccesibles",
|
||||
"other": "fuentes {names} inaccesibles"
|
||||
},
|
||||
"feeds {names} invalid": {
|
||||
"one": "fuente {names} no válida",
|
||||
"many": "fuentes {names} no válidas",
|
||||
"other": "fuentes {names} no válidas"
|
||||
},
|
||||
"unsaved provider keys": "las claves de proveedores sin guardar",
|
||||
"Exit cockpit to fly to an aircraft": "Sal de la cabina para volar a una aeronave",
|
||||
"Exit cockpit to fly to a satellite": "Sal de la cabina para volar a un satélite",
|
||||
"Exit cockpit to frame contacts": "Sal de la cabina para encuadrar contactos"
|
||||
}
|
||||
@@ -2854,5 +2854,95 @@
|
||||
},
|
||||
"FOV {value}° (DATASHEET)": "ОБЗОР {value}° (ПО ПАСПОРТУ)",
|
||||
"SPEC FOV {span}° (CAPABILITY)": "ОБЗОР ПО ПАСПОРТУ {span}° (ВОЗМОЖНОСТИ)",
|
||||
"{spec} · SPECS: CCTV-DATABASE.COM": "{spec} · ХАРАКТЕРИСТИКИ: CCTV-DATABASE.COM"
|
||||
"{spec} · SPECS: CCTV-DATABASE.COM": "{spec} · ХАРАКТЕРИСТИКИ: CCTV-DATABASE.COM",
|
||||
"Canada only · ECCC AQHI network": "Только Канада · сеть AQHI ECCC",
|
||||
"{count} stations reporting": {
|
||||
"one": "{count} станция передаёт данные",
|
||||
"few": "{count} станции передают данные",
|
||||
"many": "{count} станций передают данные",
|
||||
"other": "{count} станции передают данные"
|
||||
},
|
||||
"No readings available": "Нет доступных показаний",
|
||||
"No current readings": "Нет актуальных показаний",
|
||||
"Low health risk": "Низкий риск для здоровья",
|
||||
"Moderate health risk": "Умеренный риск для здоровья",
|
||||
"High health risk": "Высокий риск для здоровья",
|
||||
"Very High health risk": "Очень высокий риск для здоровья",
|
||||
"Air quality request failed": "Не удалось запросить данные о качестве воздуха",
|
||||
"ECCC request timed out": "Истекло время ожидания запроса к ECCC",
|
||||
"Malformed AQHI station response": "Некорректный ответ со станциями AQHI",
|
||||
"Malformed AQHI observation response": "Некорректный ответ с наблюдениями AQHI",
|
||||
"No AQHI stations available": "Нет доступных станций AQHI",
|
||||
"{n} air quality": {
|
||||
"one": "{n} станция качества воздуха",
|
||||
"few": "{n} станции качества воздуха",
|
||||
"many": "{n} станций качества воздуха",
|
||||
"other": "{n} станции качества воздуха"
|
||||
},
|
||||
"GNSS feed rate limited (HTTP 429)": "Поток GNSS: превышен лимит запросов (HTTP 429)",
|
||||
"GNSS feed unavailable (HTTP {status})": "Поток GNSS недоступен (HTTP {status})",
|
||||
"GNSS snapshot needs a view anchor": "Для снимка GNSS нужна опорная точка вида",
|
||||
"Malformed GNSS integrity snapshot": "Некорректный снимок целостности GNSS",
|
||||
"GNSS integrity snapshot has no observation time": "В снимке целостности GNSS нет времени наблюдения",
|
||||
"{feed} feed unavailable": "Поток {feed} недоступен",
|
||||
"Loading {feeds}": "Загрузка: {feeds}",
|
||||
"{count} CelesTrak groups unavailable": {
|
||||
"one": "{count} группа CelesTrak недоступна",
|
||||
"few": "{count} группы CelesTrak недоступны",
|
||||
"many": "{count} групп CelesTrak недоступны",
|
||||
"other": "{count} группы CelesTrak недоступны"
|
||||
},
|
||||
"Malformed launch snapshot": "Некорректный снимок запусков",
|
||||
"Malformed perimeter snapshot": "Некорректный снимок периметров",
|
||||
"Overpass returned an incomplete camera response": "Overpass вернул неполный ответ о камерах",
|
||||
"Fly below {km} km to a covered region": "Опуститесь ниже {km} км над регионом с покрытием",
|
||||
"No feed here yet · {n} regions available": {
|
||||
"one": "Здесь пока нет потока данных · доступен {n} регион",
|
||||
"few": "Здесь пока нет потока данных · доступно {n} региона",
|
||||
"many": "Здесь пока нет потока данных · доступно {n} регионов",
|
||||
"other": "Здесь пока нет потока данных · доступно {n} региона"
|
||||
},
|
||||
"Wind speed field unavailable": "Поле скорости ветра недоступно",
|
||||
"Decoder feeds": "Потоки декодера",
|
||||
"WebUSB + decoder feeds": "WebUSB + потоки декодера",
|
||||
"opening receiver": "открытие приёмника",
|
||||
"checking decoder feeds": "проверка потоков декодера",
|
||||
"listening": "приём",
|
||||
"USB error": "ошибка USB",
|
||||
"{count} heard": {
|
||||
"one": "слышно бортов: {count}",
|
||||
"few": "слышно бортов: {count}",
|
||||
"many": "слышно бортов: {count}",
|
||||
"other": "слышно бортов: {count}"
|
||||
},
|
||||
"{rate} msg/s": "{rate} сообщ./с",
|
||||
"USB {rate} msg/s": "USB {rate} сообщ./с",
|
||||
"{count} feeds live": {
|
||||
"one": "{count} поток онлайн",
|
||||
"few": "{count} потока онлайн",
|
||||
"many": "{count} потоков онлайн",
|
||||
"other": "{count} потока онлайн"
|
||||
},
|
||||
"feeds {names} stale": {
|
||||
"one": "поток {names} устарел",
|
||||
"few": "потоки {names} устарели",
|
||||
"many": "потоки {names} устарели",
|
||||
"other": "потоки {names} устарели"
|
||||
},
|
||||
"feeds {names} unreachable": {
|
||||
"one": "поток {names} недоступен",
|
||||
"few": "потоки {names} недоступны",
|
||||
"many": "потоки {names} недоступны",
|
||||
"other": "потоки {names} недоступны"
|
||||
},
|
||||
"feeds {names} invalid": {
|
||||
"one": "поток {names} некорректен",
|
||||
"few": "потоки {names} некорректны",
|
||||
"many": "потоки {names} некорректны",
|
||||
"other": "потоки {names} некорректны"
|
||||
},
|
||||
"unsaved provider keys": "несохранённые ключи провайдеров",
|
||||
"Exit cockpit to fly to an aircraft": "Выйдите из кабины, чтобы перелететь к самолёту",
|
||||
"Exit cockpit to fly to a satellite": "Выйдите из кабины, чтобы перелететь к спутнику",
|
||||
"Exit cockpit to frame contacts": "Выйдите из кабины, чтобы показать контакты в кадре"
|
||||
}
|
||||
@@ -88,6 +88,7 @@
|
||||
"Soul",
|
||||
"Techno",
|
||||
"Trance",
|
||||
"USB {rate} msg/s",
|
||||
"VISUAL",
|
||||
"VS",
|
||||
"WHOT/BHOT",
|
||||
@@ -107,6 +108,7 @@
|
||||
"panel",
|
||||
"view band|GLOBAL",
|
||||
"voice|ERROR",
|
||||
"{rate} msg/s",
|
||||
"{speed} kn",
|
||||
"{type} SPD: {speed} HDG: {heading}",
|
||||
"{value} KM",
|
||||
|
||||
Reference in new issue
Block a user