UNPKG

i18n-llm-translate

Version:

Automatically translates namespace-based JSON translation files across multiple languages from any source language

245 lines (244 loc) 8.73 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.formatLanguageContainerDirectoryName = formatLanguageContainerDirectoryName; exports.flattenObject = flattenObject; exports.unflattenObject = unflattenObject; exports.clearNullsFromResult = clearNullsFromResult; exports.countTranslatedKeys = countTranslatedKeys; exports.countTranslatedCharacters = countTranslatedCharacters; exports.countKeysInObject = countKeysInObject; exports.countMissingTranslationCharacters = countMissingTranslationCharacters; exports.formatDuration = formatDuration; function formatLanguageContainerDirectoryName(languageCode, options) { if (!options.namesMapping) return languageCode; if (!options.namesMapping.languages) return languageCode; if (typeof options.namesMapping.languages === 'function') return options.namesMapping.languages(languageCode, options); if (languageCode === options.baseLanguageCode) { if (!options.namesMapping.languages.base) return languageCode; return formatLanguageDirectoryName(options.namesMapping.languages.base, languageCode); } if (!options.namesMapping.languages.targets) return languageCode; return formatLanguageDirectoryName(options.namesMapping.languages.targets, languageCode); } function extractFirstBracedValue(str) { const match = str.match(/\{(.*?)}/); return match ? match[1] : null; } function formatLanguageDirectoryName(format, languageCode) { const variable = extractFirstBracedValue(format); if (!variable) throw new Error(`Invalid format: "${format}". Expected a placeholder like "{language}", but none was found.`); const tmp = variable.replace('language', ''); if (variable === tmp) throw new Error(`Invalid format: "${format}". Found placeholder "{${variable}}", but it does not contain "language".`); if (tmp === '!') languageCode = languageCode.toUpperCase(); if (tmp === '_') languageCode = languageCode.toLowerCase(); return format.replace(`{${variable}}`, languageCode); } function flattenObject(obj, parentKey = '') { let result = {}; for (const key in obj) { if (obj.hasOwnProperty(key)) { const newKey = parentKey ? `${parentKey}.${key}` : key; if (typeof obj[key] === 'object' && obj[key] !== null) { Object.assign(result, flattenObject(obj[key], newKey)); } else { result[newKey] = String(obj[key]); } } } return result; } function unflattenObject(flatTranslations) { const result = {}; for (const key in flatTranslations) { if (flatTranslations.hasOwnProperty(key)) { const keys = key.split('.'); let current = result; keys.forEach((part, index) => { if (index === keys.length - 1) { current[part] = flatTranslations[key]; } else { if (!current[part]) { current[part] = {}; } current = current[part]; } }); } } return result; } function clearNullsFromResult(result) { function walk(obj) { if (obj === null || obj === undefined) { return undefined; } if (typeof obj !== 'object' || Array.isArray(obj)) { return obj; } const cleaned = {}; let hasValidKeys = false; for (const key in obj) { if (obj.hasOwnProperty(key)) { const value = walk(obj[key]); // Only add the key if the value is not null/undefined if (value !== null && value !== undefined) { cleaned[key] = value; hasValidKeys = true; } } } // Return undefined if the object is empty after cleaning return hasValidKeys ? cleaned : undefined; } const cleanedResult = {}; for (const languageCode in result) { if (result.hasOwnProperty(languageCode)) { const cleanedTranslations = walk(result[languageCode]); // Only include language if it has valid translations if (cleanedTranslations !== undefined) { cleanedResult[languageCode] = cleanedTranslations; } } } return cleanedResult; } function countTranslatedKeys(result) { function countInObject(obj) { if (!obj || typeof obj !== 'object') { return 0; } let count = 0; for (const key in obj) { if (obj.hasOwnProperty(key)) { const value = obj[key]; if (typeof value === 'string') { // This is a translation key count++; } else if (typeof value === 'object' && value !== null) { // This is a nested object, count recursively count += countInObject(value); } } } return count; } let totalCount = 0; // Count translations across all languages for (const languageCode in result) { if (result.hasOwnProperty(languageCode)) { totalCount += countInObject(result[languageCode]); } } return totalCount; } function countTranslatedCharacters(obj) { function countInObject(obj) { if (!obj || typeof obj !== 'object') { return 0; } let count = 0; for (const key in obj) { if (obj.hasOwnProperty(key)) { const value = obj[key]; if (typeof value === 'string') { // Count characters in translation value count += value.length; } else if (typeof value === 'object' && value !== null) { // This is a nested object, count recursively count += countInObject(value); } } } return count; } return countInObject(obj); } function countKeysInObject(obj) { function countInObject(obj) { if (!obj || typeof obj !== 'object') { return 0; } let count = 0; for (const key in obj) { if (obj.hasOwnProperty(key)) { const value = obj[key]; if (typeof value === 'string') { // This is a translation key count++; } else if (typeof value === 'object' && value !== null) { // This is a nested object, count recursively count += countInObject(value); } } } return count; } return countInObject(obj); } function countMissingTranslationCharacters(baseLanguageTranslations, targetLanguageTranslationsKeys) { function countCharactersForPath(basePath, baseObj, targetLanguages) { if (typeof baseObj === 'string') { // Count how many target languages need this translation let languageCount = 0; for (const languageCode in targetLanguages) { if (hasPathInObject(targetLanguages[languageCode], basePath)) { languageCount++; } } return baseObj.length * languageCount; } if (typeof baseObj === 'object' && baseObj !== null) { let totalChars = 0; for (const key in baseObj) { if (baseObj.hasOwnProperty(key)) { const newPath = basePath ? `${basePath}.${key}` : key; totalChars += countCharactersForPath(newPath, baseObj[key], targetLanguages); } } return totalChars; } return 0; } return countCharactersForPath('', baseLanguageTranslations, targetLanguageTranslationsKeys); } function hasPathInObject(obj, path) { if (!obj || typeof obj !== 'object') { return false; } const keys = path.split('.'); let current = obj; for (const key of keys) { if (!current.hasOwnProperty(key)) { return false; } current = current[key]; } return true; } function formatDuration(ms) { if (ms < 1000) { return `${ms}ms`; } else if (ms < 60000) { return `${(ms / 1000).toFixed(1)}s`; } else { const minutes = Math.floor(ms / 60000); const seconds = ((ms % 60000) / 1000).toFixed(1); return `${minutes}m ${seconds}s`; } }