eQuantic.UIeQuantic.UI
Docs
Playground
GitHub
Docspt-BR
Localização
Edit this page
7 min read
🌐 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. Ele 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.
Janelas Photon adotam o locale da máquina ao abrir
Desde 0.2.0-preview.24
Um processo de interface aberto pelo Finder não carrega LANG/LC_*, então o .NET começa invariante mesmo numa máquina pt-BR. Os shells resolvem a verdade da plataforma antes do primeiro frame: NSLocale.preferredLanguages[0] como cultura de interface e currentLocale como cultura de formato na Apple (a mesma divisão que o .NET modela como CurrentUICulture versus CurrentCulture), Locale.getDefault() alimentando as duas no Android, e o PhotonCultureController copia o par para os statics do processo, repintando num Apply posterior exatamente como o controller de tema repinta numa troca de modo.
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.
GET /pagina + Accept-Language
escolhe catalogo (exata, pai, neutra)
shell + window.__EQ_CULTURE__
boot instala a cultura
hidrata com as MESMAS strings
Browser
Servidor
Runtime
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"), porque 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, na 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).
1
2
3
4
5
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, e o framework não vai crescer uma UI de tradução.