From bf9929c6e14190b4c184a30701bdc4ca17d20ecd Mon Sep 17 00:00:00 2001 From: Sebastian Wasser Date: Mon, 12 Aug 2019 15:15:21 +0300 Subject: [PATCH] :star: new: fallback formatting (#637) by @sebwas * Add formatting of fallback messages * Make use of (de-)activation option * Add documentation for proposed feature * :up: update: add getter/setter * :pencil: docs: move to vuepress * :shirt: refactor: update --- decls/i18n.js | 3 +++ src/index.js | 15 ++++++++++- src/mixin.js | 1 + test/unit/basic.test.js | 54 ++++++++++++++++++++++++++++++++++++++ types/index.d.ts | 3 +++ vuepress/guide/fallback.md | 50 +++++++++++++++++++++++++++++++++++ 6 files changed, 125 insertions(+), 1 deletion(-) diff --git a/decls/i18n.js b/decls/i18n.js index b503e0980..66f4580e0 100644 --- a/decls/i18n.js +++ b/decls/i18n.js @@ -71,6 +71,7 @@ declare type I18nOptions = { missing?: MissingHandler, root?: I18n, // for internal fallbackRoot?: boolean, + formatFallbackMessages?: boolean, sync?: boolean, silentTranslationWarn?: boolean | RegExp, silentFallbackWarn?: boolean | RegExp, @@ -106,6 +107,8 @@ declare interface I18n { set missing (handler: MissingHandler): void, get formatter (): Formatter, set formatter (formatter: Formatter): void, + get formatFallbackMessages (): boolean, + set formatFallbackMessages (fallback: boolean): void, get silentTranslationWarn (): boolean | RegExp, set silentTranslationWarn (silent: boolean | RegExp): void, get silentFallbackWarn (): boolean | RegExp, diff --git a/src/index.js b/src/index.js index 12abb2d40..630a30d1e 100644 --- a/src/index.js +++ b/src/index.js @@ -43,6 +43,7 @@ export default class VueI18n { _exist: Function _silentTranslationWarn: boolean | RegExp _silentFallbackWarn: boolean | RegExp + _formatFallbackMessages: boolean _dateTimeFormatters: Object _numberFormatters: Object _path: I18nPath @@ -76,6 +77,9 @@ export default class VueI18n { this._fallbackRoot = options.fallbackRoot === undefined ? true : !!options.fallbackRoot + this._formatFallbackMessages = options.formatFallbackMessages === undefined + ? false + : !!options.formatFallbackMessages this._silentTranslationWarn = options.silentTranslationWarn === undefined ? false : options.silentTranslationWarn @@ -228,6 +232,9 @@ export default class VueI18n { this._vm.$set(this._vm, 'fallbackLocale', locale) } + get formatFallbackMessages (): boolean { return this._formatFallbackMessages } + set formatFallbackMessages (fallback: boolean): void { this._formatFallbackMessages = fallback } + get missing (): ?MissingHandler { return this._missing } set missing (handler: MissingHandler): void { this._missing = handler } @@ -274,7 +281,13 @@ export default class VueI18n { ) } } - return key + + if (this._formatFallbackMessages) { + const parsedArgs = parseArgs(...values) + return this._render(key, 'string', parsedArgs.params, key) + } else { + return key + } } _isFallbackRoot (val: any): boolean { diff --git a/src/mixin.js b/src/mixin.js index 5fff85481..4a2968920 100644 --- a/src/mixin.js +++ b/src/mixin.js @@ -34,6 +34,7 @@ export default { options.i18n.root = this.$root options.i18n.formatter = this.$root.$i18n.formatter options.i18n.fallbackLocale = this.$root.$i18n.fallbackLocale + options.i18n.formatFallbackMessages = this.$root.$i18n.formatFallbackMessages options.i18n.silentTranslationWarn = this.$root.$i18n.silentTranslationWarn options.i18n.silentFallbackWarn = this.$root.$i18n.silentFallbackWarn options.i18n.pluralizationRules = this.$root.$i18n.pluralizationRules diff --git a/test/unit/basic.test.js b/test/unit/basic.test.js index 78a507418..6e3e90f02 100644 --- a/test/unit/basic.test.js +++ b/test/unit/basic.test.js @@ -161,6 +161,60 @@ describe('basic', () => { }) }) + describe('format arguments of fallback', () => { + describe('if activated', () => { + describe('named', () => { + it('should return replaced string', () => { + i18n = new VueI18n({ + locale: 'en', + fallbackLocale: 'en', + formatFallbackMessages: true + }) + + assert.strictEqual( + i18n.t('Hello {name}, how are you?', { name: 'kazupon' }), + 'Hello kazupon, how are you?' + ) + }) + }) + + describe('list', () => { + it('should return replaced string', () => { + i18n = new VueI18n({ + locale: 'en', + fallbackLocale: 'en', + formatFallbackMessages: true + }) + + assert.strictEqual( + i18n.t('Hello {0}, how are you?', ['kazupon']), + 'Hello kazupon, how are you?' + ) + }) + }) + }) + + describe('if not activated', () => { + describe('named', () => { + it('should not return replaced string', () => { + assert.strictEqual( + i18n.t('Hello {name}, how are you?', { name: 'kazupon' }), + 'Hello {name}, how are you?' + ) + }) + }) + + describe('list', () => { + it('should not return replaced string', () => { + assert.strictEqual( + i18n.t('Hello {0}, how are you?', ['kazupon']), + 'Hello {0}, how are you?' + ) + }) + }) + }) + }) + describe('locale argument', () => { it('should return empty string', () => { assert.strictEqual(i18n.t('message.hello', 'ja'), messages.ja.message.hello) diff --git a/types/index.d.ts b/types/index.d.ts index d431c1f4a..160f530c9 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -100,6 +100,7 @@ declare namespace VueI18n { formatter?: Formatter; missing?: MissingHandler; fallbackRoot?: boolean; + formatFallbackMessages?: boolean; sync?: boolean; silentTranslationWarn?: boolean | RegExp; silentFallbackWarn?: boolean | RegExp; @@ -143,6 +144,7 @@ export declare interface IVueI18n { fallbackLocale: VueI18n.Locale; missing: VueI18n.MissingHandler; formatter: VueI18n.Formatter; + formatFallbackMessages: boolean; silentTranslationWarn: boolean | RegExp; silentFallbackWarn: boolean | RegExp; preserveDirectiveContent: boolean; @@ -162,6 +164,7 @@ declare class VueI18n { fallbackLocale: VueI18n.Locale; missing: VueI18n.MissingHandler; formatter: VueI18n.Formatter; + formatFallbackMessages: boolean; silentTranslationWarn: boolean | RegExp; silentFallbackWarn: boolean | RegExp; preserveDirectiveContent: boolean; diff --git a/vuepress/guide/fallback.md b/vuepress/guide/fallback.md index d618d3a2d..e2c448afe 100644 --- a/vuepress/guide/fallback.md +++ b/vuepress/guide/fallback.md @@ -42,3 +42,53 @@ Note, that by default falling back to `fallbackLocale` generates two console war ``` To suppress these warnings (while keeping those which warn of the total absence of translation for the given key) set `silentFallbackWarn: true` when initializing the `VueI18n` instance. + +## Fallback interpolation + +Since the keys to the translations are strings, the original message can be used as a key instead of the path. +E.g. + +```javascript +const messages = { + ja: { + 'Hello world': 'こんにちは、世界' + } +} +``` + +This way the translations can be used in a very natural way, automatically falling back to the source language if the translated string cannot be found: + +```html +

{{ $t('Hello world') }}

+``` + +To enrich this feature, interpolation of fallback messages can be turned on by setting `formatFallbackMessages` to `true`: + +```javascript +const messages = { + ru: { + 'Hello {name}': 'Здравствуйте {name}' + } +} + +const i18n = new VueI18n({ + locale: 'ru', + fallbackLocale: 'en', + formatFallbackMessages: true, + messages +}) +``` + +When you template the below: + +```html +

{{ $t('Hello {name}', { name: 'John' }}) }}

+

{{ $t('The weather today is {condition}!', { condition: 'sunny' }) }}

+``` + +The following will be the output: + +```html +

Здравствуйте John

+

The weather today is sunny!

+``` \ No newline at end of file