Tradução automática, não revista por um falante nativo. O inglês é a referência.

Primeiros passosAinda não está no GitHubFonte: docs/i18n.md

Localização do motor

O texto do motor voltado às pessoas está em 17 idiomas além do inglês: nl, pl, pt-BR, pt-PT, fi, sv, da, nb, cs, uk, ru, de, fr, es, et, ja, zh-Hans. O inglês é a fonte; en-XA é uma pseudolocalidade que acentua, coloca entre colchetes e preenche cada mensagem, de modo que um teste pode ver se o texto foi para o catálogo. O framework é o Project Fluent (fluent-bundle, unic-langid); o código está em src/i18n/.

O que é localizado e o que nunca é#

Localizado (uma pessoa lê) Nunca localizado (um programa lê)
mensagens e erros da linha de comando chaves e valores JSON de result.json e report.json
a linha de resumo de cada execução cabeçalhos e linhas de CSV
o texto dos gráficos SVG (títulos, eixos, legendas, descrições) nomes de arquivos
report.html: títulos, tabelas, notas, legendas códigos de erro (E-CLOCK-VERSION)
palavras de veredito (VALIDATED, MODELLED, PARTIAL) a matriz de verificação, definições de unidades, proveniência
as descrições dos tipos de cenário (list_kinds(lang))
declarações consultivas, avisos legais e comunicados (engine-legal-*) os mesmos textos dentro de JSON, que permanecem em inglês
report.html da campanha iq, a página de comparação de estudos, o placar do bench, o relatório de resiliência summary.html de um pacote de evidências e o PNG do observatório: ambos têm hash em um manifesto (o do pacote também é assinado), por isso são gerados em inglês qualquer que seja o idioma

Os números em texto para pessoas usam o separador decimal do idioma (uma vírgula em 15 dos 18 idiomas; um ponto em en, ja, zh-Hans e en-XA). Os números em arquivos de máquina sempre usam o ponto. Uma contagem (uma variável chamada n, count, n_*, num_* ou *_count) chega ao Fluent como número, de modo que uma tradução pode escrever { $n -> [one] ... [few] ... *[other] ... }.

Escolha do idioma#

  • Linha de comando: --lang <tag>, depois KSHANA_LANG, LC_ALL, LC_MESSAGES, LANG, depois o inglês. pt é pt-BR; apenas pt-PT é o de Portugal. zh é o simplificado; o tradicional é recusado com a lista das etiquetas aceitas.
  • Biblioteca: api::run_toml_lang(toml, lang), run_toml_bilingual, run_toml_human, run_scenario_lang, list_scenario_kinds_json_lang(lang).
  • Python: lang=None em run, run_full, run_typed, validate_toml, error_kind, list_kinds, scenario_kinds; run_human(toml, lang); error_info(toml, lang).
  • WebAssembly: um lang final opcional em run_typed, run, chart_svg, summary, table_csv, run_all, error_kind, list_kinds, scenario_kinds; run_human(toml, lang); error_info(toml, lang); register_catalogue(tag, ftl_text).
  • MCP: lang em run_scenario e list_scenario_kinds.

Erros e seus códigos#

Toda mensagem de erro tem um código estável, o id da mensagem em maiúsculas (e-clock-version é E-CLOCK-VERSION), o mesmo em todos os idiomas. O texto da linha de comando em inglês não muda. Em outro idioma a linha é text [E-CODE]; com --verbose uma segunda linha code: E-CODE vem em seguida em todos os idiomas. A API fornece KshanaError::code() e error_info ({kind, code, message}); os erros do MCP trazem {kind, code, message} como dados do erro. Compare pelo código, nunca pelo texto.

Como uma execução funciona em outro idioma (a regra de executar duas vezes)#

Um arquivo de máquina não pode depender do idioma. Uma execução em outro idioma portanto executa o cenário duas vezes: uma em inglês, cujos result.json, report.json e CSV são os arquivos de máquina, e outra no idioma do usuário, para o resumo, o gráfico e report.html. O inglês é uma única execução, byte a byte o que era antes.

Medido, nativo, build de release, inglês e depois --lang de (segundos): leo-navmsg-fit-interval-trade 9,2 -> 17,9, cislunar-arc-recovery 7,8 -> 14,1, lunar-station-coverage 7,8 -> 15,0, clock-ensemble 5,8 -> 11,6, leo-navmsg-model-comparison 5,1 -> 10,5, leo-ppp-convergence 2,8 -> 6,2; todo outro cenário incluído leva menos de 4 s; os 173 cenários incluídos quando isso foi medido levaram 64 s em inglês no total. O custo é exatamente o dobro para outro idioma. O WebAssembly não foi medido (sem wasm-bindgen aqui); com o fator do navegador de cerca de 4, é um minuto para o tipo de cenário mais lento.

Uma página evita a espera mostrando o resultado em inglês de imediato e chamando run_human(toml, lang) depois: essa é apenas a segunda execução, retorna {summary, svg}, e o Studio pode dizer "traduzindo..." até que ela chegue.

Item da 0,41: uma única execução. Renderizar os dois a partir de um registro de (message id, arguments). Foi avaliado e não construído: os resultados de t! são inspecionados, medidos e têm hash calculado depois de produzidos (larguras do texto dos gráficos, hashes de proveniência sobre o texto de saída), e num() formata no ponto de chamada, de modo que um registro poria marcadores provisórios nos arquivos de máquina e faria seus bytes dependerem da repetição. Exigiria t!/num que devolvem marcadores, um finalizador em cada gravador e uma prova de identidade de bytes por gravador.

WebAssembly#

A build wasm incorpora apenas o catálogo em inglês. Os outros dezesseis acrescentariam cerca de 150 kB gzip cada um. Uma página busca i18n/<lang>/engine.ftl quando um idioma é escolhido e chama register_catalogue(tag, text); se a busca ou o registro falhar, a página permanece em inglês. Nativamente, a linha de comando e o wheel de Python incorporam todos os 18.

Tamanho, .wasm sem otimização a partir de cargo build --release --target wasm32-unknown-unknown --features wasm --lib (antes do wasm-bindgen e do wasm-opt), base bc04e98f contra esta ramificação: 18,71 MB -> 19,88 MB bruto, 5,43 MB -> 5,79 MB gzip, +357 kB gzip. O catálogo em inglês tem 521 kB brutos, 153 kB deles em gzip; o resto é fluent-bundle e suas dependências. Isso é aceito como medido.

O catálogo#

i18n/<tag>/engine.ftl contém as mensagens, com ids prefixados por engine-. i18n/en/engine.ftl é a fonte; os outros arquivos são dos tradutores e podem não ter mensagens (cada uma recorre ao inglês por mensagem). Listas complementares em i18n/en/: engine-counts.txt (mensagens com variáveis de contagem, para as formas plurais), engine-numbers.txt (números passados como placeables), engine-fragments.txt (mensagens que são partes de uma frase montada em tempo de execução: devem ser reunidas em mensagens de frase completa, nunca aumentar).

Testes#

tests/i18n_catalogue.rs prova o inglês: toda mensagem tem um modelo original registrado e é renderizada exatamente nesse texto; todo id é usado; as variáveis coincidem; a pseudolocalidade renderiza todas as mensagens. i18n_api.rs, i18n_kinds.rs, i18n_legal.rs cobrem os pontos de entrada, as descrições dos tipos de cenário e os textos legais. i18n_ratchet.rs falha se um novo literal de texto que uma pessoa leria contorna o catálogo: tolerância por arquivo em tests/fixtures/i18n/allow.json, que pode diminuir e nunca aumentar. scripts/i18n_compare.py executa cenários inteiros com um binário de referência e com o atual e compara todos os arquivos de saída.

Como mover um texto para o catálogo: docs/design/i18n-migration.md.

Dependências e licenças#

O framework acrescenta fluent-bundle 0,16 (Apache-2.0 OR MIT) e unic-langid 0,9 (MIT OR Apache-2.0), com seus crates transitivos: fluent-syntax e fluent-langneg (Apache-2.0 OR MIT), intl-memoizer (Apache-2.0 OR MIT), intl_pluralrules (Apache-2.0/MIT), type-map (MIT/Apache-2.0), self_cell (Apache-2.0 OR GPL-2.0-only: a alternativa Apache-2.0 é a usada, que deny.toml permite), tinystr e zerovec (Unicode-3.0), rustc-hash, smallvec, thiserror, displaydoc (MIT OR Apache-2.0). Todas são permissivas segundo a lista allow de deny.toml; thiserror aparece em duas versões (1 e 2), o que multiple-versions = "warn" apenas informa. A lista de licenças foi lida de cargo metadata; cargo deny check não está instalado no ambiente de elaboração e é executado na CI de integração.