@intlayer/docs 7.3.11 → 7.3.13
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 +294 -438
- 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 +284 -410
- 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 +237 -341
- 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 +254 -378
- 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 +271 -390
- 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 +278 -405
- 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 +303 -447
- 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 +266 -395
- 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 +299 -423
- 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 +309 -432
- 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 +295 -422
- 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 +273 -476
- 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 +277 -420
- 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 +287 -425
- 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 +313 -406
- 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 +273 -418
- 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 +300 -461
- 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 +10 -11
- 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,15 @@ 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=nhUcUAVQ6eQ
|
|
18
19
|
history:
|
|
20
|
+
- version: 7.3.13
|
|
21
|
+
date: 2025-12-08
|
|
22
|
+
changes: Unused TypeScript configuration
|
|
23
|
+
- version: 7.3.11
|
|
24
|
+
date: 2025-12-07
|
|
25
|
+
changes: Update LocaleSwitcher, SEO, metadata
|
|
19
26
|
- version: 5.5.10
|
|
20
27
|
date: 2025-06-29
|
|
21
28
|
changes: Init history
|
|
@@ -23,8 +30,6 @@ history:
|
|
|
23
30
|
|
|
24
31
|
# Translate your Nuxt and Vue website using Intlayer | Internationalization (i18n)
|
|
25
32
|
|
|
26
|
-
See [Application Template](https://github.com/aymericzip/intlayer-nuxt-template) on GitHub.
|
|
27
|
-
|
|
28
33
|
## Table of Contents
|
|
29
34
|
|
|
30
35
|
<TOC/>
|
|
@@ -44,14 +49,27 @@ With Intlayer, you can:
|
|
|
44
49
|
|
|
45
50
|
## Step-by-Step Guide to Set Up Intlayer in a Nuxt Application
|
|
46
51
|
|
|
52
|
+
<Tab defaultTab="video">
|
|
53
|
+
<TabItem label="Video" value="video">
|
|
54
|
+
|
|
55
|
+
<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/nhUcUAVQ6eQ?autoplay=0&origin=http://intlayer.org&controls=0&rel=1"/>
|
|
56
|
+
|
|
57
|
+
</TabItem>
|
|
58
|
+
<TabItem label="Code" value="code">
|
|
59
|
+
|
|
47
60
|
<iframe
|
|
48
|
-
src="https://stackblitz.com/github/aymericzip/intlayer-nuxt-template?embed=1&ctl=1&file=intlayer.config.ts"
|
|
61
|
+
src="https://stackblitz.com/github/aymericzip/intlayer-nuxt-4-template?embed=1&ctl=1&file=intlayer.config.ts"
|
|
49
62
|
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
63
|
title="Demo CodeSandbox - How to Internationalize your application using Intlayer"
|
|
51
64
|
sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
|
|
52
65
|
loading="lazy"
|
|
53
66
|
/>
|
|
54
67
|
|
|
68
|
+
</TabItem>
|
|
69
|
+
</Tab>
|
|
70
|
+
|
|
71
|
+
See [Application Template](https://github.com/aymericzip/intlayer-nuxt-4-template) on GitHub.
|
|
72
|
+
|
|
55
73
|
### Step 1: Install Dependencies
|
|
56
74
|
|
|
57
75
|
Install the necessary packages using npm:
|
|
@@ -98,9 +116,6 @@ const config: IntlayerConfig = {
|
|
|
98
116
|
],
|
|
99
117
|
defaultLocale: Locales.ENGLISH,
|
|
100
118
|
},
|
|
101
|
-
content: {
|
|
102
|
-
contentDir: ["."], // Because by default Intayer will watch content declaration files from the `./src` directory
|
|
103
|
-
},
|
|
104
119
|
};
|
|
105
120
|
|
|
106
121
|
export default config;
|
|
@@ -120,9 +135,6 @@ const config = {
|
|
|
120
135
|
],
|
|
121
136
|
defaultLocale: Locales.ENGLISH,
|
|
122
137
|
},
|
|
123
|
-
content: {
|
|
124
|
-
contentDir: ["."],
|
|
125
|
-
},
|
|
126
138
|
};
|
|
127
139
|
|
|
128
140
|
export default config;
|
|
@@ -142,9 +154,6 @@ const config = {
|
|
|
142
154
|
],
|
|
143
155
|
defaultLocale: Locales.ENGLISH,
|
|
144
156
|
},
|
|
145
|
-
content: {
|
|
146
|
-
contentDir: ["."],
|
|
147
|
-
},
|
|
148
157
|
};
|
|
149
158
|
|
|
150
159
|
module.exports = config;
|
|
@@ -171,188 +180,31 @@ export default defineNuxtConfig({
|
|
|
171
180
|
|
|
172
181
|
Create and manage your content declarations to store translations:
|
|
173
182
|
|
|
174
|
-
```tsx fileName="
|
|
175
|
-
import {
|
|
183
|
+
```tsx fileName="content/home-page.content.ts" contentDeclarationFormat="typescript"
|
|
184
|
+
import { type Dictionary, t } from "intlayer";
|
|
176
185
|
|
|
177
|
-
const
|
|
178
|
-
key: "
|
|
186
|
+
const content = {
|
|
187
|
+
key: "home-page",
|
|
179
188
|
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 ",
|
|
189
|
+
title: t({
|
|
190
|
+
en: "Hello world",
|
|
191
|
+
fr: "Bonjour le monde",
|
|
192
|
+
es: "Hola mundo",
|
|
196
193
|
}),
|
|
197
|
-
|
|
198
|
-
en: "
|
|
199
|
-
fr: "
|
|
200
|
-
es: "
|
|
194
|
+
metaTitle: t({
|
|
195
|
+
en: "Welcome | My Application",
|
|
196
|
+
fr: "Bienvenue | Mon Application",
|
|
197
|
+
es: "Bienvenido | Mi Aplicación",
|
|
201
198
|
}),
|
|
202
|
-
|
|
203
|
-
en: "
|
|
204
|
-
fr: "
|
|
205
|
-
es: "
|
|
199
|
+
metaDescription: t({
|
|
200
|
+
en: "Discover your multilingual Nuxt app homepage powered by Intlayer.",
|
|
201
|
+
fr: "Découvrez la page d'accueil multilingue de votre application Nuxt propulsée par Intlayer.",
|
|
202
|
+
es: "Descubre la página de inicio multilingüe de tu aplicación Nuxt impulsada por Intlayer.",
|
|
206
203
|
}),
|
|
207
204
|
},
|
|
208
205
|
} satisfies Dictionary;
|
|
209
206
|
|
|
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
|
-
}
|
|
207
|
+
export default content;
|
|
356
208
|
```
|
|
357
209
|
|
|
358
210
|
> 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 +284,54 @@ Intlayer offers different APIs to access your content:
|
|
|
432
284
|
|
|
433
285
|
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
286
|
|
|
435
|
-
Create a component to switch between languages:
|
|
287
|
+
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
288
|
|
|
437
289
|
```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
290
|
<script setup lang="ts">
|
|
449
|
-
import {
|
|
450
|
-
import { getLocaleName } from "intlayer";
|
|
291
|
+
import { getLocaleName, getLocalizedUrl } from "intlayer";
|
|
451
292
|
import { useLocale } from "vue-intlayer";
|
|
452
293
|
|
|
453
|
-
//
|
|
294
|
+
// Nuxt auto-imports useRoute
|
|
295
|
+
const route = useRoute();
|
|
454
296
|
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
297
|
</script>
|
|
298
|
+
|
|
299
|
+
<template>
|
|
300
|
+
<nav class="locale-switcher">
|
|
301
|
+
<NuxtLink
|
|
302
|
+
v-for="localeEl in availableLocales"
|
|
303
|
+
:key="localeEl"
|
|
304
|
+
:to="getLocalizedUrl(route.fullPath, localeEl)"
|
|
305
|
+
class="locale-link"
|
|
306
|
+
:class="{ 'active-locale': localeEl === locale }"
|
|
307
|
+
@click="setLocale(localeEl)"
|
|
308
|
+
>
|
|
309
|
+
{{ getLocaleName(localeEl) }}
|
|
310
|
+
</NuxtLink>
|
|
311
|
+
</nav>
|
|
470
312
|
</template>
|
|
313
|
+
```
|
|
471
314
|
|
|
472
|
-
|
|
473
|
-
.locale-switcher {
|
|
474
|
-
margin: 1rem 0;
|
|
475
|
-
}
|
|
315
|
+
> 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
316
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
317
|
+
Then, set up your `app.vue` to use layouts:
|
|
318
|
+
|
|
319
|
+
```vue fileName="app.vue"
|
|
320
|
+
<template>
|
|
321
|
+
<NuxtLayout>
|
|
322
|
+
<NuxtPage />
|
|
323
|
+
</NuxtLayout>
|
|
324
|
+
</template>
|
|
483
325
|
```
|
|
484
326
|
|
|
485
|
-
|
|
327
|
+
### (Optional) Step 6b: Create a Layout with Navigation
|
|
486
328
|
|
|
487
|
-
|
|
329
|
+
Nuxt layouts allow you to define a common structure for your pages. Create a default layout that includes the locale switcher and navigation:
|
|
330
|
+
|
|
331
|
+
```vue fileName="layouts/default.vue"
|
|
488
332
|
<script setup lang="ts">
|
|
489
|
-
import
|
|
333
|
+
import Links from "~/components/Links.vue";
|
|
490
334
|
import LocaleSwitcher from "~/components/LocaleSwitcher.vue";
|
|
491
|
-
|
|
492
|
-
const content = useIntlayer("app"); // Create related intlayer declaration file
|
|
493
335
|
</script>
|
|
494
336
|
|
|
495
337
|
<template>
|
|
@@ -498,12 +340,17 @@ const content = useIntlayer("app"); // Create related intlayer declaration file
|
|
|
498
340
|
<LocaleSwitcher />
|
|
499
341
|
</header>
|
|
500
342
|
<main>
|
|
501
|
-
<
|
|
343
|
+
<slot />
|
|
502
344
|
</main>
|
|
345
|
+
|
|
346
|
+
<Links href="/">Home</Links>
|
|
347
|
+
<Links href="/about">About</Links>
|
|
503
348
|
</div>
|
|
504
349
|
</template>
|
|
505
350
|
```
|
|
506
351
|
|
|
352
|
+
The `Links` component (shown below) ensures that internal navigation links are automatically localized.
|
|
353
|
+
|
|
507
354
|
### (Optional) Step 7: Add localized Routing to your application
|
|
508
355
|
|
|
509
356
|
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 +365,58 @@ pages/
|
|
|
518
365
|
└── index.vue → /contact, /fr/contact, /es/contact
|
|
519
366
|
```
|
|
520
367
|
|
|
521
|
-
To create
|
|
368
|
+
To create localized pages, simply create your Vue files in the `pages/` directory. Here are two example pages:
|
|
369
|
+
|
|
370
|
+
**Home page (`pages/index.vue`):**
|
|
371
|
+
|
|
372
|
+
```vue fileName="pages/index.vue"
|
|
373
|
+
<script setup lang="ts">
|
|
374
|
+
import { useIntlayer } from "vue-intlayer";
|
|
375
|
+
|
|
376
|
+
const content = useIntlayer("home-page");
|
|
377
|
+
|
|
378
|
+
useHead({
|
|
379
|
+
title: content.metaTitle.value,
|
|
380
|
+
meta: [
|
|
381
|
+
{
|
|
382
|
+
name: "description",
|
|
383
|
+
content: content.metaDescription.value,
|
|
384
|
+
},
|
|
385
|
+
],
|
|
386
|
+
});
|
|
387
|
+
</script>
|
|
388
|
+
|
|
389
|
+
<template>
|
|
390
|
+
<h1><content.title /></h1>
|
|
391
|
+
</template>
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
**About page (`pages/about.vue`):**
|
|
522
395
|
|
|
523
396
|
```vue fileName="pages/about.vue"
|
|
524
397
|
<script setup lang="ts">
|
|
525
398
|
import { useIntlayer } from "vue-intlayer";
|
|
526
399
|
|
|
527
|
-
const content = useIntlayer("about");
|
|
400
|
+
const content = useIntlayer("about-page");
|
|
401
|
+
|
|
402
|
+
useHead({
|
|
403
|
+
title: content.metaTitle.raw, // Use .raw for primitive string access
|
|
404
|
+
meta: [
|
|
405
|
+
{
|
|
406
|
+
name: "description",
|
|
407
|
+
content: content.metaDescription.raw, // Use .raw for primitive string access
|
|
408
|
+
},
|
|
409
|
+
],
|
|
410
|
+
});
|
|
528
411
|
</script>
|
|
529
412
|
|
|
530
413
|
<template>
|
|
531
|
-
<
|
|
532
|
-
<h1>{{ content.title }}</h1>
|
|
533
|
-
<p>{{ content.description }}</p>
|
|
534
|
-
</div>
|
|
414
|
+
<h1><content.title /></h1>
|
|
535
415
|
</template>
|
|
536
416
|
```
|
|
537
417
|
|
|
418
|
+
> Note: `useHead` is auto-imported in Nuxt. You can access content values using either `.value` (reactive) or `.raw` (primitive string) depending on your needs.
|
|
419
|
+
|
|
538
420
|
The `nuxt-intlayer` module will automatically:
|
|
539
421
|
|
|
540
422
|
- Detect the user's preferred locale
|
|
@@ -545,209 +427,225 @@ The `nuxt-intlayer` module will automatically:
|
|
|
545
427
|
|
|
546
428
|
### (Optional) Step 8: Creating a Localized Link Component
|
|
547
429
|
|
|
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>
|
|
430
|
+
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
431
|
|
|
432
|
+
```vue fileName="components/Links.vue"
|
|
557
433
|
<script setup lang="ts">
|
|
558
|
-
import { computed } from "vue";
|
|
559
434
|
import { getLocalizedUrl } from "intlayer";
|
|
560
435
|
import { useLocale } from "vue-intlayer";
|
|
561
436
|
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
},
|
|
567
|
-
});
|
|
437
|
+
interface Props {
|
|
438
|
+
href: string;
|
|
439
|
+
locale?: string;
|
|
440
|
+
}
|
|
568
441
|
|
|
569
|
-
const
|
|
442
|
+
const props = defineProps<Props>();
|
|
570
443
|
|
|
571
|
-
|
|
572
|
-
const isExternalLink = computed(() => /^https?:\/\//.test(props.to || ""));
|
|
444
|
+
const { locale: currentLocale } = useLocale();
|
|
573
445
|
|
|
574
|
-
//
|
|
575
|
-
const
|
|
576
|
-
|
|
577
|
-
);
|
|
446
|
+
// Compute the final path
|
|
447
|
+
const finalPath = computed(() => {
|
|
448
|
+
// 1. Check if the link is external
|
|
449
|
+
const isExternal = /^https?:\/\//.test(props.href || "");
|
|
450
|
+
|
|
451
|
+
// 2. If external, return as is (NuxtLink handles the <a> tag generation)
|
|
452
|
+
if (isExternal) return props.href;
|
|
453
|
+
|
|
454
|
+
// 3. If internal, localize the URL
|
|
455
|
+
const targetLocale = props.locale || currentLocale.value;
|
|
456
|
+
return getLocalizedUrl(props.href, targetLocale);
|
|
457
|
+
});
|
|
578
458
|
</script>
|
|
459
|
+
|
|
460
|
+
<template>
|
|
461
|
+
<NuxtLink :to="finalPath" v-bind="$attrs">
|
|
462
|
+
<slot />
|
|
463
|
+
</NuxtLink>
|
|
464
|
+
</template>
|
|
579
465
|
```
|
|
580
466
|
|
|
581
467
|
Then use this component throughout your application:
|
|
582
468
|
|
|
583
|
-
```vue fileName="
|
|
469
|
+
```vue fileName="layouts/default.vue"
|
|
470
|
+
<script setup lang="ts">
|
|
471
|
+
import Links from "~/components/Links.vue";
|
|
472
|
+
import LocaleSwitcher from "~/components/LocaleSwitcher.vue";
|
|
473
|
+
</script>
|
|
474
|
+
|
|
584
475
|
<template>
|
|
585
476
|
<div>
|
|
586
|
-
<
|
|
587
|
-
|
|
588
|
-
</
|
|
589
|
-
<
|
|
590
|
-
|
|
591
|
-
</
|
|
477
|
+
<header>
|
|
478
|
+
<LocaleSwitcher />
|
|
479
|
+
</header>
|
|
480
|
+
<main>
|
|
481
|
+
<slot />
|
|
482
|
+
</main>
|
|
483
|
+
|
|
484
|
+
<Links href="/">Home</Links>
|
|
485
|
+
<Links href="/about">About</Links>
|
|
592
486
|
</div>
|
|
593
487
|
</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
488
|
```
|
|
602
489
|
|
|
490
|
+
> By using `NuxtLink` with localized paths, you ensure that:
|
|
491
|
+
>
|
|
492
|
+
> - Search engines can crawl and index all language versions of your pages
|
|
493
|
+
> - Users can share localized URLs directly
|
|
494
|
+
> - Browser history works correctly with locale-prefixed URLs
|
|
495
|
+
|
|
603
496
|
### (Optional) Step 9: Handle Metadata and SEO
|
|
604
497
|
|
|
605
|
-
Nuxt provides excellent SEO capabilities. You can use Intlayer to handle localized metadata:
|
|
498
|
+
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
499
|
|
|
607
500
|
```vue fileName="pages/about.vue"
|
|
608
501
|
<script setup lang="ts">
|
|
609
|
-
import {
|
|
610
|
-
import { getIntlayer } from "intlayer";
|
|
611
|
-
import { useLocale } from "vue-intlayer";
|
|
502
|
+
import { useIntlayer } from "vue-intlayer";
|
|
612
503
|
|
|
613
|
-
|
|
614
|
-
const content =
|
|
504
|
+
// useHead is auto-imported in Nuxt
|
|
505
|
+
const content = useIntlayer("about-page");
|
|
615
506
|
|
|
616
|
-
|
|
617
|
-
title: content.
|
|
618
|
-
|
|
507
|
+
useHead({
|
|
508
|
+
title: content.metaTitle.raw, // Use .raw for primitive string access
|
|
509
|
+
meta: [
|
|
510
|
+
{
|
|
511
|
+
name: "description",
|
|
512
|
+
content: content.metaDescription.raw, // Use .raw for primitive string access
|
|
513
|
+
},
|
|
514
|
+
],
|
|
619
515
|
});
|
|
620
516
|
</script>
|
|
621
517
|
|
|
622
518
|
<template>
|
|
623
|
-
<
|
|
624
|
-
<h1>{{ content.pageTitle }}</h1>
|
|
625
|
-
<p>{{ content.pageContent }}</p>
|
|
626
|
-
</div>
|
|
519
|
+
<h1><content.title /></h1>
|
|
627
520
|
</template>
|
|
628
521
|
```
|
|
629
522
|
|
|
523
|
+
> Alternatively, you can use the `import { getIntlayer } from "intlayer"` function to get the content without Vue reactivity.
|
|
524
|
+
|
|
525
|
+
> **Accessing content values:**
|
|
526
|
+
>
|
|
527
|
+
> - Use `.raw` to get the primitive string value (non-reactive)
|
|
528
|
+
> - Use `.value` to get the reactive value
|
|
529
|
+
> - Use `<content.key />` component syntax for Visual Editor support
|
|
530
|
+
|
|
630
531
|
Create the corresponding content declaration:
|
|
631
532
|
|
|
632
|
-
```ts fileName="pages/about-
|
|
533
|
+
```ts fileName="pages/about-page.content.ts" contentDeclarationFormat="typescript"
|
|
633
534
|
import { t, type Dictionary } from "intlayer";
|
|
634
|
-
import type { useSeoMeta } from "nuxt/app";
|
|
635
535
|
|
|
636
|
-
const
|
|
637
|
-
key: "about-
|
|
536
|
+
const aboutPageContent = {
|
|
537
|
+
key: "about-page",
|
|
638
538
|
content: {
|
|
639
|
-
|
|
539
|
+
metaTitle: t({
|
|
640
540
|
en: "About Us - My Company",
|
|
641
541
|
fr: "À Propos - Ma Société",
|
|
642
542
|
es: "Acerca de Nosotros - Mi Empresa",
|
|
643
543
|
}),
|
|
644
|
-
|
|
544
|
+
metaDescription: t({
|
|
645
545
|
en: "Learn more about our company and our mission",
|
|
646
546
|
fr: "En savoir plus sur notre société et notre mission",
|
|
647
547
|
es: "Conozca más sobre nuestra empresa y nuestra misión",
|
|
648
548
|
}),
|
|
549
|
+
title: t({
|
|
550
|
+
en: "About Us",
|
|
551
|
+
fr: "À Propos",
|
|
552
|
+
es: "Acerca de Nosotros",
|
|
553
|
+
}),
|
|
649
554
|
},
|
|
650
|
-
} satisfies Dictionary
|
|
555
|
+
} satisfies Dictionary;
|
|
651
556
|
|
|
652
|
-
export default
|
|
557
|
+
export default aboutPageContent;
|
|
653
558
|
```
|
|
654
559
|
|
|
655
|
-
```
|
|
560
|
+
```javascript fileName="pages/about-page.content.mjs" contentDeclarationFormat="esm"
|
|
656
561
|
import { t } from "intlayer";
|
|
657
562
|
|
|
658
563
|
/** @type {import('intlayer').Dictionary} */
|
|
659
|
-
const
|
|
660
|
-
key: "about-
|
|
564
|
+
const aboutPageContent = {
|
|
565
|
+
key: "about-page",
|
|
661
566
|
content: {
|
|
662
|
-
|
|
663
|
-
zh: "关于我们 - 我的公司",
|
|
567
|
+
metaTitle: t({
|
|
664
568
|
en: "About Us - My Company",
|
|
665
569
|
fr: "À Propos - Ma Société",
|
|
666
570
|
es: "Acerca de Nosotros - Mi Empresa",
|
|
667
571
|
}),
|
|
668
|
-
|
|
669
|
-
zh: "了解更多关于我们公司和我们的使命",
|
|
572
|
+
metaDescription: t({
|
|
670
573
|
en: "Learn more about our company and our mission",
|
|
671
574
|
fr: "En savoir plus sur notre société et notre mission",
|
|
672
575
|
es: "Conozca más sobre nuestra empresa y nuestra misión",
|
|
673
576
|
}),
|
|
577
|
+
title: t({
|
|
578
|
+
en: "About Us",
|
|
579
|
+
fr: "À Propos",
|
|
580
|
+
es: "Acerca de Nosotros",
|
|
581
|
+
}),
|
|
674
582
|
},
|
|
675
583
|
};
|
|
676
584
|
|
|
677
|
-
export default
|
|
585
|
+
export default aboutPageContent;
|
|
678
586
|
```
|
|
679
587
|
|
|
680
|
-
```
|
|
588
|
+
```javascript fileName="pages/about-page.content.cjs" contentDeclarationFormat="commonjs"
|
|
681
589
|
const { t } = require("intlayer");
|
|
682
590
|
|
|
683
591
|
/** @type {import('intlayer').Dictionary} */
|
|
684
|
-
const
|
|
685
|
-
key: "about-
|
|
592
|
+
const aboutPageContent = {
|
|
593
|
+
key: "about-page",
|
|
686
594
|
content: {
|
|
687
|
-
|
|
688
|
-
zh: "关于我们 - 我的公司",
|
|
595
|
+
metaTitle: t({
|
|
689
596
|
en: "About Us - My Company",
|
|
690
597
|
fr: "À Propos - Ma Société",
|
|
691
598
|
es: "Acerca de Nosotros - Mi Empresa",
|
|
692
599
|
}),
|
|
693
|
-
|
|
694
|
-
zh: "了解更多关于我们公司和我们的使命",
|
|
600
|
+
metaDescription: t({
|
|
695
601
|
en: "Learn more about our company and our mission",
|
|
696
602
|
fr: "En savoir plus sur notre société et notre mission",
|
|
697
603
|
es: "Conozca más sobre nuestra empresa y nuestra misión",
|
|
698
604
|
}),
|
|
605
|
+
title: t({
|
|
606
|
+
en: "About Us",
|
|
607
|
+
fr: "À Propos",
|
|
608
|
+
es: "Acerca de Nosotros",
|
|
609
|
+
}),
|
|
699
610
|
},
|
|
700
611
|
};
|
|
701
612
|
|
|
702
|
-
module.exports =
|
|
613
|
+
module.exports = aboutPageContent;
|
|
703
614
|
```
|
|
704
615
|
|
|
705
|
-
```json fileName="pages/about-
|
|
616
|
+
```json fileName="pages/about-page.content.json" contentDeclarationFormat="json"
|
|
706
617
|
{
|
|
707
|
-
"
|
|
618
|
+
"$schema": "https://intlayer.org/schema.json",
|
|
619
|
+
"key": "about-page",
|
|
708
620
|
"content": {
|
|
709
|
-
"
|
|
621
|
+
"metaTitle": {
|
|
710
622
|
"nodeType": "translation",
|
|
711
|
-
"
|
|
712
|
-
"zh": "关于我们 - 我的公司",
|
|
623
|
+
"translation": {
|
|
713
624
|
"en": "About Us - My Company",
|
|
714
625
|
"fr": "À Propos - Ma Société",
|
|
715
626
|
"es": "Acerca de Nosotros - Mi Empresa"
|
|
716
627
|
}
|
|
717
628
|
},
|
|
718
|
-
"
|
|
629
|
+
"metaDescription": {
|
|
719
630
|
"nodeType": "translation",
|
|
720
|
-
"
|
|
721
|
-
"zh": "了解更多关于我们公司和我们的使命",
|
|
631
|
+
"translation": {
|
|
722
632
|
"en": "Learn more about our company and our mission",
|
|
723
633
|
"fr": "En savoir plus sur notre société et notre mission",
|
|
724
634
|
"es": "Conozca más sobre nuestra empresa y nuestra misión"
|
|
725
635
|
}
|
|
636
|
+
},
|
|
637
|
+
"title": {
|
|
638
|
+
"nodeType": "translation",
|
|
639
|
+
"translation": {
|
|
640
|
+
"en": "About Us",
|
|
641
|
+
"fr": "À Propos",
|
|
642
|
+
"es": "Acerca de Nosotros"
|
|
643
|
+
}
|
|
726
644
|
}
|
|
727
645
|
}
|
|
728
646
|
}
|
|
729
647
|
```
|
|
730
648
|
|
|
731
|
-
### Configure TypeScript
|
|
732
|
-
|
|
733
|
-
Intlayer uses module augmentation to get benefits of TypeScript and make your codebase stronger.
|
|
734
|
-
|
|
735
|
-

|
|
736
|
-
|
|
737
|
-

|
|
738
|
-
|
|
739
|
-
Ensure your TypeScript configuration includes the autogenerated types.
|
|
740
|
-
|
|
741
|
-
```json5 fileName="tsconfig.json"
|
|
742
|
-
{
|
|
743
|
-
// ... Your existing TypeScript configurations
|
|
744
|
-
"include": [
|
|
745
|
-
// ... Your existing TypeScript configurations
|
|
746
|
-
".intlayer/**/*.ts", // Include the auto-generated types
|
|
747
|
-
],
|
|
748
|
-
}
|
|
749
|
-
```
|
|
750
|
-
|
|
751
649
|
### Git Configuration
|
|
752
650
|
|
|
753
651
|
It is recommended to ignore the files generated by Intlayer. This allows you to avoid committing them to your Git repository.
|
|
@@ -779,5 +677,3 @@ For more details on how to use the extension, refer to the [Intlayer VS Code Ext
|
|
|
779
677
|
### Go Further
|
|
780
678
|
|
|
781
679
|
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
|
-
---
|