Localização
O sistema de localização da Turian (ADR 0011) é compartilhado pelo engine, pelo jogo compilado e pelo próprio Studio — um único runtime, um único formato de autoria, uma única CLI. Esta página cobre o fluxo de trabalho: autoria → extração → tradução → compilação → publicação.
Duas formas de chegar a uma string
| Ponto de entrada | Para | Chave | Escrito por |
|---|---|---|---|
tr("Open Scene…") |
Chrome de UI (menus, botões, labels de HUD) | a própria string de origem | programadores, no código |
Locale.key("dlg.act1.intro") |
Conteúdo do jogo (diálogo, nomes de itens, missões) | um id estável explícito | designers, em um asset .strings |
Ambos são compilados na mesma tabela e servidos pelo mesmo serviço Locale
— a única diferença é de onde vem o id.
// Texto simples
frame.tr("Open Scene…")
// Desambiguado por contexto (duas strings em inglês idênticas, significados diferentes)
frame.trc("door", "Open")
// Plural (a regra cardinal CLDR escolhe a forma correta para o idioma ativo)
frame.trn("# file", "# files", count, &.{})
// Interpolado (placeholders nomeados de um subconjunto ICU, não especificadores std.fmt)
frame.trArgs("Save changes to \"{title}\" before closing?", &.{
.{ .name = "title", .value = .{ .text = doc_title } },
})
// Conteúdo do jogo escrito por designers, buscado por id
frame.trKey("dlg.act1.intro", &.{})
tr/trc/trn recebem sua mensagem como um parâmetro comptime — um
argumento não literal é um erro de compilação, não um comentário de
revisão. Uma tradução faltante degrada para a fonte em inglês, nunca para
uma chave crua. key/trKey degradam para ⟦the.key⟧ em builds de debug e
para a chave nua em release, então uma tradução de conteúdo de designer
faltante nunca fica silenciosamente invisível.
A sintaxe de mensagem é um subconjunto documentado do ICU MessageFormat:
{name}, {count, plural, one {# file} other {# files}} (com um branch
opcional de valor exato =0 {...}), e {gender, select, male {He} other {They}}.
Registrando o serviço
var locale = engine.Locale.init("en"); // default_locale
locale.loadTable(embedded_pt_br_strtab) catch {};
frame.services.register(engine.Locale, &locale);
Troque de idioma em tempo de execução — sem necessidade de recarregar a cena, já que a UI é renderizada novamente a cada quadro:
locale.setLocale("pt-BR");
Tabelas carregadas são mantidas em cache pela sessão e nunca são liberadas ao trocar, então qualquer slice de string já entregue permanece válido (ele só mostra o locale antigo até ser buscado novamente).
Escrevendo traduções
Um arquivo .strings é a fonte da verdade — JSON, um arquivo por locale:
{
"version": 1,
"locale": "pt-BR",
"units": [
{ "id": "Open Scene…", "source": "Open Scene…", "target": "Abrir Cena…", "state": "translated" }
]
}
Extrair → traduzir → compilar
# Percorre uma árvore de código-fonte em busca de call sites de
# tr/trc/trn/trKey, escrevendo (ou atualizando, preservando traduções
# existentes) um arquivo .strings:
turian-cli i18n extract <src-dir> pt-BR my-project/i18n/pt-BR.strings.json
# Preencha "target" para cada unidade, depois compile para o formato de runtime:
turian-cli i18n compile my-project/i18n/pt-BR.strings.json my-project/i18n/pt-BR.strtab
extract é seguro para reexecutar a qualquer momento: ids correspondentes
mantêm seu target existente; novos call sites são adicionados com state: "new". compile só emite unidades com um target não vazio — uma string
não traduzida simplesmente não está na tabela, e recai para a fonte em
inglês em tempo de execução, então uma cobertura de tradução parcial nunca é
um erro fatal.
Traduzindo o próprio Studio
A UI do próprio Studio usa o mecanismo idêntico, através de
studio/services/StudioLocale.zig (um wrapper fino que expõe
tr/trc/trn/trArgs apoiado por uma instância Locale). O Studio não é
um projeto, então seus catálogos são incorporados via @embedFile em vez de
passar pelo pipeline de assets: studio/i18n/<locale>.strings.json (fonte)
e studio/i18n/<locale>.strtab (compilado, incorporado).
Para atualizar o catálogo pt-BR do Studio após mexer no código de UI:
turian-cli i18n extract studio pt-BR studio/i18n/pt-BR.strings.json
# traduza quaisquer unidades novas (state: "new") no .strings.json
turian-cli i18n compile studio/i18n/pt-BR.strings.json studio/i18n/pt-BR.strtab
O seletor de idioma fica em Settings → UI → Language; a troca surte
efeito imediatamente (o Studio persiste a escolha em
editor.ui.language).
Plurais
A categoria de plural é resolvida a partir das regras cardinais CLDR para o
idioma ativo — one/few/many/other (e zero/two para idiomas como
o árabe) — não condicionais escritas manualmente por idioma. trn/{n, plural, ...} cobrem o caso comum; um branch explícito no estilo =0 {...}
tem prioridade sobre a categoria quando presente.
Fora de escopo
Espelhamento de layout RTL, shaping BiDi e tradução automática não fazem
parte deste sistema. Um campo direction está reservado nos metadados de
locale para suporte futuro a RTL.