Localización del motor
El texto del motor dirigido a las personas está en 17 idiomas además del inglés: nl, pl, pt-BR, pt-PT, fi, sv, da, nb, cs, uk, ru, de, fr, es, et, ja, zh-Hans. El inglés es la fuente; en-XA es una configuración regional ficticia que acentúa, encierra entre corchetes y rellena cada mensaje, de modo que una prueba pueda ver si el texto pasó al catálogo. El marco es Project Fluent (fluent-bundle, unic-langid); el código es src/i18n/.
Qué se localiza y qué nunca#
| Se localiza (lo lee una persona) | Nunca se localiza (lo lee un programa) |
|---|---|
| mensajes y errores de la línea de comandos | claves y valores JSON de result.json y report.json |
| la línea de resumen de cada ejecución | encabezados y filas de CSV |
| el texto de los gráficos SVG (títulos, ejes, leyendas, pies) | nombres de archivo |
report.html: encabezados, tablas, notas, pies |
códigos de error (E-CLOCK-VERSION) |
| palabras de veredicto (VALIDATED, MODELLED, PARTIAL) | la matriz de verificación, las definiciones de unidades, la procedencia |
las descripciones de los tipos de escenario (list_kinds(lang)) |
|
advertencias de carácter consultivo, descargos de responsabilidad y avisos (engine-legal-*) |
los mismos textos dentro de JSON, que siguen en inglés |
report.html de la campaña de iq, la página de comparación de estudios, la clasificación de bench, el informe de resiliencia |
summary.html de un paquete de evidencias y el PNG del observatorio: ambos se incluyen en un manifiesto mediante hash (el del paquete, además, firmado), por lo que se generan en inglés sea cual sea el idioma |
Los números en el texto para personas usan la marca decimal del idioma (una coma en 15 de los 18 idiomas; un punto en en, ja, zh-Hans y en-XA). Los números en los archivos de máquina usan siempre el punto. Un recuento (una variable llamada n, count, n_*, num_* o *_count) llega a Fluent como un número, de modo que una traducción puede escribir { $n -> [one] ... [few] ... *[other] ... }.
Elegir el idioma#
- Línea de comandos:
--lang <tag>, luegoKSHANA_LANG,LC_ALL,LC_MESSAGES,LANG, luego inglés.ptes pt-BR; solopt-PTes el de Portugal.zhes el simplificado; el tradicional se rechaza con la lista de etiquetas admitidas. - Biblioteca:
api::run_toml_lang(toml, lang),run_toml_bilingual,run_toml_human,run_scenario_lang,list_scenario_kinds_json_lang(lang). - Python:
lang=Noneenrun,run_full,run_typed,validate_toml,error_kind,list_kinds,scenario_kinds;run_human(toml, lang);error_info(toml, lang). - WebAssembly: un
langfinal opcional enrun_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:
langenrun_scenarioylist_scenario_kinds.
Errores y sus códigos#
Cada mensaje de error tiene un código estable, el identificador del mensaje en mayúsculas (e-clock-version es E-CLOCK-VERSION), el mismo en todos los idiomas. El texto de la línea de comandos en inglés no cambia. En otro idioma la línea es text [E-CODE]; con --verbose sigue una segunda línea code: E-CODE en todos los idiomas. La API ofrece KshanaError::code() y error_info ({kind, code, message}); los errores de MCP llevan {kind, code, message} como datos del error. Compare por el código, nunca por el texto.
Cómo funciona una ejecución en otro idioma (la regla de ejecutar dos veces)#
Un archivo de máquina no debe depender del idioma. Por eso una ejecución en otro idioma ejecuta el escenario dos veces: una en inglés, cuyos result.json, report.json y CSV son los archivos de máquina, y otra en el idioma del usuario, para el resumen, el gráfico y report.html. El inglés es una sola ejecución, byte por byte igual a la de antes.
Medido, nativo, compilación de lanzamiento, inglés y luego --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; cada uno de los demás escenarios incluidos tarda menos de 4 s; los 173 escenarios incluidos cuando se midió tardaron 64 s en inglés en conjunto. El coste es exactamente el doble para otro idioma. WebAssembly no se midió (aquí no hay wasm-bindgen); con el factor de navegador de aproximadamente 4 es un minuto para el tipo de escenario más lento.
Una página evita la espera mostrando de inmediato el resultado en inglés y llamando a run_human(toml, lang) después: esa es solo la segunda ejecución, devuelve {summary, svg}, y Studio puede decir «traduciendo...» hasta que llega.
Elemento de 0,41: una sola ejecución. Generar ambas a partir de un registro de (message id, arguments). Se evaluó y no se construyó: los resultados de t! se inspeccionan, se miden y se calculan con hash después de generarse (anchos de texto de los gráficos, hashes de procedencia sobre el texto de salida), y num() da formato en el punto de llamada, de modo que un registro metería marcadores de posición en los archivos de máquina y haría que sus bytes dependieran de la reproducción. Requiere t!/num que devuelvan marcadores, un finalizador en cada escritor y una prueba de identidad byte a byte por escritor.
WebAssembly#
La compilación wasm incorpora solo el catálogo en inglés. Los otros dieciséis añadirían unos 150 kB gzip cada uno. Una página obtiene i18n/<lang>/engine.ftl al elegir un idioma y llama a register_catalogue(tag, text); si falla la obtención o el registro, la página sigue en inglés. En nativo, la línea de comandos y el paquete wheel de Python incorporan los 18.
Tamaño, .wasm sin optimizar de cargo build --release --target wasm32-unknown-unknown --features wasm --lib (antes de wasm-bindgen y wasm-opt), base bc04e98f frente a esta rama: 18,71 MB -> 19,88 MB en bruto, 5,43 MB -> 5,79 MB gzip, +357 kB gzip. El catálogo en inglés ocupa 521 kB en bruto, 153 kB gzip de ellos; el resto es fluent-bundle y sus dependencias. Esto se acepta tal como se midió.
El catálogo#
i18n/<tag>/engine.ftl contiene los mensajes, con identificadores con el prefijo engine-. i18n/en/engine.ftl es la fuente; los demás archivos son de los traductores y pueden carecer de mensajes (cada uno recurre al inglés mensaje por mensaje). Listas complementarias en i18n/en/: engine-counts.txt (mensajes con variables de recuento, para las formas de plural), engine-numbers.txt (números pasados como marcadores), engine-fragments.txt (mensajes que son fragmentos de una frase ensamblada en tiempo de ejecución: se deben fusionar en mensajes de frase completa, nunca aumentar).
Pruebas#
tests/i18n_catalogue.rs prueba el inglés: cada mensaje tiene una plantilla original registrada y se muestra exactamente como ese texto; se usa cada identificador; las variables coinciden; la configuración regional ficticia muestra cada mensaje. i18n_api.rs, i18n_kinds.rs, i18n_legal.rs cubren los puntos de entrada, las descripciones de los tipos de escenario y los textos legales. i18n_ratchet.rs falla si un nuevo literal de cadena que leería una persona elude el catálogo: tolerancia por archivo en tests/fixtures/i18n/allow.json, que puede bajar y nunca subir. scripts/i18n_compare.py ejecuta escenarios completos con un binario de referencia y con el actual, y compara cada archivo de salida.
Cómo mover una cadena al catálogo: docs/design/i18n-migration.md.
Dependencias y licencias#
El marco añade fluent-bundle 0,16 (Apache-2.0 OR MIT) y unic-langid 0,9 (MIT OR Apache-2.0), con sus crates transitivos: fluent-syntax y 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: se usa la alternativa Apache-2.0, que deny.toml permite), tinystr y zerovec (Unicode-3.0), rustc-hash, smallvec, thiserror, displaydoc (MIT OR Apache-2.0). Todas son permisivas según la lista allow de deny.toml; thiserror aparece en dos versiones (1 y 2), lo que multiple-versions = "warn" solo informa. La lista de licencias se leyó de cargo metadata; cargo deny check no está instalado en el entorno de redacción y se ejecuta en la CI de integración.