xlf-sync 1.0.0 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -1
- package/dist/cli.js +15 -10
- package/dist/cli.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -63,7 +63,37 @@ npx xlf-sync sync \
|
|
|
63
63
|
|
|
64
64
|
---
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
### 2. The `report` Command
|
|
70
|
+
|
|
71
|
+
Generates a detailed console report about the translation coverage for each locale file. Useful for getting a quick overview of work remaining.
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
npx xlf-sync report --source src/locale/messages.xlf --locales "src/locale/messages.*.xlf"
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Output Example:**
|
|
78
|
+
```text
|
|
79
|
+
┌────────┬─────┬──────┬────────────┬─────────┬────────┬───────┐
|
|
80
|
+
│ Locale │ XLF │ Keys │ Translated │ Pending │ % Cov │ Words │
|
|
81
|
+
├────────┼─────┼──────┼────────────┼─────────┼────────┼───────┤
|
|
82
|
+
│ de │ 2.0 │ 120 │ 115 │ 5 │ 95.8% │ 432 │
|
|
83
|
+
│ fr │ 2.0 │ 120 │ 40 │ 80 │ 33.3% │ 150 │
|
|
84
|
+
└────────┴─────┴──────┴────────────┴─────────┴────────┴───────┘
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
#### Report Options
|
|
88
|
+
|
|
89
|
+
| Option | Default | Description |
|
|
90
|
+
| :--- | :--- | :--- |
|
|
91
|
+
| `--source <path>` | `src/locale/messages.xlf` | Path to the source XLIFF file. |
|
|
92
|
+
| `--locales <glob>` | `src/locale/messages.*.xlf` | Glob pattern for target locale files. |
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
### 3. The `check` Command
|
|
67
97
|
|
|
68
98
|
A read-only command designed for **Continuous Integration (CI)** pipelines. It verifies the state of your translations without modifying any files.
|
|
69
99
|
|
|
@@ -120,6 +150,23 @@ Add this step to your Pull Request validation workflow to ensure no developer me
|
|
|
120
150
|
|
|
121
151
|
---
|
|
122
152
|
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## 🤝 Contributing
|
|
157
|
+
|
|
158
|
+
We welcome contributions! Whether it's bug reports, feature requests, or code contributions, your help is appreciated.
|
|
159
|
+
|
|
160
|
+
Please read our [Contributing Guidelines](CONTRIBUTING.md) to get started.
|
|
161
|
+
|
|
162
|
+
**Quick ways to contribute:**
|
|
163
|
+
- ⭐ [Star the project](https://github.com/atheodosiou/xlf-sync) on GitHub
|
|
164
|
+
- 🐛 [Report bugs](https://github.com/atheodosiou/xlf-sync/issues/new)
|
|
165
|
+
- 💡 [Suggest features](https://github.com/atheodosiou/xlf-sync/issues/new)
|
|
166
|
+
- 🔧 [Submit pull requests](https://github.com/atheodosiou/xlf-sync/pulls)
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
123
170
|
## 📄 License
|
|
124
171
|
|
|
125
172
|
MIT © Anastasios Theodosiou
|
package/dist/cli.js
CHANGED
|
@@ -446,7 +446,7 @@ import chalk3 from "chalk";
|
|
|
446
446
|
// package.json
|
|
447
447
|
var package_default = {
|
|
448
448
|
name: "xlf-sync",
|
|
449
|
-
version: "
|
|
449
|
+
version: "1.0.3",
|
|
450
450
|
description: "Sync Angular XLIFF (1.2 & 2.0) locale files with messages.xlf",
|
|
451
451
|
type: "module",
|
|
452
452
|
bin: {
|
|
@@ -494,21 +494,24 @@ var package_default = {
|
|
|
494
494
|
};
|
|
495
495
|
|
|
496
496
|
// src/ui/banner.ts
|
|
497
|
-
function
|
|
497
|
+
function getBanner(command) {
|
|
498
498
|
const logo = figlet.textSync("XLF-SYNC", {
|
|
499
499
|
font: "Standard",
|
|
500
|
-
// μπορείς να αλλάξεις font
|
|
501
500
|
horizontalLayout: "default",
|
|
502
501
|
verticalLayout: "default"
|
|
503
502
|
});
|
|
504
|
-
|
|
505
|
-
|
|
503
|
+
const lines = [
|
|
504
|
+
chalk3.cyanBright(logo),
|
|
506
505
|
chalk3.bold.white(
|
|
507
506
|
`XLF-SYNC v${package_default.version}${command ? ` [${command}]` : ""}`
|
|
508
|
-
)
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
507
|
+
),
|
|
508
|
+
chalk3.gray("Sync & validate Angular XLIFF files"),
|
|
509
|
+
chalk3.gray("Author: Anastasios Theodosiou\n")
|
|
510
|
+
];
|
|
511
|
+
return lines.join("\n");
|
|
512
|
+
}
|
|
513
|
+
function renderBanner(command) {
|
|
514
|
+
console.log(getBanner(command));
|
|
512
515
|
}
|
|
513
516
|
|
|
514
517
|
// src/commands/check.ts
|
|
@@ -812,7 +815,9 @@ function registerSyncCommand(program2) {
|
|
|
812
815
|
|
|
813
816
|
// src/cli.ts
|
|
814
817
|
var program = new Command();
|
|
815
|
-
program.name("xlf-sync").description(
|
|
818
|
+
program.name("xlf-sync").description(
|
|
819
|
+
"A powerful CLI tool to keep your Angular XLIFF translation files (1.2 & 2.0) in sync with your source messages.\n\nAutomatically merges new translations, marks obsolete keys, and validates structure."
|
|
820
|
+
).version("1.0.1").addHelpText("before", getBanner());
|
|
816
821
|
registerSyncCommand(program);
|
|
817
822
|
registerCheckCommand(program);
|
|
818
823
|
registerReportCommand(program);
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../src/commands/check.ts","../src/ui/console.ts","../src/core/discover.ts","../src/core/xlf/index.ts","../src/core/xlf/v12.ts","../src/core/xlf/v20.ts","../src/core/xlf/write-v12.ts","../src/core/xlf/write-v20.ts","../src/core/sync.ts","../src/ui/table.ts","../src/ui/banner.ts","../package.json","../src/commands/report.ts","../src/commands/sync.ts","../src/core/graveyard.ts"],"sourcesContent":["#!/usr/bin/env node\r\nimport { Command } from \"commander\";\r\nimport { registerCheckCommand } from \"./commands/check\";\r\nimport { registerReportCommand } from \"./commands/report\";\r\nimport { registerSyncCommand } from \"./commands/sync\";\r\n\r\nconst program = new Command();\r\n\r\nprogram\r\n .name(\"xlf-sync\")\r\n .description(\"Sync Angular XLIFF (1.2 & 2.0) locale files with messages.xlf\")\r\n .version(\"0.1.0\");\r\n\r\nregisterSyncCommand(program);\r\nregisterCheckCommand(program);\r\nregisterReportCommand(program);\r\n\r\nprogram.parse(process.argv);\r\n\r\n\r\n// https://chatgpt.com/c/6978c595-664c-8328-9756-4a51d628277b","import { Command } from \"commander\";\r\nimport ora from \"ora\";\r\nimport { ui } from \"../ui/console.js\";\r\nimport { discoverFiles } from \"../core/discover.js\";\r\nimport { readFile } from \"node:fs/promises\";\r\nimport { parseXlf } from \"../core/xlf/index.js\";\r\nimport { syncLocale } from \"../core/sync.js\";\r\nimport { renderSummaryTable } from \"../ui/table.js\";\r\nimport { renderBanner } from \"../ui/banner.js\";\r\n\r\nexport function registerCheckCommand(program: Command) {\r\n program\r\n .command(\"check\")\r\n .description(\"Check if locale XLF files are in sync (CI-friendly)\")\r\n .option(\"--source <path>\", \"Path to source messages.xlf\", \"src/locale/messages.xlf\")\r\n .option(\"--locales <glob>\", \"Glob for locale files\", \"src/locale/messages.*.xlf\")\r\n .option(\"--fail-on-missing\", \"Exit non-zero if missing targets exist\", false)\r\n .option(\"--fail-on-obsolete\", \"Exit non-zero if obsolete keys exist\", false)\r\n .option(\"--fail-on-added\", \"Exit non-zero if new keys would be added\", false)\r\n .option(\"--new-target <mode>\", \"todo | empty | source (used for diff only)\", \"todo\")\r\n .option(\"--verbose\", \"Print missing keys per locale\", false)\r\n .action(async (opts) => {\r\n renderBanner(\"check\");\r\n\r\n const spinner = ora(\"Checking...\").start();\r\n\r\n try {\r\n const res = await discoverFiles({\r\n sourcePath: opts.source,\r\n localesGlob: opts.locales,\r\n });\r\n\r\n const sourceXml = await readFile(res.sourcePath, \"utf-8\");\r\n const sourceParsed = parseXlf(sourceXml);\r\n\r\n const rows: any[] = [];\r\n const missingKeysByLocale: Record<string, string[]> = {};\r\n\r\n let hasMissing = false;\r\n let hasObsolete = false;\r\n let hasAdded = false;\r\n\r\n for (const lf of res.localeFiles) {\r\n const xml = await readFile(lf.filePath, \"utf-8\");\r\n const parsed = parseXlf(xml);\r\n\r\n const diff = syncLocale(sourceParsed.entries, parsed.entries, {\r\n newTarget: opts.newTarget,\r\n // For check: we only want detection, not modifications\r\n obsolete: \"delete\",\r\n });\r\n\r\n const missingTargets = diff.missingTargets.length;\r\n const obsolete = diff.obsoleteKeys.length;\r\n const added = diff.addedKeys.length;\r\n\r\n if (missingTargets > 0) {\r\n hasMissing = true;\r\n missingKeysByLocale[lf.locale] = diff.missingTargets.slice();\r\n }\r\n if (obsolete > 0) hasObsolete = true;\r\n if (added > 0) hasAdded = true;\r\n\r\n rows.push({\r\n locale: lf.locale,\r\n version: parsed.version,\r\n sourceKeys: sourceParsed.entries.size,\r\n localeKeys: parsed.entries.size,\r\n added,\r\n obsolete,\r\n missingTargets,\r\n });\r\n }\r\n\r\n spinner.stop();\r\n renderSummaryTable(rows);\r\n\r\n if (opts.verbose) {\r\n const locales = Object.keys(missingKeysByLocale);\r\n if (locales.length === 0) {\r\n ui.success(\"No missing targets.\");\r\n } else {\r\n ui.info(\"Missing targets:\");\r\n for (const locale of locales) {\r\n ui.info(`- ${locale}:`);\r\n for (const key of missingKeysByLocale[locale]) {\r\n ui.info(` • ${key}`);\r\n }\r\n }\r\n }\r\n }\r\n\r\n const reasons: string[] = [];\r\n if (opts.failOnMissing && hasMissing) reasons.push(\"missing targets\");\r\n if (opts.failOnObsolete && hasObsolete) reasons.push(\"obsolete keys\");\r\n if (opts.failOnAdded && hasAdded) reasons.push(\"new keys need adding\");\r\n\r\n if (reasons.length > 0) {\r\n ui.error(`Check failed: ${reasons.join(\", \")}`);\r\n process.exitCode = 1;\r\n } else {\r\n ui.success(\"Check OK\");\r\n }\r\n } catch (e: any) {\r\n spinner.fail(\"Failed\");\r\n ui.error(e?.message ?? String(e));\r\n process.exitCode = 1;\r\n }\r\n });\r\n}\r\n","import chalk from \"chalk\";\r\nimport logSymbols from \"log-symbols\";\r\nimport boxen from \"boxen\";\r\n\r\nexport const ui = {\r\n info: (msg: string) => console.log(chalk.cyan(msg)),\r\n success: (msg: string) => console.log(`${logSymbols.success} ${chalk.green(msg)}`),\r\n warn: (msg: string) => console.log(`${logSymbols.warning} ${chalk.yellow(msg)}`),\r\n error: (msg: string) => console.error(`${logSymbols.error} ${chalk.red(msg)}`),\r\n headerBox: (title: string, subtitle?: string) =>\r\n console.log(\r\n boxen(\r\n `${chalk.bold(title)}${subtitle ? `\\n${chalk.dim(subtitle)}` : \"\"}`,\r\n { padding: 1, borderStyle: \"round\" }\r\n )\r\n ),\r\n};\r\n","import fg from \"fast-glob\";\r\nimport { promises as fs } from \"node:fs\";\r\nimport path from \"node:path\";\r\n\r\nexport interface DiscoverOptions {\r\n sourcePath: string;\r\n localesGlob: string;\r\n}\r\n\r\nexport interface LocaleFile {\r\n locale: string; // e.g. \"el\" or \"el-GR\"\r\n filePath: string;\r\n}\r\n\r\nexport interface DiscoverResult {\r\n sourcePath: string;\r\n localeFiles: LocaleFile[];\r\n}\r\n\r\nfunction extractLocaleFromFilename(filePath: string): string | null {\r\n // supports:\r\n // messages.el.xlf\r\n // messages.el-GR.xlf\r\n // messages.fr-CA.xlf\r\n const base = path.basename(filePath);\r\n const m = base.match(/^messages\\.([a-z]{2}(?:-[A-Z]{2})?)\\.xlf$/);\r\n return m?.[1] ?? null;\r\n}\r\n\r\nexport async function discoverFiles(opts: DiscoverOptions): Promise<DiscoverResult> {\r\n // validate source exists\r\n await fs.access(opts.sourcePath);\r\n\r\n const matches = await fg(opts.localesGlob, { onlyFiles: true, unique: true });\r\n\r\n const localeFiles: LocaleFile[] = [];\r\n for (const filePath of matches) {\r\n // ignore the source file if glob accidentally matches it\r\n if (path.resolve(filePath) === path.resolve(opts.sourcePath)) continue;\r\n\r\n const locale = extractLocaleFromFilename(filePath);\r\n if (!locale) continue;\r\n\r\n localeFiles.push({ locale, filePath });\r\n }\r\n\r\n // sort for stable output\r\n localeFiles.sort((a, b) => a.locale.localeCompare(b.locale));\r\n\r\n // dedupe locale collisions (same locale appears twice)\r\n const seen = new Map<string, string>();\r\n for (const lf of localeFiles) {\r\n if (seen.has(lf.locale)) {\r\n const prev = seen.get(lf.locale)!;\r\n throw new Error(\r\n `Duplicate locale \"${lf.locale}\" detected:\\n- ${prev}\\n- ${lf.filePath}\\n` +\r\n `Fix by removing one file or adjusting --locales glob.`\r\n );\r\n }\r\n seen.set(lf.locale, lf.filePath);\r\n }\r\n\r\n return { sourcePath: opts.sourcePath, localeFiles };\r\n}\r\n","import { XMLParser } from \"fast-xml-parser\";\r\nimport { ParsedXlf, MessageEntry, WriteOptions } from \"../../types/model.js\";\r\nimport { parseV12 } from \"./v12.js\";\r\nimport { parseV20 } from \"./v20.js\";\r\nimport { writeV12 } from \"./write-v12.js\";\r\nimport { writeV20 } from \"./write-v20.js\";\r\n\r\nconst parser = new XMLParser({\r\n ignoreAttributes: false,\r\n attributeNamePrefix: \"@_\",\r\n preserveOrder: false,\r\n});\r\n\r\nexport function parseXlf(xml: string): ParsedXlf {\r\n const doc = parser.parse(xml);\r\n\r\n const xliff = doc?.xliff;\r\n if (!xliff) throw new Error(\"Invalid XLF: missing <xliff>\");\r\n\r\n const version = xliff[\"@_version\"];\r\n if (version === \"1.2\") return parseV12(doc);\r\n if (version === \"2.0\") return parseV20(doc);\r\n\r\n throw new Error(`Unsupported XLIFF version: ${version}`);\r\n}\r\n\r\nexport function writeXlf(\r\n parsed: ParsedXlf,\r\n merged: Map<string, MessageEntry>,\r\n obsoleteKeys: string[],\r\n opts: WriteOptions\r\n): string {\r\n if (parsed.version === \"1.2\") return writeV12(parsed.raw, merged, obsoleteKeys, opts);\r\n return writeV20(parsed.raw, merged, obsoleteKeys, opts);\r\n}\r\n","import { ParsedXlf, MessageEntry } from \"../../types/model.js\";\r\n\r\nfunction asArray<T>(v: T | T[] | undefined | null): T[] {\r\n if (!v) return [];\r\n return Array.isArray(v) ? v : [v];\r\n}\r\n\r\nexport function parseV12(doc: any): ParsedXlf {\r\n const entries = new Map<string, MessageEntry>();\r\n\r\n const xliff = doc.xliff;\r\n const file = xliff.file;\r\n const locale = file?.[\"@_target-language\"]; // optional\r\n\r\n const body = file?.body;\r\n if (!body) throw new Error(\"Invalid XLF 1.2: missing <body>\");\r\n\r\n const transUnits = asArray(body[\"trans-unit\"]);\r\n for (const tu of transUnits) {\r\n const id = tu?.[\"@_id\"];\r\n if (!id) continue;\r\n\r\n const source = tu.source ?? \"\";\r\n const target = tu.target;\r\n\r\n entries.set(id, {\r\n key: id,\r\n sourceXml: toXmlText(source),\r\n targetXml: target !== undefined ? toXmlText(target) : undefined,\r\n });\r\n }\r\n\r\n return {\r\n version: \"1.2\",\r\n locale,\r\n entries,\r\n raw: doc,\r\n };\r\n}\r\n\r\n// MVP: keep it simple (text-only). We'll upgrade later for inline tags.\r\nfunction toXmlText(v: any): string {\r\n if (v === null || v === undefined) return \"\";\r\n if (typeof v === \"string\") return v;\r\n if (typeof v === \"object\") {\r\n if (typeof v[\"#text\"] === \"string\") return v[\"#text\"];\r\n }\r\n // fast-xml-parser can produce objects for mixed content; fallback:\r\n return String(v);\r\n}\r\n","import { ParsedXlf, MessageEntry } from \"../../types/model.js\";\r\n\r\nfunction asArray<T>(v: T | T[] | undefined | null): T[] {\r\n if (!v) return [];\r\n return Array.isArray(v) ? v : [v];\r\n}\r\n\r\nexport function parseV20(doc: any): ParsedXlf {\r\n const entries = new Map<string, MessageEntry>();\r\n\r\n const xliff = doc.xliff;\r\n const locale = xliff?.[\"@_trgLang\"]; // optional\r\n\r\n const file = xliff.file;\r\n if (!file) throw new Error(\"Invalid XLF 2.0: missing <file>\");\r\n\r\n const units = asArray(file.unit);\r\n for (const unit of units) {\r\n const unitId = unit?.[\"@_id\"];\r\n if (!unitId) continue;\r\n\r\n const segments = asArray(unit.segment);\r\n // Angular exports usually have one segment, but support many\r\n segments.forEach((seg, idx) => {\r\n const source = seg?.source ?? \"\";\r\n const target = seg?.target;\r\n\r\n const key = segments.length > 1 ? `${unitId}:${idx}` : unitId;\r\n\r\n entries.set(key, {\r\n key,\r\n sourceXml: toXmlText(source),\r\n targetXml: target !== undefined ? toXmlText(target) : undefined,\r\n });\r\n });\r\n }\r\n\r\n return {\r\n version: \"2.0\",\r\n locale,\r\n entries,\r\n raw: doc,\r\n };\r\n}\r\n\r\nfunction toXmlText(v: any): string {\r\n if (v === null || v === undefined) return \"\";\r\n if (typeof v === \"string\") return v;\r\n if (typeof v === \"object\") {\r\n if (typeof v[\"#text\"] === \"string\") return v[\"#text\"];\r\n // fallback for other usage, though mostly #text is the key\r\n }\r\n return String(v);\r\n}\r\n","import { MessageEntry } from \"../../types/model.js\";\r\n\r\nexport type NewTargetMode = \"todo\" | \"empty\" | \"source\";\r\nexport type ObsoleteMode = \"delete\" | \"mark\" | \"graveyard\";\r\n\r\nexport interface WriteOptions {\r\n newTarget: NewTargetMode;\r\n obsolete: ObsoleteMode;\r\n}\r\n\r\n/**\r\n * Defensive normalization:\r\n * - Ensures we never stringify objects into \"[object Object]\"\r\n * - Helps recover from previously \"dirty\" files.\r\n */\r\nfunction normalizeText(v: any): string {\r\n if (v == null) return \"\";\r\n if (typeof v === \"string\") return v;\r\n\r\n // If some upstream step accidentally produced a fast-xml-parser style object\r\n if (typeof v === \"object\") {\r\n if (typeof v[\"#text\"] === \"string\") return v[\"#text\"];\r\n if (typeof v.text === \"string\") return v.text;\r\n\r\n // Sometimes arrays happen in edge cases\r\n if (Array.isArray(v)) {\r\n return v.map(normalizeText).join(\"\");\r\n }\r\n }\r\n\r\n return \"\";\r\n}\r\n\r\nexport function writeV12(\r\n rawDoc: any,\r\n merged: Map<string, MessageEntry>,\r\n obsoleteKeys: string[],\r\n opts: WriteOptions\r\n): string {\r\n const xliff = rawDoc.xliff;\r\n const file = xliff.file;\r\n const body = file.body;\r\n\r\n // rebuild trans-units from merged (source-of-truth order)\r\n const transUnits: any[] = [];\r\n\r\n for (const entry of merged.values()) {\r\n const tu: any = {\r\n \"@_id\": normalizeText(entry.key),\r\n source: normalizeText(entry.sourceXml),\r\n };\r\n\r\n if (entry.targetXml !== undefined) {\r\n tu.target = normalizeText(entry.targetXml);\r\n }\r\n\r\n transUnits.push(tu);\r\n }\r\n\r\n // OBSOLETE MARK (safe, string-only, recovery-friendly)\r\n if (opts.obsolete === \"mark\") {\r\n const originalUnits: any[] = body[\"trans-unit\"] ?? [];\r\n\r\n for (const key of obsoleteKeys) {\r\n const original = originalUnits.find((u) => u[\"@_id\"] === key);\r\n if (!original) continue;\r\n\r\n transUnits.push({\r\n \"@_id\": normalizeText(key),\r\n source: normalizeText(original.source),\r\n target: `__OBSOLETE__${normalizeText(original.target)}`,\r\n note: \"Marked obsolete by xlf-sync\",\r\n });\r\n }\r\n }\r\n\r\n // apply rebuilt units\r\n body[\"trans-unit\"] = transUnits;\r\n\r\n return toXmlV12(rawDoc);\r\n}\r\n\r\n/* =======================\r\n XML SERIALIZER (1.2)\r\n ======================= */\r\n\r\nfunction escapeXml(s: string) {\r\n return s\r\n .replaceAll(\"&\", \"&\")\r\n .replaceAll(\"<\", \"<\")\r\n .replaceAll(\">\", \">\")\r\n .replaceAll('\"', \""\")\r\n .replaceAll(\"'\", \"'\");\r\n}\r\n\r\nfunction toXmlV12(doc: any): string {\r\n const xliff = doc.xliff;\r\n const file = xliff.file;\r\n const body = file.body;\r\n\r\n const headerAttrs = `version=\"${escapeXml(normalizeText(xliff[\"@_version\"] ?? \"1.2\"))}\"`;\r\n\r\n const fileAttrs: string[] = [];\r\n for (const [k, v] of Object.entries(file)) {\r\n if (k.startsWith(\"@_\")) {\r\n fileAttrs.push(`${k.slice(2)}=\"${escapeXml(normalizeText(v))}\"`);\r\n }\r\n }\r\n\r\n const units: any[] = Array.isArray(body[\"trans-unit\"])\r\n ? body[\"trans-unit\"]\r\n : [];\r\n\r\n const unitsXml = units\r\n .map((tu) => {\r\n const id = escapeXml(normalizeText(tu[\"@_id\"]));\r\n const source = escapeXml(normalizeText(tu.source));\r\n\r\n let targetXml = \"\";\r\n const targetRaw = tu.target;\r\n\r\n if (typeof targetRaw === \"string\") {\r\n if (targetRaw.startsWith(\"__OBSOLETE__\")) {\r\n const text = targetRaw.replace(\"__OBSOLETE__\", \"\");\r\n targetXml = `<target state=\"obsolete\">${escapeXml(normalizeText(text))}</target>`;\r\n } else {\r\n targetXml = `<target>${escapeXml(normalizeText(targetRaw))}</target>`;\r\n }\r\n }\r\n\r\n const noteXml = tu.note\r\n ? `<note>${escapeXml(normalizeText(tu.note))}</note>`\r\n : \"\";\r\n\r\n return (\r\n ` <trans-unit id=\"${id}\">\\n` +\r\n ` <source>${source}</source>\\n` +\r\n (targetXml ? ` ${targetXml}\\n` : \"\") +\r\n (noteXml ? ` ${noteXml}\\n` : \"\") +\r\n ` </trans-unit>`\r\n );\r\n })\r\n .join(\"\\n\\n\");\r\n\r\n return (\r\n `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\\n` +\r\n `<xliff ${headerAttrs}>\\n` +\r\n ` <file ${fileAttrs.join(\" \")}>\\n` +\r\n ` <body>\\n` +\r\n `${unitsXml}\\n` +\r\n ` </body>\\n` +\r\n ` </file>\\n` +\r\n `</xliff>\\n`\r\n );\r\n}\r\n","import { MessageEntry } from \"../../types/model.js\";\r\n\r\nexport type NewTargetMode = \"todo\" | \"empty\" | \"source\";\r\nexport type ObsoleteMode = \"delete\" | \"mark\" | \"graveyard\";\r\n\r\nexport interface WriteOptions {\r\n newTarget: NewTargetMode;\r\n obsolete: ObsoleteMode;\r\n}\r\n\r\nexport function writeV20(\r\n rawDoc: any,\r\n merged: Map<string, MessageEntry>,\r\n obsoleteKeys: string[],\r\n opts: WriteOptions\r\n): string {\r\n const xliff = rawDoc.xliff;\r\n const file = xliff.file;\r\n\r\n // Build units from merged (source-of-truth order)\r\n const units: any[] = [];\r\n\r\n for (const entry of merged.values()) {\r\n const unit: any = {\r\n \"@_id\": entry.key,\r\n segment: {\r\n source: entry.sourceXml ?? \"\",\r\n },\r\n };\r\n\r\n if (entry.targetXml !== undefined) {\r\n unit.segment.target = entry.targetXml;\r\n }\r\n\r\n units.push(unit);\r\n }\r\n\r\n // OBSOLETE MARK (safe, string-only)\r\n if (opts.obsolete === \"mark\") {\r\n const originalUnits: any[] = file.unit ?? [];\r\n\r\n for (const key of obsoleteKeys) {\r\n const original = originalUnits.find((u) => u[\"@_id\"] === key);\r\n if (!original) continue;\r\n\r\n const seg = original.segment ?? {};\r\n\r\n units.push({\r\n \"@_id\": key,\r\n segment: {\r\n source: seg.source ?? \"\",\r\n target: `__OBSOLETE__${seg.target ?? \"\"}`,\r\n },\r\n });\r\n }\r\n }\r\n\r\n // apply rebuilt units\r\n file.unit = units;\r\n\r\n return toXmlV20(rawDoc);\r\n}\r\n\r\n/* =======================\r\n XML SERIALIZER (2.0)\r\n ======================= */\r\n\r\nfunction escapeXml(s: string) {\r\n return s\r\n .replaceAll(\"&\", \"&\")\r\n .replaceAll(\"<\", \"<\")\r\n .replaceAll(\">\", \">\")\r\n .replaceAll('\"', \""\")\r\n .replaceAll(\"'\", \"'\");\r\n}\r\n\r\nfunction toXmlV20(doc: any): string {\r\n const xliff = doc.xliff;\r\n const file = xliff.file;\r\n\r\n const xliffAttrs: string[] = [];\r\n for (const [k, v] of Object.entries(xliff)) {\r\n if (k.startsWith(\"@_\")) {\r\n xliffAttrs.push(`${k.slice(2)}=\"${escapeXml(String(v))}\"`);\r\n }\r\n }\r\n\r\n const fileAttrs: string[] = [];\r\n for (const [k, v] of Object.entries(file)) {\r\n if (k.startsWith(\"@_\")) {\r\n fileAttrs.push(`${k.slice(2)}=\"${escapeXml(String(v))}\"`);\r\n }\r\n }\r\n\r\n const units: any[] = Array.isArray(file.unit) ? file.unit : [];\r\n\r\n const unitsXml = units\r\n .map((u) => {\r\n const id = escapeXml(String(u[\"@_id\"]));\r\n const seg = u.segment ?? {};\r\n const source = escapeXml(String(seg.source ?? \"\"));\r\n\r\n let targetXml = \"\";\r\n if (typeof seg.target === \"string\") {\r\n if (seg.target.startsWith(\"__OBSOLETE__\")) {\r\n const text = seg.target.replace(\"__OBSOLETE__\", \"\");\r\n targetXml = `<target state=\"obsolete\">${escapeXml(text)}</target>`;\r\n } else {\r\n targetXml = `<target>${escapeXml(seg.target)}</target>`;\r\n }\r\n }\r\n\r\n return (\r\n ` <unit id=\"${id}\">\\n` +\r\n ` <segment>\\n` +\r\n ` <source>${source}</source>\\n` +\r\n (targetXml ? ` ${targetXml}\\n` : \"\") +\r\n ` </segment>\\n` +\r\n ` </unit>`\r\n );\r\n })\r\n .join(\"\\n\\n\");\r\n\r\n return (\r\n `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n` +\r\n `<xliff ${xliffAttrs.join(\" \")}>\\n` +\r\n ` <file ${fileAttrs.join(\" \")}>\\n` +\r\n `${unitsXml}\\n` +\r\n ` </file>\\n` +\r\n `</xliff>\\n`\r\n );\r\n}\r\n","import { MessageEntry } from \"../types/model.js\";\r\n\r\nexport type NewTargetMode = \"todo\" | \"empty\" | \"source\";\r\nexport type ObsoleteMode = \"delete\" | \"mark\" | \"graveyard\";\r\n\r\nexport interface SyncOptions {\r\n newTarget: NewTargetMode;\r\n obsolete: ObsoleteMode;\r\n}\r\n\r\nexport interface SyncResult {\r\n merged: Map<string, MessageEntry>;\r\n addedKeys: string[];\r\n obsoleteKeys: string[];\r\n keptKeys: string[];\r\n missingTargets: string[]; // keys that exist but have no target\r\n}\r\n\r\nexport function syncLocale(\r\n source: Map<string, MessageEntry>,\r\n locale: Map<string, MessageEntry>,\r\n opts: SyncOptions\r\n): SyncResult {\r\n const merged = new Map<string, MessageEntry>();\r\n\r\n const addedKeys: string[] = [];\r\n const obsoleteKeys: string[] = [];\r\n const keptKeys: string[] = [];\r\n const missingTargets: string[] = [];\r\n\r\n // 1) Build merged in the exact order of source\r\n for (const [key, srcEntry] of source.entries()) {\r\n const locEntry = locale.get(key);\r\n\r\n if (locEntry) {\r\n // keep existing translation if present\r\n const targetXml = locEntry.targetXml;\r\n merged.set(key, {\r\n key,\r\n sourceXml: srcEntry.sourceXml,\r\n targetXml,\r\n });\r\n keptKeys.push(key);\r\n if (!targetXml || targetXml.trim() === \"\") missingTargets.push(key);\r\n } else {\r\n // add new entry\r\n const targetXml = makeNewTarget(srcEntry.sourceXml, opts.newTarget);\r\n merged.set(key, {\r\n key,\r\n sourceXml: srcEntry.sourceXml,\r\n targetXml,\r\n });\r\n addedKeys.push(key);\r\n if (!targetXml || targetXml.trim() === \"\") missingTargets.push(key);\r\n }\r\n }\r\n\r\n // 2) Find obsolete keys (present in locale, not in source)\r\n for (const key of locale.keys()) {\r\n if (!source.has(key)) {\r\n obsoleteKeys.push(key);\r\n // for now we don't apply obsolete policy into merged (next step)\r\n }\r\n }\r\n\r\n return { merged, addedKeys, obsoleteKeys, keptKeys, missingTargets };\r\n}\r\n\r\nfunction makeNewTarget(sourceXml: string, mode: NewTargetMode): string | undefined {\r\n if (mode === \"empty\") return undefined;\r\n if (mode === \"source\") return sourceXml;\r\n // todo\r\n return \"TODO\";\r\n}\r\n","import Table from \"cli-table3\";\r\nimport chalk from \"chalk\";\r\n\r\nexport interface LocaleRow {\r\n locale: string;\r\n version: string;\r\n sourceKeys: number;\r\n localeKeys: number;\r\n added: number;\r\n obsolete: number;\r\n missingTargets: number;\r\n}\r\n\r\nexport interface ReportRow {\r\n locale: string;\r\n version: string;\r\n total: number;\r\n done: number;\r\n todo: number;\r\n coverage: number;\r\n words: number;\r\n}\r\n\r\nexport function renderSummaryTable(rows: LocaleRow[]) {\r\n const table = new Table({\r\n head: [\r\n chalk.bold(\"Locale\"),\r\n chalk.bold(\"XLF\"),\r\n chalk.bold(\"Source\"),\r\n chalk.bold(\"Locale\"),\r\n chalk.bold(\"Add\"),\r\n chalk.bold(\"Obsolete\"),\r\n chalk.bold(\"Missing targets\"),\r\n ],\r\n });\r\n\r\n for (const r of rows) {\r\n table.push([\r\n r.locale,\r\n r.version,\r\n r.sourceKeys,\r\n r.localeKeys,\r\n r.added === 0 ? chalk.dim(\"0\") : chalk.yellow(String(r.added)),\r\n r.obsolete === 0 ? chalk.dim(\"0\") : chalk.red(String(r.obsolete)),\r\n r.missingTargets === 0 ? chalk.dim(\"0\") : chalk.yellow(String(r.missingTargets)),\r\n ]);\r\n }\r\n\r\n console.log(table.toString());\r\n}\r\n\r\nexport function renderReportTable(rows: ReportRow[]) {\r\n const table = new Table({\r\n head: [\r\n chalk.bold(\"Locale\"),\r\n chalk.bold(\"XLF\"),\r\n chalk.bold(\"Keys\"),\r\n chalk.bold(\"Translated\"),\r\n chalk.bold(\"Pending\"),\r\n chalk.bold(\"% Cov\"),\r\n chalk.bold(\"Words\"),\r\n ],\r\n });\r\n\r\n for (const r of rows) {\r\n const covColor =\r\n r.coverage === 100\r\n ? chalk.green\r\n : r.coverage >= 80\r\n ? chalk.yellow\r\n : chalk.red;\r\n\r\n table.push([\r\n r.locale,\r\n r.version,\r\n r.total,\r\n r.done,\r\n r.todo === 0 ? chalk.dim(\"0\") : chalk.yellow(String(r.todo)),\r\n covColor(`${r.coverage.toFixed(1)}%`),\r\n r.words,\r\n ]);\r\n }\r\n\r\n console.log(table.toString());\r\n}\r\n","import figlet from \"figlet\";\r\nimport chalk from \"chalk\";\r\nimport pkg from \"../../package.json\";\r\n\r\nexport function renderBanner(command?: string) {\r\n const logo = figlet.textSync(\"XLF-SYNC\", {\r\n font: \"Standard\", // μπορείς να αλλάξεις font\r\n horizontalLayout: \"default\",\r\n verticalLayout: \"default\",\r\n });\r\n\r\n console.log(chalk.cyanBright(logo));\r\n\r\n console.log(\r\n chalk.bold.white(\r\n `XLF-SYNC v${pkg.version}${command ? ` [${command}]` : \"\"}`\r\n )\r\n );\r\n\r\n console.log(chalk.gray(\"Sync & validate Angular XLIFF files\"));\r\n console.log(chalk.gray(\"Author: Anastasios Theodosiou\\n\"));\r\n}\r\n","{\n \"name\": \"xlf-sync\",\n \"version\": \"0.1.0\",\n \"description\": \"Sync Angular XLIFF (1.2 & 2.0) locale files with messages.xlf\",\n \"type\": \"module\",\n \"bin\": {\n \"xlf-sync\": \"dist/cli.js\"\n },\n \"files\": [\n \"dist\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"node --enable-source-maps dist/cli.js\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"lint\": \"echo 'No linter configured'\"\n },\n \"keywords\": [\n \"angular\",\n \"i18n\",\n \"xliff\",\n \"xlf\",\n \"translation\",\n \"sync\",\n \"locale\",\n \"merge\"\n ],\n \"author\": \"Anastasios Theodosiou\",\n \"license\": \"MIT\",\n \"dependencies\": {\n \"boxen\": \"^8.0.1\",\n \"chalk\": \"^5.6.2\",\n \"cli-table3\": \"^0.6.5\",\n \"commander\": \"^14.0.2\",\n \"fast-glob\": \"^3.3.3\",\n \"fast-xml-parser\": \"^5.3.3\",\n \"figlet\": \"^1.10.0\",\n \"log-symbols\": \"^7.0.1\",\n \"ora\": \"^9.1.0\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^25.0.10\",\n \"tsup\": \"^8.5.1\",\n \"typescript\": \"^5.9.3\",\n \"vitest\": \"^4.0.18\"\n }\n}","import { Command } from \"commander\";\r\nimport ora from \"ora\";\r\nimport { ui } from \"../ui/console.js\";\r\nimport { discoverFiles } from \"../core/discover.js\";\r\nimport { readFile } from \"node:fs/promises\";\r\nimport { parseXlf } from \"../core/xlf/index.js\";\r\nimport { renderReportTable, ReportRow } from \"../ui/table.js\";\r\nimport { renderBanner } from \"../ui/banner.js\";\r\n\r\nfunction isUntranslated(target: string | undefined): boolean {\r\n if (!target) return true;\r\n const t = target.trim();\r\n return t === \"\" || t.toUpperCase() === \"TODO\";\r\n}\r\n\r\nfunction countWords(text: string | undefined): number {\r\n if (!text) return 0;\r\n return text.trim().split(/\\s+/).length;\r\n}\r\n\r\nexport function registerReportCommand(program: Command) {\r\n program\r\n .command(\"report\")\r\n .description(\"Generate translation statistics report\")\r\n .option(\"--source <path>\", \"Path to source messages.xlf\", \"src/locale/messages.xlf\")\r\n .option(\"--locales <glob>\", \"Glob for locale files\", \"src/locale/messages.*.xlf\")\r\n .action(async (opts) => {\r\n renderBanner(\"report\");\r\n\r\n const spinner = ora(\"Scanning files...\").start();\r\n\r\n try {\r\n // 1) Discover files\r\n const res = await discoverFiles({\r\n sourcePath: opts.source,\r\n localesGlob: opts.locales,\r\n });\r\n\r\n const rows: ReportRow[] = [];\r\n\r\n // 2) Parse each locale file\r\n for (const lf of res.localeFiles) {\r\n const xml = await readFile(lf.filePath, \"utf-8\");\r\n const parsed = parseXlf(xml);\r\n\r\n let total = 0;\r\n let todo = 0;\r\n let words = 0;\r\n\r\n for (const entry of parsed.entries.values()) {\r\n total++;\r\n\r\n if (isUntranslated(entry.targetXml)) {\r\n todo++;\r\n } else {\r\n // count words only for done translations\r\n words += countWords(entry.targetXml);\r\n }\r\n }\r\n\r\n const done = total - todo;\r\n const coverage = total > 0 ? (done / total) * 100 : 100;\r\n\r\n rows.push({\r\n locale: lf.locale,\r\n version: parsed.version,\r\n total,\r\n done,\r\n todo,\r\n coverage,\r\n words,\r\n });\r\n }\r\n\r\n spinner.stop();\r\n\r\n // 3) Render table\r\n if (rows.length === 0) {\r\n ui.warn(\"No locale files found.\");\r\n } else {\r\n renderReportTable(rows);\r\n }\r\n } catch (e: any) {\r\n spinner.fail(\"Failed\");\r\n ui.error(e?.message ?? String(e));\r\n process.exitCode = 1;\r\n }\r\n });\r\n}\r\n","import { Command } from \"commander\";\r\nimport ora from \"ora\";\r\nimport { ui } from \"../ui/console.js\";\r\nimport { discoverFiles } from \"../core/discover.js\";\r\nimport { readFile, writeFile } from \"node:fs/promises\";\r\nimport { parseXlf, writeXlf } from \"../core/xlf/index.js\";\r\nimport { syncLocale } from \"../core/sync.js\";\r\nimport { renderSummaryTable } from \"../ui/table.js\";\r\nimport { buildGraveyardEntries } from \"../core/graveyard.js\";\r\nimport { renderBanner } from \"../ui/banner.js\";\r\n\r\nfunction resolveGraveyardPath(pattern: string, locale: string) {\r\n return pattern.replaceAll(\"{locale}\", locale);\r\n}\r\n\r\ntype Plan = {\r\n lf: { locale: string; filePath: string };\r\n mainOutputXml: string;\r\n graveyardOutputXml?: string;\r\n graveyardPath?: string;\r\n};\r\n\r\nexport function registerSyncCommand(program: Command) {\r\n program\r\n .command(\"sync\")\r\n .description(\"Sync locale XLF files with the source messages.xlf\")\r\n .option(\"--source <path>\", \"Path to source messages.xlf\", \"src/locale/messages.xlf\")\r\n .option(\"--locales <glob>\", \"Glob for locale files\", \"src/locale/messages.*.xlf\")\r\n .option(\"--dry-run\", \"Do not write files, only report changes\", false)\r\n .option(\"--new-target <mode>\", \"todo | empty | source\", \"todo\")\r\n .option(\"--obsolete <mode>\", \"delete | mark | graveyard\", \"mark\")\r\n .option(\"--fail-on-missing\", \"Fail if missing targets exist (no files written)\", false)\r\n .option(\r\n \"--graveyard-file <path>\",\r\n \"Graveyard output path pattern\",\r\n \"src/locale/_obsolete.{locale}.xlf\"\r\n )\r\n .action(async (opts) => {\r\n renderBanner(\"sync\");\r\n\r\n const spinner = ora(\"Scanning files...\").start();\r\n\r\n try {\r\n // 1️⃣ Discover source + locale files\r\n const res = await discoverFiles({\r\n sourcePath: opts.source,\r\n localesGlob: opts.locales,\r\n });\r\n\r\n spinner.succeed(`Found ${res.localeFiles.length} locale file(s)`);\r\n\r\n for (const lf of res.localeFiles) {\r\n ui.info(`- ${lf.locale}: ${lf.filePath}`);\r\n }\r\n\r\n // 2️⃣ Parse source file\r\n const sourceXml = await readFile(res.sourcePath, \"utf-8\");\r\n const sourceParsed = parseXlf(sourceXml);\r\n\r\n const rows: {\r\n locale: string;\r\n version: string;\r\n sourceKeys: number;\r\n localeKeys: number;\r\n added: number;\r\n obsolete: number;\r\n missingTargets: number;\r\n }[] = [];\r\n\r\n const plans: Plan[] = [];\r\n let hasMissing = false;\r\n\r\n // 3️⃣ PASS 1: compute diffs + prepare outputs (NO WRITES)\r\n for (const lf of res.localeFiles) {\r\n const localeXml = await readFile(lf.filePath, \"utf-8\");\r\n const parsed = parseXlf(localeXml);\r\n\r\n const diff = syncLocale(sourceParsed.entries, parsed.entries, {\r\n newTarget: opts.newTarget,\r\n obsolete: opts.obsolete,\r\n });\r\n\r\n if (diff.missingTargets.length > 0) hasMissing = true;\r\n\r\n rows.push({\r\n locale: lf.locale,\r\n version: parsed.version,\r\n sourceKeys: sourceParsed.entries.size,\r\n localeKeys: parsed.entries.size,\r\n added: diff.addedKeys.length,\r\n obsolete: diff.obsoleteKeys.length,\r\n missingTargets: diff.missingTargets.length,\r\n });\r\n\r\n // MAIN OUTPUT\r\n const mainObsoleteKeys = opts.obsolete === \"mark\" ? diff.obsoleteKeys : [];\r\n\r\n const mainParsedClone = {\r\n ...parsed,\r\n raw: structuredClone(parsed.raw),\r\n };\r\n\r\n const mainOutputXml = writeXlf(mainParsedClone, diff.merged, mainObsoleteKeys, {\r\n newTarget: opts.newTarget,\r\n // if graveyard, main behaves like delete (keeps file clean)\r\n obsolete: opts.obsolete === \"graveyard\" ? \"delete\" : opts.obsolete,\r\n });\r\n\r\n // GRAVEYARD OUTPUT (optional)\r\n let graveyardOutputXml: string | undefined;\r\n let graveyardPath: string | undefined;\r\n\r\n if (opts.obsolete === \"graveyard\" && diff.obsoleteKeys.length > 0) {\r\n const graveyardEntries = buildGraveyardEntries(parsed, diff.obsoleteKeys);\r\n\r\n if (graveyardEntries.size > 0) {\r\n graveyardPath = resolveGraveyardPath(opts.graveyardFile, lf.locale);\r\n\r\n const graveParsedClone = {\r\n ...parsed,\r\n raw: structuredClone(parsed.raw),\r\n };\r\n\r\n graveyardOutputXml = writeXlf(graveParsedClone, graveyardEntries, [], {\r\n newTarget: opts.newTarget,\r\n obsolete: \"delete\",\r\n });\r\n }\r\n }\r\n\r\n plans.push({\r\n lf,\r\n mainOutputXml,\r\n graveyardOutputXml,\r\n graveyardPath,\r\n });\r\n }\r\n\r\n spinner.stop();\r\n\r\n // 4️⃣ Render summary table\r\n renderSummaryTable(rows);\r\n\r\n // 5️⃣ FAIL GATE (prevents partial writes)\r\n if (opts.failOnMissing && hasMissing) {\r\n ui.error(\r\n \"Sync failed: missing targets. Fix translations or choose a different --new-target strategy.\"\r\n );\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n // 6️⃣ PASS 2: write outputs\r\n if (!opts.dryRun) {\r\n for (const p of plans) {\r\n await writeFile(p.lf.filePath, p.mainOutputXml, \"utf-8\");\r\n\r\n if (opts.obsolete === \"graveyard\" && p.graveyardOutputXml && p.graveyardPath) {\r\n await writeFile(p.graveyardPath, p.graveyardOutputXml, \"utf-8\");\r\n }\r\n }\r\n }\r\n\r\n if (opts.dryRun) {\r\n ui.success(\"Diff OK (dry-run)\");\r\n } else {\r\n ui.success(\r\n opts.obsolete === \"graveyard\"\r\n ? \"Sync OK (files updated + graveyard written)\"\r\n : \"Sync OK (files updated)\"\r\n );\r\n }\r\n } catch (e: any) {\r\n spinner.fail(\"Failed\");\r\n ui.error(e?.message ?? String(e));\r\n process.exitCode = 1;\r\n }\r\n });\r\n}\r\n","import { ParsedXlf, MessageEntry } from \"../types/model.js\";\r\n\r\nfunction normalizeText(v: any): string {\r\n if (v == null) return \"\";\r\n if (typeof v === \"string\") return v;\r\n if (typeof v === \"object\") {\r\n if (typeof v[\"#text\"] === \"string\") return v[\"#text\"];\r\n if (typeof v.text === \"string\") return v.text;\r\n if (Array.isArray(v)) return v.map(normalizeText).join(\"\");\r\n }\r\n return \"\";\r\n}\r\n\r\n/**\r\n * Extracts obsolete entries from the *original parsed raw doc* and returns them as MessageEntry map.\r\n * Targets are prefixed with \"__OBSOLETE__\" so writers emit state=\"obsolete\".\r\n */\r\nexport function buildGraveyardEntries(parsed: ParsedXlf, obsoleteKeys: string[]): Map<string, MessageEntry> {\r\n const out = new Map<string, MessageEntry>();\r\n\r\n if (obsoleteKeys.length === 0) return out;\r\n\r\n if (parsed.version === \"1.2\") {\r\n const body = parsed.raw?.xliff?.file?.body;\r\n const units: any[] = body?.[\"trans-unit\"] ?? [];\r\n\r\n for (const key of obsoleteKeys) {\r\n const u = units.find((x) => x?.[\"@_id\"] === key);\r\n if (!u) continue;\r\n\r\n const source = normalizeText(u.source);\r\n const target = normalizeText(u.target);\r\n\r\n out.set(key, {\r\n key,\r\n sourceXml: source,\r\n targetXml: `__OBSOLETE__${target}`,\r\n });\r\n }\r\n\r\n return out;\r\n }\r\n\r\n // 2.0\r\n const file = parsed.raw?.xliff?.file;\r\n const units: any[] = file?.unit ?? [];\r\n\r\n for (const key of obsoleteKeys) {\r\n const u = units.find((x) => x?.[\"@_id\"] === key);\r\n if (!u) continue;\r\n\r\n const seg = u.segment ?? {};\r\n const source = normalizeText(seg.source);\r\n const target = normalizeText(seg.target);\r\n\r\n out.set(key, {\r\n key,\r\n sourceXml: source,\r\n targetXml: `__OBSOLETE__${target}`,\r\n });\r\n }\r\n\r\n return out;\r\n}\r\n"],"mappings":"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","names":["asArray","toXmlText","escapeXml","chalk","chalk","chalk","program","ora","readFile","program","ora","readFile","ora","readFile","normalizeText","units","program","ora","readFile"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/commands/check.ts","../src/ui/console.ts","../src/core/discover.ts","../src/core/xlf/index.ts","../src/core/xlf/v12.ts","../src/core/xlf/v20.ts","../src/core/xlf/write-v12.ts","../src/core/xlf/write-v20.ts","../src/core/sync.ts","../src/ui/table.ts","../src/ui/banner.ts","../package.json","../src/commands/report.ts","../src/commands/sync.ts","../src/core/graveyard.ts"],"sourcesContent":["#!/usr/bin/env node\r\nimport { Command } from \"commander\";\r\nimport { registerCheckCommand } from \"./commands/check\";\r\nimport { registerReportCommand } from \"./commands/report\";\r\nimport { registerSyncCommand } from \"./commands/sync\";\r\nimport { getBanner } from \"./ui/banner\";\r\n\r\nconst program = new Command();\r\n\r\nprogram\r\n .name(\"xlf-sync\")\r\n .description(\r\n \"A powerful CLI tool to keep your Angular XLIFF translation files (1.2 & 2.0) in sync with your source messages.\\n\\n\" +\r\n \"Automatically merges new translations, marks obsolete keys, and validates structure.\"\r\n )\r\n .version(\"1.0.1\")\r\n .addHelpText(\"before\", getBanner());\r\n\r\nregisterSyncCommand(program);\r\nregisterCheckCommand(program);\r\nregisterReportCommand(program);\r\n\r\nprogram.parse(process.argv);\r\n\r\n\r\n// https://chatgpt.com/c/6978c595-664c-8328-9756-4a51d628277b","import { Command } from \"commander\";\r\nimport ora from \"ora\";\r\nimport { ui } from \"../ui/console.js\";\r\nimport { discoverFiles } from \"../core/discover.js\";\r\nimport { readFile } from \"node:fs/promises\";\r\nimport { parseXlf } from \"../core/xlf/index.js\";\r\nimport { syncLocale } from \"../core/sync.js\";\r\nimport { renderSummaryTable } from \"../ui/table.js\";\r\nimport { renderBanner } from \"../ui/banner.js\";\r\n\r\nexport function registerCheckCommand(program: Command) {\r\n program\r\n .command(\"check\")\r\n .description(\"Check if locale XLF files are in sync (CI-friendly)\")\r\n .option(\"--source <path>\", \"Path to source messages.xlf\", \"src/locale/messages.xlf\")\r\n .option(\"--locales <glob>\", \"Glob for locale files\", \"src/locale/messages.*.xlf\")\r\n .option(\"--fail-on-missing\", \"Exit non-zero if missing targets exist\", false)\r\n .option(\"--fail-on-obsolete\", \"Exit non-zero if obsolete keys exist\", false)\r\n .option(\"--fail-on-added\", \"Exit non-zero if new keys would be added\", false)\r\n .option(\"--new-target <mode>\", \"todo | empty | source (used for diff only)\", \"todo\")\r\n .option(\"--verbose\", \"Print missing keys per locale\", false)\r\n .action(async (opts) => {\r\n renderBanner(\"check\");\r\n\r\n const spinner = ora(\"Checking...\").start();\r\n\r\n try {\r\n const res = await discoverFiles({\r\n sourcePath: opts.source,\r\n localesGlob: opts.locales,\r\n });\r\n\r\n const sourceXml = await readFile(res.sourcePath, \"utf-8\");\r\n const sourceParsed = parseXlf(sourceXml);\r\n\r\n const rows: any[] = [];\r\n const missingKeysByLocale: Record<string, string[]> = {};\r\n\r\n let hasMissing = false;\r\n let hasObsolete = false;\r\n let hasAdded = false;\r\n\r\n for (const lf of res.localeFiles) {\r\n const xml = await readFile(lf.filePath, \"utf-8\");\r\n const parsed = parseXlf(xml);\r\n\r\n const diff = syncLocale(sourceParsed.entries, parsed.entries, {\r\n newTarget: opts.newTarget,\r\n // For check: we only want detection, not modifications\r\n obsolete: \"delete\",\r\n });\r\n\r\n const missingTargets = diff.missingTargets.length;\r\n const obsolete = diff.obsoleteKeys.length;\r\n const added = diff.addedKeys.length;\r\n\r\n if (missingTargets > 0) {\r\n hasMissing = true;\r\n missingKeysByLocale[lf.locale] = diff.missingTargets.slice();\r\n }\r\n if (obsolete > 0) hasObsolete = true;\r\n if (added > 0) hasAdded = true;\r\n\r\n rows.push({\r\n locale: lf.locale,\r\n version: parsed.version,\r\n sourceKeys: sourceParsed.entries.size,\r\n localeKeys: parsed.entries.size,\r\n added,\r\n obsolete,\r\n missingTargets,\r\n });\r\n }\r\n\r\n spinner.stop();\r\n renderSummaryTable(rows);\r\n\r\n if (opts.verbose) {\r\n const locales = Object.keys(missingKeysByLocale);\r\n if (locales.length === 0) {\r\n ui.success(\"No missing targets.\");\r\n } else {\r\n ui.info(\"Missing targets:\");\r\n for (const locale of locales) {\r\n ui.info(`- ${locale}:`);\r\n for (const key of missingKeysByLocale[locale]) {\r\n ui.info(` • ${key}`);\r\n }\r\n }\r\n }\r\n }\r\n\r\n const reasons: string[] = [];\r\n if (opts.failOnMissing && hasMissing) reasons.push(\"missing targets\");\r\n if (opts.failOnObsolete && hasObsolete) reasons.push(\"obsolete keys\");\r\n if (opts.failOnAdded && hasAdded) reasons.push(\"new keys need adding\");\r\n\r\n if (reasons.length > 0) {\r\n ui.error(`Check failed: ${reasons.join(\", \")}`);\r\n process.exitCode = 1;\r\n } else {\r\n ui.success(\"Check OK\");\r\n }\r\n } catch (e: any) {\r\n spinner.fail(\"Failed\");\r\n ui.error(e?.message ?? String(e));\r\n process.exitCode = 1;\r\n }\r\n });\r\n}\r\n","import chalk from \"chalk\";\r\nimport logSymbols from \"log-symbols\";\r\nimport boxen from \"boxen\";\r\n\r\nexport const ui = {\r\n info: (msg: string) => console.log(chalk.cyan(msg)),\r\n success: (msg: string) => console.log(`${logSymbols.success} ${chalk.green(msg)}`),\r\n warn: (msg: string) => console.log(`${logSymbols.warning} ${chalk.yellow(msg)}`),\r\n error: (msg: string) => console.error(`${logSymbols.error} ${chalk.red(msg)}`),\r\n headerBox: (title: string, subtitle?: string) =>\r\n console.log(\r\n boxen(\r\n `${chalk.bold(title)}${subtitle ? `\\n${chalk.dim(subtitle)}` : \"\"}`,\r\n { padding: 1, borderStyle: \"round\" }\r\n )\r\n ),\r\n};\r\n","import fg from \"fast-glob\";\r\nimport { promises as fs } from \"node:fs\";\r\nimport path from \"node:path\";\r\n\r\nexport interface DiscoverOptions {\r\n sourcePath: string;\r\n localesGlob: string;\r\n}\r\n\r\nexport interface LocaleFile {\r\n locale: string; // e.g. \"el\" or \"el-GR\"\r\n filePath: string;\r\n}\r\n\r\nexport interface DiscoverResult {\r\n sourcePath: string;\r\n localeFiles: LocaleFile[];\r\n}\r\n\r\nfunction extractLocaleFromFilename(filePath: string): string | null {\r\n // supports:\r\n // messages.el.xlf\r\n // messages.el-GR.xlf\r\n // messages.fr-CA.xlf\r\n const base = path.basename(filePath);\r\n const m = base.match(/^messages\\.([a-z]{2}(?:-[A-Z]{2})?)\\.xlf$/);\r\n return m?.[1] ?? null;\r\n}\r\n\r\nexport async function discoverFiles(opts: DiscoverOptions): Promise<DiscoverResult> {\r\n // validate source exists\r\n await fs.access(opts.sourcePath);\r\n\r\n const matches = await fg(opts.localesGlob, { onlyFiles: true, unique: true });\r\n\r\n const localeFiles: LocaleFile[] = [];\r\n for (const filePath of matches) {\r\n // ignore the source file if glob accidentally matches it\r\n if (path.resolve(filePath) === path.resolve(opts.sourcePath)) continue;\r\n\r\n const locale = extractLocaleFromFilename(filePath);\r\n if (!locale) continue;\r\n\r\n localeFiles.push({ locale, filePath });\r\n }\r\n\r\n // sort for stable output\r\n localeFiles.sort((a, b) => a.locale.localeCompare(b.locale));\r\n\r\n // dedupe locale collisions (same locale appears twice)\r\n const seen = new Map<string, string>();\r\n for (const lf of localeFiles) {\r\n if (seen.has(lf.locale)) {\r\n const prev = seen.get(lf.locale)!;\r\n throw new Error(\r\n `Duplicate locale \"${lf.locale}\" detected:\\n- ${prev}\\n- ${lf.filePath}\\n` +\r\n `Fix by removing one file or adjusting --locales glob.`\r\n );\r\n }\r\n seen.set(lf.locale, lf.filePath);\r\n }\r\n\r\n return { sourcePath: opts.sourcePath, localeFiles };\r\n}\r\n","import { XMLParser } from \"fast-xml-parser\";\r\nimport { ParsedXlf, MessageEntry, WriteOptions } from \"../../types/model.js\";\r\nimport { parseV12 } from \"./v12.js\";\r\nimport { parseV20 } from \"./v20.js\";\r\nimport { writeV12 } from \"./write-v12.js\";\r\nimport { writeV20 } from \"./write-v20.js\";\r\n\r\nconst parser = new XMLParser({\r\n ignoreAttributes: false,\r\n attributeNamePrefix: \"@_\",\r\n preserveOrder: false,\r\n});\r\n\r\nexport function parseXlf(xml: string): ParsedXlf {\r\n const doc = parser.parse(xml);\r\n\r\n const xliff = doc?.xliff;\r\n if (!xliff) throw new Error(\"Invalid XLF: missing <xliff>\");\r\n\r\n const version = xliff[\"@_version\"];\r\n if (version === \"1.2\") return parseV12(doc);\r\n if (version === \"2.0\") return parseV20(doc);\r\n\r\n throw new Error(`Unsupported XLIFF version: ${version}`);\r\n}\r\n\r\nexport function writeXlf(\r\n parsed: ParsedXlf,\r\n merged: Map<string, MessageEntry>,\r\n obsoleteKeys: string[],\r\n opts: WriteOptions\r\n): string {\r\n if (parsed.version === \"1.2\") return writeV12(parsed.raw, merged, obsoleteKeys, opts);\r\n return writeV20(parsed.raw, merged, obsoleteKeys, opts);\r\n}\r\n","import { ParsedXlf, MessageEntry } from \"../../types/model.js\";\r\n\r\nfunction asArray<T>(v: T | T[] | undefined | null): T[] {\r\n if (!v) return [];\r\n return Array.isArray(v) ? v : [v];\r\n}\r\n\r\nexport function parseV12(doc: any): ParsedXlf {\r\n const entries = new Map<string, MessageEntry>();\r\n\r\n const xliff = doc.xliff;\r\n const file = xliff.file;\r\n const locale = file?.[\"@_target-language\"]; // optional\r\n\r\n const body = file?.body;\r\n if (!body) throw new Error(\"Invalid XLF 1.2: missing <body>\");\r\n\r\n const transUnits = asArray(body[\"trans-unit\"]);\r\n for (const tu of transUnits) {\r\n const id = tu?.[\"@_id\"];\r\n if (!id) continue;\r\n\r\n const source = tu.source ?? \"\";\r\n const target = tu.target;\r\n\r\n entries.set(id, {\r\n key: id,\r\n sourceXml: toXmlText(source),\r\n targetXml: target !== undefined ? toXmlText(target) : undefined,\r\n });\r\n }\r\n\r\n return {\r\n version: \"1.2\",\r\n locale,\r\n entries,\r\n raw: doc,\r\n };\r\n}\r\n\r\n// MVP: keep it simple (text-only). We'll upgrade later for inline tags.\r\nfunction toXmlText(v: any): string {\r\n if (v === null || v === undefined) return \"\";\r\n if (typeof v === \"string\") return v;\r\n if (typeof v === \"object\") {\r\n if (typeof v[\"#text\"] === \"string\") return v[\"#text\"];\r\n }\r\n // fast-xml-parser can produce objects for mixed content; fallback:\r\n return String(v);\r\n}\r\n","import { ParsedXlf, MessageEntry } from \"../../types/model.js\";\r\n\r\nfunction asArray<T>(v: T | T[] | undefined | null): T[] {\r\n if (!v) return [];\r\n return Array.isArray(v) ? v : [v];\r\n}\r\n\r\nexport function parseV20(doc: any): ParsedXlf {\r\n const entries = new Map<string, MessageEntry>();\r\n\r\n const xliff = doc.xliff;\r\n const locale = xliff?.[\"@_trgLang\"]; // optional\r\n\r\n const file = xliff.file;\r\n if (!file) throw new Error(\"Invalid XLF 2.0: missing <file>\");\r\n\r\n const units = asArray(file.unit);\r\n for (const unit of units) {\r\n const unitId = unit?.[\"@_id\"];\r\n if (!unitId) continue;\r\n\r\n const segments = asArray(unit.segment);\r\n // Angular exports usually have one segment, but support many\r\n segments.forEach((seg, idx) => {\r\n const source = seg?.source ?? \"\";\r\n const target = seg?.target;\r\n\r\n const key = segments.length > 1 ? `${unitId}:${idx}` : unitId;\r\n\r\n entries.set(key, {\r\n key,\r\n sourceXml: toXmlText(source),\r\n targetXml: target !== undefined ? toXmlText(target) : undefined,\r\n });\r\n });\r\n }\r\n\r\n return {\r\n version: \"2.0\",\r\n locale,\r\n entries,\r\n raw: doc,\r\n };\r\n}\r\n\r\nfunction toXmlText(v: any): string {\r\n if (v === null || v === undefined) return \"\";\r\n if (typeof v === \"string\") return v;\r\n if (typeof v === \"object\") {\r\n if (typeof v[\"#text\"] === \"string\") return v[\"#text\"];\r\n // fallback for other usage, though mostly #text is the key\r\n }\r\n return String(v);\r\n}\r\n","import { MessageEntry } from \"../../types/model.js\";\r\n\r\nexport type NewTargetMode = \"todo\" | \"empty\" | \"source\";\r\nexport type ObsoleteMode = \"delete\" | \"mark\" | \"graveyard\";\r\n\r\nexport interface WriteOptions {\r\n newTarget: NewTargetMode;\r\n obsolete: ObsoleteMode;\r\n}\r\n\r\n/**\r\n * Defensive normalization:\r\n * - Ensures we never stringify objects into \"[object Object]\"\r\n * - Helps recover from previously \"dirty\" files.\r\n */\r\nfunction normalizeText(v: any): string {\r\n if (v == null) return \"\";\r\n if (typeof v === \"string\") return v;\r\n\r\n // If some upstream step accidentally produced a fast-xml-parser style object\r\n if (typeof v === \"object\") {\r\n if (typeof v[\"#text\"] === \"string\") return v[\"#text\"];\r\n if (typeof v.text === \"string\") return v.text;\r\n\r\n // Sometimes arrays happen in edge cases\r\n if (Array.isArray(v)) {\r\n return v.map(normalizeText).join(\"\");\r\n }\r\n }\r\n\r\n return \"\";\r\n}\r\n\r\nexport function writeV12(\r\n rawDoc: any,\r\n merged: Map<string, MessageEntry>,\r\n obsoleteKeys: string[],\r\n opts: WriteOptions\r\n): string {\r\n const xliff = rawDoc.xliff;\r\n const file = xliff.file;\r\n const body = file.body;\r\n\r\n // rebuild trans-units from merged (source-of-truth order)\r\n const transUnits: any[] = [];\r\n\r\n for (const entry of merged.values()) {\r\n const tu: any = {\r\n \"@_id\": normalizeText(entry.key),\r\n source: normalizeText(entry.sourceXml),\r\n };\r\n\r\n if (entry.targetXml !== undefined) {\r\n tu.target = normalizeText(entry.targetXml);\r\n }\r\n\r\n transUnits.push(tu);\r\n }\r\n\r\n // OBSOLETE MARK (safe, string-only, recovery-friendly)\r\n if (opts.obsolete === \"mark\") {\r\n const originalUnits: any[] = body[\"trans-unit\"] ?? [];\r\n\r\n for (const key of obsoleteKeys) {\r\n const original = originalUnits.find((u) => u[\"@_id\"] === key);\r\n if (!original) continue;\r\n\r\n transUnits.push({\r\n \"@_id\": normalizeText(key),\r\n source: normalizeText(original.source),\r\n target: `__OBSOLETE__${normalizeText(original.target)}`,\r\n note: \"Marked obsolete by xlf-sync\",\r\n });\r\n }\r\n }\r\n\r\n // apply rebuilt units\r\n body[\"trans-unit\"] = transUnits;\r\n\r\n return toXmlV12(rawDoc);\r\n}\r\n\r\n/* =======================\r\n XML SERIALIZER (1.2)\r\n ======================= */\r\n\r\nfunction escapeXml(s: string) {\r\n return s\r\n .replaceAll(\"&\", \"&\")\r\n .replaceAll(\"<\", \"<\")\r\n .replaceAll(\">\", \">\")\r\n .replaceAll('\"', \""\")\r\n .replaceAll(\"'\", \"'\");\r\n}\r\n\r\nfunction toXmlV12(doc: any): string {\r\n const xliff = doc.xliff;\r\n const file = xliff.file;\r\n const body = file.body;\r\n\r\n const headerAttrs = `version=\"${escapeXml(normalizeText(xliff[\"@_version\"] ?? \"1.2\"))}\"`;\r\n\r\n const fileAttrs: string[] = [];\r\n for (const [k, v] of Object.entries(file)) {\r\n if (k.startsWith(\"@_\")) {\r\n fileAttrs.push(`${k.slice(2)}=\"${escapeXml(normalizeText(v))}\"`);\r\n }\r\n }\r\n\r\n const units: any[] = Array.isArray(body[\"trans-unit\"])\r\n ? body[\"trans-unit\"]\r\n : [];\r\n\r\n const unitsXml = units\r\n .map((tu) => {\r\n const id = escapeXml(normalizeText(tu[\"@_id\"]));\r\n const source = escapeXml(normalizeText(tu.source));\r\n\r\n let targetXml = \"\";\r\n const targetRaw = tu.target;\r\n\r\n if (typeof targetRaw === \"string\") {\r\n if (targetRaw.startsWith(\"__OBSOLETE__\")) {\r\n const text = targetRaw.replace(\"__OBSOLETE__\", \"\");\r\n targetXml = `<target state=\"obsolete\">${escapeXml(normalizeText(text))}</target>`;\r\n } else {\r\n targetXml = `<target>${escapeXml(normalizeText(targetRaw))}</target>`;\r\n }\r\n }\r\n\r\n const noteXml = tu.note\r\n ? `<note>${escapeXml(normalizeText(tu.note))}</note>`\r\n : \"\";\r\n\r\n return (\r\n ` <trans-unit id=\"${id}\">\\n` +\r\n ` <source>${source}</source>\\n` +\r\n (targetXml ? ` ${targetXml}\\n` : \"\") +\r\n (noteXml ? ` ${noteXml}\\n` : \"\") +\r\n ` </trans-unit>`\r\n );\r\n })\r\n .join(\"\\n\\n\");\r\n\r\n return (\r\n `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\\n` +\r\n `<xliff ${headerAttrs}>\\n` +\r\n ` <file ${fileAttrs.join(\" \")}>\\n` +\r\n ` <body>\\n` +\r\n `${unitsXml}\\n` +\r\n ` </body>\\n` +\r\n ` </file>\\n` +\r\n `</xliff>\\n`\r\n );\r\n}\r\n","import { MessageEntry } from \"../../types/model.js\";\r\n\r\nexport type NewTargetMode = \"todo\" | \"empty\" | \"source\";\r\nexport type ObsoleteMode = \"delete\" | \"mark\" | \"graveyard\";\r\n\r\nexport interface WriteOptions {\r\n newTarget: NewTargetMode;\r\n obsolete: ObsoleteMode;\r\n}\r\n\r\nexport function writeV20(\r\n rawDoc: any,\r\n merged: Map<string, MessageEntry>,\r\n obsoleteKeys: string[],\r\n opts: WriteOptions\r\n): string {\r\n const xliff = rawDoc.xliff;\r\n const file = xliff.file;\r\n\r\n // Build units from merged (source-of-truth order)\r\n const units: any[] = [];\r\n\r\n for (const entry of merged.values()) {\r\n const unit: any = {\r\n \"@_id\": entry.key,\r\n segment: {\r\n source: entry.sourceXml ?? \"\",\r\n },\r\n };\r\n\r\n if (entry.targetXml !== undefined) {\r\n unit.segment.target = entry.targetXml;\r\n }\r\n\r\n units.push(unit);\r\n }\r\n\r\n // OBSOLETE MARK (safe, string-only)\r\n if (opts.obsolete === \"mark\") {\r\n const originalUnits: any[] = file.unit ?? [];\r\n\r\n for (const key of obsoleteKeys) {\r\n const original = originalUnits.find((u) => u[\"@_id\"] === key);\r\n if (!original) continue;\r\n\r\n const seg = original.segment ?? {};\r\n\r\n units.push({\r\n \"@_id\": key,\r\n segment: {\r\n source: seg.source ?? \"\",\r\n target: `__OBSOLETE__${seg.target ?? \"\"}`,\r\n },\r\n });\r\n }\r\n }\r\n\r\n // apply rebuilt units\r\n file.unit = units;\r\n\r\n return toXmlV20(rawDoc);\r\n}\r\n\r\n/* =======================\r\n XML SERIALIZER (2.0)\r\n ======================= */\r\n\r\nfunction escapeXml(s: string) {\r\n return s\r\n .replaceAll(\"&\", \"&\")\r\n .replaceAll(\"<\", \"<\")\r\n .replaceAll(\">\", \">\")\r\n .replaceAll('\"', \""\")\r\n .replaceAll(\"'\", \"'\");\r\n}\r\n\r\nfunction toXmlV20(doc: any): string {\r\n const xliff = doc.xliff;\r\n const file = xliff.file;\r\n\r\n const xliffAttrs: string[] = [];\r\n for (const [k, v] of Object.entries(xliff)) {\r\n if (k.startsWith(\"@_\")) {\r\n xliffAttrs.push(`${k.slice(2)}=\"${escapeXml(String(v))}\"`);\r\n }\r\n }\r\n\r\n const fileAttrs: string[] = [];\r\n for (const [k, v] of Object.entries(file)) {\r\n if (k.startsWith(\"@_\")) {\r\n fileAttrs.push(`${k.slice(2)}=\"${escapeXml(String(v))}\"`);\r\n }\r\n }\r\n\r\n const units: any[] = Array.isArray(file.unit) ? file.unit : [];\r\n\r\n const unitsXml = units\r\n .map((u) => {\r\n const id = escapeXml(String(u[\"@_id\"]));\r\n const seg = u.segment ?? {};\r\n const source = escapeXml(String(seg.source ?? \"\"));\r\n\r\n let targetXml = \"\";\r\n if (typeof seg.target === \"string\") {\r\n if (seg.target.startsWith(\"__OBSOLETE__\")) {\r\n const text = seg.target.replace(\"__OBSOLETE__\", \"\");\r\n targetXml = `<target state=\"obsolete\">${escapeXml(text)}</target>`;\r\n } else {\r\n targetXml = `<target>${escapeXml(seg.target)}</target>`;\r\n }\r\n }\r\n\r\n return (\r\n ` <unit id=\"${id}\">\\n` +\r\n ` <segment>\\n` +\r\n ` <source>${source}</source>\\n` +\r\n (targetXml ? ` ${targetXml}\\n` : \"\") +\r\n ` </segment>\\n` +\r\n ` </unit>`\r\n );\r\n })\r\n .join(\"\\n\\n\");\r\n\r\n return (\r\n `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n` +\r\n `<xliff ${xliffAttrs.join(\" \")}>\\n` +\r\n ` <file ${fileAttrs.join(\" \")}>\\n` +\r\n `${unitsXml}\\n` +\r\n ` </file>\\n` +\r\n `</xliff>\\n`\r\n );\r\n}\r\n","import { MessageEntry } from \"../types/model.js\";\r\n\r\nexport type NewTargetMode = \"todo\" | \"empty\" | \"source\";\r\nexport type ObsoleteMode = \"delete\" | \"mark\" | \"graveyard\";\r\n\r\nexport interface SyncOptions {\r\n newTarget: NewTargetMode;\r\n obsolete: ObsoleteMode;\r\n}\r\n\r\nexport interface SyncResult {\r\n merged: Map<string, MessageEntry>;\r\n addedKeys: string[];\r\n obsoleteKeys: string[];\r\n keptKeys: string[];\r\n missingTargets: string[]; // keys that exist but have no target\r\n}\r\n\r\nexport function syncLocale(\r\n source: Map<string, MessageEntry>,\r\n locale: Map<string, MessageEntry>,\r\n opts: SyncOptions\r\n): SyncResult {\r\n const merged = new Map<string, MessageEntry>();\r\n\r\n const addedKeys: string[] = [];\r\n const obsoleteKeys: string[] = [];\r\n const keptKeys: string[] = [];\r\n const missingTargets: string[] = [];\r\n\r\n // 1) Build merged in the exact order of source\r\n for (const [key, srcEntry] of source.entries()) {\r\n const locEntry = locale.get(key);\r\n\r\n if (locEntry) {\r\n // keep existing translation if present\r\n const targetXml = locEntry.targetXml;\r\n merged.set(key, {\r\n key,\r\n sourceXml: srcEntry.sourceXml,\r\n targetXml,\r\n });\r\n keptKeys.push(key);\r\n if (!targetXml || targetXml.trim() === \"\") missingTargets.push(key);\r\n } else {\r\n // add new entry\r\n const targetXml = makeNewTarget(srcEntry.sourceXml, opts.newTarget);\r\n merged.set(key, {\r\n key,\r\n sourceXml: srcEntry.sourceXml,\r\n targetXml,\r\n });\r\n addedKeys.push(key);\r\n if (!targetXml || targetXml.trim() === \"\") missingTargets.push(key);\r\n }\r\n }\r\n\r\n // 2) Find obsolete keys (present in locale, not in source)\r\n for (const key of locale.keys()) {\r\n if (!source.has(key)) {\r\n obsoleteKeys.push(key);\r\n // for now we don't apply obsolete policy into merged (next step)\r\n }\r\n }\r\n\r\n return { merged, addedKeys, obsoleteKeys, keptKeys, missingTargets };\r\n}\r\n\r\nfunction makeNewTarget(sourceXml: string, mode: NewTargetMode): string | undefined {\r\n if (mode === \"empty\") return undefined;\r\n if (mode === \"source\") return sourceXml;\r\n // todo\r\n return \"TODO\";\r\n}\r\n","import Table from \"cli-table3\";\r\nimport chalk from \"chalk\";\r\n\r\nexport interface LocaleRow {\r\n locale: string;\r\n version: string;\r\n sourceKeys: number;\r\n localeKeys: number;\r\n added: number;\r\n obsolete: number;\r\n missingTargets: number;\r\n}\r\n\r\nexport interface ReportRow {\r\n locale: string;\r\n version: string;\r\n total: number;\r\n done: number;\r\n todo: number;\r\n coverage: number;\r\n words: number;\r\n}\r\n\r\nexport function renderSummaryTable(rows: LocaleRow[]) {\r\n const table = new Table({\r\n head: [\r\n chalk.bold(\"Locale\"),\r\n chalk.bold(\"XLF\"),\r\n chalk.bold(\"Source\"),\r\n chalk.bold(\"Locale\"),\r\n chalk.bold(\"Add\"),\r\n chalk.bold(\"Obsolete\"),\r\n chalk.bold(\"Missing targets\"),\r\n ],\r\n });\r\n\r\n for (const r of rows) {\r\n table.push([\r\n r.locale,\r\n r.version,\r\n r.sourceKeys,\r\n r.localeKeys,\r\n r.added === 0 ? chalk.dim(\"0\") : chalk.yellow(String(r.added)),\r\n r.obsolete === 0 ? chalk.dim(\"0\") : chalk.red(String(r.obsolete)),\r\n r.missingTargets === 0 ? chalk.dim(\"0\") : chalk.yellow(String(r.missingTargets)),\r\n ]);\r\n }\r\n\r\n console.log(table.toString());\r\n}\r\n\r\nexport function renderReportTable(rows: ReportRow[]) {\r\n const table = new Table({\r\n head: [\r\n chalk.bold(\"Locale\"),\r\n chalk.bold(\"XLF\"),\r\n chalk.bold(\"Keys\"),\r\n chalk.bold(\"Translated\"),\r\n chalk.bold(\"Pending\"),\r\n chalk.bold(\"% Cov\"),\r\n chalk.bold(\"Words\"),\r\n ],\r\n });\r\n\r\n for (const r of rows) {\r\n const covColor =\r\n r.coverage === 100\r\n ? chalk.green\r\n : r.coverage >= 80\r\n ? chalk.yellow\r\n : chalk.red;\r\n\r\n table.push([\r\n r.locale,\r\n r.version,\r\n r.total,\r\n r.done,\r\n r.todo === 0 ? chalk.dim(\"0\") : chalk.yellow(String(r.todo)),\r\n covColor(`${r.coverage.toFixed(1)}%`),\r\n r.words,\r\n ]);\r\n }\r\n\r\n console.log(table.toString());\r\n}\r\n","import figlet from \"figlet\";\r\nimport chalk from \"chalk\";\r\nimport pkg from \"../../package.json\";\r\n\r\nexport function getBanner(command?: string) {\r\n const logo = figlet.textSync(\"XLF-SYNC\", {\r\n font: \"Standard\",\r\n horizontalLayout: \"default\",\r\n verticalLayout: \"default\",\r\n });\r\n\r\n const lines = [\r\n chalk.cyanBright(logo),\r\n chalk.bold.white(\r\n `XLF-SYNC v${pkg.version}${command ? ` [${command}]` : \"\"}`\r\n ),\r\n chalk.gray(\"Sync & validate Angular XLIFF files\"),\r\n chalk.gray(\"Author: Anastasios Theodosiou\\n\"),\r\n ];\r\n\r\n return lines.join(\"\\n\");\r\n}\r\n\r\nexport function renderBanner(command?: string) {\r\n console.log(getBanner(command));\r\n}\r\n","{\n \"name\": \"xlf-sync\",\n \"version\": \"1.0.3\",\n \"description\": \"Sync Angular XLIFF (1.2 & 2.0) locale files with messages.xlf\",\n \"type\": \"module\",\n \"bin\": {\n \"xlf-sync\": \"dist/cli.js\"\n },\n \"files\": [\n \"dist\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"node --enable-source-maps dist/cli.js\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"lint\": \"echo 'No linter configured'\"\n },\n \"keywords\": [\n \"angular\",\n \"i18n\",\n \"xliff\",\n \"xlf\",\n \"translation\",\n \"sync\",\n \"locale\",\n \"merge\"\n ],\n \"author\": \"Anastasios Theodosiou\",\n \"license\": \"MIT\",\n \"dependencies\": {\n \"boxen\": \"^8.0.1\",\n \"chalk\": \"^5.6.2\",\n \"cli-table3\": \"^0.6.5\",\n \"commander\": \"^14.0.2\",\n \"fast-glob\": \"^3.3.3\",\n \"fast-xml-parser\": \"^5.3.3\",\n \"figlet\": \"^1.10.0\",\n \"log-symbols\": \"^7.0.1\",\n \"ora\": \"^9.1.0\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^25.0.10\",\n \"tsup\": \"^8.5.1\",\n \"typescript\": \"^5.9.3\",\n \"vitest\": \"^4.0.18\"\n }\n}","import { Command } from \"commander\";\r\nimport ora from \"ora\";\r\nimport { ui } from \"../ui/console.js\";\r\nimport { discoverFiles } from \"../core/discover.js\";\r\nimport { readFile } from \"node:fs/promises\";\r\nimport { parseXlf } from \"../core/xlf/index.js\";\r\nimport { renderReportTable, ReportRow } from \"../ui/table.js\";\r\nimport { renderBanner } from \"../ui/banner.js\";\r\n\r\nfunction isUntranslated(target: string | undefined): boolean {\r\n if (!target) return true;\r\n const t = target.trim();\r\n return t === \"\" || t.toUpperCase() === \"TODO\";\r\n}\r\n\r\nfunction countWords(text: string | undefined): number {\r\n if (!text) return 0;\r\n return text.trim().split(/\\s+/).length;\r\n}\r\n\r\nexport function registerReportCommand(program: Command) {\r\n program\r\n .command(\"report\")\r\n .description(\"Generate translation statistics report\")\r\n .option(\"--source <path>\", \"Path to source messages.xlf\", \"src/locale/messages.xlf\")\r\n .option(\"--locales <glob>\", \"Glob for locale files\", \"src/locale/messages.*.xlf\")\r\n .action(async (opts) => {\r\n renderBanner(\"report\");\r\n\r\n const spinner = ora(\"Scanning files...\").start();\r\n\r\n try {\r\n // 1) Discover files\r\n const res = await discoverFiles({\r\n sourcePath: opts.source,\r\n localesGlob: opts.locales,\r\n });\r\n\r\n const rows: ReportRow[] = [];\r\n\r\n // 2) Parse each locale file\r\n for (const lf of res.localeFiles) {\r\n const xml = await readFile(lf.filePath, \"utf-8\");\r\n const parsed = parseXlf(xml);\r\n\r\n let total = 0;\r\n let todo = 0;\r\n let words = 0;\r\n\r\n for (const entry of parsed.entries.values()) {\r\n total++;\r\n\r\n if (isUntranslated(entry.targetXml)) {\r\n todo++;\r\n } else {\r\n // count words only for done translations\r\n words += countWords(entry.targetXml);\r\n }\r\n }\r\n\r\n const done = total - todo;\r\n const coverage = total > 0 ? (done / total) * 100 : 100;\r\n\r\n rows.push({\r\n locale: lf.locale,\r\n version: parsed.version,\r\n total,\r\n done,\r\n todo,\r\n coverage,\r\n words,\r\n });\r\n }\r\n\r\n spinner.stop();\r\n\r\n // 3) Render table\r\n if (rows.length === 0) {\r\n ui.warn(\"No locale files found.\");\r\n } else {\r\n renderReportTable(rows);\r\n }\r\n } catch (e: any) {\r\n spinner.fail(\"Failed\");\r\n ui.error(e?.message ?? String(e));\r\n process.exitCode = 1;\r\n }\r\n });\r\n}\r\n","import { Command } from \"commander\";\r\nimport ora from \"ora\";\r\nimport { ui } from \"../ui/console.js\";\r\nimport { discoverFiles } from \"../core/discover.js\";\r\nimport { readFile, writeFile } from \"node:fs/promises\";\r\nimport { parseXlf, writeXlf } from \"../core/xlf/index.js\";\r\nimport { syncLocale } from \"../core/sync.js\";\r\nimport { renderSummaryTable } from \"../ui/table.js\";\r\nimport { buildGraveyardEntries } from \"../core/graveyard.js\";\r\nimport { renderBanner } from \"../ui/banner.js\";\r\n\r\nfunction resolveGraveyardPath(pattern: string, locale: string) {\r\n return pattern.replaceAll(\"{locale}\", locale);\r\n}\r\n\r\ntype Plan = {\r\n lf: { locale: string; filePath: string };\r\n mainOutputXml: string;\r\n graveyardOutputXml?: string;\r\n graveyardPath?: string;\r\n};\r\n\r\nexport function registerSyncCommand(program: Command) {\r\n program\r\n .command(\"sync\")\r\n .description(\"Sync locale XLF files with the source messages.xlf\")\r\n .option(\"--source <path>\", \"Path to source messages.xlf\", \"src/locale/messages.xlf\")\r\n .option(\"--locales <glob>\", \"Glob for locale files\", \"src/locale/messages.*.xlf\")\r\n .option(\"--dry-run\", \"Do not write files, only report changes\", false)\r\n .option(\"--new-target <mode>\", \"todo | empty | source\", \"todo\")\r\n .option(\"--obsolete <mode>\", \"delete | mark | graveyard\", \"mark\")\r\n .option(\"--fail-on-missing\", \"Fail if missing targets exist (no files written)\", false)\r\n .option(\r\n \"--graveyard-file <path>\",\r\n \"Graveyard output path pattern\",\r\n \"src/locale/_obsolete.{locale}.xlf\"\r\n )\r\n .action(async (opts) => {\r\n renderBanner(\"sync\");\r\n\r\n const spinner = ora(\"Scanning files...\").start();\r\n\r\n try {\r\n // 1️⃣ Discover source + locale files\r\n const res = await discoverFiles({\r\n sourcePath: opts.source,\r\n localesGlob: opts.locales,\r\n });\r\n\r\n spinner.succeed(`Found ${res.localeFiles.length} locale file(s)`);\r\n\r\n for (const lf of res.localeFiles) {\r\n ui.info(`- ${lf.locale}: ${lf.filePath}`);\r\n }\r\n\r\n // 2️⃣ Parse source file\r\n const sourceXml = await readFile(res.sourcePath, \"utf-8\");\r\n const sourceParsed = parseXlf(sourceXml);\r\n\r\n const rows: {\r\n locale: string;\r\n version: string;\r\n sourceKeys: number;\r\n localeKeys: number;\r\n added: number;\r\n obsolete: number;\r\n missingTargets: number;\r\n }[] = [];\r\n\r\n const plans: Plan[] = [];\r\n let hasMissing = false;\r\n\r\n // 3️⃣ PASS 1: compute diffs + prepare outputs (NO WRITES)\r\n for (const lf of res.localeFiles) {\r\n const localeXml = await readFile(lf.filePath, \"utf-8\");\r\n const parsed = parseXlf(localeXml);\r\n\r\n const diff = syncLocale(sourceParsed.entries, parsed.entries, {\r\n newTarget: opts.newTarget,\r\n obsolete: opts.obsolete,\r\n });\r\n\r\n if (diff.missingTargets.length > 0) hasMissing = true;\r\n\r\n rows.push({\r\n locale: lf.locale,\r\n version: parsed.version,\r\n sourceKeys: sourceParsed.entries.size,\r\n localeKeys: parsed.entries.size,\r\n added: diff.addedKeys.length,\r\n obsolete: diff.obsoleteKeys.length,\r\n missingTargets: diff.missingTargets.length,\r\n });\r\n\r\n // MAIN OUTPUT\r\n const mainObsoleteKeys = opts.obsolete === \"mark\" ? diff.obsoleteKeys : [];\r\n\r\n const mainParsedClone = {\r\n ...parsed,\r\n raw: structuredClone(parsed.raw),\r\n };\r\n\r\n const mainOutputXml = writeXlf(mainParsedClone, diff.merged, mainObsoleteKeys, {\r\n newTarget: opts.newTarget,\r\n // if graveyard, main behaves like delete (keeps file clean)\r\n obsolete: opts.obsolete === \"graveyard\" ? \"delete\" : opts.obsolete,\r\n });\r\n\r\n // GRAVEYARD OUTPUT (optional)\r\n let graveyardOutputXml: string | undefined;\r\n let graveyardPath: string | undefined;\r\n\r\n if (opts.obsolete === \"graveyard\" && diff.obsoleteKeys.length > 0) {\r\n const graveyardEntries = buildGraveyardEntries(parsed, diff.obsoleteKeys);\r\n\r\n if (graveyardEntries.size > 0) {\r\n graveyardPath = resolveGraveyardPath(opts.graveyardFile, lf.locale);\r\n\r\n const graveParsedClone = {\r\n ...parsed,\r\n raw: structuredClone(parsed.raw),\r\n };\r\n\r\n graveyardOutputXml = writeXlf(graveParsedClone, graveyardEntries, [], {\r\n newTarget: opts.newTarget,\r\n obsolete: \"delete\",\r\n });\r\n }\r\n }\r\n\r\n plans.push({\r\n lf,\r\n mainOutputXml,\r\n graveyardOutputXml,\r\n graveyardPath,\r\n });\r\n }\r\n\r\n spinner.stop();\r\n\r\n // 4️⃣ Render summary table\r\n renderSummaryTable(rows);\r\n\r\n // 5️⃣ FAIL GATE (prevents partial writes)\r\n if (opts.failOnMissing && hasMissing) {\r\n ui.error(\r\n \"Sync failed: missing targets. Fix translations or choose a different --new-target strategy.\"\r\n );\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n // 6️⃣ PASS 2: write outputs\r\n if (!opts.dryRun) {\r\n for (const p of plans) {\r\n await writeFile(p.lf.filePath, p.mainOutputXml, \"utf-8\");\r\n\r\n if (opts.obsolete === \"graveyard\" && p.graveyardOutputXml && p.graveyardPath) {\r\n await writeFile(p.graveyardPath, p.graveyardOutputXml, \"utf-8\");\r\n }\r\n }\r\n }\r\n\r\n if (opts.dryRun) {\r\n ui.success(\"Diff OK (dry-run)\");\r\n } else {\r\n ui.success(\r\n opts.obsolete === \"graveyard\"\r\n ? \"Sync OK (files updated + graveyard written)\"\r\n : \"Sync OK (files updated)\"\r\n );\r\n }\r\n } catch (e: any) {\r\n spinner.fail(\"Failed\");\r\n ui.error(e?.message ?? String(e));\r\n process.exitCode = 1;\r\n }\r\n });\r\n}\r\n","import { ParsedXlf, MessageEntry } from \"../types/model.js\";\r\n\r\nfunction normalizeText(v: any): string {\r\n if (v == null) return \"\";\r\n if (typeof v === \"string\") return v;\r\n if (typeof v === \"object\") {\r\n if (typeof v[\"#text\"] === \"string\") return v[\"#text\"];\r\n if (typeof v.text === \"string\") return v.text;\r\n if (Array.isArray(v)) return v.map(normalizeText).join(\"\");\r\n }\r\n return \"\";\r\n}\r\n\r\n/**\r\n * Extracts obsolete entries from the *original parsed raw doc* and returns them as MessageEntry map.\r\n * Targets are prefixed with \"__OBSOLETE__\" so writers emit state=\"obsolete\".\r\n */\r\nexport function buildGraveyardEntries(parsed: ParsedXlf, obsoleteKeys: string[]): Map<string, MessageEntry> {\r\n const out = new Map<string, MessageEntry>();\r\n\r\n if (obsoleteKeys.length === 0) return out;\r\n\r\n if (parsed.version === \"1.2\") {\r\n const body = parsed.raw?.xliff?.file?.body;\r\n const units: any[] = body?.[\"trans-unit\"] ?? [];\r\n\r\n for (const key of obsoleteKeys) {\r\n const u = units.find((x) => x?.[\"@_id\"] === key);\r\n if (!u) continue;\r\n\r\n const source = normalizeText(u.source);\r\n const target = normalizeText(u.target);\r\n\r\n out.set(key, {\r\n key,\r\n sourceXml: source,\r\n targetXml: `__OBSOLETE__${target}`,\r\n });\r\n }\r\n\r\n return out;\r\n }\r\n\r\n // 2.0\r\n const file = parsed.raw?.xliff?.file;\r\n const units: any[] = file?.unit ?? [];\r\n\r\n for (const key of obsoleteKeys) {\r\n const u = units.find((x) => x?.[\"@_id\"] === key);\r\n if (!u) continue;\r\n\r\n const seg = u.segment ?? {};\r\n const source = normalizeText(seg.source);\r\n const target = normalizeText(seg.target);\r\n\r\n out.set(key, {\r\n key,\r\n sourceXml: source,\r\n targetXml: `__OBSOLETE__${target}`,\r\n });\r\n }\r\n\r\n return out;\r\n}\r\n"],"mappings":"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","names":["asArray","toXmlText","escapeXml","chalk","chalk","chalk","program","ora","readFile","program","ora","readFile","ora","readFile","normalizeText","units","program","ora","readFile"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xlf-sync",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Sync Angular XLIFF (1.2 & 2.0) locale files with messages.xlf",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -45,4 +45,4 @@
|
|
|
45
45
|
"typescript": "^5.9.3",
|
|
46
46
|
"vitest": "^4.0.18"
|
|
47
47
|
}
|
|
48
|
-
}
|
|
48
|
+
}
|