@intlayer/docs 6.1.4 → 6.1.6-canary.0

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 (75) hide show
  1. package/blog/ar/next-i18next_vs_next-intl_vs_intlayer.md +1366 -75
  2. package/blog/ar/nextjs-multilingual-seo-comparison.md +364 -0
  3. package/blog/de/next-i18next_vs_next-intl_vs_intlayer.md +1288 -72
  4. package/blog/de/nextjs-multilingual-seo-comparison.md +362 -0
  5. package/blog/en/intlayer_with_next-i18next.mdx +431 -0
  6. package/blog/en/intlayer_with_next-intl.mdx +335 -0
  7. package/blog/en/next-i18next_vs_next-intl_vs_intlayer.md +583 -336
  8. package/blog/en/nextjs-multilingual-seo-comparison.md +360 -0
  9. package/blog/en-GB/next-i18next_vs_next-intl_vs_intlayer.md +1144 -37
  10. package/blog/en-GB/nextjs-multilingual-seo-comparison.md +360 -0
  11. package/blog/es/next-i18next_vs_next-intl_vs_intlayer.md +1236 -64
  12. package/blog/es/nextjs-multilingual-seo-comparison.md +363 -0
  13. package/blog/fr/next-i18next_vs_next-intl_vs_intlayer.md +1142 -75
  14. package/blog/fr/nextjs-multilingual-seo-comparison.md +362 -0
  15. package/blog/hi/nextjs-multilingual-seo-comparison.md +363 -0
  16. package/blog/it/next-i18next_vs_next-intl_vs_intlayer.md +1130 -55
  17. package/blog/it/nextjs-multilingual-seo-comparison.md +363 -0
  18. package/blog/ja/next-i18next_vs_next-intl_vs_intlayer.md +1150 -76
  19. package/blog/ja/nextjs-multilingual-seo-comparison.md +362 -0
  20. package/blog/ko/next-i18next_vs_next-intl_vs_intlayer.md +1139 -73
  21. package/blog/ko/nextjs-multilingual-seo-comparison.md +362 -0
  22. package/blog/pt/next-i18next_vs_next-intl_vs_intlayer.md +1143 -76
  23. package/blog/pt/nextjs-multilingual-seo-comparison.md +362 -0
  24. package/blog/ru/next-i18next_vs_next-intl_vs_intlayer.md +1150 -74
  25. package/blog/ru/nextjs-multilingual-seo-comparison.md +370 -0
  26. package/blog/tr/next-i18next_vs_next-intl_vs_intlayer.md +2 -0
  27. package/blog/tr/nextjs-multilingual-seo-comparison.md +362 -0
  28. package/blog/zh/next-i18next_vs_next-intl_vs_intlayer.md +1152 -75
  29. package/blog/zh/nextjs-multilingual-seo-comparison.md +394 -0
  30. package/dist/cjs/generated/blog.entry.cjs +16 -0
  31. package/dist/cjs/generated/blog.entry.cjs.map +1 -1
  32. package/dist/cjs/generated/docs.entry.cjs +16 -0
  33. package/dist/cjs/generated/docs.entry.cjs.map +1 -1
  34. package/dist/esm/generated/blog.entry.mjs +16 -0
  35. package/dist/esm/generated/blog.entry.mjs.map +1 -1
  36. package/dist/esm/generated/docs.entry.mjs +16 -0
  37. package/dist/esm/generated/docs.entry.mjs.map +1 -1
  38. package/dist/types/generated/blog.entry.d.ts +1 -0
  39. package/dist/types/generated/blog.entry.d.ts.map +1 -1
  40. package/dist/types/generated/docs.entry.d.ts +1 -0
  41. package/dist/types/generated/docs.entry.d.ts.map +1 -1
  42. package/docs/ar/component_i18n.md +186 -0
  43. package/docs/ar/vs_code_extension.md +48 -109
  44. package/docs/de/component_i18n.md +186 -0
  45. package/docs/de/vs_code_extension.md +46 -107
  46. package/docs/en/component_i18n.md +186 -0
  47. package/docs/en/interest_of_intlayer.md +2 -2
  48. package/docs/en/intlayer_with_nextjs_14.md +18 -1
  49. package/docs/en/intlayer_with_nextjs_15.md +18 -1
  50. package/docs/en/vs_code_extension.md +24 -114
  51. package/docs/en-GB/component_i18n.md +186 -0
  52. package/docs/en-GB/vs_code_extension.md +42 -103
  53. package/docs/es/component_i18n.md +182 -0
  54. package/docs/es/vs_code_extension.md +53 -114
  55. package/docs/fr/component_i18n.md +186 -0
  56. package/docs/fr/vs_code_extension.md +50 -111
  57. package/docs/hi/component_i18n.md +186 -0
  58. package/docs/hi/vs_code_extension.md +49 -110
  59. package/docs/it/component_i18n.md +186 -0
  60. package/docs/it/vs_code_extension.md +50 -111
  61. package/docs/ja/component_i18n.md +186 -0
  62. package/docs/ja/vs_code_extension.md +50 -111
  63. package/docs/ko/component_i18n.md +186 -0
  64. package/docs/ko/vs_code_extension.md +48 -109
  65. package/docs/pt/component_i18n.md +186 -0
  66. package/docs/pt/vs_code_extension.md +46 -107
  67. package/docs/ru/component_i18n.md +186 -0
  68. package/docs/ru/vs_code_extension.md +48 -109
  69. package/docs/tr/component_i18n.md +186 -0
  70. package/docs/tr/vs_code_extension.md +54 -115
  71. package/docs/zh/component_i18n.md +186 -0
  72. package/docs/zh/vs_code_extension.md +51 -105
  73. package/package.json +11 -11
  74. package/src/generated/blog.entry.ts +16 -0
  75. package/src/generated/docs.entry.ts +16 -0
@@ -0,0 +1,362 @@
1
+ ---
2
+ createdAt: 2025-09-28
3
+ updatedAt: 2025-09-28
4
+ title: SEO et i18n dans Next.js
5
+ description: Apprenez à configurer le SEO multilingue dans votre application Next.js en utilisant next-intl, next-i18next et Intlayer.
6
+ keywords:
7
+ - Intlayer
8
+ - SEO
9
+ - Internationalisation
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 et i18n dans Next.js : Traduire ne suffit pas
22
+
23
+ Lorsque les développeurs pensent à l'internationalisation (i18n), le premier réflexe est souvent : _traduire le contenu_. Mais on oublie généralement que l'objectif principal de l'internationalisation est de rendre votre site web plus visible dans le monde.
24
+ Si votre application Next.js multilingue n'indique pas aux moteurs de recherche comment explorer et comprendre vos différentes versions linguistiques, la plupart de vos efforts risquent de passer inaperçus.
25
+
26
+ Dans ce blog, nous allons explorer **pourquoi l'i18n est une superpuissance du SEO** et comment l'implémenter correctement dans Next.js avec `next-intl`, `next-i18next` et `Intlayer`.
27
+
28
+ ---
29
+
30
+ ## Pourquoi le SEO et l'i18n
31
+
32
+ Ajouter des langues ne concerne pas seulement l'expérience utilisateur (UX). C'est aussi un levier puissant pour la **visibilité organique**. Voici pourquoi :
33
+
34
+ 1. **Meilleure découvrabilité :** Les moteurs de recherche indexent les versions localisées et les classent pour les utilisateurs recherchant dans leur langue maternelle.
35
+ 2. **Éviter le contenu dupliqué :** Les balises canoniques et alternates appropriées indiquent aux crawlers quelle page appartient à quelle locale.
36
+ 3. **Meilleure UX :** Les visiteurs arrivent immédiatement sur la bonne version de votre site.
37
+ 4. **Avantage concurrentiel :** Peu de sites mettent en œuvre correctement le SEO multilingue, ce qui signifie que vous pouvez vous démarquer.
38
+
39
+ ---
40
+
41
+ ## Meilleures pratiques pour le SEO multilingue dans Next.js
42
+
43
+ Voici une liste de contrôle que toute application multilingue devrait mettre en œuvre :
44
+
45
+ - **Définir les balises méta `hreflang` dans `<head>`**
46
+ Aide Google à comprendre quelles versions existent pour chaque langue.
47
+
48
+ - **Lister toutes les pages traduites dans `sitemap.xml`**
49
+ Utilisez le schéma `xhtml` pour que les crawlers puissent facilement trouver les alternatives.
50
+
51
+ - **Exclure les routes privées/localisées dans `robots.txt`**
52
+ Par exemple, ne pas laisser `/dashboard`, `/fr/dashboard`, `/es/dashboard` être indexés.
53
+
54
+ - **Utiliser des liens localisés**
55
+ Exemple : `<a href="/fr/about">À propos</a>` au lieu de lier vers la version par défaut `/about`.
56
+
57
+ Ce sont des étapes simples — mais les ignorer peut vous coûter en visibilité.
58
+
59
+ ---
60
+
61
+ ## Exemples d'implémentation
62
+
63
+ Les développeurs oublient souvent de référencer correctement leurs pages selon les locales, voyons donc comment cela fonctionne en pratique avec différentes bibliothèques.
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
+ // Fonction pour obtenir le chemin localisé selon la locale
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
+ // Récupère les traductions pour la locale et le 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
+ // ... Reste du code de la page
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: "mensuel",
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
+ /** Préfixer le chemin avec la locale sauf si c'est la locale par défaut */
168
+ export function localizedPath(locale: string, path: string) {
169
+ return locale === defaultLocale ? path : `/${locale}${path}`;
170
+ }
171
+
172
+ /** Helper pour URL absolue */
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
+ // Importer dynamiquement le fichier JSON correct
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>À propos</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", // fréquence de changement
226
+ priority: 0.7, // priorité du sitemap
227
+ alternates: { languages }, // langues alternatives
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
+ * Génère un objet contenant toutes les URL pour chaque locale.
279
+ *
280
+ * Exemple :
281
+ * ```ts
282
+ * getMultilingualUrls('/about');
283
+ *
284
+ * // Retourne
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
+ // ... Reste du code de la page
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
+ const robots = (): MetadataRoute.Robots => ({
328
+ rules: {
329
+ userAgent: "*",
330
+ allow: ["/"],
331
+ disallow: getAllMultilingualUrls(["/dashboard"]),
332
+ },
333
+ host: "https://example.com",
334
+ sitemap: `https://example.com/sitemap.xml`,
335
+ });
336
+
337
+ export default robots;
338
+ ```
339
+
340
+ > Intlayer fournit une fonction `getMultilingualUrls` pour générer des URLs multilingues pour votre sitemap.
341
+
342
+ </TabItem>
343
+ </Tabs>
344
+
345
+ ---
346
+
347
+ ## Conclusion
348
+
349
+ Bien gérer l’i18n dans Next.js ne consiste pas seulement à traduire du texte, mais à s’assurer que les moteurs de recherche et les utilisateurs savent exactement quelle version de votre contenu afficher.
350
+ Configurer hreflang, les sitemaps et les règles robots est ce qui transforme les traductions en une véritable valeur SEO.
351
+
352
+ Alors que next-intl et next-i18next offrent des moyens solides pour mettre cela en place, ils nécessitent généralement beaucoup de configuration manuelle pour maintenir la cohérence entre les locales.
353
+
354
+ C’est là que Intlayer brille vraiment :
355
+
356
+ Il est livré avec des helpers intégrés comme getMultilingualUrls, rendant l’intégration de hreflang, sitemap et robots presque sans effort.
357
+
358
+ Les métadonnées restent centralisées au lieu d’être dispersées dans des fichiers JSON ou des utilitaires personnalisés.
359
+
360
+ Il est conçu pour Next.js dès le départ, vous passez donc moins de temps à déboguer la configuration et plus de temps à déployer.
361
+
362
+ Si votre objectif n’est pas seulement de traduire, mais de faire évoluer le SEO multilingue sans friction, Intlayer vous offre la configuration la plus propre et la plus pérenne.