@intlayer/docs 6.1.4 → 6.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/blog/ar/next-i18next_vs_next-intl_vs_intlayer.md +1135 -75
  2. package/blog/ar/nextjs-multilingual-seo-comparison.md +364 -0
  3. package/blog/de/next-i18next_vs_next-intl_vs_intlayer.md +1139 -72
  4. package/blog/de/nextjs-multilingual-seo-comparison.md +362 -0
  5. package/blog/en/next-i18next_vs_next-intl_vs_intlayer.md +224 -240
  6. package/blog/en/nextjs-multilingual-seo-comparison.md +360 -0
  7. package/blog/en-GB/next-i18next_vs_next-intl_vs_intlayer.md +1134 -37
  8. package/blog/en-GB/nextjs-multilingual-seo-comparison.md +360 -0
  9. package/blog/es/next-i18next_vs_next-intl_vs_intlayer.md +1122 -64
  10. package/blog/es/nextjs-multilingual-seo-comparison.md +363 -0
  11. package/blog/fr/next-i18next_vs_next-intl_vs_intlayer.md +1132 -75
  12. package/blog/fr/nextjs-multilingual-seo-comparison.md +362 -0
  13. package/blog/hi/nextjs-multilingual-seo-comparison.md +363 -0
  14. package/blog/it/next-i18next_vs_next-intl_vs_intlayer.md +1120 -55
  15. package/blog/it/nextjs-multilingual-seo-comparison.md +363 -0
  16. package/blog/ja/next-i18next_vs_next-intl_vs_intlayer.md +1140 -76
  17. package/blog/ja/nextjs-multilingual-seo-comparison.md +362 -0
  18. package/blog/ko/next-i18next_vs_next-intl_vs_intlayer.md +1129 -73
  19. package/blog/ko/nextjs-multilingual-seo-comparison.md +362 -0
  20. package/blog/pt/next-i18next_vs_next-intl_vs_intlayer.md +1133 -76
  21. package/blog/pt/nextjs-multilingual-seo-comparison.md +362 -0
  22. package/blog/ru/next-i18next_vs_next-intl_vs_intlayer.md +1142 -74
  23. package/blog/ru/nextjs-multilingual-seo-comparison.md +370 -0
  24. package/blog/tr/nextjs-multilingual-seo-comparison.md +362 -0
  25. package/blog/zh/next-i18next_vs_next-intl_vs_intlayer.md +1142 -75
  26. package/blog/zh/nextjs-multilingual-seo-comparison.md +394 -0
  27. package/dist/cjs/generated/blog.entry.cjs +16 -0
  28. package/dist/cjs/generated/blog.entry.cjs.map +1 -1
  29. package/dist/esm/generated/blog.entry.mjs +16 -0
  30. package/dist/esm/generated/blog.entry.mjs.map +1 -1
  31. package/dist/types/generated/blog.entry.d.ts +1 -0
  32. package/dist/types/generated/blog.entry.d.ts.map +1 -1
  33. package/docs/en/interest_of_intlayer.md +2 -2
  34. package/package.json +10 -10
  35. package/src/generated/blog.entry.ts +16 -0
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  createdAt: 2025-08-23
3
- updatedAt: 2025-08-23
3
+ updatedAt: 2025-09-29
4
4
  title: next-i18next vs next-intl vs Intlayer
5
5
  description: Comparar next-i18next com next-intl e Intlayer para a internacionalização (i18n) de uma aplicação Next.js
6
6
  keywords:
@@ -19,144 +19,1201 @@ slugs:
19
19
 
20
20
  # next-i18next VS next-intl VS intlayer | Internacionalização (i18n) no Next.js
21
21
 
22
- Este guia compara três opções amplamente usadas de i18n para **Next.js**: **next-intl**, **next-i18next** e **Intlayer**.
22
+ Vamos analisar as semelhanças e diferenças entre três opções de i18n para Next.js: next-i18next, next-intl e Intlayer.
23
+
24
+ Este não é um tutorial completo. É uma comparação para ajudar você a escolher.
25
+
23
26
  Focamos no **Next.js 13+ App Router** (com **React Server Components**) e avaliamos:
24
27
 
25
28
  1. **Arquitetura e organização de conteúdo**
26
29
  2. **TypeScript e segurança**
27
- 3. **Tratamento de traduções faltantes**
30
+ 3. **Tratamento de traduções ausentes**
28
31
  4. **Roteamento e middleware**
29
32
  5. **Desempenho e comportamento de carregamento**
30
33
  6. **Experiência do desenvolvedor (DX), ferramentas e manutenção**
31
- 7. **SEO e escalabilidade para projetos grandes**
34
+ 7. **SEO e escalabilidade para grandes projetos**
35
+
36
+ > **resumo**: Todos os três podem localizar uma aplicação Next.js. Se você deseja **conteúdo com escopo por componente**, **tipos TypeScript rigorosos**, **verificações de chaves ausentes em tempo de build**, **dicionários otimizados por tree-shaking** e **suporte nativo ao App Router + helpers de SEO**, **Intlayer** é a escolha mais completa e moderna.
32
37
 
33
- > **resumo**: Todos os três podem localizar uma aplicação Next.js. Se você deseja **conteúdo com escopo por componente**, **tipos TypeScript rigorosos**, **verificações de chaves ausentes em tempo de build**, **dicionários otimizados por tree-shaking** e **helpers de App Router + SEO de primeira classe**, **Intlayer** é a escolha mais completa e moderna.
38
+ > Uma confusão comum feita por desenvolvedores é pensar que `next-intl` é a versão Next.js do `react-intl`. Não é `next-intl` é mantido por [Amann](https://github.com/amannn), enquanto `react-intl` é mantido por [FormatJS](https://github.com/formatjs/formatjs).
34
39
 
35
40
  ---
36
41
 
37
- ## Posicionamento em alto nível
42
+ ## Em resumo
38
43
 
39
- - **next-intl** - Formatação de mensagens leve e direta com suporte sólido para Next.js. Catálogos centralizados são comuns; a experiência do desenvolvedor (DX) é simples, mas a segurança e a manutenção em larga escala permanecem principalmente sob sua responsabilidade.
40
- - **next-i18next** - i18next com roupagem Next.js. Ecossistema maduro e recursos via plugins (por exemplo, ICU), mas a configuração pode ser verbosa e os catálogos tendem a se centralizar conforme os projetos crescem.
44
+ - **next-intl** - Formatação de mensagens leve e direta com suporte sólido ao Next.js. Catálogos centralizados são comuns; a experiência do desenvolvedor (DX) é simples, mas a segurança e a manutenção em larga escala permanecem principalmente sob sua responsabilidade.
45
+ - **next-i18next** - i18next com aparência de Next.js. Ecossistema maduro e recursos via plugins (por exemplo, ICU), mas a configuração pode ser verbosa e os catálogos tendem a se centralizar conforme os projetos crescem.
41
46
  - **Intlayer** - Modelo de conteúdo centrado em componentes para Next.js, **tipagem TS rigorosa**, **verificações em tempo de build**, **tree-shaking**, **middleware e helpers de SEO integrados**, **Editor Visual/CMS** opcional e **traduções assistidas por IA**.
42
47
 
43
48
  ---
44
49
 
50
+ | Library | GitHub Stars | Total Commits | Last Commit | First Version | NPM Version | NPM Downloads |
51
+ | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
52
+ | `aymericzip/intlayer` | [![GitHub Repo stars](https://img.shields.io/github/stars/aymericzip/intlayer?style=for-the-badge&label=%E2%AD%90%20stars)](https://github.com/aymericzip/intlayer/stargazers) | [![GitHub commit activity](https://img.shields.io/github/commit-activity/t/aymericzip/intlayer?style=for-the-badge&label=commits)](https://github.com/aymericzip/intlayer/commits) | [![Last Commit](https://img.shields.io/github/last-commit/aymericzip/intlayer?style=for-the-badge)](https://github.com/aymericzip/intlayer/commits) | April 2024 | [![npm](https://img.shields.io/npm/v/intlayer?style=for-the-badge)](https://www.npmjs.com/package/intlayer) | [![npm downloads](https://img.shields.io/npm/dm/intlayer?style=for-the-badge)](https://www.npmjs.com/package/intlayer) |
53
+ | `amannn/next-intl` | [![GitHub Repo stars](https://img.shields.io/github/stars/amannn/next-intl?style=for-the-badge&label=%E2%AD%90%20stars)](https://github.com/amannn/next-intl/stargazers) | [![GitHub commit activity](https://img.shields.io/github/commit-activity/t/amannn/next-intl?style=for-the-badge&label=commits)](https://github.com/amannn/next-intl/commits) | [![Last Commit](https://img.shields.io/github/last-commit/amannn/next-intl?style=for-the-badge)](https://github.com/amannn/next-intl/commits) | Nov 2020 | [![npm](https://img.shields.io/npm/v/next-intl?style=for-the-badge)](https://www.npmjs.com/package/next-intl) | [![npm downloads](https://img.shields.io/npm/dm/next-intl?style=for-the-badge)](https://www.npmjs.com/package/next-intl) |
54
+ | `i18next/i18next` | [![GitHub Repo stars](https://img.shields.io/github/stars/i18next/i18next?style=for-the-badge&label=%E2%AD%90%20stars)](https://github.com/i18next/i18next/stargazers) | [![GitHub commit activity](https://img.shields.io/github/commit-activity/t/i18next/i18next?style=for-the-badge&label=commits)](https://github.com/i18next/i18next/commits) | [![Last Commit](https://img.shields.io/github/last-commit/i18next/i18next?style=for-the-badge)](https://github.com/i18next/i18next/commits) | Jan 2012 | [![npm](https://img.shields.io/npm/v/i18next?style=for-the-badge)](https://www.npmjs.com/package/i18next) | [![npm downloads](https://img.shields.io/npm/dm/i18next?style=for-the-badge)](https://www.npmjs.com/package/i18next) |
55
+ | `i18next/next-i18next` | [![GitHub Repo stars](https://img.shields.io/github/stars/i18next/next-i18next?style=for-the-badge&label=%E2%AD%90%20stars)](https://github.com/i18next/next-i18next/stargazers) | [![GitHub commit activity](https://img.shields.io/github/commit-activity/t/i18next/next-i18next?style=for-the-badge&label=commits)](https://github.com/i18next/next-i18next/commits) | [![Last Commit](https://img.shields.io/github/last-commit/i18next/next-i18next?style=for-the-badge)](https://github.com/i18next/next-i18next/commits) | Nov 2018 | [![npm](https://img.shields.io/npm/v/next-i18next?style=for-the-badge)](https://www.npmjs.com/package/next-i18next) | [![npm downloads](https://img.shields.io/npm/dm/next-i18next?style=for-the-badge)](https://www.npmjs.com/package/next-i18next) |
56
+
57
+ > Os distintivos são atualizados automaticamente. As capturas de tela podem variar ao longo do tempo.
58
+
59
+ ---
60
+
45
61
  ## Comparação de Recursos Lado a Lado (focado em Next.js)
46
62
 
47
- | Recurso | `next-intlayer` (Intlayer) | `next-intl` | `next-i18next` |
48
- | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ |
49
- | **Traduções Próximas aos Componentes** | ✅ Sim, conteúdo localizado junto a cada componente | ❌ Não | ❌ Não |
50
- | **Integração com TypeScript** | ✅ Avançada, tipos estritos gerados automaticamente | ✅ Boa | ⚠️ Básica |
51
- | **Detecção de Tradução Ausente** | ✅ Destaque de erro no TypeScript e erro/aviso em tempo de compilação | ⚠️ Recurso de fallback em tempo de execução | ⚠️ Recurso de fallback em tempo de execução |
52
- | **Conteúdo Rico (JSX/Markdown/componentes)** | ✅ Suporte direto | ❌ Não projetado para nós ricos | ⚠️ Limitado |
53
- | **Tradução com IA** | ✅ Sim, suporta múltiplos provedores de IA. Usável com suas próprias chaves de API. Considera o contexto da sua aplicação e o escopo do conteúdo | ❌ Não | ❌ Não |
54
- | **Editor Visual** | ✅ Sim, Editor Visual local + CMS opcional; pode externalizar conteúdo da base de código; incorporável | ❌ Não / disponível via plataformas externas de localização | ❌ Não / disponível via plataformas externas de localização |
55
- | **Roteamento Localizado** | ✅ Sim, suporta caminhos localizados nativamente (funciona com Next.js & Vite) | ✅ Integrado, App Router suporta o segmento `[locale]` | ✅ Integrado |
56
- | **Geração Dinâmica de Rotas** | ✅ Sim | ✅ Sim | ✅ Sim |
57
- | **Pluralização** | ✅ Padrões baseados em enumeração | ✅ Bom | ✅ Bom |
58
- | **Formatação (datas, números, moedas)** | ✅ Formatadores otimizados (Intl por trás dos panos) | ✅ Bom (helpers Intl) | ✅ Bom (helpers Intl) |
59
- | **Formato de Conteúdo** | ✅ .tsx, .ts, .js, .json, .md, .txt, (.yaml em desenvolvimento) | ✅ .json, .js, .ts | ⚠️ .json |
60
- | **Suporte ICU** | ⚠️ Em desenvolvimento | ✅ Sim | ⚠️ Via plugin (`i18next-icu`) |
61
- | **Auxiliares de SEO (hreflang, sitemap)** | ✅ Ferramentas integradas: auxiliares para sitemap, robots.txt, metadados | ✅ Bom | ✅ Bom |
62
- | **Ecossistema / Comunidade** | ⚠️ Menor, mas crescendo rápido e reativa | ✅ Médio porte, focado em Next.js | ✅ Médio porte, focado em Next.js |
63
- | **Renderização no lado do servidor & Componentes de Servidor** | ✅ Sim, otimizado para SSR / Componentes de Servidor React | ⚠️ Suportado a nível de página, mas é necessário passar funções t na árvore de componentes para componentes filhos de servidor | ⚠️ Suportado a nível de página, mas é necessário passar funções t na árvore de componentes para componentes filhos de servidor |
64
- | **Tree-shaking (carregar apenas conteúdo usado)** | ✅ Sim, por componente em tempo de build via plugins Babel/SWC | ⚠️ Parcial | ⚠️ Parcial |
65
- | **Carregamento preguiçoso (Lazy loading)** | ✅ Sim, por localidade / por dicionário | ✅ Sim (por rota/por localidade), necessita de gestão de namespace | ✅ Sim (por rota/por localidade), necessita de gestão de namespace |
66
- | **Remoção de conteúdo não utilizado** | ✅ Sim, por dicionário em tempo de build | ❌ Não, pode ser gerenciado manualmente com gestão de namespace | ❌ Não, pode ser gerenciado manualmente com gestão de namespace |
67
- | **Gestão de Grandes Projetos** | ✅ Incentiva modularidade, adequado para sistemas de design | ✅ Modular com configuração | ✅ Modular com configuração |
68
-
69
- ---
70
-
71
- ## Comparação detalhada
72
-
73
- ### 1) Arquitetura e escalabilidade
74
-
75
- - **next-intl / next-i18next**: Por padrão, utiliza **catálogos centralizados** por localidade (além de **namespaces** no i18next). Funciona bem no início, mas frequentemente se torna uma grande área compartilhada com aumento do acoplamento e da rotatividade de chaves.
76
- - **Intlayer**: Incentiva dicionários **por componente** (ou por funcionalidade) **co-localizados** com o código que eles atendem. Isso reduz a carga cognitiva, facilita a duplicação/migração de partes da interface e diminui conflitos entre equipes. Conteúdo não utilizado é naturalmente mais fácil de identificar e eliminar.
63
+ | Recurso | `next-intlayer` (Intlayer) | `next-intl` | `next-i18next` |
64
+ | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ |
65
+ | **Traduções Próximas aos Componentes** | ✅ Sim, conteúdo localizado junto a cada componente | ❌ Não | ❌ Não |
66
+ | **Integração com TypeScript** | ✅ Avançada, tipos estritos gerados automaticamente | ✅ Boa | ⚠️ Básica |
67
+ | **Detecção de Tradução Ausente** | ✅ Destaque de erro no TypeScript e erro/aviso em tempo de compilação | ⚠️ Retorno em tempo de execução | ⚠️ Retorno em tempo de execução |
68
+ | **Conteúdo Rico (JSX/Markdown/componentes)** | ✅ Suporte direto | ❌ Não projetado para nós ricos | ⚠️ Limitado |
69
+ | **Tradução com IA** | ✅ Sim, suporta múltiplos provedores de IA. Usável com suas próprias chaves de API. Considera o contexto da sua aplicação e o escopo do conteúdo | ❌ Não | ❌ Não |
70
+ | **Editor Visual** | ✅ Sim, Editor Visual local + CMS opcional; pode externalizar conteúdo da base de código; incorporável | ❌ Não / disponível via plataformas externas de localização | ❌ Não / disponível via plataformas externas de localização |
71
+ | **Roteamento Localizado** | ✅ Sim, suporta caminhos localizados nativamente (funciona com Next.js & Vite) | ✅ Integrado, App Router suporta segmento `[locale]` | ✅ Integrado |
72
+ | **Geração Dinâmica de Rotas** | ✅ Sim | ✅ Sim | ✅ Sim |
73
+ | **Pluralização** | ✅ Padrões baseados em enumeração | ✅ Bom | ✅ Bom |
74
+ | **Formatação (datas, números, moedas)** | ✅ Formatadores otimizados (Intl por trás dos panos) | ✅ Bom (helpers do Intl) | ✅ Bom (helpers do Intl) |
75
+ | **Formato do Conteúdo** | ✅ .tsx, .ts, .js, .json, .md, .txt, (.yaml em desenvolvimento) | ✅ .json, .js, .ts | ⚠️ .json |
76
+ | **Suporte ICU** | ⚠️ Em desenvolvimento | ✅ Sim | ⚠️ Via plugin (`i18next-icu`) |
77
+ | **Auxiliares de SEO (hreflang, sitemap)** | ✅ Ferramentas integradas: auxiliares para sitemap, robots.txt, metadados | ✅ Bom | ✅ Bom |
78
+ | **Ecossistema / Comunidade** | ⚠️ Menor, mas crescendo rápido e reativo | ✅ Bom | ✅ Bom |
79
+ | **Renderização do lado servidor & Componentes de Servidor** | ✅ Sim, otimizado para SSR / Componentes de Servidor React | ⚠️ Suportado a nível de página, mas é necessário passar funções t na árvore de componentes para componentes filhos de servidor | ⚠️ Suportado a nível de página, mas é necessário passar funções t na árvore de componentes para componentes filhos de servidor |
80
+ | **Tree-shaking (carregar apenas o conteúdo usado)** | ✅ Sim, por componente no momento da build via plugins Babel/SWC | ⚠️ Parcial | ⚠️ Parcial |
81
+ | **Carregamento preguiçoso (Lazy loading)** | ✅ Sim, por localidade / por dicionário | ✅ Sim (por rota/por localidade), necessita gerenciamento de namespace | ✅ Sim (por rota/por localidade), necessita gerenciamento de namespace |
82
+ | **Remover conteúdo não utilizado** | ✅ Sim, por dicionário em tempo de build | ❌ Não, pode ser gerenciado manualmente com gerenciamento de namespace | ❌ Não, pode ser gerenciado manualmente com gerenciamento de namespace |
83
+ | **Gerenciamento de Grandes Projetos** | ✅ Incentiva modularidade, adequado para design-system | ✅ Modular com configuração | ✅ Modular com configuração |
84
+ | **Testando Traduções Ausentes (CLI/CI)** | ✅ CLI: `npx intlayer content test` (auditoria amigável para CI) | ⚠️ Não embutido; a documentação sugere `npx @lingual/i18n-check` | ⚠️ Não embutido; depende das ferramentas do i18next / runtime `saveMissing` |
85
+
86
+ ---
87
+
88
+ ## Introdução
89
+
90
+ Next.js oferece suporte embutido para roteamento internacionalizado (ex.: segmentos de localidade). Mas essa funcionalidade não realiza traduções por si só. Você ainda precisa de uma biblioteca para renderizar conteúdo localizado para seus usuários.
91
+
92
+ Existem muitas bibliotecas i18n, mas no universo Next.js hoje, três estão ganhando destaque: next-i18next, next-intl e Intlayer.
93
+
94
+ ---
95
+
96
+ ## Arquitetura & escalabilidade
97
+
98
+ - **next-intl / next-i18next**: Por padrão, utilizam **catálogos centralizados** por localidade (além de **namespaces** no i18next). Funciona bem no início, mas frequentemente se torna uma grande área compartilhada com aumento do acoplamento e da rotatividade de chaves.
99
+ - **Intlayer**: Incentiva dicionários **por componente** (ou por funcionalidade) **co-localizados** com o código que atendem. Isso reduz a carga cognitiva, facilita a duplicação/migração de partes da interface e diminui conflitos entre equipes. Conteúdo não utilizado é naturalmente mais fácil de identificar e eliminar.
77
100
 
78
101
  **Por que isso importa:** Em grandes bases de código ou configurações de sistemas de design, **conteúdo modular** escala melhor do que catálogos monolíticos.
79
102
 
80
103
  ---
81
104
 
82
- ### 2) TypeScript & segurança
105
+ ## Tamanhos de bundle e dependências
106
+
107
+ Após a construção da aplicação, o bundle é o JavaScript que o navegador irá carregar para renderizar a página. Portanto, o tamanho do bundle é importante para o desempenho da aplicação.
108
+
109
+ Dois componentes são importantes no contexto de um bundle de aplicação multilíngue:
110
+
111
+ - O código da aplicação
112
+ - O conteúdo carregado pelo navegador
113
+
114
+ ## Código da Aplicação
115
+
116
+ A importância do código da aplicação é mínima neste caso. As três soluções são tree-shakable, o que significa que as partes não utilizadas do código não são incluídas no bundle.
117
+
118
+ Aqui está uma comparação do tamanho do bundle JavaScript carregado pelo navegador para uma aplicação multilíngue com as três soluções.
119
+
120
+ Se não precisarmos de nenhum formatador na aplicação, a lista de funções exportadas após o tree-shaking será:
121
+
122
+ - **next-intlayer**: `useIntlayer`, `useLocale`, `NextIntlClientProvider`, (O tamanho do bundle é 180,6 kB -> 78,6 kB (gzip))
123
+ - **next-intl**: `useTranslations`, `useLocale`, `NextIntlClientProvider`, (O tamanho do bundle é 101,3 kB -> 31,4 kB (gzip))
124
+ - **next-i18next**: `useTranslation`, `useI18n`, `I18nextProvider`, (O tamanho do bundle é 80,7 kB -> 25,5 kB (gzip))
125
+
126
+ Estas funções são apenas wrappers em torno do contexto/estado do React, portanto o impacto total da biblioteca i18n no tamanho do bundle é mínimo.
127
+
128
+ > Intlayer é ligeiramente maior que `next-intl` e `next-i18next` porque inclui mais lógica na função `useIntlayer`. Isso está relacionado à integração com markdown e `intlayer-editor`.
129
+
130
+ ## Conteúdo e Traduções
131
+
132
+ Esta parte é frequentemente ignorada pelos desenvolvedores, mas vamos considerar o caso de uma aplicação composta por 10 páginas em 10 idiomas. Vamos supor que cada página integre 100% de conteúdo único para simplificar o cálculo (na realidade, muito conteúdo é redundante entre páginas, por exemplo, título da página, cabeçalho, rodapé, etc.).
133
+
134
+ Um usuário que deseja visitar a página `/fr/about` carregará o conteúdo de uma página em um determinado idioma. Ignorar a otimização do conteúdo significaria carregar 8.200% `((1 + (((10 páginas - 1) × (10 idiomas - 1)))) × 100)` do conteúdo da aplicação desnecessariamente. Você vê o problema? Mesmo que esse conteúdo permaneça texto, e enquanto você provavelmente prefere pensar em otimizar as imagens do seu site, você está enviando conteúdo inútil pelo mundo todo e fazendo os computadores dos usuários processarem isso sem necessidade.
135
+
136
+ Dois problemas importantes:
137
+
138
+ - **Divisão por rota:**
139
+
140
+ > Se eu estiver na página `/about`, não quero carregar o conteúdo da página `/home`
141
+
142
+ - **Divisão por localidade:**
143
+
144
+ > Se eu estiver na página `/fr/about`, não quero carregar o conteúdo da página `/en/about`
145
+
146
+ Novamente, as três soluções estão cientes desses problemas e permitem gerenciar essas otimizações. A diferença entre as três soluções é a DX (Experiência do Desenvolvedor).
83
147
 
84
- - **next-intl**: Suporte sólido ao TypeScript, mas **as chaves não são estritamente tipadas por padrão**; você precisará manter padrões de segurança manualmente.
85
- - **next-i18next**: Tipagens básicas para hooks; **tipagem estrita das chaves requer ferramentas/configurações adicionais**.
86
- - **Intlayer**: **Gera tipos estritos** a partir do seu conteúdo. A **autocompletação no IDE** e os **erros em tempo de compilação** capturam erros de digitação e chaves ausentes antes do deploy.
148
+ `next-intl` e `next-i18next` usam uma abordagem centralizada para gerenciar traduções, permitindo dividir o JSON por localidade e por sub-arquivos. No `next-i18next`, chamamos os arquivos JSON de 'namespaces'; o `next-intl` permite declarar mensagens. No `intlayer`, chamamos os arquivos JSON de 'dicionários'.
87
149
 
88
- **Por que isso importa:** A tipagem forte desloca falhas para a **esquerda** (CI/build) em vez de para a **direita** (tempo de execução).
150
+ - No caso do `next-intl`, assim como no `next-i18next`, o conteúdo é carregado no nível da página/layout, e então esse conteúdo é carregado em um provedor de contexto. Isso significa que o desenvolvedor deve gerenciar manualmente os arquivos JSON que serão carregados para cada página.
151
+
152
+ > Na prática, isso implica que os desenvolvedores frequentemente pulam essa otimização, preferindo carregar todo o conteúdo no provedor de contexto da página para simplificar.
153
+
154
+ - No caso do `intlayer`, todo o conteúdo é carregado na aplicação. Depois, um plugin (`@intlayer/babel` / `@intlayer/swc`) cuida de otimizar o bundle carregando apenas o conteúdo usado na página. Portanto, o desenvolvedor não precisa gerenciar manualmente os dicionários que serão carregados. Isso permite uma melhor otimização, melhor manutenção e reduz o tempo de desenvolvimento.
155
+
156
+ À medida que a aplicação cresce (especialmente quando vários desenvolvedores trabalham na aplicação), é comum esquecer de remover conteúdo que não é mais utilizado dos arquivos JSON.
157
+
158
+ > Note que todos os JSON são carregados em todos os casos (next-intl, next-i18next, intlayer).
159
+
160
+ É por isso que a abordagem do Intlayer é mais performática: se um componente não é mais utilizado, seu dicionário não é carregado no bundle.
161
+
162
+ Como a biblioteca lida com os fallbacks também é importante. Vamos considerar que a aplicação está em inglês por padrão, e o usuário visita a página `/fr/about`. Se as traduções estiverem faltando em francês, consideraremos o fallback para o inglês.
163
+
164
+ No caso do `next-intl` e do `next-i18next`, a biblioteca exige o carregamento do JSON relacionado ao idioma atual, mas também ao idioma de fallback. Assim, considerando que todo o conteúdo foi traduzido, cada página carregará 100% de conteúdo desnecessário. **Em comparação, o `intlayer` processa o fallback no momento da construção do dicionário. Portanto, cada página carregará apenas o conteúdo utilizado.**
165
+
166
+ Aqui está um exemplo do impacto da otimização do tamanho do bundle usando `intlayer` em uma aplicação vite + react:
167
+
168
+ | Bundle otimizado | Bundle não otimizado |
169
+ | -------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
170
+ | ![pacote otimizado](https://github.com/aymericzip/intlayer/blob/main/docs/assets/bundle.png) | ![pacote não otimizado](https://github.com/aymericzip/intlayer/blob/main/docs/assets/bundle_no_optimization.png) |
89
171
 
90
172
  ---
91
173
 
92
- ### 3) Tratamento de traduções ausentes
174
+ ## TypeScript e segurança
175
+
176
+ <Columns>
177
+ <Column>
178
+
179
+ **next-intl**
180
+
181
+ - Suporte sólido ao TypeScript, mas **as chaves não são estritamente tipadas por padrão**; você precisará manter os padrões de segurança manualmente.
182
+
183
+ </Column>
184
+ <Column>
185
+
186
+ **next-i18next**
187
+
188
+ - Tipagens básicas para hooks; **tipagem estrita das chaves requer ferramentas/configurações extras**.
189
+
190
+ </Column>
191
+ <Column>
192
+
193
+ **intlayer**
93
194
 
94
- - **next-intl / next-i18next**: Dependem de **fallbacks em tempo de execução** (ex.: mostrar a chave ou o idioma padrão). A build não falha.
95
- - **Intlayer**: **Detecção em tempo de build** com **avisos/erros** para locais ou chaves ausentes.
195
+ - **Gera tipos estritos** a partir do seu conteúdo. **Autocompletação na IDE** e **erros em tempo de compilação** detectam erros de digitação e chaves ausentes antes do deploy.
96
196
 
97
- **Por que isso importa:** Detectar lacunas durante a build evita “strings misteriosas” em produção e está alinhado com gates rigorosos de lançamento.
197
+ </Column>
198
+ </Columns>
199
+
200
+ **Por que isso importa:** Tipagem forte desloca falhas para a **esquerda** (CI/build) em vez da **direita** (tempo de execução).
98
201
 
99
202
  ---
100
203
 
101
- ### 4) Roteamento, middleware e estratégia de URL
204
+ ## Tratamento de traduções ausentes
205
+
206
+ **next-intl**
207
+
208
+ - Depende de **fallbacks em tempo de execução** (ex.: mostrar a chave ou o locale padrão). A build não falha.
209
+
210
+ **next-i18next**
211
+
212
+ - Depende de **fallbacks em tempo de execução** (ex.: mostrar a chave ou o locale padrão). A build não falha.
102
213
 
103
- - Todos os três funcionam com **roteamento localizado do Next.js** no App Router.
104
- - **Intlayer** vai além com **middleware i18n** (detecção de localidade via headers/cookies) e **helpers** para gerar URLs localizadas e tags `<link rel="alternate" hreflang="…">`.
214
+ **intlayer**
105
215
 
106
- **Por que isso importa:** Menos camadas personalizadas; **UX consistente** e **SEO limpo** entre as localidades.
216
+ - **Detecção em tempo de build** com **avisos/erros** para locais ou chaves ausentes.
217
+
218
+ **Por que isso importa:** Detectar lacunas durante a build evita “strings misteriosas” em produção e está alinhado com gates rigorosos de release.
107
219
 
108
220
  ---
109
221
 
110
- ### 5) Alinhamento com Server Components (RSC)
222
+ ## Roteamento, middleware e estratégia de URL
111
223
 
112
- - **Todos** suportam Next.js 13+.
113
- - **Intlayer** suaviza a **fronteira servidor/cliente** com uma API consistente e providers projetados para RSC, para que você não precise passar formatadores ou funções t através das árvores de componentes.
224
+ <Columns>
225
+ <Column>
114
226
 
115
- **Por que isso importa:** Modelo mental mais limpo e menos casos extremos em árvores híbridas.
227
+ **next-intl**
228
+
229
+ - Funciona com **roteamento localizado do Next.js** no App Router.
230
+
231
+ </Column>
232
+ <Column>
233
+
234
+ **next-i18next**
235
+
236
+ - Funciona com **roteamento localizado do Next.js** no App Router.
237
+
238
+ </Column>
239
+ <Column>
240
+
241
+ **intlayer**
242
+
243
+ - Tudo o que foi mencionado acima, além de **middleware i18n** (detecção de local via headers/cookies) e **helpers** para gerar URLs localizadas e tags `<link rel="alternate" hreflang="…">`.
244
+
245
+ </Column>
246
+ </Columns>
247
+
248
+ **Por que isso importa:** Menos camadas de integração personalizadas; **UX consistente** e **SEO limpo** entre os locais.
116
249
 
117
250
  ---
118
251
 
119
- ### 6) Performance e comportamento de carregamento
252
+ ## Alinhamento com Server Components (RSC)
253
+
254
+ <Columns>
255
+ <Column>
120
256
 
121
- - **next-intl / next-i18next**: Controle parcial via **namespaces** e **divisões por rota**; risco de incluir strings não usadas se a disciplina falhar.
122
- - **Intlayer**: **Elimina código morto** na build e **carrega de forma preguiçosa por dicionário/locale**. Conteúdo não usado não é enviado.
257
+ **next-intl**
123
258
 
124
- **Por que isso importa:** Pacotes menores e inicialização mais rápida, especialmente em sites multilíngues.
259
+ - Suporta Next.js 13+. Frequentemente requer passar funções t/formatadores através das árvores de componentes em configurações híbridas.
260
+
261
+ </Column>
262
+ <Column>
263
+
264
+ **next-i18next**
265
+
266
+ - Suporta Next.js 13+. Restrições semelhantes ao passar utilitários de tradução através de fronteiras.
267
+
268
+ </Column>
269
+ <Column>
270
+
271
+ **intlayer**
272
+
273
+ - Suporta Next.js 13+ e suaviza a **fronteira servidor/cliente** com uma API consistente e provedores orientados para RSC, evitando o transporte de formatadores ou funções t.
274
+
275
+ </Column>
276
+ </Columns>
277
+
278
+ **Por que isso importa:** Modelo mental mais limpo e menos casos extremos em árvores híbridas.
125
279
 
126
280
  ---
127
281
 
128
- ### 7) DX, ferramentas e manutenção
282
+ ## DX, ferramentas e manutenção
283
+
284
+ <Columns>
285
+ <Column>
286
+
287
+ **next-intl**
288
+
289
+ - Comumente emparelhado com plataformas externas de localização e fluxos editoriais.
290
+
291
+ </Column>
292
+ <Column>
293
+
294
+ **next-i18next**
295
+
296
+ - Comumente emparelhado com plataformas externas de localização e fluxos editoriais.
297
+
298
+ </Column>
299
+ <Column>
300
+
301
+ **intlayer**
302
+
303
+ - Fornece um **Editor Visual gratuito** e um **CMS opcional** (compatível com Git ou externalizado), além de uma **extensão para VSCode** e **traduções assistidas por IA** usando suas próprias chaves de provedor.
304
+
305
+ </Column>
306
+ </Columns>
307
+
308
+ **Por que isso importa:** Reduz o custo operacional e encurta o ciclo entre desenvolvedores e autores de conteúdo.
309
+
310
+ ## Integração com plataformas de localização (TMS)
311
+
312
+ Grandes organizações frequentemente dependem de Sistemas de Gerenciamento de Tradução (TMS) como **Crowdin**, **Phrase**, **Lokalise**, **Localizely** ou **Localazy**.
313
+
314
+ - **Por que as empresas se importam**
315
+ - **Colaboração e papéis**: Vários atores estão envolvidos: desenvolvedores, gerentes de produto, tradutores, revisores, equipes de marketing.
316
+ - **Escala e eficiência**: localização contínua, revisão em contexto.
317
+
318
+ - **next-intl / next-i18next**
319
+ - Normalmente usam **catálogos JSON centralizados**, portanto a exportação/importação com TMS é direta.
320
+ - Ecossistemas maduros e exemplos/integrações para as plataformas mencionadas.
321
+
322
+ - **Intlayer**
323
+ - Incentiva **dicionários descentralizados por componente** e suporta conteúdo em **TypeScript/TSX/JS/JSON/MD**.
324
+ - Isso melhora a modularidade no código, mas pode dificultar a integração plug-and-play com TMS quando a ferramenta espera arquivos JSON centralizados e planos.
325
+ - O Intlayer oferece alternativas: **traduções assistidas por IA** (usando suas próprias chaves de provedor), um **Editor Visual/CMS** e fluxos de trabalho **CLI/CI** para detectar e preencher lacunas.
326
+
327
+ > Nota: `next-intl` e `i18next` também aceitam catálogos em TypeScript. Se sua equipe armazena mensagens em arquivos `.ts` ou as descentraliza por funcionalidade, você pode enfrentar atritos semelhantes com TMS. No entanto, muitas configurações do `next-intl` permanecem centralizadas em uma pasta `locales/`, o que facilita um pouco a refatoração para JSON para TMS.
328
+
329
+ ## Experiência do Desenvolvedor
330
+
331
+ Esta parte faz uma comparação profunda entre as três soluções. Em vez de considerar casos simples, como descrito na documentação de 'primeiros passos' para cada solução, consideraremos um caso de uso real, mais semelhante a um projeto real.
332
+
333
+ ### Estrutura do aplicativo
334
+
335
+ A estrutura do aplicativo é importante para garantir uma boa manutenção da sua base de código.
336
+
337
+ <Tab defaultTab="next-intl" group='techno'>
338
+
339
+ <TabItem label="next-i18next" value="next-i18next">
340
+
341
+ ```bash
342
+ .
343
+ ├── public
344
+ │ └── locales
345
+ │ ├── en
346
+ │ │ ├── home.json
347
+ │ │ └── navbar.json
348
+ │ ├── fr
349
+ │ │ ├── home.json
350
+ │ │ └── navbar.json
351
+ │ └── es
352
+ │ ├── home.json
353
+ │ └── navbar.json
354
+ ├── next-i18next.config.js
355
+ └── src
356
+ ├── middleware.ts
357
+ ├── app
358
+ │ └── home.tsx
359
+ └── components
360
+ └── Navbar
361
+ └── index.tsx
362
+ ```
363
+
364
+ </TabItem>
365
+ <TabItem label="next-intl" value="next-intl">
366
+
367
+ ```bash
368
+ .
369
+ ├── locales
370
+ │ ├── en
371
+ │ │ ├── home.json
372
+ │ │ └── navbar.json
373
+ │ ├── fr
374
+ │ │ ├── home.json
375
+ │ │ └── navbar.json
376
+ │ └── es
377
+ │ ├── home.json
378
+ │ └── navbar.json
379
+ ├── i18n.ts
380
+ └── src
381
+ ├── middleware.ts
382
+ ├── app
383
+ │ └── home.tsx
384
+ └── components
385
+ └── Navbar
386
+ └── index.tsx
387
+ ```
388
+
389
+ </TabItem>
390
+ <TabItem label="intlayer" value="intlayer">
391
+
392
+ ```bash
393
+ .
394
+ ├── intlayer.config.ts
395
+ └── src
396
+ ├── middleware.ts
397
+ ├── app
398
+ │ └── home
399
+ │ └── index.tsx
400
+ │ └── index.content.ts
401
+ └── components
402
+ └── Navbar
403
+ ├── index.tsx
404
+ └── index.content.ts
405
+ ```
406
+
407
+ </TabItem>
408
+ </Tab>
409
+
410
+ #### Comparação
411
+
412
+ - **next-intl / next-i18next**: Catálogos centralizados (JSON; namespaces/mensagens). Estrutura clara, integra-se bem com plataformas de tradução, mas pode levar a mais edições entre arquivos conforme as aplicações crescem.
413
+ - **Intlayer**: Dicionários `.content.{ts|js|json}` por componente, localizados junto aos componentes. Facilita o reuso de componentes e o raciocínio local; adiciona arquivos e depende de ferramentas em tempo de build.
414
+
415
+ #### Configuração e Carregamento de Conteúdo
416
+
417
+ Como mencionado anteriormente, você deve otimizar a forma como cada arquivo JSON é importado no seu código.
418
+ A forma como a biblioteca lida com o carregamento de conteúdo é importante.
419
+
420
+ <Tab defaultTab="next-intl" group='techno'>
421
+ <TabItem label="next-i18next" value="next-i18next">
422
+
423
+ ```tsx fileName="next-i18next.config.js"
424
+ module.exports = {
425
+ i18n: {
426
+ locales: ["en", "fr", "es"],
427
+ defaultLocale: "en",
428
+ },
429
+ };
430
+ ```
431
+
432
+ ```tsx fileName="src/app/_app.tsx"
433
+ import { appWithTranslation } from "next-i18next";
434
+
435
+ const MyApp = ({ Component, pageProps }) => <Component {...pageProps} />;
436
+
437
+ export default appWithTranslation(MyApp);
438
+ ```
439
+
440
+ ```tsx fileName="src/app/[locale]/about/page.tsx"
441
+ import type { GetStaticProps } from "next";
442
+ import { serverSideTranslations } from "next-i18next/serverSideTranslations";
443
+ import { useTranslation } from "next-i18next";
444
+ import { I18nextProvider, initReactI18next } from "react-i18next";
445
+ import { createInstance } from "i18next";
446
+ import { ClientComponent, ServerComponent } from "@components";
447
+
448
+ export default function HomePage({ locale }: { locale: string }) {
449
+ // Declare explicitamente o namespace usado por este componente
450
+ const resources = await loadMessagesFor(locale); // seu carregador (JSON, etc.)
451
+
452
+ const i18n = createInstance();
453
+ i18n.use(initReactI18next).init({
454
+ lng: locale,
455
+ fallbackLng: "en",
456
+ resources,
457
+ ns: ["common", "about"],
458
+ defaultNS: "common",
459
+ interpolation: { escapeValue: false },
460
+ });
461
+
462
+ const { t } = useTranslation("about");
463
+
464
+ return (
465
+ <I18nextProvider i18n={i18n}>
466
+ <main>
467
+ <h1>{t("title")}</h1>
468
+ <ClientComponent />
469
+ <ServerComponent />
470
+ </main>
471
+ </I18nextProvider>
472
+ );
473
+ }
474
+
475
+ export const getStaticProps: GetStaticProps = async ({ locale }) => {
476
+ // Pré-carregue apenas os namespaces necessários para ESTA página
477
+ return {
478
+ props: {
479
+ ...(await serverSideTranslations(locale ?? "en", ["common", "about"])),
480
+ },
481
+ };
482
+ };
483
+ ```
484
+
485
+ </TabItem>
486
+ <TabItem label="next-intl" value="next-intl">
487
+
488
+ ```tsx fileName="i18n.ts"
489
+ import { getRequestConfig } from "next-intl/server";
490
+ import { notFound } from "next/navigation";
491
+
492
+ // Pode ser importado de uma configuração compartilhada
493
+ const locales = ["en", "fr", "es"];
494
+
495
+ export default getRequestConfig(async ({ locale }) => {
496
+ // Valida se o parâmetro `locale` recebido é válido
497
+ if (!locales.includes(locale as any)) notFound();
498
+
499
+ return {
500
+ messages: (await import(`../messages/${locale}.json`)).default,
501
+ };
502
+ });
503
+ ```
504
+
505
+ ```tsx fileName="src/app/[locale]/about/layout.tsx"
506
+ import { NextIntlClientProvider } from "next-intl";
507
+ import { getMessages, unstable_setRequestLocale } from "next-intl/server";
508
+ import pick from "lodash/pick";
509
+
510
+ export default async function LocaleLayout({
511
+ children,
512
+ params,
513
+ }: {
514
+ children: React.ReactNode;
515
+ params: { locale: string };
516
+ }) {
517
+ const { locale } = params;
518
+
519
+ // Define o locale ativo da requisição para esta renderização no servidor (RSC)
520
+ unstable_setRequestLocale(locale);
521
+
522
+ // As mensagens são carregadas no servidor via src/i18n/request.ts
523
+ // (veja a documentação do next-intl). Aqui, enviamos apenas um subconjunto para o cliente
524
+ // que é necessário para os componentes cliente (otimização do payload).
525
+ const messages = await getMessages();
526
+ const clientMessages = pick(messages, ["common", "about"]);
527
+
528
+ return (
529
+ <html lang={locale}>
530
+ <body>
531
+ <NextIntlClientProvider locale={locale} messages={clientMessages}>
532
+ {children}
533
+ </NextIntlClientProvider>
534
+ </body>
535
+ </html>
536
+ );
537
+ }
538
+ ```
539
+
540
+ ```tsx fileName="src/app/[locale]/about/page.tsx"
541
+ import { getTranslations } from "next-intl/server";
542
+ import { ClientComponent, ServerComponent } from "@components";
543
+
544
+ export default async function LandingPage({
545
+ params,
546
+ }: {
547
+ params: { locale: string };
548
+ }) {
549
+ // Carregamento estritamente do lado do servidor (não hidratado no cliente)
550
+ const t = await getTranslations("about");
551
+
552
+ return (
553
+ <main>
554
+ <h1>{t("title")}</h1>
555
+ <ClientComponent />
556
+ <ServerComponent />
557
+ </main>
558
+ );
559
+ }
560
+ ```
561
+
562
+ </TabItem>
563
+ <TabItem label="intlayer" value="intlayer">
564
+
565
+ ```tsx fileName="intlayer.config.ts"
566
+ export default {
567
+ internationalization: {
568
+ locales: ["en", "fr", "es"],
569
+ defaultLocale: "en",
570
+ },
571
+ };
572
+ ```
573
+
574
+ ```tsx fileName="src/app/[locale]/layout.tsx"
575
+ import { getHTMLTextDir } from "intlayer";
576
+ import {
577
+ IntlayerClientProvider,
578
+ generateStaticParams,
579
+ type NextLayoutIntlayer,
580
+ } from "next-intlayer";
129
581
 
130
- - **next-intl / next-i18next**: Normalmente você integra plataformas externas para traduções e fluxos editoriais.
131
- - **Intlayer**: Inclui um **Editor Visual gratuito** e **CMS opcional** (compatível com Git ou externalizado). Além disso, **extensão para VSCode** para autoria de conteúdo e **traduções assistidas por IA** usando suas próprias chaves de provedores.
582
+ export const dynamic = "force-static";
132
583
 
133
- **Por que isso importa:** Reduz os custos operacionais e encurta o ciclo entre desenvolvedores e autores de conteúdo.
584
+ const LandingLayout: NextLayoutIntlayer = async ({ children, params }) => {
585
+ const { locale } = await params;
586
+
587
+ return (
588
+ <html lang={locale} dir={getHTMLTextDir(locale)}>
589
+ <IntlayerClientProvider locale={locale}>
590
+ {children}
591
+ </IntlayerClientProvider>
592
+ </html>
593
+ );
594
+ };
595
+
596
+ export default LandingLayout;
597
+ ```
598
+
599
+ ```tsx fileName="src/app/[locale]/about/page.tsx"
600
+ import { PageContent } from "@components/PageContent";
601
+ import type { NextPageIntlayer } from "next-intlayer";
602
+ import { IntlayerServerProvider, useIntlayer } from "next-intlayer/server";
603
+ import { ClientComponent, ServerComponent } from "@components";
604
+
605
+ const LandingPage: NextPageIntlayer = async ({ params }) => {
606
+ const { locale } = await params;
607
+ const { title } = useIntlayer("about", locale);
608
+
609
+ return (
610
+ <IntlayerServerProvider locale={locale}>
611
+ <main>
612
+ <h1>{title}</h1>
613
+ <ClientComponent />
614
+ <ServerComponent />
615
+ </main>
616
+ </IntlayerServerProvider>
617
+ );
618
+ };
619
+
620
+ export default LandingPage;
621
+ ```
622
+
623
+ </TabItem>
624
+ </Tab>
625
+
626
+ #### Comparação
627
+
628
+ Todos os três suportam carregamento de conteúdo e provedores por localidade.
629
+
630
+ - Com **next-intl/next-i18next**, normalmente você carrega mensagens/namespaces selecionados por rota e coloca os providers onde necessário.
631
+
632
+ - Com **Intlayer**, adiciona uma análise em tempo de build para inferir o uso, o que pode reduzir a configuração manual e permitir um único provider raiz.
633
+
634
+ Escolha entre controle explícito e automação com base na preferência da equipe.
635
+
636
+ ### Uso em um componente cliente
637
+
638
+ Vamos pegar um exemplo de um componente cliente que renderiza um contador.
639
+
640
+ <Tab defaultTab="next-intl" group='techno'>
641
+ <TabItem label="next-i18next" value="next-i18next">
642
+
643
+ **Traduções (devem ser JSON reais em `public/locales/...`)**
644
+
645
+ ```json fileName="public/locales/en/about.json"
646
+ {
647
+ "counter": {
648
+ "label": "Counter",
649
+ "increment": "Increment"
650
+ }
651
+ }
652
+ ```
653
+
654
+ ```json fileName="public/locales/fr/about.json"
655
+ {
656
+ "counter": {
657
+ "label": "Contador",
658
+ "increment": "Incrementar"
659
+ }
660
+ }
661
+ ```
662
+
663
+ **Componente cliente**
664
+
665
+ ```tsx fileName="src/components/ClientComponentExample.tsx"
666
+ "use client";
667
+
668
+ import React, { useMemo, useState } from "react";
669
+ import { useTranslation } from "next-i18next";
670
+
671
+ const ClientComponentExample = () => {
672
+ const { t, i18n } = useTranslation("about");
673
+ const [count, setCount] = useState(0);
674
+
675
+ // next-i18next não expõe useNumber; use Intl.NumberFormat
676
+ const numberFormat = new Intl.NumberFormat(i18n.language);
677
+
678
+ return (
679
+ <div>
680
+ <p>{numberFormat.format(count)}</p>
681
+ <button
682
+ aria-label={t("counter.label")}
683
+ onClick={() => setCount((count) => count + 1)}
684
+ >
685
+ {t("counter.increment")}
686
+ </button>
687
+ </div>
688
+ );
689
+ };
690
+ ```
691
+
692
+ > Não se esqueça de adicionar o namespace "about" na função serverSideTranslations da página
693
+ > Aqui usamos a versão do React 19.x.x, mas para versões inferiores, será necessário usar useMemo para armazenar a instância do formatador, pois é uma função pesada
694
+
695
+ </TabItem>
696
+ <TabItem label="next-intl" value="next-intl">
697
+
698
+ **Traduções (mesma estrutura; carregue-as nas mensagens do next-intl conforme preferir)**
699
+
700
+ ```json fileName="locales/en/about.json"
701
+ {
702
+ "counter": {
703
+ "label": "Counter",
704
+ "increment": "Increment"
705
+ }
706
+ }
707
+ ```
708
+
709
+ ```json fileName="locales/fr/about.json"
710
+ {
711
+ "counter": {
712
+ "label": "Compteur",
713
+ "increment": "Incrémenter"
714
+ }
715
+ }
716
+ ```
717
+
718
+ **Componente cliente**
719
+
720
+ ```tsx fileName="src/components/ClientComponentExample.tsx"
721
+ "use client";
722
+
723
+ import React, { useState } from "react";
724
+ import { useTranslations, useFormatter } from "next-intl";
725
+
726
+ const ClientComponentExample = () => {
727
+ // Escopo diretamente para o objeto aninhado
728
+ const t = useTranslations("about.counter");
729
+ const format = useFormatter();
730
+ const [count, setCount] = useState(0);
731
+
732
+ return (
733
+ <div>
734
+ <p>{format.number(count)}</p>
735
+ <button
736
+ aria-label={t("label")}
737
+ onClick={() => setCount((count) => count + 1)}
738
+ >
739
+ {t("increment")}
740
+ </button>
741
+ </div>
742
+ );
743
+ };
744
+ ```
745
+
746
+ > Não esqueça de adicionar a mensagem "about" na mensagem do cliente da página
747
+
748
+ </TabItem>
749
+ <TabItem label="intlayer" value="intlayer">
750
+
751
+ **Conteúdo**
752
+
753
+ ```ts fileName="src/components/ClientComponentExample/index.content.ts"
754
+ import { t, type Dictionary } from "intlayer";
755
+
756
+ const counterContent = {
757
+ key: "counter",
758
+ content: {
759
+ label: t({ pt: "Contador", en: "Counter", fr: "Compteur" }),
760
+ increment: t({ pt: "Incrementar", en: "Increment", fr: "Incrémenter" }),
761
+ },
762
+ } satisfies Dictionary;
763
+
764
+ export default counterContent;
765
+ ```
766
+
767
+ **Componente cliente**
768
+
769
+ ```tsx fileName="src/components/ClientComponentExample/index.tsx"
770
+ "use client";
771
+
772
+ import React, { useState } from "react";
773
+ import { useNumber, useIntlayer } from "next-intlayer";
774
+
775
+ const ClientComponentExample = () => {
776
+ const [count, setCount] = useState(0);
777
+ const { label, increment } = useIntlayer("counter"); // retorna strings
778
+ const { number } = useNumber();
779
+
780
+ return (
781
+ <div>
782
+ <p>{number(count)}</p>
783
+ <button aria-label={label} onClick={() => setCount((count) => count + 1)}>
784
+ {increment}
785
+ </button>
786
+ </div>
787
+ );
788
+ };
789
+ ```
790
+
791
+ </TabItem>
792
+ </Tab>
793
+
794
+ #### Comparação
795
+
796
+ - **Formatação de números**
797
+ - **next-i18next**: não possui `useNumber`; usa `Intl.NumberFormat` (ou i18next-icu).
798
+ - **next-intl**: `useFormatter().number(value)`.
799
+ - **Intlayer**: `useNumber()` embutido.
800
+
801
+ - **Chaves**
802
+ - Mantenha uma estrutura aninhada (`about.counter.label`) e escopo seu hook de acordo (`useTranslation("about")` + `t("counter.label")` ou `useTranslations("about.counter")` + `t("label")`).
803
+
804
+ - **Localização dos arquivos**
805
+ - **next-i18next** espera JSON em `public/locales/{lng}/{ns}.json`.
806
+ - **next-intl** é flexível; carrega mensagens conforme sua configuração.
807
+ - **Intlayer** armazena conteúdo em dicionários TS/JS e resolve por chave.
134
808
 
135
809
  ---
136
810
 
137
- ## Quando escolher qual?
811
+ ### Uso em um componente servidor
812
+
813
+ Vamos considerar o caso de um componente de interface do usuário (UI). Este componente é um componente de servidor e deve ser capaz de ser inserido como filho de um componente cliente. (página (componente de servidor) -> componente cliente -> componente de servidor). Como este componente pode ser inserido como filho de um componente cliente, ele não pode ser assíncrono.
814
+
815
+ <Tab defaultTab="next-intl" group='techno'>
816
+ <TabItem label="next-i18next" value="next-i18next">
817
+
818
+ ```tsx fileName="src/pages/about.tsx"
819
+ import type { GetStaticProps } from "next";
820
+ import { useTranslation } from "next-i18next";
821
+
822
+ type ServerComponentProps = {
823
+ count: number;
824
+ };
825
+
826
+ const ServerComponent = ({ count }: ServerComponentProps) => {
827
+ const { t, i18n } = useTranslation("about");
828
+ const formatted = new Intl.NumberFormat(i18n.language).format(count);
829
+
830
+ return (
831
+ <div>
832
+ <p>{formatted}</p>
833
+ <button aria-label={t("counter.label")}>{t("counter.increment")}</button>
834
+ </div>
835
+ );
836
+ };
837
+ ```
838
+
839
+ </TabItem>
840
+ <TabItem label="next-intl" value="next-intl">
841
+
842
+ ```tsx fileName="src/components/ServerComponent.tsx"
843
+ type ServerComponentProps = {
844
+ count: number;
845
+ t: (key: string) => string;
846
+ };
847
+
848
+ const ServerComponent = ({ t, count }: ServerComponentProps) => {
849
+ const formatted = new Intl.NumberFormat(i18n.language).format(count);
850
+
851
+ return (
852
+ <div>
853
+ <p>{formatted}</p>
854
+ <button aria-label={t("label")}>{t("increment")}</button>
855
+ </div>
856
+ );
857
+ };
858
+ ```
859
+
860
+ > Como o componente do servidor não pode ser assíncrono, você precisa passar as traduções e a função formatadora como props.
861
+ >
862
+ > - `const t = await getTranslations("about.counter");`
863
+ > - `const format = await getFormatter();`
864
+
865
+ </TabItem>
866
+ <TabItem label="intlayer" value="intlayer">
867
+
868
+ ```tsx fileName="src/components/ServerComponent.tsx"
869
+ import { useIntlayer, useNumber } from "next-intlayer/server";
870
+
871
+ const ServerComponent = ({ count }: { count: number }) => {
872
+ const { label, increment } = useIntlayer("counter");
873
+ const { number } = useNumber();
874
+
875
+ return (
876
+ <div>
877
+ <p>{number(count)}</p>
878
+ <button aria-label={label}>{increment}</button>
879
+ </div>
880
+ );
881
+ };
882
+ ```
883
+
884
+ </TabItem>
885
+ </Tab>
886
+
887
+ > O Intlayer expõe hooks **seguros para o servidor** via `next-intlayer/server`. Para funcionar, `useIntlayer` e `useNumber` usam uma sintaxe semelhante a hooks, parecida com os hooks do cliente, mas dependem, nos bastidores, do contexto do servidor (`IntlayerServerProvider`).
888
+
889
+ ### Metadados / Sitemap / Robots
890
+
891
+ Traduzir conteúdo é ótimo. Mas as pessoas geralmente esquecem que o principal objetivo da internacionalização é tornar seu site mais visível para o mundo. I18n é uma alavanca incrível para melhorar a visibilidade do seu site.
138
892
 
139
- - **Escolha next-intl** se você quer uma solução **minimalista**, está confortável com catálogos centralizados e seu aplicativo é de **pequeno a médio porte**.
140
- - **Escolha next-i18next** se você precisa do **ecossistema de plugins do i18next** (por exemplo, regras avançadas ICU via plugins) e sua equipe já conhece o i18next, aceitando **mais configuração** para maior flexibilidade.
141
- - **Escolha Intlayer** se você valoriza **conteúdo com escopo por componente**, **TypeScript rigoroso**, **garantias em tempo de build**, **tree-shaking** e ferramentas completas de roteamento/SEO/edição - especialmente para **Next.js App Router** e **bases de código grandes e modulares**.
893
+ Aqui está uma lista de boas práticas relacionadas ao SEO multilíngue.
894
+
895
+ - definir meta tags hreflang na tag `<head>`
896
+ > Isso ajuda os motores de busca a entender quais idiomas estão disponíveis na página
897
+ - liste todas as traduções das páginas no sitemap.xml usando o esquema XML `http://www.w3.org/1999/xhtml`
898
+ >
899
+ - não esqueça de excluir as páginas prefixadas do robots.txt (ex.: `/dashboard`, e `/fr/dashboard`, `/es/dashboard`)
900
+ >
901
+ - use um componente Link personalizado para redirecionar para a página mais localizada (ex.: em francês `<a href="/fr/about">A propos</a>`)
902
+ >
903
+
904
+ Os desenvolvedores frequentemente esquecem de referenciar corretamente suas páginas entre os diferentes locais.
905
+
906
+ <Tab defaultTab="next-intl" group='techno'>
907
+
908
+ <TabItem label="next-i18next" value="next-i18next">
909
+
910
+ ```ts fileName="i18n.config.ts"
911
+ export const locales = ["en", "fr"] as const;
912
+ export type Locale = (typeof locales)[number];
913
+ export const defaultLocale: Locale = "en";
914
+
915
+ export function localizedPath(locale: string, path: string) {
916
+ return locale === defaultLocale ? path : "/" + locale + path;
917
+ }
918
+
919
+ const ORIGIN = "https://example.com";
920
+ export function abs(locale: string, path: string) {
921
+ return ORIGIN + localizedPath(locale, path);
922
+ }
923
+ ```
924
+
925
+ ```tsx fileName="src/app/[locale]/about/layout.tsx"
926
+ import type { Metadata } from "next";
927
+ import { locales, defaultLocale, localizedPath } from "@/i18n.config";
928
+
929
+ export async function generateMetadata({
930
+ params,
931
+ }: {
932
+ params: { locale: string };
933
+ }): Promise<Metadata> {
934
+ const { locale } = params;
935
+
936
+ // Importa dinamicamente o arquivo JSON correto
937
+ const messages = (
938
+ await import("@/../public/locales/" + locale + "/about.json")
939
+ ).default;
940
+
941
+ const languages = Object.fromEntries(
942
+ locales.map((locale) => [locale, localizedPath(locale, "/about")])
943
+ );
944
+
945
+ return {
946
+ title: messages.title,
947
+ description: messages.description,
948
+ alternates: {
949
+ canonical: localizedPath(locale, "/about"),
950
+ languages: { ...languages, "x-default": "/about" },
951
+ },
952
+ };
953
+ }
954
+
955
+ export default async function AboutPage() {
956
+ return <h1>Sobre</h1>;
957
+ }
958
+ ```
959
+
960
+ ```ts fileName="src/app/sitemap.ts"
961
+ import type { MetadataRoute } from "next";
962
+ import { locales, defaultLocale, abs } from "@/i18n.config";
963
+
964
+ export default function sitemap(): MetadataRoute.Sitemap {
965
+ const languages = Object.fromEntries(
966
+ locales.map((locale) => [locale, abs(locale, "/about")])
967
+ );
968
+ return [
969
+ {
970
+ url: abs(defaultLocale, "/about"),
971
+ lastModified: new Date(),
972
+ changeFrequency: "monthly",
973
+ priority: 0.7,
974
+ alternates: { languages },
975
+ },
976
+ ];
977
+ }
978
+ ```
979
+
980
+ ```ts fileName="src/app/robots.ts"
981
+ import type { MetadataRoute } from "next";
982
+ import { locales, defaultLocale, localizedPath } from "@/i18n.config";
983
+
984
+ const ORIGIN = "https://example.com";
985
+
986
+ const expandAllLocales = (path: string) => [
987
+ localizedPath(defaultLocale, path),
988
+ ...locales
989
+ .filter((locale) => locale !== defaultLocale)
990
+ .map((locale) => localizedPath(locale, path)),
991
+ ];
992
+
993
+ export default function robots(): MetadataRoute.Robots {
994
+ const disallow = [
995
+ ...expandAllLocales("/dashboard"),
996
+ ...expandAllLocales("/admin"),
997
+ ];
998
+
999
+ return {
1000
+ rules: { userAgent: "*", allow: ["/"], disallow },
1001
+ host: ORIGIN,
1002
+ sitemap: ORIGIN + "/sitemap.xml",
1003
+ };
1004
+ }
1005
+ ```
1006
+
1007
+ </TabItem>
1008
+ <TabItem label="next-intl" value="next-intl">
1009
+
1010
+ ```tsx fileName="src/app/[locale]/about/layout.tsx"
1011
+ import type { Metadata } from "next";
1012
+ import { locales, defaultLocale } from "@/i18n";
1013
+ import { getTranslations } from "next-intl/server";
1014
+
1015
+ function localizedPath(locale: string, path: string) {
1016
+ return locale === defaultLocale ? path : "/" + locale + path;
1017
+ }
1018
+
1019
+ export async function generateMetadata({
1020
+ params,
1021
+ }: {
1022
+ params: { locale: string };
1023
+ }): Promise<Metadata> {
1024
+ const { locale } = params;
1025
+ const t = await getTranslations({ locale, namespace: "about" });
1026
+
1027
+ const url = "/about";
1028
+ const languages = Object.fromEntries(
1029
+ locales.map((locale) => [locale, localizedPath(locale, url)])
1030
+ );
1031
+
1032
+ return {
1033
+ title: t("title"),
1034
+ description: t("description"),
1035
+ alternates: {
1036
+ canonical: localizedPath(locale, url),
1037
+ languages: { ...languages, "x-default": url },
1038
+ },
1039
+ };
1040
+ }
1041
+
1042
+ // ... Resto do código da página
1043
+ ```
1044
+
1045
+ ```tsx fileName="src/app/sitemap.ts"
1046
+ import type { MetadataRoute } from "next";
1047
+ import { locales, defaultLocale } from "@/i18n";
1048
+
1049
+ const origin = "https://example.com";
1050
+
1051
+ const formatterLocalizedPath = (locale: string, path: string) =>
1052
+ locale === defaultLocale ? origin + path : origin + "/" + locale + path;
1053
+
1054
+ export default function sitemap(): MetadataRoute.Sitemap {
1055
+ const aboutLanguages = Object.fromEntries(
1056
+ locales.map((l) => [l, formatterLocalizedPath(l, "/about")])
1057
+ );
1058
+
1059
+ return [
1060
+ {
1061
+ url: formatterLocalizedPath(defaultLocale, "/about"),
1062
+ lastModified: new Date(),
1063
+ changeFrequency: "monthly",
1064
+ priority: 0.7,
1065
+ alternates: { languages: aboutLanguages },
1066
+ },
1067
+ ];
1068
+ }
1069
+ ```
1070
+
1071
+ ```tsx fileName="src/app/robots.ts"
1072
+ import type { MetadataRoute } from "next";
1073
+ import { locales, defaultLocale } from "@/i18n";
1074
+
1075
+ const origin = "https://example.com";
1076
+ const withAllLocales = (path: string) => [
1077
+ path,
1078
+ ...locales
1079
+ .filter((locale) => locale !== defaultLocale)
1080
+ .map((locale) => "/" + locale + path),
1081
+ ];
1082
+
1083
+ export default function robots(): MetadataRoute.Robots {
1084
+ const disallow = [
1085
+ ...withAllLocales("/dashboard"),
1086
+ ...withAllLocales("/admin"),
1087
+ ];
1088
+
1089
+ return {
1090
+ rules: { userAgent: "*", allow: ["/"], disallow },
1091
+ host: origin,
1092
+ sitemap: origin + "/sitemap.xml",
1093
+ };
1094
+ }
1095
+ ```
1096
+
1097
+ </TabItem>
1098
+ <TabItem label="intlayer" value="intlayer">
1099
+
1100
+ ```typescript fileName="src/app/[locale]/about/layout.tsx"
1101
+ import { getIntlayer, getMultilingualUrls } from "intlayer";
1102
+ import type { Metadata } from "next";
1103
+ import type { LocalPromiseParams } from "next-intlayer";
1104
+
1105
+ export const generateMetadata = async ({
1106
+ params,
1107
+ }: LocalPromiseParams): Promise<Metadata> => {
1108
+ const { locale } = await params;
1109
+
1110
+ const metadata = getIntlayer("page-metadata", locale);
1111
+
1112
+ const multilingualUrls = getMultilingualUrls("/about");
1113
+
1114
+ return {
1115
+ ...metadata,
1116
+ alternates: {
1117
+ canonical: multilingualUrls[locale as keyof typeof multilingualUrls],
1118
+ languages: { ...multilingualUrls, "x-default": "/about" },
1119
+ },
1120
+ };
1121
+ };
1122
+
1123
+ // ... Resto do código da página
1124
+ ```
1125
+
1126
+ ```tsx fileName="src/app/sitemap.ts"
1127
+ import { getMultilingualUrls } from "intlayer";
1128
+ import type { MetadataRoute } from "next";
1129
+
1130
+ const sitemap = (): MetadataRoute.Sitemap => [
1131
+ {
1132
+ url: "https://example.com/about",
1133
+ alternates: {
1134
+ languages: { ...getMultilingualUrls("https://example.com/about") },
1135
+ },
1136
+ },
1137
+ ];
1138
+ ```
1139
+
1140
+ ```tsx fileName="src/app/robots.ts"
1141
+ import { getMultilingualUrls } from "intlayer";
1142
+ import type { MetadataRoute } from "next";
1143
+
1144
+ const getAllMultilingualUrls = (urls: string[]) =>
1145
+ urls.flatMap((url) => Object.values(getMultilingualUrls(url)) as string[]);
1146
+
1147
+ // Configuração do arquivo robots.txt para controlar o acesso dos robôs de busca
1148
+ const robots = (): MetadataRoute.Robots => ({
1149
+ rules: {
1150
+ userAgent: "*",
1151
+ allow: ["/"],
1152
+ disallow: getAllMultilingualUrls(["/dashboard"]), // Bloqueia URLs multilíngues do dashboard
1153
+ },
1154
+ host: "https://example.com",
1155
+ sitemap: "https://example.com/sitemap.xml",
1156
+ });
1157
+
1158
+ export default robots;
1159
+ ```
1160
+
1161
+ </TabItem>
1162
+ </Tab>
1163
+
1164
+ > Intlayer fornece uma função `getMultilingualUrls` para gerar URLs multilíngues para seu sitemap.
1165
+
1166
+ ---
142
1167
 
143
1168
  ---
144
1169
 
145
- ## Notas práticas de migração (next-intl / next-i18next → Intlayer)
1170
+ ## E o vencedor é…
1171
+
1172
+ Não é simples. Cada opção tem seus prós e contras. Aqui está como eu vejo:
1173
+
1174
+ <Columns>
1175
+ <Column>
1176
+
1177
+ **next-intl**
1178
+
1179
+ - a mais simples, leve, com menos decisões impostas a você. Se você quer uma solução **mínima**, está confortável com catálogos centralizados, e seu app é **pequeno a médio porte**.
1180
+
1181
+ </Column>
1182
+ <Column>
1183
+
1184
+ **next-i18next**
1185
+
1186
+ - madura, cheia de recursos, muitos plugins da comunidade, mas com custo de configuração mais alto. Se você precisa do **ecossistema de plugins do i18next** (por exemplo, regras avançadas ICU via plugins) e sua equipe já conhece o i18next, aceitando **mais configuração** para flexibilidade.
1187
+
1188
+ </Column>
1189
+ <Column>
1190
+
1191
+ **Intlayer**
1192
+
1193
+ - construído para o Next.js moderno, com conteúdo modular, segurança de tipos, ferramentas e menos código repetitivo. Se você valoriza **conteúdo com escopo de componente**, **TypeScript rigoroso**, **garantias em tempo de build**, **tree-shaking** e ferramentas de roteamento/SEO/editor **com tudo incluído** - especialmente para **Next.js App Router**, sistemas de design e **bases de código grandes e modulares**.
1194
+
1195
+ </Column>
1196
+ </Columns>
1197
+
1198
+ Se você prefere uma configuração mínima e aceita algum trabalho manual, next-intl é uma boa escolha. Se você precisa de todos os recursos e não se importa com a complexidade, next-i18next funciona. Mas se você quer uma solução moderna, escalável e modular com ferramentas integradas, Intlayer pretende oferecer isso pronto para uso.
1199
+
1200
+ > **Alternativa para equipes empresariais**: Se você precisa de uma solução comprovada que funcione perfeitamente com plataformas de localização estabelecidas como **Crowdin**, **Phrase** ou outros sistemas profissionais de gerenciamento de tradução, considere **next-intl** ou **next-i18next** pelo seu ecossistema maduro e integrações comprovadas.
1201
+
1202
+ > **Roteiro futuro**: O Intlayer também planeja desenvolver plugins que funcionem sobre as soluções **i18next** e **next-intl**. Isso lhe dará as vantagens do Intlayer para automação, sintaxe e gerenciamento de conteúdo, mantendo a segurança e estabilidade fornecidas por essas soluções consolidadas no código da sua aplicação.
1203
+
1204
+ ## Estrelas no GitHub (GitHub STARs)
1205
+
1206
+ As estrelas no GitHub são um forte indicador da popularidade de um projeto, da confiança da comunidade e da relevância a longo prazo. Embora não sejam uma medida direta da qualidade técnica, refletem quantos desenvolvedores consideram o projeto útil, acompanham seu progresso e provavelmente o adotam. Para estimar o valor de um projeto, as estrelas ajudam a comparar a tração entre alternativas e fornecem insights sobre o crescimento do ecossistema.
146
1207
 
147
- - **Comece por funcionalidade**: Mova uma rota ou componente de cada vez para **dicionários locais**.
148
- - **Mantenha os catálogos antigos em paralelo**: Faça a ponte durante a migração; evite um grande corte abrupto.
149
- - **Ative verificações rigorosas**: Permita que a detecção em tempo de build identifique lacunas cedo.
150
- - **Adote middleware e helpers**: Padronize a detecção de localidade e as tags de SEO em todo o site.
151
- - **Meça os bundles**: Espere **reduções no tamanho do bundle** à medida que o conteúdo não utilizado é descartado.
1208
+ [![Gráfico do Histórico de Estrelas](https://api.star-history.com/svg?repos=i18next/next-i18next&repos=amannn/next-intl&repos=aymericzip/intlayer&type=Date)](https://www.star-history.com/#i18next/next-i18next&amannn/next-intl&aymericzip/intlayer)
152
1209
 
153
1210
  ---
154
1211
 
155
1212
  ## Conclusão
156
1213
 
157
- As três bibliotecas têm sucesso na localização básica. A diferença está em **quanto trabalho você precisa fazer** para alcançar uma configuração robusta e escalável no **Next.js moderno**:
1214
+ Todas as três bibliotecas têm sucesso na localização principal. A diferença é **quanto trabalho você deve fazer** para alcançar uma configuração robusta e escalável no **Next.js moderno**:
158
1215
 
159
- - Com **Intlayer**, **conteúdo modular**, **TS rigoroso**, **segurança em tempo de build**, **bundles otimizados por tree-shaking** e **ferramentas de App Router + SEO de primeira classe** são **padrões**, não tarefas.
160
- - Se sua equipe valoriza **manutenibilidade e velocidade** em um app multi-idioma orientado a componentes, o Intlayer oferece a experiência **mais completa** atualmente.
1216
+ - Com o **Intlayer**, **conteúdo modular**, **TS rigoroso**, **segurança em tempo de compilação**, **pacotes otimizados por tree-shaking** e **App Router de primeira classe + ferramentas de SEO** são **padrões**, não tarefas.
1217
+ - Se sua equipe valoriza **manutenibilidade e velocidade** em um aplicativo multi-idioma orientado a componentes, o Intlayer oferece a experiência **mais completa** atualmente.
161
1218
 
162
1219
  Consulte o documento ['Por que Intlayer?'](https://intlayer.org/doc/why) para mais detalhes.