Central de Ajuda

Angular i18next

A internacionalização (i18n) é essencial para construir aplicações Angular globais. Este guia mostra como implementar i18next no Angular com carregamento adequado de recursos, inicialização e padrões de uso.

Por que i18next para Angular?

i18next é uma das estruturas de i18n mais populares, oferecendo recursos poderosos e flexibilidade:

  • Independente de framework - funciona perfeitamente com Angular, React, Vue e outros.
  • Interpolação poderosa, pluralização e suporte a contexto.
  • Suporte a carregamento preguiçoso e divisão de código.
  • Suporte a TypeScript com segurança de tipo.
  • Grande ecossistema com plugins e extensões.

Instalação

Instale o i18next usando npm ou yarn:

npm install i18next

É isso! i18next é uma biblioteca independente sem dependências específicas de framework. Você não precisa de angular-i18next ou qualquer outro wrapper - basta usar i18next diretamente na sua aplicação Angular.

Carregando Recursos de Tradução

Existem duas abordagens principais para carregar recursos de tradução no Angular com i18next:

Imports Dinâmicos (Recomendado)

Usar imports dinâmicos aproveita a divisão de código do Webpack para carregar traduções sob demanda. Esta abordagem é ideal para aplicações com muitos idiomas ou grandes arquivos de tradução.

Vantagens:

  • Otimização do tamanho do pacote - carregue apenas as traduções necessárias.
  • Melhor desempenho - tempo de carregamento inicial mais rápido.
  • Funciona perfeitamente com SSR e pré-renderização.
  • Nenhuma solicitação HTTP adicional necessária.

Exemplo: Carregando com Imports Dinâmicos

private async loadLanguageResources(
  lang: string,
): Promise<Record<string, Record<string, unknown>>> {
  const languageResources: Record<string, Record<string, unknown>> = {};

  for (const namespace of NAMESPACES) {
    const translationModule = await this.loadJson(lang, namespace);
    languageResources[namespace] = translationModule.default;
  }
  return languageResources;
}

private async loadJson(
  language: string,
  namespace: string,
): Promise<{ default: Record<string, unknown> }> {
  // Webpack automatically code-splits this into separate chunks
  return import(`../../i18n/${language}/${namespace}.json`);
}

Esta abordagem importa arquivos de tradução como módulos ES, permitindo que o Webpack os divida automaticamente em partes separadas que são carregadas sob demanda.

i18next-http-backend

O plugin HTTP backend carrega traduções via solicitações HTTP de um servidor ou CDN. Isso é útil quando você deseja atualizar traduções sem reconstruir seu aplicativo.

Vantagens:

  • Atualize traduções sem reimplantação.
  • Carregue traduções de um CDN externo.
  • Útil para gerenciamento de tradução em tempo de execução.

Desvantagens:

  • Solicitações HTTP adicionais desaceleram o carregamento inicial.
  • Requer conectividade de rede.
  • Configuração SSR mais complexa.

Exemplo: Carregando com HTTP Backend

import HttpBackend from 'i18next-http-backend';

await init({
  lng: initialLanguage,
  fallbackLng: DEFAULT_LANGUAGE,
  backend: {
    loadPath: '/assets/i18n/{{lng}}/{{ns}}.json',
  },
  use: [HttpBackend],
  ns: NAMESPACES,
  defaultNS: DEFAULT_NAMESPACE,
});

Esta abordagem busca arquivos de tradução de um servidor em tempo de execução. Note que isso requer configuração adicional para SSR.

Qual Abordagem Escolher?

Use Imports Dinâmicos quando:

  • Você deseja um tamanho de pacote e desempenho otimizados.
  • As traduções fazem parte do seu processo de construção.
  • Você precisa de suporte a SSR/pré-renderização.

Use HTTP Backend quando:

  • Você precisa atualizar traduções sem reimplantação.
  • As traduções são gerenciadas externamente.
  • Você tem um CDN para servir arquivos de tradução.

Inicialização Adequada do i18next

Inicializar o i18next corretamente é crucial para evitar erros em tempo de execução e garantir que as traduções estejam disponíveis quando seu aplicativo renderiza.

Usando provideAppInitializer

A função provideAppInitializer do Angular (introduzida no Angular 18) garante que o i18next esteja totalmente inicializado antes que a aplicação comece a renderizar. A função fornecida é executada durante a inicialização do aplicativo, e a inicialização não é concluída até que a Promise seja resolvida. Isso evita que chaves de tradução apareçam em vez de texto traduzido.

Exemplo: Configuração do Inicializador do App

export const appConfig: ApplicationConfig = {
  providers: [
    // ... other providers
    provideAppInitializer(initializeI18n),
    // ... more providers
  ],
};

Por que isso é importante:

  • A função é executada no contexto de injeção durante a inicialização da aplicação.
  • Evita flickering ou exibição de chaves de tradução no carregamento inicial.
  • Garante que as traduções estejam disponíveis em todos os componentes desde o início.
  • Funciona corretamente com SSR, pré-renderização e renderização do lado do cliente.

Função de Inicialização do i18next

A função de inicialização lida com o carregamento de recursos e a configuração das definições do i18next.

Exemplo: Inicialização do I18next

public async initializeI18n(): Promise<void> {
  const initialLanguage = this.getInitialLanguage();
  const resources: Record<string, Record<string, Record<string, unknown>>> = {};

  if (isPlatformBrowser(this.platformId)) {
    // Browser: Only load the specific language needed
    const languageResources = await this.loadLanguageResources(initialLanguage);
    resources[initialLanguage] = languageResources;
  } else {
    // SSR: Load all languages for prerendering
    for (const lang of SUPPORTED_LANGUAGES) {
      const languageResources = await this.loadLanguageResources(lang);
      resources[lang] = languageResources;
    }
  }

  await init({
    lng: initialLanguage,
    fallbackLng: DEFAULT_LANGUAGE,
    resources,
    ns: NAMESPACES,
    defaultNS: DEFAULT_NAMESPACE,
    interpolation: {
      escapeValue: false,
    },
  });
}

Pontos-chave:

  • Carregue apenas o idioma atual no navegador para desempenho ideal.
  • Carregue todos os idiomas durante SSR para suporte à pré-renderização.
  • Use fallbackLng para lidar com traduções ausentes de forma elegante.
  • Configure namespaces para melhor organização.

Pipe de Tradução

O pipe de tradução do Angular fornece uma maneira limpa de usar traduções em templates.

Implementação do Pipe

Exemplo: Pipe de Tradução

import { Pipe, PipeTransform } from '@angular/core';
import { t } from 'i18next';

@Pipe({
  name: 't',
  standalone: true,
  pure: false, // Need to update when language changes
})
export class TranslatePipe implements PipeTransform {
  transform(key: string, options?: Record<string, unknown>): string {
    return t(key, options);
  }
}

O pipe é marcado como puro: false para garantir que ele seja atualizado quando o idioma mudar. Isso é importante porque as mudanças de idioma não modificam a chave de tradução em si.

Usando o Pipe de Tradução

Em Templates:

<!-- With namespace -->
<p>{{ "common:welcomeMessage" | t }}</p>

<!-- With nested keys -->
<p>{{ "dashboard.title" | t }}</p>

<!-- Simple interpolation -->
<p>{{ "greeting" | t: { name: userName } }}</p>
<!-- Result: "Hello, John!" from greeting: "Hello, {{name}}!" -->

<!-- With Angular pipes -->
<p>{{ "price" | t: { amount: 29.99 | currency } }}</p>
<!-- Result: "Price: $29.99" from: "Price: {{amount}}" -->

<!-- Multiple variables -->
<p>{{ "updated" | t: { date: lastModified | date, user: currentUser } }}</p>
<!-- Result: "Updated on Jan 15, 2024 by Alice" from: "Updated on {{date}} by {{user}}." -->

A interpolação permite que você insira valores dinâmicos em suas traduções. Você pode passar variáveis, usar pipes do Angular para formatação e combinar múltiplos valores em uma única string de tradução.

No Código do Componente:

import { Component } from '@angular/core';
import { t } from 'i18next';

@Component({
  selector: 'app-dashboard',
  template: `
    <h1>{{ title }}</h1>
    <p>{{ welcomeMsg }}</p>
  `,
})
export class DashboardComponent {
  title = t('dashboard.title');
  welcomeMsg = t('greeting', { name: 'John' });
}

Para uso em templates, a sintaxe do pipe é limpa e legível. Para acesso programático, chame t() diretamente. Isso é útil para mostrar notificações, definir títulos dinâmicos ou lidar com traduções na lógica do componente.

Melhores Práticas para Angular i18next

Use Chaves Significativas

Use notação de ponto que reflita a estrutura do seu app: 'auth:login.title' em vez de 'loginTitle' ou texto cru.

Organize com Namespaces

Divida as traduções em namespaces lógicos (por exemplo, common.json, auth.json, dashboard.json) para melhor manutenção.

Lide com Pluralização

Use o suporte de pluralização embutido do i18next para lidar corretamente com formas singulares/plurais em todos os idiomas.

Aguarde a Inicialização

Sempre garanta que o i18next esteja inicializado antes de renderizar seu app usando provideAppInitializer() para executar a inicialização durante a inicialização do app.

Segurança de Tipo para Chaves de Tradução

Use TypeScript para extrair chaves de tradução diretamente dos seus arquivos JSON para segurança de tipo completa. Em vez de manter manualmente definições de tipo, aproveite o typeof do TypeScript e imports dinâmicos para gerar tipos automaticamente a partir dos seus arquivos de tradução reais.

Defina um tipo que extraia todas as chaves possíveis dos seus arquivos de tradução JSON usando um tipo auxiliar NestedKeys recursivo.

import common from './en/common.json'; // defaultNS: DEFAULT_NAMESPACE,
import auth from './en/auth.json'; // namespaces

type NestedKeys<T, Prefix extends string = ''> = T extends object
  ? {
      [K in keyof T & (string | number)]: T[K] extends object
        ?
            | NestedKeys<T[K], `${Prefix}${K & (string | number)}.`>
            | `${Prefix}${K & (string | number)}`
        : `${Prefix}${K & (string | number)}`;
    }[keyof T & (string | number)]
  : never;

export type TranslationKey =
  | `common:${NestedKeys<typeof common>}`
  | `auth:${NestedKeys<typeof auth>}`;

Altere o método transform do pipe para aceitar TranslationKey em vez de string. Isso fornece segurança de tipo em templates e garante que apenas chaves de tradução válidas possam ser usadas.

import { Pipe, PipeTransform } from '@angular/core';
import { t } from 'i18next';

import { TranslationKey } from '../../i18n/translation-key';

@Pipe({
  name: 't',
  standalone: true,
  pure: false, // Need to update when language changes
})
export class TranslatePipe implements PipeTransform {
  transform(key: TranslationKey, options?: Record<string, unknown>): string {
    return t(key, options);
  }
}

Agora você obtém autocompletar completo e erros em tempo de compilação para chaves inválidas tanto no código TypeScript quanto nos templates.

t('common:greeting');       // ✅ Autocomplete
t('auth:login.title');      // ✅ Valid
t('invalid.key');           // ❌ IDE shows error

{{ "common:greeting" | t }} // ✅ Valid in templates
{{ "invalid.key" | t }}     // ❌ IDE shows error

Benefícios das Chaves de Tradução Seguras em Tipo:

  • Autocompletar no seu IDE para todas as chaves de tradução disponíveis em todos os namespaces.
  • Erros em tempo de compilação se você usar uma chave inexistente, capturando erros de digitação antes da execução.
  • Suporte a refatoração - renomeie chaves com segurança em todo o seu código.
  • Zero sobrecarga em tempo de execução - tipos são completamente removidos durante a compilação.
  • Auto-gerado a partir dos seus arquivos JSON reais - nenhuma manutenção manual de tipo necessária.

Detecção e Troca de Idioma

Implemente detecção de idioma inteligente e permita que os usuários troquem de idioma em tempo de execução com carregamento automático de recursos.

Determine o idioma do usuário a ser usado quando seu app iniciar verificando múltiplas fontes em ordem de prioridade.

private getInitialLanguage(): string {
  if (isPlatformBrowser(this.platformId)) {
    // Try to get language from localStorage (user preference)
    const savedLang = localStorage.getItem('language');
    if (savedLang) {
      return savedLang;
    }

    // Fall back to browser language
    return navigator.language ?? DEFAULT_LANGUAGE;
  }

  // Default language for SSR
  return DEFAULT_LANGUAGE;
}

Essa abordagem verifica o localStorage primeiro (preferência do usuário), depois recorre ao idioma do navegador (navigator.language), e finalmente define o idioma padrão do app.

Troque de idiomas em tempo de execução enquanto carrega automaticamente os recursos de tradução sob demanda.

public async switchLanguage(
  language: string
): Promise<void> {
  // In browser, check if we need to load the translation first
  if (isPlatformBrowser(this.platformId)) {
    const i18next = await import('i18next');

    // Check if any namespace is missing for this language
    for (const namespace of NAMESPACES) {
      const currentResources = i18next.default.getResourceBundle(lang, namespace);

      if (!currentResources) {
        // Translation not loaded yet, load it dynamically
        const translationModule = await this.loadJson(lang, namespace);
        i18next.default.addResourceBundle(
          lang,
          namespace,
          translationModule.default
        );
      }
    }
  }

  // Switch to the new language
  await changeLanguage(lang);

  // Save preference to localStorage
  if (isPlatformBrowser(this.platformId)) {
    localStorage.setItem('language', lang);
  }
}

Ao usar imports dinâmicos, as traduções são carregadas sob demanda. O método switchLanguage() verifica se os recursos já estão carregados usando getResourceBundle(). Se estiverem faltando, ele importa dinamicamente e os adiciona usando addResourceBundle(). Isso previne requisições de rede redundantes enquanto garante que todas as traduções necessárias estejam disponíveis.

Detalhes Chave da Implementação:

  • Verifique o localStorage primeiro para restaurar o idioma preferido do usuário.
  • Use navigator.language como fallback para a preferência de idioma do navegador.
  • Carregue recursos faltantes dinamicamente com addResourceBundle().

Automatize a Tradução com IA

Gerenciar traduções manualmente em vários idiomas é demorado e propenso a erros. É aí que a localização impulsionada por IA entra.

Por que l10n.dev?

l10n.dev é um serviço de tradução impulsionado por IA especificamente projetado para arquivos JSON do i18next:

  • Preserva a estrutura JSON, chaves e objetos aninhados.
  • Mantém placeholders como {{name}}, {{count}} corretamente.
  • Gera automaticamente formas plurais para todos os idiomas.
  • Lida com contexto e interpolação de forma inteligente.
  • Suporta todos os recursos do i18next, incluindo namespaces.

Fluxo de Trabalho Simples

  1. Exporte seu arquivo JSON do idioma base (por exemplo, en/common.json)
  2. Faça upload para l10n.dev e selecione os idiomas-alvo.
  3. A IA traduz com consciência de contexto e formatação adequada.
  4. Baixe arquivos traduzidos prontos para uso no seu app Angular.

Economize horas de trabalho manual de tradução e evite erros comuns como placeholders quebrados ou formas plurais incorretas.

Pronto para agilizar seu fluxo de trabalho de i18n no Angular?