Central de Ajuda

Angular i18next

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

Por que i18next para Angular?

O i18next é um dos frameworks de i18n mais populares, oferecendo recursos poderosos e flexibilidade:

  • Agnóstico ao framework - funciona perfeitamente com Angular, React, Vue e outros.
  • Interpolação poderosa, pluralização e suporte a contexto.
  • Suporte a carregamento preguiçoso (lazy loading) e divisão de código (code splitting).
  • Suporte a TypeScript com segurança de tipos.
  • Grande ecossistema com plugins e extensões.

Instalação

Instale o i18next usando npm ou yarn:

npm install i18next

É isso! O i18next é uma biblioteca independente sem dependências específicas de framework. Você não precisa do angular-i18next ou de qualquer outro wrapper - basta usar o 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:

Importações Dinâmicas (Recomendado)

O uso de importações dinâmicas 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 arquivos de tradução grandes.

Vantagens:

  • Otimização do tamanho do bundle - 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 requisição HTTP adicional necessária.

Exemplo: Carregando com Importações Dinâmicas

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

  // Load all namespaces for the given language in parallel
  await Promise.all(
    NAMESPACES.map(async (namespace) => {
      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 blocos separados que são carregados sob demanda.

i18next-http-backend

O plugin HTTP backend carrega traduções via requisições HTTP de um servidor ou CDN. Isso é útil quando você deseja atualizar traduções sem reconstruir sua aplicação.

Vantagens:

  • Atualize traduções sem redeploy.
  • Carregue traduções de uma CDN externa.
  • Útil para gerenciamento de tradução em tempo de execução.

Desvantagens:

  • Requisições HTTP adicionais diminuem a velocidade do carregamento inicial.
  • Requer conectividade de rede.
  • Configuração de 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. Observe que isso requer configuração adicional para SSR.

Qual abordagem escolher?

Use Importações Dinâmicas quando:

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

Use HTTP Backend quando:

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

Inicialização Correta 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 sua aplicação renderizar.

Usando provideAppInitializer

A função provideAppInitializer do Angular (introduzida no Angular 18) garante que o i18next seja totalmente inicializado antes que a aplicação comece a renderizar. A função fornecida é executada durante o bootstrap da aplicação, 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 do texto traduzido.

Exemplo: Configuração do App Initializer

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 cintilação ou a 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 no 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 obter o desempenho ideal.
  • Carregue todos os idiomas durante o 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 pure: false para garantir que ele seja atualizado quando o idioma mudar. Isso é importante porque 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 vários 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 exibir notificações, definir títulos dinâmicos ou lidar com traduções na lógica do componente.

Melhores Práticas para i18next no Angular

Use Chaves Significativas

Use notação de ponto que reflita a estrutura da sua aplicação: 'auth:login.title' em vez de 'loginTitle' ou texto bruto.

Organize com Namespaces

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

Lide com a Pluralização

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

Aguarde a Inicialização

Sempre garanta que o i18next seja inicializado antes de renderizar sua aplicação usando provideAppInitializer() para executar a inicialização durante o bootstrap da aplicação.

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

Use TypeScript para extrair chaves de tradução diretamente dos seus arquivos JSON para segurança total de tipos. Em vez de manter manualmente as definições de tipo, aproveite o typeof do TypeScript e importações dinâmicas 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 tipos nos 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 preenchimento automático completo via IntelliSense 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 com Segurança de Tipos:

  • Preenchimento automático na sua 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, detectando erros de digitação antes da execução.
  • Suporte a refatoração - renomeie chaves com segurança em toda a sua base de código.
  • Zero sobrecarga em tempo de execução - os tipos são completamente removidos durante a compilação.
  • Gerado automaticamente a partir dos seus arquivos JSON reais - nenhuma manutenção manual de tipos necessária.

Detecção e Alternância de Idioma

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

Determine o idioma do usuário a ser usado quando sua aplicação iniciar, verificando várias 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;
}

Esta abordagem verifica o localStorage primeiro (preferência do usuário), depois recorre ao idioma do navegador (navigator.language) e, finalmente, assume o idioma padrão da aplicação.

Alterne idiomas em tempo de execução enquanto carrega automaticamente 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');

    await Promise.all(
      NAMESPACES.map(async (namespace) => {
        const currentResources = i18next.default.getResourceBundle(
          lang,
          namespace,
        );
        // Check if any namespace is missing for this language
        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 importações dinâmicas, as traduções são carregadas sob demanda. O método switchLanguage() verifica se os recursos já estão carregados usando getResourceBundle(). Se estiverem ausentes, ele os importa dinamicamente e os adiciona usando addResourceBundle(). Isso evita requisições de rede redundantes enquanto garante que todas as traduções necessárias estejam disponíveis.

Detalhes 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 ausentes dinamicamente com addResourceBundle().

Automatize a Tradução com IA

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

Por que l10n.dev?

l10n.dev é um serviço de tradução via IA projetado especificamente 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 de idioma base (por exemplo, en/common.json)
  2. Faça o upload para o l10n.dev e selecione os idiomas de destino.
  3. A IA traduz com consciência de contexto e formatação adequada.
  4. Baixe os arquivos traduzidos prontos para uso na sua aplicação Angular.

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

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