Communitygithub.com

mwasobaddy/skills

Arquitetar e implementar internacionalização para apps Next.js App Router — estrutura de pastas, roteamento com prefixo de localidade, arquivos de mensagens tipados, plurais ICU e SEO localizado com hreflang.

O que é skills?

Esta skill fornece orientação estrutural completa para colocar um aplicativo Next.js em vários idiomas usando o App Router. Ela ajuda a escolher a biblioteca de i18n correta (next-intl como padrão, Lingui ou next-i18next), configura a estrutura de pastas canônica com dicionários de mensagens fora de `src/`, implementa roteamento com segmento `[locale]` e proxy, e trata de SEO localizado com hreflang. A skill é acionada por termos como i18n, localização, multilíngue, traduções, roteamento de localidade, atributo lang e hreflang, e suporta Next.js 15+. Ela inclui práticas recomendadas, armadilhas comuns (como o uso legado de `setRequestLocale`, Promise em `params` e problemas de hidratação com datas), e orientação para retrofit de aplicativos existentes com impacto de URL e redirecionamentos. Também fornece scripts para encontrar strings não traduzidas e testar se a descrição da skill aciona corretamente os prompts esperados.

Funciona com~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/mwasobaddy/skills/tree/HEAD/nextjs-i18n

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

i18n no Next.js

Orientação estrutural para colocar um aplicativo Next.js em vários idiomas: qual deve ser o layout da pasta, o que quebra quando está errado e o que evitar.

Escolha a biblioteca primeiro

Padrão: next-intl para qualquer coisa nova no App Router. Não precisa de plugin de build, etapa de macro ou gerenciamento de instâncias por localidade; as mensagens são JSON simples tipadas a partir da sua localidade de origem.

BibliotecaUse quandoCusto
next-intl (padrão)Novo trabalho no App Router, qualquer tamanho de equipeNenhum — JSON simples, sem geração de código
@lingui/reactVocê quer extração em tempo de compilação e um runtime pequeno, ou uma camada de i18n entre React/Vue/AstroPlugin de macro SWC ou Babel, fixado exatamente
next-i18nextA equipe já conhece i18next, ou você migra código do Pages RouterInstância por requisição, divisão getT/useT
i18n embutido do Next.jsApenas Pages Router. Nunca App RouterNão aplicável

A configuração i18n: { locales, defaultLocale } do Next.js é apenas para Pages Router. No App Router ela não faz nada — roteamento de localidade lá significa um segmento [locale] mais um proxy. Não deixe o agente "ativar" o i18n embutido para um projeto App Router.

Comparação completa, incluindo custo de bundle e trade-offs por biblioteca: references/library-comparison.md.

Arquitetura canônica

Mantenha dicionários estáticos fora de src/ para que o empacotamento e os caminhos de importação permaneçam óbvios, e mantenha toda a maquinaria de localidade em uma pasta.

messages/                      # dicionários estáticos puros — mantenha fora de /src
├── en.json                     # fonte da verdade; todas as outras localidades espelham
├── de.json
└── ja.json
src/
├── i18n/
│   ├── routing.ts              # defineRouting: localidades + defaultLocale
│   ├── request.ts              # getRequestConfig: resolve localidade, carrega mensagens
│   └── navigation.ts           # createNavigation: Link/redirect com ciência de localidade
├── proxy.ts                    # detecção de localidade + redirecionamento (Next 16; era middleware.ts)
└── app/
    ├── [locale]/               # envolve todo o app localizado
    │   ├── layout.tsx          # valida localidade, <html lang>, generateStaticParams
    │   └── page.tsx
    └── not-found.tsx           # 404 global fica FORA de [locale]

Regras que fazem essa forma funcionar:

  • [locale] é um segmento dinâmico raiz. Tudo localizado vive sob ele. Rotas de nível raiz (not-found.tsx, error.tsx, api/) ficam fora e não devem chamar hooks de i18n incondicionalmente, ou quebram em tempo de execução.
  • Uma localidade de origem. messages/en.json é o mestre. Todos os outros arquivos devem espelhar exatamente sua forma.
  • Todo conhecimento de localidade em src/i18n/. Componentes importam Link de @/i18n/navigation, nunca de next/link. Um lugar para adicionar um idioma.

Configuração (greenfield)

Execute nesta ordem. A ordem importa — proxy.ts depende de routing.ts, e o layout depende de request.ts.

  1. Instale next-intl, envolva next.config.ts com createNextIntlPlugin().
  2. Crie src/i18n/routing.ts com defineRouting.
  3. Crie src/proxy.ts exportando createMiddleware(routing) mais o matcher padrão.
  4. Crie src/i18n/request.ts com getRequestConfig resolvendo a localidade.
  5. Mova app/** para app/[locale]/**; adicione generateStaticParams e valide o parâmetro.
  6. Crie src/i18n/navigation.ts com createNavigation(routing).
  7. Crie messages/en.json e substitua strings codificadas por useTranslations.
  8. Defina <html lang> e adicione metadados localizados + hreflang.

Conteúdo exato dos arquivos para cada etapa: references/next-intl-setup.md.

Armadilhas (Gotchas)

Leia esta seção antes de escrever qualquer código de i18n. Estas são as falhas que custam horas.

  • setRequestLocale é legado. No Next.js 16.3+, leia a localidade com next/root-params dentro de getRequestConfig. A maioria dos guias, tutoriais e dados de treinamento de LLM ainda ensinam setRequestLocale(params.locale). Ainda funciona e está documentado como retrocompatível — mas é a API antiga. Use apenas no Next < 16.3. generateStaticParams ainda é necessário de qualquer forma.
  • params é uma Promise no Next 15+. const {locale} = await params. Trate params.locale como um erro de tipo, não de runtime.
  • Valide a localidade ou valores desconhecidos renderizam. Use hasLocale(routing.locales, locale) e notFound(). Sem isso, /xx/about renderiza sua página em inglês em uma URL falsa.
  • proxy.ts, não middleware.ts, no Next 16. middleware.ts está obsoleto. O proxy roda apenas em Node.js — o runtime Edge não é suportado. Lingui e next-i18next anunciam proxies seguros para Edge; se você precisa de Edge, você tem um trade-off real, não uma vitória gratuita.
  • Nunca concatene strings traduzidas. "Você tem " + contagem + " itens" é agramatical em japonês e árabe. Use ICU: "{count, plural, =1 {Um item} other {# itens}}".
  • Nunca interpole com + ou template literals dentro de uma mensagem. Use placeholders nomeados — {nome} — para que a ordem das palavras possa mudar.
  • Datas e números causam diferenças de hidratação. new Date() no servidor e no navegador diferem por fuso horário e relógio. Defina timeZone em getRequestConfig, ou renderize apenas no cliente para tempos relativos. Sintoma: "Text content does not match server-rendered HTML".
  • Formatação depende da localidade. toLocaleString() sem uma localidade explícita formata pela localidade do runtime, não pela localidade solicitada. Use useFormatter() / getFormatter().
  • getRequestConfig executa por requisição e é cacheado pelo React. Não coloque estado que varia com a requisição (carrinho, preferências do usuário) lá. Apenas configuração que varia com a localidade.
  • Server Actions e Route Handlers não têm parâmetros raiz. await locale() funciona apenas em Server Components. Passe a localidade explicitamente como argumento em actions e handlers.

Retrofitting de um aplicativo existente

Trate isso como uma migração com impacto de URL, não uma mudança de configuração. Toda URL existente muda de forma quando você adiciona um prefixo [locale].

Planeje nesta ordem:

  1. Inventário de strings — execute scripts/find-untranslated.sh para encontrar texto JSX codificado.
  2. Escolha a estratégia de roteamento antes de codificar. Prefixo de localidade sempre (/en/about, /de/about) é o padrão; é inequívoco e limpo para hreflang. localePrefix: 'as-needed' serve a localidade padrão no caminho base — URLs melhores, sinal SEO mais fraco. Decida agora; mudar depois é uma remigração completa.
  3. Implemente o roteamento antes de tocar em qualquer componente. Faça proxy.ts + [locale] funcionar com mensagens apenas em inglês.
  4. Mova as rotas para [locale]/ — isso é um grande git mv mecânico. Faça em um commit para que git log --follow permaneça útil.
  5. Substitua strings por namespace, não por arquivo. Traduza um namespace de ponta a ponta para que a forma da chave seja comprovada antes de se multiplicar.
  6. Adicione redirecionamentos para cada URL que mudou. /about → /en/about (ou 307 para a localidade detectada). Sem isso, você perde todos os rankings existentes. Não negociável.
  7. Adicione hreflang + metadados localizados apenas depois que cada localidade tenha a rota, ou você emite alternates apontando para 404s.

Sequência completa de migração com notas de redirecionamento e rollback: references/migration.md.

O que evitar

  • Não coloque messages/ dentro de src/. Dicionários somente servidor na árvore de origem borram o que é enviado ao cliente.
  • Não importe Link de next/link em rotas localizadas. Isso remove o prefixo de localidade e linka silenciosamente para o idioma errado.
  • Não escreva manualmente prefixos de localidade em hrefs (href={/${locale}/about}). Use Link de @/i18n/navigation, que lida com roteamento prefixado, as-needed e de domínio.
  • Não use um namespace de chave único e plano. Agrupe por funcionalidade/página. Chaves planas colidem e não são pesquisáveis.
  • Não traduza automaticamente em tempo de build como fonte da verdade. A saída da máquina é um rascunho; cometê-la sem revisão torna os erros permanentes e não rastreáveis.
  • Não adicione uma localidade a routing.locales e considere pronto. Sem um messages/xx.json traduzido, cada página renderiza chaves ou cai de volta silenciosamente.
  • Não misture estratégias de roteamento. as-needed mais um hreflang apontando para URLs prefixadas é um bug SEO autoinfligido.

Referências

Leia sob demanda, não antecipadamente:

  • references/library-comparison.md — escolha entre next-intl, Lingui, next-i18next; custo de bundle, gatilhos de migração, formato de configuração por biblioteca.
  • references/next-intl-setup.md — todo arquivo na configuração canônica, prontos para copiar e colar.
  • references/messages-and-keys.md — estrutura de namespace, nomeação de chaves, sintaxe ICU, formatação de data/número/lista, mensagens tipadas.
  • references/seo.md — hreflang, <html lang>, generateMetadata localizado, canonical, alternates de sitemap, regras de SEO para trocador de localidade.
  • references/migration.md — retrofit de aplicativo existente, redirecionamentos, rollback.

Scripts

  • scripts/find-untranslated.sh — sinaliza strings provavelmente codificadas voltadas ao usuário em um projeto para que você possa dimensionar o trabalho antes de começar. Execute como etapa 1 de um retrofit.
  • scripts/run-trigger-eval.sh — mede se a description desta skill dispara nos prompts certos. Use ao alterar a descrição.

Testando a descrição

A description é a única coisa que o agente vê antes de decidir carregar esta skill. Se ela falhar em disparar, nada abaixo importa.

bash scripts/run-trigger-eval.sh --dry-run          # mostra o plano, sem chamadas de API
bash scripts/run-trigger-eval.sh --split train     # 12 consultas x 3 execuções
bash scripts/run-trigger-eval.sh --split validation --out report.json

Conjunto de consultas: evals/trigger-queries.json — 12 devem disparar, 8 não devem disparar, divididos 60/40 em um conjunto fixo de treino e validação.

Ao editar a descrição:

  • Corrija falhas usando apenas o conjunto de treino. Toque no conjunto de validação apenas uma vez no final para verificar se a mudança generaliza — se o treino melhora e a validação cai, você superajustou.
  • Falhas de "deve disparar" significam que a descrição é muito estreita: adicione o conceito que a consulta representa, nunca as palavras-chave da consulta.
  • Falhas de "não deve disparar" significam que é muito ampla: diga o que a skill não cobre.
  • Execute novamente antes de editar. O disparo é não determinístico, e uma execução não prova nada.

Não adicione palavras-chave de uma consulta com falha — é assim que descrições acabam parecendo uma lista de palavras-chave e param de corresponder a prompts reais.

Habilidades Relacionadas