Skip to content
This repository was archived by the owner on Dec 31, 2024. It is now read-only.

Commit ddc0c79

Browse files
SzNagyMisukazupon
authored andcommitted
⚡ improvement(index): silence fallback warnings (#510) by @SzNagyMisu
* ⚡ improvement(option): silentFallbackWarn (#139) * feature(option): add silentFallbackWarn to VueI18n constructor * silence fallback warnings * warn only if no translation is found at all * adding typescript property declaration * 📝 docs(options): document silentFallbackWarn * Update vuepress/api/README.md Co-Authored-By: SzNagyMisu <szijjartonagy.misu@gmail.com> * ⚡ improvement(option): silentFallbackWarn * include case when pathRet is not null, undefined, array, plain object or string * provide test case
1 parent e879024 commit ddc0c79

6 files changed

Lines changed: 203 additions & 19 deletions

File tree

src/index.js

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export default class VueI18n {
5454
_watcher: any
5555
_i18nWatcher: Function
5656
_silentTranslationWarn: boolean
57+
_silentFallbackWarn: boolean
5758
_dateTimeFormatters: Object
5859
_numberFormatters: Object
5960
_path: I18nPath
@@ -89,6 +90,9 @@ export default class VueI18n {
8990
this._silentTranslationWarn = options.silentTranslationWarn === undefined
9091
? false
9192
: !!options.silentTranslationWarn
93+
this._silentFallbackWarn = options.silentFallbackWarn === undefined
94+
? false
95+
: !!options.silentFallbackWarn
9296
this._dateTimeFormatters = {}
9397
this._numberFormatters = {}
9498
this._path = new I18nPath()
@@ -184,6 +188,9 @@ export default class VueI18n {
184188
get silentTranslationWarn (): boolean { return this._silentTranslationWarn }
185189
set silentTranslationWarn (silent: boolean): void { this._silentTranslationWarn = silent }
186190

191+
get silentFallbackWarn (): boolean { return this._silentFallbackWarn }
192+
set silentFallbackWarn (silent: boolean): void { this._silentFallbackWarn = silent }
193+
187194
_getMessages (): LocaleMessages { return this._vm.messages }
188195
_getDateTimeFormats (): DateTimeFormats { return this._vm.dateTimeFormats }
189196
_getNumberFormats (): NumberFormats { return this._vm.numberFormats }
@@ -210,6 +217,10 @@ export default class VueI18n {
210217
return !val && !isNull(this._root) && this._fallbackRoot
211218
}
212219

220+
_isSilentFallback (locale: Locale): boolean {
221+
return this._silentFallbackWarn && (this._isFallbackRoot() || locale !== this.fallbackLocale)
222+
}
223+
213224
_interpolate (
214225
locale: Locale,
215226
message: LocaleMessageObject,
@@ -230,7 +241,7 @@ export default class VueI18n {
230241
if (isPlainObject(message)) {
231242
ret = message[key]
232243
if (typeof ret !== 'string') {
233-
if (process.env.NODE_ENV !== 'production' && !this._silentTranslationWarn) {
244+
if (process.env.NODE_ENV !== 'production' && !this._silentTranslationWarn && !this._isSilentFallback(locale)) {
234245
warn(`Value of key '${key}' is not a string!`)
235246
}
236247
return null
@@ -243,7 +254,7 @@ export default class VueI18n {
243254
if (typeof pathRet === 'string') {
244255
ret = pathRet
245256
} else {
246-
if (process.env.NODE_ENV !== 'production' && !this._silentTranslationWarn) {
257+
if (process.env.NODE_ENV !== 'production' && !this._silentTranslationWarn && !this._isSilentFallback(locale)) {
247258
warn(`Value of key '${key}' is not a string!`)
248259
}
249260
return null
@@ -359,7 +370,7 @@ export default class VueI18n {
359370

360371
res = this._interpolate(fallback, messages[fallback], key, host, interpolateMode, args, [key])
361372
if (!isNull(res)) {
362-
if (process.env.NODE_ENV !== 'production' && !this._silentTranslationWarn) {
373+
if (process.env.NODE_ENV !== 'production' && !this._silentTranslationWarn && !this._silentFallbackWarn) {
363374
warn(`Fall back to translate the keypath '${key}' with '${fallback}' locale.`)
364375
}
365376
return res
@@ -379,7 +390,7 @@ export default class VueI18n {
379390
host, 'string', parsedArgs.params
380391
)
381392
if (this._isFallbackRoot(ret)) {
382-
if (process.env.NODE_ENV !== 'production' && !this._silentTranslationWarn) {
393+
if (process.env.NODE_ENV !== 'production' && !this._silentTranslationWarn && !this._silentFallbackWarn) {
383394
warn(`Fall back to translate the keypath '${key}' with root locale.`)
384395
}
385396
/* istanbul ignore if */

src/mixin.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export default {
3737
options.i18n.formatter = this.$root.$i18n.formatter
3838
options.i18n.fallbackLocale = this.$root.$i18n.fallbackLocale
3939
options.i18n.silentTranslationWarn = this.$root.$i18n.silentTranslationWarn
40+
options.i18n.silentFallbackWarn = this.$root.$i18n.silentFallbackWarn
4041
options.i18n.pluralizationRules = this.$root.$i18n.pluralizationRules
4142
options.i18n.preserveDirectiveContent = this.$root.$i18n.preserveDirectiveContent
4243
}

test/unit/silent.test.js

Lines changed: 158 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,168 @@
11
describe('silent', () => {
2-
it('should be suppressed translate warnings', () => {
3-
const vm = new Vue({
4-
i18n: new VueI18n({
5-
locale: 'en',
6-
silentTranslationWarn: true,
2+
let spy
3+
beforeEach(() => {
4+
spy = sinon.spy(console, 'warn')
5+
})
6+
afterEach(() => {
7+
spy.restore()
8+
})
9+
10+
describe('silentTranslationWarn', () => {
11+
it('should be suppressed translate warnings', () => {
12+
const vm = new Vue({
13+
i18n: new VueI18n({
14+
locale: 'en',
15+
silentTranslationWarn: true,
16+
messages: {
17+
en: { who: 'root' },
18+
ja: { who: 'ルート' }
19+
}
20+
})
21+
})
22+
23+
vm.$t('foo.bar.buz')
24+
assert(spy.notCalled === true)
25+
26+
// change
27+
vm.$i18n.silentTranslationWarn = false
28+
vm.$t('foo.bar.buz')
29+
assert(spy.callCount === 2)
30+
})
31+
})
32+
33+
describe('silentFallbackWarn', () => {
34+
let i18n
35+
beforeEach(() => {
36+
i18n = new VueI18n({
37+
locale: 'hu',
38+
fallbackLocale: 'en',
39+
silentFallbackWarn: true,
740
messages: {
8-
en: { who: 'root' },
9-
ja: { who: 'ルート' }
41+
en: { winner: 'winner' },
42+
hu: { chickenDinner: 'csirkevacsora' }
1043
}
1144
})
1245
})
1346

14-
const spy = sinon.spy(console, 'warn')
15-
vm.$t('foo.bar.buz')
16-
assert(spy.notCalled === true)
47+
it('should suppress `Fall back to ${fallback} locale` warnings', () => {
48+
const vm = new Vue({ i18n })
49+
const warningRegex = /Fall back to .* 'en' locale./
50+
vm.$t('winner')
51+
assert(spy.getCalls().some(call => call.args[0].match(warningRegex)) === false)
1752

18-
// change
19-
vm.$i18n.silentTranslationWarn = false
20-
vm.$t('foo.bar.buz')
21-
assert(spy.callCount === 2)
53+
vm.$i18n.silentFallbackWarn = false
54+
vm.$t('winner')
55+
assert(spy.getCalls().some(call => call.args[0].match(warningRegex)) === true)
56+
})
2257

23-
spy.restore()
58+
it('should suppress `Fall back to root locale` warnings.', () => {
59+
const el = document.createElement('div')
60+
const root = new Vue({
61+
i18n,
62+
components: {
63+
subComponent: {
64+
i18n: { messages: { hu: { name: 'Név' } } },
65+
render (h) { return h('p') }
66+
}
67+
},
68+
render (h) { return h('sub-component') }
69+
}).$mount(el)
70+
const vm = root.$children[0]
71+
const warningRegex = /Fall back to .* root locale./
72+
73+
vm.$t('chickenDinner')
74+
assert(spy.getCalls().some(call => call.args[0].match(warningRegex)) === false)
75+
76+
vm.$i18n.silentFallbackWarn = false
77+
vm.$t('chickenDinner')
78+
assert(spy.getCalls().some(call => call.args[0].match(warningRegex)) === true)
79+
})
80+
81+
describe('if first try is null or undefined,', () => {
82+
it('should suppress `not a string` warnings for fallback to fallbackLocale.', () => {
83+
const vm = new Vue({ i18n })
84+
const warningRegex = /Value of .* is not a string./
85+
vm.$t('winner')
86+
assert(spy.getCalls().some(call => call.args[0].match(warningRegex)) === false)
87+
88+
vm.$i18n.silentFallbackWarn = false
89+
vm.$t('winner')
90+
assert(spy.getCalls().some(call => call.args[0].match(warningRegex)) === true)
91+
})
92+
93+
it('should supress `not a string` warnings for fallback to root.', () => {
94+
const el = document.createElement('div')
95+
const root = new Vue({
96+
i18n,
97+
components: {
98+
subComponent: {
99+
i18n: { messages: { hu: { name: 'Név' } } },
100+
render (h) { return h('p') }
101+
}
102+
},
103+
render (h) { return h('sub-component') }
104+
}).$mount(el)
105+
const vm = root.$children[0]
106+
const warningRegex = /Value of .* is not a string./
107+
vm.$t('chickenDinner')
108+
assert(spy.getCalls().some(call => call.args[0].match(warningRegex)) === false)
109+
110+
vm.$i18n.silentFallbackWarn = false
111+
vm.$t('chickenDinner')
112+
assert(spy.getCalls().some(call => call.args[0].match(warningRegex)) === true)
113+
})
114+
})
115+
116+
describe('if first try is not null, undefined, array, plain object or string,', () => {
117+
it('should suppress `not a string` warnings for fallback to fallbackLocale.', () => {
118+
const vm = new Vue({
119+
i18n: new VueI18n({
120+
locale: 'hu',
121+
fallbackLocale: 'en',
122+
silentFallbackWarn: true,
123+
messages: {
124+
en: { winner: 'winner' },
125+
hu: { winner: true } // translation value is boolean
126+
}
127+
})
128+
})
129+
const warningRegex = /Value of .* is not a string./
130+
vm.$t('winner')
131+
assert(spy.getCalls().some(call => call.args[0].match(warningRegex)) === false)
132+
133+
vm.$i18n.silentFallbackWarn = false
134+
vm.$t('winner')
135+
assert(spy.getCalls().some(call => call.args[0].match(warningRegex)) === true)
136+
})
137+
138+
it('should supress `not a string` warnings for fallback to root.', () => {
139+
const el = document.createElement('div')
140+
const root = new Vue({
141+
i18n,
142+
components: {
143+
subComponent: {
144+
i18n: { messages: { hu: { chickenDinner: 11 } } }, // translation value is number
145+
render (h) { return h('p') }
146+
}
147+
},
148+
render (h) { return h('sub-component') }
149+
}).$mount(el)
150+
const vm = root.$children[0]
151+
const warningRegex = /Value of .* is not a string./
152+
vm.$t('chickenDinner')
153+
assert(spy.getCalls().some(call => call.args[0].match(warningRegex)) === false)
154+
155+
vm.$i18n.silentFallbackWarn = false
156+
vm.$t('chickenDinner')
157+
assert(spy.getCalls().some(call => call.args[0].match(warningRegex)) === true)
158+
})
159+
})
160+
161+
it('should not suppress `not a string` warnings when no further fallback is possible.', () => {
162+
const vm = new Vue({ i18n })
163+
const warningRegex = /Value of .* is not a string./
164+
vm.$t('loser')
165+
assert(spy.getCalls().some(call => call.args[0].match(warningRegex)) === true)
166+
})
24167
})
25168
})

vuepress/api/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,17 @@ Whether suppress warnings outputted when localization fails.
259259

260260
If `true`, suppress localization fail warnings.
261261

262+
#### silentFallbackWarn
263+
264+
> :new: 8.8+
265+
266+
* **Type:** `Boolean`
267+
* **Default:** `false`
268+
269+
Whether suppress warnings when falling back to either `fallbackLocale` or `root`.
270+
271+
If `true`, warnings will be generated only when no translation is available at all, and not for fallbacks.
272+
262273
#### preserveDirectiveContent
263274

264275
> 8.7+

vuepress/guide/component.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,15 @@ Outputs the following:
7373

7474
As in the example above, if the component doesn't have the locale message, it falls back to globally defined localization info. The component uses the language set in the root instance (in the above example: `locale: 'ja'`).
7575

76+
Note, that by default falling back to root locale generates two warnings in the console:
77+
78+
```console
79+
[vue-i18n] Value of key 'message.greeting' is not a string!
80+
[vue-i18n] Fall back to translate the keypath 'message.greeting' with root locale.
81+
```
82+
83+
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.
84+
7685
If you hope localize in the component locale, you can realize with `sync: false` and `locale` in `i18n` option.
7786

7887
## Translation in functional component

vuepress/guide/fallback.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,12 @@ Output the below:
3333
```html
3434
<p>hello world</p>
3535
```
36+
37+
Note, that by default falling back to `fallbackLocale` generates two console warnings:
38+
39+
```console
40+
[vue-i18n] Value of key 'message' is not a string!
41+
[vue-i18n] Fall back to translate the keypath 'message' with 'en' locale.
42+
```
43+
44+
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.

0 commit comments

Comments
 (0)