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
JavaScript
;
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`;
}
}