From b9437ead90b80392c86fa15a8fbe1e0bc5fe351c Mon Sep 17 00:00:00 2001 From: Alexey Date: Fri, 21 Dec 2018 20:11:43 +0300 Subject: [PATCH] :zap: improvement(format): Add the path as argument to the custom formatter (#489) by @Raiondesu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ⚡improvement(types): typed autocomplete in date and number format options Replace Number and Date format options with standard TS `Intl` types, while also adding guiding TS autocomplete to them. This change exterminates the confusion of which types to follow, while preserving backwards-compatibility for types and adding optional autocomplete. * ⚡improvement(types/test): add type constraints to format options It's useful to check static variables types whenever possible. * ⚡new(formatter): add path to the formatter arguments and allow optional formatting * ⚡improvement(types): backward-compatibility for #484 * 📃docs(formatter): add case for the undefined return type * ⚡improvement(types): include the possible undefined return type * ✅tests(issues): make formatter return undefined in test for #484 * update(formatter): change default foramtter invocation from undefined to null --- decls/i18n.js | 2 +- gitbook/en/formatting.md | 7 ++++++- src/index.js | 18 +++++++++++++----- test/unit/issues.test.js | 28 ++++++++++++++++++++++++++++ types/index.d.ts | 2 +- 5 files changed, 49 insertions(+), 8 deletions(-) diff --git a/decls/i18n.js b/decls/i18n.js index e45e4ba1e..83d3193c2 100644 --- a/decls/i18n.js +++ b/decls/i18n.js @@ -110,5 +110,5 @@ declare interface I18n { }; declare interface Formatter { - interpolate (message: string, values?: any): Array + interpolate (message: string, values: any, path: string): (Array | null) }; diff --git a/gitbook/en/formatting.md b/gitbook/en/formatting.md index 5e4f35216..9dd989dd2 100644 --- a/gitbook/en/formatting.md +++ b/gitbook/en/formatting.md @@ -157,12 +157,17 @@ class CustomFormatter { // -> passed values: Array (included VNode): // `[VNode{ tag: 'p', text: 'kazupon', ...}, VNode{ tag: 'p', text: 'how are you?', ...}]` // + // @param {string} path + // a path to the message, as passed into the $t/t() functions. + // - $t('hello.louis') -> path === 'hello.louis' + // // @return {Array} // interpolated values. you need to return the following: // - array of string, when is using `$t` or `$tc`. // - array included VNode object, when is using `i18n` functional component. + // - null - if you want the default vue-i18n formatter to handle the case // - interpolate (message, values) { + interpolate (message, values, path) { // implement interpolation logic here // ... diff --git a/src/index.js b/src/index.js index 505da80de..ce7492587 100644 --- a/src/index.js +++ b/src/index.js @@ -37,6 +37,8 @@ const formatters = { 'lower': (str) => str.toLocaleLowerCase() } +const defaultFormatter = new BaseFormatter() + export default class VueI18n { static install: () => void static version: string @@ -76,7 +78,7 @@ export default class VueI18n { const numberFormats = options.numberFormats || {} this._vm = null - this._formatter = options.formatter || new BaseFormatter() + this._formatter = options.formatter || defaultFormatter this._missing = options.missing || null this._root = options.root || null this._sync = options.sync === undefined ? true : !!options.sync @@ -246,7 +248,7 @@ export default class VueI18n { ret = this._link(locale, message, ret, host, interpolateMode, values, visitedLinkStack) } - return this._render(ret, interpolateMode, values) + return this._render(ret, interpolateMode, values, key) } _link ( @@ -322,9 +324,15 @@ export default class VueI18n { return ret } - _render (message: string, interpolateMode: string, values: any): any { - const ret = this._formatter.interpolate(message, values) - // if interpolateMode is **not** 'string' ('raw'), + _render (message: string, interpolateMode: string, values: any, path: string): any { + let ret = this._formatter.interpolate(message, values, path) + + // If the custom formatter refuses to work - apply the default one + if (!ret) { + ret = defaultFormatter.interpolate(message, values, path) + } + + // if interpolateMode is **not** 'string' ('row'), // return the compiled data (e.g. ['foo', VNode, 'bar']) with formatter return interpolateMode === 'string' ? ret.join('') : ret } diff --git a/test/unit/issues.test.js b/test/unit/issues.test.js index df9abf4da..5fd2e9a0c 100644 --- a/test/unit/issues.test.js +++ b/test/unit/issues.test.js @@ -630,4 +630,32 @@ describe('issues', () => { VueI18n.prototype.getChoiceIndex = defaultImpl }) }) + + describe('#484', () => { + it('passes path to the formatter', () => { + const testPath = 'test.deep.message' + + i18n = new VueI18n({ + locale: 'en', + messages: { + en: { + test: { + deep: { + message: 'Hello!' + } + } + } + }, + formatter: { + interpolate (message, values, path) { + assert(path, testPath) + + return null // pass the case to the default formatter + } + } + }) + + assert(i18n.t(testPath), 'Hello!') + }) + }) }) diff --git a/types/index.d.ts b/types/index.d.ts index 59b318f77..496e1b178 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -70,7 +70,7 @@ declare namespace VueI18n { }; interface Formatter { - interpolate(message: string, values?: Values): any[]; + interpolate(message: string, values: Values | undefined, path: string): (any[] | null); } type MissingHandler = (locale: Locale, key: Path, vm?: Vue) => string | void;