Usamos cookies para medir audiência e melhorar sua experiência. Você pode aceitar ou recusar a qualquer momento. Veja sobre o iMasters.
Ao lidar com internacionalização em aplicações React, um desafio recorrente é garantir que as chaves utilizadas para buscar traduções estejam corretas e existam no dicionário. Essa validação é fundamental para evitar erros em tempo de execução, que podem impactar a experiência do usuário e aumentar os custos de manutenção. Este guia técnico oferece uma abordagem prática para implementar a validação de chaves em dicionários i18n usando TypeScript, aproveitando os recursos mais recentes da linguagem para oferecer uma experiência de desenvolvimento mais segura e eficiente.
No desenvolvimento de interfaces multilíngues, é comum usar funções como t() para buscar textos no idioma desejado. Entretanto, a ausência de validação dessas chaves pode gerar referências inválidas, causando textos vazios ou mensagens de erro visíveis ao usuário. Além disso, a ausência de verificação em tempo de compilação força os desenvolvedores a dependerem de testes manuais ou validações em runtime, o que é pouco ideal.
Normalmente, a validação de chaves em i18n é feita por testes automatizados ou validações em runtime, como verificações de existência no objeto de dicionário. Contudo, esses métodos não oferecem garantia em tempo de compilação, dificultando o feedback imediato ao desenvolvedor. Algumas soluções tentam usar enums ou tipos fixos, mas essas abordagens tornam-se pouco escaláveis à medida que os dicionários evoluem, além de não suportarem facilmente chaves aninhadas. Sem esse critério, a solução pode parecer simples no começo e cara no suporte. O valor aparece melhor quando operação, produto e engenharia olham para o mesmo risco.
A partir do TypeScript 4.1, é possível usar tipos literais de template para inferir e validar chaves de objetos aninhados. Essa abordagem consiste em criar tipos auxiliares que representam as possíveis combinações de chaves do dicionário, permitindo que o compilador identifique chaves inválidas no momento do desenvolvimento.
Por exemplo, considere um dicionário de traduções:
const dict = {
footer: {
copyright: "Algumas copyrights",
},
header: {
logo: "Logo",
link: "Link",
},
}.
Podemos criar um tipo que gera todas as combinações possíveis de chaves, incluindo os caminhos aninhados, como 'footer.copyright', 'header.logo' e 'header.link'. Assim, ao usar a função de tradução com um tipo restrito, o TypeScript consegue alertar se uma chave não existe.
Implementação prática:
// Gera todas as chaves possíveis do dicionário, incluindo caminhos aninhados
type DeepKeys<T> = T extends object ? {
[K in keyof T]-?: `${K & string}` | Concat<K & string, DeepKeys<T[K]>>. }[keyof T] : "". type Concat<K extends string, P extends string> = P extends "" ? K : `${K}.${P}`. // Tipo que valida a chave passada pelo desenvolvedor
function t<P extends DeepKeys<typeof dict>>(p: P): GetDictValue<P, typeof dict> {
// implementação fictícia
} A decisão fica mais saudável quando o time consegue medir o impacto depois. Sem esse critério, a solução pode parecer simples no começo e cara no suporte. O valor aparece melhor quando operação, produto e engenharia olham para o mesmo risco.
// Exemplo de uso
const texto1 = t("footer.copyright"). // válido
const texto2 = t("footer.logo"). // erro de compilação
Com essa abordagem, o TypeScript fornece auto-complete na IDE e aponta erros de chaves inválidas em tempo de compilação, ajudando na manutenção e na confiabilidade do código. O valor aparece melhor quando operação, produto e engenharia olham para o mesmo risco. A decisão fica mais saudável quando o time consegue medir o impacto depois. A decisão fica mais saudável quando o time consegue medir o impacto depois. A decisão fica mais saudável quando o time consegue medir o impacto depois. A decisão fica mais saudável quando o time consegue medir o impacto depois. Sem esse critério, a solução pode parecer simples no começo e cara no suporte. A decisão fica mais saudável quando o time consegue medir o impacto depois.
Apesar de poderosa, essa técnica pode aumentar a complexidade dos tipos e impactar a performance do compilador em dicionários muito grandes ou profundamente aninhados. Além disso, ela requer que o dicionário seja fortemente tipado, o que pode não ser viável em casos de dicionários dinâmicos ou carregados em runtime. Por isso, o recorte precisa considerar manutenção, validação e caminho de volta. Sem esse critério, a solução pode parecer simples no começo e cara no suporte. O valor aparece melhor quando operação, produto e engenharia olham para o mesmo risco. Sem esse critério, a solução pode parecer simples no começo e cara no suporte. Sem esse critério, a solução pode parecer simples no começo e cara no suporte.
A implementação ideal deve equilibrar o nível de validação desejado e o impacto na produtividade do time. Para projetos de grande escala, uma combinação de validação estática para chaves principais e validação em runtime para chaves dinâmicas pode ser mais eficiente. Esse contexto ajuda a separar ganho real de novidade difícil de sustentar. O valor aparece melhor quando operação, produto e engenharia olham para o mesmo risco. O valor aparece melhor quando operação, produto e engenharia olham para o mesmo risco. O valor aparece melhor quando operação, produto e engenharia olham para o mesmo risco. O valor aparece melhor quando operação, produto e engenharia olham para o mesmo risco.
1. Definir o dicionário de traduções com tipos explícitos.
2. Criar tipos auxiliares que gerem todos os caminhos possíveis de chaves.
3. Restringir a assinatura da função t() para aceitar apenas chaves validas.
4. Testar a integração na IDE, verificando auto-complete e erros de validação.
5. Monitorar o impacto na build e ajustar a profundidade de validação conforme necessário.
Utilizar os recursos do TypeScript para validar chaves de i18n no momento do desenvolvimento ajuda a evitar erros comuns e melhora a experiência do time. Essa técnica de tipagem avançada não só reduz bugs, mas também promove uma maior disciplina na manutenção dos dicionários de tradução, facilitando a escalabilidade do projeto. A adoção dessa prática exige atenção ao impacto na performance e na complexidade dos tipos, mas os benefícios em segurança e produtividade compensam bastante. A decisão fica mais saudável quando o time consegue medir o impacto depois. Sem esse critério, a solução pode parecer simples no começo e cara no suporte. Por isso, o recorte precisa considerar manutenção, validação e caminho de volta.
Boa dica, Nicolas!
Ótima abordagem! Já passei por isso de validar chaves no tempo de desenvolvimento, ajuda demais a evitar bug na release. Acho que a parte de gerar todas as combinações de chaves é o pulo do gato.
Concordo, mas cuidado com o impacto na performance do TypeScript em dicionários muito grandes. Em projetos com milhares de chaves, pode ficar lento na hora de compilar.
Só uma dúvida: em dicionários que mudam bastante, como cuidar para que a tipagem automática se mantém atualizada? Isso dá trabalho depois pra manter sincronizado.