Skip to content

Commit 5d18f3e

Browse files
committed
Add ignoreEmptyKeys option to ignore empty keys in source files
Fixes #634
1 parent 7dfd4ea commit 5d18f3e

File tree

7 files changed

+97
-7
lines changed

7 files changed

+97
-7
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
# Unreleased
4+
5+
- Add `ignoreEmptyKeys` option (default: true) to ignore empty keys when scanning source files #634
6+
37
# 9.3.0
48

59
- Allow to use multiple translation functions with different namespaces and keyPrefixes #1083 #494 #973 #737

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,11 @@ export default {
252252
// Example:
253253
// {
254254
// lineWidth: -1,
255-
// }
255+
256+
ignoreEmptyKeys: true,
257+
// When true (default), ignores empty keys when scanning source files.
258+
// This allows using t('') which is useful with i18next's "returnEmptyString: false" setting.
259+
// When false, empty keys are included in the translation files and will fail with --fail-on-update.
256260
}
257261
```
258262

index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,4 +129,5 @@ export interface UserConfig {
129129
resetDefaultValueLocale?: string | null
130130
i18nextOptions?: Record<string, unknown> | null
131131
yamlOptions?: Record<string, unknown> | null
132+
ignoreEmptyKeys?: boolean
132133
}

src/helpers.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,16 @@ function dotPathToHash(entry, target = {}, options = {}) {
3232
entry.keyWithNamespace.length
3333
)
3434

35-
// There is no key to process so we return an empty object
36-
if (!key) {
35+
// The key is empty so we return an empty object
36+
if (key === '') {
3737
if (!target[entry.namespace]) {
3838
target[entry.namespace] = {}
3939
}
40+
41+
if (!options.ignoreEmptyKeys) {
42+
target[entry.namespace][key] = '';
43+
}
44+
4045
return { target, duplicate, conflict }
4146
}
4247

src/transform.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export default class i18nTransform extends Transform {
4040
customValueTemplate: null,
4141
failOnWarnings: false,
4242
yamlOptions: null,
43+
ignoreEmptyKeys: true,
4344
}
4445

4546
this.options = { ...this.defaults, ...options }
@@ -180,6 +181,7 @@ export default class i18nTransform extends Transform {
180181
pluralSeparator: this.options.pluralSeparator,
181182
value: this.options.defaultValue,
182183
customValueTemplate: this.options.customValueTemplate,
184+
ignoreEmptyKeys: this.options.ignoreEmptyKeys,
183185
})
184186

185187
if (duplicate) {
@@ -197,6 +199,9 @@ export default class i18nTransform extends Transform {
197199
}`
198200
)
199201
}
202+
} else if (entry.key === '' && this.options.ignoreEmptyKeys) {
203+
// Ignore empty keys when scanning source files
204+
// This allows using t('') which is useful with i18next's "returnEmptyString: false" setting
200205
} else {
201206
uniqueCount[entry.namespace] += 1
202207
if (suffix) {

test/helpers/dotPathToHash.test.js

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,37 @@ describe('dotPathToHash helper function', () => {
3636
})
3737

3838
it('handles an empty namespace', (done) => {
39-
const { target, duplicate } = dotPathToHash({
40-
keyWithNamespace: 'ns.',
41-
namespace: 'ns',
42-
})
39+
const { target, duplicate } = dotPathToHash(
40+
{
41+
keyWithNamespace: 'ns.',
42+
namespace: 'ns',
43+
},
44+
{},
45+
{
46+
ignoreEmptyKeys: true,
47+
}
48+
)
4349
assert.deepEqual(target, { ns: {} })
4450
assert.equal(duplicate, false)
4551
done()
4652
})
4753

54+
it('handles an empty namespace when ignoreEmptyKeys is false', (done) => {
55+
const { target, duplicate } = dotPathToHash(
56+
{
57+
keyWithNamespace: 'ns.',
58+
namespace: 'ns',
59+
},
60+
{},
61+
{
62+
ignoreEmptyKeys: false,
63+
}
64+
)
65+
assert.deepEqual(target, { ns: { '': '' } })
66+
assert.equal(duplicate, false)
67+
done()
68+
})
69+
4870
it('handles a target hash', (done) => {
4971
const { target, duplicate } = dotPathToHash(
5072
{ keyWithNamespace: 'one.two.three' },

test/parser.test.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,55 @@ describe('parser', () => {
359359
i18nextParser.end(fakeFile)
360360
})
361361

362+
it('ignores empty keys by default', (done) => {
363+
let resultContent
364+
const i18nextParser = new i18nTransform({
365+
locales: ['en'],
366+
defaultNamespace: 'test_empty_keys',
367+
})
368+
const fakeFile = new Vinyl({
369+
contents: Buffer.from("t('key'); t('');"),
370+
path: 'file.js',
371+
})
372+
373+
i18nextParser.on('data', (file) => {
374+
if (file.relative.endsWith(path.normalize('en/test_empty_keys.json'))) {
375+
resultContent = JSON.parse(file.contents)
376+
}
377+
})
378+
i18nextParser.on('end', () => {
379+
assert.deepEqual(resultContent, { key: '' })
380+
done()
381+
})
382+
383+
i18nextParser.end(fakeFile)
384+
})
385+
386+
it('includes empty keys when ignoreEmptyKeys is false', (done) => {
387+
let resultContent
388+
const i18nextParser = new i18nTransform({
389+
locales: ['en'],
390+
defaultNamespace: 'test_ignore_empty',
391+
ignoreEmptyKeys: false,
392+
})
393+
const fakeFile = new Vinyl({
394+
contents: Buffer.from("t('key'); t('');"),
395+
path: 'file.js',
396+
})
397+
398+
i18nextParser.on('data', (file) => {
399+
if (file.relative.endsWith(path.normalize('en/test_ignore_empty.json'))) {
400+
resultContent = JSON.parse(file.contents)
401+
}
402+
})
403+
i18nextParser.on('end', () => {
404+
assert.deepEqual(resultContent, { key: '', '': '' })
405+
done()
406+
})
407+
408+
i18nextParser.end(fakeFile)
409+
})
410+
362411
it('applies withTranslation namespace globally', (done) => {
363412
let result
364413
const i18nextParser = new i18nTransform()

0 commit comments

Comments
 (0)