@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.
- package/README.md +177 -0
- package/linguo.config.schema.json +53 -0
- package/package.json +38 -0
- package/src/cli.d.ts +2 -0
- package/src/cli.js +287 -0
- package/src/cli.js.map +1 -0
- package/src/index.d.ts +10 -0
- package/src/index.js +18 -0
- package/src/index.js.map +1 -0
- package/src/interactive.d.ts +12 -0
- package/src/interactive.js +679 -0
- package/src/interactive.js.map +1 -0
- package/src/lib/apply.d.ts +20 -0
- package/src/lib/apply.js +43 -0
- package/src/lib/apply.js.map +1 -0
- package/src/lib/clipboard.d.ts +17 -0
- package/src/lib/clipboard.js +96 -0
- package/src/lib/clipboard.js.map +1 -0
- package/src/lib/compile.d.ts +12 -0
- package/src/lib/compile.js +29 -0
- package/src/lib/compile.js.map +1 -0
- package/src/lib/config.d.ts +104 -0
- package/src/lib/config.js +185 -0
- package/src/lib/config.js.map +1 -0
- package/src/lib/merge.d.ts +13 -0
- package/src/lib/merge.js +34 -0
- package/src/lib/merge.js.map +1 -0
- package/src/lib/normalize.d.ts +15 -0
- package/src/lib/normalize.js +21 -0
- package/src/lib/normalize.js.map +1 -0
- package/src/lib/po.d.ts +25 -0
- package/src/lib/po.js +110 -0
- package/src/lib/po.js.map +1 -0
- package/src/lib/prompt.d.ts +33 -0
- package/src/lib/prompt.js +80 -0
- package/src/lib/prompt.js.map +1 -0
- package/src/lib/runner.d.ts +62 -0
- package/src/lib/runner.js +102 -0
- package/src/lib/runner.js.map +1 -0
- package/src/lib/scan.d.ts +31 -0
- package/src/lib/scan.js +183 -0
- package/src/lib/scan.js.map +1 -0
- package/src/lib/translation-prompt.txt +214 -0
- package/src/lib/translator.d.ts +83 -0
- package/src/lib/translator.js +91 -0
- 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
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
|
package/src/index.js.map
ADDED
|
@@ -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>;
|