@intlayer/docs 7.3.10 → 7.3.12
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.
- package/dist/cjs/generated/docs.entry.cjs +19 -0
- package/dist/cjs/generated/docs.entry.cjs.map +1 -1
- package/dist/esm/generated/docs.entry.mjs +19 -0
- package/dist/esm/generated/docs.entry.mjs.map +1 -1
- package/dist/types/generated/docs.entry.d.ts +1 -0
- package/dist/types/generated/docs.entry.d.ts.map +1 -1
- package/docs/ar/intlayer_with_nuxt.md +305 -421
- package/docs/ar/intlayer_with_react_router_v7.md +33 -4
- package/docs/ar/intlayer_with_react_router_v7_fs_routes.md +516 -0
- package/docs/ar/intlayer_with_tanstack.md +2 -12
- package/docs/ar/intlayer_with_vite+vue.md +1 -0
- package/docs/de/intlayer_with_nuxt.md +296 -394
- package/docs/de/intlayer_with_react_router_v7.md +33 -4
- package/docs/de/intlayer_with_react_router_v7_fs_routes.md +573 -0
- package/docs/de/intlayer_with_tanstack.md +1 -0
- package/docs/de/intlayer_with_vite+vue.md +1 -0
- package/docs/en/intlayer_with_nuxt.md +242 -321
- package/docs/en/intlayer_with_react_router_v7.md +24 -0
- package/docs/en/intlayer_with_react_router_v7_fs_routes.md +570 -0
- package/docs/en/intlayer_with_tanstack.md +2 -12
- package/docs/en/intlayer_with_vite+vue.md +49 -48
- package/docs/en-GB/intlayer_with_nuxt.md +262 -358
- package/docs/en-GB/intlayer_with_react_router_v7.md +33 -4
- package/docs/en-GB/intlayer_with_react_router_v7_fs_routes.md +513 -0
- package/docs/en-GB/intlayer_with_tanstack.md +2 -12
- package/docs/en-GB/intlayer_with_vite+vue.md +1 -0
- package/docs/es/intlayer_with_nuxt.md +284 -375
- package/docs/es/intlayer_with_react_router_v7.md +33 -4
- package/docs/es/intlayer_with_react_router_v7_fs_routes.md +575 -0
- package/docs/es/intlayer_with_tanstack.md +1 -0
- package/docs/es/intlayer_with_vite+vue.md +1 -2
- package/docs/fr/intlayer_with_nuxt.md +288 -387
- package/docs/fr/intlayer_with_react_router_v7.md +34 -5
- package/docs/fr/intlayer_with_react_router_v7_fs_routes.md +574 -0
- package/docs/fr/intlayer_with_tanstack.md +1 -0
- package/docs/fr/intlayer_with_vite+vue.md +1 -0
- package/docs/hi/intlayer_with_nuxt.md +318 -434
- package/docs/hi/intlayer_with_react_router_v7.md +33 -4
- package/docs/hi/intlayer_with_react_router_v7_fs_routes.md +518 -0
- package/docs/hi/intlayer_with_tanstack.md +2 -12
- package/docs/hi/intlayer_with_vite+vue.md +1 -0
- package/docs/id/intlayer_with_nuxt.md +275 -376
- package/docs/id/intlayer_with_react_router_v7.md +29 -4
- package/docs/id/intlayer_with_react_router_v7_fs_routes.md +521 -0
- package/docs/id/intlayer_with_tanstack.md +2 -12
- package/docs/id/intlayer_with_vite+vue.md +1 -0
- package/docs/it/intlayer_with_nuxt.md +312 -408
- package/docs/it/intlayer_with_react_router_v7.md +33 -4
- package/docs/it/intlayer_with_react_router_v7_fs_routes.md +574 -0
- package/docs/it/intlayer_with_tanstack.md +1 -0
- package/docs/ja/intlayer_with_nuxt.md +319 -414
- package/docs/ja/intlayer_with_react_router_v7.md +33 -4
- package/docs/ja/intlayer_with_react_router_v7_fs_routes.md +574 -0
- package/docs/ja/intlayer_with_tanstack.md +2 -12
- package/docs/ja/intlayer_with_vite+vue.md +1 -0
- package/docs/ko/intlayer_with_nuxt.md +307 -406
- package/docs/ko/intlayer_with_react_router_v7.md +33 -4
- package/docs/ko/intlayer_with_react_router_v7_fs_routes.md +515 -0
- package/docs/ko/intlayer_with_tanstack.md +2 -12
- package/docs/ko/intlayer_with_vite+vue.md +1 -0
- package/docs/pl/intlayer_with_nuxt.md +282 -457
- package/docs/pl/intlayer_with_react_router_v7.md +32 -5
- package/docs/pl/intlayer_with_react_router_v7_fs_routes.md +615 -0
- package/docs/pl/intlayer_with_tanstack.md +2 -12
- package/docs/pl/intlayer_with_vite+vue.md +1 -0
- package/docs/pt/intlayer_with_nuxt.md +288 -403
- package/docs/pt/intlayer_with_react_router_v7.md +28 -0
- package/docs/pt/intlayer_with_tanstack.md +1 -0
- package/docs/ru/intlayer_with_nuxt.md +300 -410
- package/docs/ru/intlayer_with_react_router_v7.md +33 -4
- package/docs/ru/intlayer_with_react_router_v7_fs_routes.md +574 -0
- package/docs/ru/intlayer_with_tanstack.md +1 -0
- package/docs/ru/intlayer_with_vite+vue.md +1 -0
- package/docs/tr/intlayer_with_nuxt.md +327 -392
- package/docs/tr/intlayer_with_react_router_v7.md +33 -4
- package/docs/tr/intlayer_with_react_router_v7_fs_routes.md +572 -0
- package/docs/tr/intlayer_with_tanstack.md +2 -12
- package/docs/tr/intlayer_with_vite+vue.md +1 -0
- package/docs/vi/intlayer_with_nuxt.md +282 -399
- package/docs/vi/intlayer_with_react_router_v7.md +29 -4
- package/docs/vi/intlayer_with_react_router_v7_fs_routes.md +523 -0
- package/docs/vi/intlayer_with_tanstack.md +2 -12
- package/docs/vi/intlayer_with_vite+vue.md +1 -0
- package/docs/zh/intlayer_with_nuxt.md +311 -444
- package/docs/zh/intlayer_with_react_router_v7.md +33 -4
- package/docs/zh/intlayer_with_react_router_v7_fs_routes.md +516 -0
- package/docs/zh/intlayer_with_tanstack.md +2 -12
- package/docs/zh/intlayer_with_vite+vue.md +1 -0
- package/package.json +6 -6
- package/src/generated/docs.entry.ts +19 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
createdAt: 2025-06-18
|
|
3
|
-
updatedAt: 2025-
|
|
3
|
+
updatedAt: 2025-12-07
|
|
4
4
|
title: How to translate your Nuxt and Vue app – i18n guide 2025
|
|
5
5
|
description: Discover how to make your Nuxt and Vue website multilingual. Follow the documentation to internationalize (i18n) and translate it.
|
|
6
6
|
keywords:
|
|
@@ -14,8 +14,12 @@ slugs:
|
|
|
14
14
|
- doc
|
|
15
15
|
- environment
|
|
16
16
|
- nuxt-and-vue
|
|
17
|
-
applicationTemplate: https://github.com/aymericzip/intlayer-nuxt-template
|
|
17
|
+
applicationTemplate: https://github.com/aymericzip/intlayer-nuxt-4-template
|
|
18
|
+
youtubeVideo: https://www.youtube.com/watch?v=IE3XWkZ6a5U
|
|
18
19
|
history:
|
|
20
|
+
- version: 7.3.11
|
|
21
|
+
date: 2025-12-07
|
|
22
|
+
changes: Update LocaleSwitcher, SEO, metadata
|
|
19
23
|
- version: 5.5.10
|
|
20
24
|
date: 2025-06-29
|
|
21
25
|
changes: Init history
|
|
@@ -23,8 +27,6 @@ history:
|
|
|
23
27
|
|
|
24
28
|
# Translate your Nuxt and Vue website using Intlayer | Internationalization (i18n)
|
|
25
29
|
|
|
26
|
-
See [Application Template](https://github.com/aymericzip/intlayer-nuxt-template) on GitHub.
|
|
27
|
-
|
|
28
30
|
## Table of Contents
|
|
29
31
|
|
|
30
32
|
<TOC/>
|
|
@@ -45,7 +47,7 @@ With Intlayer, you can:
|
|
|
45
47
|
## Step-by-Step Guide to Set Up Intlayer in a Nuxt Application
|
|
46
48
|
|
|
47
49
|
<iframe
|
|
48
|
-
src="https://stackblitz.com/github/aymericzip/intlayer-nuxt-template?embed=1&ctl=1&file=intlayer.config.ts"
|
|
50
|
+
src="https://stackblitz.com/github/aymericzip/intlayer-nuxt-4-template?embed=1&ctl=1&file=intlayer.config.ts"
|
|
49
51
|
className="m-auto overflow-hidden rounded-lg border-0 max-md:size-full max-md:h-[700px] md:aspect-16/9 md:w-full"
|
|
50
52
|
title="Demo CodeSandbox - How to Internationalize your application using Intlayer"
|
|
51
53
|
sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
|
|
@@ -54,6 +56,27 @@ With Intlayer, you can:
|
|
|
54
56
|
|
|
55
57
|
### Step 1: Install Dependencies
|
|
56
58
|
|
|
59
|
+
<Tab defaultTab="video">
|
|
60
|
+
<TabItem label="Video" value="video">
|
|
61
|
+
|
|
62
|
+
<iframe title="How to translate your Nuxt and Vue app using Intlayer? Discover Intlayer" class="m-auto aspect-[16/9] w-full overflow-hidden rounded-lg border-0" allow="autoplay; gyroscope;" loading="lazy" width="1080" height="auto" src="https://www.youtube.com/embed/IE3XWkZ6a5U?autoplay=0&origin=http://intlayer.org&controls=0&rel=1"/>
|
|
63
|
+
|
|
64
|
+
</TabItem>
|
|
65
|
+
<TabItem label="Code" value="code">
|
|
66
|
+
|
|
67
|
+
<iframe
|
|
68
|
+
src="https://stackblitz.com/github/aymericzip/intlayer-nuxt-4-template?embed=1&ctl=1&file=intlayer.config.ts"
|
|
69
|
+
className="m-auto overflow-hidden rounded-lg border-0 max-md:size-full max-md:h-[700px] md:aspect-16/9 md:w-full"
|
|
70
|
+
title="Demo CodeSandbox - How to Internationalize your application using Intlayer"
|
|
71
|
+
sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
|
|
72
|
+
loading="lazy"
|
|
73
|
+
/>
|
|
74
|
+
|
|
75
|
+
</TabItem>
|
|
76
|
+
</Tab>
|
|
77
|
+
|
|
78
|
+
See [Application Template](https://github.com/aymericzip/intlayer-nuxt-4-template) on GitHub.
|
|
79
|
+
|
|
57
80
|
Install the necessary packages using npm:
|
|
58
81
|
|
|
59
82
|
```bash packageManager="npm"
|
|
@@ -98,9 +121,6 @@ const config: IntlayerConfig = {
|
|
|
98
121
|
],
|
|
99
122
|
defaultLocale: Locales.ENGLISH,
|
|
100
123
|
},
|
|
101
|
-
content: {
|
|
102
|
-
contentDir: ["."], // Because by default Intayer will watch content declaration files from the `./src` directory
|
|
103
|
-
},
|
|
104
124
|
};
|
|
105
125
|
|
|
106
126
|
export default config;
|
|
@@ -120,9 +140,6 @@ const config = {
|
|
|
120
140
|
],
|
|
121
141
|
defaultLocale: Locales.ENGLISH,
|
|
122
142
|
},
|
|
123
|
-
content: {
|
|
124
|
-
contentDir: ["."],
|
|
125
|
-
},
|
|
126
143
|
};
|
|
127
144
|
|
|
128
145
|
export default config;
|
|
@@ -142,9 +159,6 @@ const config = {
|
|
|
142
159
|
],
|
|
143
160
|
defaultLocale: Locales.ENGLISH,
|
|
144
161
|
},
|
|
145
|
-
content: {
|
|
146
|
-
contentDir: ["."],
|
|
147
|
-
},
|
|
148
162
|
};
|
|
149
163
|
|
|
150
164
|
module.exports = config;
|
|
@@ -171,188 +185,31 @@ export default defineNuxtConfig({
|
|
|
171
185
|
|
|
172
186
|
Create and manage your content declarations to store translations:
|
|
173
187
|
|
|
174
|
-
```tsx fileName="
|
|
175
|
-
import {
|
|
188
|
+
```tsx fileName="content/home-page.content.ts" contentDeclarationFormat="typescript"
|
|
189
|
+
import { type Dictionary, t } from "intlayer";
|
|
176
190
|
|
|
177
|
-
const
|
|
178
|
-
key: "
|
|
191
|
+
const content = {
|
|
192
|
+
key: "home-page",
|
|
179
193
|
content: {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
es: "Edita <code>components/HelloWorld.vue</code> y guarda para probar HMR",
|
|
185
|
-
}),
|
|
186
|
-
checkOut: t({ en: "Check out ", fr: "Vérifiez ", es: "Compruebe " }),
|
|
187
|
-
nuxtIntlayer: t({
|
|
188
|
-
en: "Nuxt Intlayer documentation",
|
|
189
|
-
fr: "Documentation de Nuxt Intlayer",
|
|
190
|
-
es: "Documentación de Nuxt Intlayer",
|
|
191
|
-
}),
|
|
192
|
-
learnMore: t({
|
|
193
|
-
en: "Learn more about Nuxt in the ",
|
|
194
|
-
fr: "En savoir plus sur Nuxt dans la ",
|
|
195
|
-
es: "Aprenda más sobre Nuxt en la ",
|
|
194
|
+
title: t({
|
|
195
|
+
en: "Hello world",
|
|
196
|
+
fr: "Bonjour le monde",
|
|
197
|
+
es: "Hola mundo",
|
|
196
198
|
}),
|
|
197
|
-
|
|
198
|
-
en: "
|
|
199
|
-
fr: "
|
|
200
|
-
es: "
|
|
199
|
+
metaTitle: t({
|
|
200
|
+
en: "Welcome | My Application",
|
|
201
|
+
fr: "Bienvenue | Mon Application",
|
|
202
|
+
es: "Bienvenido | Mi Aplicación",
|
|
201
203
|
}),
|
|
202
|
-
|
|
203
|
-
en: "
|
|
204
|
-
fr: "
|
|
205
|
-
es: "
|
|
204
|
+
metaDescription: t({
|
|
205
|
+
en: "Discover your multilingual Nuxt app homepage powered by Intlayer.",
|
|
206
|
+
fr: "Découvrez la page d'accueil multilingue de votre application Nuxt propulsée par Intlayer.",
|
|
207
|
+
es: "Descubre la página de inicio multilingüe de tu aplicación Nuxt impulsada por Intlayer.",
|
|
206
208
|
}),
|
|
207
209
|
},
|
|
208
210
|
} satisfies Dictionary;
|
|
209
211
|
|
|
210
|
-
export default
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
```javascript fileName="components/helloWorld.content.mjs" contentDeclarationFormat="esm"
|
|
214
|
-
import { t } from "intlayer";
|
|
215
|
-
|
|
216
|
-
/** @type {import('intlayer').Dictionary} */
|
|
217
|
-
const helloWorldContent = {
|
|
218
|
-
key: "helloworld",
|
|
219
|
-
content: {
|
|
220
|
-
count: t({ en: "count is ", fr: "le compte est ", es: "el recuento es " }),
|
|
221
|
-
edit: t({
|
|
222
|
-
en: "Edit <code>components/HelloWorld.vue</code> and save to test HMR",
|
|
223
|
-
fr: "Éditez <code>components/HelloWorld.vue</code> et enregistrez pour tester HMR",
|
|
224
|
-
es: "Edita <code>components/HelloWorld.vue</code> y guarda para probar HMR",
|
|
225
|
-
}),
|
|
226
|
-
checkOut: t({ en: "Check out ", fr: "Vérifiez ", es: "Compruebe " }),
|
|
227
|
-
nuxtIntlayer: t({
|
|
228
|
-
en: "Nuxt Intlayer documentation",
|
|
229
|
-
fr: "Documentation de Nuxt Intlayer",
|
|
230
|
-
es: "Documentación de Nuxt Intlayer",
|
|
231
|
-
}),
|
|
232
|
-
learnMore: t({
|
|
233
|
-
en: "Learn more about Nuxt in the ",
|
|
234
|
-
fr: "En savoir plus sur Nuxt dans la ",
|
|
235
|
-
es: "Aprenda más sobre Nuxt en la ",
|
|
236
|
-
}),
|
|
237
|
-
nuxtDocs: t({
|
|
238
|
-
en: "Nuxt Documentation",
|
|
239
|
-
fr: "Documentation Nuxt",
|
|
240
|
-
es: "Documentación de Nuxt",
|
|
241
|
-
}),
|
|
242
|
-
readTheDocs: t({
|
|
243
|
-
en: "Click on the Nuxt logo to learn more",
|
|
244
|
-
fr: "Cliquez sur le logo Nuxt pour en savoir plus",
|
|
245
|
-
es: "Haga clic en el logotipo de Nuxt para obtener más información",
|
|
246
|
-
}),
|
|
247
|
-
},
|
|
248
|
-
};
|
|
249
|
-
|
|
250
|
-
export default helloWorldContent;
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
```javascript fileName="components/helloWorld.content.cjs" contentDeclarationFormat="commonjs"
|
|
254
|
-
const { t } = require("intlayer");
|
|
255
|
-
|
|
256
|
-
/** @type {import('intlayer').Dictionary} */
|
|
257
|
-
const helloWorldContent = {
|
|
258
|
-
key: "helloworld",
|
|
259
|
-
content: {
|
|
260
|
-
count: t({ en: "count is ", fr: "le compte est ", es: "el recuento es " }),
|
|
261
|
-
edit: t({
|
|
262
|
-
en: "Edit <code>components/HelloWorld.vue</code> and save to test HMR",
|
|
263
|
-
fr: "Éditez <code>components/HelloWorld.vue</code> et enregistrez pour tester HMR",
|
|
264
|
-
es: "Edita <code>components/HelloWorld.vue</code> y guarda para probar HMR",
|
|
265
|
-
}),
|
|
266
|
-
checkOut: t({ en: "Check out ", fr: "Vérifiez ", es: "Compruebe " }),
|
|
267
|
-
nuxtIntlayer: t({
|
|
268
|
-
en: "Nuxt Intlayer documentation",
|
|
269
|
-
fr: "Documentation de Nuxt Intlayer",
|
|
270
|
-
es: "Documentación de Nuxt Intlayer",
|
|
271
|
-
}),
|
|
272
|
-
learnMore: t({
|
|
273
|
-
en: "Learn more about Nuxt in the ",
|
|
274
|
-
fr: "En savoir plus sur Nuxt dans la ",
|
|
275
|
-
es: "Aprenda más sobre Nuxt en la ",
|
|
276
|
-
}),
|
|
277
|
-
nuxtDocs: t({
|
|
278
|
-
en: "Nuxt Documentation",
|
|
279
|
-
fr: "Documentation Nuxt",
|
|
280
|
-
es: "Documentación de Nuxt",
|
|
281
|
-
}),
|
|
282
|
-
readTheDocs: t({
|
|
283
|
-
en: "Click on the Nuxt logo to learn more",
|
|
284
|
-
fr: "Cliquez sur le logo Nuxt pour en savoir plus",
|
|
285
|
-
es: "Haga clic en el logotipo de Nuxt para obtener más información",
|
|
286
|
-
}),
|
|
287
|
-
},
|
|
288
|
-
};
|
|
289
|
-
|
|
290
|
-
module.exports = helloWorldContent;
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
```json fileName="components/helloWorld.content.json" contentDeclarationFormat="json"
|
|
294
|
-
{
|
|
295
|
-
"$schema": "https://intlayer.org/schema.json",
|
|
296
|
-
"key": "helloworld",
|
|
297
|
-
"content": {
|
|
298
|
-
"count": {
|
|
299
|
-
"nodeType": "translation",
|
|
300
|
-
"translation": {
|
|
301
|
-
"en": "count is ",
|
|
302
|
-
"fr": "le compte est ",
|
|
303
|
-
"es": "el recuento es "
|
|
304
|
-
}
|
|
305
|
-
},
|
|
306
|
-
"edit": {
|
|
307
|
-
"nodeType": "translation",
|
|
308
|
-
"translation": {
|
|
309
|
-
"en": "Edit <code>components/HelloWorld.vue</code> and save to test HMR",
|
|
310
|
-
"fr": "Éditez <code>components/HelloWorld.vue</code> et enregistrez pour tester HMR",
|
|
311
|
-
"es": "Edita <code>components/HelloWorld.vue</code> y guarda para probar HMR"
|
|
312
|
-
}
|
|
313
|
-
},
|
|
314
|
-
"checkOut": {
|
|
315
|
-
"nodeType": "translation",
|
|
316
|
-
"translation": {
|
|
317
|
-
"en": "Check out ",
|
|
318
|
-
"fr": "Vérifiez ",
|
|
319
|
-
"es": "Compruebe "
|
|
320
|
-
}
|
|
321
|
-
},
|
|
322
|
-
"nuxtIntlayer": {
|
|
323
|
-
"nodeType": "translation",
|
|
324
|
-
"translation": {
|
|
325
|
-
"en": "Nuxt Intlayer documentation",
|
|
326
|
-
"fr": "Documentation de Nuxt Intlayer",
|
|
327
|
-
"es": "Documentación de Nuxt Intlayer"
|
|
328
|
-
}
|
|
329
|
-
},
|
|
330
|
-
"learnMore": {
|
|
331
|
-
"nodeType": "translation",
|
|
332
|
-
"translation": {
|
|
333
|
-
"en": "Learn more about Nuxt in the ",
|
|
334
|
-
"fr": "En savoir plus sur Nuxt dans la ",
|
|
335
|
-
"es": "Aprenda más sobre Nuxt en la "
|
|
336
|
-
}
|
|
337
|
-
},
|
|
338
|
-
"nuxtDocs": {
|
|
339
|
-
"nodeType": "translation",
|
|
340
|
-
"translation": {
|
|
341
|
-
"en": "Nuxt Documentation",
|
|
342
|
-
"fr": "Documentation Nuxt",
|
|
343
|
-
"es": "Documentación de Nuxt"
|
|
344
|
-
}
|
|
345
|
-
},
|
|
346
|
-
"readTheDocs": {
|
|
347
|
-
"nodeType": "translation",
|
|
348
|
-
"translation": {
|
|
349
|
-
"en": "Click on the Nuxt logo to learn more",
|
|
350
|
-
"fr": "Cliquez sur le logo Nuxt pour en savoir plus",
|
|
351
|
-
"es": "Haga clic en el logotipo de Nuxt para obtener más información"
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
}
|
|
212
|
+
export default content;
|
|
356
213
|
```
|
|
357
214
|
|
|
358
215
|
> Your content declarations can be defined anywhere in your application as long as they are included in the `contentDir` directory (by default, `./src`). And match the content declaration file extension (by default, `.content.{json,ts,tsx,js,jsx,mjs,mjx,cjs,cjx}`).
|
|
@@ -432,64 +289,54 @@ Intlayer offers different APIs to access your content:
|
|
|
432
289
|
|
|
433
290
|
To change the language of your content, you can use the `setLocale` function provided by the `useLocale` composable. This function allows you to set the locale of the application and update the content accordingly.
|
|
434
291
|
|
|
435
|
-
Create a component to switch between languages:
|
|
292
|
+
Create a component to switch between languages using `NuxtLink`. **Using links instead of buttons for locale switching is a best practice for SEO and page discoverability**, as it allows search engines to crawl and index all localized versions of your pages:
|
|
436
293
|
|
|
437
294
|
```vue fileName="components/LocaleSwitcher.vue"
|
|
438
|
-
<template>
|
|
439
|
-
<div class="locale-switcher">
|
|
440
|
-
<select v-model="selectedLocale" @change="changeLocale">
|
|
441
|
-
<option v-for="loc in availableLocales" :key="loc" :value="loc">
|
|
442
|
-
{{ getLocaleName(loc) }}
|
|
443
|
-
</option>
|
|
444
|
-
</select>
|
|
445
|
-
</div>
|
|
446
|
-
</template>
|
|
447
|
-
|
|
448
295
|
<script setup lang="ts">
|
|
449
|
-
import {
|
|
450
|
-
import { getLocaleName } from "intlayer";
|
|
296
|
+
import { getLocaleName, getLocalizedUrl } from "intlayer";
|
|
451
297
|
import { useLocale } from "vue-intlayer";
|
|
452
298
|
|
|
453
|
-
//
|
|
299
|
+
// Nuxt auto-imports useRoute
|
|
300
|
+
const route = useRoute();
|
|
454
301
|
const { locale, availableLocales, setLocale } = useLocale();
|
|
455
|
-
|
|
456
|
-
// Track the selected locale with a ref
|
|
457
|
-
const selectedLocale = ref(locale.value);
|
|
458
|
-
|
|
459
|
-
// Update the locale when the selection changes
|
|
460
|
-
const changeLocale = () => setLocale(selectedLocale.value);
|
|
461
|
-
|
|
462
|
-
// Keep the selectedLocale in sync with the global locale
|
|
463
|
-
watch(
|
|
464
|
-
() => locale.value,
|
|
465
|
-
(newLocale) => {
|
|
466
|
-
selectedLocale.value = newLocale;
|
|
467
|
-
}
|
|
468
|
-
);
|
|
469
302
|
</script>
|
|
303
|
+
|
|
304
|
+
<template>
|
|
305
|
+
<nav class="locale-switcher">
|
|
306
|
+
<NuxtLink
|
|
307
|
+
v-for="localeEl in availableLocales"
|
|
308
|
+
:key="localeEl"
|
|
309
|
+
:to="getLocalizedUrl(route.fullPath, localeEl)"
|
|
310
|
+
class="locale-link"
|
|
311
|
+
:class="{ 'active-locale': localeEl === locale }"
|
|
312
|
+
@click="setLocale(localeEl)"
|
|
313
|
+
>
|
|
314
|
+
{{ getLocaleName(localeEl) }}
|
|
315
|
+
</NuxtLink>
|
|
316
|
+
</nav>
|
|
470
317
|
</template>
|
|
318
|
+
```
|
|
471
319
|
|
|
472
|
-
|
|
473
|
-
.locale-switcher {
|
|
474
|
-
margin: 1rem 0;
|
|
475
|
-
}
|
|
320
|
+
> Using `NuxtLink` with proper `href` attributes (via `getLocalizedUrl`) ensures that search engines can discover all language variants of your pages. This is preferable to JavaScript-only locale switching, which search engine crawlers may not follow.
|
|
476
321
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
322
|
+
Then, set up your `app.vue` to use layouts:
|
|
323
|
+
|
|
324
|
+
```vue fileName="app.vue"
|
|
325
|
+
<template>
|
|
326
|
+
<NuxtLayout>
|
|
327
|
+
<NuxtPage />
|
|
328
|
+
</NuxtLayout>
|
|
329
|
+
</template>
|
|
483
330
|
```
|
|
484
331
|
|
|
485
|
-
|
|
332
|
+
### (Optional) Step 6b: Create a Layout with Navigation
|
|
486
333
|
|
|
487
|
-
|
|
334
|
+
Nuxt layouts allow you to define a common structure for your pages. Create a default layout that includes the locale switcher and navigation:
|
|
335
|
+
|
|
336
|
+
```vue fileName="layouts/default.vue"
|
|
488
337
|
<script setup lang="ts">
|
|
489
|
-
import
|
|
338
|
+
import Links from "~/components/Links.vue";
|
|
490
339
|
import LocaleSwitcher from "~/components/LocaleSwitcher.vue";
|
|
491
|
-
|
|
492
|
-
const content = useIntlayer("app"); // Create related intlayer declaration file
|
|
493
340
|
</script>
|
|
494
341
|
|
|
495
342
|
<template>
|
|
@@ -498,12 +345,17 @@ const content = useIntlayer("app"); // Create related intlayer declaration file
|
|
|
498
345
|
<LocaleSwitcher />
|
|
499
346
|
</header>
|
|
500
347
|
<main>
|
|
501
|
-
<
|
|
348
|
+
<slot />
|
|
502
349
|
</main>
|
|
350
|
+
|
|
351
|
+
<Links href="/">Home</Links>
|
|
352
|
+
<Links href="/about">About</Links>
|
|
503
353
|
</div>
|
|
504
354
|
</template>
|
|
505
355
|
```
|
|
506
356
|
|
|
357
|
+
The `Links` component (shown below) ensures that internal navigation links are automatically localized.
|
|
358
|
+
|
|
507
359
|
### (Optional) Step 7: Add localized Routing to your application
|
|
508
360
|
|
|
509
361
|
Nuxt automatically handles localized routing when using the `nuxt-intlayer` module. This creates routes for each language automatically based on your pages directory structure.
|
|
@@ -518,23 +370,58 @@ pages/
|
|
|
518
370
|
└── index.vue → /contact, /fr/contact, /es/contact
|
|
519
371
|
```
|
|
520
372
|
|
|
521
|
-
To create
|
|
373
|
+
To create localized pages, simply create your Vue files in the `pages/` directory. Here are two example pages:
|
|
374
|
+
|
|
375
|
+
**Home page (`pages/index.vue`):**
|
|
376
|
+
|
|
377
|
+
```vue fileName="pages/index.vue"
|
|
378
|
+
<script setup lang="ts">
|
|
379
|
+
import { useIntlayer } from "vue-intlayer";
|
|
380
|
+
|
|
381
|
+
const content = useIntlayer("home-page");
|
|
382
|
+
|
|
383
|
+
useHead({
|
|
384
|
+
title: content.metaTitle.value,
|
|
385
|
+
meta: [
|
|
386
|
+
{
|
|
387
|
+
name: "description",
|
|
388
|
+
content: content.metaDescription.value,
|
|
389
|
+
},
|
|
390
|
+
],
|
|
391
|
+
});
|
|
392
|
+
</script>
|
|
393
|
+
|
|
394
|
+
<template>
|
|
395
|
+
<h1><content.title /></h1>
|
|
396
|
+
</template>
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
**About page (`pages/about.vue`):**
|
|
522
400
|
|
|
523
401
|
```vue fileName="pages/about.vue"
|
|
524
402
|
<script setup lang="ts">
|
|
525
403
|
import { useIntlayer } from "vue-intlayer";
|
|
526
404
|
|
|
527
|
-
const content = useIntlayer("about");
|
|
405
|
+
const content = useIntlayer("about-page");
|
|
406
|
+
|
|
407
|
+
useHead({
|
|
408
|
+
title: content.metaTitle.raw, // Use .raw for primitive string access
|
|
409
|
+
meta: [
|
|
410
|
+
{
|
|
411
|
+
name: "description",
|
|
412
|
+
content: content.metaDescription.raw, // Use .raw for primitive string access
|
|
413
|
+
},
|
|
414
|
+
],
|
|
415
|
+
});
|
|
528
416
|
</script>
|
|
529
417
|
|
|
530
418
|
<template>
|
|
531
|
-
<
|
|
532
|
-
<h1>{{ content.title }}</h1>
|
|
533
|
-
<p>{{ content.description }}</p>
|
|
534
|
-
</div>
|
|
419
|
+
<h1><content.title /></h1>
|
|
535
420
|
</template>
|
|
536
421
|
```
|
|
537
422
|
|
|
423
|
+
> Note: `useHead` is auto-imported in Nuxt. You can access content values using either `.value` (reactive) or `.raw` (primitive string) depending on your needs.
|
|
424
|
+
|
|
538
425
|
The `nuxt-intlayer` module will automatically:
|
|
539
426
|
|
|
540
427
|
- Detect the user's preferred locale
|
|
@@ -545,184 +432,220 @@ The `nuxt-intlayer` module will automatically:
|
|
|
545
432
|
|
|
546
433
|
### (Optional) Step 8: Creating a Localized Link Component
|
|
547
434
|
|
|
548
|
-
To ensure that your application's navigation respects the current locale, you can create a custom `
|
|
549
|
-
|
|
550
|
-
```vue fileName="components/LocalizedLink.vue"
|
|
551
|
-
<template>
|
|
552
|
-
<NuxtLink :to="localizedHref" v-bind="$attrs">
|
|
553
|
-
<slot />
|
|
554
|
-
</NuxtLink>
|
|
555
|
-
</template>
|
|
435
|
+
To ensure that your application's navigation respects the current locale, you can create a custom `Links` component. This component automatically prefixes internal URLs with the current language, which is essential for **SEO and page discoverability**.
|
|
556
436
|
|
|
437
|
+
```vue fileName="components/Links.vue"
|
|
557
438
|
<script setup lang="ts">
|
|
558
|
-
import { computed } from "vue";
|
|
559
439
|
import { getLocalizedUrl } from "intlayer";
|
|
560
440
|
import { useLocale } from "vue-intlayer";
|
|
561
441
|
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
},
|
|
567
|
-
});
|
|
442
|
+
interface Props {
|
|
443
|
+
href: string;
|
|
444
|
+
locale?: string;
|
|
445
|
+
}
|
|
568
446
|
|
|
569
|
-
const
|
|
447
|
+
const props = defineProps<Props>();
|
|
570
448
|
|
|
571
|
-
|
|
572
|
-
const isExternalLink = computed(() => /^https?:\/\//.test(props.to || ""));
|
|
449
|
+
const { locale: currentLocale } = useLocale();
|
|
573
450
|
|
|
574
|
-
//
|
|
575
|
-
const
|
|
576
|
-
|
|
577
|
-
);
|
|
451
|
+
// Compute the final path
|
|
452
|
+
const finalPath = computed(() => {
|
|
453
|
+
// 1. Check if the link is external
|
|
454
|
+
const isExternal = /^https?:\/\//.test(props.href || "");
|
|
455
|
+
|
|
456
|
+
// 2. If external, return as is (NuxtLink handles the <a> tag generation)
|
|
457
|
+
if (isExternal) return props.href;
|
|
458
|
+
|
|
459
|
+
// 3. If internal, localize the URL
|
|
460
|
+
const targetLocale = props.locale || currentLocale.value;
|
|
461
|
+
return getLocalizedUrl(props.href, targetLocale);
|
|
462
|
+
});
|
|
578
463
|
</script>
|
|
464
|
+
|
|
465
|
+
<template>
|
|
466
|
+
<NuxtLink :to="finalPath" v-bind="$attrs">
|
|
467
|
+
<slot />
|
|
468
|
+
</NuxtLink>
|
|
469
|
+
</template>
|
|
579
470
|
```
|
|
580
471
|
|
|
581
472
|
Then use this component throughout your application:
|
|
582
473
|
|
|
583
|
-
```vue fileName="
|
|
474
|
+
```vue fileName="layouts/default.vue"
|
|
475
|
+
<script setup lang="ts">
|
|
476
|
+
import Links from "~/components/Links.vue";
|
|
477
|
+
import LocaleSwitcher from "~/components/LocaleSwitcher.vue";
|
|
478
|
+
</script>
|
|
479
|
+
|
|
584
480
|
<template>
|
|
585
481
|
<div>
|
|
586
|
-
<
|
|
587
|
-
|
|
588
|
-
</
|
|
589
|
-
<
|
|
590
|
-
|
|
591
|
-
</
|
|
482
|
+
<header>
|
|
483
|
+
<LocaleSwitcher />
|
|
484
|
+
</header>
|
|
485
|
+
<main>
|
|
486
|
+
<slot />
|
|
487
|
+
</main>
|
|
488
|
+
|
|
489
|
+
<Links href="/">Home</Links>
|
|
490
|
+
<Links href="/about">About</Links>
|
|
592
491
|
</div>
|
|
593
492
|
</template>
|
|
594
|
-
|
|
595
|
-
<script setup lang="ts">
|
|
596
|
-
import { useIntlayer } from "vue-intlayer";
|
|
597
|
-
import LocalizedLink from "~/components/LocalizedLink.vue";
|
|
598
|
-
|
|
599
|
-
const content = useIntlayer("home");
|
|
600
|
-
</script>
|
|
601
493
|
```
|
|
602
494
|
|
|
495
|
+
> By using `NuxtLink` with localized paths, you ensure that:
|
|
496
|
+
>
|
|
497
|
+
> - Search engines can crawl and index all language versions of your pages
|
|
498
|
+
> - Users can share localized URLs directly
|
|
499
|
+
> - Browser history works correctly with locale-prefixed URLs
|
|
500
|
+
|
|
603
501
|
### (Optional) Step 9: Handle Metadata and SEO
|
|
604
502
|
|
|
605
|
-
Nuxt provides excellent SEO capabilities. You can use Intlayer to handle localized metadata:
|
|
503
|
+
Nuxt provides excellent SEO capabilities via the `useHead` composable (auto-imported). You can use Intlayer to handle localized metadata using the `.raw` or `.value` accessor to get the primitive string value:
|
|
606
504
|
|
|
607
505
|
```vue fileName="pages/about.vue"
|
|
608
506
|
<script setup lang="ts">
|
|
609
|
-
import {
|
|
610
|
-
import { getIntlayer } from "intlayer";
|
|
611
|
-
import { useLocale } from "vue-intlayer";
|
|
507
|
+
import { useIntlayer } from "vue-intlayer";
|
|
612
508
|
|
|
613
|
-
|
|
614
|
-
const content =
|
|
509
|
+
// useHead is auto-imported in Nuxt
|
|
510
|
+
const content = useIntlayer("about-page");
|
|
615
511
|
|
|
616
|
-
|
|
617
|
-
title: content.
|
|
618
|
-
|
|
512
|
+
useHead({
|
|
513
|
+
title: content.metaTitle.raw, // Use .raw for primitive string access
|
|
514
|
+
meta: [
|
|
515
|
+
{
|
|
516
|
+
name: "description",
|
|
517
|
+
content: content.metaDescription.raw, // Use .raw for primitive string access
|
|
518
|
+
},
|
|
519
|
+
],
|
|
619
520
|
});
|
|
620
521
|
</script>
|
|
621
522
|
|
|
622
523
|
<template>
|
|
623
|
-
<
|
|
624
|
-
<h1>{{ content.pageTitle }}</h1>
|
|
625
|
-
<p>{{ content.pageContent }}</p>
|
|
626
|
-
</div>
|
|
524
|
+
<h1><content.title /></h1>
|
|
627
525
|
</template>
|
|
628
526
|
```
|
|
629
527
|
|
|
528
|
+
> Alternatively, you can use the `import { getIntlayer } from "intlayer"` function to get the content without Vue reactivity.
|
|
529
|
+
|
|
530
|
+
> **Accessing content values:**
|
|
531
|
+
>
|
|
532
|
+
> - Use `.raw` to get the primitive string value (non-reactive)
|
|
533
|
+
> - Use `.value` to get the reactive value
|
|
534
|
+
> - Use `<content.key />` component syntax for Visual Editor support
|
|
535
|
+
|
|
630
536
|
Create the corresponding content declaration:
|
|
631
537
|
|
|
632
|
-
```ts fileName="pages/about-
|
|
538
|
+
```ts fileName="pages/about-page.content.ts" contentDeclarationFormat="typescript"
|
|
633
539
|
import { t, type Dictionary } from "intlayer";
|
|
634
|
-
import type { useSeoMeta } from "nuxt/app";
|
|
635
540
|
|
|
636
|
-
const
|
|
637
|
-
key: "about-
|
|
541
|
+
const aboutPageContent = {
|
|
542
|
+
key: "about-page",
|
|
638
543
|
content: {
|
|
639
|
-
|
|
544
|
+
metaTitle: t({
|
|
640
545
|
en: "About Us - My Company",
|
|
641
546
|
fr: "À Propos - Ma Société",
|
|
642
547
|
es: "Acerca de Nosotros - Mi Empresa",
|
|
643
548
|
}),
|
|
644
|
-
|
|
549
|
+
metaDescription: t({
|
|
645
550
|
en: "Learn more about our company and our mission",
|
|
646
551
|
fr: "En savoir plus sur notre société et notre mission",
|
|
647
552
|
es: "Conozca más sobre nuestra empresa y nuestra misión",
|
|
648
553
|
}),
|
|
554
|
+
title: t({
|
|
555
|
+
en: "About Us",
|
|
556
|
+
fr: "À Propos",
|
|
557
|
+
es: "Acerca de Nosotros",
|
|
558
|
+
}),
|
|
649
559
|
},
|
|
650
|
-
} satisfies Dictionary
|
|
560
|
+
} satisfies Dictionary;
|
|
651
561
|
|
|
652
|
-
export default
|
|
562
|
+
export default aboutPageContent;
|
|
653
563
|
```
|
|
654
564
|
|
|
655
|
-
```
|
|
565
|
+
```javascript fileName="pages/about-page.content.mjs" contentDeclarationFormat="esm"
|
|
656
566
|
import { t } from "intlayer";
|
|
657
567
|
|
|
658
568
|
/** @type {import('intlayer').Dictionary} */
|
|
659
|
-
const
|
|
660
|
-
key: "about-
|
|
569
|
+
const aboutPageContent = {
|
|
570
|
+
key: "about-page",
|
|
661
571
|
content: {
|
|
662
|
-
|
|
663
|
-
zh: "关于我们 - 我的公司",
|
|
572
|
+
metaTitle: t({
|
|
664
573
|
en: "About Us - My Company",
|
|
665
574
|
fr: "À Propos - Ma Société",
|
|
666
575
|
es: "Acerca de Nosotros - Mi Empresa",
|
|
667
576
|
}),
|
|
668
|
-
|
|
669
|
-
zh: "了解更多关于我们公司和我们的使命",
|
|
577
|
+
metaDescription: t({
|
|
670
578
|
en: "Learn more about our company and our mission",
|
|
671
579
|
fr: "En savoir plus sur notre société et notre mission",
|
|
672
580
|
es: "Conozca más sobre nuestra empresa y nuestra misión",
|
|
673
581
|
}),
|
|
582
|
+
title: t({
|
|
583
|
+
en: "About Us",
|
|
584
|
+
fr: "À Propos",
|
|
585
|
+
es: "Acerca de Nosotros",
|
|
586
|
+
}),
|
|
674
587
|
},
|
|
675
588
|
};
|
|
676
589
|
|
|
677
|
-
export default
|
|
590
|
+
export default aboutPageContent;
|
|
678
591
|
```
|
|
679
592
|
|
|
680
|
-
```
|
|
593
|
+
```javascript fileName="pages/about-page.content.cjs" contentDeclarationFormat="commonjs"
|
|
681
594
|
const { t } = require("intlayer");
|
|
682
595
|
|
|
683
596
|
/** @type {import('intlayer').Dictionary} */
|
|
684
|
-
const
|
|
685
|
-
key: "about-
|
|
597
|
+
const aboutPageContent = {
|
|
598
|
+
key: "about-page",
|
|
686
599
|
content: {
|
|
687
|
-
|
|
688
|
-
zh: "关于我们 - 我的公司",
|
|
600
|
+
metaTitle: t({
|
|
689
601
|
en: "About Us - My Company",
|
|
690
602
|
fr: "À Propos - Ma Société",
|
|
691
603
|
es: "Acerca de Nosotros - Mi Empresa",
|
|
692
604
|
}),
|
|
693
|
-
|
|
694
|
-
zh: "了解更多关于我们公司和我们的使命",
|
|
605
|
+
metaDescription: t({
|
|
695
606
|
en: "Learn more about our company and our mission",
|
|
696
607
|
fr: "En savoir plus sur notre société et notre mission",
|
|
697
608
|
es: "Conozca más sobre nuestra empresa y nuestra misión",
|
|
698
609
|
}),
|
|
610
|
+
title: t({
|
|
611
|
+
en: "About Us",
|
|
612
|
+
fr: "À Propos",
|
|
613
|
+
es: "Acerca de Nosotros",
|
|
614
|
+
}),
|
|
699
615
|
},
|
|
700
616
|
};
|
|
701
617
|
|
|
702
|
-
module.exports =
|
|
618
|
+
module.exports = aboutPageContent;
|
|
703
619
|
```
|
|
704
620
|
|
|
705
|
-
```json fileName="pages/about-
|
|
621
|
+
```json fileName="pages/about-page.content.json" contentDeclarationFormat="json"
|
|
706
622
|
{
|
|
707
|
-
"
|
|
623
|
+
"$schema": "https://intlayer.org/schema.json",
|
|
624
|
+
"key": "about-page",
|
|
708
625
|
"content": {
|
|
709
|
-
"
|
|
626
|
+
"metaTitle": {
|
|
710
627
|
"nodeType": "translation",
|
|
711
|
-
"
|
|
712
|
-
"zh": "关于我们 - 我的公司",
|
|
628
|
+
"translation": {
|
|
713
629
|
"en": "About Us - My Company",
|
|
714
630
|
"fr": "À Propos - Ma Société",
|
|
715
631
|
"es": "Acerca de Nosotros - Mi Empresa"
|
|
716
632
|
}
|
|
717
633
|
},
|
|
718
|
-
"
|
|
634
|
+
"metaDescription": {
|
|
719
635
|
"nodeType": "translation",
|
|
720
|
-
"
|
|
721
|
-
"zh": "了解更多关于我们公司和我们的使命",
|
|
636
|
+
"translation": {
|
|
722
637
|
"en": "Learn more about our company and our mission",
|
|
723
638
|
"fr": "En savoir plus sur notre société et notre mission",
|
|
724
639
|
"es": "Conozca más sobre nuestra empresa y nuestra misión"
|
|
725
640
|
}
|
|
641
|
+
},
|
|
642
|
+
"title": {
|
|
643
|
+
"nodeType": "translation",
|
|
644
|
+
"translation": {
|
|
645
|
+
"en": "About Us",
|
|
646
|
+
"fr": "À Propos",
|
|
647
|
+
"es": "Acerca de Nosotros"
|
|
648
|
+
}
|
|
726
649
|
}
|
|
727
650
|
}
|
|
728
651
|
}
|
|
@@ -779,5 +702,3 @@ For more details on how to use the extension, refer to the [Intlayer VS Code Ext
|
|
|
779
702
|
### Go Further
|
|
780
703
|
|
|
781
704
|
To go further, you can implement the [visual editor](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/intlayer_visual_editor.md) or externalize your content using the [CMS](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/intlayer_CMS.md).
|
|
782
|
-
|
|
783
|
-
---
|