Skip to content

Localization pt BR

Edgar Mesquita edited this page Aug 14, 2026 · 11 revisions

Localização

🌐 Esta página em: English · Português

Multi-idioma no eQuantic.UI segue uma lei: o desenvolvedor localiza com o que o próprio .NET oferece.resx, o acessor fortemente tipado, CultureInfo — e nunca vê um catálogo JavaScript nem uma DSL do framework. O design completo (Track L: como o eqc faz a ponte do ResourceManager até o browser, catálogos por cultura, o subconjunto de formatação, as cercas honestas) vive no repositório em docs/I18N-PLAN.md.

As strings do próprio SDK

Desde 0.2.0-preview.24

Componentes nunca embutem uma string de interface. Todo rótulo interno — os anúncios que um leitor de tela ouve ("Marcado", "Ativado"), as afordâncias de dispensar, o placeholder de busca, a barra de localizar do editor — lê SdkStrings, que hoje é um resx com satélites. Um app com zero resx próprio já anuncia "Marcado" para um leitor de tela em pt-BR: as chaves do SDK entram no catálogo de toda cultura que o app emite.

Uma página fala resx no web, de ponta a ponta

Desde 0.2.0-preview.26

Um app escreve localização exatamente como qualquer app .NET — um Resources/Strings.resx mais um Strings.pt-BR.resx, os acessores comuns do Designer, string.Format(Strings.Greeting, nome) — e a página responde na cultura do request nas DUAS metades do alvo web.

sequenceDiagram
  participant B as Browser
  participant S as Servidor
  participant R as Runtime
  B->>S: GET /pagina + Accept-Language
  S->>S: escolhe catalogo (exata, pai, neutra)
  S-->>B: shell + window.__EQ_CULTURE__
  B->>R: boot instala a cultura
  R->>R: hidrata com as MESMAS strings
Loading

As peças, espelhando a ponte de tema encaixe por encaixe:

  • O eqc REESCREVE os acessores, nunca os inline. Strings.Hero_Title compila para $eq.str("Strings", "Hero.Title") — fazer inline gravaria a cultura da máquina de build dentro do bundle. A detecção é por FORMA (as propriedades estáticas ResourceManager + Culture do Designer), então qualquer resx em qualquer lugar do projeto funciona, com o nome que o desenvolvedor quiser.
  • O build emite catálogos a partir das chaves que o app realmente usa. wwwroot/_equantic/strings/neutral.json mais um {cultura}.json por variante autorada, cada um já ACHATADO contra a cadeia de fallback do .NET — o servidor escolhe um arquivo, nunca mescla.
  • O servidor embute a resposta. O UseRequestLocalization (o APP liga a negociação, o SDK só lê o que o middleware definiu) alimenta o <html lang> e um slot window.__EQ_CULTURE__ = { name, formatName, strings }; o boot o instala ANTES da hidratação, então o cliente resolve exatamente as strings que o servidor renderizou.

Trocar de idioma re-renderiza — nunca recarrega

Desde 0.2.0-preview.27

ICultureController é a mão do app sobre o IDIOMA — a mesma forma do IThemeController, resolvido por nome de interface, então um componente que oferece o seletor nunca aprende qual alvo respondeu. Ele fala nomes BCP-47, nunca CultureInfo: o contrato atravessa para um browser onde esse tipo não existe, e um nome é a única moeda que .NET, web e as duas plataformas móveis compartilham.

  • Web: o setCulture busca o catálogo da cultura só quando ele ainda não está em memória — escolhendo o arquivo do mesmo jeito que o SERVIDOR escolhe (exata → pais → neutra) — troca, e re-renderiza pelo mesmo agendador que o SetState usa. O estado sobrevive; só o texto que mudou é tocado. A troca também sobrevive à página: o controller escreve o cookie de cultura do próprio ASP.NET, no formato dele, então o próximo request já chega traduzido.
  • Nativo: o PhotonCultureController realiza a mesma interface — os statics do processo mudam e a janela repinta.
  • CultureSwitcher é a superfície embutida: um segmented control para dois ou três idiomas, um menu acima disso, acendendo a opção ativa por nome exato e depois pela parte do idioma (uma página servida como pt-BR precisa acender uma entrada pt).
new CultureSwitcher([
    new("en", "English"),
    new("pt-BR", "Português"),
    new("es", "Español"),
])

Um especificador de formato sobrevive à viagem

Desde 0.2.0-preview.27

{0:C2} escrito uma vez em C# imprime R$ 1.234,50 numa página pt-BR e 1234,50 € numa es — com SSR e cliente concordando caractere por caractere, e uma troca de cultura reformatando ao vivo junto com as strings.

A prova é o método: a fixture pinada é gerada do .NET real (value.ToString(spec, culture), três culturas escolhidas para discordar) e o runtime transpilado precisa reproduzi-la exatamente. Essa direção pegou todas as divergências em tempo de build — o Intl arredonda o meio para longe do zero onde o .NET arredonda para o par, então o formatador pré-arredonda com o helper banqueiro; os presets do Intl derrubam a data curta de en-US para ano de dois dígitos, então os padrões próprios de cada cultura viajam no catálogo dela, junto com o código ISO da moeda (o Intl exige um código, e nenhuma API do browser deriva um a partir do locale).

O que não consegue concordar é recusado no build, nunca aproximado: EQ2100 rejeita alinhamento e especificadores fora do subconjunto num template resx; EQ2101 confere os placeholders de cada TRADUÇÃO contra o resx neutro, então uma string pt-BR que pede {2} onde a neutra tem {0}/{1} falha o build em vez de estourar só para os leitores brasileiros.

Nativo: satélites provados sob AOT

O Photon roda .NET de verdade, sem transpilação: ResourceManager e assemblies satélite já funcionam lá — um app nativo localiza hoje com resx comum. O risco não é o mecanismo, é o EMPACOTAMENTO: um publish com trimming/NativeAOT que derruba os satélites não quebra, ele responde inglês em silêncio. Por isso a prova é executável (scripts/verify-aot-satellites.sh): um binário NativeAOT que referencia a biblioteca só passa se en/pt-BR/es responderem com a TRADUÇÃO, e se es-AR cair em es pelo caminho de pai real.

Duas culturas numa janela

Desde 0.2.0-preview.29

A metade web desta história é uma requisição: uma cultura entra, SSR e hidratação concordam. Uma janela não tem requisição, então uma troca ali tem três juntas que o web nunca tem... a plataforma decide o locale inicial, alguém o copia para os statics do .NET, e a árvore precisa RECONSTRUIR para a mudança alcançar os pixels.

Um componente não diz nada disso. A seção Language do sample Studio é um CultureSwitcher comum, que resolve o ICultureController pelo contexto e não nomeia plataforma nenhuma... a mesma classe que o browser renderiza. Aplicar uma cultura escreve os dois statics (o par do D13: recursos e formatos são decisões separadas) e repinta a janela, então a troca cai no frame seguinte com todo o estado da página intacto.

O caminho de screenshot a renderiza dos dois jeitos, que foi o que fechou o marco: sem argumento a janela mostra o par da MÁQUINA (interface em inglês com formatos portugueses, num Mac configurado assim), e com --culture pt-BR ela mostra Marcado, Ativado, Planilha ao lado de R$ 1.234,50.

Escrever o sample achou o bug que os testes não achavam: o host registrava só o controller concreto, então um componente que pedia a INTERFACE resolvia nulo e o switcher não trocava nada. Dois registros, uma instância. Uma capacidade que ninguém resolve numa janela de verdade é uma capacidade que não funciona.

Um app novo nasce localizado

Desde 0.2.0-preview.28

O dotnet new equantic-app agora scaffolda o que esta página prega: um Resources/Strings.resx com o satélite pt-BR e o acessor comum do Designer, um CultureSwitcher na página inicial, e a história inteira numa linha — string.Format(Strings.CountedTimes, _count). O primeiro projeto de uma pessoa responde em dois idiomas antes de ela escrever qualquer código, que é o momento mais barato de aprender que string de interface nunca se fixa no código. Veja Primeiros passos para o scaffold completo.

Cercas honestas

  • Chave ausente nunca lança. Renderiza a chave e avisa uma vez. As chaves do próprio SDK têm uma rede a mais: o inglês neutro viaja no runtime como dado gerado, então uma página sem catálogo nenhum lê "Search…" em vez de "SearchPlaceholder".
  • Uma cultura negociada sem catálogo autorado cai para os fatos NEUTROS (formatos invariantes, determinísticos — nunca dependentes do browser).
  • Plurais em v1 são chaves explícitas (correto para as línguas de 2 formas); 3+ formas é uma cerca documentada, não uma resposta errada em silêncio.
  • RTL e cobertura de scripts (CJK, árabe) são outros trilhos, explicitamente.
  • O fluxo do tradutor é .resx/XLIFF — o framework não vai crescer uma UI de tradução.

Clone this wiki locally