@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
@@ -0,0 +1,362 @@
1
+ ---
2
+ createdAt: 2025-09-28
3
+ updatedAt: 2025-09-28
4
+ title: SEO e i18n no Next.js
5
+ description: Aprenda como configurar SEO multilíngue no seu app Next.js usando next-intl, next-i18next e Intlayer.
6
+ keywords:
7
+ - Intlayer
8
+ - SEO
9
+ - Internacionalização
10
+ - Next.js
11
+ - i18n
12
+ - JavaScript
13
+ - React
14
+ - next-intl
15
+ - next-i18next
16
+ slugs:
17
+ - blog
18
+ - blog-seo-i18n-nextjs
19
+ ---
20
+
21
+ # SEO e i18n no Next.js: Traduzir não é suficiente
22
+
23
+ Quando os desenvolvedores pensam em internacionalização (i18n), o primeiro reflexo é frequentemente: _traduzir o conteúdo_. Mas as pessoas geralmente esquecem que o principal objetivo da internacionalização é tornar seu site mais visível para o mundo.
24
+ Se o seu app Next.js multilíngue não informar aos motores de busca como rastrear e entender suas diferentes versões de idioma, a maior parte do seu esforço pode passar despercebida.
25
+
26
+ Neste blog, vamos explorar **por que a i18n é uma superpotência do SEO** e como implementá-la corretamente no Next.js com `next-intl`, `next-i18next` e `Intlayer`.
27
+
28
+ ---
29
+
30
+ ## Por que SEO e i18n
31
+
32
+ Adicionar idiomas não é apenas sobre UX. É também uma alavanca poderosa para a **visibilidade orgânica**. Eis o porquê:
33
+
34
+ 1. **Melhor descobribilidade:** Os motores de busca indexam versões localizadas e as classificam para usuários que buscam em seu idioma nativo.
35
+ 2. **Evitar conteúdo duplicado:** Tags canônicas e alternadas adequadas indicam aos rastreadores qual página pertence a qual localidade.
36
+ 3. **Melhor UX:** Os visitantes chegam imediatamente à versão correta do seu site.
37
+ 4. **Vantagem competitiva:** Poucos sites implementam SEO multilíngue corretamente, o que significa que você pode se destacar.
38
+
39
+ ---
40
+
41
+ ## Melhores práticas para SEO multilíngue no Next.js
42
+
43
+ Aqui está uma lista de verificação que todo app multilíngue deve implementar:
44
+
45
+ - **Defina as meta tags `hreflang` no `<head>`**
46
+ Ajuda o Google a entender quais versões existem para cada idioma.
47
+
48
+ - **Liste todas as páginas traduzidas no `sitemap.xml`**
49
+ Use o esquema `xhtml` para que os rastreadores encontrem facilmente as versões alternativas.
50
+
51
+ - **Exclua rotas privadas/localizadas no `robots.txt`**
52
+ Exemplo: não permita que `/dashboard`, `/fr/dashboard`, `/es/dashboard` sejam indexados.
53
+
54
+ - **Use links localizados**
55
+ Exemplo: `<a href="/fr/about">À propos</a>` em vez de linkar para o padrão `/about`.
56
+
57
+ Estes são passos simples — mas ignorá-los pode custar sua visibilidade.
58
+
59
+ ---
60
+
61
+ ## Exemplos de Implementação
62
+
63
+ Os desenvolvedores frequentemente esquecem de referenciar corretamente suas páginas entre os idiomas, então vamos ver como isso funciona na prática com diferentes bibliotecas.
64
+
65
+ ### **next-intl**
66
+
67
+ <Tabs>
68
+ <TabItem label="next-intl">
69
+
70
+ ```tsx fileName="src/app/[locale]/about/layout.tsx"
71
+ import type { Metadata } from "next";
72
+ import { locales, defaultLocale } from "@/i18n";
73
+ import { getTranslations, unstable_setRequestLocale } from "next-intl/server";
74
+
75
+ // Função para gerar o caminho localizado conforme o idioma
76
+ function localizedPath(locale: string, path: string) {
77
+ return locale === defaultLocale ? path : `/${locale}${path}`;
78
+ }
79
+
80
+ export async function generateMetadata({
81
+ params,
82
+ }: {
83
+ params: { locale: string };
84
+ }): Promise<Metadata> {
85
+ const { locale } = params;
86
+ const t = await getTranslations({ locale, namespace: "about" });
87
+
88
+ const url = "/about";
89
+ const languages = Object.fromEntries(
90
+ locales.map((l) => [l, localizedPath(l, url)])
91
+ );
92
+
93
+ return {
94
+ title: t("title"),
95
+ description: t("description"),
96
+ alternates: {
97
+ canonical: localizedPath(locale, url),
98
+ languages: { ...languages, "x-default": url },
99
+ },
100
+ };
101
+ }
102
+
103
+ // ... Resto do código da página
104
+ ```
105
+
106
+ ```tsx fileName="src/app/sitemap.ts"
107
+ import type { MetadataRoute } from "next";
108
+ import { locales, defaultLocale } from "@/i18n";
109
+
110
+ const origin = "https://example.com";
111
+
112
+ const formatterLocalizedPath = (locale: string, path: string) =>
113
+ locale === defaultLocale ? `${origin}${path}` : `${origin}/${locale}${path}`;
114
+
115
+ export default function sitemap(): MetadataRoute.Sitemap {
116
+ const aboutLanguages = Object.fromEntries(
117
+ locales.map((l) => [l, formatterLocalizedPath(l, "/about")])
118
+ );
119
+
120
+ return [
121
+ {
122
+ url: formatterLocalizedPath(defaultLocale, "/about"),
123
+ lastModified: new Date(),
124
+ changeFrequency: "monthly",
125
+ priority: 0.7,
126
+ alternates: { languages: aboutLanguages },
127
+ },
128
+ ];
129
+ }
130
+ ```
131
+
132
+ ```tsx fileName="src/app/robots.ts"
133
+ import type { MetadataRoute } from "next";
134
+ import { locales, defaultLocale } from "@/i18n";
135
+
136
+ const origin = "https://example.com";
137
+ const withAllLocales = (path: string) => [
138
+ path,
139
+ ...locales.filter((l) => l !== defaultLocale).map((l) => `/${l}${path}`),
140
+ ];
141
+
142
+ export default function robots(): MetadataRoute.Robots {
143
+ const disallow = [
144
+ ...withAllLocales("/dashboard"),
145
+ ...withAllLocales("/admin"),
146
+ ];
147
+
148
+ return {
149
+ rules: { userAgent: "*", allow: ["/"], disallow },
150
+ host: origin,
151
+ sitemap: `${origin}/sitemap.xml`,
152
+ };
153
+ }
154
+ ```
155
+
156
+ ### **next-i18next**
157
+
158
+ </TabItem>
159
+ <TabItem label="next-i18next">
160
+
161
+ ```ts fileName="i18n.config.ts"
162
+ export const locales = ["en", "fr"] as const;
163
+ export type Locale = (typeof locales)[number];
164
+ export const defaultLocale: Locale = "en";
165
+
166
+ /** Prefixa o caminho com o locale, a menos que seja o locale padrão */
167
+ export function localizedPath(locale: string, path: string) {
168
+ return locale === defaultLocale ? path : `/${locale}${path}`;
169
+ }
170
+
171
+ /** Helper para URL absoluta */
172
+ const ORIGIN = "https://example.com";
173
+ export function abs(locale: string, path: string) {
174
+ return `${ORIGIN}${localizedPath(locale, path)}`;
175
+ }
176
+ ```
177
+
178
+ ```tsx fileName="src/app/[locale]/about/layout.tsx"
179
+ import type { Metadata } from "next";
180
+ import { locales, defaultLocale, localizedPath } from "@/i18n.config";
181
+
182
+ export async function generateMetadata({
183
+ params,
184
+ }: {
185
+ params: { locale: string };
186
+ }): Promise<Metadata> {
187
+ const { locale } = params;
188
+
189
+ // Importa dinamicamente o arquivo JSON correto
190
+ const messages = (await import(`@/../public/locales/${locale}/about.json`))
191
+ .default;
192
+
193
+ const languages = Object.fromEntries(
194
+ locales.map((l) => [l, localizedPath(l, "/about")])
195
+ );
196
+
197
+ return {
198
+ title: messages.title,
199
+ description: messages.description,
200
+ alternates: {
201
+ canonical: localizedPath(locale, "/about"),
202
+ languages: { ...languages, "x-default": "/about" },
203
+ },
204
+ };
205
+ }
206
+
207
+ export default async function AboutPage() {
208
+ return <h1>Sobre</h1>;
209
+ }
210
+ ```
211
+
212
+ ```ts fileName="src/app/sitemap.ts"
213
+ import type { MetadataRoute } from "next";
214
+ import { locales, defaultLocale, abs } from "@/i18n.config";
215
+
216
+ export default function sitemap(): MetadataRoute.Sitemap {
217
+ const languages = Object.fromEntries(
218
+ locales.map((l) => [l, abs(l, "/about")])
219
+ );
220
+ return [
221
+ {
222
+ url: abs(defaultLocale, "/about"),
223
+ lastModified: new Date(),
224
+ changeFrequency: "monthly",
225
+ priority: 0.7,
226
+ alternates: { languages },
227
+ },
228
+ ];
229
+ }
230
+ ```
231
+
232
+ ```ts fileName="src/app/robots.ts"
233
+ import type { MetadataRoute } from "next";
234
+ import { locales, defaultLocale, localizedPath } from "@/i18n.config";
235
+
236
+ const ORIGIN = "https://example.com";
237
+
238
+ const expandAllLocales = (path: string) => [
239
+ localizedPath(defaultLocale, path),
240
+ ...locales
241
+ .filter((l) => l !== defaultLocale)
242
+ .map((l) => localizedPath(l, path)),
243
+ ];
244
+
245
+ export default function robots(): MetadataRoute.Robots {
246
+ const disallow = [
247
+ ...expandAllLocales("/dashboard"),
248
+ ...expandAllLocales("/admin"),
249
+ ];
250
+
251
+ return {
252
+ rules: { userAgent: "*", allow: ["/"], disallow },
253
+ host: ORIGIN,
254
+ sitemap: `${ORIGIN}/sitemap.xml`,
255
+ };
256
+ }
257
+ ```
258
+
259
+ ### **Intlayer**
260
+
261
+ </TabItem>
262
+ <TabItem label="intlayer">
263
+
264
+ ````typescript fileName="src/app/[locale]/about/layout.tsx"
265
+ import { getIntlayer, getMultilingualUrls } from "intlayer";
266
+ import type { Metadata } from "next";
267
+ import type { LocalPromiseParams } from "next-intlayer";
268
+
269
+ export const generateMetadata = async ({
270
+ params,
271
+ }: LocalPromiseParams): Promise<Metadata> => {
272
+ const { locale } = await params;
273
+
274
+ const metadata = getIntlayer("page-metadata", locale);
275
+
276
+ /**
277
+ * Gera um objeto contendo todas as URLs para cada localidade.
278
+ *
279
+ * Exemplo:
280
+ * ```ts
281
+ * getMultilingualUrls('/about');
282
+ *
283
+ * // Retorna
284
+ * // {
285
+ * // en: '/about',
286
+ * // fr: '/fr/about',
287
+ * // es: '/es/about',
288
+ * // }
289
+ * ```
290
+ */
291
+ const multilingualUrls = getMultilingualUrls("/about");
292
+
293
+ return {
294
+ ...metadata,
295
+ alternates: {
296
+ canonical: multilingualUrls[locale as keyof typeof multilingualUrls],
297
+ languages: { ...multilingualUrls, "x-default": "/about" },
298
+ },
299
+ };
300
+ };
301
+
302
+ // ... Resto do código da página
303
+ ````
304
+
305
+ ```tsx fileName="src/app/sitemap.ts"
306
+ import { getMultilingualUrls } from "intlayer";
307
+ import type { MetadataRoute } from "next";
308
+
309
+ const sitemap = (): MetadataRoute.Sitemap => [
310
+ {
311
+ url: "https://example.com/about",
312
+ alternates: {
313
+ languages: { ...getMultilingualUrls("https://example.com/about") },
314
+ },
315
+ },
316
+ ];
317
+ ```
318
+
319
+ ```tsx fileName="src/app/robots.ts"
320
+ import { getMultilingualUrls } from "intlayer";
321
+ import type { MetadataRoute } from "next";
322
+
323
+ // Função para obter todas as URLs multilíngues a partir de uma lista de URLs
324
+ const getAllMultilingualUrls = (urls: string[]) =>
325
+ urls.flatMap((url) => Object.values(getMultilingualUrls(url)) as string[]);
326
+
327
+ const robots = (): MetadataRoute.Robots => ({
328
+ rules: {
329
+ userAgent: "*",
330
+ allow: ["/"],
331
+ disallow: getAllMultilingualUrls(["/dashboard"]), // Bloqueia o acesso multilíngue à rota /dashboard
332
+ },
333
+ host: "https://example.com",
334
+ sitemap: `https://example.com/sitemap.xml`,
335
+ });
336
+
337
+ export default robots;
338
+ ```
339
+
340
+ > Intlayer fornece uma função `getMultilingualUrls` para gerar URLs multilíngues para o seu sitemap.
341
+
342
+ </TabItem>
343
+ </Tabs>
344
+
345
+ ---
346
+
347
+ ## Conclusão
348
+
349
+ Fazer a i18n corretamente no Next.js não é apenas sobre traduzir texto, mas sim garantir que os motores de busca e os usuários saibam exatamente qual versão do seu conteúdo deve ser exibida.
350
+ Configurar hreflang, sitemaps e regras de robots é o que transforma traduções em valor real para SEO.
351
+
352
+ Enquanto next-intl e next-i18next oferecem formas sólidas de configurar isso, geralmente exigem muita configuração manual para manter a consistência entre os locais.
353
+
354
+ É aqui que o Intlayer realmente se destaca:
355
+
356
+ Ele vem com helpers integrados como getMultilingualUrls, tornando a integração de hreflang, sitemap e robots quase sem esforço.
357
+
358
+ Os metadados permanecem centralizados em vez de dispersos em arquivos JSON ou utilitários personalizados.
359
+
360
+ Foi projetado para Next.js desde o início, para que você gaste menos tempo depurando configurações e mais tempo entregando.
361
+
362
+ Se o seu objetivo não é apenas traduzir, mas escalar o SEO multilíngue sem atritos, o Intlayer oferece a configuração mais limpa e preparada para o futuro.