UNPKG

zlocalz

Version:

ZLocalz - TUI Locale Guardian for Flutter ARB l10n/i18n validation and translation with AI-powered fixes

239 lines 9.02 kB
"use strict"; 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