From 6f76d336db66028c5374d5f2377ba5e923c3a22e Mon Sep 17 00:00:00 2001 From: freakzlike Date: Sat, 2 Nov 2024 19:55:17 +0100 Subject: [PATCH] feat: Print missing translation keys on extract and check closes #72 --- src/merge.ts | 44 ++++++--- tests/merge.spec.ts | 223 +++++++++++++++++++++++++++----------------- 2 files changed, 164 insertions(+), 103 deletions(-) diff --git a/src/merge.ts b/src/merge.ts index dc9c0b5..9b6f608 100644 --- a/src/merge.ts +++ b/src/merge.ts @@ -14,7 +14,7 @@ export const generateNewTranslations = async ( existingTranslations: TranslationMapLoad, options: I18nExtractOptions ): Promise => { - return generateTranslationMap(options, async ({ language, namespace }) => { + return generateTranslationMap(options, async ({ language, namespace, filePath }) => { const _existingTranslations = existingTranslations[language]?.[namespace]?.translations || {} let translations: TranslationStructure = options.keepMissing @@ -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 { @@ -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 @@ -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++ } diff --git a/tests/merge.spec.ts b/tests/merge.spec.ts index b72f59c..ab5be0a 100644 --- a/tests/merge.spec.ts +++ b/tests/merge.spec.ts @@ -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[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), @@ -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' @@ -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__' }, @@ -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__' @@ -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__' }, @@ -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' @@ -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: { @@ -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__' @@ -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: { @@ -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'