Skip to content

Commit

Permalink
feat: Print missing translation keys on extract and check
Browse files Browse the repository at this point in the history
closes #72
  • Loading branch information
freakzlike committed Nov 2, 2024
1 parent 8a7e644 commit 6f76d33
Show file tree
Hide file tree
Showing 2 changed files with 164 additions and 103 deletions.
44 changes: 29 additions & 15 deletions src/merge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const generateNewTranslations = async (
existingTranslations: TranslationMapLoad,
options: I18nExtractOptions
): Promise<TranslationMapWrite> => {
return generateTranslationMap(options, async ({ language, namespace }) => {
return generateTranslationMap(options, async ({ language, namespace, filePath }) => {
const _existingTranslations = existingTranslations[language]?.[namespace]?.translations || {}

let translations: TranslationStructure = options.keepMissing
Expand All @@ -27,13 +27,17 @@ export const generateNewTranslations = async (

const _translationKeys = translationKeys[namespace] || []
for (const translationKey of _translationKeys) {
[translations, result] = await writeTranslationStructure(
[translations, result] = await writeTranslationStructure({
translationKey,
_existingTranslations,
filePath,
language,
namespace,
fullTranslationKey: translationKey,
existingTranslations: _existingTranslations,
translations,
result,
options
)
})
}

return {
Expand All @@ -50,24 +54,33 @@ const mapTranslationStructure = (
: translationValue

export const writeTranslationStructure = async (
translationKey: string,
existingTranslations: TranslationStructure,
translations: TranslationStructure,
result: TranslationResultWrite,
options: I18nExtractOptions
): Promise<[TranslationStructure, TranslationResultWrite]> => {
{ translationKey, filePath, language, namespace, fullTranslationKey, existingTranslations, translations, result, options }: {
translationKey: string,
filePath: string,
language: string,
namespace: string,
fullTranslationKey: string,
existingTranslations: TranslationStructure,
translations: TranslationStructure,
result: TranslationResultWrite,
options: I18nExtractOptions
}): Promise<[TranslationStructure, TranslationResultWrite]> => {
const contextIdx = translationKey.indexOf('.')
// Nested translation
if (contextIdx > -1) {
const context = translationKey.substring(0, contextIdx)
const nestedKey = translationKey.substring(contextIdx + 1)
const [_translations, _result] = await writeTranslationStructure(
nestedKey,
mapTranslationStructure(existingTranslations[context]),
mapTranslationStructure(translations[context]),
const [_translations, _result] = await writeTranslationStructure({
translationKey: nestedKey,
filePath,
language,
namespace,
fullTranslationKey,
existingTranslations: mapTranslationStructure(existingTranslations[context]),
translations: mapTranslationStructure(translations[context]),
result,
options
)
})

translations[context] = _translations

Expand All @@ -87,6 +100,7 @@ export const writeTranslationStructure = async (
translations[translationKey] = defaultTranslation
}
if (translations[translationKey] === defaultTranslation) {
console.info(`Missing translation for key: "${fullTranslationKey}" in file: ${filePath}`)
result.untranslatedCount++
}

Expand Down
223 changes: 135 additions & 88 deletions tests/merge.spec.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,42 @@
import { describe, expect, it } from 'vitest'
import { writeTranslationStructure } from '@/merge'
import { I18nExtractOptions } from '@/types'

/**
* writeTranslationStructure
*/
describe('writeTranslationStructure', () => {
const defaultOptions: I18nExtractOptions = {
input: ['src/**'],
output: 'examples/default/locales/{{lng}}.json',
languages: ['de', 'en-GB']
}

it('should write simple translation key', async () => {
expect(await writeTranslationStructure('key_1', {
key_1: 'Key 1',
key_2: 'Key 2'
}, {}, {
const defaultParams = (translationKey: string): Omit<Parameters<typeof writeTranslationStructure>[0], 'existingTranslations'> => ({
translationKey,
fullTranslationKey: translationKey,
filePath: 'examples/default/locales/en.json',
language: 'en',
namespace: 'default',
translations: {},
result: {
translations: {},
untranslatedCount: 0
}, defaultOptions)).toStrictEqual([
},
options: {
input: ['src/**'],
output: 'examples/default/locales/{{lng}}.json',
languages: ['de', 'en-GB'],
suffixes: ['_plural']
}
})

it('should write simple translation key', async () => {
expect(await writeTranslationStructure({
...defaultParams('key_1'),
existingTranslations: {
key_1: 'Key 1',
key_1_plural: 'Key 1 plural',
key_1_other: 'Key 1 other',
key_2: 'Key 2'
}
})).toStrictEqual([
{
key_1: 'Key 1'
key_1: 'Key 1',
key_1_plural: 'Key 1 plural'
},
{
translations: expect.any(Object),
Expand All @@ -31,14 +46,15 @@ describe('writeTranslationStructure', () => {
})

it('should merge simple translation key', async () => {
expect(await writeTranslationStructure('key_1', {
key_1: 'Key 1'
}, {
key_2: 'Key 2'
}, {
translations: {},
untranslatedCount: 0
}, defaultOptions)).toStrictEqual([
expect(await writeTranslationStructure({
...defaultParams('key_1'),
existingTranslations: {
key_1: 'Key 1'
},
translations: {
key_2: 'Key 2'
}
})).toStrictEqual([
{
key_1: 'Key 1',
key_2: 'Key 2'
Expand All @@ -51,12 +67,12 @@ describe('writeTranslationStructure', () => {
})

it('should write simple translation key with missing translation', async () => {
expect(await writeTranslationStructure('key_1', {
key_2: 'Key 2'
}, {}, {
translations: {},
untranslatedCount: 0
}, defaultOptions)).toStrictEqual([
expect(await writeTranslationStructure({
...defaultParams('key_1'),
existingTranslations: {
key_2: 'Key 2'
}
})).toStrictEqual([
{
key_1: '__MISSING_TRANSLATION__'
},
Expand All @@ -68,14 +84,20 @@ describe('writeTranslationStructure', () => {
})

it('should write simple translation key with missing translation', async () => {
expect(await writeTranslationStructure('key_1', {
key_2: 'Key 2'
}, {}, {
translations: {},
untranslatedCount: 5
}, {
...defaultOptions,
defaultValue: '__NEW_KEY__'
const params = defaultParams('key_1')
expect(await writeTranslationStructure({
...params,
existingTranslations: {
key_2: 'Key 2'
},
result: {
translations: {},
untranslatedCount: 5
},
options: {
...params.options,
defaultValue: '__NEW_KEY__'
}
})).toStrictEqual([
{
key_1: '__NEW_KEY__'
Expand All @@ -87,15 +109,34 @@ describe('writeTranslationStructure', () => {
])
})

it('should write and count missing translation key', async () => {
expect(await writeTranslationStructure({
...defaultParams('key_1'),
existingTranslations: {
key_1: '__MISSING_TRANSLATION__',
key_1_plural: '__MISSING_TRANSLATION__'
}
})).toStrictEqual([
{
key_1: '__MISSING_TRANSLATION__',
key_1_plural: '__MISSING_TRANSLATION__'
},
{
translations: expect.any(Object),
untranslatedCount: 2
}
])
})

it('should write missing translation key if different context', async () => {
expect(await writeTranslationStructure('key_1', {
key_1: {
nested: '1'
expect(await writeTranslationStructure({
...defaultParams('key_1'),
existingTranslations: {
key_1: {
nested: '1'
}
}
}, {}, {
translations: {},
untranslatedCount: 0
}, defaultOptions)).toStrictEqual([
})).toStrictEqual([
{
key_1: '__MISSING_TRANSLATION__'
},
Expand All @@ -107,15 +148,15 @@ describe('writeTranslationStructure', () => {
})

it('should write nested translation key', async () => {
expect(await writeTranslationStructure('context.nested', {
context: {
nested: 'Nested translation',
other: 'other'
expect(await writeTranslationStructure({
...defaultParams('context.nested'),
existingTranslations: {
context: {
nested: 'Nested translation',
other: 'other'
}
}
}, {}, {
translations: {},
untranslatedCount: 0
}, defaultOptions)).toStrictEqual([
})).toStrictEqual([
{
context: {
nested: 'Nested translation'
Expand All @@ -129,18 +170,18 @@ describe('writeTranslationStructure', () => {
})

it('should write multiple nested translation key', async () => {
expect(await writeTranslationStructure('context.nested.more.key', {
context: {
nested: {
more: {
key: 'Key'
expect(await writeTranslationStructure({
...defaultParams('context.nested.more.key'),
existingTranslations: {
context: {
nested: {
more: {
key: 'Key'
}
}
}
}
}, {}, {
translations: {},
untranslatedCount: 0
}, defaultOptions)).toStrictEqual([
})).toStrictEqual([
{
context: {
nested: {
Expand All @@ -158,14 +199,18 @@ describe('writeTranslationStructure', () => {
})

it('should write nested translation key with missing translation', async () => {
expect(await writeTranslationStructure('context.nested', {
context: {
other: 'other'
expect(await writeTranslationStructure({
...defaultParams('context.nested'),
existingTranslations: {
context: {
other: 'other'
}
},
result: {
translations: {},
untranslatedCount: 2
}
}, {}, {
translations: {},
untranslatedCount: 2
}, defaultOptions)).toStrictEqual([
})).toStrictEqual([
{
context: {
nested: '__MISSING_TRANSLATION__'
Expand All @@ -179,19 +224,20 @@ describe('writeTranslationStructure', () => {
})

it('should merge nested translation key', async () => {
expect(await writeTranslationStructure('context.nested', {
context: {
nested: 'Nested translation'
}
}, {
key: 'key',
context: {
other: 'other'
expect(await writeTranslationStructure({
...defaultParams('context.nested'),
existingTranslations: {
context: {
nested: 'Nested translation'
}
},
translations: {
key: 'key',
context: {
other: 'other'
}
}
}, {
translations: {},
untranslatedCount: 0
}, defaultOptions)).toStrictEqual([
})).toStrictEqual([
{
key: 'key',
context: {
Expand All @@ -207,16 +253,17 @@ describe('writeTranslationStructure', () => {
})

it('should handle existing string on nested translation', async () => {
expect(await writeTranslationStructure('context.nested', {
context: {
nested: 'Nested translation'
expect(await writeTranslationStructure({
...defaultParams('context.nested'),
existingTranslations: {
context: {
nested: 'Nested translation'
}
},
translations: {
context: 'Context'
}
}, {
context: 'Context'
}, {
translations: {},
untranslatedCount: 0
}, defaultOptions)).toStrictEqual([
})).toStrictEqual([
{
context: {
nested: 'Nested translation'
Expand Down

0 comments on commit 6f76d33

Please sign in to comment.