@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
package/README.md ADDED
@@ -0,0 +1,177 @@
1
+ # @ng-linguo/extract
2
+
3
+ The build-time CLI for [ng-linguo](https://github.com/jmwierzbicki/linguo). Pure
4
+ Node — it has **zero Angular/RxJS dependencies** — so installing it never drags
5
+ the framework into your tooling. It scans your source for translatable strings,
6
+ maintains gettext `.po` catalogs, and compiles them to the runtime `.json`
7
+ dictionaries `@ng-linguo/linguo` loads.
8
+
9
+ ```bash
10
+ pnpm add -D @ng-linguo/extract
11
+ ```
12
+
13
+ ## Quick start
14
+
15
+ ```bash
16
+ # Create linguo.config.json (interactive in a terminal, or pass flags for CI)
17
+ linguo-extract init --locales en,pl,de
18
+
19
+ # Scan source → update <locale>.po catalogs
20
+ linguo-extract extract
21
+
22
+ # Compile <locale>.po → runtime <locale>.json
23
+ linguo-extract compile
24
+ ```
25
+
26
+ Run `linguo-extract` with no command in a terminal to open the guided menu,
27
+ which can also create and edit your config (a BIOS-style settings screen where
28
+ each value carries a description).
29
+
30
+ ## Configuration
31
+
32
+ `linguo.config.json`, discovered automatically by searching up from the cwd and
33
+ then down through the workspace. A JSON Schema ships with the package — add a
34
+ `$schema` (the `init` command writes it for you) for editor autocomplete, hover
35
+ docs, and validation:
36
+
37
+ ```json
38
+ {
39
+ "$schema": "./node_modules/@ng-linguo/extract/linguo.config.schema.json",
40
+ "locales": ["en", "pl", "de"]
41
+ }
42
+ ```
43
+
44
+ | Field | Description |
45
+ | --------------- | ------------------------------------------------------------------- |
46
+ | `locales` | Languages you ship. Comma-separated BCP-47 codes. |
47
+ | `sourceLocale` | The language your source strings are authored in. |
48
+ | `src` | Directory scanned for translatable strings. |
49
+ | `catalogs` | Where the editable `<locale>.po` catalogs live. |
50
+ | `output` | Where compiled runtime `<locale>.json` dictionaries are written. |
51
+ | `referenceBase` | `#:` references relative to the config file (`config`) or the cwd. |
52
+ | `translator` | Optional module path enabling automatic AI translation (see below). |
53
+
54
+ ## Excluding code from extraction
55
+
56
+ Scanning is regex-based over your source, so a string that _looks_ like a message
57
+ is one — even inside a documentation sample or a fixture. Wrap such regions in
58
+ `linguo-ignore` comment directives and the scanner skips them. They are matched
59
+ as plain text, so the comment style (`//`, `/* */`, `<!-- -->`) doesn't matter:
60
+
61
+ ```ts
62
+ // linguo-ignore-start
63
+ const sample = `<p t="This is documentation, not a real message"></p>`;
64
+ const icu = mark('.input {$n :number} .match $n one {{…}} * {{…}}');
65
+ // linguo-ignore-end
66
+ ```
67
+
68
+ | Directive | Effect |
69
+ | ------------------------------------------- | ------------------------------------------------------------------------- |
70
+ | `linguo-ignore-start` / `linguo-ignore-end` | Skip everything between them (an unmatched `start` skips to end of file). |
71
+ | `linguo-ignore-next-line` | Skip the single line after the directive. |
72
+ | `linguo-ignore-file` | Skip the whole file, wherever the marker appears. |
73
+
74
+ ## Translating with an LLM
75
+
76
+ Every untranslated entry is seeded with the source text behind a
77
+ `<MISSING TRANSLATION>` marker. ng-linguo builds a single, self-contained prompt
78
+ that teaches the model your concepts — `msgctxt`, slot tags (`[name]…[/name]`),
79
+ and MessageFormat 2 plural/selection rules — so the result is high quality.
80
+
81
+ There are two ways to run it, and **the clipboard method is always available** —
82
+ including inside the interactive menu — whether or not you configure a translator.
83
+
84
+ ### Manual — clipboard (no API key, no config)
85
+
86
+ ```bash
87
+ linguo-extract copyprompt pl # copies the prompt to your clipboard
88
+ ```
89
+
90
+ Paste it into any chat model, then save the reply over `pl.po`. Or use the
91
+ interactive menu: **Translate → Manual** copies the prompt, you paste the model's
92
+ reply back to your clipboard, and ng-linguo merges it into the catalog for you.
93
+ (When no `translator` is configured, "Translate" goes straight to this flow.)
94
+
95
+ ### Automatic — your translator function
96
+
97
+ Because every AI provider has a different SDK and secret handling, **you** supply
98
+ the API call. Point `translator` at a module that exports a `translate` function;
99
+ ng-linguo builds the prompt and merges the reply, and your function does nothing
100
+ but send the prompt to your provider and return its answer.
101
+
102
+ The function receives a request object and returns the model's reply (the
103
+ translated `.po` text) — synchronously or as a `Promise`:
104
+
105
+ | Field | Type | Description |
106
+ | -------------- | -------- | ----------------------------------------- |
107
+ | `prompt` | `string` | The ready-to-send prompt for one catalog. |
108
+ | `targetLocale` | `string` | The catalog's locale code, e.g. `"pl"`. |
109
+ | `targetLabel` | `string` | A readable label, e.g. `"Polish (pl)"`. |
110
+ | `sourceLocale` | `string` | The source locale, e.g. `"en"`. |
111
+
112
+ A complete, copy-pasteable implementation (OpenAI):
113
+
114
+ ```js
115
+ // linguo.translator.mjs
116
+ import OpenAI from 'openai';
117
+
118
+ const client = new OpenAI(); // reads OPENAI_API_KEY from the environment
119
+
120
+ /**
121
+ * @param {{ prompt: string, targetLocale: string, targetLabel: string, sourceLocale: string }} req
122
+ * @returns {Promise<string>} the model's reply — a complete .po block
123
+ */
124
+ export async function translate(req) {
125
+ const res = await client.chat.completions.create({
126
+ model: 'gpt-4o',
127
+ temperature: 0, // deterministic catalogs
128
+ messages: [{ role: 'user', content: req.prompt }],
129
+ });
130
+ const reply = res.choices[0]?.message?.content;
131
+ if (!reply) {
132
+ throw new Error(`Empty reply translating ${req.targetLabel}`);
133
+ }
134
+ return reply;
135
+ }
136
+ ```
137
+
138
+ The same shape works for any provider — only the request/response wiring changes.
139
+ Anthropic, for example:
140
+
141
+ ```js
142
+ // linguo.translator.mjs
143
+ import Anthropic from '@anthropic-ai/sdk';
144
+
145
+ const client = new Anthropic(); // reads ANTHROPIC_API_KEY
146
+
147
+ export async function translate({ prompt, targetLabel }) {
148
+ const msg = await client.messages.create({
149
+ model: 'claude-sonnet-4-6',
150
+ max_tokens: 8192,
151
+ messages: [{ role: 'user', content: prompt }],
152
+ });
153
+ const block = msg.content.find((b) => b.type === 'text');
154
+ if (!block) throw new Error(`Empty reply translating ${targetLabel}`);
155
+ return block.text;
156
+ }
157
+ ```
158
+
159
+ > A TypeScript module works too (`./linguo.translator.ts` via a loader like
160
+ > `tsx`/`ts-node`); import the `TranslateFunction` type from `@ng-linguo/extract`
161
+ > to type-check the signature. The export may also be the module's `default`.
162
+
163
+ Then link it and run:
164
+
165
+ ```json
166
+ { "locales": ["en", "pl", "de"], "translator": "./linguo.translator.mjs" }
167
+ ```
168
+
169
+ ```bash
170
+ linguo-extract translate --locale pl # one language
171
+ linguo-extract translate --all # every language with missing entries
172
+ ```
173
+
174
+ `translate` only touches entries that are still missing, then compiles. The same
175
+ function powers the **Automatic** option in the interactive menu (which still
176
+ offers **Manual** clipboard alongside it). Your provider, your model, your
177
+ secrets — never ours.
@@ -0,0 +1,53 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://ng-linguo.dev/schema/linguo.config.schema.json",
4
+ "title": "ng-linguo extract configuration",
5
+ "description": "Configuration for the @ng-linguo/extract CLI (linguo-extract). Paths are resolved relative to this file's directory.",
6
+ "type": "object",
7
+ "required": ["locales"],
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "$schema": {
11
+ "type": "string",
12
+ "description": "Path or URL to this JSON schema, enabling editor autocomplete and validation."
13
+ },
14
+ "locales": {
15
+ "type": "array",
16
+ "description": "Languages to generate catalogs for. The first is usually your source language. Use BCP-47 codes, e.g. \"en\", \"pl\", \"de\".",
17
+ "items": { "type": "string", "minLength": 1 },
18
+ "minItems": 1,
19
+ "examples": [["en", "pl", "de"]]
20
+ },
21
+ "sourceLocale": {
22
+ "type": "string",
23
+ "description": "The language your source strings are authored in. Its catalog is never flagged as missing.",
24
+ "default": "en"
25
+ },
26
+ "src": {
27
+ "type": "string",
28
+ "description": "Directory scanned for translatable strings (the t pipe/directive and t() calls).",
29
+ "default": "src"
30
+ },
31
+ "catalogs": {
32
+ "type": "string",
33
+ "description": "Directory holding the editable <locale>.po translation catalogs.",
34
+ "default": "i18n"
35
+ },
36
+ "output": {
37
+ "type": "string",
38
+ "description": "Directory for the compiled runtime <locale>.json dictionaries your app loads.",
39
+ "default": "src/assets/i18n"
40
+ },
41
+ "referenceBase": {
42
+ "type": "string",
43
+ "description": "How #: source references are written in the .po files: 'config' (relative to this file — portable, gettext-style) or 'workspace' (relative to the cwd — clickable in the terminal).",
44
+ "enum": ["config", "workspace"],
45
+ "default": "config"
46
+ },
47
+ "translator": {
48
+ "type": "string",
49
+ "description": "Path to a module exporting a translate() function for automatic AI translation (linguo-extract translate). Omit to translate via the clipboard instead.",
50
+ "examples": ["./linguo.translator.mjs"]
51
+ }
52
+ }
53
+ }
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@ng-linguo/extract",
3
+ "version": "0.9.0",
4
+ "description": "The ng-linguo extraction CLI — scans Angular source for translatable messages, manages gettext .po catalogs, and compiles them to runtime JSON. Pure Node, zero Angular deps.",
5
+ "license": "MIT",
6
+ "author": "jmwierzbicki",
7
+ "homepage": "https://github.com/jmwierzbicki/linguo/tree/main/packages/extract#readme",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/jmwierzbicki/linguo.git",
11
+ "directory": "packages/extract"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/jmwierzbicki/linguo/issues"
15
+ },
16
+ "keywords": [
17
+ "angular",
18
+ "i18n",
19
+ "internationalization",
20
+ "translation",
21
+ "extract",
22
+ "gettext",
23
+ "po",
24
+ "cli",
25
+ "ng-linguo"
26
+ ],
27
+ "type": "commonjs",
28
+ "main": "./src/index.js",
29
+ "types": "./src/index.d.ts",
30
+ "bin": {
31
+ "linguo-extract": "./src/cli.js"
32
+ },
33
+ "dependencies": {
34
+ "@clack/prompts": "^1.4.0",
35
+ "tslib": "^2.3.0"
36
+ },
37
+ "sideEffects": false
38
+ }
package/src/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/src/cli.js ADDED
@@ -0,0 +1,287 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const node_fs_1 = require("node:fs");
5
+ const node_path_1 = require("node:path");
6
+ const clipboard_1 = require("./lib/clipboard");
7
+ const config_1 = require("./lib/config");
8
+ const prompt_1 = require("./lib/prompt");
9
+ const runner_1 = require("./lib/runner");
10
+ const translator_1 = require("./lib/translator");
11
+ const interactive_1 = require("./interactive");
12
+ /** Flags that take a following value (`--name value`), used to skip them when
13
+ * scanning for the positional argument. */
14
+ const VALUE_FLAGS = new Set([
15
+ 'config',
16
+ 'locales',
17
+ 'locale',
18
+ 'source-locale',
19
+ 'src',
20
+ 'catalogs',
21
+ 'out',
22
+ 'po',
23
+ 'reference-base',
24
+ 'translator',
25
+ ]);
26
+ function flag(args, name) {
27
+ const prefix = `--${name}=`;
28
+ for (let i = 0; i < args.length; i += 1) {
29
+ const arg = args[i];
30
+ if (arg === undefined)
31
+ continue;
32
+ if (arg === `--${name}`) {
33
+ return args[i + 1];
34
+ }
35
+ if (arg.startsWith(prefix)) {
36
+ return arg.slice(prefix.length);
37
+ }
38
+ }
39
+ return undefined;
40
+ }
41
+ /** Whether a boolean flag (`--name`) is present. */
42
+ function hasFlag(args, name) {
43
+ return args.includes(`--${name}`);
44
+ }
45
+ /** The first non-flag argument, skipping `--flag value` pairs. */
46
+ function positional(args) {
47
+ for (let i = 0; i < args.length; i += 1) {
48
+ const arg = args[i];
49
+ if (arg === undefined)
50
+ continue;
51
+ if (arg.startsWith('--')) {
52
+ // `--name value` (no `=`) consumes the next token as its value.
53
+ if (!arg.includes('=') && VALUE_FLAGS.has(arg.slice(2))) {
54
+ i += 1;
55
+ }
56
+ continue;
57
+ }
58
+ return arg;
59
+ }
60
+ return undefined;
61
+ }
62
+ /** Resolve config from a `linguo.config.json` (if present) with flag overrides. */
63
+ function resolveConfig(rest) {
64
+ // Honor an explicit --config; otherwise discover the config by searching up
65
+ // from the cwd, then down through the workspace (so it works from anywhere).
66
+ const configPath = flag(rest, 'config') ?? (0, config_1.findConfigFile)(process.cwd());
67
+ const fromFile = configPath !== undefined && (0, node_fs_1.existsSync)(configPath)
68
+ ? (0, config_1.parseConfig)((0, node_fs_1.readFileSync)(configPath, 'utf8'))
69
+ : null;
70
+ const baseDir = configPath !== undefined && fromFile ? (0, node_path_1.dirname)((0, node_path_1.resolve)(configPath)) : process.cwd();
71
+ const localesFlag = flag(rest, 'locales');
72
+ const locales = localesFlag ? localesFlag.split(',').filter(Boolean) : fromFile?.locales;
73
+ if (!locales || locales.length === 0) {
74
+ throw new Error('No locales. Provide --locales en,pl or a linguo.config.json with a "locales" array.');
75
+ }
76
+ const refFlag = flag(rest, 'reference-base');
77
+ const referenceBase = refFlag === 'workspace' || refFlag === 'config'
78
+ ? refFlag
79
+ : (fromFile?.referenceBase ?? config_1.DEFAULT_CONFIG.referenceBase);
80
+ const translator = flag(rest, 'translator') ?? fromFile?.translator;
81
+ return {
82
+ baseDir,
83
+ config: {
84
+ locales,
85
+ sourceLocale: flag(rest, 'source-locale') ?? fromFile?.sourceLocale ?? config_1.DEFAULT_CONFIG.sourceLocale,
86
+ src: flag(rest, 'src') ?? fromFile?.src ?? config_1.DEFAULT_CONFIG.src,
87
+ catalogs: flag(rest, 'catalogs') ?? fromFile?.catalogs ?? config_1.DEFAULT_CONFIG.catalogs,
88
+ output: fromFile?.output ?? config_1.DEFAULT_CONFIG.output,
89
+ referenceBase,
90
+ ...(translator !== undefined ? { translator } : {}),
91
+ },
92
+ };
93
+ }
94
+ function pad(value, width) {
95
+ return String(value).padEnd(width);
96
+ }
97
+ function reportExtractStats(stats) {
98
+ const lines = [`ng-linguo: ${stats.messages} message(s) found in ${stats.files} file(s)`];
99
+ const width = Math.max(6, ...stats.locales.map((s) => s.locale.length));
100
+ for (const s of stats.locales) {
101
+ lines.push(` ${pad(s.locale, width)} ${pad(`${s.total} total`, 10)} ` +
102
+ `+${pad(`${s.added} new`, 8)} -${pad(`${s.removed} removed`, 12)} ${s.missing} missing`);
103
+ }
104
+ process.stdout.write(`${lines.join('\n')}\n`);
105
+ }
106
+ async function main(argv) {
107
+ const [command, ...rest] = argv;
108
+ // No command on an interactive terminal → open the guided menu.
109
+ if (command === undefined && process.stdin.isTTY && process.stdout.isTTY) {
110
+ await (0, interactive_1.runInteractive)();
111
+ return;
112
+ }
113
+ if (command === 'extract') {
114
+ const { config, baseDir } = resolveConfig(rest);
115
+ const stats = (0, runner_1.extractToCatalogs)({
116
+ srcDir: (0, node_path_1.resolve)(baseDir, config.src),
117
+ outDir: (0, node_path_1.resolve)(baseDir, flag(rest, 'out') ?? config.catalogs),
118
+ locales: config.locales,
119
+ sourceLocale: config.sourceLocale,
120
+ // 'workspace' makes #: refs relative to where the CLI runs (clickable);
121
+ // 'config' keeps them relative to the config file (gettext-style).
122
+ cwd: config.referenceBase === 'workspace' ? process.cwd() : baseDir,
123
+ });
124
+ reportExtractStats(stats);
125
+ return;
126
+ }
127
+ if (command === 'compile') {
128
+ const { config, baseDir } = resolveConfig(rest);
129
+ (0, runner_1.compileCatalogs)({
130
+ poDir: (0, node_path_1.resolve)(baseDir, flag(rest, 'po') ?? config.catalogs),
131
+ outDir: (0, node_path_1.resolve)(baseDir, flag(rest, 'out') ?? config.output),
132
+ });
133
+ return;
134
+ }
135
+ if (command === 'translate') {
136
+ const { config, baseDir } = resolveConfig(rest);
137
+ if (config.translator === undefined) {
138
+ throw new Error('translate: no "translator" configured. Add a "translator" module path to ' +
139
+ 'linguo.config.json (a module exporting a translate() function), or use ' +
140
+ '"copyprompt" / the interactive menu for the clipboard flow.');
141
+ }
142
+ const all = hasFlag(rest, 'all');
143
+ const localeFlag = flag(rest, 'locale');
144
+ if (!all && localeFlag === undefined) {
145
+ throw new Error('translate: pass --locale <code> or --all.');
146
+ }
147
+ if (localeFlag !== undefined && !config.locales.includes(localeFlag)) {
148
+ throw new Error(`translate: "${localeFlag}" is not a configured locale (${config.locales.join(', ')}).`);
149
+ }
150
+ const requested = all ? config.locales : localeFlag !== undefined ? [localeFlag] : [];
151
+ const targets = requested.filter((l) => l !== config.sourceLocale);
152
+ if (targets.length === 0) {
153
+ process.stdout.write('Nothing to translate (only the source locale was selected).\n');
154
+ return;
155
+ }
156
+ const translate = await (0, translator_1.loadTranslator)((0, node_path_1.resolve)(baseDir, config.translator));
157
+ let totalApplied = 0;
158
+ for (const locale of targets) {
159
+ const label = (0, prompt_1.localeLabel)(locale);
160
+ const poPath = (0, node_path_1.resolve)(baseDir, config.catalogs, `${locale}.po`);
161
+ if (!(0, node_fs_1.existsSync)(poPath)) {
162
+ process.stderr.write(` ${label}: no catalog at ${poPath} — run "extract" first. Skipped.\n`);
163
+ continue;
164
+ }
165
+ const outcome = await (0, translator_1.autoTranslateCatalog)({
166
+ translate,
167
+ poText: (0, node_fs_1.readFileSync)(poPath, 'utf8'),
168
+ targetLocale: locale,
169
+ targetLabel: label,
170
+ sourceLocale: config.sourceLocale,
171
+ });
172
+ if (outcome.untranslated === 0) {
173
+ process.stdout.write(` ${label}: already fully translated.\n`);
174
+ continue;
175
+ }
176
+ (0, node_fs_1.writeFileSync)(poPath, outcome.po, 'utf8');
177
+ totalApplied += outcome.applied;
178
+ const tail = outcome.remaining > 0 ? ` (${outcome.remaining} still missing)` : '';
179
+ process.stdout.write(` ${label}: applied ${outcome.applied}/${outcome.untranslated}${tail}.\n`);
180
+ }
181
+ if (totalApplied > 0) {
182
+ (0, runner_1.compileCatalogs)({
183
+ poDir: (0, node_path_1.resolve)(baseDir, config.catalogs),
184
+ outDir: (0, node_path_1.resolve)(baseDir, config.output),
185
+ });
186
+ process.stdout.write(`Compiled catalogs → ${config.output}\n`);
187
+ }
188
+ return;
189
+ }
190
+ if (command === 'copyprompt') {
191
+ const language = positional(rest);
192
+ if (language === undefined) {
193
+ throw new Error('Usage: copyprompt <language> (e.g. "Polski", "German", or "pl").');
194
+ }
195
+ const { config, baseDir } = resolveConfig(rest);
196
+ const target = (0, prompt_1.resolveTargetLocale)(language, config.locales);
197
+ if (!target) {
198
+ throw new Error(`Could not match "${language}" to a configured locale. ` +
199
+ `Configured locales: ${config.locales.join(', ')}.`);
200
+ }
201
+ const poPath = (0, node_path_1.resolve)(baseDir, flag(rest, 'po') ?? config.catalogs, `${target.locale}.po`);
202
+ if (!(0, node_fs_1.existsSync)(poPath)) {
203
+ throw new Error(`No catalog at ${poPath}. Run "linguo-extract extract" first.`);
204
+ }
205
+ const prompt = (0, prompt_1.buildTranslationPrompt)(target.label, (0, node_fs_1.readFileSync)(poPath, 'utf8'));
206
+ // `--stdout` prints the prompt instead of copying — useful for piping or
207
+ // when no clipboard tool is available (CI, headless).
208
+ if (hasFlag(rest, 'stdout')) {
209
+ process.stdout.write(`${prompt}\n`);
210
+ return;
211
+ }
212
+ if ((0, clipboard_1.copyToClipboard)(prompt)) {
213
+ process.stdout.write(`Copied translation prompt for ${target.label} to the clipboard ` +
214
+ `(${prompt.length} chars). Paste it into an LLM, then save the reply over ${target.locale}.po.\n`);
215
+ return;
216
+ }
217
+ process.stderr.write('Could not access the clipboard. Re-run with --stdout to print the prompt instead.\n');
218
+ process.exitCode = 1;
219
+ return;
220
+ }
221
+ if (command === 'init') {
222
+ // On a TTY (and without --locales), open the interactive create/edit form.
223
+ if (process.stdin.isTTY && process.stdout.isTTY && flag(rest, 'locales') === undefined) {
224
+ await (0, interactive_1.runInit)();
225
+ return;
226
+ }
227
+ // Non-interactive: build from flags + defaults (scriptable / CI).
228
+ const localesFlag = flag(rest, 'locales');
229
+ if (localesFlag === undefined) {
230
+ throw new Error('init: provide --locales en,pl[,…] for non-interactive use, or run it in a terminal for the interactive form.');
231
+ }
232
+ const locales = localesFlag.split(',').filter(Boolean);
233
+ if (locales.length === 0) {
234
+ throw new Error('init: --locales must list at least one locale code.');
235
+ }
236
+ const refFlag = flag(rest, 'reference-base');
237
+ const config = (0, config_1.buildInitConfig)({
238
+ locales,
239
+ sourceLocale: flag(rest, 'source-locale'),
240
+ src: flag(rest, 'src'),
241
+ catalogs: flag(rest, 'catalogs'),
242
+ output: flag(rest, 'out'),
243
+ referenceBase: refFlag === 'workspace' ? 'workspace' : 'config',
244
+ translator: flag(rest, 'translator'),
245
+ });
246
+ const target = (0, node_path_1.resolve)(process.cwd(), flag(rest, 'config') ?? 'linguo.config.json');
247
+ const existed = (0, node_fs_1.existsSync)(target);
248
+ if (existed && !hasFlag(rest, 'force')) {
249
+ throw new Error(`${target} already exists. Pass --force to overwrite.`);
250
+ }
251
+ (0, node_fs_1.writeFileSync)(target, (0, config_1.serializeConfig)(config), 'utf8');
252
+ process.stdout.write(`${existed ? 'Overwrote' : 'Created'} ${target}\n`);
253
+ return;
254
+ }
255
+ process.stderr.write([
256
+ 'Usage: linguo-extract [command] [options]',
257
+ '',
258
+ 'Run with no command in a terminal to open the guided interactive menu',
259
+ '(which can also create or edit linguo.config.json).',
260
+ 'Reads linguo.config.json (found automatically, or --config <path>); flags override.',
261
+ '',
262
+ ' init [--locales en,pl] [--source-locale en] [--src <dir>] [--catalogs <dir>]',
263
+ ' [--out <dir>] [--reference-base config|workspace] [--translator <module>] [--force]',
264
+ ' Create (or, in a terminal, create/edit) linguo.config.json.',
265
+ '',
266
+ ' extract [--config <path>] [--src <dir>] [--out <dir>] [--locales en,pl] [--source-locale en]',
267
+ ' Scan sources and create/update <locale>.po catalogs.',
268
+ '',
269
+ ' compile [--config <path>] [--po <dir>] [--out <dir>]',
270
+ ' Compile <locale>.po catalogs into runtime <locale>.json.',
271
+ '',
272
+ ' translate (--locale pl | --all) [--config <path>] [--translator <module>]',
273
+ ' Translate missing entries automatically via the configured "translator"',
274
+ ' module (one exporting a translate() function), then compile.',
275
+ '',
276
+ ' copyprompt <language> [--config <path>] [--po <dir>] [--stdout]',
277
+ ' Copy an LLM translation prompt for <language>.po to the clipboard.',
278
+ ' <language> may be a code (pl), English name (Polish), or endonym (Polski).',
279
+ '',
280
+ ].join('\n'));
281
+ process.exitCode = command === undefined ? 1 : 2;
282
+ }
283
+ main(process.argv.slice(2)).catch((error) => {
284
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
285
+ process.exitCode = 1;
286
+ });
287
+ //# sourceMappingURL=cli.js.map
package/src/cli.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../../../packages/extract/src/cli.ts"],"names":[],"mappings":"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"}
package/src/index.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ export { normalizeMessage } from './lib/normalize';
2
+ export { extractMessages } from './lib/scan';
3
+ export type { SourceFile, ExtractedMessage } from './lib/scan';
4
+ export { parsePo, serializePo } from './lib/po';
5
+ export type { PoEntry } from './lib/po';
6
+ export { mergeCatalog } from './lib/merge';
7
+ export { compileEntries } from './lib/compile';
8
+ export { parseConfig, DEFAULT_CONFIG } from './lib/config';
9
+ export type { LinguoConfig } from './lib/config';
10
+ export type { TranslateFunction, TranslateRequest } from './lib/translator';
package/src/index.js ADDED
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_CONFIG = exports.parseConfig = exports.compileEntries = exports.mergeCatalog = exports.serializePo = exports.parsePo = exports.extractMessages = exports.normalizeMessage = void 0;
4
+ var normalize_1 = require("./lib/normalize");
5
+ Object.defineProperty(exports, "normalizeMessage", { enumerable: true, get: function () { return normalize_1.normalizeMessage; } });
6
+ var scan_1 = require("./lib/scan");
7
+ Object.defineProperty(exports, "extractMessages", { enumerable: true, get: function () { return scan_1.extractMessages; } });
8
+ var po_1 = require("./lib/po");
9
+ Object.defineProperty(exports, "parsePo", { enumerable: true, get: function () { return po_1.parsePo; } });
10
+ Object.defineProperty(exports, "serializePo", { enumerable: true, get: function () { return po_1.serializePo; } });
11
+ var merge_1 = require("./lib/merge");
12
+ Object.defineProperty(exports, "mergeCatalog", { enumerable: true, get: function () { return merge_1.mergeCatalog; } });
13
+ var compile_1 = require("./lib/compile");
14
+ Object.defineProperty(exports, "compileEntries", { enumerable: true, get: function () { return compile_1.compileEntries; } });
15
+ var config_1 = require("./lib/config");
16
+ Object.defineProperty(exports, "parseConfig", { enumerable: true, get: function () { return config_1.parseConfig; } });
17
+ Object.defineProperty(exports, "DEFAULT_CONFIG", { enumerable: true, get: function () { return config_1.DEFAULT_CONFIG; } });
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../packages/extract/src/index.ts"],"names":[],"mappings":";;;AAAA,6CAAmD;AAA1C,6GAAA,gBAAgB,OAAA;AACzB,mCAA6C;AAApC,uGAAA,eAAe,OAAA;AAExB,+BAAgD;AAAvC,6FAAA,OAAO,OAAA;AAAE,iGAAA,WAAW,OAAA;AAE7B,qCAA2C;AAAlC,qGAAA,YAAY,OAAA;AACrB,yCAA+C;AAAtC,yGAAA,cAAc,OAAA;AACvB,uCAA2D;AAAlD,qGAAA,WAAW,OAAA;AAAE,wGAAA,cAAc,OAAA"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Run the standalone config wizard (the `init` command on a TTY): edit an
3
+ * existing `linguo.config.json`, or create one in the current directory.
4
+ */
5
+ export declare function runInit(): Promise<void>;
6
+ /**
7
+ * Run the guided interactive menu (clack). Invoked by the CLI when it is given
8
+ * no command and is attached to a TTY. Discovers `linguo.config.json`, then
9
+ * loops: extract, compile, translate via an LLM, run the full pipeline, or exit.
10
+ * All actions are also available non-interactively as commands.
11
+ */
12
+ export declare function runInteractive(): Promise<void>;