Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

# Unreleased

- Add `ignoreEmptyKeys` option (default: true) to ignore empty keys when scanning source files #634

# 9.3.0

- Allow to use multiple translation functions with different namespaces and keyPrefixes #1083 #494 #973 #737
Expand Down
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,14 @@ export default {
// Example:
// {
// lineWidth: -1,
// }

ignoreEmptyKeys: true,
// When true (default), ignores empty keys when scanning source files.
//
// This allows using t('') without adding '' to translation files or
// failing update checks.
//
// When false, empty keys are included in the translation files and will fail with --fail-on-update.
}
```

Expand Down
1 change: 1 addition & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,5 @@ export interface UserConfig {
resetDefaultValueLocale?: string | null
i18nextOptions?: Record<string, unknown> | null
yamlOptions?: Record<string, unknown> | null
ignoreEmptyKeys?: boolean
}
9 changes: 7 additions & 2 deletions src/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,16 @@ function dotPathToHash(entry, target = {}, options = {}) {
entry.keyWithNamespace.length
)

// There is no key to process so we return an empty object
if (!key) {
// The key is empty so we return an empty object
if (key === '') {
if (!target[entry.namespace]) {
target[entry.namespace] = {}
}

if (!options.ignoreEmptyKeys) {
target[entry.namespace][key] = '';
}

return { target, duplicate, conflict }
}

Expand Down
5 changes: 5 additions & 0 deletions src/transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export default class i18nTransform extends Transform {
customValueTemplate: null,
failOnWarnings: false,
yamlOptions: null,
ignoreEmptyKeys: true,
}

this.options = { ...this.defaults, ...options }
Expand Down Expand Up @@ -180,6 +181,7 @@ export default class i18nTransform extends Transform {
pluralSeparator: this.options.pluralSeparator,
value: this.options.defaultValue,
customValueTemplate: this.options.customValueTemplate,
ignoreEmptyKeys: this.options.ignoreEmptyKeys,
})

if (duplicate) {
Expand All @@ -197,6 +199,9 @@ export default class i18nTransform extends Transform {
}`
)
}
} else if (entry.key === '' && this.options.ignoreEmptyKeys) {
// Ignore empty keys when scanning source files
// This allows using `t('')`
} else {
uniqueCount[entry.namespace] += 1
if (suffix) {
Expand Down
30 changes: 26 additions & 4 deletions test/helpers/dotPathToHash.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,37 @@ describe('dotPathToHash helper function', () => {
})

it('handles an empty namespace', (done) => {
const { target, duplicate } = dotPathToHash({
keyWithNamespace: 'ns.',
namespace: 'ns',
})
const { target, duplicate } = dotPathToHash(
{
keyWithNamespace: 'ns.',
namespace: 'ns',
},
{},
{
ignoreEmptyKeys: true,
}
)
assert.deepEqual(target, { ns: {} })
assert.equal(duplicate, false)
done()
})

it('handles an empty namespace when ignoreEmptyKeys is false', (done) => {
const { target, duplicate } = dotPathToHash(
{
keyWithNamespace: 'ns.',
namespace: 'ns',
},
{},
{
ignoreEmptyKeys: false,
}
)
assert.deepEqual(target, { ns: { '': '' } })
assert.equal(duplicate, false)
done()
})

it('handles a target hash', (done) => {
const { target, duplicate } = dotPathToHash(
{ keyWithNamespace: 'one.two.three' },
Expand Down
49 changes: 49 additions & 0 deletions test/parser.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,55 @@ describe('parser', () => {
i18nextParser.end(fakeFile)
})

it('ignores empty keys by default', (done) => {
let resultContent
const i18nextParser = new i18nTransform({
locales: ['en'],
defaultNamespace: 'test_empty_keys',
})
const fakeFile = new Vinyl({
contents: Buffer.from("t('key'); t('');"),
path: 'file.js',
})

i18nextParser.on('data', (file) => {
if (file.relative.endsWith(path.normalize('en/test_empty_keys.json'))) {
resultContent = JSON.parse(file.contents)
}
})
i18nextParser.on('end', () => {
assert.deepEqual(resultContent, { key: '' })
done()
})

i18nextParser.end(fakeFile)
})

it('includes empty keys when ignoreEmptyKeys is false', (done) => {
let resultContent
const i18nextParser = new i18nTransform({
locales: ['en'],
defaultNamespace: 'test_ignore_empty',
ignoreEmptyKeys: false,
})
const fakeFile = new Vinyl({
contents: Buffer.from("t('key'); t('');"),
path: 'file.js',
})

i18nextParser.on('data', (file) => {
if (file.relative.endsWith(path.normalize('en/test_ignore_empty.json'))) {
resultContent = JSON.parse(file.contents)
}
})
i18nextParser.on('end', () => {
assert.deepEqual(resultContent, { key: '', '': '' })
done()
})

i18nextParser.end(fakeFile)
})

it('applies withTranslation namespace globally', (done) => {
let result
const i18nextParser = new i18nTransform()
Expand Down