Localização

Esta página foi traduzida automaticamente e pode conter erros. Encontrou um problema? Use o link "Edit this page" para ajudar a corrigir.

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.


Três 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.keyFallback("edit.undo", "Undo") Texto dev-autoral extraído de uma lista em tempo de execução (um comando/título de painel registrado) um id estável explícito, com o próprio valor em execução como fallback 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

Todos os três são compilados na mesma tabela e servidos pelo mesmo serviço Locale — a única diferença é de onde vem o id, e para o que uma falta degrada.

// 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. keyFallback/trKeyFallback ficam entre os dois: como key, ids não literais são aceitáveis (o id é um valor em tempo de execução); como tr, uma falta degrada para texto real — o fallback fornecido pelo chamador — não um marcador entre colchetes, já que esse fallback é sempre um padrão inglês conhecido e seguro, não conteúdo de designer arbitrário.

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).

Traduzindo listas em tempo de execução

Parte do texto de UI lista itens orientados a dados em tempo de execução com uma string de exibição — um comando registrado, um painel encaixável, um plugin descoberto — onde a string é em inglês (criada pelo desenvolvedor), mas só existe como um valor em tempo de execução (um campo de struct) quando chega à chamada de desenho. tr() não pode aceitá-lo: seu parâmetro msg precisa ser um literal comptime.

A solução é usar o id estável da própria entrada como chave, com a string em tempo de execução como fallback:

// Local de registro — um literal struct comum, nada extra para escrever:
.{ .id = "edit.undo", .title = "Undo", .defaults = &.{...} }

// Local de desenho — traduzido por id, recorrendo ao título inglês (já final):
StudioLocale.trKeyFallback(entry.desc.id, entry.desc.title, &.{})

Nada precisa ser mantido em sincronia manualmente: turian-cli i18n extract também percorre literais struct, colhendo qualquer par de campos irmãos .id/.title como uma entrada de catálogo com chave de id, e o texto literal de .title se torna a fonte inglesa para o tradutor. Isso funciona para qualquer struct que siga a convenção de nomenclatura .id/.titleeditor.shortcuts.CommandDesc e o PanelDesc de studio/main-window/Panels.zig hoje — não apenas um tipo específico.

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/trKeyFallback 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.


← Toda a documentação Editar esta página