mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 10:24:02 +00:00
i18n(pt): apply review round 2
Three-model review (astra, sol, fable), round 2: 29 of 30 findings
applied, one of them in part.
- pt: FIRMS «mais recente há», CCTV «cruzamentos», degraded radio
directory, «{layers}: ativação recente» for any layer list, «Sem
imagem», NIVELADO, AJUSTA-SE, quoted Provider Settings, V/S kept on
the ADS-B card, no-break space in «(24 h)» / «(30 d)», and more
(see docs/translations/pt.md).
- Malformed cyclone snapshot is now a catalog message (all locales).
- Markup supports data-i18n-context; the radio STATION TAG option uses
"radio tag|All", so it no longer flips gender when the list loads.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
3c0c10d3b1
commit
858ce827e5
13 files changed
+79
-46
No files matched your search
+1
-1
@@ -35,7 +35,7 @@ A module the server, tools or other portable code also imports must not load the
|
||||
<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. A module that holds work a reload would lose (an unsaved edit, an open session) registers it with `registerUnsavedWork(() => busy ? msg('unsaved …') : null)` from `src/i18n/index.js`, and the language picker asks before reloading.
|
||||
`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. `data-i18n-context="radio tag"` gives the element's text a context, the catalog entry `radio tag|All`, as `t('All', { context: 'radio tag' })` does in code. Markup is translated once at boot, and switching language reloads the page. A module that holds work a reload would lose (an unsaved edit, an open session) registers it with `registerUnsavedWork(() => busy ? msg('unsaved …') : null)` from `src/i18n/index.js`, and the language picker asks before reloading.
|
||||
|
||||
## Rules
|
||||
|
||||
|
||||
@@ -9,15 +9,15 @@ Translation source: abiyeenzo (#909).
|
||||
- Brazilian Portuguese (pt-BR): câmera, tela, compartilhar, arquivo, baixar, ônibus, metrô. Every `pt` tag, `pt-PT` included, gets this catalog, so avoid slang and regionalisms.
|
||||
- Address the user with «você» imperatives: «Clique no mapa», «Ative o CCTV», «Tente novamente». Tooltips that name an action use the infinitive: «Expandir painel», «Redefinir a câmera».
|
||||
- Sentence case, not Title Case: "Live Flights" → «Voos ao vivo». UPPERCASE English stays UPPERCASE: «AO VIVO», «SEM DADOS».
|
||||
- Quotes inside Portuguese text are straight double quotes: «Excluir o plano "{shot}"?». A sentence that names a panel uses «o painel "Configurações de provedores"», so the verb agrees.
|
||||
- Quotes inside Portuguese text are straight double quotes: «Excluir o plano "{shot}"?». A sentence that names a panel quotes it: «o painel "Configurações de provedores"» as the subject, so the verb agrees, and «adicione em "Configurações de provedores"» after a preposition.
|
||||
- A dash between clauses is a spaced em dash « — », even where the English runs it in.
|
||||
- A date or time after a participle takes a preposition: «emitido em {time}», «válida para {valid}», «Posição em {time}».
|
||||
- A date or time after a participle takes a preposition: «emitido em {time}», «válida para {valid}», «Posição em {time}», «onde estava em {time}». Use «em», not «às», wherever {time} can include a date.
|
||||
- Short labels, chips and HUD readouts stay short. A standard abbreviation with a period beats a wrapped line: ANT., PRÓX., DESLIG., EXCL., INCL., ALT., VEL., câm.
|
||||
- The HUD codes HDG, BRG, CRS, FOV, DEST, SPD, RANGE, ALT, GS and TRK stay in English, as in cockpit avionics («ALT {altitude} FT · GS {speed} KT · TRK {track}»), and so does the HUD recording mark REC. Words used in a sentence are translated: heading → «rumo», range → «alcance».
|
||||
- The HUD codes HDG, BRG, CRS, FOV, DEST, SPD, RANGE, ALT, GS, TRK and V/S stay in English, as in cockpit avionics («ALT {altitude} FT · GS {speed} KT · TRK {track}», «V/S {rate} FT/MIN»), and so does the HUD recording mark REC. Words used in a sentence are translated: heading → «rumo», range → «alcance».
|
||||
- Keyboard keys stay in English: Space, Esc, Enter, Tab, Shift, Backspace, H, 1–9 («Segure Space para falar»).
|
||||
- Common English tech words used in Brazil stay: feed, stream, snapshot, briefing, dock, layout, bloom, status, app, link, token.
|
||||
- Units: km, m, km/h, m/s, kt (knots), ft (feet), h, min, s, always spaced from the number («Terremotos (24 h)», «Missões espaciais (30 d)»); in UPPERCASE text KM, KM/S, KT, FT, MIN, H. "m" is always metres, so minutes are «min» («há 5 min»). MW stays MW. `°` and `%` stay. Decimal comma in written numbers («M4,4»).
|
||||
- "ago" goes before the time: «há {count} min». When a placeholder already holds a localized "ago" text (`{age}`, `{ago}`), do not add «há» again.
|
||||
- Units: km, m, km/h, m/s, kt (knots), ft (feet), h, min, s, always spaced from the number («Terremotos (24 h)», «Missões espaciais (30 d)»; in the layer names the space is a no-break space, U+00A0, so the unit never wraps away from the number); in UPPERCASE text KM, KM/S, KT, FT, MIN, H. "m" is always metres, so minutes are «min» («há 5 min»). MW stays MW. `°` and `%` stay. Decimal comma in written numbers («M4,4»).
|
||||
- "ago" goes before the time: «há {count} min», «mais recente há {value}». When a placeholder already holds a localized "ago" text (`{age}`, `{ago}`), do not add «há» again.
|
||||
- Money: «US$ {amount} mil / mi / bi» for thousands, millions and billions.
|
||||
- Compass points use Leste/Oeste: N, NNE, NE, ENE, L, ESE, SE, SSE, S, SSO, SO, OSO, O, ONO, NO, NNO; hemisphere letters N, S, L, O.
|
||||
|
||||
@@ -84,6 +84,7 @@ The `many` form is the `other` text with «de» after the number: «{count} de c
|
||||
| Cyber (HUD layout, effect) | Ciber |
|
||||
| heading / course / altitude / ground speed | rumo / curso / altitude / velocidade solo (VELOC. SOLO) |
|
||||
| HDG / BRG / CRS / FOV / DEST / SPD / RANGE | HDG / BRG / CRS / FOV / DEST / SPD / RANGE (unchanged) |
|
||||
| V/S (vertical speed code) | V/S (unchanged) |
|
||||
| PREV / NEXT | ANT. / PRÓX. |
|
||||
| Previous / Next (button text) | Anterior / Próximo (Próxima) |
|
||||
| L / R (relative bearing) | E / D |
|
||||
@@ -91,4 +92,7 @@ The `many` form is the `other` text with «de» after the number: «{count} de c
|
||||
| REGIONAL NEWS (fits the 16-character kicker) | NOTÍCIAS DA ÁREA |
|
||||
| PITCH / HGT (CCTV calibration chips) | INCL. / ALT. |
|
||||
| NOT AN ALL-CLEAR | NÃO GARANTE SEGURANÇA |
|
||||
| Last report (transit vehicle position) | Último registro |
|
||||
| intersection (CCTV) | cruzamento |
|
||||
| {layers} just turned on (any list of layers) | «{layers}: ativação recente» (no agreement needed) |
|
||||
| turn-by-turn: turn left / keep left / lane | vire à esquerda / mantenha-se à esquerda / faixa da esquerda |
|
||||
+5
-2
@@ -125,8 +125,11 @@ export function extractMessages() {
|
||||
const html = readFileSync(path.join(ROOT, file), 'utf8');
|
||||
for (const [, tag, attrs, text] of html.matchAll(TAG)) {
|
||||
const where = `${file} <${tag}>`;
|
||||
if (/\sdata-i18n(\s|=|$)/.test(attrs))
|
||||
add(normalize(decodeHtml(text)), where);
|
||||
if (/\sdata-i18n(\s|=|$)/.test(attrs)) {
|
||||
const context = attrs.match(/\sdata-i18n-context="([^"]*)"/);
|
||||
const message = normalize(decodeHtml(text));
|
||||
add(context ? `${decodeHtml(context[1])}|${message}` : message, where);
|
||||
}
|
||||
for (const [, name] of attrs.matchAll(
|
||||
/\sdata-i18n-(title|aria-label|placeholder)\b/g,
|
||||
)) {
|
||||
|
||||
@@ -216,6 +216,21 @@ test('applyDocumentTranslations translates opted-in text and attributes in place
|
||||
assert.equal(unknown.textContent, 'No such message');
|
||||
});
|
||||
|
||||
test('applyDocumentTranslations reads data-i18n-context for element text', () => {
|
||||
const option = fakeElement({
|
||||
text: 'All',
|
||||
attributes: { 'data-i18n-context': 'radio tag' },
|
||||
});
|
||||
const doc = {
|
||||
querySelectorAll: (selector) =>
|
||||
selector === '[data-i18n]' ? [option] : [],
|
||||
};
|
||||
setLocale('pt');
|
||||
applyDocumentTranslations(doc);
|
||||
assert.equal(option.textContent, t('All', { context: 'radio tag' }));
|
||||
assert.notEqual(option.textContent, t('All'));
|
||||
});
|
||||
|
||||
test('persistLocaleAndReload keeps the choice in ?lang when storage is blocked', () => {
|
||||
const calls = [];
|
||||
const location = {
|
||||
|
||||
+7
-2
@@ -228,7 +228,9 @@ function normalizeMarkupText(text) {
|
||||
* Markup opt-in: the element's own English text or attribute is the source.
|
||||
* <span data-i18n>Normal</span>
|
||||
* <button title="Pin" data-i18n-title>…</button>
|
||||
* Also data-i18n-aria-label and data-i18n-placeholder.
|
||||
* Also data-i18n-aria-label and data-i18n-placeholder. data-i18n-context
|
||||
* gives the element's text a context, as t(text, { context }) does:
|
||||
* <option data-i18n data-i18n-context="radio tag">All</option>
|
||||
*/
|
||||
const DOCUMENT_TRANSLATION_TARGETS = Object.freeze([
|
||||
['data-i18n', null],
|
||||
@@ -253,7 +255,10 @@ export function applyDocumentTranslations(doc) {
|
||||
? element.getAttribute(attribute)
|
||||
: normalizeMarkupText(element.textContent);
|
||||
if (!source) continue;
|
||||
const text = t(source);
|
||||
const context = attribute
|
||||
? null
|
||||
: element.getAttribute?.('data-i18n-context');
|
||||
const text = context ? t(source, { context }) : t(source);
|
||||
if (text === source) continue;
|
||||
if (attribute) element.setAttribute(attribute, text);
|
||||
else element.textContent = text;
|
||||
|
||||
@@ -2227,6 +2227,7 @@
|
||||
"The Google Maps JavaScript API could not load": "No se pudo cargar la API de Maps JavaScript de Google",
|
||||
"Weather request timed out": "La petición del clima agotó el tiempo de espera",
|
||||
"Cyclone request timed out": "La petición de ciclones agotó el tiempo de espera",
|
||||
"Malformed cyclone snapshot": "Instantánea de ciclones mal formada",
|
||||
"Malformed weather manifest": "Manifiesto del clima mal formado",
|
||||
"Weather HTTP {status}": "Clima: HTTP {status}",
|
||||
"Weather image HTTP {status}": "Imagen del clima: HTTP {status}",
|
||||
|
||||
+33
-32
@@ -300,12 +300,12 @@
|
||||
"Source configured; file checked when loaded": "Fonte configurada; o arquivo será verificado ao carregar",
|
||||
"Source unavailable": "Fonte indisponível",
|
||||
"ENVIRONMENTAL": "AMBIENTAL",
|
||||
"EARTH WATCH": "VIGIA DA TERRA",
|
||||
"EARTH WATCH": "VIGILÂNCIA DA TERRA",
|
||||
"ACTIVE EVENTS": "EVENTOS ATIVOS",
|
||||
"Starting live contacts…": "Iniciando contatos ao vivo…",
|
||||
"Opening space missions…": "Abrindo missões espaciais…",
|
||||
"Scanning active events…": "Varrendo eventos ativos…",
|
||||
"Live earthquakes and active fires, from USGS and NASA": "Terremotos e incêndios ativos ao vivo, de USGS e NASA",
|
||||
"Live earthquakes and active fires, from USGS and NASA": "Terremotos e incêndios ativos ao vivo, do USGS e da NASA",
|
||||
"It feels like a forbidden cockpit—then you realize the sources are public and the data is real.": "Parece uma cabine proibida — até você perceber que as fontes são públicas e os dados são reais.",
|
||||
"Working…": "Processando…",
|
||||
"Could not open that mission{detail}. Retry or explore manually.": "Não foi possível abrir essa missão{detail}. Tente novamente ou explore manualmente.",
|
||||
@@ -376,7 +376,7 @@
|
||||
"{name} is longer than any real key ({limit} max)": "{name}: o valor é mais longo que qualquer chave real (máximo {limit})",
|
||||
"{name} may only contain printable characters with no spaces": "{name}: só são aceitos caracteres imprimíveis, sem espaços",
|
||||
"{name} contains a character that is not valid in a key (#, quotes, $, \\, or backtick)": "{name}: há um caractere inválido para uma chave (#, aspas, $, \\ ou crase)",
|
||||
"{name} is configured outside Provider Settings and can only be changed where it was set": "{name} foi configurada fora de Configurações de provedores e só pode ser alterada onde foi definida",
|
||||
"{name} is configured outside Provider Settings and can only be changed where it was set": "{name} foi configurada fora de \"Configurações de provedores\" e só pode ser alterada onde foi definida",
|
||||
"The key was not saved: could not restrict the credential file to your account; nothing was saved": "A chave não foi salva: não foi possível restringir o arquivo de credenciais à sua conta; nada foi salvo",
|
||||
"The key was not saved: the existing configuration could not be read, so nothing was changed": "A chave não foi salva: não foi possível ler a configuração existente, então nada foi alterado",
|
||||
"Could not write the {store} store": "Não foi possível gravar no armazenamento {store}",
|
||||
@@ -430,7 +430,7 @@
|
||||
"Live active-fire detections": "Detecções de incêndios ativos ao vivo",
|
||||
"Real live traffic (keyless runs a simulation)": "Trânsito real ao vivo (sem chave, roda uma simulação)",
|
||||
"Bing imagery map stacks + world terrain": "Pilhas de mapas com imagens do Bing + relevo mundial",
|
||||
"More flight-polling credits (anonymous works without)": "Mais créditos de consulta de voos (o acesso anônimo funciona sem)",
|
||||
"More flight-polling credits (anonymous works without)": "Mais créditos de consulta de voos (opcional: o acesso anônimo também funciona)",
|
||||
"Street-level imagery and coverage in the Street Level layer. Free: register an app in the Mapillary developer dashboard and paste its Client Token": "Imagens e cobertura no nível da rua na camada \"Nível da rua\". Grátis: registre um app no painel de desenvolvedores do Mapillary e cole o token de cliente (Client Token)",
|
||||
"Public space-missions access needs no key. Leave blank for standard use; paid tokens only raise request limits.": "O acesso público às missões espaciais não precisa de chave. Deixe em branco para o uso padrão; tokens pagos só aumentam os limites de requisições.",
|
||||
"Air quality request failed": "Falha na requisição de qualidade do ar",
|
||||
@@ -716,7 +716,7 @@
|
||||
"FIRE": "INCÊNDIO",
|
||||
"FIRES": "INCÊNDIOS",
|
||||
"max {value} MW": "máx {value} MW",
|
||||
"new {value}": "novo {value}",
|
||||
"new {value}": "mais recente há {value}",
|
||||
"{value}°N": "{value}°N",
|
||||
"{value}°S": "{value}°S",
|
||||
"{value}°E": "{value}°L",
|
||||
@@ -822,7 +822,7 @@
|
||||
"FLIGHT {count}": "VOO {count}",
|
||||
"REUSED": "REUTILIZADO",
|
||||
"NO STAGE RE-ENTRY / RECOVERY DATA": "SEM DADOS DE REENTRADA / RECUPERAÇÃO DE ESTÁGIO",
|
||||
"{count} / 30D": "{count} / 30D",
|
||||
"{count} / 30D": "{count} / 30 D",
|
||||
"NO MISSIONS AVAILABLE IN THE CURRENT 30-DAY WINDOW": "NENHUMA MISSÃO DISPONÍVEL NA JANELA ATUAL DE 30 DIAS",
|
||||
"DATE UNAVAILABLE": "DATA INDISPONÍVEL",
|
||||
"UNSPECIFIED OPERATOR": "OPERADOR NÃO ESPECIFICADO",
|
||||
@@ -881,8 +881,8 @@
|
||||
"Your RTL-SDR receiver": "Seu receptor RTL-SDR",
|
||||
"NO CALLSIGN": "SEM INDICATIVO",
|
||||
"ALT {altitude} FT · GS {speed} KT · TRK {track}": "ALT {altitude} FT · GS {speed} KT · TRK {track}",
|
||||
"V/S {rate}": "VEL. VERT. {rate}",
|
||||
"V/S {rate} FPM": "VEL. VERT. {rate} FT/MIN",
|
||||
"V/S {rate}": "V/S {rate}",
|
||||
"V/S {rate} FPM": "V/S {rate} FT/MIN",
|
||||
"POSITION {age}": "POSIÇÃO {age}",
|
||||
"POSITION {seconds} S AGO": "POSIÇÃO HÁ {seconds} S",
|
||||
"{count} MSGS": {
|
||||
@@ -954,8 +954,8 @@
|
||||
"Traffic / Transit": "Trânsito / Transporte",
|
||||
"Music": "Música",
|
||||
"Other": "Outras",
|
||||
"Directory refresh degraded; showing the previous station catalog.": "A atualização do diretório foi parcial; mostrando o catálogo de estações anterior.",
|
||||
"Radio directory coverage is degraded.": "O diretório de rádio está funcionando com falhas.",
|
||||
"Directory refresh degraded; showing the previous station catalog.": "A atualização do diretório retornou dados degradados; mostrando o catálogo de estações anterior.",
|
||||
"Radio directory coverage is degraded.": "A cobertura do diretório de rádio está degradada.",
|
||||
"Directory refresh failed; showing the previous station catalog.": "A atualização do diretório falhou; mostrando o catálogo de estações anterior.",
|
||||
"Radio directory is temporarily unavailable.": "O diretório de rádio está temporariamente indisponível.",
|
||||
"radio|NEWS": "NOTÍCIAS",
|
||||
@@ -1069,7 +1069,7 @@
|
||||
"KartaView sent too much data": "O KartaView enviou dados demais",
|
||||
"KartaView is rate-limiting requests — try again in a moment": "O KartaView está limitando as requisições — tente novamente em instantes",
|
||||
"KartaView has no such photo": "O KartaView não tem essa foto",
|
||||
"Mapillary rejected MAPILLARY_CLIENT_TOKEN — replace it in Provider Settings": "O Mapillary recusou o MAPILLARY_CLIENT_TOKEN — substitua-o em Configurações de provedores",
|
||||
"Mapillary rejected MAPILLARY_CLIENT_TOKEN — replace it in Provider Settings": "O Mapillary recusou o MAPILLARY_CLIENT_TOKEN — substitua-o em \"Configurações de provedores\"",
|
||||
"Coverage requests are being rate-limited — they resume on their own": "As requisições de cobertura estão sendo limitadas — elas são retomadas sozinhas",
|
||||
"The Panoramax photo could not be loaded": "Não foi possível carregar a foto do Panoramax",
|
||||
"Panoramax is busy — coverage resumes on its own": "O Panoramax está sobrecarregado — a cobertura volta sozinha",
|
||||
@@ -1120,7 +1120,7 @@
|
||||
"LIVE · Roads: OpenStreetMap · Flow: TomTom · {percent}% cov": "AO VIVO · Vias: OpenStreetMap · Fluxo: TomTom · {percent}% cob",
|
||||
"SIMULATED · Roads: OpenStreetMap · Flow: TomTom (no matches)": "SIMULADO · Vias: OpenStreetMap · Fluxo: TomTom (sem correspondências)",
|
||||
"SIMULATED — traffic service unreachable": "SIMULADO — serviço de trânsito inacessível",
|
||||
"SIMULATED — add TomTom key for live": "SIMULADO — adicione a chave TomTom para dados ao vivo",
|
||||
"SIMULATED — add TomTom key for live": "SIMULADO — adicione a chave do TomTom para dados ao vivo",
|
||||
"OpenFreeMap tiles rate-limited": "Blocos do OpenFreeMap com limite de requisições",
|
||||
"OpenFreeMap tiles timed out": "Os blocos do OpenFreeMap excederam o tempo limite",
|
||||
"OpenFreeMap tiles unavailable": "Blocos do OpenFreeMap indisponíveis",
|
||||
@@ -1140,9 +1140,9 @@
|
||||
"Moving": "Em movimento",
|
||||
"reported {speed} km/h": "velocidade informada: {speed} km/h",
|
||||
"reported hdg {heading}°": "rumo informado: {heading}°",
|
||||
"Last report: stopped at stop {stop}": "Último relato: parado no ponto {stop}",
|
||||
"Last report: arriving at stop {stop}": "Último relato: chegando ao ponto {stop}",
|
||||
"Last report: next stop {stop}": "Último relato: próximo ponto {stop}",
|
||||
"Last report: stopped at stop {stop}": "Último registro: parado no ponto {stop}",
|
||||
"Last report: arriving at stop {stop}": "Último registro: chegando ao ponto {stop}",
|
||||
"Last report: next stop {stop}": "Último registro: próximo ponto {stop}",
|
||||
"Shown at street level · depth not in the feed": "Exibido no nível da rua · o feed não informa a profundidade",
|
||||
"Reported {seconds} s ago": "Informado há {seconds} s",
|
||||
"Reported {minutes} min ago": "Informado há {minutes} min",
|
||||
@@ -1184,7 +1184,7 @@
|
||||
"Hidden by this map source · choose a globe map": "Oculto com esta fonte do mapa · escolha um mapa de globo",
|
||||
"Weather image HTTP {status}": "Imagem do clima: HTTP {status}",
|
||||
"Weather HTTP {status}": "Clima: HTTP {status}",
|
||||
"Frame unavailable; previous observation retained": "Quadro indisponível; a observação anterior foi mantida",
|
||||
"Frame unavailable; previous observation retained": "Quadro indisponível; mantido o anterior",
|
||||
"Weather imagery unavailable": "Imagens meteorológicas indisponíveis",
|
||||
"Weather source unavailable; previous observation retained": "Fonte meteorológica indisponível; a observação anterior foi mantida",
|
||||
"Weather unavailable": "Clima indisponível",
|
||||
@@ -1259,6 +1259,7 @@
|
||||
"Malformed weather manifest": "Manifesto do clima malformado",
|
||||
"Weather request timed out": "A requisição do clima excedeu o tempo limite",
|
||||
"Cyclone request timed out": "A requisição de ciclones excedeu o tempo limite",
|
||||
"Malformed cyclone snapshot": "Snapshot de ciclones malformado",
|
||||
"Temperature field unavailable": "Campo de temperatura indisponível",
|
||||
"Mean sea level pressure field unavailable": "Campo de pressão ao nível do mar indisponível",
|
||||
"Wind upstream unavailable": "Origem dos dados de vento indisponível",
|
||||
@@ -1338,12 +1339,12 @@
|
||||
"Malformed wind manifest": "Manifesto de vento malformado",
|
||||
"Malformed wind grid": "Grade de vento malformada",
|
||||
"Error: {message}": "Erro: {message}",
|
||||
"Needs {envVars} — add it in Provider Settings": "Requer {envVars} — adicione em Configurações de provedores",
|
||||
"Needs {envVars} — add it in Provider Settings": "Requer {envVars} — adicione em \"Configurações de provedores\"",
|
||||
"{label} is unavailable": "{label} indisponível",
|
||||
"This map stack": "Esta pilha de mapas",
|
||||
"{label} unavailable: {hint}": "{label} indisponível: {hint}",
|
||||
"Google 3D tiles unavailable — check the key's API restrictions, quota, or network": "Google 3D Tiles indisponível — verifique as restrições de API da chave, a cota ou a rede",
|
||||
"Needs {envVars} — add it in Provider Settings — or a Cesium ion token for the ion-hosted route": "Requer {envVars} — adicione em Configurações de provedores — ou um token do Cesium ion para a rota hospedada no ion",
|
||||
"Needs {envVars} — add it in Provider Settings — or a Cesium ion token for the ion-hosted route": "Requer {envVars} — adicione em \"Configurações de provedores\" — ou um token do Cesium ion para a rota hospedada no ion",
|
||||
"Exit cockpit to fly to a camera": "Saia da cabine para voar até uma câmera",
|
||||
"Exit cockpit to fly to a location": "Saia da cabine para voar até um local",
|
||||
"Exit cockpit to fly to a photo": "Saia da cabine para voar até uma foto",
|
||||
@@ -1384,7 +1385,7 @@
|
||||
"many": "{count} de planos adicionados: {scene}",
|
||||
"other": "{count} planos adicionados: {scene}"
|
||||
},
|
||||
"Cannot capture shot: camera not ready": "Não foi possível capturar o plano: câmera não está pronta",
|
||||
"Cannot capture shot: camera not ready": "Não foi possível capturar o plano: a câmera não está pronta",
|
||||
"Captured: {scene} / {shot}": "Capturado: {scene} / {shot}",
|
||||
"Select a shot first": "Selecione um plano primeiro",
|
||||
"Updated: {scene} / {shot}": "Atualizado: {scene} / {shot}",
|
||||
@@ -1623,7 +1624,7 @@
|
||||
"many": "{count} de câmeras carregadas · ative o CCTV para usar",
|
||||
"other": "{count} câmeras carregadas · ative o CCTV para usar"
|
||||
},
|
||||
"Enable CCTV to load camera intersections": "Ative o CCTV para carregar interseções de câmeras",
|
||||
"Enable CCTV to load camera intersections": "Ative o CCTV para carregar os cruzamentos com câmeras",
|
||||
"Enable CCTV to start camera-linked intelligence summaries.": "Ative o CCTV para iniciar resumos de inteligência vinculados às câmeras.",
|
||||
"No summary available.": "Nenhum resumo disponível.",
|
||||
"loading frames": "carregando quadros",
|
||||
@@ -1675,12 +1676,12 @@
|
||||
"many": "{count} DE ENTRADAS DESCONHECIDAS · NÃO GARANTE SEGURANÇA",
|
||||
"other": "{count} ENTRADAS DESCONHECIDAS · NÃO GARANTE SEGURANÇA"
|
||||
},
|
||||
"AVAILABLE INPUTS CURRENT · NOT AN ALL-CLEAR": "ENTRADAS DISPONÍVEIS ATUAIS · NÃO GARANTE SEGURANÇA",
|
||||
"AVAILABLE INPUTS CURRENT · NOT AN ALL-CLEAR": "ENTRADAS DISPONÍVEIS ATUALIZADAS · NÃO GARANTE SEGURANÇA",
|
||||
"SQUAWK {code} · {status}": "SQUAWK {code} · {status}",
|
||||
"ADS-B PRIORITY · {status}": "PRIORIDADE ADS-B · {status}",
|
||||
"ADS-B EMERGENCY · {status}": "EMERGÊNCIA ADS-B · {status}",
|
||||
"AIRCRAFT": "AERONAVE",
|
||||
"ACQUIRING SURFACE": "ADQUIRINDO SUPERFÍCIE",
|
||||
"ACQUIRING SURFACE": "CARREGANDO SUPERFÍCIE",
|
||||
"SURFACE FALLBACK": "SUPERFÍCIE ALTERNATIVA",
|
||||
"STALE FEED": "FEED DESATUALIZADO",
|
||||
"LIVE TRACK": "RASTRO AO VIVO",
|
||||
@@ -1795,9 +1796,9 @@
|
||||
"{requirement}, or {offer}": "{requirement}, ou {offer}",
|
||||
"Live Flights": "Voos ao vivo",
|
||||
"Military Flights": "Voos militares",
|
||||
"Earthquakes (24h)": "Terremotos (24 h)",
|
||||
"Earthquakes (24h)": "Terremotos (24 h)",
|
||||
"Satellites": "Satélites",
|
||||
"Space Missions (30d)": "Missões espaciais (30 d)",
|
||||
"Space Missions (30d)": "Missões espaciais (30 d)",
|
||||
"Street Traffic": "Trânsito urbano",
|
||||
"CCTV": "CCTV",
|
||||
"Radio": "Rádio",
|
||||
@@ -1909,7 +1910,7 @@
|
||||
"📍 Location: --": "📍 Local: --",
|
||||
"Landmark: --": "Ponto de referência: --",
|
||||
"Searched location": "Local pesquisado",
|
||||
"ORBIT": "ÓRBITA",
|
||||
"ORBIT": "ORBITANDO",
|
||||
"Location not found": "Local não encontrado",
|
||||
"Check the spelling and press Enter to search again.": "Verifique a grafia e pressione Enter para buscar de novo.",
|
||||
"Search failed": "Falha na busca",
|
||||
@@ -2084,7 +2085,7 @@
|
||||
"Every day is empty here": "Todos os dias estão vazios aqui",
|
||||
"Sources off": "Fontes desativadas",
|
||||
"VS": "VS",
|
||||
"Not set": "Não definido",
|
||||
"Not set": "Sem imagem",
|
||||
"Basemap": "Mapa base",
|
||||
"Source off": "Fonte desativada",
|
||||
"Unpin {slot}": "Desafixar {slot}",
|
||||
@@ -2149,7 +2150,7 @@
|
||||
"Shared {subject} could not be restored — feed unavailable": "Não foi possível restaurar o compartilhamento de {subject} — feed indisponível",
|
||||
"Shared {subject} is unavailable": "O compartilhamento de {subject} está indisponível",
|
||||
"{id} isn't reporting now — showing where it was last seen": "{id} não está transmitindo agora — mostrando onde foi visto pela última vez",
|
||||
"{id} isn't reporting now — showing where it was at {time}": "{id} não está transmitindo agora — mostrando onde estava às {time}",
|
||||
"{id} isn't reporting now — showing where it was at {time}": "{id} não está transmitindo agora — mostrando onde estava em {time}",
|
||||
"This share link can't be opened": "Não é possível abrir este link de compartilhamento",
|
||||
"Shared vessel is unavailable": "A embarcação compartilhada está indisponível",
|
||||
"ACQUIRING": "ADQUIRINDO",
|
||||
@@ -2358,7 +2359,7 @@
|
||||
"Invalid Gemini connection endpoint": "Endpoint de conexão do Gemini inválido",
|
||||
"Gemini voice could not connect. Try again.": "Não foi possível conectar a voz do Gemini. Tente novamente.",
|
||||
"Gemini voice requires a same-origin app request": "A voz do Gemini exige uma requisição da mesma origem do app",
|
||||
"Add a Gemini API key in Provider Settings": "Adicione uma chave de API do Gemini em Configurações de provedores",
|
||||
"Add a Gemini API key in Provider Settings": "Adicione uma chave de API do Gemini em \"Configurações de provedores\"",
|
||||
"The Gemini Live model configuration is invalid": "A configuração do modelo do Gemini Live é inválida",
|
||||
"Gemini voice is starting too often. Try again shortly.": "A voz do Gemini está sendo iniciada com frequência demais. Tente novamente em breve.",
|
||||
"Gemini quota is unavailable. Try again later.": "A cota do Gemini está indisponível. Tente novamente mais tarde.",
|
||||
@@ -2529,7 +2530,7 @@
|
||||
"{label} error": "{label}: erro",
|
||||
"counted the first {returned} loaded records": "foram contados os primeiros {returned} registros carregados",
|
||||
"counted {returned} of {total} loaded records": "foram contados {returned} de {total} registros carregados",
|
||||
"{layers} just turned on — still loading, counts will rise": "{layers}: acabou de ser ativado — ainda carregando, as contagens vão subir",
|
||||
"{layers} just turned on — still loading, counts will rise": "{layers}: ativação recente — ainda carregando, as contagens vão subir",
|
||||
"Contacts panel snapshot at {time} — the same retained cohort supplies the card, speech, and follow-up memory": "Snapshot do painel Contatos ({time}) — o mesmo grupo retido alimenta o cartão, a fala e a memória de acompanhamento",
|
||||
"Contacts panel snapshot — the same retained cohort supplies the card, speech, and follow-up memory": "Snapshot do painel Contatos — o mesmo grupo retido alimenta o cartão, a fala e a memória de acompanhamento",
|
||||
"Fly below {km} km to a covered region": "Desça abaixo de {km} km sobre uma região com cobertura",
|
||||
@@ -2662,7 +2663,7 @@
|
||||
"Next satellite pass": "Próxima passagem do satélite",
|
||||
"Mark the map": "Marcar o mapa",
|
||||
"Mark {targets} +{more}": "Marcar {targets} +{more}",
|
||||
"Mark {targets}": "Marcar: {targets}",
|
||||
"Mark {targets}": "Marcar {targets}",
|
||||
"Fly to {place}": "Voar até {place}",
|
||||
"Fly to Location": "Voar até o local",
|
||||
"Nearest aircraft to {place}": "Aeronave mais próxima de {place}",
|
||||
@@ -2694,7 +2695,7 @@
|
||||
"Recent speech": "Fala recente",
|
||||
"HERE": "AQUI",
|
||||
"Aircraft cockpit view": "Vista da cabine da aeronave",
|
||||
"LEVEL": "NÍVEL",
|
||||
"LEVEL": "NIVELADO",
|
||||
"Estimated destination direction": "Direção estimada do destino",
|
||||
"DEST ---°": "DEST ---°",
|
||||
"OPTICAL PLANE · 01": "PLANO ÓPTICO · 01",
|
||||
@@ -2825,7 +2826,7 @@
|
||||
"DRAG TO TUNE": "ARRASTE PARA SINTONIZAR",
|
||||
"Tune available internet radio stations": "Sintonizar as estações de rádio pela internet disponíveis",
|
||||
"ALL · DRAG THE NEEDLE": "TODAS · ARRASTE A AGULHA",
|
||||
"SNAPS TO AVAILABLE STATIONS": "AJUSTA ÀS ESTAÇÕES DISPONÍVEIS",
|
||||
"SNAPS TO AVAILABLE STATIONS": "AJUSTA-SE ÀS ESTAÇÕES DISPONÍVEIS",
|
||||
"Radio playback": "Reprodução do rádio",
|
||||
"Previous filtered station": "Estação filtrada anterior",
|
||||
"Play selected station": "Reproduzir a estação selecionada",
|
||||
|
||||
@@ -2263,6 +2263,7 @@
|
||||
"The Google Maps JavaScript API could not load": "Не удалось загрузить Google Maps JavaScript API",
|
||||
"Weather request timed out": "Истекло время ожидания запроса погоды",
|
||||
"Cyclone request timed out": "Истекло время ожидания запроса циклонов",
|
||||
"Malformed cyclone snapshot": "Некорректный снимок циклонов",
|
||||
"Malformed weather manifest": "Некорректный манифест погоды",
|
||||
"Weather HTTP {status}": "Погода: HTTP {status}",
|
||||
"Weather image HTTP {status}": "Снимок погоды: HTTP {status}",
|
||||
|
||||
@@ -1203,6 +1203,7 @@
|
||||
"Malformed weather manifest": "天气清单格式错误",
|
||||
"Weather request timed out": "天气请求超时",
|
||||
"Cyclone request timed out": "气旋请求超时",
|
||||
"Malformed cyclone snapshot": "气旋快照格式错误",
|
||||
"Temperature field unavailable": "温度场不可用",
|
||||
"Mean sea level pressure field unavailable": "平均海平面气压场不可用",
|
||||
"Wind upstream unavailable": "风场上游数据源不可用",
|
||||
|
||||
@@ -1198,6 +1198,7 @@
|
||||
"Malformed weather manifest": "天氣清單格式錯誤",
|
||||
"Weather request timed out": "天氣請求超時",
|
||||
"Cyclone request timed out": "氣旋請求超時",
|
||||
"Malformed cyclone snapshot": "氣旋快照格式錯誤",
|
||||
"Temperature field unavailable": "溫度場不可用",
|
||||
"Mean sea level pressure field unavailable": "平均海平面氣壓場不可用",
|
||||
"Wind upstream unavailable": "風場上游數據源不可用",
|
||||
|
||||
@@ -225,6 +225,7 @@
|
||||
"Techno",
|
||||
"Trance",
|
||||
"USB {rate} msg/s",
|
||||
"V/S {rate}",
|
||||
"VISUAL",
|
||||
"VOLUME",
|
||||
"VS",
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { readResponseJsonCapped } from '../../sources/httpBody.js';
|
||||
|
||||
// Shared with the AI tools, so no i18n import: the timeout stays English here
|
||||
// and the cyclones layer translates it where it shows it (translateKnown).
|
||||
// Shared with the AI tools, so no i18n import: these errors stay English here
|
||||
// and the cyclones layer translates them where it shows them (translateKnown).
|
||||
const msg = (text) => text;
|
||||
|
||||
export const CYCLONE_RESPONSE_LIMIT = 4 * 1024 * 1024;
|
||||
const malformed = () => new Error('Malformed cyclone snapshot');
|
||||
const malformed = () => new Error(msg('Malformed cyclone snapshot'));
|
||||
const text = (value, max = 160) => {
|
||||
if (
|
||||
typeof value !== 'string' ||
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
<button id="radio-enable-btn" class="panel-layer-toggle" type="button" aria-pressed="false" data-i18n>ENABLE</button>
|
||||
<label class="radio-filter-label" for="radio-filter" data-i18n>STATION TAG</label>
|
||||
<select class="gev-field" id="radio-filter" aria-label="Filter stations by station tag" disabled data-i18n-aria-label>
|
||||
<option value="all" data-i18n>All</option>
|
||||
<option value="all" data-i18n data-i18n-context="radio tag">All</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="radio-station-card" class="radio-station-card" aria-live="polite">
|
||||
|
||||
Reference in new issue
Block a user