Faça sua pergunta e obtenha um resumo do documento referenciando esta página e o provedor AI de sua escolha
O conteúdo desta página foi traduzido com uma IA.
Veja a última versão do conteúdo original em inglêsSe você tiver uma ideia para melhorar esta documentação, sinta-se à vontade para contribuir enviando uma pull request no GitHub.
Link do GitHub para a documentaçãoCopiar o Markdown do documento para a área de transferência
next-intl VS Intlayer: Benchmark de Internacionalização (i18n) do Next.js

next-intl é a biblioteca i18n mais popular para Next.js. Intlayer é uma alternativa baseada em compilador e com escopo de componente. Ambas localizam uma aplicação App Router. A questão é quanto cada uma custa uma vez que a aplicação está compilada.
Este artigo não é um tutorial. É uma comparação apoiada por números do Benchmark Bloom, um conjunto de benchmark open-source que constrói a mesma aplicação com cada biblioteca e mede o que o navegador realmente faz download e executa.
tl;dr: Na mesma aplicação Next.js,next-intladiciona +12.6 KB gzip de JavaScript em cada página, versus +0.3 KB para Intlayer. Sem trabalho extra,next-intlenvia ~90% das strings de páginas estrangeiras com cada página. Alcançar 0% de vazamento comnext-intlrequer escopo de namespace epick(messages, [...])por página. Intlayer alcança 0% por padrão, porque seu compiler escopeia o conteúdo por componente. Se você quer a APInext-intlcom a saída do Intlayer, o adaptador@intlayer/next-intlmediu 147.5 KB por página versus 153.6 KB com o original.
Em resumo
- next-intl - Leve, bem documentado, formato de mensagem ICU, suporte de primeira classe ao App Router com middleware, formatters e helpers de navegação. O conteúdo reside em catálogos JSON centralizados; otimizações de desempenho (namespaces, seleção de mensagens por página, lazy loading) são sua responsabilidade.
- Intlayer - Modelo de conteúdo centrado em componentes. Dicionários
.content.tsficam ao lado do componente que servem, um compilador em tempo de build faz tree-shake e lazy-loads deles por componente e por locale, tipos TypeScript rigorosos são gerados a partir do seu conteúdo, e traduções ausentes falham em tempo de build. Inclui middleware, helpers de SEO, um Visual Editor / CMS e tradução assistida por IA.
Abrir a tabela em um modal para ver todo o conteúdo claramente
Os emblemas são atualizados automaticamente. Os snapshots variarão ao longo do tempo.
Comparação de recursos lado a lado
Abrir a tabela em um modal para ver todo o conteúdo claramente
| Recurso | next-intlayer (Intlayer) | next-intl |
|---|---|---|
| Traduções próximas aos componentes | ✅ Sim, .content.ts colocado junto com cada componente | ❌ Não, centralizado em messages/{locale}.json |
| Integração TypeScript | ✅ Tipos estritos gerados automaticamente a partir do conteúdo | ✅ Bom, chaves tipadas via augmentação global.d.ts |
| Detecção de tradução ausente | ✅ Erro TypeScript + erro/aviso em tempo de build | ⚠️ Fallback em tempo de execução + aviso no console |
| Conteúdo rico (JSX / Markdown / componentes) | ✅ Suporte direto | ⚠️ t.rich() / t.markup() com placeholders de tag |
| Suporte ICU | ⚠️ Em desenvolvimento | ✅ Sim |
| Formatação (datas, números, moedas) | ✅ useNumber, useDate, ... (Intl sob o capô) | ✅ useFormatter() (Intl sob o capô) |
| Roteamento localizado e middleware | ✅ Proxy/middleware integrado, getMultilingualUrls | ✅ Middleware integrado, Link, redirect, usePathname |
| Auxiliares de SEO (hreflang, sitemap, robots) | ✅ Auxiliares integrados | ⚠️ Manual, baseado na configuração de roteamento |
| Componentes de servidor síncrono | ✅ useIntlayer de next-intlayer/server funciona em qualquer componente servidor filho | ⚠️ getTranslations é assíncrono; filhos síncronos precisam de t passado como props |
| Renderização estática | ✅ Não bloqueia a renderização estática | ⚠️ Requer setRequestLocale(); catálogos nomeados ainda optaram páginas fora da renderização estática em nossos testes |
| Tree-shaking (enviar apenas conteúdo usado) | ✅ Por componente, por locale, automatizado pelo compilador | ⚠️ Manual: namespaces + pick(messages, [...]) por página |
| Lazy loading | ✅ importMode: 'dynamic' (uma linha de config) | ⚠️ Importações dinâmicas manuais em getRequestConfig |
| Purge unused content | ✅ Dicionários não utilizados são removidos no momento da compilação | ❌ Não incluído |
| Testing missing translations (CLI / CI) | ✅ npx intlayer content test | ⚠️ Não incluído; docs sugerem npx @lingual/i18n-check |
| AI-powered translation | ✅ Incluído, usa suas próprias chaves de provedor | ❌ Não |
| Editor Visual / CMS | ✅ Editor Visual gratuito + CMS opcional | ❌ Não (plataformas de localização externas) |
| Servidor MCP & Agent Skills | ✅ Sim | ❌ Não |
| Ecossistema / comunidade | ⚠️ Menor mas crescendo rapidamente | ✅ Grande, a referência do Next.js |
O benchmark
O que foi medido
O suite Benchmark Bloom constrói a mesma aplicação com cada biblioteca: 10 páginas (home, about, blog, careers, contact, FAQ, pricing, products, settings, team), 10 locales (en, fr, es, de, it, pt, zh, ja, ko, ru), componentes idênticos e conteúdo idêntico. As páginas são medidas em en e fr. Cada biblioteca é implementada em até quatro estratégias de carregamento, da configuração ingênua até a otimizada:
Abrir a tabela em um modal para ver todo o conteúdo claramente
| Strategy | Description | Who does this |
|---|---|---|
| static | Cada locale e cada página agrupadas juntas | Protótipos rápidos, código gerado por IA |
| dynamic | Apenas a locale ativa é carregada, mas todas as páginas de uma vez | A maioria dos projetos |
| scoped-static | Namespaces por rota, sem lazy loading | Raro |
| scoped-dynamic | Namespaces por rota + lazy loading. Apenas a página atual na locale atual é enviada | Apps com orçamento de performance rigoroso |
Intlayer não possui uma variante "scoped": o compilador agrupa conteúdo por componente automaticamente, então suas linhas static e dynamic já estão agrupadas.
Para cada build, a suite registra:
- Lib size: tamanho gzip da biblioteca i18n. O custo fixo do runtime.
- Page JS: JavaScript gzip baixado por página, calculado em média sobre todas as páginas e locales.
- Locale leak %: proporção de strings traduzidas encontradas no JS baixado que pertencem a um locale que o usuário não está visualizando (fingerprinted em
enefr, então 50% significa "o outro locale medido está totalmente presente"; com 10 locales agrupados, o desperdício real é maior). - Page leak %: proporção de strings traduzidas encontradas no JS baixado que pertencem a uma página em que o usuário não está.
- Component avg: tamanho gzip médio de cada componente compilado isoladamente. Mostra quanto runtime i18n um único componente carrega.
- E2E reactivity: tempo real entre selecionar uma nova locale e
html[lang]atualizar no DOM (Playwright, 5 iterações). - Hydration: duração da fase de hidratação do React.
Os números abaixo vêm da execução datada de 2026-09-12 comnext-intl4.14.2,use-intl4.14.2 eintlayer9.5.1. A aplicação de teste é deliberadamente pequena (algumas dezenas de strings por locale), portanto as porcentagens de vazamento descrevem um padrão: eles crescem com seu conteúdo enquanto o custo de runtime permanece fixo.
Resultados no Next.js (App Router)
Escolha as métricas e bibliotecas do seu interesse:
Métrica
Carregamento JSON dinâmico
Carrega as traduções tardiamente em tempo de execução
JSON com escopo (namespacing)
Namespaces de tradução por página
O que é essa métrica?
O tamanho total compactado em gzip do pacote da biblioteca de internacionalização. Inclui apenas o provedor e a lógica de recuperação de conteúdo após o tree-shaking e a minificação.
Por que é importante?
Um tamanho de biblioteca menor reduz a carga útil inicial de JavaScript, resultando em tempos de download e execução mais rápidos no cliente.
Ver como
Abrir a tabela em um modal para ver todo o conteúdo claramente
| Library | Strategy | Lib size (gz) | Page JS avg (gz) | Locale leak | Page leak | Component avg (gz) | E2E reactivity | Hydration |
|---|---|---|---|---|---|---|---|---|
| base (sem i18n) | - | 0.0 KB | 141.0 KB | 0.0% | 0.0% | 0.9 KB | 13.4 ms | 11.8 ms |
next-intl | static | 14.7 KB | 153.6 KB | 4.2% | 89.8% | 21.8 KB | 16.0 ms | 14.7 ms |
next-intl | dynamic | 14.7 KB | 153.6 KB | 9.7% | 89.9% | 21.8 KB | 15.6 ms | 14.8 ms |
next-intl | scoped-static | 14.7 KB | 153.6 KB | 0.0% | 0.0% | 80.1 KB | 17.9 ms | 17.4 ms |
next-intl | scoped-dynamic | 14.7 KB | 153.6 KB | 0.0% | 0.0% | 22.9 KB | 17.8 ms | 16.8 ms |
next-intlayer | static | 5.5 KB | 141.3 KB | 0.0% | 0.0% | 8.5 KB | 15.5 ms | 16.9 ms |
next-intlayer | dynamic | 5.5 KB | 141.3 KB | 0.0% | 0.0% | 6.9 KB | 15.3 ms | 15.9 ms |
@intlayer/next-intl (compat) | static | 8.0 KB | 147.5 KB | 0.0% | 0.0% | 8.1 KB | 14.5 ms | 12.8 ms |
@intlayer/next-intl (compat) | dynamic | 8.0 KB | 148.7 KB | 0.0% | 0.0% | 8.1 KB | 11.7 ms | 12.8 ms |
Como ler isso
- Custo de runtime. A aplicação base pesa 141.0 KB por página.
next-intla leva para 153.6 KB (+12.6 KB gzip em cada página), Intlayer para 141.3 KB (+0.3 KB). Esta diferença não depende de quantas strings você tem: é o runtime da biblioteca. - Vazamento. Nos dois setups que a maioria das equipes realmente implementa (
staticedynamic),next-intlentrega ~90% das strings de páginas estrangeiras com cada página: todo oen.jsonvai para o provider do cliente. Para chegar a 0% é necessário os setupsscoped-*: dividir catálogos em namespaces e depois fazerpick()dos corretos em cada página. Intlayer está em 0% em ambas as linhas sem nada disso. - O JS por página não se moveu para
next-intlentre estratégias. O conteúdo do teste é pequeno, então o vazamento de ~90% é apenas alguns KB aqui. Em uma app real com centenas de strings por página, essa proporção se torna o custo dominante. Enquanto isso, o runtime de +12.6 KB é pago em cada configuração. - Tamanho do componente. Um componente que chama
useTranslations()compila para 21.8 KB em média; o mesmo componente comuseIntlayer()compila para 6.9 KB. Na configuraçãoscoped-static, os componentesnext-intlsaltam para 80.1 KB porque cada um internaliza seu catálogo de namespace. - Reatividade e hidratação estão no mesmo patamar para ambas as bibliotecas no Next.js (15-18 ms). Nenhuma delas é um gargalo aqui.
Tabela completa, cada biblioteca e cada estratégia, no relatório de benchmark do Next.js.
Resultados no TanStack Start (use-intl)
use-intl é o núcleo agnóstico de framework do next-intl. Mesma API, mesmo formato de mensagem. Comparar isso contra intlayer no TanStack Start remove as partes específicas do Next.js da equação.
Abrir a tabela em um modal para ver todo o conteúdo claramente
| Library | Strategy | Lib size (gz) | Page JS avg (gz) | Locale leak | Page leak | Component avg (gz) | E2E reactivity |
|---|---|---|---|---|---|---|---|
| base (sem i18n) | - | 0.0 KB | 111.0 KB | 0.0% | 0.0% | 0.7 KB | 8.1 ms |
use-intl | static | 14.1 KB | 179.8 KB | 50.0% | 89.8% | 76.0 KB | 6.7 ms |
use-intl | dynamic | 14.1 KB | 119.4 KB | 0.0% | 89.8% | 75.9 KB | 7.0 ms |
use-intl | scoped-static | 14.1 KB | 128.7 KB | 0.0% | 0.0% | 87.1 KB | 20.9 ms |
use-intl | scoped-dynamic | 14.1 KB | 128.7 KB | 0.0% | 0.0% | 87.1 KB | 13.3 ms |
intlayer | static | 5.0 KB | 125.8 KB | 50.0% | 0.0% | 8.1 KB | 3.2 ms |
intlayer | dynamic | 5.0 KB | 118.6 KB | 0.0% | 0.0% | 6.3 KB | 3.6 ms |
@intlayer/use-intl (compat) | dynamic | 7.3 KB | 129.7 KB | 0.0% | 0.0% | 9.3 KB | 8.7 ms |
Como ler isso
- A configuração ingênua
use-intlenvia 68.8 KB a mais de JS por página do que a aplicação base, com metade das strings pertencendo à locale errada e 90% à página errada. use-intlem mododynamicchega a 119.4 KB, próximo ao 118.6 KB do Intlayer, mas ainda carrega 89.8% de vazamento de página: as strings de todas as páginas para a locale ativa são carregadas em cada página. Escopo-las por rota (scoped-*) remove o vazamento, mas custa outro ~9 KB de overhead de chunk.- Intlayer's
staticjá tem 0% de vazamento de página: o compilador só agrupa os dicionários usados pelos componentes na página. HabilitandoimportMode: 'dynamic'(uma linha emintlayer.config.ts) remove também o vazamento de locale. - O tamanho do componente é onde a arquitetura se manifesta: 76-87 KB por componente com
use-intlversus 6-8 KB com Intlayer.useTranslations()vincula cada componente à árvore de mensagens global;useIntlayer()vincula-o ao seu próprio dicionário. - Mudança de locale é 2x-4x mais rápida com Intlayer (3 ms vs 7-21 ms).
Tabela completa no relatório de benchmark do TanStack Start.
Por que a diferença? Catálogos centralizados vs. dicionários compilados

next-intl segue o modelo clássico: um JSON por locale, carregado em getRequestConfig, inserido em um NextIntlClientProvider, lido através de t("namespace.key").
Copiar o código para a área de transferência
O runtime não consegue saber quais keys uma página usará, então o padrão seguro é enviar o catálogo completo. Otimizar significa você dividir o catálogo em namespaces, você decidir quais namespaces cada página precisa, e você manter esse mapeamento sincronizado conforme os componentes se movem. A linha scoped-dynamic do benchmark é a recompensa por esse trabalho, e a maioria das equipes nunca chega lá.
O custo de não chegar lá cresce em dois eixos ao mesmo tempo, páginas e idiomas:

Intlayer inverte a responsabilidade. O conteúdo é declarado ao lado do componente:
Copiar o código para a área de transferência
No momento da compilação, o compilador (@intlayer/swc / @intlayer/babel) vê qual componente importa qual dicionário. Ele agrupa apenas esses dicionários, apenas para o locale ativo, e descarta os que nada importa. O padrão "scoped-dynamic" torna-se a saída da compilação em vez de uma disciplina que o time tem que manter.
Para obter os números da linhadynamic, definadictionary.importMode: 'dynamic'emintlayer.config.ts. Veja a documentação de otimização de bundle.
Experiência do desenvolvedor
Componente cliente
Copiar o código para a área de transferência
Copiar o código para a área de transferência
Lembrez-se de incluir o namespacecounternas mensagens passadas paraNextIntlClientProviderem cada página que renderiza este componente.
Copiar o código para a área de transferência
Copiar o código para a área de transferência
Nada para registrar na página: o componente traz seu próprio conteúdo.
Componente de servidor síncrono
Peças de design-system (navbar, footer, cards) são frequentemente componentes server renderizados como children de componentes client, então não podem ser async.
Copiar o código para a área de transferência
A página tem que await getTranslations("counter") e await getFormatter(), depois passar os resultados para baixo como props. O componente não é mais auto-contido.
Copiar o código para a área de transferência
Metadados
Copiar o código para a área de transferência
Copiar o código para a área de transferência
Mantenha a API do next-intl, obtenha a saída do Intlayer
Você não precisa reescrever componentes para obter os números de benchmark acima. @intlayer/next-intl é um adaptador pronto para usar: mantém useTranslations, getTranslations, useFormatter, t.rich(), plurais ICU e os auxiliares next-intl/navigation, e os fornece a partir dos dicionários compilados do Intlayer pelo compilador Intlayer.
Copiar o código para a área de transferência
Na benchmark, a build de compatibilidade da mesma aplicação passou de 153.6 KB para 147.5 KB por página, de 21.8 KB para 8.1 KB por componente, e de ~90% page leakage para 0%, com o código da aplicação intacto. Seus arquivos messages/{locale}.json existentes podem continuar sendo a fonte de verdade através do plugin JSON sync.
Veja o guia de migração next-intl para o passo a passo.
Quando escolher qual?
Você quer o padrão do ecossistema para Next.js, depende do formato ICU MessageFormat, seu aplicativo é de porte pequeno a médio, ou você integra com uma plataforma de tradução (Crowdin, Phrase, Lokalise...) que espera JSON centralizado. Reserve tempo para estruturar catálogos por namespace e selecionar mensagens com pick() por página se o desempenho for essencial.
Você quer conteúdo com escopo por componente, TypeScript rigoroso, erros de chaves ausentes em tempo de build, tree-shaking e lazy loading sem esforço, componentes de servidor síncronos e ferramentas editoriais integradas (Editor Visual, CMS, tradução por IA, servidor MCP). Especialmente relevante para bases de código modulares grandes e sistemas de design.
Você já está usando o next-intl e quer os ganhos de tamanho de bundle sem uma reescrita completa. O adaptador de compatibilidade mantém seus imports e seu arquivo messages/{locale}.json como a fonte da verdade. Medido lado a lado em next-intl vs @intlayer/next-intl.
FAQ
Não no momento da renderização. A diferença está no que é enviado ao cliente: o next-intl custa +12.6 KB gzip de runtime em cada página e, na maioria das configurações comuns, envia cerca de 90% das strings de páginas estrangeiras em cada página. A troca de idioma e a hidratação são comparáveis no Next.js (15-18 ms); no TanStack Start, o use-intl leva de 7 a 21 ms contra 3 a 4 ms do Intlayer.
Sim, com a configuração scoped-dynamic: divida messages/{locale}.json em um namespace por rota, depois use pick(messages, [...]) em cada página e mantenha esse mapeamento correto conforme os componentes mudam. As linhas scoped-* do benchmark representam exatamente esse esforço. O Intlayer atinge 0% por padrão sem nada disso, pois o compilador delimita o conteúdo por componente. Veja otimização de bundle.
Não. O @intlayer/next-intl mantém useTranslations, getTranslations, useFormatter, t.rich(), plurais ICU e os helpers de navegação, servindo-os a partir de dicionários compilados. Apenas uma linha de plugin no next.config.ts. Passo a passo no guia de migração do next-intl.
O suporte a ICU está em desenvolvimento na API nativa. Os adaptadores de compatibilidade (@intlayer/next-intl, @intlayer/use-intl) executam o ICU: plurais, select, selectordinal, # e {ts, date, long} passam pelo resolvedor ICU do Intlayer. Leia formato de mensagem ICU para mais detalhes.
Sim. O plugin de sincronização JSON lê os arquivos, divide as chaves principais em dicionários e regrava as traduções nos mesmos arquivos quando a CLI ou o CMS os atualiza. O fluxo de trabalho de seus tradutores não muda.
Comparações relacionadas
Mesmo benchmark, outras bibliotecas:
Indo além com next-intl:
Documentos de referência:
Para entender de onde vêm essas bibliotecas, leia a história do i18n em JavaScript.
GitHub STARs
As estrelas do GitHub são um forte indicador de popularidade de um projeto, confiança da comunidade e relevância de longo prazo. Embora não sejam uma medida direta de qualidade técnica, elas refletem quantos desenvolvedores acham o projeto útil, acompanham seu progresso e provavelmente vão adotá-lo.
Atividade de commits
As estrelas mostram popularidade. Os commits mostram quanto trabalho é investido num projeto. No momento em que este texto foi escrito, o Intlayer soma cerca de 7.500 commits, mais do que a maioria das bibliotecas comparadas aqui e cerca de 5 vezes mais do que next-intl ou next-i18next.
- amannn/next-intl
- aymericzip/intlayer
Commits no branch padrão, fonte: API do GitHub.
O Intlayer é um monorepo, então o total inclui cada pacote de framework, a CLI e a documentação. Leia os commits como um sinal de atividade, não de qualidade.
Downloads no npm
- next-intl
- next-intlayer
Fonte: API de downloads do registro npm.
Os downloads recompensam as soluções mais antigas, não as melhores. Uma biblioteca lançada há anos continua sendo instalada por cada projeto que a escolheu na época, por cada execução de CI e por cada pacote que depende dela. O número mede a inércia mais do que uma escolha atual.
Os assistentes de IA amplificam o efeito. next-intl, i18next e vue-i18n estão por toda parte no código com que foram treinados, então eles os sugerem por padrão, sem comparar as alternativas. Cada sugestão gera downloads, que alimentam a próxima sugestão. Compare pelo benchmark, não pelo número de downloads.
Conclusão
next-intl é uma biblioteca sólida e bem mantida, e o benchmark confirma que está longe de ser a pior opção no Next.js. Mas seu modelo de catálogo centralizado coloca cada otimização nas mãos do desenvolvedor: a configuração ingênua vaza ~90% do conteúdo de páginas estrangeiras, e o runtime sozinho custa +12.6 KB gzip em cada página.
Intlayer move esse trabalho para o compilador. Dicionários por componente, lazy loading por idioma e eliminação de conteúdo não utilizado são saídas da compilação, não convenções manuais. O resultado na mesma aplicação: +0.3 KB por página, 0% de vazamento, componentes 3x menores, e troca de idioma 2x-4x mais rápida no TanStack Start.
Todos os dados brutos, aplicações de teste e scripts estão no repositório Benchmark Bloom. Execute você mesmo.
Consulte a documentação 'Por que Intlayer?' para mais detalhes.
Comentários
Ainda sem comentários. Seja o primeiro a compartilhar seus pensamentos.
