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/next-intl: Mesma API, Bundle Diferente

@intlayer/next-intl é um adaptador de compatibilidade: expõe a API next-intl (useTranslations, getTranslations, useLocale, t.rich(), plurais ICU, NextIntlClientProvider...) e a serve a partir de dicionários compilados pelo Intlayer. O código da aplicação não muda. O bundle muda.
Este artigo compara os dois na mesma aplicação Next.js, construída uma vez com next-intl e outra com o adaptador. Os números vêm de Benchmark Bloom, uma suite open-source que registra o que o navegador realmente baixa. Se você quiser a comparação next-intl vs Intlayer como bibliotecas, leia next-intl vs Intlayer. Este é sobre o que o adaptador muda quando você mantém seus componentes como estão.
tl;dr: No mesmo aplicativo Next.js, trocarnext-intlpor@intlayer/next-intlreduziu o JavaScript por página de 153.6 KB para 147.5 KB gzip, o componente médio de 21.8 KB para 8.1 KB, vazamento de strings de página estrangeira de ~90% para 0%, e hidratação de 14.7 ms para 12.8 ms, sem editar nenhum componente. No TanStack Start, o equivalenteuse-intl(@intlayer/use-intl) reduziu componentes de 76-87 KB para 9-11 KB e alternância de localidade de 7-21 ms para 4-9 ms. O adaptador custa 8.0 KB de runtime versus 14.7 KB paranext-intle 5.5 KB paranext-intlayernativo. Navegação e middleware são re-implementados na configuração de roteamento do Intlayer; nomes de caminho localizados (pathnames) são o único recurso não transferido.
O que é @intlayer/next-intl
next-intl é um runtime: getRequestConfig carrega um messages/{locale}.json por requisição, NextIntlClientProvider o envia para o cliente, e useTranslations("about") lê chaves daquele objeto no tempo de renderização. Cada otimização (namespaces, pick(messages, [...]) por página, lazy loading) é sua responsabilidade escrever.
@intlayer/next-intl mantém a primeira e última parte dessa cadeia e substitui o meio. Seus componentes ainda chamam useTranslations("about"); o que eles recebem vem de um dicionário Intlayer compilado no tempo de construção, escopo para aquele componente, apenas na locale ativa.
Três mecanismos fazem isso funcionar:
- Import aliasing.
createNextIntlPlugin()de@intlayer/next-intl/pluginencapsulawithIntlayere adiciona aliases do Webpack / Turbopack para quenext-intl,next-intl/server,next-intl/navigationenext-intl/middlewaresejam resolvidos para@intlayer/next-intl. Nenhuma importação em sua codebase é renomeada. - JSON como fonte de verdade. O plugin
syncJSONlê seusmessages/{locale}.jsonexistentes, divide suas chaves de nível superior em um dicionário por namespace e escreve as traduções de volta nos mesmos arquivos quando a CLI ou o CMS as atualiza. O fluxo de trabalho de seus tradutores permanece inalterado. - Call-site binding. A otimização do Intlayer (Babel ou SWC) reescreve
useTranslations("about")em uma chamada que recebe o dicionárioaboutdiretamente. O componente não alcança mais uma árvore de mensagens global; ele alcança seu próprio conteúdo.
Copiar o código para a área de transferência
Copiar o código para a área de transferência
Essa reescrita é por isso que as colunas de tamanho de componente e vazamento de página abaixo se movem: uma página apenas puxa os dicionários dos componentes que renderiza, e apenas na localidade sendo servida.
O que o adaptador mantém, ignora e não substitui
Abrir a tabela em um modal para ver todo o conteúdo claramente
next-intl API | Com @intlayer/next-intl |
|---|---|
useTranslations("ns") / getTranslations("ns") | ✅ Mantido. Vinculado ao dicionário ns em tempo de construção. As chaves são tipadas em relação ao seu conteúdo. |
getTranslations({ locale, namespace }) | ✅ Mantido |
t("key", { name }), t.rich(), t.markup(), t.raw() | ✅ Mantido. Plurais ICU, select, selectordinal, #, {ts, date, long} executados através do resolvedor ICU do Intlayer |
useLocale() / getLocale() / setRequestLocale() / setLocale | ✅ Mantido |
useFormatter() | ✅ Mantido. dateTime, number, relativeTime, list, dateTimeRange conectam ao Intl nativo |
NextIntlClientProvider | ✅ Mantido. Os props messages, timeZone e now são aceitos mas ignorados (um aviso de desenvolvimento o informa) |
getMessages() | ✅ Mantido para compatibilidade; não é mais necessário |
getRequestConfig() em src/i18n.ts | ⚠️ Não necessário. Os dicionários são compilados em tempo de build; não há carregamento de mensagens por requisição |
defineRouting() | ✅ Mantido. Os campos omitidos (locales, defaultLocale, localePrefix) são lidos de intlayer.config.ts |
createNavigation(), Link, redirect, usePathname, useRouter | ✅ Mantido. Re-implementado na configuração de roteamento do Intlayer; o argumento routing é aceito mas ignorado |
pathnames (nomes de rotas localizadas) | ❌ Aceito para digitação, não interpolado. Mantenha pathnames simples ou mova esse mapeamento para rewrite do Intlayer |
createMiddleware() | ✅ Mantido. Retorna o proxy do Intlayer; define o cookie NEXT_LOCALE para que useLocale() e seu comutador continuem funcionando |
NEXT_LOCALE cookie | ✅ Lido por padrão (a menos que você configure routing.storage por conta própria) |
Bare useTranslations() com nenhum namespace | ⚠️ Funciona, mas o local da chamada não está vinculado: ele se resolve através do registro de tempo de execução. Passe um namespace para obter os ganhos de bundle |
O benchmark
O que foi medido
A suite Benchmark Bloom constrói a mesma aplicação com cada setup: 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.
next-intl foi construído em quatro estratégias de carregamento, da configuração ingênua (arquivo messages/{locale}.json carregado integralmente) até a ótima (um namespace por rota + pick() por página). O adapter foi construído sobre os mesmos componentes da configuração ingênua, com apenas next.config.ts e intlayer.config.ts alterados. Não possui uma variante "scoped": o compilador faz o escopo do conteúdo por componente, portanto suas linhas static e dynamic já estão no escopo.
Para cada build, a suite registra:
- Lib size: tamanho gzip da biblioteca i18n de um componente vazio que apenas importa a biblioteca. O custo fixo do runtime.
- Page JS: JavaScript gzip baixado por página, calculado em média em todas as páginas e locales.
- Locale leak %: compartilha de strings traduzidas encontradas no JS baixado que pertencem a uma locale que o usuário não está visualizando.
- Page leak %: compartilha de strings traduzidas encontradas no JS baixado que pertencem a uma página na qual o usuário não está.
- Component avg: tamanho gzip médio de cada componente compilado isoladamente. Mostra quanto runtime i18n e catálogo um único componente carrega.
- E2E reactivity: tempo de parede entre selecionar uma nova locale e
html[lang]atualizar no DOM (Playwright, 5 iterações). - Hydration: duração da fase de hydration do React.
Os números abaixo vêm da execução datada de 2026-09-12 comnext-intl/use-intl4.14.2 e@intlayer/*9.5.1. A aplicação de teste é deliberadamente pequena (algumas dezenas de strings por locale), então os percentuais de vazamento descrevem um padrão: eles crescem com seu conteúdo enquanto o custo de runtime permanece fixo.
Resultados no Next.js
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
| Setup | 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 |
@intlayer/next-intl | static | 8.0 KB | 147.5 KB | 0.0% | 0.0% | 8.1 KB | 14.5 ms | 12.8 ms |
@intlayer/next-intl | dynamic | 8.0 KB | 148.7 KB | 0.0% | 0.0% | 8.1 KB | 11.7 ms | 12.8 ms |
next-intlayer (native) | static | 5.5 KB | 141.3 KB | 0.0% | 0.0% | 8.5 KB | 15.5 ms | 16.9 ms |
next-intlayer (native) | dynamic | 5.5 KB | 141.3 KB | 0.0% | 0.0% | 6.9 KB | 15.3 ms | 15.9 ms |
Como ler
- Mesmos componentes, 6 KB menos por página. A compilação do adapter da aplicação ingênua chega a 147.5 KB, abaixo de todas as configurações
next-intl, incluindo a totalmente otimizada (153.6 KB). O próprio runtime é a diferença: 8.0 KB versus 14.7 KB, pagos em cada página. - Vazamento vai a 0% sem tocar em um componente. A configuração ingênua de
next-intlenvia ~90% de strings de páginas estrangeiras em cada página. Atingir 0% comnext-intlsignifica as configuraçõesscoped-*: um namespace por rota, epick(messages, [...])em cada página. O adapter atinge 0% a partir do código ingênuo porque a passagem de otimização vincula cadauseTranslations("ns")ao seu próprio dicionário. - Componentes encolhem 2.7x. Um componente compilado isoladamente tem média de 21.8 KB com
next-intl(ele atinge o provider e a árvore de mensagens) e 8.1 KB com o adapter. Na configuraçãoscoped-staticdonext-intlesse número sobe para 80 KB, porque o arquivo de namespace de cada rota fica acessível a partir da página que o seleciona. - Hidratação é 2 ms mais rápida (12.8 vs 14.7 ms): não há objeto de mensagem para desserializar do payload RSC antes do React poder hidratar.
- O adapter não é o runtime nativo.
next-intlayerfica em 141.3 KB, +0.3 KB sobre a app base, com um runtime de 5.5 KB. O adapter carrega a superfície de API donext-intl(useFormatter,t.rich, o resolver ICU) no topo do core do Intlayer, daí 8.0 KB e +6 KB por página. É a ponte, não o destino.
Tabela completa, cada biblioteca e cada estratégia, no relatório de benchmark do Next.js.
Resultados em TanStack Start (use-intl)
use-intl é o core agnóstico de framework do next-intl. Seu adapter, @intlayer/use-intl, segue o mesmo design com um plugin Vite (@intlayer/use-intl/plugin).
Abrir a tabela em um modal para ver todo o conteúdo claramente
| Configuração | Estratégia | Tamanho da biblioteca (gz) | JS médio da página (gz) | Vazamento de localidade | Vazamento de página | Componente médio (gz) | Reatividade E2E | Hidratação |
|---|---|---|---|---|---|---|---|---|
| base (sem i18n) | - | 0.0 KB | 111.0 KB | 0.0% | 0.0% | 0.7 KB | 8.1 ms | 21.6 ms |
use-intl | static | 14.1 KB | 179.8 KB | 50.0% | 89.8% | 76.0 KB | 6.7 ms | 15.3 ms |
use-intl | dynamic | 14.1 KB | 119.4 KB | 0.0% | 89.8% | 75.9 KB | 7.0 ms | 15.4 ms |
use-intl | scoped-static | 14.1 KB | 128.7 KB | 0.0% | 0.0% | 87.1 KB | 20.9 ms | 24.8 ms |
use-intl | scoped-dynamic | 14.1 KB | 128.7 KB | 0.0% | 0.0% | 87.1 KB | 13.3 ms | 25.9 ms |
@intlayer/use-intl | static | 7.3 KB | 135.8 KB | 49.7% | 0.0% | 10.9 KB | 4.2 ms | 10.5 ms |
@intlayer/use-intl | dynamic | 7.3 KB | 129.7 KB | 0.0% | 0.0% | 9.3 KB | 8.7 ms | 16.1 ms |
intlayer (native) | static | 5.0 KB | 125.8 KB | 50.0% | 0.0% | 8.1 KB | 3.2 ms | 11.5 ms |
intlayer (native) | dynamic | 5.0 KB | 118.6 KB | 0.0% | 0.0% | 6.3 KB | 3.6 ms | 14.1 ms |
Como ler
- Bytes por página são equivalentes ao
use-intlotimizado.@intlayer/use-intlem mododynamic(129.7 KB) está dentro de 1 KB doscoped-dynamic(128.7 KB) douse-intl, e 10 KB acima dodynamicsimples (119.4 KB) douse-intl. Essa linhadynamicsimples ainda vaza 90% das strings de páginas estrangeiras; a contagem de bytes é baixa porque o conteúdo da aplicação de teste é pequeno. O 0% do adapter é o que permanece constante conforme o conteúdo cresce. - Os componentes são 7-9x menores. Os componentes
use-intltêm em média 76-87 KB em todas as estratégias, porqueuseTranslationsestá vinculado ao objeto de mensagens completo do provedor. O adapter tem em média 9-11 KB. - A alternância de locale é mais rápida. As configurações otimizadas de
use-intllevam 13-21 ms para atualizarhtml[lang]; o adapter leva 4-9 ms. Menos componentes são re-renderizados, e nada é re-selecionado de uma árvore de mensagens. staticmantém cada locale. A linhastaticdo adapter mostra 49,7% de vazamento de locale, o mesmo que o Intlayer nativo em modostatic: todos os locales são agrupados, apenas os dicionários da página. Uma linha de configuração (importMode: 'dynamic') remove isso.
Tabela completa no relatório de benchmark do TanStack Start.
Por que os números mudam

Nada no componente mudou, então os ganhos vêm inteiramente do que useTranslations está vinculado.
Com next-intl, a vinculação é o provedor. NextIntlClientProvider recebe o objeto messages inteiro para a localidade; cada useTranslations("about") lê a partir dele. O bundler vê um componente importando um hook que lê um contexto, e não pode saber que apenas a ramificação about é usada. As rotas abaixo compartilham o mesmo objeto de mensagens, então a coluna page-leak lê ~90% até você dividir o arquivo você mesmo, e o desperdício cresce em dois eixos ao mesmo tempo, páginas e idiomas:

Copiar o código para a área de transferência
Com @intlayer/next-intl, a vinculação é o dicionário. syncJSON transforma messages/en.json em um dicionário por chave de nível superior; o compilador resolve qual componente chama useTranslations("about") e lhe passa about diretamente, na locale ativa, como um import que o bundler pode rastrear e dividir.
Copiar o código para a área de transferência
src/i18n.ts e a prop messages desaparecem. Tudo o resto é idêntico.
Migração em três passos
Instalar
bashCopiar códigoCopiar o código para a área de transferência
O comando detecta
next-intle instalaintlayer,next-intlayer,@intlayer/next-intle@intlayer/sync-json-plugin. Mantenhanext-intlinstalado: é uma dependência peer do adaptador e fornece os tipos.Aponte o Intlayer para suas mensagens
intlayer.config.tsCopiar códigoCopiar o código para a área de transferência
messages/{locale}.jsonpermanece onde está. Cada chave de nível superior torna-se um dicionário;useTranslations("about")mapeia para o dicionárioabout.Envolver next.config.ts
next.config.tsCopiar códigoCopiar o código para a área de transferência
createNextIntlPlugin()compõewithIntlayer(monitoramento de conteúdo, compilação de dicionário, a otimização) e os aliasesnext-intl→@intlayer/next-intlpara Webpack e Turbopack. Faça o build, e os números nas tabelas acima são seus.
O que você pode deletar depois
Abrir a tabela em um modal para ver todo o conteúdo claramente
| Arquivo / padrão | Por quê |
|---|---|
getRequestConfig em src/i18n.ts | Sem carregamento de mensagens por requisição. Mantenha o arquivo apenas se ele também exportar helpers createNavigation |
messages={...} no NextIntlClientProvider | O adapter lê a saída compilada; a prop é ignorada e registra um aviso em desenvolvimento |
await getMessages() em layouts | Mesmo motivo |
pick(messages, [...]) por página | O compilador faz a seleção, por componente |
O que você ganha além de bytes
- Chaves tipadas.
useTranslations("about")é tipado contra o dicionário compiladoabout.t("does.not.exist")é um erro TypeScript, não um fallback em tempo de execução. npx intlayer testfalha no CI quando um locale está faltando uma chave.npx intlayer filltraduz as chaves ausentes com o provedor de sua escolha (OpenAI, Anthropic, Mistral, Gemini...) usando sua própria chave, e escreve o resultado de volta emmessages/{locale}.json.- Visual Editor e CMS funcionam nos mesmos dicionários, portanto, não-desenvolvedores podem editar
messages/fr.jsonatravés de uma UI e o arquivo é atualizado. - Migração incremental para
.content.ts. Qualquer componente pode alternar deuseTranslations("about")parauseIntlayer("about")com um arquivo de conteúdo co-localizado, um de cada vez. Os dicionários JSON e.content.tscoexistem e se mesclam.
Limites a conhecer antes de começar
createNavigation(routing) e createMiddleware(routing) mantêm sua assinatura mas ignoram o argumento: idiomas, idioma padrão e estratégia de prefixo vêm da configuração routing do Intlayer. Se você usa os pathnames localizados do next-intl (/about para /a-propos), o adaptador não os interpola; o routing.rewrite do Intlayer cobre esse caso mas é uma mudança separada.
A etapa de otimização precisa de um namespace estático para saber qual dicionário importar. Uma chamada sem namespace ainda funciona através de um registro em tempo de execução que faz referência a cada dicionário, o que é exatamente o vazamento que você estava tentando remover. Passe o namespace.
8.0 KB de tempo de execução versus 5.5 KB para next-intlayer, e +6-7 KB por página em relação ao build nativo. Ele paga pela superfície de API do next-intl. Se você atingir o ponto em que cada componente foi migrado para useIntlayer, remova o adaptador.
Os formatadores são baseados no Intl nativo e apenas o idioma influencia sua saída. Se você depende de um fuso horário forçado ou de um now fixo para datas estáveis na hidratação, lide com isso no local da chamada. Veja formatação de data, hora e número.
Quando usar qual?
Seu aplicativo é pequeno, seu bundle não é uma preocupação e sua equipe lida bem gerenciando namespaces e pick() por página.
Você já está no next-intl hoje e quer os ganhos de bundle, vazamento e hidratação, chaves tipadas e as ferramentas de CLI / CMS sem uma reescrita. Este é o ponto de entrada recomendado para qualquer base de código next-intl existente.
Para novos projetos, ou assim que o adaptador tiver cumprido seu papel. É o mais leve dos três (5.5 KB, +0.3 KB por página) e desbloqueia componentes de servidor síncronos, arquivos .content.ts por componente e o conjunto completo de recursos. Comece com Intlayer com Next.js.
FAQ
No Next.js, sim para componentes: a compilação do benchmark alterou apenas next.config.ts e intlayer.config.ts. getRequestConfig em src/i18n.ts, a prop messages no provider e as chamadas pick() por página tornam-se código morto que você pode excluir depois.
Elas continuam funcionando. t("key", { count }), t.rich(), t.markup(), select, selectordinal, # e {ts, date, long} são resolvidos pelo resolvedor ICU do Intlayer. Veja formato de mensagem ICU.
Ele carrega a superfície da API do next-intl sobre o núcleo do Intlayer: useFormatter, t.rich, o resolvedor ICU, os helpers de navegação. Isso representa 8.0 KB contra 5.5 KB, e +6 KB por página. É a ponte, não o destino.
Sim. Qualquer componente pode mudar de useTranslations("about") para useIntlayer("about") com um arquivo .content.ts colocalizado. Dicionários JSON e .content.ts coexistem e se fundem.
Não através dos pathnames do next-intl: o adaptador os aceita para tipagem, mas não os interpola. Use em vez disso routing.rewrite do Intlayer, que emite os literais localizados no registro de tipos.
Related comparisons
Mesma série de adaptadores:
As bibliotecas comparadas diretamente:
Documentação de referência:
Para entender de onde vêm essas bibliotecas, leia a história do i18n em JavaScript.
Conclusão
@intlayer/next-intl faz uma coisa: muda o que useTranslations está vinculado, de um provider que contém todas as mensagens para um dicionário compilado para esse componente. No mesmo app Next.js que vale 6 KB por página, componentes 2,7x menores, 0% de vazamento e 2 ms de hidratação, antes de qualquer pessoa abrir um arquivo de componente. Navigation e middleware mantêm sua API no topo da configuração de roteamento do Intlayer, e o runtime nativo next-intlayer permanece ainda mais leve.
Todos os dados brutos, os apps de teste e os scripts estão no repositório Benchmark Bloom. Execute você mesmo.
Consulte o doc 'Por que Intlayer?' para mais detalhes.
Comentários
Ainda sem comentários. Seja o primeiro a compartilhar seus pensamentos.
