Skip to content

Commit 132f344

Browse files
authored
feat: resources merge to global scope (#206)
* feat: resources merge to global scope * docs: update migration category
1 parent 2f20723 commit 132f344

File tree

6 files changed

+181
-4
lines changed

6 files changed

+181
-4
lines changed

docs/advanced/composition.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,12 @@ The above code sets the `useI18n` option to `useScope: 'global'`, which allows `
299299

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

302+
:::tip NOTE
303+
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.
304+
305+
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.
306+
:::
307+
302308
### Implicit with injected properties and functions
303309

304310
Another way to refer a global scope Composer instance is through properties and functions implicitly injected into the component.

docs/advanced/sfc.md

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ const config: UserConfig = {
173173
export default config
174174
```
175175

176-
## Define Locale Messages Importing
176+
## Define locale lessages importing
177177

178178
You can use `src` attribute:
179179

@@ -209,7 +209,7 @@ You can use `locale` attribute:
209209

210210
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`.
211211

212-
## Define Multiple Locale Messages
212+
## Define multiple locale lessages
213213

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

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

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

234-
## Locale Messages Other Formats
234+
## Locale messages other formats
235235

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

@@ -271,6 +271,27 @@ The `i18n` custom blocks below of `json5` format:
271271
</i18n>
272272
```
273273

274+
## Define locale messages for global scope
275+
276+
You can use define locale messages for global scope with `global` attribute:
277+
278+
```html
279+
<i18n global>
280+
{
281+
"en": {
282+
"hello": "hello world!"
283+
}
284+
}
285+
</i18n>
286+
```
287+
288+
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.
289+
290+
:::warning NOTICE
291+
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).
292+
:::
293+
294+
274295
## Vue CLI
275296

276297
[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.

docs/migration/features.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,7 @@ export default {
5959
For Datetime localization, since Vue I18n v9, we also offer the DatetimeFormat component like the [NumberFormat component](../essentials/number#custom-formatting).
6060

6161
See the [Datetime localization custom formatting](../essentials/datetime#custom-formatting)
62+
63+
## i18n Custom Block
64+
65+
- See the [`global` attribute](../advanced/sfc#define-locale-messages-for-global-scope)

src/composer.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ export interface ComposerInternalOptions<
311311
Message = VueMessageType
312312
> {
313313
__i18n?: CustomBlocks<Message>
314+
__i18nGlobal?: CustomBlocks<Message>
314315
__root?: Composer<Messages, DateTimeFormats, NumberFormats, Message>
315316
}
316317

src/i18n.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { Locale, FallbackLocale, LocaleMessageDictionary } from './core/context'
1313
import { DateTimeFormat, NumberFormat } from './core/types'
1414
import {
1515
VueMessageType,
16+
getLocaleMessages,
1617
Composer,
1718
ComposerOptions,
1819
ComposerInternalOptions,
@@ -30,7 +31,7 @@ import { I18nWarnCodes, getWarnMessage } from './warnings'
3031
import { I18nErrorCodes, createI18nError } from './errors'
3132
import { apply } from './plugin'
3233
import { defineMixin } from './mixin'
33-
import { isEmptyObject, isBoolean, warn, makeSymbol } from './utils'
34+
import { isEmptyObject, isObject, isBoolean, warn, makeSymbol } from './utils'
3435
import {
3536
devtoolsRegisterI18n,
3637
enableDevTools,
@@ -558,6 +559,38 @@ export function useI18n<
558559
: options.useScope
559560

560561
if (scope === 'global') {
562+
let messages = isObject(options.messages) ? options.messages : {}
563+
if ('__i18nGlobal' in instance.type) {
564+
messages = getLocaleMessages(global.locale.value, {
565+
messages,
566+
__i18n: instance.type.__i18nGlobal
567+
})
568+
}
569+
// merge locale messages
570+
const locales = Object.keys(messages)
571+
if (locales.length) {
572+
locales.forEach(locale => {
573+
global.mergeLocaleMessage(locale, messages![locale])
574+
})
575+
}
576+
// merge datetime formats
577+
if (isObject(options.datetimeFormats)) {
578+
const locales = Object.keys(options.datetimeFormats)
579+
if (locales.length) {
580+
locales.forEach(locale => {
581+
global.mergeDateTimeFormat(locale, options.datetimeFormats![locale])
582+
})
583+
}
584+
}
585+
// merge number formats
586+
if (isObject(options.numberFormats)) {
587+
const locales = Object.keys(options.numberFormats)
588+
if (locales.length) {
589+
locales.forEach(locale => {
590+
global.mergeNumberFormat(locale, options.numberFormats![locale])
591+
})
592+
}
593+
}
561594
return global
562595
}
563596

test/i18n.test.ts

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,3 +541,115 @@ test('multi instance', async () => {
541541
expect(app1.__VUE_I18N_SYMBOL__ !== app2.__VUE_I18N_SYMBOL__).toEqual(true)
542542
expect(i18n1.global).not.toEqual(i18n2.global)
543543
})
544+
545+
test('merge useI18n resources to global scope', async () => {
546+
const i18n = createI18n({
547+
legacy: false,
548+
locale: 'ja',
549+
messages: {
550+
en: {
551+
hello: 'hello!'
552+
}
553+
}
554+
})
555+
556+
const App = defineComponent({
557+
setup() {
558+
useI18n({
559+
useScope: 'global',
560+
messages: {
561+
ja: {
562+
hello: 'こんにちは!'
563+
}
564+
},
565+
datetimeFormats: {
566+
'en-US': {
567+
short: {
568+
year: 'numeric',
569+
month: '2-digit',
570+
day: '2-digit',
571+
hour: '2-digit',
572+
minute: '2-digit'
573+
}
574+
}
575+
},
576+
numberFormats: {
577+
'en-US': {
578+
currency: {
579+
style: 'currency',
580+
currency: 'USD',
581+
currencyDisplay: 'symbol'
582+
}
583+
}
584+
}
585+
})
586+
return {}
587+
},
588+
template: `<p>foo</p>`
589+
})
590+
await mount(App, i18n)
591+
592+
expect(i18n.global.getLocaleMessage('ja')).toEqual({ hello: 'こんにちは!' })
593+
expect(i18n.global.getDateTimeFormat('en-US')).toEqual({
594+
short: {
595+
year: 'numeric',
596+
month: '2-digit',
597+
day: '2-digit',
598+
hour: '2-digit',
599+
minute: '2-digit'
600+
}
601+
})
602+
expect(i18n.global.getNumberFormat('en-US')).toEqual({
603+
currency: {
604+
style: 'currency',
605+
currency: 'USD',
606+
currencyDisplay: 'symbol'
607+
}
608+
})
609+
})
610+
611+
test('merge i18n custom blocks to global scope', async () => {
612+
const i18n = createI18n({
613+
legacy: false,
614+
locale: 'ja',
615+
messages: {
616+
en: {
617+
hello: 'hello!'
618+
}
619+
}
620+
})
621+
622+
const App = defineComponent({
623+
setup() {
624+
const instance = getCurrentInstance()
625+
if (instance == null) {
626+
throw new Error()
627+
}
628+
const options = instance.type as ComponentOptions
629+
options.__i18nGlobal = [
630+
JSON.stringify({ en: { foo: 'foo!' } }),
631+
JSON.stringify({ ja: { foo: 'ふー!' } })
632+
]
633+
useI18n({
634+
useScope: 'global',
635+
messages: {
636+
ja: {
637+
hello: 'こんにちは!'
638+
}
639+
}
640+
})
641+
return {}
642+
},
643+
template: `<p>foo</p>`
644+
})
645+
await mount(App, i18n)
646+
647+
expect(i18n.global.getLocaleMessage('en')).toEqual({
648+
hello: 'hello!',
649+
foo: 'foo!'
650+
})
651+
expect(i18n.global.getLocaleMessage('ja')).toEqual({
652+
hello: 'こんにちは!',
653+
foo: 'ふー!'
654+
})
655+
})

0 commit comments

Comments
 (0)