Skip to content

Commit 64aa732

Browse files
committed
feat: add i18n support (en, es, de)
1 parent da368c7 commit 64aa732

File tree

8 files changed

+757
-0
lines changed

8 files changed

+757
-0
lines changed

app/pages/index.vue

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,38 @@ function removeCategory(categoryId: string) {
3636
/>
3737
</div>
3838

39+
<!-- Language Selector -->
40+
<DevOnly>
41+
<div fixed top-16 right-16 z-50>
42+
<SelectRoot v-model="$i18n.locale.value">
43+
<SelectTrigger
44+
bg-white outline="~ 1.5 neutral-300" rounded-8 shadow-sm cursor-pointer
45+
flex="~ items-center gap-8" f-px-md py-8 text-f-sm font-medium
46+
>
47+
<SelectValue placeholder="Language" />
48+
<Icon name="i-tabler:chevron-down" />
49+
</SelectTrigger>
50+
<SelectContent
51+
position="popper" bg-white outline="~ 1.5 neutral-200" rounded-8
52+
shadow z-50 max-h-256 of-auto
53+
>
54+
<SelectViewport f-p-xs>
55+
<SelectItem
56+
v-for="locale in $i18n.locales.value"
57+
:key="locale.code"
58+
:value="locale.code"
59+
flex="~ items-center gap-8" text="f-sm neutral-800 data-[highlighted]:neutral-900"
60+
bg="data-[highlighted]:neutral-50" py-10 outline-none rounded-6 cursor-pointer
61+
transition-colors f-px-md
62+
>
63+
{{ locale.name }}
64+
</SelectItem>
65+
</SelectViewport>
66+
</SelectContent>
67+
</SelectRoot>
68+
</div>
69+
</DevOnly>
70+
3971
<div mx-auto max-w-640 relative z-1 f-px-md>
4072
<div f-mb-2xl>
4173
<h1 text="neutral-900 f-2xl" font-bold f-mb-xs>

i18n/locales/de.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"welcome": "Willkommen"
3+
}

i18n/locales/en.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"welcome": "Welcome"
3+
}

i18n/locales/es.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"welcome": "Bienvenido"
3+
}

nuxt.config.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export default defineNuxtConfig({
1414
'@nuxt/icon',
1515
'reka-ui/nuxt',
1616
'@nuxt/image',
17+
'@nuxtjs/i18n',
1718
],
1819
hub: {
1920
database: true,
@@ -50,5 +51,21 @@ export default defineNuxtConfig({
5051
collections: ['tabler'],
5152
customCollections: [nimiqIcons],
5253
},
54+
i18n: {
55+
locales: [
56+
{ code: 'en', language: 'en-US', name: 'English', file: 'en.json' },
57+
{ code: 'es', language: 'es-ES', name: 'Español', file: 'es.json' },
58+
{ code: 'de', language: 'de-DE', name: 'Deutsch', file: 'de.json' },
59+
],
60+
defaultLocale: 'en',
61+
lazy: true,
62+
langDir: 'locales',
63+
strategy: 'no_prefix',
64+
detectBrowserLanguage: {
65+
useCookie: true,
66+
cookieKey: 'i18n_redirected',
67+
redirectOn: 'root',
68+
},
69+
},
5370
compatibilityDate: '2025-10-01',
5471
})

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"@nuxt/icon": "catalog:",
2424
"@nuxt/image": "catalog:",
2525
"@nuxthub/core": "catalog:",
26+
"@nuxtjs/i18n": "^10.1.0",
2627
"@vueuse/nuxt": "catalog:",
2728
"consola": "catalog:",
2829
"drizzle-orm": "catalog:",

plugins/i18n-locale.client.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
export default defineNuxtPlugin(() => {
2+
const { locale, locales } = useI18n()
3+
const route = useRoute()
4+
5+
// Get available locale codes
6+
const availableLocales = (locales.value as { code: string }[]).map(l => l.code)
7+
8+
// Check for locale in query param
9+
const queryLocale = route.query.locale as string | undefined
10+
11+
if (queryLocale && availableLocales.includes(queryLocale)) {
12+
// If query param exists and is valid, save to localStorage and set locale
13+
localStorage.setItem('user-locale', queryLocale)
14+
locale.value = queryLocale
15+
}
16+
else {
17+
// Check localStorage for saved locale
18+
const savedLocale = localStorage.getItem('user-locale')
19+
if (savedLocale && availableLocales.includes(savedLocale)) {
20+
locale.value = savedLocale
21+
}
22+
}
23+
24+
// Watch for query param changes
25+
watch(() => route.query.locale, (newLocale) => {
26+
if (newLocale && typeof newLocale === 'string' && availableLocales.includes(newLocale)) {
27+
localStorage.setItem('user-locale', newLocale)
28+
locale.value = newLocale
29+
}
30+
})
31+
32+
// Watch for locale changes (including from selector) and save to localStorage
33+
watch(locale, (newLocale) => {
34+
if (newLocale && availableLocales.includes(newLocale)) {
35+
localStorage.setItem('user-locale', newLocale)
36+
}
37+
})
38+
})

0 commit comments

Comments
 (0)