diff --git a/decls/i18n.js b/decls/i18n.js index e7c934e16..cf4a4f709 100644 --- a/decls/i18n.js +++ b/decls/i18n.js @@ -89,6 +89,7 @@ declare type I18nOptions = { modifiers?: Modifiers, root?: I18n, // for internal fallbackRoot?: boolean, + fallbackRootWithEmptyString?: boolean, formatFallbackMessages?: boolean, sync?: boolean, silentTranslationWarn?: boolean | RegExp, diff --git a/gitbook/en/api.md b/gitbook/en/api.md index 81c50a136..976422cfb 100644 --- a/gitbook/en/api.md +++ b/gitbook/en/api.md @@ -235,6 +235,20 @@ You can specify the below some options of `I18nOptions` constructor options of [ If `false`, it's warned, and is returned the key. +#### fallbackRootWithEmptyString + +> :new: 8.26+ + +- **Type:** `Boolean` + +- **Default:** `true` + + In the component localization, whether to fall back to root level (global) localization when local message is an empty string. + + Please note the default behavior in vue-i18n 9.x is to not falling back to root for local message that is empty string. + + If `false`, the empty local message will not fall back to root and will be kept as empty string. + #### sync - **Type:** `Boolean` diff --git a/src/index.js b/src/index.js index 43c8aea3d..5e9ed8b4c 100644 --- a/src/index.js +++ b/src/index.js @@ -48,6 +48,7 @@ export default class VueI18n { _root: any _sync: boolean _fallbackRoot: boolean + _fallbackRootWithEmptyString: boolean _localeChainCache: { [key: string]: Array; } _missing: ?MissingHandler _exist: Function @@ -95,6 +96,9 @@ export default class VueI18n { this._fallbackRoot = options.fallbackRoot === undefined ? true : !!options.fallbackRoot + this._fallbackRootWithEmptyString = options.fallbackRootWithEmptyString === undefined + ? true + : !!options.fallbackRootWithEmptyString this._formatFallbackMessages = options.formatFallbackMessages === undefined ? false : !!options.formatFallbackMessages @@ -379,7 +383,7 @@ export default class VueI18n { } _isFallbackRoot (val: any): boolean { - return !val && !isNull(this._root) && this._fallbackRoot + return (this._fallbackRootWithEmptyString? !val : isNull(val)) && !isNull(this._root) && this._fallbackRoot } _isSilentFallbackWarn (key: Path): boolean { @@ -461,7 +465,7 @@ export default class VueI18n { // We are going to replace each of // them with its translation const matches: any = ret.match(linkKeyMatcher) - + // eslint-disable-next-line no-autofix/prefer-const for (let idx in matches) { // ie compatible: filter custom array diff --git a/test/unit/component.test.js b/test/unit/component.test.js index 4e7a27907..54320ab57 100644 --- a/test/unit/component.test.js +++ b/test/unit/component.test.js @@ -137,4 +137,131 @@ describe('component translation', () => { vm.$destroy() }).then(done) }) + + it('fallbackRootWithEmptyString default to be true', done => { + const el = document.createElement('div') + let vm = new Vue({ + i18n, + components: { + child: { // translation with component + i18n: { + locale: 'en-US', + sync: false, + messages: { + 'en-US': { + who: 'child' + }, + 'ja-JP': { + who: '子', + } + }, + }, + components: { + 'sub-child': { // translation with root + i18n: { + locale: 'ja-JP', + sync: false, + messages: { + 'en-US': { + who: 'sub-child' + }, + 'ja-JP': { + who: '' + } + }, + sharedMessages: { // shared messages for child1 component + 'en-US': { foo: { bar: 'bar' } }, + 'ja-JP': { foo: { bar: 'バー' } } + } + }, + render (h) { + return h('div', {}, [ + h('p', { ref: 'who' }, [this.$t('who')]) + ]) + } + } + }, + render (h) { + return h('div', {}, [ + h('p', { ref: 'who' }, [this.$t('who')]), + h('sub-child', { ref: 'sub-child' }) + ]) + } + }, + }, + render (h) { + return h('div', {}, [ + h('p', { ref: 'who' }, [this.$t('who')]), + h('child', { ref: 'child' }), + ]) + } + }).$mount(el) + Vue.nextTick().then(() => { + assert.strictEqual(vm.$refs.child.$refs['sub-child'].$refs.who.textContent, 'ルート') + }).then(done) + }) + + it('fallbackRootWithEmptyString should work when set to false', done => { + const el = document.createElement('div') + let vm = new Vue({ + i18n, + components: { + child: { // translation with component + i18n: { + locale: 'en-US', + sync: false, + messages: { + 'en-US': { + who: 'child' + }, + 'ja-JP': { + who: '子', + } + }, + }, + components: { + 'sub-child': { // translation with root + i18n: { + locale: 'ja-JP', + sync: false, + fallbackRootWithEmptyString: false, + messages: { + 'en-US': { + who: 'sub-child' + }, + 'ja-JP': { + who: '' + } + }, + sharedMessages: { // shared messages for child1 component + 'en-US': { foo: { bar: 'bar' } }, + 'ja-JP': { foo: { bar: 'バー' } } + } + }, + render (h) { + return h('div', {}, [ + h('p', { ref: 'who' }, [this.$t('who')]) + ]) + } + } + }, + render (h) { + return h('div', {}, [ + h('p', { ref: 'who' }, [this.$t('who')]), + h('sub-child', { ref: 'sub-child' }) + ]) + } + }, + }, + render (h) { + return h('div', {}, [ + h('p', { ref: 'who' }, [this.$t('who')]), + h('child', { ref: 'child' }), + ]) + } + }).$mount(el) + Vue.nextTick().then(() => { + assert.strictEqual(vm.$refs.child.$refs['sub-child'].$refs.who.textContent, '') + }).then(done) + }) }) diff --git a/types/index.d.ts b/types/index.d.ts index 727bf7a64..134032d5e 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -145,6 +145,7 @@ declare namespace VueI18n { modifiers?: Modifiers, missing?: MissingHandler; fallbackRoot?: boolean; + fallbackRootWithEmptyString?: boolean, formatFallbackMessages?: boolean; sync?: boolean; silentTranslationWarn?: boolean | RegExp; @@ -240,7 +241,7 @@ declare class VueI18n { te(key: VueI18n.Path, locale?: VueI18n.Locale): boolean; d(value: number | Date, key?: VueI18n.Path, locale?: VueI18n.Locale): VueI18n.DateTimeFormatResult; d(value: number | Date, args?: { [key: string]: string }): VueI18n.DateTimeFormatResult; - d(value: number | Date, options?: VueI18n.DateTimeFormatOptions): VueI18n.DateTimeFormatResult; + d(value: number | Date, options?: VueI18n.DateTimeFormatOptions): VueI18n.DateTimeFormatResult; n(value: number, key?: VueI18n.Path, locale?: VueI18n.Locale): VueI18n.NumberFormatResult; n(value: number, args?: { [key: string]: string }): VueI18n.NumberFormatResult; n(value: number, options?: VueI18n.NumberFormatOptions, locale?: VueI18n.Locale): VueI18n.NumberFormatResult; diff --git a/vuepress/api/README.md b/vuepress/api/README.md index 93074c784..5012cb0cb 100644 --- a/vuepress/api/README.md +++ b/vuepress/api/README.md @@ -272,6 +272,20 @@ In the component localization, whether to fall back to root level (global) local If `false`, it's warned, and is returned the key. +#### fallbackRootWithEmptyString + +> :new: 8.26+ + +- **Type:** `Boolean` + +- **Default:** `true` + + In the component localization, whether to fall back to root level (global) localization when local message is an empty string. + + Please note the default behavior in vue-i18n 9.x is to not falling back to root for local message that is empty string. + + If `false`, the empty local message will not fall back to root and will be kept as empty string. + #### sync * **Type:** `Boolean` diff --git a/vuepress/pt/api/README.md b/vuepress/pt/api/README.md index 2fd3b060a..a4404738c 100644 --- a/vuepress/pt/api/README.md +++ b/vuepress/pt/api/README.md @@ -272,6 +272,20 @@ Ao usar a localização em componentes, determina se deve consultar a localizaç Se definido como `false`, um aviso será lançado e uma chave retornada. +#### fallbackRootWithEmptyString + +> :new: Adicionado na versão 8.26+ + +* **Tipo:** `Boolean` + +* **Padrão:** `true` + +Na localização do componente, se deve retornar à localização de nível raiz (global) quando a mensagem local for uma string vazia. + +Por favor, note que o comportamento padrão no vue-i18n 9.x é não voltar ao root para a mensagem local que é uma string vazia. + +Se for `false`, a mensagem local vazia não retornará ao root e será mantida como string vazia. + #### sync * **Tipo:** `Boolean` diff --git a/vuepress/ru/api/README.md b/vuepress/ru/api/README.md index 8b7849243..abc3cb4a1 100644 --- a/vuepress/ru/api/README.md +++ b/vuepress/ru/api/README.md @@ -272,6 +272,20 @@ sidebar: auto При значении `false` будет выбрасываться предупреждение и возвращаться ключ. +#### fallbackRootWithEmptyString + +> :new: Добавлено в версии 8.26+ + +* **Тип:** `Boolean` + +* **По умолчанию:** `true` + +В локализации компонента укажите, следует ли вернуться к локализации корневого уровня (глобальной), если локальное сообщение представляет собой пустую строку. + +Обратите внимание, что поведение по умолчанию в vue-i18n 9.x заключается в том, чтобы не возвращаться к корневому каталогу для локального сообщения, которое является пустой строкой. + +Если `false`, пустое локальное сообщение не будет возвращено в корень и будет храниться как пустая строка. + #### sync * **Тип:** `Boolean` diff --git a/vuepress/zh/api/README.md b/vuepress/zh/api/README.md index eef690e17..5019573cb 100755 --- a/vuepress/zh/api/README.md +++ b/vuepress/zh/api/README.md @@ -254,6 +254,20 @@ vue-i18n 版本 如果为 `false`,则会发出警告,并返回 key。 +#### fallbackRootWithEmptyString + +> :new: 8.26+ 新增 + + * **Type:** `Boolean` + + * **Default:** `true` + +在组件本地化中,当本地化文本为空字符串时,是否回退到根级别 (全局) 本地化。 + +请注意,vue-i18n 9.x版本的默认行为是对空字符串本地化文本进行回退到根级别本地化。 + +如果为`false`,则空的本地化文本将不会回退到根目录,并将保留为空字符串。 + #### sync * **类型:**`Boolean`