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: resources merge to global scope #206

Merged
merged 2 commits into from
Dec 3, 2020
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
6 changes: 6 additions & 0 deletions docs/advanced/composition.md
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,12 @@ The above code sets the `useI18n` option to `useScope: 'global'`, which allows `

Then you can compose using the functions and properties exposed from the Composer instance.

:::tip NOTE
If you set `useI18n` to `messages`, `datetimeFormats`, and `numberFormats` together `useScope: 'global’`, **they will be merged into global scope**. That is, they will be managed by `messages`, `datetimeFormasts`, and `numberFormats` of the global scope Composer instance.

And also, if [`global` is specified in i18n custom blocks](../advanced/sfc#define-locale-messages-for-global-scope) (e.g. `<i18n global>{ … }</i18n>`), the locale messags defined in the blocks are merged with the global scope.
:::

### Implicit with injected properties and functions

Another way to refer a global scope Composer instance is through properties and functions implicitly injected into the component.
Expand Down
27 changes: 24 additions & 3 deletions docs/advanced/sfc.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ const config: UserConfig = {
export default config
```

## Define Locale Messages Importing
## Define locale lessages importing

You can use `src` attribute:

Expand Down Expand Up @@ -209,7 +209,7 @@ You can use `locale` attribute:

In the above example, since the `locale` attribute is set to `en`, the locale messages defined in `i18n` custom blocks can be used as a resource for locale messages of `en`.

## Define Multiple Locale Messages
## Define multiple locale lessages

You can use locale messages with multiple `i18n` custom blocks.

Expand All @@ -231,7 +231,7 @@ In the above, first custom block load the common locale message with `src` attri

In this way, multiple custom blocks useful when want to be used as module.

## Locale Messages Other Formats
## Locale messages other formats

`i18n` custom blocks supports resource formats other than `json`.

Expand Down Expand Up @@ -271,6 +271,27 @@ The `i18n` custom blocks below of `json5` format:
</i18n>
```

## Define locale messages for global scope

You can use define locale messages for global scope with `global` attribute:

```html
<i18n global>
{
"en": {
"hello": "hello world!"
}
}
</i18n>
```

In the above example, since the `global` attribute is set, the locale messages defined in `i18n` custom blocks can be merged as a resource for locale messages of global scope.

:::warning NOTICE
The locale messages for global scope defined in i18n custom blocks are available **only composition API mode**. You need to run `useI18n` option to `useScope: 'global'` at `setup`. About details, see the [Composition API](/advanced/composition).
:::


## Vue CLI

[Vue CLI](https://github.com/vuejs/vue-cli) hides the webpack configuration, so, if we want to add support to the `<i18n>` tag inside a single file component we need to modify the existing configuration.
Expand Down
4 changes: 4 additions & 0 deletions docs/migration/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,7 @@ export default {
For Datetime localization, since Vue I18n v9, we also offer the DatetimeFormat component like the [NumberFormat component](../essentials/number#custom-formatting).

See the [Datetime localization custom formatting](../essentials/datetime#custom-formatting)

## i18n Custom Block

- See the [`global` attribute](../advanced/sfc#define-locale-messages-for-global-scope)
1 change: 1 addition & 0 deletions src/composer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ export interface ComposerInternalOptions<
Message = VueMessageType
> {
__i18n?: CustomBlocks<Message>
__i18nGlobal?: CustomBlocks<Message>
__root?: Composer<Messages, DateTimeFormats, NumberFormats, Message>
}

Expand Down
35 changes: 34 additions & 1 deletion src/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { Locale, FallbackLocale, LocaleMessageDictionary } from './core/context'
import { DateTimeFormat, NumberFormat } from './core/types'
import {
VueMessageType,
getLocaleMessages,
Composer,
ComposerOptions,
ComposerInternalOptions,
Expand All @@ -30,7 +31,7 @@ import { I18nWarnCodes, getWarnMessage } from './warnings'
import { I18nErrorCodes, createI18nError } from './errors'
import { apply } from './plugin'
import { defineMixin } from './mixin'
import { isEmptyObject, isBoolean, warn, makeSymbol } from './utils'
import { isEmptyObject, isObject, isBoolean, warn, makeSymbol } from './utils'
import {
devtoolsRegisterI18n,
enableDevTools,
Expand Down Expand Up @@ -558,6 +559,38 @@ export function useI18n<
: options.useScope

if (scope === 'global') {
let messages = isObject(options.messages) ? options.messages : {}
if ('__i18nGlobal' in instance.type) {
messages = getLocaleMessages(global.locale.value, {
messages,
__i18n: instance.type.__i18nGlobal
})
}
// merge locale messages
const locales = Object.keys(messages)
if (locales.length) {
locales.forEach(locale => {
global.mergeLocaleMessage(locale, messages![locale])
})
}
// merge datetime formats
if (isObject(options.datetimeFormats)) {
const locales = Object.keys(options.datetimeFormats)
if (locales.length) {
locales.forEach(locale => {
global.mergeDateTimeFormat(locale, options.datetimeFormats![locale])
})
}
}
// merge number formats
if (isObject(options.numberFormats)) {
const locales = Object.keys(options.numberFormats)
if (locales.length) {
locales.forEach(locale => {
global.mergeNumberFormat(locale, options.numberFormats![locale])
})
}
}
return global
}

Expand Down
112 changes: 112 additions & 0 deletions test/i18n.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -541,3 +541,115 @@ test('multi instance', async () => {
expect(app1.__VUE_I18N_SYMBOL__ !== app2.__VUE_I18N_SYMBOL__).toEqual(true)
expect(i18n1.global).not.toEqual(i18n2.global)
})

test('merge useI18n resources to global scope', async () => {
const i18n = createI18n({
legacy: false,
locale: 'ja',
messages: {
en: {
hello: 'hello!'
}
}
})

const App = defineComponent({
setup() {
useI18n({
useScope: 'global',
messages: {
ja: {
hello: 'こんにちは!'
}
},
datetimeFormats: {
'en-US': {
short: {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
}
}
},
numberFormats: {
'en-US': {
currency: {
style: 'currency',
currency: 'USD',
currencyDisplay: 'symbol'
}
}
}
})
return {}
},
template: `<p>foo</p>`
})
await mount(App, i18n)

expect(i18n.global.getLocaleMessage('ja')).toEqual({ hello: 'こんにちは!' })
expect(i18n.global.getDateTimeFormat('en-US')).toEqual({
short: {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
}
})
expect(i18n.global.getNumberFormat('en-US')).toEqual({
currency: {
style: 'currency',
currency: 'USD',
currencyDisplay: 'symbol'
}
})
})

test('merge i18n custom blocks to global scope', async () => {
const i18n = createI18n({
legacy: false,
locale: 'ja',
messages: {
en: {
hello: 'hello!'
}
}
})

const App = defineComponent({
setup() {
const instance = getCurrentInstance()
if (instance == null) {
throw new Error()
}
const options = instance.type as ComponentOptions
options.__i18nGlobal = [
JSON.stringify({ en: { foo: 'foo!' } }),
JSON.stringify({ ja: { foo: 'ふー!' } })
]
useI18n({
useScope: 'global',
messages: {
ja: {
hello: 'こんにちは!'
}
}
})
return {}
},
template: `<p>foo</p>`
})
await mount(App, i18n)

expect(i18n.global.getLocaleMessage('en')).toEqual({
hello: 'hello!',
foo: 'foo!'
})
expect(i18n.global.getLocaleMessage('ja')).toEqual({
hello: 'こんにちは!',
foo: 'ふー!'
})
})