@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,363 @@
1
+ ---
2
+ createdAt: 2025-09-28
3
+ updatedAt: 2025-09-28
4
+ title: SEO e i18n in Next.js
5
+ description: Impara come configurare la SEO multilingue nella tua app Next.js usando next-intl, next-i18next e Intlayer.
6
+ keywords:
7
+ - Intlayer
8
+ - SEO
9
+ - Internazionalizzazione
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 in Next.js: Tradurre non basta
22
+
23
+ Quando gli sviluppatori pensano all'internazionalizzazione (i18n), il primo riflesso è spesso: _tradurre il contenuto_. Ma di solito si dimentica che l'obiettivo principale dell'internazionalizzazione è rendere il tuo sito web più visibile al mondo.
24
+ Se la tua app Next.js multilingue non indica ai motori di ricerca come eseguire la scansione e comprendere le diverse versioni linguistiche, la maggior parte del tuo impegno potrebbe passare inosservata.
25
+
26
+ In questo blog, esploreremo **perché l’i18n è un superpotere per la SEO** e come implementarla correttamente in Next.js con `next-intl`, `next-i18next` e `Intlayer`.
27
+
28
+ ---
29
+
30
+ ## Perché SEO e i18n
31
+
32
+ Aggiungere lingue non riguarda solo l’esperienza utente (UX). È anche una leva potente per la **visibilità organica**. Ecco perché:
33
+
34
+ 1. **Migliore scoperta:** I motori di ricerca indicizzano le versioni localizzate e le posizionano per gli utenti che cercano nella loro lingua madre.
35
+ 2. **Evitare contenuti duplicati:** I tag canonical e alternate corretti indicano ai crawler quale pagina appartiene a quale locale.
36
+ 3. **Migliore UX:** I visitatori arrivano immediatamente alla versione giusta del tuo sito.
37
+ 4. **Vantaggio competitivo:** Pochi siti implementano correttamente la SEO multilingue, il che significa che puoi distinguerti.
38
+
39
+ ---
40
+
41
+ ## Best practice per la SEO multilingue in Next.js
42
+
43
+ Ecco una checklist che ogni app multilingue dovrebbe implementare:
44
+
45
+ - **Imposta i meta tag `hreflang` nel `<head>`**
46
+ Aiuta Google a capire quali versioni esistono per ogni lingua.
47
+
48
+ - **Elenca tutte le pagine tradotte in `sitemap.xml`**
49
+ Usa lo schema `xhtml` in modo che i crawler possano trovare facilmente le versioni alternative.
50
+
51
+ - **Escludi le rotte private/localizzate in `robots.txt`**
52
+ Ad esempio, non permettere che `/dashboard`, `/fr/dashboard`, `/es/dashboard` vengano indicizzate.
53
+
54
+ - **Usa link localizzati**
55
+ Esempio: `<a href="/fr/about">À propos</a>` invece di collegarti alla versione predefinita `/about`.
56
+
57
+ Questi sono passaggi semplici — ma saltarli può costarti visibilità.
58
+
59
+ ---
60
+
61
+ ## Esempi di Implementazione
62
+
63
+ Gli sviluppatori spesso dimenticano di riferire correttamente le loro pagine tra le diverse localizzazioni, quindi vediamo come funziona in pratica con diverse librerie.
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
+ // Funzione per ottenere il percorso localizzato
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
+ // Ottieni le traduzioni per il namespace "about"
87
+ const t = await getTranslations({ locale, namespace: "about" });
88
+
89
+ const url = "/about";
90
+ const languages = Object.fromEntries(
91
+ locales.map((l) => [l, localizedPath(l, url)])
92
+ );
93
+
94
+ return {
95
+ title: t("title"),
96
+ description: t("description"),
97
+ alternates: {
98
+ canonical: localizedPath(locale, url),
99
+ languages: { ...languages, "x-default": url },
100
+ },
101
+ };
102
+ }
103
+
104
+ // ... Resto del codice della pagina
105
+ ```
106
+
107
+ ```tsx fileName="src/app/sitemap.ts"
108
+ import type { MetadataRoute } from "next";
109
+ import { locales, defaultLocale } from "@/i18n";
110
+
111
+ const origin = "https://example.com";
112
+
113
+ const formatterLocalizedPath = (locale: string, path: string) =>
114
+ locale === defaultLocale ? `${origin}${path}` : `${origin}/${locale}${path}`;
115
+
116
+ export default function sitemap(): MetadataRoute.Sitemap {
117
+ const aboutLanguages = Object.fromEntries(
118
+ locales.map((l) => [l, formatterLocalizedPath(l, "/about")])
119
+ );
120
+
121
+ return [
122
+ {
123
+ url: formatterLocalizedPath(defaultLocale, "/about"),
124
+ lastModified: new Date(),
125
+ changeFrequency: "mensile",
126
+ priority: 0.7,
127
+ alternates: { languages: aboutLanguages },
128
+ },
129
+ ];
130
+ }
131
+ ```
132
+
133
+ ```tsx fileName="src/app/robots.ts"
134
+ import type { MetadataRoute } from "next";
135
+ import { locales, defaultLocale } from "@/i18n";
136
+
137
+ const origin = "https://example.com";
138
+ const withAllLocales = (path: string) => [
139
+ path,
140
+ ...locales.filter((l) => l !== defaultLocale).map((l) => `/${l}${path}`),
141
+ ];
142
+
143
+ export default function robots(): MetadataRoute.Robots {
144
+ const disallow = [
145
+ ...withAllLocales("/dashboard"),
146
+ ...withAllLocales("/admin"),
147
+ ];
148
+
149
+ return {
150
+ rules: { userAgent: "*", allow: ["/"], disallow },
151
+ host: origin,
152
+ sitemap: `${origin}/sitemap.xml`,
153
+ };
154
+ }
155
+ ```
156
+
157
+ ### **next-i18next**
158
+
159
+ </TabItem>
160
+ <TabItem label="next-i18next">
161
+
162
+ ```ts fileName="i18n.config.ts"
163
+ export const locales = ["en", "fr"] as const;
164
+ export type Locale = (typeof locales)[number];
165
+ export const defaultLocale: Locale = "en";
166
+
167
+ /** Prefisso del percorso con la locale a meno che non sia la locale predefinita */
168
+ export function localizedPath(locale: string, path: string) {
169
+ return locale === defaultLocale ? path : `/${locale}${path}`;
170
+ }
171
+
172
+ /** Helper per URL assoluti */
173
+ const ORIGIN = "https://example.com";
174
+ export function abs(locale: string, path: string) {
175
+ return `${ORIGIN}${localizedPath(locale, path)}`;
176
+ }
177
+ ```
178
+
179
+ ```tsx fileName="src/app/[locale]/about/layout.tsx"
180
+ import type { Metadata } from "next";
181
+ import { locales, defaultLocale, localizedPath } from "@/i18n.config";
182
+
183
+ export async function generateMetadata({
184
+ params,
185
+ }: {
186
+ params: { locale: string };
187
+ }): Promise<Metadata> {
188
+ const { locale } = params;
189
+
190
+ // Importa dinamicamente il file JSON corretto
191
+ const messages = (await import(`@/../public/locales/${locale}/about.json`))
192
+ .default;
193
+
194
+ const languages = Object.fromEntries(
195
+ locales.map((l) => [l, localizedPath(l, "/about")])
196
+ );
197
+
198
+ return {
199
+ title: messages.title,
200
+ description: messages.description,
201
+ alternates: {
202
+ canonical: localizedPath(locale, "/about"),
203
+ languages: { ...languages, "x-default": "/about" },
204
+ },
205
+ };
206
+ }
207
+
208
+ export default async function AboutPage() {
209
+ return <h1>Informazioni</h1>;
210
+ }
211
+ ```
212
+
213
+ ```ts fileName="src/app/sitemap.ts"
214
+ import type { MetadataRoute } from "next";
215
+ import { locales, defaultLocale, abs } from "@/i18n.config";
216
+
217
+ export default function sitemap(): MetadataRoute.Sitemap {
218
+ const languages = Object.fromEntries(
219
+ locales.map((l) => [l, abs(l, "/about")])
220
+ );
221
+ return [
222
+ {
223
+ url: abs(defaultLocale, "/about"),
224
+ lastModified: new Date(),
225
+ changeFrequency: "monthly",
226
+ priority: 0.7,
227
+ alternates: { languages },
228
+ },
229
+ ];
230
+ }
231
+ ```
232
+
233
+ ```ts fileName="src/app/robots.ts"
234
+ import type { MetadataRoute } from "next";
235
+ import { locales, defaultLocale, localizedPath } from "@/i18n.config";
236
+
237
+ const ORIGIN = "https://example.com";
238
+
239
+ const expandAllLocales = (path: string) => [
240
+ localizedPath(defaultLocale, path),
241
+ ...locales
242
+ .filter((l) => l !== defaultLocale)
243
+ .map((l) => localizedPath(l, path)),
244
+ ];
245
+
246
+ export default function robots(): MetadataRoute.Robots {
247
+ const disallow = [
248
+ ...expandAllLocales("/dashboard"),
249
+ ...expandAllLocales("/admin"),
250
+ ];
251
+
252
+ return {
253
+ rules: { userAgent: "*", allow: ["/"], disallow },
254
+ host: ORIGIN,
255
+ sitemap: `${ORIGIN}/sitemap.xml`,
256
+ };
257
+ }
258
+ ```
259
+
260
+ ### **Intlayer**
261
+
262
+ </TabItem>
263
+ <TabItem label="intlayer">
264
+
265
+ ````typescript fileName="src/app/[locale]/about/layout.tsx"
266
+ import { getIntlayer, getMultilingualUrls } from "intlayer";
267
+ import type { Metadata } from "next";
268
+ import type { LocalPromiseParams } from "next-intlayer";
269
+
270
+ export const generateMetadata = async ({
271
+ params,
272
+ }: LocalPromiseParams): Promise<Metadata> => {
273
+ const { locale } = await params;
274
+
275
+ const metadata = getIntlayer("page-metadata", locale);
276
+
277
+ /**
278
+ * Genera un oggetto contenente tutti gli URL per ogni lingua.
279
+ *
280
+ * Esempio:
281
+ * ```ts
282
+ * getMultilingualUrls('/about');
283
+ *
284
+ * // Restituisce
285
+ * // {
286
+ * // en: '/about',
287
+ * // fr: '/fr/about',
288
+ * // es: '/es/about',
289
+ * // }
290
+ * ```
291
+ */
292
+ const multilingualUrls = getMultilingualUrls("/about");
293
+
294
+ return {
295
+ ...metadata,
296
+ alternates: {
297
+ canonical: multilingualUrls[locale as keyof typeof multilingualUrls],
298
+ languages: { ...multilingualUrls, "x-default": "/about" },
299
+ },
300
+ };
301
+ };
302
+
303
+ // ... Resto del codice della pagina
304
+ ````
305
+
306
+ ```tsx fileName="src/app/sitemap.ts"
307
+ import { getMultilingualUrls } from "intlayer";
308
+ import type { MetadataRoute } from "next";
309
+
310
+ const sitemap = (): MetadataRoute.Sitemap => [
311
+ {
312
+ url: "https://example.com/about",
313
+ alternates: {
314
+ languages: { ...getMultilingualUrls("https://example.com/about") },
315
+ },
316
+ },
317
+ ];
318
+ ```
319
+
320
+ ```tsx fileName="src/app/robots.ts"
321
+ import { getMultilingualUrls } from "intlayer";
322
+ import type { MetadataRoute } from "next";
323
+
324
+ const getAllMultilingualUrls = (urls: string[]) =>
325
+ urls.flatMap((url) => Object.values(getMultilingualUrls(url)) as string[]);
326
+
327
+ // Funzione per ottenere tutti gli URL multilingue da un array di URL
328
+ const robots = (): MetadataRoute.Robots => ({
329
+ rules: {
330
+ userAgent: "*",
331
+ allow: ["/"],
332
+ disallow: getAllMultilingualUrls(["/dashboard"]), // Blocca l'accesso a tutte le versioni multilingue di /dashboard
333
+ },
334
+ host: "https://example.com",
335
+ sitemap: `https://example.com/sitemap.xml`,
336
+ });
337
+
338
+ export default robots;
339
+ ```
340
+
341
+ > Intlayer fornisce una funzione `getMultilingualUrls` per generare URL multilingue per la tua sitemap.
342
+
343
+ </TabItem>
344
+ </Tabs>
345
+
346
+ ---
347
+
348
+ ## Conclusione
349
+
350
+ Gestire correttamente l'i18n in Next.js non significa solo tradurre il testo, ma assicurarsi che i motori di ricerca e gli utenti sappiano esattamente quale versione del tuo contenuto mostrare.
351
+ Configurare hreflang, sitemap e regole per i robots è ciò che trasforma le traduzioni in un vero valore SEO.
352
+
353
+ Mentre next-intl e next-i18next offrono modi solidi per collegare tutto questo, di solito richiedono molta configurazione manuale per mantenere la coerenza tra le diverse localizzazioni.
354
+
355
+ È qui che Intlayer brilla davvero:
356
+
357
+ Include helper integrati come getMultilingualUrls, rendendo quasi senza sforzo l'integrazione di hreflang, sitemap e robots.
358
+
359
+ I metadata rimangono centralizzati invece di essere sparsi tra file JSON o utility personalizzate.
360
+
361
+ È progettato per Next.js fin dalle fondamenta, così trascorri meno tempo a fare debug della configurazione e più tempo a rilasciare.
362
+
363
+ Se il tuo obiettivo non è solo tradurre ma scalare la SEO multilingue senza attriti, Intlayer ti offre la configurazione più pulita e a prova di futuro.