diff --git a/docs/translations/pt.md b/docs/translations/pt.md index 7417fd64f..bf1208dce 100644 --- a/docs/translations/pt.md +++ b/docs/translations/pt.md @@ -9,12 +9,14 @@ 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}"?». +- 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. +- 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}». - 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. Words used in a sentence are translated: heading → «rumo», range → «alcance». +- 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». - 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; 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»). +- 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. - 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. @@ -41,6 +43,7 @@ The `many` form is the `other` text with «de» after the number: «{count} de c | SIMULATED | SIMULADO | | stale / degraded / unavailable | desatualizado / degradado / indisponível | | retry / retrying | nova tentativa / tentando de novo | +| retrying (a fetch) / retrying slowly | buscando de novo / tentando de novo em intervalos maiores | | key (API key) / POWER UP / POWERED UP | chave / ATIVAR / ATIVADO | | Provider Settings | Configurações de provedores | | ON / OFF (state chips) | ATIVO / INATIVO | @@ -48,23 +51,32 @@ The `many` form is the `other` text with «de» after the number: «{count} de c | globe / map / map source / map stack | globo / mapa / fonte do mapa / pilha de mapas | | basemap / tile | mapa base / bloco | | aircraft, plane / flight / military flights | aeronave, avião / voo / voos militares | -| vessel, ship | embarcação | +| vessel, ship / TANKER (AIS class) | embarcação / NAVIO-TANQUE | | satellite / shell (Starlink) / DENSE | satélite / camada orbital / DENSO | +| SPARSE / BALANCED / DENSE (detection modes) | ESPARSO / EQUILIBRADO / DENSO (o modo) | | launch (rocket) / launch site / space mission | lançamento / local de lançamento / missão espacial | +| KM DOWNRANGE / EST. DOWNRANGE | KM DO LOCAL DE LANÇAMENTO / EST. PELA DIST. DO LANÇAMENTO | | ascent / replay / payload / stage | subida / reprise / carga útil / estágio | | contact / tracking / track | contato / rastreamento / rastro | | installation / site | instalação / local | +| PLACES CANDIDATE (installation class) | CANDIDATO DO GOOGLE PLACES | | mapped / observed | mapeado / observado | | camera / CCTV camera | câmera / câmera de CCTV | +| CALIBRATED / CURATED (CCTV badge) | CALIBRADA / COM CURADORIA | +| AUTO HOP ON / OFF (CCTV) | TROCA AUTO. LIGADA / DESLIG. (as the other CCTV toggles) | | earthquake / active fire | terremoto / incêndio ativo | | advisory (NHC/CPHC cyclone) | boletim | +| weather (layer, card) / weather satellite | clima / meteorologia, meteorológico | +| FOG / cloud unknown | NEVOEIRO / nebulosidade desconhecida | +| AQHI bands Low … Very High | Risco baixo … Risco muito alto | | radio station / broadcaster | estação de rádio / emissora | +| Oldies / World (radio tags) | Flashback / Músicas do mundo | | road / flow (traffic) | via / fluxo | | Street Level (layer) | Nível da rua | | scene / shot / scene recipe | cena / plano / receita de cena | | briefing / panel / dock | briefing / painel / dock | | cockpit | cabine | -| preset / visual preset | predefinição / predefinição visual | +| preset / visual preset / EXPORT PRESETS | predefinição / predefinição visual / EXPORTAR PREDEF. | | Scope (circular viewport mask) / keyhole | mira / visor | | feather / fade | suavização / esmaecimento | | detection overlay | sobreposição de detecção | @@ -76,6 +88,7 @@ The `many` form is the `other` text with «de» after the number: «{count} de c | Previous / Next (button text) | Anterior / Próximo (Próxima) | | L / R (relative bearing) | E / D | | SIG / NEWS / LOCAL (briefing tabs) | SIG / NOTÍCIAS / LOCAL | +| 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 | | turn-by-turn: turn left / keep left / lane | vire à esquerda / mantenha-se à esquerda / faixa da esquerda | diff --git a/src/i18n/locales/es.json b/src/i18n/locales/es.json index 0285917b0..3db1ff129 100644 --- a/src/i18n/locales/es.json +++ b/src/i18n/locales/es.json @@ -2226,6 +2226,7 @@ "GOOGLE_MAPS_API_KEY is not set": "GOOGLE_MAPS_API_KEY no está configurada", "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 weather manifest": "Manifiesto del clima mal formado", "Weather HTTP {status}": "Clima: HTTP {status}", "Weather image HTTP {status}": "Imagen del clima: HTTP {status}", diff --git a/src/i18n/locales/pt.json b/src/i18n/locales/pt.json index 5f260e65b..9f2bbe765 100644 --- a/src/i18n/locales/pt.json +++ b/src/i18n/locales/pt.json @@ -49,7 +49,7 @@ "Rasuwagadhi witness video": "Vídeo de testemunha em Rasuwagadhi", "Bhote Koshi valley footage": "Imagens do vale do Bhote Koshi", "Flood footage": "Imagens da inundação", - "Compilation and casualty claims": "Compilação e relatos de vítimas", + "Compilation and casualty claims": "Compilação e relatos sobre vítimas", "SOURCE GEOCONFIRMED · WORLD-PINNED": "FONTE GEOCONFIRMADA · FIXADA NO MAPA", "SOURCE CREDITED · LOCATION UNRESOLVED": "FONTE CREDITADA · LOCAL NÃO DETERMINADO", "REFERENCE ONLY · CLAIMS UNVERIFIED": "SÓ REFERÊNCIA · RELATOS NÃO VERIFICADOS", @@ -242,7 +242,7 @@ "Map tiles temporarily unavailable": "Blocos do mapa temporariamente indisponíveis", "Mapped names temporarily unavailable": "Nomes mapeados temporariamente indisponíveis", "Overpass temporarily unavailable": "Overpass temporariamente indisponível", - "Retrying mapped sites…": "Tentando de novo os locais mapeados…", + "Retrying mapped sites…": "Buscando de novo os locais mapeados…", "Fetching mapped sites…": "Buscando locais mapeados…", "{reason} — retrying in {seconds}s": "{reason} — nova tentativa em {seconds} s", "{reason} — retry pending": "{reason} — nova tentativa pendente", @@ -271,7 +271,7 @@ "NAV": "NAV", "GNSS navigation — GPS, GLONASS, Galileo": "Navegação GNSS — GPS, GLONASS, Galileo", "GEO": "GEO", - "Geostationary belt — comms and weather, fixed over the equator": "Cinturão geoestacionário — comunicações e clima, fixo sobre o equador", + "Geostationary belt — comms and weather, fixed over the equator": "Cinturão geoestacionário — comunicações e meteorologia, fixo sobre o equador", "VISUAL": "VISUAL", "Brightest naked-eye objects — CelesTrak visual group": "Objetos mais brilhantes a olho nu — grupo visual do CelesTrak", "COMMS": "TELECOM", @@ -293,7 +293,7 @@ "HIGH-SPEED": "ALTA VELOCIDADE", "PASSENGER": "PASSAGEIROS", "CARGO": "CARGUEIRO", - "TANKER": "PETROLEIRO", + "TANKER": "NAVIO-TANQUE", "OTHER": "OUTROS", "Included in bundle": "Incluído no pacote", "Missing bundle file — reimport its bundle": "Arquivo ausente no pacote — importe o pacote de novo", @@ -306,7 +306,7 @@ "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", - "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.", + "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.", "This browser is blocking storage, so that could not be saved.": "Este navegador está bloqueando o armazenamento, então não foi possível salvar.", @@ -340,7 +340,7 @@ "view band|GLOBAL": "GLOBAL", "SUMMARY": "RESUMO", "Awaiting telemetry...": "Aguardando telemetria...", - "REC": "GRAV", + "REC": "REC", "AIS: --": "AIS: --", "N/A": "N/D", "{mode} {band} {locality} | {region} | ALT {alt} | WINDOW {window} | SUN {sun}° | ONA {ona}° | {zone}": "{mode} · {band} · {locality} | {region} | ALT {alt} | JANELA {window} | SOL {sun}° | ONA {ona}° | {zone}", @@ -356,11 +356,11 @@ "other": "ATIVAR · {count} CHAVES PENDENTES" }, "POWERED UP": "ATIVADO", - "Provider Settings does not answer proxied requests": "Configurações de provedores não responde a requisições via proxy", + "Provider Settings does not answer proxied requests": "O painel \"Configurações de provedores\" não responde a requisições via proxy", "Provider Settings is disabled while sharing is enabled": "O painel \"Configurações de provedores\" fica desativado enquanto o compartilhamento estiver ativo", - "Provider Settings answers only the machine running the server": "Configurações de provedores só responde à máquina que executa o servidor", - "Provider Settings answers only local hostnames": "Configurações de provedores só responde a nomes de host locais", - "Provider Settings requires an exact local Origin": "Configurações de provedores exige um Origin local exato", + "Provider Settings answers only the machine running the server": "O painel \"Configurações de provedores\" só responde à máquina que executa o servidor", + "Provider Settings answers only local hostnames": "O painel \"Configurações de provedores\" só responde a nomes de host locais", + "Provider Settings requires an exact local Origin": "O painel \"Configurações de provedores\" exige um Origin local exato", "Unrecognized Origin refused": "Origin não reconhecido: requisição recusada", "Cross-origin requests are refused": "Requisições de outra origem são recusadas", "Content-Type must be application/json": "Content-Type deve ser application/json", @@ -426,7 +426,7 @@ "Places context + Street View fallback; optional separate key": "Contexto de lugares + Street View como alternativa; chave separada opcional", "Voice control — API key or ChatGPT OAuth": "Controle por voz — chave de API ou OAuth do ChatGPT", "Gemini Live voice control": "Controle por voz com Gemini Live", - "Live ships, worldwide": "Navios ao vivo, no mundo todo", + "Live ships, worldwide": "Embarcações ao vivo, no mundo todo", "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", @@ -442,13 +442,13 @@ "other": "{count} estações com dados" }, "Highest air-quality readings in view": "Leituras de qualidade do ar mais altas na vista", - "Low": "Baixa", + "Low": "Risco baixo", "Low health risk": "Risco baixo para a saúde", - "Moderate": "Moderada", + "Moderate": "Risco moderado", "Moderate health risk": "Risco moderado para a saúde", - "High": "Alta", + "High": "Risco alto", "High health risk": "Risco alto para a saúde", - "Very High": "Muito alta", + "Very High": "Risco muito alto", "Very High health risk": "Risco muito alto para a saúde", "Air quality": "Qualidade do ar", "Canada only · ECCC AQHI network": "Só Canadá · rede AQHI do ECCC", @@ -473,7 +473,7 @@ "many": "{count} de câmeras próximas", "other": "{count} próximas" }, - "retrying mapped ALPR cameras": "tentando de novo as câmeras ALPR mapeadas", + "retrying mapped ALPR cameras": "buscando de novo as câmeras ALPR mapeadas", "loading mapped ALPR cameras": "carregando câmeras ALPR mapeadas", "No ALPR data for this area — US and Canada only": "Sem dados ALPR para esta área — só EUA e Canadá", "Zoom in to load mapped cameras": "Aproxime o zoom para carregar as câmeras mapeadas", @@ -520,7 +520,7 @@ "GBFS fetch error": "erro ao obter dados GBFS", "Station {id}": "Estação {id}", "Station": "Estação", - "🚲 {bikes} avail · {docks} docks · {capacity} cap": "🚲 {bikes} disp · {docks} docas · {capacity} cap", + "🚲 {bikes} avail · {docks} docks · {capacity} cap": "🚲 {bikes} disp. · {docks} vagas · {capacity} cap.", "⚠️ Not installed": "⚠️ Não instalada", "⚠️ Not renting": "⚠️ Sem aluguel", "⚠️ Not returning": "⚠️ Sem devolução", @@ -561,9 +561,9 @@ "many": "{count} de ciclones ativos", "other": "{count} ciclones ativos" }, - "Position as of {time}": "Posição às {time}", + "Position as of {time}": "Posição em {time}", "Maximum sustained wind: {wind} · Pressure: {pressure}": "Vento máximo sustentado: {wind} · Pressão: {pressure}", - "{name} · {classification} · Advisory {number} · issued {time}": "{name} · {classification} · Boletim {number} · emitido {time}", + "{name} · {classification} · Advisory {number} · issued {time}": "{name} · {classification} · Boletim {number} · emitido em {time}", "No active NHC/CPHC systems": "Nenhum sistema ativo do NHC/CPHC", "Advisories unavailable": "Boletins indisponíveis", "Cyclones · NHC / CPHC": "Ciclones · NHC / CPHC", @@ -785,7 +785,7 @@ "installation class|BASE": "BASE", "installation class|BARRACKS": "QUARTEL", "installation class|TRAINING AREA": "ÁREA DE TREINAMENTO", - "installation class|PLACES CANDIDATE": "DO GOOGLE PLACES", + "installation class|PLACES CANDIDATE": "CANDIDATO DO GOOGLE PLACES", "installation class|INSTALLATION": "INSTALAÇÃO", "No mapped sites within {km} km of the contact": "Nenhum local mapeado a até {km} km do contato", "{count} mapped sites within {km} km of the contact": { @@ -817,7 +817,7 @@ "UNSPECIFIED": "NÃO ESPECIFICADO", "+{count} additional payload records": "+{count} registros de carga útil adicionais", "PAYLOAD DATA UNAVAILABLE": "DADOS DA CARGA ÚTIL INDISPONÍVEIS", - "{value} KM DOWNRANGE": "{value} KM DE PERCURSO", + "{value} KM DOWNRANGE": "A {value} KM DO LOCAL DE LANÇAMENTO", "POSITION UNAVAILABLE": "POSIÇÃO INDISPONÍVEL", "FLIGHT {count}": "VOO {count}", "REUSED": "REUTILIZADO", @@ -858,7 +858,7 @@ "SHOW ALL / DESELECT": "MOSTRAR TODAS / DESMARCAR", "Pause replay": "Pausar reprise", "Cancel replay": "Cancelar reprise", - "EST. DOWNRANGE": "DIST. ESTIMADA", + "EST. DOWNRANGE": "EST. PELA DIST. DO LANÇAMENTO", "PLANNED · ": "PLANEJADO · ", "SUPPLIED TRAJECTORY POINTS": "PONTOS DE TRAJETÓRIA FORNECIDOS", "RECONSTRUCTED ESTIMATE": "ESTIMATIVA RECONSTRUÍDA", @@ -880,7 +880,7 @@ "Your decoder feed": "Seu feed do decodificador", "Your RTL-SDR receiver": "Seu receptor RTL-SDR", "NO CALLSIGN": "SEM INDICATIVO", - "ALT {altitude} FT · GS {speed} KT · TRK {track}": "ALT {altitude} FT · VEL. SOLO {speed} KT · TRK {track}", + "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", "POSITION {age}": "POSIÇÃO {age}", @@ -984,7 +984,7 @@ "Jazz": "Jazz", "Latin": "Latina", "Metal": "Metal", - "Oldies": "Clássicos", + "Oldies": "Flashback", "Pop": "Pop", "Punk": "Punk", "R&B": "R&B", @@ -993,7 +993,7 @@ "Soul": "Soul", "Techno": "Techno", "Trance": "Trance", - "World": "Mundial", + "World": "Músicas do mundo", "overview": "visão geral", "cloud unknown": "nebulosidade desconhecida", "{percent}% scene cloud": "{percent}% de nuvens na cena", @@ -1176,9 +1176,9 @@ "API key rejected — check AISSTREAM_API_KEY": "Chave de API recusada — verifique a AISSTREAM_API_KEY", "feed silent {seconds}s — no AIS data": "feed em silêncio há {seconds} s — sem dados AIS", "feed silent — no AIS data": "feed em silêncio — sem dados AIS", - "feed down — retrying slowly (attempt {attempt})": "feed fora do ar — tentando de novo devagar (tentativa {attempt})", + "feed down — retrying slowly (attempt {attempt})": "feed fora do ar — tentando de novo em intervalos maiores (tentativa {attempt})", "reconnecting to feed… (attempt {attempt})": "reconectando ao feed… (tentativa {attempt})", - "feed down — retrying slowly": "feed fora do ar — tentando de novo devagar", + "feed down — retrying slowly": "feed fora do ar — tentando de novo em intervalos maiores", "reconnecting to feed…": "reconectando ao feed…", "Focus vessel {name}, MMSI {mmsi}": "Focar a embarcação {name}, MMSI {mmsi}", "Hidden by this map source · choose a globe map": "Oculto com esta fonte do mapa · escolha um mapa de globo", @@ -1258,6 +1258,7 @@ "Some weather tiles unavailable": "Alguns blocos meteorológicos indisponíveis", "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", "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", @@ -1287,7 +1288,7 @@ "Temperature": "Temperatura", "Wind trails with no field shading": "Rastros de vento sem sombreamento do campo", "How hard the surface wind is blowing": "Com que força o vento sopra na superfície", - "Sea-level air pressure: broad highs and lows": "Pressão do ar ao nível do mar: altas e baixas amplas", + "Sea-level air pressure: broad highs and lows": "Pressão do ar ao nível do mar: amplas áreas de alta e baixa pressão", "Air temperature two meters above the surface": "Temperatura do ar a dois metros acima da superfície", "Wind speed units": "Unidades de velocidade do vento", "Read wind at map center": "Ler o vento no centro do mapa", @@ -1327,7 +1328,7 @@ " · calm": " · calmo", " from {direction}": " de {direction}", "WIND AT {coordinates}": "VENTO EM {coordinates}", - "{model} · valid {time}": "{model} · válida {time}", + "{model} · valid {time}": "{model} · válida para {time}", "Wind speed field unavailable": "Campo de velocidade do vento indisponível", "Globe imagery unavailable": "Imagens do globo indisponíveis", "Globe field image unavailable": "Imagem do campo no globo indisponível", @@ -1339,7 +1340,7 @@ "Error: {message}": "Erro: {message}", "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 mapa", + "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", @@ -1596,15 +1597,15 @@ "FRAME · LOADING": "QUADRO · CARREGANDO", "FRAME · UNAVAILABLE": "QUADRO · INDISPONÍVEL", "CALIBRATED": "CALIBRADA", - "CURATED": "CURADA", + "CURATED": "COM CURADORIA", "RAW PRIOR": "ESTIMATIVA BRUTA", "CCTV ON": "CCTV LIGADO", "CCTV OFF": "CCTV DESLIG.", "VIEWSHED ON": "ÁREA VISÍVEL LIGADA", "COVERAGE ON": "COBERTURA LIGADA", "COVERAGE OFF": "COBERTURA DESLIG.", - "AUTO HOP ON": "AUTO SALTO LIGADO", - "AUTO HOP OFF": "AUTO SALTO DESLIG.", + "AUTO HOP ON": "TROCA AUTO. LIGADA", + "AUTO HOP OFF": "TROCA AUTO. DESLIG.", "PROJECTION ON": "PROJEÇÃO LIGADA", "PROJECTION OFF": "PROJEÇÃO DESLIG.", "CAL · EDITED (UNSAVED)": "CAL · EDITADA (NÃO SALVA)", @@ -1633,7 +1634,7 @@ "CLEAR": "CÉU LIMPO", "PARTLY CLOUDY": "PARCIALMENTE NUBLADO", "OVERCAST": "ENCOBERTO", - "FOG": "NEBLINA", + "FOG": "NEVOEIRO", "DRIZZLE": "GAROA", "RAIN": "CHUVA", "RAIN SHOWERS": "PANCADAS DE CHUVA", @@ -1644,17 +1645,17 @@ "CYCLE OFF": "CICLO DESLIG.", "Stop automatic page cycling. Previous, Next, and the SIG/NEWS/LOCAL tabs stay available.": "Parar a rotação automática de páginas. Anterior, Próxima e as abas SIG/NOTÍCIAS/LOCAL continuam disponíveis.", "Cycle briefing pages automatically every 9 seconds (Signals → News → Local). Pauses while you hover or focus the panel. Live signal data refreshes continuously either way.": "Alterna as páginas do briefing automaticamente a cada 9 segundos (Sinais → Notícias → Local). Pausa enquanto você passa o mouse ou foca o painel. Os dados de sinais ao vivo continuam atualizando de qualquer forma.", - "ACQUIRING REGIONAL NEWS": "OBTENDO NOTÍCIAS LOCAIS", - "REGIONAL NEWS UNAVAILABLE": "NOTÍCIAS LOCAIS INDISPONÍVEIS", + "ACQUIRING REGIONAL NEWS": "OBTENDO NOTÍCIAS DA ÁREA", + "REGIONAL NEWS UNAVAILABLE": "NOTÍCIAS DA ÁREA INDISPONÍVEIS", "RESOLVING REGION": "IDENTIFICANDO REGIÃO", "REGION UNAVAILABLE": "REGIÃO INDISPONÍVEL", "NO RECENT LOCATION MATCHES": "NENHUMA CORRESPONDÊNCIA RECENTE COM O LOCAL", "{domain} · {age}": "{domain} · {age}", "{value} KM/H": "{value} KM/H", "CLOUD {pct}%": "NUVENS {pct}%", - "CLOUD UNKNOWN": "NUVENS DESCONHECIDAS", + "CLOUD UNKNOWN": "NEBULOSIDADE DESCONHECIDA", "{source} · LOCATION QUERY": "{source} · CONSULTA POR LOCAL", - "REGIONAL NEWS": "NOTÍCIAS LOCAIS", + "REGIONAL NEWS": "NOTÍCIAS DA ÁREA", "{label} · {radius} AIR/SEA WINDOW": "{label} · AR/MAR {radius}", "{scope} · INSTALLATIONS {coverage}": "{scope} · INSTALAÇÕES: {coverage}", "CONTEXT STANDBY": "CONTEXTO EM ESPERA", @@ -1676,8 +1677,8 @@ }, "AVAILABLE INPUTS CURRENT · NOT AN ALL-CLEAR": "ENTRADAS DISPONÍVEIS ATUAIS · NÃO GARANTE SEGURANÇA", "SQUAWK {code} · {status}": "SQUAWK {code} · {status}", - "ADS-B PRIORITY · {status}": "ADS-B PRIORIDADE · {status}", - "ADS-B EMERGENCY · {status}": "ADS-B EMERGÊNCIA · {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", "SURFACE FALLBACK": "SUPERFÍCIE ALTERNATIVA", @@ -1698,7 +1699,7 @@ "Anime": "Anime", "Noir": "Noir", "Snow": "Neve", - "Current cockpit vision style: {style}. Activate for next style.": "Estilo de visão da cabine atual: {style}. Ative para o próximo estilo.", + "Current cockpit vision style: {style}. Activate for next style.": "Estilo atual de visão da cabine: {style}. Ative para o próximo estilo.", "Current style: {style} — click for next": "Estilo atual: {style} — clique para o próximo", "Collapse Contact panel": "Recolher painel de contato", "Expand Contact panel": "Expandir painel de contato", @@ -1794,7 +1795,7 @@ "{requirement}, or {offer}": "{requirement}, ou {offer}", "Live Flights": "Voos ao vivo", "Military Flights": "Voos militares", - "Earthquakes (24h)": "Terremotos (24h)", + "Earthquakes (24h)": "Terremotos (24 h)", "Satellites": "Satélites", "Space Missions (30d)": "Missões espaciais (30 d)", "Street Traffic": "Trânsito urbano", @@ -2093,7 +2094,7 @@ "Download {slot} as a PNG": "Baixar {slot} como PNG", "Download the image as a PNG": "Baixar a imagem como PNG", "Box {width} × {height} km": "Área de {width} × {height} km", - "Acquired {time}": "Adquirida {time}", + "Acquired {time}": "Adquirida em {time}", "Coverage {coverage}": "Cobertura {coverage}", "START HERE · {reason}": "COMECE AQUI · {reason}", "Daily overview shows little detail in a box this small": "A visão geral diária mostra pouco detalhe em uma área tão pequena", @@ -2116,8 +2117,8 @@ "Delete scene \"{scene}\" and all shots?": "Excluir a cena \"{scene}\" e todos os planos?", "Delete shot \"{shot}\"?": "Excluir o plano \"{shot}\"?", "Stopped (Esc)": "Parado (Esc)", - "SPARSE": "ESPARSA", - "BALANCED": "EQUILIBRADA", + "SPARSE": "ESPARSO", + "BALANCED": "EQUILIBRADO", "No shots yet. Use CAPTURE SHOT to save current look.": "Ainda não há planos. Use CAPTURAR PLANO para salvar o visual atual.", "Double-click to rename": "Clique duplo para renomear", "Shot name": "Nome do plano", @@ -2155,14 +2156,14 @@ "SHARED {subject}": "COMPARTILHADO: {subject}", "subject": "alvo", "LOADING LIVE DATA": "CARREGANDO DADOS AO VIVO", - "LOAD COMPLETE": "CARGA CONCLUÍDA", - "LOAD CANCELLED": "CARGA CANCELADA", - "LOAD FAILED": "FALHA NA CARGA", + "LOAD COMPLETE": "CARREGAMENTO CONCLUÍDO", + "LOAD CANCELLED": "CARREGAMENTO CANCELADO", + "LOAD FAILED": "FALHA NO CARREGAMENTO", "LIVE DATA OFF": "DADOS AO VIVO DESLIGADOS", "MAPPED SITES LOADED": "LOCAIS MAPEADOS CARREGADOS", - "RETRYING MAPPED SITES": "TENTANDO DE NOVO OS LOCAIS MAPEADOS", + "RETRYING MAPPED SITES": "BUSCANDO DE NOVO OS LOCAIS MAPEADOS", "FETCHING MAPPED SITES": "BUSCANDO LOCAIS MAPEADOS", - "RETRYING ALPR CAMERAS": "TENTANDO DE NOVO AS CÂMERAS ALPR", + "RETRYING ALPR CAMERAS": "BUSCANDO DE NOVO AS CÂMERAS ALPR", "FETCHING ALPR CAMERAS": "BUSCANDO CÂMERAS ALPR", "OVERPASS TEMPORARILY UNAVAILABLE": "OVERPASS TEMPORARIAMENTE INDISPONÍVEL", "OVERPASS RATE-LIMITED": "OVERPASS COM LIMITE DE REQUISIÇÕES", @@ -2306,9 +2307,9 @@ "{time} · {minutes} min ago": "{time} · há {minutes} min", "Active weather": "Camadas de clima ativas", "LATEST · newest per product": "MAIS RECENTE · último por produto", - "Forecast · valid {valid} · issued {issued}": "Previsão · válida {valid} · emitida {issued}", + "Forecast · valid {valid} · issued {issued}": "Previsão · válida para {valid} · emitida em {issued}", "{detail} · Does not follow history": "{detail} · Não acompanha o histórico", - "Forecast · valid {valid}": "Previsão · válida {valid}", + "Forecast · valid {valid}": "Previsão · válida para {valid}", "voice|OFF": "DESLIG.", "voice|CONNECTING": "CONECT…", "voice|LISTENING": "OUVINDO", @@ -2703,9 +2704,9 @@ "FIRST PERSON": "PRIMEIRA PESSOA", "LIVE TRACK · COURSE ALIGNED": "RASTRO AO VIVO · CURSO ALINHADO", "Cockpit vision style": "Estilo de visão da cabine", - "Previous cockpit vision style": "Estilo de visão da cabine anterior", + "Previous cockpit vision style": "Estilo anterior de visão da cabine", "Previous vision style": "Estilo de visão anterior", - "Current cockpit vision style: NORMAL. Activate for next style.": "Estilo de visão da cabine atual: NORMAL. Ative para o próximo estilo.", + "Current cockpit vision style: NORMAL. Activate for next style.": "Estilo atual de visão da cabine: NORMAL. Ative para o próximo estilo.", "Current style: NORMAL — click for next": "Estilo atual: NORMAL — clique para o próximo", "CURRENT": "ATUAL", "Next cockpit vision style": "Próximo estilo de visão da cabine", @@ -2736,7 +2737,7 @@ "Previous briefing page": "Página anterior do briefing", "Next briefing page": "Próxima página do briefing", "Live signals": "Sinais ao vivo", - "Latest regional news": "Últimas notícias locais", + "Latest regional news": "Últimas notícias da área", "Location-based information": "Informações baseadas na localização", "TEMP": "TEMP", "WIND": "VENTO", @@ -2748,7 +2749,7 @@ "SIG": "SIG", "Show Live Signals": "Mostrar sinais ao vivo", "NEWS": "NOTÍCIAS", - "Show Regional News": "Mostrar notícias locais", + "Show Regional News": "Mostrar notícias da área", "LOCAL": "LOCAL", "Show Local Info": "Mostrar informações locais", "Cockpit display and Radio controls": "Controles de exibição e rádio da cabine", @@ -2819,7 +2820,7 @@ "Internet radio and local SDR": "Rádio pela internet e SDR local", "STATION TAG": "TAG DA ESTAÇÃO", "Filter stations by station tag": "Filtrar estações por tag", - "All": "Todas", + "All": "Todos", "Enable Radio, then choose a globe marker or use next.": "Ative o rádio e escolha um marcador no globo ou use Próxima.", "DRAG TO TUNE": "ARRASTE PARA SINTONIZAR", "Tune available internet radio stations": "Sintonizar as estações de rádio pela internet disponíveis", @@ -2834,7 +2835,7 @@ "Radio volume": "Volume do rádio", "STATION SITE": "SITE DA ESTAÇÃO", "DIRECTORY: RADIO BROWSER": "DIRETÓRIO: RADIO BROWSER", - "Audio connects directly to the broadcaster after you press play. Your IP is visible to that broadcaster.": "O áudio conecta direto à emissora depois que você aperta play. Seu IP fica visível para essa emissora.", + "Audio connects directly to the broadcaster after you press play. Your IP is visible to that broadcaster.": "O áudio conecta direto à emissora depois que você aperta \"Ouvir\". Seu IP fica visível para essa emissora.", "Local RTL-SDR receiver": "Receptor RTL-SDR local", "LOCAL RTL-SDR": "RTL-SDR LOCAL", "Chrome/Edge · WebUSB · one local tuner shared by FM and the Local ADS-B layer.": "Chrome/Edge · WebUSB · um sintonizador local compartilhado entre o FM e a camada ADS-B local.", @@ -2872,7 +2873,7 @@ "Sonar intensity": "Intensidade do sonar", "Opacity": "Opacidade", "Cyber contact and label opacity": "Opacidade de contatos e rótulos Ciber", - "Minimum opacity for map dots, icons, models, brackets, and labels between sonar passes": "Opacidade mínima de pontos, ícones, modelos, colchetes e rótulos do mapa entre as passagens do sonar", + "Minimum opacity for map dots, icons, models, brackets, and labels between sonar passes": "Opacidade mínima de pontos, ícones, modelos, marcadores de alvo e rótulos do mapa entre as passagens do sonar", "Sector": "Setor", "Sonar sector width": "Largura do setor do sonar", "Detection Overlay (D)": "Sobreposição de detecção (D)", @@ -2972,14 +2973,14 @@ "CAPTURE SHOT": "CAPTURAR PLANO", "UPDATE SHOT": "ATUALIZAR PLANO", "START": "INICIAR", - "EXPORT PRESETS": "EXPORTAR PREDEFINIÇÕES", + "EXPORT PRESETS": "EXPORTAR PREDEF.", "IMPORT": "IMPORTAR", "RUN LOG": "REGISTRO", "POWER UP": "ATIVAR", "GROUND STATION · PROVIDER SETTINGS": "ESTAÇÃO TERRESTRE · CONFIGURAÇÕES DE PROVEDORES", "Close key setup": "Fechar configuração de chaves", "Power up the globe": "Ative o globo", - "The globe already flies keyless. Every key below switches on another real feed — paste one and it's saved into this app's local configuration, then the server restarts itself. Server-side keys stay on this machine; Google Maps and Cesium ion run in the browser and must be provider-restricted. Keys you configured elsewhere are shown but never touched.": "O globo já voa sem chaves. Cada chave abaixo ativa mais uma fonte real — cole uma e ela será salva na configuração local deste app, e então o servidor reinicia sozinho. As chaves do servidor ficam nesta máquina; Google Maps e Cesium ion rodam no navegador e devem ser restritas ao provedor. Chaves configuradas em outro lugar são exibidas, mas nunca alteradas.", + "The globe already flies keyless. Every key below switches on another real feed — paste one and it's saved into this app's local configuration, then the server restarts itself. Server-side keys stay on this machine; Google Maps and Cesium ion run in the browser and must be provider-restricted. Keys you configured elsewhere are shown but never touched.": "O globo já voa sem chaves. Cada chave abaixo ativa mais uma fonte real — cole uma e ela será salva na configuração local deste app, e então o servidor reinicia sozinho. As chaves do servidor ficam nesta máquina; as chaves do Google Maps e do Cesium ion são usadas no navegador e precisam ter restrições de uso configuradas no respectivo provedor. Chaves configuradas em outro lugar são exibidas, mas nunca alteradas.", "SAVE KEYS": "SALVAR CHAVES", "ESC to close": "ESC para fechar", "The Google Maps key buys the photorealistic planet — everything else stacks on top.": "A chave do Google Maps libera o planeta fotorrealista — todo o resto se soma a ela.", @@ -2995,7 +2996,7 @@ "MISSION CONTROL · FIRST LAUNCH": "CENTRO DE CONTROLE · PRIMEIRO ACESSO", "Choose your first view": "Escolha sua primeira vista", "LIVE CONTACTS": "CONTATOS AO VIVO", - "Aircraft, vessels and nearby intelligence": "Aeronaves, embarcações e inteligência próxima", + "Aircraft, vessels and nearby intelligence": "Aeronaves, embarcações e informações da região", "Launches, spacecraft and orbital context": "Lançamentos, espaçonaves e contexto orbital", "EXPLORE MANUALLY": "EXPLORAR MANUALMENTE", "Begin with a clean globe": "Comece com um globo limpo", diff --git a/src/i18n/locales/ru.json b/src/i18n/locales/ru.json index b1c1be229..93e2b871d 100644 --- a/src/i18n/locales/ru.json +++ b/src/i18n/locales/ru.json @@ -2262,6 +2262,7 @@ "GOOGLE_MAPS_API_KEY is not set": "GOOGLE_MAPS_API_KEY не задан", "The Google Maps JavaScript API could not load": "Не удалось загрузить Google Maps JavaScript API", "Weather request timed out": "Истекло время ожидания запроса погоды", + "Cyclone request timed out": "Истекло время ожидания запроса циклонов", "Malformed weather manifest": "Некорректный манифест погоды", "Weather HTTP {status}": "Погода: HTTP {status}", "Weather image HTTP {status}": "Снимок погоды: HTTP {status}", diff --git a/src/i18n/locales/zh-CN.json b/src/i18n/locales/zh-CN.json index 9bf76700c..aca16f65a 100644 --- a/src/i18n/locales/zh-CN.json +++ b/src/i18n/locales/zh-CN.json @@ -1202,6 +1202,7 @@ "Some weather tiles unavailable": "部分天气瓦片不可用", "Malformed weather manifest": "天气清单格式错误", "Weather request timed out": "天气请求超时", + "Cyclone request timed out": "气旋请求超时", "Temperature field unavailable": "温度场不可用", "Mean sea level pressure field unavailable": "平均海平面气压场不可用", "Wind upstream unavailable": "风场上游数据源不可用", diff --git a/src/i18n/locales/zh-Hant.json b/src/i18n/locales/zh-Hant.json index d95a18e39..9fe469f6e 100644 --- a/src/i18n/locales/zh-Hant.json +++ b/src/i18n/locales/zh-Hant.json @@ -1197,6 +1197,7 @@ "Some weather tiles unavailable": "部分天氣瓦片不可用", "Malformed weather manifest": "天氣清單格式錯誤", "Weather request timed out": "天氣請求超時", + "Cyclone request timed out": "氣旋請求超時", "Temperature field unavailable": "溫度場不可用", "Mean sea level pressure field unavailable": "平均海平面氣壓場不可用", "Wind upstream unavailable": "風場上游數據源不可用", diff --git a/src/i18n/same-as-english.json b/src/i18n/same-as-english.json index 920016262..90abbc6a8 100644 --- a/src/i18n/same-as-english.json +++ b/src/i18n/same-as-english.json @@ -129,6 +129,7 @@ "978 MHz UAT", "AIS: --", "AIS: {name}", + "ALT {altitude} FT · GS {speed} KT · TRK {track}", "ALTITUDE", "ALTITUDE · FT", "ANIME", @@ -202,6 +203,7 @@ "Pop", "Punk", "RANGE", + "REC", "Reggae", "Rock", "S2 GhostMaps", diff --git a/src/layers/cyclones/source.js b/src/layers/cyclones/source.js index 05c805fbd..e0039a20e 100644 --- a/src/layers/cyclones/source.js +++ b/src/layers/cyclones/source.js @@ -1,5 +1,9 @@ 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). +const msg = (text) => text; + export const CYCLONE_RESPONSE_LIMIT = 4 * 1024 * 1024; const malformed = () => new Error('Malformed cyclone snapshot'); const text = (value, max = 160) => { @@ -215,7 +219,7 @@ export function createCycloneSource({ const abort = () => controller.abort(signal.reason); signal?.addEventListener('abort', abort, { once: true }); const timer = setTimeout( - () => controller.abort(new Error('Cyclone request timed out')), + () => controller.abort(new Error(msg('Cyclone request timed out'))), timeoutMs, ); try { diff --git a/src/scenes/recipes.js b/src/scenes/recipes.js index ba3940a57..348579744 100644 --- a/src/scenes/recipes.js +++ b/src/scenes/recipes.js @@ -1062,24 +1062,37 @@ export function displaySceneTitle(scene) { * 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, appliedShotPacks?: {id: string}[]}} [scene] The + * shot's scene, for shots without a source pack: projects saved before + * shots recorded one, and the legacy base shots a pack bootstrap rewrote. * @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, 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 a shot with this title: + * its own keyframes, or the legacy base shots an applied append pack wrote + * (those carry no sourcePackId, see SceneDirector._bootstrapLegacyShotPacks). + */ +function recipeDeclaresShotTitle(scene, title) { + const declares = (cameraPath) => + Boolean(cameraPath?.some((keyframe) => keyframe?.title === title)); + const recipe = scene?.id ? getSceneRecipeById(scene.id) : null; + if (declares(recipe?.cameraPath)) return true; + const packs = Array.isArray(scene?.appliedShotPacks) + ? scene.appliedShotPacks + : []; + return packs.some((entry) => + declares( + getSceneAppendRecipeById(entry?.id)?.legacySceneBootstrap?.cameraPath, + ), ); } diff --git a/src/scenes/sceneTitleDisplay.test.mjs b/src/scenes/sceneTitleDisplay.test.mjs index 604e01c49..2a9aad9f9 100644 --- a/src/scenes/sceneTitleDisplay.test.mjs +++ b/src/scenes/sceneTitleDisplay.test.mjs @@ -141,3 +141,26 @@ test('the scene context of a shot carries its display title by provenance', asyn title, ); }); + +test('legacy base shots of an applied append pack show their built-in names', async () => { + const { SCENE_APPEND_RECIPES } = await import('./recipes.js'); + const pack = SCENE_APPEND_RECIPES.find( + (recipe) => recipe.legacySceneBootstrap?.cameraPath?.length, + ); + if (!pack) return; + setLocale('ru'); + const title = pack.legacySceneBootstrap.cameraPath[0].title; + const shot = { id: 'legacy-base', title }; + // The bootstrap writes these shots without sourcePackId, into a scene whose + // own recipe only declares "Shot N". + assert.equal( + displayShotTitle(title, shot, { + id: 'bhote-koshi-nepal-scene', + appliedShotPacks: [{ id: pack.id, version: pack.version }], + }), + displayShotTitle(title), + ); + assert.notEqual(displayShotTitle(title), title); + // Without the pack marker the same title in another scene stays as typed. + assert.equal(displayShotTitle(title, shot, { id: 'scene-user' }), title); +});