Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/modernice/vue-i18n-modules

Lazy-loaded, namespaced messages for vue-i18n.
https://github.com/modernice/vue-i18n-modules

i18n lazy messages module namespace vue vue-i18n

Last synced: about 2 hours ago
JSON representation

Lazy-loaded, namespaced messages for vue-i18n.

Awesome Lists containing this project

README

        

# vue-i18n-modules

An extension for [vue-i18n](https://github.com/intlify/vue-i18n-next) that
provides namespaced lazy-loading of messages, as opposed to the default behavior
of vue-i18n, which always loads all messages for a specific locale.

## Introduction

Large applications that use vue-i18n for internationaliztion often have a large
amount of message files for different parts of the application. For better
performance, it is desirable to only load a set of messages when they're
actually needed, instead of loading all messages for a specific locale all at
once.

### Limitations

- Only supports vue-i18n's [Composition API](https://vue-i18n.intlify.dev/guide/advanced/composition.html); does not support legacy mode
- Currently only supports Vite's `import.meta.glob` for loading message files
- Message directory must be organized in a specific way

## Installation

### Vue

```sh
npm i @modernice/vue-i18n-modules

pnpm i @modernice/vue-i18n-modules

yarn add @modernice/vue-i18n-modules
```

### Nuxt

```sh
npm i @modernice/nuxt-i18n-modules

pnpm i @modernice/nuxt-i18n-modules

yarn add @modernice/nuxt-i18n-modules
```

## Setup

### Nuxt

Add the module to your `nuxt.config`:

```ts
// nuxt.config.ts
export default defineNuxtModule({
modules: ['@modernice/nuxt-i18n-modules'],

i18nModules: {
dictionary: 'path/to/messages', // defaults to ./dictionary
initial: ['foo', 'bar'] // initial messages are loaded for every page
}
})
```

### Vue

```ts
import { createApp } from 'vue'
import { createI18n } from 'vue-i18n'
import { createExtension, createPlugin } from 'vue-i18n-modules'
import { createGlobLoader } from 'vue-i18n-modules/vite'

// First create the vue-i18n instance.
const i18n = createI18n({
// Legacy mode is not supported!
legacy: false,
})

// Setup a loader for message files in the `./messages` directory.
const loader = createGlobLoader(import.meta.glob('./messages/**/*.json'), {
// The prefix allows you to reference/load message modules without
// having to specify the full path to the directory.
prefix: './messages/'
})

// Create the extension from the vue-i18n instance and module loader.
const extension = createExtension({ i18n, loader })

// Create the Vue plugin from the extension.
const plugin = createPlugin(extension)

createApp()
.use(i18n)
.use(plugin)
.mount('#app')
```

## Basic usage

Given that your message modules are in the `./messages` directory, you must
create a directory structure like this:

- `./messages`
- `./foo`
- `./en.json`
- `./de.json`
- `./fr.json`
- `./foobar`
- `./en.json`
- `./de.json`
- `./fr.json`
- `./bar`
- `./en.json`
- `./de.json`
- `./fr.json`
- `./barbaz`
- `./en.json`
- `./de.json`
- `./fr.json`

The above directory structure defines 4 modules: "foo", "bar", "foo.foobar", and
"bar.barbaz". Each module provides its messages for the locales that are configured
in the vue-i18n instance.

Given that `./messages/foo/foobar/en.json` has the following contents:

```json
{
"page": {
"title": "Hello, Foo!"
}
}
```

You can lazy-load and translate the title like this:

```ts
// Within a Vue setup function
import { useExtension } from 'vue-i18n-modules'

const { loadModule, translate } = useExtension()

// Loads the messages of the "foo.foobar" module.
await loadModule('foo.foobar')

// Translate the title in the currently active locale.
const title = translate('foo.foobar', 'page.title')
```

The `translate` function utilities vue-i18n's translate function internally,
so you can pass vue-i18n's translate options to this function.

## Typed modules

You can define types for your message modules by augmenting the `DefineModules`
interface. When defining at least one module, this extension enforces strictness
in module names passed to the `translate` function.

For example, you could create `./messages/foo/foobar/index.ts` to define the
type of the `foo.foobar` module:

```ts
// ./messages/foo/foobar/index.ts
import type foobar from './en.json'

/**
* Foobar messages.
*/
export type Foobar = typeof foobar
```

Then augment the `DefineModules` interface to enable strict typing:

```ts
// in a project-wide *.d.ts file
import { Foobar } from './messages/foo/foobar/index'

declare module '@modernice/vue-i18n-modules' {
interface DefineModules {
'foo.foobar': Foobar
}
}
```

If at least one module is defined, the `translate` function only accepts defined
module names instead of arbitrary strings. The following will raise a TypeScript
error:

```ts
// Within a Vue setup function
import { useExtension } from 'vue-i18n-modules'

const { loadModule, translate } = useExtension()

// This will not work
await loadModule('unknown-module-name')

// This won't work either
const title = translate('unknown-module-name', 'some.key')

// This also won't work because the key does not exist in the file
const title = translate('foo.foobar', 'some.key')
```

## useMessages()

Instead of `useExtension()`, you can call `useMessages()` to get a translate
function for a specific module:

```ts
// Within a Vue setup function
import { useMessages } from 'vue-i18n-modules'

const { translate } = useMessages('foo.foobar', {
// Immediately load the module within `onServerPrefetch`
// and/or `onMounted`.
load: true,
})

// Now you can omit the module name
const title = translate('page.title')
```

### Middleware

A middleware for loading message modules is provided for Nuxt applications:

```ts
// Within a Vue setup function

definePageMeta({
middleware: 'i18n:messages',

// Load the "foo" and "foo.bar" message modules in a
// middleware before rendering the page.
translate: ['foo', 'foo.bar'],
})
```

## License

[MIT](./LICENSE)