Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add i18n support #2

Merged
merged 11 commits into from
Jul 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
dist
node_modules
templates
3 changes: 2 additions & 1 deletion .npmrc
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
shamefully-hoist=true
shamefully-hoist=true
shell-emulator=true
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Zero-config Nuxt module for Vuetify
- 🛠️ **Versatile**: custom Vuetify [directives](https://vuetifyjs.com/en/getting-started/installation/#manual-steps) and [labs components](https://vuetifyjs.com/en/labs/introduction/) registration
- ✨ **Configurable styles**: configure your variables using [Vuetify SASS Variables](https://vuetifyjs.com/en/features/sass-variables/)
- 💥 **SSR**: automatic SSR detection and configuration
- 🌍 **I18n ready**: install [@nuxtjs/i18n](https://v8.i18n.nuxtjs.org/) Nuxt module, and you're ready to use Vuetify [internationalization](https://vuetifyjs.com/en/features/internationalization/) features
- 🦾 **Type Strong**: written in [TypeScript](https://www.typescriptlang.org/)

## 📦 Install
Expand Down Expand Up @@ -73,6 +74,18 @@ export default defineNuxtConfig({
})
```

## 🌍 I18n support

> Requires latest [@nuxtjs/i18n](https://v8.i18n.nuxtjs.org/) Nuxt module: `8.0.0.beta.12`.

There is a [bug](https://github.com/nuxt-modules/i18n/pull/2193) in the current version that prevents `@nuxtjs/i18n` module to work properly when using `lazy` i18n files.

If you're using `lazy` i18n files per locale, apply [this patch](./patches/@nuxtjs__i18n@8.0.0-beta.12.patch) to your project: check how to apply it when using `pnpm` in the root `package.json` file in this repo: [./package.json#L25-L26](./package.json#L97-L101).

You can check the playground folder, you can run it using single or multiple json files per locale:
- for single file per locale: run from root folder `pnpm install && nr dev:prepare && nr dev`
- for multiple files per locale: run from root folder `pnpm install && nr dev:prepare:multiple-json && nr dev:multiple-json`

<!--
Read the [📖 documentation](https://vite-pwa-org.netlify.app/frameworks/nuxt) for a complete guide on how to configure and use
this plugin.
Expand Down
19 changes: 14 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,19 @@
"*.d.ts"
],
"scripts": {
"prepack": "nuxt-module-build",
"build": "pnpm dev:prepare && nuxt-module-build",
"dev": "nuxi dev playground",
"dev:build": "nuxi build playground",
"dev:multiple-json": "MULTIPLE_LANG_FILES=true nuxi dev playground",
"dev:prepare": "nuxt-module-build --stub && nuxi prepare playground",
"prepublishOnly": "npm run prepack",
"release": "bumpp && npm publish",
"dev:prepare:multiple-json": "nuxt-module-build --stub && MULTIPLE_LANG_FILES=true nuxi prepare playground",
"dev:build": "nuxi build playground",
"dev:build:multiple-json": "MULTIPLE_LANG_FILES=true nuxi build playground",
"lint": "eslint .",
"lint:fix": "nr lint --fix",
"test": "vitest run",
"test:watch": "vitest watch"
"test:watch": "vitest watch",
"prepublishOnly": "pnpm build",
"release": "bumpp && npm publish"
},
"peerDependencies": {
"@nuxt/kit": "^3.5.3",
Expand All @@ -65,6 +68,7 @@
"@nuxt/module-builder": "^0.4.0",
"@nuxt/schema": "^3.6.1",
"@nuxt/test-utils": "^3.6.1",
"@nuxtjs/i18n": "^8.0.0-beta.12",
"@parcel/watcher": "^2.1.0",
"@types/node": "^18",
"bumpp": "^9.1.1",
Expand All @@ -90,5 +94,10 @@
"vite-plugin-vuetify",
"vuetify"
]
},
"pnpm": {
"patchedDependencies": {
"@nuxtjs/i18n@8.0.0-beta.12": "patches/@nuxtjs__i18n@8.0.0-beta.12.patch"
}
}
}
13 changes: 13 additions & 0 deletions patches/@nuxtjs__i18n@8.0.0-beta.12.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
diff --git a/dist/runtime/utils.mjs b/dist/runtime/utils.mjs
index 21f7780ac104abddfd5c8038e1574bc0e3212cf6..b2e7584912c9eede4d152f0ddaeb70ac917ec828 100644
--- a/dist/runtime/utils.mjs
+++ b/dist/runtime/utils.mjs
@@ -77,7 +77,7 @@ export async function loadInitialMessages(context, messages, options) {
const fallbackLocales = makeFallbackLocaleCodes(fallbackLocale, [defaultLocale, initialLocale]);
await Promise.all(fallbackLocales.map((locale) => loadLocale(context, locale, setter)));
}
- const locales = lazy ? [...(/* @__PURE__ */ new Set()).add(defaultLocale).add(initialLocale)] : localeCodes;
+ const locales = lazy ? localeCodes : [...(/* @__PURE__ */ new Set()).add(defaultLocale).add(initialLocale)];
await Promise.all(locales.map((locale) => loadLocale(context, locale, setter)));
}
return messages;
12 changes: 12 additions & 0 deletions playground/config/i18n.config.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { availableLocales } from './i18n'

export default defineI18nConfig(() => {
return {
legacy: false,
availableLocales: availableLocales.map(l => l.code),
fallbackLocale: 'en-US',
fallbackWarn: true,
// eslint-disable-next-line @typescript-eslint/comma-dangle
missingWarn: true
}
})
89 changes: 89 additions & 0 deletions playground/config/i18n.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import type { LocaleObject } from '#i18n'

const multipleJson = process.env.MULTIPLE_LANG_FILES === 'true'

const countryLocaleVariants: Record<string, LocaleObject[]> = {
en: [
// en.json contains en-US translations
{ code: 'en-US', name: 'English (US)' },
{ code: 'en-GB', name: 'English (UK)' },
],
es: [
// es.json contains es-ES translations
// { code: 'es-AR', name: 'Español (Argentina)' },
// { code: 'es-BO', name: 'Español (Bolivia)' },
// { code: 'es-CL', name: 'Español (Chile)' },
// { code: 'es-CO', name: 'Español (Colombia)' },
// { code: 'es-CR', name: 'Español (Costa Rica)' },
// { code: 'es-DO', name: 'Español (República Dominicana)' },
// { code: 'es-EC', name: 'Español (Ecuador)' },
{ code: 'es-ES', name: 'Español (España)' },
// TODO: Support es-419, if we include spanish country variants remove also fix on utils/language.ts module
{ code: 'es-419', name: 'Español (Latinoamérica)' },
// { code: 'es-GT', name: 'Español (Guatemala)' },
// { code: 'es-HN', name: 'Español (Honduras)' },
// { code: 'es-MX', name: 'Español (México)' },
// { code: 'es-NI', name: 'Español (Nicaragua)' },
// { code: 'es-PA', name: 'Español (Panamá)' },
// { code: 'es-PE', name: 'Español (Perú)' },
// { code: 'es-PR', name: 'Español (Puerto Rico)' },
// { code: 'es-SV', name: 'Español (El Salvador)' },
// { code: 'es-US', name: 'Español (Estados Unidos)' },
// { code: 'es-UY', name: 'Español (Uruguay)' },
// { code: 'es-VE', name: 'Español (Venezuela)' },
],
}

const locales: LocaleObject[] = [
{
code: 'en',
file: 'en.json',
name: 'English',
},
{
code: 'es',
file: 'es.json',
name: 'Español',
},
]

function buildLocales() {
const useLocales = Object.values(locales).reduce((acc, data) => {
const locales = countryLocaleVariants[data.code]
if (locales) {
locales.forEach((l) => {
let entry: LocaleObject
if (multipleJson) {
entry = {
...data,
code: l.code,
name: l.name,
files: [data.file!, `${l.code}.json`],
}
delete entry.file
}
else {
entry = {
...data,
code: l.code,
name: l.name,
file: `${l.code}.json`,
}
}

acc.push(entry)
})
}
else {
acc.push(data)
}

return acc
}, <LocaleObject[]>[])

return useLocales.sort((a, b) => a.code.localeCompare(b.code))
}

export const availableLocales = buildLocales()

export const langDir = multipleJson ? 'locales/multiple' : 'locales/single'
16 changes: 8 additions & 8 deletions playground/layouts/default.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@
</script>

<template>
<VContainer>
<VRow>
<VCol>
<VSheet
<v-container>
<v-row>
<v-col>
<v-sheet
class="pa-4 d-flex align-center flex-column"
color="grey-lighten-3"
rounded="lg"
>
<NuxtPage />
</VSheet>
</VCol>
</VRow>
</VContainer>
</v-sheet>
</v-col>
</v-row>
</v-container>
</template>
3 changes: 3 additions & 0 deletions playground/locales/multiple/en-GB.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"xxx": "Favourite"
}
3 changes: 3 additions & 0 deletions playground/locales/multiple/en-US.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"xxx": "Favorite"
}
3 changes: 3 additions & 0 deletions playground/locales/multiple/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"xxx": "Favorite"
}
1 change: 1 addition & 0 deletions playground/locales/multiple/es-419.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
1 change: 1 addition & 0 deletions playground/locales/multiple/es-ES.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
3 changes: 3 additions & 0 deletions playground/locales/multiple/es.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"xxx": "Favorito"
}
3 changes: 3 additions & 0 deletions playground/locales/single/en-GB.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"xxx": "Favourite"
}
3 changes: 3 additions & 0 deletions playground/locales/single/en-US.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"xxx": "Favorite"
}
3 changes: 3 additions & 0 deletions playground/locales/single/es-419.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"xxx": "Favorito (es-419)"
}
3 changes: 3 additions & 0 deletions playground/locales/single/es-ES.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"xxx": "Favorito (es-ES)"
}
19 changes: 18 additions & 1 deletion playground/nuxt.config.ts → playground/nuxt.config.mts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { md3 } from 'vuetify/blueprints'
import { availableLocales, langDir } from './config/i18n'

export default defineNuxtConfig({
ssr: true,
Expand All @@ -10,7 +11,23 @@ export default defineNuxtConfig({
},
},
},
modules: ['../src/module'],
imports: {
autoImport: true,
injectAtEnd: true,
},
modules: ['@nuxtjs/i18n', '../src/module'],
i18n: {
locales: availableLocales,
lazy: true,
strategy: 'no_prefix',
detectBrowserLanguage: false,
langDir,
defaultLocale: 'en-US',
customRoutes: undefined,
dynamicRouteParams: false,
// debug: true,
vueI18n: './config/i18n.config.mts',
},
vuetify: {
moduleOptions: {
styles: { configFile: '/settings.scss' },
Expand Down
3 changes: 3 additions & 0 deletions playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
"build": "nuxi build",
"generate": "nuxi generate"
},
"dependencies": {
"@nuxtjs/i18n": "^8.0.0-beta.12"
},
"devDependencies": {
"nuxt": "latest",
"sass": "^1.63.6"
Expand Down
31 changes: 28 additions & 3 deletions playground/pages/index.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<script setup lang="ts">
import { useLocale } from 'vuetify'

const value = reactive<{
name1?: string
name2?: string
Expand All @@ -8,17 +10,40 @@ const value = reactive<{
name2: undefined,
name3: undefined,
})
const { locales, t } = useI18n()
const { current } = useLocale()
watch(current, () => {
console.log('current', t('xxx', { locale: current.value }))
})
</script>

<template>
<div>
<VTextField
<div>Vuetify useLocale(): {{ current }}</div>
<div>$i18n current: {{ $i18n.locale }}</div>
<div>$vuetify.locale.current: {{ $vuetify.locale.current }}</div>
<div>t without locale: {{ t('xxx') }}</div>
<div>t with I18N locale: {{ t('xxx', { locale: $i18n.locale }) }}</div>
<div>t with Vuetify current locale: {{ t('xxx', { locale: current }) }}</div>
<div>$t {{ $t('xxx') }}</div>
<div>$vuetify.locale.t {{ $vuetify.locale.t('xxx') }}</div>
<v-select
v-model="current"
:items="locales"
item-title="name"
item-value="code"
outlined
/>
<v-text-field
v-model="value.name1"
label="Name 1"
:label="t('xxx')"
hint="name 1"
persistent-hint
outlined
/>
<VBtn>ja</VBtn>
<v-btn>{{ t('xxx') }}</v-btn>
<v-locale-provider locale="es-ES">
<v-btn>{{ $vuetify.locale.t('xxx') }}</v-btn>
</v-locale-provider>
</div>
</template>
Loading