@ng-linguo/extract 0.9.0

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.
Files changed (46) hide show
  1. package/README.md +177 -0
  2. package/linguo.config.schema.json +53 -0
  3. package/package.json +38 -0
  4. package/src/cli.d.ts +2 -0
  5. package/src/cli.js +287 -0
  6. package/src/cli.js.map +1 -0
  7. package/src/index.d.ts +10 -0
  8. package/src/index.js +18 -0
  9. package/src/index.js.map +1 -0
  10. package/src/interactive.d.ts +12 -0
  11. package/src/interactive.js +679 -0
  12. package/src/interactive.js.map +1 -0
  13. package/src/lib/apply.d.ts +20 -0
  14. package/src/lib/apply.js +43 -0
  15. package/src/lib/apply.js.map +1 -0
  16. package/src/lib/clipboard.d.ts +17 -0
  17. package/src/lib/clipboard.js +96 -0
  18. package/src/lib/clipboard.js.map +1 -0
  19. package/src/lib/compile.d.ts +12 -0
  20. package/src/lib/compile.js +29 -0
  21. package/src/lib/compile.js.map +1 -0
  22. package/src/lib/config.d.ts +104 -0
  23. package/src/lib/config.js +185 -0
  24. package/src/lib/config.js.map +1 -0
  25. package/src/lib/merge.d.ts +13 -0
  26. package/src/lib/merge.js +34 -0
  27. package/src/lib/merge.js.map +1 -0
  28. package/src/lib/normalize.d.ts +15 -0
  29. package/src/lib/normalize.js +21 -0
  30. package/src/lib/normalize.js.map +1 -0
  31. package/src/lib/po.d.ts +25 -0
  32. package/src/lib/po.js +110 -0
  33. package/src/lib/po.js.map +1 -0
  34. package/src/lib/prompt.d.ts +33 -0
  35. package/src/lib/prompt.js +80 -0
  36. package/src/lib/prompt.js.map +1 -0
  37. package/src/lib/runner.d.ts +62 -0
  38. package/src/lib/runner.js +102 -0
  39. package/src/lib/runner.js.map +1 -0
  40. package/src/lib/scan.d.ts +31 -0
  41. package/src/lib/scan.js +183 -0
  42. package/src/lib/scan.js.map +1 -0
  43. package/src/lib/translation-prompt.txt +214 -0
  44. package/src/lib/translator.d.ts +83 -0
  45. package/src/lib/translator.js +91 -0
  46. package/src/lib/translator.js.map +1 -0
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mergeCatalog = mergeCatalog;
4
+ // Joins context and key when matching entries (gettext EOT glue).
5
+ const GLUE = String.fromCharCode(4);
6
+ function identity(context, msgid) {
7
+ return context === '' ? msgid : `${context}${GLUE}${msgid}`;
8
+ }
9
+ /**
10
+ * Merge freshly extracted messages into an existing catalog.
11
+ *
12
+ * The existing `translation` (msgstr) is preserved for entries that still appear
13
+ * in the source — matched by their (context, key) identity — and `references`
14
+ * are refreshed to the current locations. Entries no longer
15
+ * present are dropped; new ones are added with an empty translation. The result
16
+ * follows the extraction order (order of discovery), so re-extraction keeps the
17
+ * catalog stable and readable.
18
+ */
19
+ function mergeCatalog(existing, extracted) {
20
+ const previous = new Map();
21
+ for (const entry of existing) {
22
+ previous.set(identity(entry.context, entry.msgid), entry);
23
+ }
24
+ return extracted.map((message) => {
25
+ const prior = previous.get(identity(message.context, message.keyId));
26
+ return {
27
+ context: message.context,
28
+ msgid: message.keyId,
29
+ msgstr: prior?.msgstr ?? '',
30
+ references: message.references,
31
+ };
32
+ });
33
+ }
34
+ //# sourceMappingURL=merge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge.js","sourceRoot":"","sources":["../../../../../packages/extract/src/lib/merge.ts"],"names":[],"mappings":";;AAoBA,oCAkBC;AAnCD,kEAAkE;AAClE,MAAM,IAAI,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;AAEpC,SAAS,QAAQ,CAAC,OAAe,EAAE,KAAa;IAC9C,OAAO,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,OAAO,GAAG,IAAI,GAAG,KAAK,EAAE,CAAC;AAC9D,CAAC;AAED;;;;;;;;;GASG;AACH,SAAgB,YAAY,CAC1B,QAA4B,EAC5B,SAAsC;IAEtC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAmB,CAAC;IAC5C,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;QAC7B,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC;IAC5D,CAAC;IAED,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC,OAAO,EAAW,EAAE;QACxC,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;QACrE,OAAO;YACL,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,MAAM,EAAE,KAAK,EAAE,MAAM,IAAI,EAAE;YAC3B,UAAU,EAAE,OAAO,CAAC,UAAU;SAC/B,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Normalize a translator-facing source message into its canonical form.
3
+ *
4
+ * Leading/trailing whitespace is trimmed and any internal run of whitespace
5
+ * (including newlines from multi-line templates) is collapsed to a single
6
+ * space. This is the build-time half of the runtime/extractor parity contract
7
+ * (CLAUDE.md §5.2): the runtime normalizer must produce identical output for
8
+ * the same input, verified against the shared fixture.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * normalizeMessage(' Hello\n world '); // 'Hello world'
13
+ * ```
14
+ */
15
+ export declare function normalizeMessage(source: string): string;
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeMessage = normalizeMessage;
4
+ /**
5
+ * Normalize a translator-facing source message into its canonical form.
6
+ *
7
+ * Leading/trailing whitespace is trimmed and any internal run of whitespace
8
+ * (including newlines from multi-line templates) is collapsed to a single
9
+ * space. This is the build-time half of the runtime/extractor parity contract
10
+ * (CLAUDE.md §5.2): the runtime normalizer must produce identical output for
11
+ * the same input, verified against the shared fixture.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * normalizeMessage(' Hello\n world '); // 'Hello world'
16
+ * ```
17
+ */
18
+ function normalizeMessage(source) {
19
+ return source.trim().replace(/\s+/g, ' ');
20
+ }
21
+ //# sourceMappingURL=normalize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.js","sourceRoot":"","sources":["../../../../../packages/extract/src/lib/normalize.ts"],"names":[],"mappings":";;AAcA,4CAEC;AAhBD;;;;;;;;;;;;;GAaG;AACH,SAAgB,gBAAgB,CAAC,MAAc;IAC7C,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAC5C,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * A single gettext `.po` catalog entry.
3
+ *
4
+ * `msgid` is the normalized source string and `msgstr` is the translation
5
+ * (empty until a translator fills it in). `context` is the `msgctxt` (empty when
6
+ * none) — it lets the same source text carry different translations and becomes
7
+ * part of the compiled runtime key. `references` are the `#:` source locations.
8
+ */
9
+ export interface PoEntry {
10
+ readonly context: string;
11
+ readonly msgid: string;
12
+ readonly msgstr: string;
13
+ readonly references: readonly string[];
14
+ }
15
+ /**
16
+ * Serialize catalog entries to gettext `.po` text, including a minimal UTF-8
17
+ * header so standard translator tooling accepts the file.
18
+ */
19
+ export declare function serializePo(entries: readonly PoEntry[]): string;
20
+ /**
21
+ * Parse gettext `.po` text into catalog entries. The header entry (empty
22
+ * `msgid`) is dropped. Tolerant of multi-line quoted continuations and ignores
23
+ * comment kinds it does not use (`#`, `#,`).
24
+ */
25
+ export declare function parsePo(content: string): PoEntry[];
package/src/lib/po.js ADDED
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.serializePo = serializePo;
4
+ exports.parsePo = parsePo;
5
+ function escapePo(value) {
6
+ return value
7
+ .replace(/\\/g, '\\\\')
8
+ .replace(/"/g, '\\"')
9
+ .replace(/\n/g, '\\n')
10
+ .replace(/\t/g, '\\t');
11
+ }
12
+ function unescapePo(value) {
13
+ return value.replace(/\\([\\"nt])/g, (_match, char) => char === 'n' ? '\n' : char === 't' ? '\t' : char);
14
+ }
15
+ function quoted(value) {
16
+ const start = value.indexOf('"');
17
+ const end = value.lastIndexOf('"');
18
+ if (start < 0 || end <= start) {
19
+ return '';
20
+ }
21
+ return unescapePo(value.slice(start + 1, end));
22
+ }
23
+ const HEADER = ['msgid ""', 'msgstr ""', '"Content-Type: text/plain; charset=UTF-8\\n"'].join('\n');
24
+ /**
25
+ * Serialize catalog entries to gettext `.po` text, including a minimal UTF-8
26
+ * header so standard translator tooling accepts the file.
27
+ */
28
+ function serializePo(entries) {
29
+ const blocks = [HEADER];
30
+ for (const entry of entries) {
31
+ const lines = [];
32
+ for (const reference of entry.references) {
33
+ lines.push(`#: ${reference}`);
34
+ }
35
+ if (entry.context.length > 0) {
36
+ lines.push(`msgctxt "${escapePo(entry.context)}"`);
37
+ }
38
+ lines.push(`msgid "${escapePo(entry.msgid)}"`);
39
+ lines.push(`msgstr "${escapePo(entry.msgstr)}"`);
40
+ blocks.push(lines.join('\n'));
41
+ }
42
+ return `${blocks.join('\n\n')}\n`;
43
+ }
44
+ function emptyDraft() {
45
+ return { context: null, references: [], msgid: null, msgstr: null };
46
+ }
47
+ /**
48
+ * Parse gettext `.po` text into catalog entries. The header entry (empty
49
+ * `msgid`) is dropped. Tolerant of multi-line quoted continuations and ignores
50
+ * comment kinds it does not use (`#`, `#,`).
51
+ */
52
+ function parsePo(content) {
53
+ const entries = [];
54
+ let draft = emptyDraft();
55
+ let mode = null;
56
+ const flush = () => {
57
+ if (draft.msgid !== null && draft.msgid !== '') {
58
+ entries.push({
59
+ context: draft.context ?? '',
60
+ msgid: draft.msgid,
61
+ msgstr: draft.msgstr ?? '',
62
+ references: draft.references,
63
+ });
64
+ }
65
+ draft = emptyDraft();
66
+ mode = null;
67
+ };
68
+ for (const rawLine of content.split(/\r?\n/)) {
69
+ const line = rawLine.trimEnd();
70
+ if (line.trim() === '') {
71
+ flush();
72
+ continue;
73
+ }
74
+ if (line.startsWith('#:')) {
75
+ draft.references.push(line.slice(2).trim());
76
+ mode = null;
77
+ }
78
+ else if (line.startsWith('#')) {
79
+ // Other comment kinds (`#.`, `#,`, translator comments) are ignored.
80
+ mode = null;
81
+ }
82
+ else if (line.startsWith('msgctxt ')) {
83
+ draft.context = quoted(line.slice('msgctxt '.length));
84
+ mode = 'msgctxt';
85
+ }
86
+ else if (line.startsWith('msgid ')) {
87
+ draft.msgid = quoted(line.slice('msgid '.length));
88
+ mode = 'msgid';
89
+ }
90
+ else if (line.startsWith('msgstr ')) {
91
+ draft.msgstr = quoted(line.slice('msgstr '.length));
92
+ mode = 'msgstr';
93
+ }
94
+ else if (line.startsWith('"')) {
95
+ const continuation = quoted(line);
96
+ if (mode === 'msgctxt') {
97
+ draft.context = (draft.context ?? '') + continuation;
98
+ }
99
+ else if (mode === 'msgid') {
100
+ draft.msgid = (draft.msgid ?? '') + continuation;
101
+ }
102
+ else if (mode === 'msgstr') {
103
+ draft.msgstr = (draft.msgstr ?? '') + continuation;
104
+ }
105
+ }
106
+ }
107
+ flush();
108
+ return entries;
109
+ }
110
+ //# sourceMappingURL=po.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"po.js","sourceRoot":"","sources":["../../../../../packages/extract/src/lib/po.ts"],"names":[],"mappings":";;AA4CA,kCAeC;AAkBD,0BAoDC;AAlHD,SAAS,QAAQ,CAAC,KAAa;IAC7B,OAAO,KAAK;SACT,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;SACpB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;SACrB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAC3B,CAAC;AAED,SAAS,UAAU,CAAC,KAAa;IAC/B,OAAO,KAAK,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC,MAAM,EAAE,IAAY,EAAE,EAAE,CAC5D,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CACjD,CAAC;AACJ,CAAC;AAED,SAAS,MAAM,CAAC,KAAa;IAC3B,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACjC,MAAM,GAAG,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACnC,IAAI,KAAK,GAAG,CAAC,IAAI,GAAG,IAAI,KAAK,EAAE,CAAC;QAC9B,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,MAAM,GAAG,CAAC,UAAU,EAAE,WAAW,EAAE,8CAA8C,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEpG;;;GAGG;AACH,SAAgB,WAAW,CAAC,OAA2B;IACrD,MAAM,MAAM,GAAG,CAAC,MAAM,CAAC,CAAC;IACxB,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,KAAK,MAAM,SAAS,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;YACzC,KAAK,CAAC,IAAI,CAAC,MAAM,SAAS,EAAE,CAAC,CAAC;QAChC,CAAC;QACD,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7B,KAAK,CAAC,IAAI,CAAC,YAAY,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACrD,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,UAAU,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/C,KAAK,CAAC,IAAI,CAAC,WAAW,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;AACpC,CAAC;AASD,SAAS,UAAU;IACjB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AACtE,CAAC;AAED;;;;GAIG;AACH,SAAgB,OAAO,CAAC,OAAe;IACrC,MAAM,OAAO,GAAc,EAAE,CAAC;IAC9B,IAAI,KAAK,GAAG,UAAU,EAAE,CAAC;IACzB,IAAI,IAAI,GAA0C,IAAI,CAAC;IAEvD,MAAM,KAAK,GAAG,GAAS,EAAE;QACvB,IAAI,KAAK,CAAC,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,KAAK,KAAK,EAAE,EAAE,CAAC;YAC/C,OAAO,CAAC,IAAI,CAAC;gBACX,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,EAAE;gBAC5B,KAAK,EAAE,KAAK,CAAC,KAAK;gBAClB,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,EAAE;gBAC1B,UAAU,EAAE,KAAK,CAAC,UAAU;aAC7B,CAAC,CAAC;QACL,CAAC;QACD,KAAK,GAAG,UAAU,EAAE,CAAC;QACrB,IAAI,GAAG,IAAI,CAAC;IACd,CAAC,CAAC;IAEF,KAAK,MAAM,OAAO,IAAI,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;QAC/B,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YACvB,KAAK,EAAE,CAAC;YACR,SAAS;QACX,CAAC;QACD,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1B,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;YAC5C,IAAI,GAAG,IAAI,CAAC;QACd,CAAC;aAAM,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAChC,qEAAqE;YACrE,IAAI,GAAG,IAAI,CAAC;QACd,CAAC;aAAM,IAAI,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YACvC,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;YACtD,IAAI,GAAG,SAAS,CAAC;QACnB,CAAC;aAAM,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YACrC,KAAK,CAAC,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;YAClD,IAAI,GAAG,OAAO,CAAC;QACjB,CAAC;aAAM,IAAI,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YACtC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;YACpD,IAAI,GAAG,QAAQ,CAAC;QAClB,CAAC;aAAM,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAChC,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;YAClC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBACvB,KAAK,CAAC,OAAO,GAAG,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC,GAAG,YAAY,CAAC;YACvD,CAAC;iBAAM,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;gBAC5B,KAAK,CAAC,KAAK,GAAG,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC,GAAG,YAAY,CAAC;YACnD,CAAC;iBAAM,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC7B,KAAK,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,MAAM,IAAI,EAAE,CAAC,GAAG,YAAY,CAAC;YACrD,CAAC;QACH,CAAC;IACH,CAAC;IACD,KAAK,EAAE,CAAC;IACR,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Build a single, self-contained prompt that instructs an LLM to translate a
3
+ * whole `.po` catalog. The template explains ng-linguo's concepts (context,
4
+ * slot tags, MessageFormat 2 plurals/selection) and is filled with the target
5
+ * language label and the catalog contents.
6
+ *
7
+ * @param targetLanguage human-readable label for the language to translate
8
+ * into, e.g. `"Polish (pl)"`.
9
+ * @param poContents the entire `.po` file to translate.
10
+ * @returns the ready-to-send prompt.
11
+ */
12
+ export declare function buildTranslationPrompt(targetLanguage: string, poContents: string): string;
13
+ /** A configured locale matched from user input, with a label for the prompt. */
14
+ export interface ResolvedLocale {
15
+ /** The locale code whose `<locale>.po` catalog should be translated. */
16
+ readonly locale: string;
17
+ /** Human-readable label injected into the prompt, e.g. `"Polish (pl)"`. */
18
+ readonly label: string;
19
+ }
20
+ /**
21
+ * A human-readable label for a locale, e.g. `"Polish (pl)"`, used in prompts
22
+ * and menus. Falls back to the bare code when no display name is available.
23
+ */
24
+ export declare function localeLabel(locale: string): string;
25
+ /**
26
+ * Resolve a user-supplied language to one of the configured locales. The input
27
+ * may be the locale code (`pl`), the English name (`Polish`), or the endonym —
28
+ * the language's own name (`Polski`) — all matched case-insensitively.
29
+ *
30
+ * @returns the matched locale and a `"<English name> (<code>)"` label, or
31
+ * `undefined` when nothing matches.
32
+ */
33
+ export declare function resolveTargetLocale(input: string, locales: readonly string[]): ResolvedLocale | undefined;
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildTranslationPrompt = buildTranslationPrompt;
4
+ exports.localeLabel = localeLabel;
5
+ exports.resolveTargetLocale = resolveTargetLocale;
6
+ const node_fs_1 = require("node:fs");
7
+ const node_path_1 = require("node:path");
8
+ /** Placeholder replaced with the human-readable target language label. */
9
+ const LANGUAGE_SLOT = '{{TARGET_LANGUAGE}}';
10
+ /** Placeholder replaced with the full `.po` catalog contents. */
11
+ const PO_SLOT = '{{PO_FILE}}';
12
+ /**
13
+ * The translation prompt template, shipped as a sibling `.txt` asset so the
14
+ * long instructions stay readable (and editable) without escaping. Read once
15
+ * and cached for the process.
16
+ */
17
+ let cachedTemplate;
18
+ function loadTemplate() {
19
+ if (cachedTemplate === undefined) {
20
+ cachedTemplate = (0, node_fs_1.readFileSync)((0, node_path_1.join)(__dirname, 'translation-prompt.txt'), 'utf8');
21
+ }
22
+ return cachedTemplate;
23
+ }
24
+ /**
25
+ * Build a single, self-contained prompt that instructs an LLM to translate a
26
+ * whole `.po` catalog. The template explains ng-linguo's concepts (context,
27
+ * slot tags, MessageFormat 2 plurals/selection) and is filled with the target
28
+ * language label and the catalog contents.
29
+ *
30
+ * @param targetLanguage human-readable label for the language to translate
31
+ * into, e.g. `"Polish (pl)"`.
32
+ * @param poContents the entire `.po` file to translate.
33
+ * @returns the ready-to-send prompt.
34
+ */
35
+ function buildTranslationPrompt(targetLanguage, poContents) {
36
+ return loadTemplate().split(LANGUAGE_SLOT).join(targetLanguage).split(PO_SLOT).join(poContents);
37
+ }
38
+ function displayName(locale, inLocale) {
39
+ try {
40
+ return new Intl.DisplayNames([inLocale], { type: 'language' }).of(locale);
41
+ }
42
+ catch {
43
+ // Unknown locale or no ICU data — fall back to code-only matching.
44
+ return undefined;
45
+ }
46
+ }
47
+ /**
48
+ * A human-readable label for a locale, e.g. `"Polish (pl)"`, used in prompts
49
+ * and menus. Falls back to the bare code when no display name is available.
50
+ */
51
+ function localeLabel(locale) {
52
+ const english = displayName(locale, 'en');
53
+ return english ? `${english} (${locale})` : locale;
54
+ }
55
+ /**
56
+ * Resolve a user-supplied language to one of the configured locales. The input
57
+ * may be the locale code (`pl`), the English name (`Polish`), or the endonym —
58
+ * the language's own name (`Polski`) — all matched case-insensitively.
59
+ *
60
+ * @returns the matched locale and a `"<English name> (<code>)"` label, or
61
+ * `undefined` when nothing matches.
62
+ */
63
+ function resolveTargetLocale(input, locales) {
64
+ const needle = input.trim().toLowerCase();
65
+ if (needle === '') {
66
+ return undefined;
67
+ }
68
+ for (const locale of locales) {
69
+ const english = displayName(locale, 'en');
70
+ const endonym = displayName(locale, locale);
71
+ const matches = locale.toLowerCase() === needle ||
72
+ english?.toLowerCase() === needle ||
73
+ endonym?.toLowerCase() === needle;
74
+ if (matches) {
75
+ return { locale, label: localeLabel(locale) };
76
+ }
77
+ }
78
+ return undefined;
79
+ }
80
+ //# sourceMappingURL=prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.js","sourceRoot":"","sources":["../../../../../packages/extract/src/lib/prompt.ts"],"names":[],"mappings":";;AAiCA,wDAEC;AAuBD,kCAGC;AAUD,kDAuBC;AA9FD,qCAAuC;AACvC,yCAAiC;AAEjC,0EAA0E;AAC1E,MAAM,aAAa,GAAG,qBAAqB,CAAC;AAC5C,iEAAiE;AACjE,MAAM,OAAO,GAAG,aAAa,CAAC;AAE9B;;;;GAIG;AACH,IAAI,cAAkC,CAAC;AAEvC,SAAS,YAAY;IACnB,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;QACjC,cAAc,GAAG,IAAA,sBAAY,EAAC,IAAA,gBAAI,EAAC,SAAS,EAAE,wBAAwB,CAAC,EAAE,MAAM,CAAC,CAAC;IACnF,CAAC;IACD,OAAO,cAAc,CAAC;AACxB,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAgB,sBAAsB,CAAC,cAAsB,EAAE,UAAkB;IAC/E,OAAO,YAAY,EAAE,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;AAClG,CAAC;AAUD,SAAS,WAAW,CAAC,MAAc,EAAE,QAAgB;IACnD,IAAI,CAAC;QACH,OAAO,IAAI,IAAI,CAAC,YAAY,CAAC,CAAC,QAAQ,CAAC,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;IAC5E,CAAC;IAAC,MAAM,CAAC;QACP,mEAAmE;QACnE,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAgB,WAAW,CAAC,MAAc;IACxC,MAAM,OAAO,GAAG,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC1C,OAAO,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,KAAK,MAAM,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC;AACrD,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,mBAAmB,CACjC,KAAa,EACb,OAA0B;IAE1B,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC1C,IAAI,MAAM,KAAK,EAAE,EAAE,CAAC;QAClB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAG,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC1C,MAAM,OAAO,GAAG,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC5C,MAAM,OAAO,GACX,MAAM,CAAC,WAAW,EAAE,KAAK,MAAM;YAC/B,OAAO,EAAE,WAAW,EAAE,KAAK,MAAM;YACjC,OAAO,EAAE,WAAW,EAAE,KAAK,MAAM,CAAC;QAEpC,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC;QAChD,CAAC;IACH,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Prefix marking a translation that has not been provided yet. New entries in
3
+ * non-source locales are seeded with this followed by the source text, so the
4
+ * string is visible and obviously flagged in the running app until translated.
5
+ */
6
+ export declare const MISSING_PREFIX = "<MISSING TRANSLATION>";
7
+ /** Per-language summary of an extraction run. */
8
+ export interface LocaleExtractStats {
9
+ readonly locale: string;
10
+ /** Total entries in the catalog after the run. */
11
+ readonly total: number;
12
+ /** Entries newly added this run. */
13
+ readonly added: number;
14
+ /** Entries dropped this run (no longer in the source). */
15
+ readonly removed: number;
16
+ /** Entries still untranslated (empty or `<MISSING TRANSLATION>`-prefixed). */
17
+ readonly missing: number;
18
+ }
19
+ /** Summary of an extraction run across all locales. */
20
+ export interface ExtractStats {
21
+ readonly files: number;
22
+ readonly messages: number;
23
+ readonly locales: readonly LocaleExtractStats[];
24
+ }
25
+ /** Options for {@link extractToCatalogs}. */
26
+ export interface ExtractOptions {
27
+ /** Directory to scan recursively for translatable strings. */
28
+ readonly srcDir: string;
29
+ /** Directory to write `<locale>.po` catalogs into. */
30
+ readonly outDir: string;
31
+ /** Locales to create or update, e.g. `['en', 'pl']`. */
32
+ readonly locales: readonly string[];
33
+ /** Base used to make `#:` references relative. Defaults to `process.cwd()`. */
34
+ readonly cwd?: string;
35
+ /**
36
+ * Locale whose new entries are seeded with the source text itself (the
37
+ * canonical value) instead of a `<MISSING TRANSLATION>` placeholder. Defaults
38
+ * to `'en'`.
39
+ */
40
+ readonly sourceLocale?: string;
41
+ }
42
+ /**
43
+ * Scan `srcDir`, then create or update one `.po` catalog per locale in
44
+ * `outDir`, merging with any existing catalog so translations are preserved.
45
+ *
46
+ * Newly discovered entries are seeded: the source locale gets the source text,
47
+ * other locales get `"<MISSING TRANSLATION> <source>"` so the string renders
48
+ * visibly flagged until translated. Returns per-language stats.
49
+ */
50
+ export declare function extractToCatalogs(options: ExtractOptions): ExtractStats;
51
+ /** Options for {@link compileCatalogs}. */
52
+ export interface CompileOptions {
53
+ /** Directory containing `<locale>.po` catalogs. */
54
+ readonly poDir: string;
55
+ /** Directory to write `<locale>.json` runtime dictionaries into. */
56
+ readonly outDir: string;
57
+ }
58
+ /**
59
+ * Compile every `.po` catalog in `poDir` into a runtime `<locale>.json`
60
+ * dictionary in `outDir` (the format the loader consumes).
61
+ */
62
+ export declare function compileCatalogs(options: CompileOptions): void;
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MISSING_PREFIX = void 0;
4
+ exports.extractToCatalogs = extractToCatalogs;
5
+ exports.compileCatalogs = compileCatalogs;
6
+ const node_fs_1 = require("node:fs");
7
+ const node_path_1 = require("node:path");
8
+ const compile_1 = require("./compile");
9
+ const merge_1 = require("./merge");
10
+ const po_1 = require("./po");
11
+ const scan_1 = require("./scan");
12
+ const SCANNED_EXTENSIONS = ['.html', '.ts'];
13
+ /**
14
+ * Prefix marking a translation that has not been provided yet. New entries in
15
+ * non-source locales are seeded with this followed by the source text, so the
16
+ * string is visible and obviously flagged in the running app until translated.
17
+ */
18
+ exports.MISSING_PREFIX = '<MISSING TRANSLATION>';
19
+ function walk(dir, files = []) {
20
+ for (const name of (0, node_fs_1.readdirSync)(dir)) {
21
+ if (name === 'node_modules' || name.startsWith('.')) {
22
+ continue;
23
+ }
24
+ const full = (0, node_path_1.join)(dir, name);
25
+ if ((0, node_fs_1.statSync)(full).isDirectory()) {
26
+ walk(full, files);
27
+ }
28
+ else if (SCANNED_EXTENSIONS.some((ext) => name.endsWith(ext)) && !name.endsWith('.spec.ts')) {
29
+ files.push(full);
30
+ }
31
+ }
32
+ return files;
33
+ }
34
+ function identity(entry) {
35
+ return entry.context === ''
36
+ ? entry.msgid
37
+ : `${entry.context}${String.fromCharCode(4)}${entry.msgid}`;
38
+ }
39
+ function isMissing(msgstr) {
40
+ return msgstr === '' || msgstr.startsWith(exports.MISSING_PREFIX);
41
+ }
42
+ /**
43
+ * Scan `srcDir`, then create or update one `.po` catalog per locale in
44
+ * `outDir`, merging with any existing catalog so translations are preserved.
45
+ *
46
+ * Newly discovered entries are seeded: the source locale gets the source text,
47
+ * other locales get `"<MISSING TRANSLATION> <source>"` so the string renders
48
+ * visibly flagged until translated. Returns per-language stats.
49
+ */
50
+ function extractToCatalogs(options) {
51
+ const base = options.cwd ?? process.cwd();
52
+ const sourceLocale = options.sourceLocale ?? 'en';
53
+ const files = walk(options.srcDir).map((path) => ({
54
+ path: (0, node_path_1.relative)(base, path).split('\\').join('/'),
55
+ content: (0, node_fs_1.readFileSync)(path, 'utf8'),
56
+ }));
57
+ const messages = (0, scan_1.extractMessages)(files);
58
+ (0, node_fs_1.mkdirSync)(options.outDir, { recursive: true });
59
+ const locales = [];
60
+ for (const locale of options.locales) {
61
+ const poPath = (0, node_path_1.join)(options.outDir, `${locale}.po`);
62
+ const existing = (0, node_fs_1.existsSync)(poPath) ? (0, po_1.parsePo)((0, node_fs_1.readFileSync)(poPath, 'utf8')) : [];
63
+ const existingIds = new Set(existing.map(identity));
64
+ const merged = (0, merge_1.mergeCatalog)(existing, messages);
65
+ let added = 0;
66
+ const seeded = merged.map((entry) => {
67
+ const isNew = !existingIds.has(identity(entry));
68
+ if (!isNew) {
69
+ return entry;
70
+ }
71
+ added += 1;
72
+ const fallback = locale === sourceLocale ? entry.msgid : `${exports.MISSING_PREFIX} ${entry.msgid}`;
73
+ return entry.msgstr === '' ? { ...entry, msgstr: fallback } : entry;
74
+ });
75
+ (0, node_fs_1.writeFileSync)(poPath, (0, po_1.serializePo)(seeded));
76
+ const mergedIds = new Set(seeded.map(identity));
77
+ locales.push({
78
+ locale,
79
+ total: seeded.length,
80
+ added,
81
+ removed: [...existingIds].filter((id) => !mergedIds.has(id)).length,
82
+ missing: seeded.filter((entry) => isMissing(entry.msgstr)).length,
83
+ });
84
+ }
85
+ return { files: files.length, messages: messages.length, locales };
86
+ }
87
+ /**
88
+ * Compile every `.po` catalog in `poDir` into a runtime `<locale>.json`
89
+ * dictionary in `outDir` (the format the loader consumes).
90
+ */
91
+ function compileCatalogs(options) {
92
+ (0, node_fs_1.mkdirSync)(options.outDir, { recursive: true });
93
+ for (const name of (0, node_fs_1.readdirSync)(options.poDir)) {
94
+ if (!name.endsWith('.po')) {
95
+ continue;
96
+ }
97
+ const locale = name.slice(0, -'.po'.length);
98
+ const entries = (0, po_1.parsePo)((0, node_fs_1.readFileSync)((0, node_path_1.join)(options.poDir, name), 'utf8'));
99
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(options.outDir, `${locale}.json`), `${JSON.stringify((0, compile_1.compileEntries)(entries), null, 2)}\n`);
100
+ }
101
+ }
102
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../../../../../packages/extract/src/lib/runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /** A source file to scan for translatable strings. */
2
+ export interface SourceFile {
3
+ /** Path used in `#:` references, e.g. `src/app/player.html`. */
4
+ readonly path: string;
5
+ readonly content: string;
6
+ }
7
+ /** A translatable string found during scanning, with its source locations. */
8
+ export interface ExtractedMessage {
9
+ /** Normalized source string — the catalog `msgid`. */
10
+ readonly keyId: string;
11
+ /** Disambiguating context (`msgctxt`), or `''` for the default entry. */
12
+ readonly context: string;
13
+ /** Sorted, de-duplicated `path:line` references. */
14
+ readonly references: readonly string[];
15
+ }
16
+ /**
17
+ * Scan source files for translatable strings used by the `t` pipe
18
+ * (`'Play' | t: { context }`), the `[t]` directive (`t="message"` with an
19
+ * optional `tContext`), the `t('...', { context })` helper call, and the
20
+ * `mark()` marker, returning one {@link ExtractedMessage} per unique
21
+ * (context, key) pair with all of its source references.
22
+ *
23
+ * Pure and DOM-free, so it runs in CI on plain Node (CLAUDE.md §2.1). Entries
24
+ * are returned in order of discovery — files in the order given, and within a
25
+ * file by source position — so re-extraction produces a stable, readable order.
26
+ *
27
+ * Regions marked with `linguo-ignore` comment directives are skipped, so
28
+ * documentation samples containing `mark(`, `'…' | t`, or `t="…"` are not
29
+ * scanned as real messages (see {@link ignoredRanges}).
30
+ */
31
+ export declare function extractMessages(files: readonly SourceFile[]): ExtractedMessage[];