zlocalz
Version:
ZLocalz - TUI Locale Guardian for Flutter ARB l10n/i18n validation and translation with AI-powered fixes
239 lines • 9.02 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.Fixer = void 0;
const lodash = __importStar(require("lodash"));
class Fixer {
config;
sourceFile;
constructor(config, sourceFile) {
this.config = config;
this.sourceFile = sourceFile;
}
async autoFix(targetFile, issues) {
const fixedFile = lodash.cloneDeep(targetFile);
const appliedFixes = [];
for (const issue of issues) {
const fix = this.generateFix(issue, fixedFile);
if (fix && this.config.doAutoFix) {
this.applyFix(fix, fixedFile);
appliedFixes.push(fix);
}
}
if (this.config.preferOrder === 'mirror-source') {
this.reorderToMatchSource(fixedFile);
}
else if (this.config.preferOrder === 'alphabetical') {
this.reorderAlphabetically(fixedFile);
}
return { fixedFile, appliedFixes };
}
generateFix(issue, targetFile) {
switch (issue.type) {
case 'missing':
return this.generateMissingKeyFix(issue);
case 'extra':
return this.generateExtraKeyFix(issue);
case 'duplicate':
return this.generateDuplicateFix(issue, targetFile);
case 'placeholderMismatch':
return this.generatePlaceholderFix(issue);
case 'formatting':
return this.generateFormattingFix(issue);
case 'icuError':
return this.generateICUFix(issue);
default:
return null;
}
}
generateMissingKeyFix(issue) {
const sourceEntry = this.sourceFile.entries[issue.key];
const placeholders = this.extractPlaceholders(sourceEntry.value);
let newValue = 'TODO';
if (placeholders.length > 0) {
newValue = sourceEntry.value.replace(/[^{}]+/g, 'TODO');
}
return {
type: 'addMissing',
locale: issue.locale,
key: issue.key,
newValue,
description: `Add missing key "${issue.key}" with placeholder structure from source`
};
}
generateExtraKeyFix(issue) {
return {
type: 'removeExtra',
locale: issue.locale,
key: issue.key,
oldValue: issue.targetValue,
description: `Remove extra key "${issue.key}" not present in source`
};
}
generateDuplicateFix(issue, targetFile) {
return {
type: 'removeDuplicate',
locale: issue.locale,
key: issue.key,
oldValue: targetFile.entries[issue.key]?.value,
description: `Remove duplicate key "${issue.key}"`
};
}
generatePlaceholderFix(issue) {
if (!issue.sourceValue || !issue.targetValue)
return {
type: 'fixPlaceholder',
locale: issue.locale,
key: issue.key,
description: 'Cannot fix placeholder without source/target values'
};
const sourcePlaceholders = this.extractPlaceholders(issue.sourceValue);
let fixedValue = issue.targetValue;
const targetPlaceholders = this.extractPlaceholders(issue.targetValue);
const placeholderMap = new Map();
for (let i = 0; i < Math.min(sourcePlaceholders.length, targetPlaceholders.length); i++) {
if (sourcePlaceholders[i] !== targetPlaceholders[i]) {
placeholderMap.set(targetPlaceholders[i], sourcePlaceholders[i]);
}
}
for (const [oldPh, newPh] of placeholderMap) {
fixedValue = fixedValue.replace(new RegExp(`\\{${oldPh}\\}`, 'g'), `{${newPh}}`);
}
return {
type: 'fixPlaceholder',
locale: issue.locale,
key: issue.key,
oldValue: issue.targetValue,
newValue: fixedValue,
description: `Fix placeholder names to match source`
};
}
generateFormattingFix(issue) {
return {
type: 'fixFormatting',
locale: issue.locale,
key: issue.key,
oldValue: issue.targetValue,
newValue: issue.suggestion || issue.targetValue?.trim().replace(/\s+/g, ' '),
description: `Fix formatting issues in "${issue.key}"`
};
}
generateICUFix(issue) {
if (!issue.sourceValue)
return {
type: 'fixICU',
locale: issue.locale,
key: issue.key,
description: 'Cannot fix ICU without source value'
};
const icuPattern = /(\{[^,]+,\s*(plural|select|selectordinal),)([^}]+)\}/g;
const sourceMatches = [...issue.sourceValue.matchAll(icuPattern)];
let fixedValue = issue.targetValue || '';
for (const match of sourceMatches) {
const [fullMatch, _prefix, _type, _options] = match;
const structureOnly = fullMatch.replace(/[^{}=\s]+(?=[^{}]*[{}])/g, 'TODO');
fixedValue = structureOnly;
}
return {
type: 'fixICU',
locale: issue.locale,
key: issue.key,
oldValue: issue.targetValue,
newValue: fixedValue,
description: `Fix ICU structure to match source`
};
}
applyFix(fix, targetFile) {
switch (fix.type) {
case 'addMissing':
const sourceEntry = this.sourceFile.entries[fix.key];
targetFile.entries[fix.key] = {
key: fix.key,
value: fix.newValue || 'TODO',
metadata: sourceEntry.metadata,
description: sourceEntry.description,
placeholders: sourceEntry.placeholders
};
break;
case 'removeExtra':
case 'removeDuplicate':
delete targetFile.entries[fix.key];
delete targetFile.raw[fix.key];
delete targetFile.raw[`@${fix.key}`];
break;
case 'fixPlaceholder':
case 'fixFormatting':
case 'fixICU':
if (fix.newValue && targetFile.entries[fix.key]) {
targetFile.entries[fix.key].value = fix.newValue;
}
break;
}
}
reorderToMatchSource(targetFile) {
const sourceKeys = Object.keys(this.sourceFile.entries);
const reordered = {};
for (const key of sourceKeys) {
if (targetFile.entries[key]) {
reordered[key] = targetFile.entries[key];
}
}
const targetOnlyKeys = Object.keys(targetFile.entries)
.filter(k => !sourceKeys.includes(k));
for (const key of targetOnlyKeys) {
reordered[key] = targetFile.entries[key];
}
targetFile.entries = reordered;
}
reorderAlphabetically(targetFile) {
const sortedKeys = Object.keys(targetFile.entries).sort();
const reordered = {};
for (const key of sortedKeys) {
reordered[key] = targetFile.entries[key];
}
targetFile.entries = reordered;
}
extractPlaceholders(value) {
const placeholderRegex = /\{([^}]+)\}/g;
const placeholders = [];
let match;
while ((match = placeholderRegex.exec(value)) !== null) {
placeholders.push(match[1]);
}
return placeholders;
}
}
exports.Fixer = Fixer;
//# sourceMappingURL=fixer.js.map