champollion 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +133 -0
- package/README.md +387 -0
- package/bin/cli.js +278 -0
- package/index.js +135 -0
- package/lib/api-key.js +127 -0
- package/lib/autofix.js +432 -0
- package/lib/bridge/method_bridge.py +430 -0
- package/lib/card-source-resolution.mjs +284 -0
- package/lib/cards/cache.js +169 -0
- package/lib/cards/env.js +82 -0
- package/lib/cards/fetch-card-child.js +38 -0
- package/lib/cards/reader.js +435 -0
- package/lib/cards/refresh.js +111 -0
- package/lib/cards/remote.js +387 -0
- package/lib/cldf-export.mjs +540 -0
- package/lib/cldf-terms.mjs +62 -0
- package/lib/command-help.js +790 -0
- package/lib/commands/audit.js +49 -0
- package/lib/commands/card.js +454 -0
- package/lib/commands/doctor.js +559 -0
- package/lib/commands/fonts.js +489 -0
- package/lib/commands/help.js +91 -0
- package/lib/commands/init.js +1259 -0
- package/lib/commands/integrity.js +148 -0
- package/lib/commands/leaderboard.js +478 -0
- package/lib/commands/lint.js +30 -0
- package/lib/commands/models.js +177 -0
- package/lib/commands/plugin.js +103 -0
- package/lib/commands/provenance.js +45 -0
- package/lib/commands/recommend.js +75 -0
- package/lib/commands/register-corpus.js +678 -0
- package/lib/commands/repair-script.js +42 -0
- package/lib/commands/seal-corpus.js +355 -0
- package/lib/commands/seo.js +72 -0
- package/lib/commands/serve.js +147 -0
- package/lib/commands/status.js +265 -0
- package/lib/commands/submit.js +332 -0
- package/lib/commands/sync.js +89 -0
- package/lib/commands/tm.js +573 -0
- package/lib/commands/verify.js +39 -0
- package/lib/commands/watch.js +20 -0
- package/lib/commands/wrap.js +138 -0
- package/lib/commands/xliff.js +327 -0
- package/lib/commercial-eligibility.js +235 -0
- package/lib/concurrent.js +87 -0
- package/lib/config.js +523 -0
- package/lib/contamination-lane.js +76 -0
- package/lib/content-sync.js +731 -0
- package/lib/content.js +733 -0
- package/lib/corpus-registration.mjs +608 -0
- package/lib/cost-report.js +346 -0
- package/lib/diff.js +155 -0
- package/lib/docusaurus-sync.js +1256 -0
- package/lib/flatten.js +55 -0
- package/lib/format.js +954 -0
- package/lib/hash.js +159 -0
- package/lib/icu.js +473 -0
- package/lib/integrity.js +689 -0
- package/lib/license-gate.mjs +478 -0
- package/lib/license-identify.mjs +229 -0
- package/lib/lint.js +629 -0
- package/lib/method-manifest.js +60 -0
- package/lib/methods/anthropic.js +140 -0
- package/lib/methods/apertium.js +163 -0
- package/lib/methods/api.js +316 -0
- package/lib/methods/base.js +184 -0
- package/lib/methods/content-separator.js +45 -0
- package/lib/methods/deepl.js +426 -0
- package/lib/methods/direct-llm.js +586 -0
- package/lib/methods/external.js +332 -0
- package/lib/methods/fetch-with-retry.js +124 -0
- package/lib/methods/gemini.js +147 -0
- package/lib/methods/google-translate.js +402 -0
- package/lib/methods/http-utils.js +122 -0
- package/lib/methods/libretranslate.js +314 -0
- package/lib/methods/llm-coached.js +670 -0
- package/lib/methods/llm.js +592 -0
- package/lib/methods/local.js +76 -0
- package/lib/methods/microsoft-translator.js +331 -0
- package/lib/methods/openai.js +131 -0
- package/lib/methods/openrouter-client.js +327 -0
- package/lib/methods/openrouter-pricing.js +156 -0
- package/lib/methods/provider-env.js +115 -0
- package/lib/methods/provider-pricing.js +310 -0
- package/lib/methods/tilde.js +150 -0
- package/lib/methods/translated.js +229 -0
- package/lib/methods/translation-error.js +80 -0
- package/lib/models.js +258 -0
- package/lib/no-translate.js +233 -0
- package/lib/output.js +238 -0
- package/lib/pairs.js +547 -0
- package/lib/plugins.js +447 -0
- package/lib/provenance.js +323 -0
- package/lib/recommend.js +648 -0
- package/lib/registers.js +1185 -0
- package/lib/repair-script.js +266 -0
- package/lib/scripts.js +994 -0
- package/lib/seal.mjs +464 -0
- package/lib/sealed-qualifier.mjs +211 -0
- package/lib/security.js +59 -0
- package/lib/segment.js +369 -0
- package/lib/seo.js +275 -0
- package/lib/serve.js +854 -0
- package/lib/string-classify.js +85 -0
- package/lib/submit.mjs +344 -0
- package/lib/sync.js +969 -0
- package/lib/tags/bcp47.js +202 -0
- package/lib/tags/resolve.js +314 -0
- package/lib/terminology.js +111 -0
- package/lib/tm-seed.js +294 -0
- package/lib/tm.js +515 -0
- package/lib/translate-pair.js +197 -0
- package/lib/translate.js +203 -0
- package/lib/types.js +230 -0
- package/lib/validate.js +510 -0
- package/lib/verify.js +451 -0
- package/lib/watch.js +145 -0
- package/lib/xliff.js +184 -0
- package/package.json +93 -0
- package/shared/ATTRIBUTION.md +145 -0
- package/shared/CORPORA-CARDS.md +288 -0
- package/shared/DATA-SOVEREIGNTY.md +500 -0
- package/shared/LANGUAGE-CARD-FIELDS.md +532 -0
- package/shared/card-lint-baseline.json +3189 -0
- package/shared/cards-fallback.json +1 -0
- package/shared/catalogue/card-config.json +6091 -0
- package/shared/catalogue/external-results.json +3888 -0
- package/shared/catalogue/gender-guidance.json +1038 -0
- package/shared/catalogue/method-coverage.json +1751 -0
- package/shared/catalogue/metric-coverage.json +170 -0
- package/shared/catalogue/metric-reliability.json +1 -0
- package/shared/catalogue/register-presets.json +3180 -0
- package/shared/catalogue/vitality-scales.json +55 -0
- package/shared/cldr-index.json +1115 -0
- package/shared/code-bridge.json +253 -0
- package/shared/corpora-cards-v1-reference.md +281 -0
- package/shared/curated-dictionary-flags.json +35 -0
- package/shared/curated-endonyms.json +35 -0
- package/shared/curated-fsts.json +51 -0
- package/shared/curated-orthography-conventions.json +26 -0
- package/shared/curated-sil-resources.json +374 -0
- package/shared/curated-tools.json +41 -0
- package/shared/docent/corpus.json +11333 -0
- package/shared/docent/faq.en.json +564 -0
- package/shared/docent/register-blocks.json +60 -0
- package/shared/docent/system-prompt.md +144 -0
- package/shared/domain-taxonomy.json +35 -0
- package/shared/explainers/glossary.json +2975 -0
- package/shared/explainers/tc-features.json +20112 -0
- package/shared/explainers/term-watchlist.json +147 -0
- package/shared/human-services.json +59 -0
- package/shared/license-corrections.json +261 -0
- package/shared/license-evidence.json +13452 -0
- package/shared/licenses.json +6781 -0
- package/shared/method-registry.json +236 -0
- package/shared/metric-registry.json +620 -0
- package/shared/model-aliases.json +7 -0
- package/shared/schemas/champollion-plugin.schema.json +206 -0
- package/shared/schemas/corpora-card.schema.json +957 -0
- package/shared/schemas/domain-taxonomy.schema.json +64 -0
- package/shared/schemas/external-results.schema.json +314 -0
- package/shared/schemas/human-services.schema.json +90 -0
- package/shared/schemas/language-card.schema.json +1308 -0
- package/shared/schemas/licenses.schema.json +155 -0
- package/shared/schemas/method-card.schema.json +412 -0
- package/shared/schemas/method-registry.schema.json +85 -0
- package/shared/schemas/metric-registry.schema.json +96 -0
- package/shared/schemas/metric-reliability.schema.json +178 -0
- package/shared/schemas/model-aliases.schema.json +27 -0
- package/shared/schemas/source-snapshot.schema.json +96 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* translate-pair.js — Shared translation pipeline for a single pair
|
|
3
|
+
*
|
|
4
|
+
* Encapsulates the common sequence used by both the standard sync path
|
|
5
|
+
* and the Docusaurus sync path:
|
|
6
|
+
*
|
|
7
|
+
* 1. TM partition: split keys into cached hits and API misses
|
|
8
|
+
* 2. API call: translate the misses via translateBatch
|
|
9
|
+
* 3. Quality gate: validate translations (hallucination, script, length)
|
|
10
|
+
* — TM hits that fail are evicted so the poison cannot be re-served
|
|
11
|
+
* — gate failures get ONE feedback retry (the rejection reason is
|
|
12
|
+
* injected into the prompt; a blind retry at temperature 0 would
|
|
13
|
+
* return byte-identical output)
|
|
14
|
+
* 4. TM store: cache only gate-validated API translations
|
|
15
|
+
*
|
|
16
|
+
* ORDER MATTERS: the TM must only ever hold gate-validated values. An
|
|
17
|
+
* earlier version stored API output before validation; one degenerate
|
|
18
|
+
* response (e.g. "for translating" → "吗") then poisoned the cache — every
|
|
19
|
+
* later sync served it, failed the gate, and never consulted the API again.
|
|
20
|
+
*
|
|
21
|
+
* Callers get back a structured result and handle their own control flow
|
|
22
|
+
* (fallback decisions, error messages, script conversion, etc.) because
|
|
23
|
+
* those details differ between sync paths.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { translateBatch } from './translate.js';
|
|
27
|
+
import { validateTranslations, logGateFailures } from './validate.js';
|
|
28
|
+
import { partitionByTM, storeTM, evictTM, tmMethodKey } from './tm.js';
|
|
29
|
+
import { output } from './output.js';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Translate a set of string keys through the TM + API + quality gate pipeline.
|
|
33
|
+
*
|
|
34
|
+
* @param {string[]} stringKeys - Keys to translate (must be string-valued in sourceFlat)
|
|
35
|
+
* @param {object} sourceFlat - Full flattened source locale map
|
|
36
|
+
* @param {object} pairConfig - Pair configuration (target, method, model, batchSize, etc.)
|
|
37
|
+
* @param {string} pairKey - Human-readable pair identifier for logging (e.g. "en→fr")
|
|
38
|
+
* @param {object} options
|
|
39
|
+
* @param {string} options.apiKey - API key for translation provider
|
|
40
|
+
* @param {object} options.tm - Translation Memory object (mutable — entries are stored in-place)
|
|
41
|
+
* @param {string} options.targetCode - Target language code
|
|
42
|
+
* @param {object} [options.descriptions] - Optional key descriptions (Docusaurus format)
|
|
43
|
+
* @param {Function} [options.onProgress] - Progress callback: (completed, total) => void
|
|
44
|
+
* @returns {Promise<TranslateResult>}
|
|
45
|
+
*
|
|
46
|
+
* @typedef {object} TranslateResult
|
|
47
|
+
* @property {object|null} translated - Validated translations, or null if all failed
|
|
48
|
+
* @property {number} tmHitCount - Number of keys served from TM cache
|
|
49
|
+
* @property {Array} failures - Quality gate failures (for caller logging)
|
|
50
|
+
* @property {boolean} apiCalled - Whether the API was actually invoked
|
|
51
|
+
* @property {boolean} apiReturnedNull - Whether the API was called but returned null
|
|
52
|
+
*/
|
|
53
|
+
export async function translateAndValidate(stringKeys, sourceFlat, pairConfig, pairKey, options) {
|
|
54
|
+
const { apiKey, tm, targetCode, descriptions } = options;
|
|
55
|
+
const method = pairConfig.method || 'llm';
|
|
56
|
+
|
|
57
|
+
// TM entries are keyed on the FULL method key (method|model|register|coaching),
|
|
58
|
+
// not the bare method name: switching model, register, or coaching must be a
|
|
59
|
+
// cache miss, never a silent re-serve of old-style translations. See tm.js.
|
|
60
|
+
const tmKey = tmMethodKey(pairConfig);
|
|
61
|
+
|
|
62
|
+
// Step 1: TM partition — serve cached hits, identify API misses
|
|
63
|
+
const { hits: tmHits, misses: tmMisses } = partitionByTM(
|
|
64
|
+
tm, sourceFlat, stringKeys, targetCode, tmKey
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
const tmHitCount = Object.keys(tmHits).length;
|
|
68
|
+
if (tmHitCount > 0) {
|
|
69
|
+
output.info(`[TM] ${tmHitCount} key(s) served from cache`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Start with TM hits as the base
|
|
73
|
+
const translated = { ...tmHits };
|
|
74
|
+
let apiCalled = false;
|
|
75
|
+
let apiReturnedNull = false;
|
|
76
|
+
|
|
77
|
+
// Provenance: keys whose current value came from the API this run.
|
|
78
|
+
// Only these are eligible for TM storage after validation, and only
|
|
79
|
+
// non-API (TM-served) failures need cache eviction.
|
|
80
|
+
const apiKeys = new Set();
|
|
81
|
+
|
|
82
|
+
const batchOptions = {
|
|
83
|
+
apiKey,
|
|
84
|
+
model: pairConfig.model,
|
|
85
|
+
batchSize: pairConfig.batchSize,
|
|
86
|
+
onProgress: options.onProgress || null,
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// Docusaurus passes descriptions for disambiguation context
|
|
90
|
+
if (descriptions) {
|
|
91
|
+
batchOptions.descriptions = descriptions;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Step 2: API call for misses
|
|
95
|
+
if (tmMisses.length > 0) {
|
|
96
|
+
output.progress(` Translating ${tmMisses.length} key(s) to ${pairConfig.name} (${method})...`);
|
|
97
|
+
|
|
98
|
+
const apiResult = await translateBatch(tmMisses, sourceFlat, pairConfig, batchOptions);
|
|
99
|
+
apiCalled = true;
|
|
100
|
+
|
|
101
|
+
if (apiResult) {
|
|
102
|
+
Object.assign(translated, apiResult);
|
|
103
|
+
for (const k of Object.keys(apiResult)) apiKeys.add(k);
|
|
104
|
+
} else {
|
|
105
|
+
apiReturnedNull = true;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Step 3: Quality gate — validate translations before accepting
|
|
110
|
+
let validated = {};
|
|
111
|
+
let failures = [];
|
|
112
|
+
if (Object.keys(translated).length > 0) {
|
|
113
|
+
const result = validateTranslations(translated, sourceFlat, pairConfig);
|
|
114
|
+
validated = result.validated;
|
|
115
|
+
failures = result.failures;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Step 3a: Evict poisoned TM entries. A TM-served value that fails the
|
|
119
|
+
// gate would otherwise be re-served (and re-fail) on every future sync
|
|
120
|
+
// without the API ever being consulted again.
|
|
121
|
+
for (const f of failures) {
|
|
122
|
+
if (f.key in tmHits && !apiKeys.has(f.key) && typeof sourceFlat[f.key] === 'string') {
|
|
123
|
+
evictTM(tm, sourceFlat[f.key], targetCode, tmKey);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Step 3b: Feedback retry — one corrective round for gate failures.
|
|
128
|
+
// The rejection reason is injected as per-key context so the prompt
|
|
129
|
+
// actually changes; without it, a temperature-0 retry is a no-op.
|
|
130
|
+
//
|
|
131
|
+
// NOT gated on `apiKey`: that is the OpenRouter key, and the retry runs
|
|
132
|
+
// through the pair's OWN method — google-translate/deepl/direct providers
|
|
133
|
+
// carry their own credentials. The old `&& apiKey` guard silently skipped
|
|
134
|
+
// the corrective round for every direct provider, which meant a TM entry
|
|
135
|
+
// evicted by the gate (step 3a) was only re-billed on the NEXT sync — a
|
|
136
|
+
// two-pass heal nobody asked for. A method that genuinely cannot run
|
|
137
|
+
// returns null here and the failures simply stand.
|
|
138
|
+
if (failures.length > 0) {
|
|
139
|
+
const retryKeys = failures
|
|
140
|
+
.map(f => f.key)
|
|
141
|
+
.filter(k => typeof sourceFlat[k] === 'string');
|
|
142
|
+
|
|
143
|
+
if (retryKeys.length > 0) {
|
|
144
|
+
output.progress(` Quality gate rejected ${retryKeys.length} key(s) — retrying with feedback...`);
|
|
145
|
+
|
|
146
|
+
const feedbackDescriptions = { ...(descriptions || {}) };
|
|
147
|
+
for (const f of failures) {
|
|
148
|
+
const rejected = String(f.value ?? '').slice(0, 60);
|
|
149
|
+
const base = feedbackDescriptions[f.key] ? `${feedbackDescriptions[f.key]} — ` : '';
|
|
150
|
+
feedbackDescriptions[f.key] =
|
|
151
|
+
`${base}RETRY: a previous attempt ("${rejected}") was rejected by the quality gate: ${f.reason}. ` +
|
|
152
|
+
`Provide a complete, self-contained ${pairConfig.name} translation of this exact string.`;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const retryResult = await translateBatch(retryKeys, sourceFlat, pairConfig, {
|
|
156
|
+
...batchOptions,
|
|
157
|
+
onProgress: null, // avoid double-counting progress
|
|
158
|
+
descriptions: feedbackDescriptions,
|
|
159
|
+
});
|
|
160
|
+
apiCalled = true;
|
|
161
|
+
|
|
162
|
+
if (retryResult) {
|
|
163
|
+
const retryValidation = validateTranslations(retryResult, sourceFlat, pairConfig);
|
|
164
|
+
Object.assign(validated, retryValidation.validated);
|
|
165
|
+
for (const k of Object.keys(retryValidation.validated)) apiKeys.add(k);
|
|
166
|
+
|
|
167
|
+
// Keys that passed on retry are no longer failures; keys the retry
|
|
168
|
+
// produced a fresh (still failing) value for get the newer record.
|
|
169
|
+
const passed = new Set(Object.keys(retryValidation.validated));
|
|
170
|
+
const retryFailureByKey = new Map(retryValidation.failures.map(f => [f.key, f]));
|
|
171
|
+
failures = failures
|
|
172
|
+
.filter(f => !passed.has(f.key))
|
|
173
|
+
.map(f => retryFailureByKey.get(f.key) || f);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (failures.length > 0) {
|
|
179
|
+
logGateFailures(failures, pairKey);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Step 4: TM store — only gate-validated values that came from the API.
|
|
183
|
+
// TM hits are already cached; unvalidated output must never enter the TM.
|
|
184
|
+
for (const [k, v] of Object.entries(validated)) {
|
|
185
|
+
if (apiKeys.has(k) && typeof v === 'string' && typeof sourceFlat[k] === 'string') {
|
|
186
|
+
storeTM(tm, sourceFlat[k], targetCode, tmKey, v);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return {
|
|
191
|
+
translated: Object.keys(validated).length > 0 ? validated : null,
|
|
192
|
+
tmHitCount,
|
|
193
|
+
failures,
|
|
194
|
+
apiCalled,
|
|
195
|
+
apiReturnedNull,
|
|
196
|
+
};
|
|
197
|
+
}
|
package/lib/translate.js
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Translation orchestrator — delegates to method-specific implementations.
|
|
3
|
+
*
|
|
4
|
+
* v3 ARCHITECTURE:
|
|
5
|
+
* In v2, this module contained all the OpenRouter API logic directly.
|
|
6
|
+
* In v3, the actual API calls live in lib/methods/llm.js (and future
|
|
7
|
+
* method implementations). This module is now the orchestrator:
|
|
8
|
+
*
|
|
9
|
+
* 1. Receives a pair config (from pairs.js) with method/model/qualityTier
|
|
10
|
+
* 2. Instantiates the correct TranslationMethod subclass
|
|
11
|
+
* 3. Delegates the translation call
|
|
12
|
+
* 4. Returns the result
|
|
13
|
+
*
|
|
14
|
+
* This separation means adding a new translation strategy (e.g., fst-gated,
|
|
15
|
+
* human-review) requires only implementing a new method class — zero changes
|
|
16
|
+
* to the sync pipeline or any other consumer.
|
|
17
|
+
*
|
|
18
|
+
* BACKWARD COMPAT:
|
|
19
|
+
* The exported API (translateBatch, translateRawContent, isUnsafeKey) is
|
|
20
|
+
* preserved so that sync.js and content.js continue to work without
|
|
21
|
+
* changes during the transition. The only difference is that translateBatch
|
|
22
|
+
* now accepts an optional pairConfig as the third argument.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { LLMMethod, buildPrompt, inferKeyTypes } from './methods/llm.js';
|
|
26
|
+
import { isUnsafeKey } from './security.js';
|
|
27
|
+
import { LLMCoachedMethod } from './methods/llm-coached.js';
|
|
28
|
+
import { GoogleTranslateMethod } from './methods/google-translate.js';
|
|
29
|
+
import { APIMethod } from './methods/api.js';
|
|
30
|
+
import { DeepLMethod } from './methods/deepl.js';
|
|
31
|
+
import { MicrosoftTranslatorMethod } from './methods/microsoft-translator.js';
|
|
32
|
+
import { LibreTranslateMethod } from './methods/libretranslate.js';
|
|
33
|
+
import { ApertiumMethod } from './methods/apertium.js';
|
|
34
|
+
import { TildeMethod } from './methods/tilde.js';
|
|
35
|
+
import { TranslatedMethod } from './methods/translated.js';
|
|
36
|
+
import { OpenAIMethod } from './methods/openai.js';
|
|
37
|
+
import { AnthropicMethod } from './methods/anthropic.js';
|
|
38
|
+
import { GeminiMethod } from './methods/gemini.js';
|
|
39
|
+
import { LocalMethod } from './methods/local.js';
|
|
40
|
+
import { ExternalMethod } from './methods/external.js';
|
|
41
|
+
import { DEFAULT_OPENROUTER_MODEL } from './config.js';
|
|
42
|
+
import { manifestEntries, cliNameFor } from './method-manifest.js';
|
|
43
|
+
import { assertRoutable } from './commercial-eligibility.js';
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Registry of available translation methods.
|
|
47
|
+
*
|
|
48
|
+
* Each entry maps a method name to its constructor.
|
|
49
|
+
* To add a new method:
|
|
50
|
+
* 1. Create the class in lib/methods/<name>.js
|
|
51
|
+
* 2. Register it here
|
|
52
|
+
* 3. Users can reference it in config pairs: { method: "<name>" }
|
|
53
|
+
*/
|
|
54
|
+
const METHOD_REGISTRY = {
|
|
55
|
+
'llm': LLMMethod,
|
|
56
|
+
'llm-coached': LLMCoachedMethod,
|
|
57
|
+
'google-translate': GoogleTranslateMethod,
|
|
58
|
+
'api': APIMethod,
|
|
59
|
+
'deepl': DeepLMethod,
|
|
60
|
+
'microsoft-translator': MicrosoftTranslatorMethod,
|
|
61
|
+
'libretranslate': LibreTranslateMethod,
|
|
62
|
+
'apertium': ApertiumMethod,
|
|
63
|
+
'tilde': TildeMethod,
|
|
64
|
+
'translated': TranslatedMethod,
|
|
65
|
+
'openai': OpenAIMethod,
|
|
66
|
+
'anthropic': AnthropicMethod,
|
|
67
|
+
'gemini': GeminiMethod,
|
|
68
|
+
'local': LocalMethod,
|
|
69
|
+
'external': ExternalMethod,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Find a shared-registry entry that exists but is NOT served in the CLI runtime.
|
|
74
|
+
*
|
|
75
|
+
* An entry is harness-only when it declares `runtimes` and that list excludes
|
|
76
|
+
* 'cli' (e.g. amazon-translate, local-model). Matches against both the
|
|
77
|
+
* canonical manifest name and the CLI alias (cli_name). Returns null when the
|
|
78
|
+
* manifest is absent (standalone package) or the name is genuinely unknown.
|
|
79
|
+
*
|
|
80
|
+
* @param {string} methodName
|
|
81
|
+
* @returns {{ name: string, entry: object } | null}
|
|
82
|
+
*/
|
|
83
|
+
function findHarnessOnlyEntry(methodName) {
|
|
84
|
+
for (const [name, entry] of Object.entries(manifestEntries())) {
|
|
85
|
+
if (name !== methodName && cliNameFor(name, entry) !== methodName) continue;
|
|
86
|
+
if (Array.isArray(entry.runtimes) && !entry.runtimes.includes('cli')) {
|
|
87
|
+
return { name, entry };
|
|
88
|
+
}
|
|
89
|
+
// Found a matching entry that DOES run in the CLI — not the harness-only case.
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Get a TranslationMethod instance for the given method name.
|
|
97
|
+
*
|
|
98
|
+
* **This is the routing-time licence gate.** Every translation in the CLI is
|
|
99
|
+
* dispatched through here, so it is the one place that can refuse a route.
|
|
100
|
+
* When the caller is operating a COMMERCIAL lane — a paid routing API, a
|
|
101
|
+
* billed `champollion serve` deployment — a method whose licence does not
|
|
102
|
+
* permit commercial use is refused before it can be instantiated, rather
|
|
103
|
+
* than warned about after the fact (lib/provenance.js reports; this
|
|
104
|
+
* enforces). The default lane is non-commercial and is never gated: NC and
|
|
105
|
+
* copyleft engines are legitimate there, and that is the open project.
|
|
106
|
+
*
|
|
107
|
+
* The lane comes from the pair config (`useContext`), so it travels with the
|
|
108
|
+
* route rather than being global state.
|
|
109
|
+
*
|
|
110
|
+
* @param {string} methodName - Method name from pair config
|
|
111
|
+
* @param {import('./types.js').PairConfig} [pluginContext] - Plugin context for API methods
|
|
112
|
+
* @returns {import('./methods/base.js').TranslationMethod} Method instance
|
|
113
|
+
* @throws {CommercialRouteBlockedError} when the lane is commercial and the
|
|
114
|
+
* method is not cleared for it
|
|
115
|
+
*/
|
|
116
|
+
function getMethod(methodName, pluginContext) {
|
|
117
|
+
// Gate FIRST: an ineligible method must be refused on licence grounds, not
|
|
118
|
+
// on "unknown method", and must never reach construction.
|
|
119
|
+
assertRoutable(methodName, {
|
|
120
|
+
useContext: pluginContext?.useContext,
|
|
121
|
+
pluginProvenance: pluginContext?.pluginProvenance,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const MethodClass = METHOD_REGISTRY[methodName];
|
|
125
|
+
if (!MethodClass) {
|
|
126
|
+
// Before declaring it unknown (which reads like a typo), check whether this
|
|
127
|
+
// is a real, documented engine that simply has no CLI adapter yet. Engines
|
|
128
|
+
// such as `amazon-translate` and `local-model` are declared harness-only in
|
|
129
|
+
// the shared method registry (runtimes: ["harness"]). Surfacing them as
|
|
130
|
+
// "Unknown method" misleads the user into thinking they misspelled it.
|
|
131
|
+
const harnessOnly = findHarnessOnlyEntry(methodName);
|
|
132
|
+
if (harnessOnly) {
|
|
133
|
+
throw new Error(
|
|
134
|
+
`Translation engine "${methodName}" is harness-only — it has no CLI ` +
|
|
135
|
+
`adapter yet, so \`champollion\` cannot run it. Run it with the ` +
|
|
136
|
+
`evaluation harness instead:\n` +
|
|
137
|
+
` mt-eval run --method ${methodName}`
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
const known = Object.keys(METHOD_REGISTRY).join(', ');
|
|
141
|
+
throw new Error(
|
|
142
|
+
`Unknown translation method "${methodName}". ` +
|
|
143
|
+
`Available methods: ${known}. ` +
|
|
144
|
+
`Check your champollion.config.json pairs configuration.`
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// APIMethod needs plugin context (endpoint, provenance, quality tier)
|
|
149
|
+
if (methodName === 'api' && pluginContext) {
|
|
150
|
+
return new MethodClass({
|
|
151
|
+
endpoint: pluginContext.endpoint,
|
|
152
|
+
methodName: pluginContext.pluginName,
|
|
153
|
+
methodVersion: pluginContext.pluginVersion,
|
|
154
|
+
qualityTier: pluginContext.qualityTier,
|
|
155
|
+
provenance: pluginContext.pluginProvenance,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ExternalMethod needs methodPath to find the Python plugin directory
|
|
160
|
+
if (methodName === 'external') {
|
|
161
|
+
return new MethodClass({
|
|
162
|
+
methodPath: pluginContext?.methodPath || null,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return new MethodClass();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Translate a batch of key-value pairs using the method specified in the pair config.
|
|
171
|
+
*
|
|
172
|
+
* @param {string[]} keys - Flat dot-notation keys to translate
|
|
173
|
+
* @param {object} sourceFlat - Full flattened source locale
|
|
174
|
+
* @param {import('./types.js').PairConfig} pairConfig - Pair config with method, model, register, etc.
|
|
175
|
+
* @param {object} options - { apiKey, model, batchSize }
|
|
176
|
+
* @returns {object|null} Map of key → translated value, or null
|
|
177
|
+
*/
|
|
178
|
+
async function translateBatch(keys, sourceFlat, pairConfig, options) {
|
|
179
|
+
const method = getMethod(pairConfig.method || 'llm', pairConfig);
|
|
180
|
+
return method.translate(keys, sourceFlat, pairConfig, options);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Translate freeform text content (e.g., Markdown body).
|
|
185
|
+
*
|
|
186
|
+
* @param {string} prompt - Complete translation prompt
|
|
187
|
+
* @param {object} options - { apiKey, model } or { apiKey, pairConfig }
|
|
188
|
+
* @returns {string|null} Translated text, or null on failure
|
|
189
|
+
*/
|
|
190
|
+
async function translateRawContent(prompt, options) {
|
|
191
|
+
const pairConfig = options.pairConfig || {
|
|
192
|
+
model: options.model || DEFAULT_OPENROUTER_MODEL,
|
|
193
|
+
method: 'llm',
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
// Pass the pair config as plugin context so methods that need it
|
|
197
|
+
// (api → endpoint, external → methodPath) are constructed the same
|
|
198
|
+
// way here as on the key-value path in translateBatch.
|
|
199
|
+
const method = getMethod(pairConfig.method, pairConfig);
|
|
200
|
+
return method.translateContent(prompt, pairConfig, options);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export { translateBatch, translateRawContent, buildPrompt, isUnsafeKey, inferKeyTypes, getMethod, METHOD_REGISTRY };
|
package/lib/types.js
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared type definitions for champollion.
|
|
3
|
+
*
|
|
4
|
+
* This file contains JSDoc @typedef declarations for the core data shapes
|
|
5
|
+
* that flow between modules. It has no runtime code — it exists only so
|
|
6
|
+
* that editors can resolve type references via imports.
|
|
7
|
+
*
|
|
8
|
+
* USAGE IN OTHER MODULES:
|
|
9
|
+
* /** @typedef {import('./types.js').PairConfig} PairConfig * /
|
|
10
|
+
*
|
|
11
|
+
* Or, for VS Code auto-resolution without explicit import, just reference
|
|
12
|
+
* the type name — jsconfig.json's include paths make these globally visible.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
// -----------------------------------------------------------------
|
|
16
|
+
// ChampollionConfig — the fully resolved project configuration
|
|
17
|
+
// Produced by: config.js:resolveConfig()
|
|
18
|
+
// Consumed by: sync.js, commands/*, seo.js, lint.js, pairs.js
|
|
19
|
+
// -----------------------------------------------------------------
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @typedef {object} ChampollionConfig
|
|
23
|
+
* @property {number} version - Config schema version (currently 3)
|
|
24
|
+
* @property {string} inputLocale - Source locale code (e.g., 'en')
|
|
25
|
+
* @property {string} baseUrl - Site base URL for SEO commands
|
|
26
|
+
* @property {string} localesDir - Absolute path to locale files directory
|
|
27
|
+
* @property {string|null} contentDir - Hugo/Docusaurus content directory, or null if disabled
|
|
28
|
+
* @property {string[]|null} translatableFields - Override for content translatable fields, or null for defaults
|
|
29
|
+
* @property {Array<string>|object} languages - Target languages (array of codes or object with config)
|
|
30
|
+
* @property {Object<string, LanguageConfig>} [resolvedLanguages] - Fully resolved language map (code → config). Set by resolveConfig.
|
|
31
|
+
* @property {string[]} noTranslate - Dot-path keys / glob patterns whose value is copied verbatim to every locale (e.g. ['**.url']). Never sent to a backend, gated, or billed.
|
|
32
|
+
* @property {boolean} noTranslateUrls - Auto-treat source values that are bare `scheme://` URLs as no-translate (default true)
|
|
33
|
+
* @property {object|null} pairs - Advanced per-pair overrides, or null
|
|
34
|
+
* @property {string} model - Default translation model (e.g., 'openai/gpt-4o-mini')
|
|
35
|
+
* @property {string} defaultMethod - Global default method: 'llm', 'google-translate', 'api'
|
|
36
|
+
* @property {number} batchSize - Default batch size for translation calls
|
|
37
|
+
* @property {string} fallbackPrefix - Prefix for untranslated fallback values (default: '[EN] ')
|
|
38
|
+
* @property {string} apiKeyEnvVar - Environment variable name for the API key
|
|
39
|
+
* @property {string} format - Locale file format: 'json', 'toml', 'yaml', 'auto', 'docusaurus'
|
|
40
|
+
* @property {string[]} [forceKeys] - Dot-notation keys to force re-translate (from --force-keys). Set by resolveConfig.
|
|
41
|
+
* @property {{ srcDir: string|null, ignore: string[], minLength: number }} lint - Lint config
|
|
42
|
+
* @property {{ urlPattern: string, pages: string[]|null }} seo - SEO config
|
|
43
|
+
* @property {{ output: string|null, autoGenerate: boolean }} typegen - Type generation config
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
// -----------------------------------------------------------------
|
|
47
|
+
// PairConfig — a single source→target translation pair
|
|
48
|
+
// Produced by: pairs.js:resolvePairs(), enriched by plugins.js
|
|
49
|
+
// Consumed by: translate.js, methods/*.js, sync.js, provenance.js
|
|
50
|
+
// -----------------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @typedef {object} PairConfig
|
|
54
|
+
* @property {string} source - Source locale code (e.g., 'en')
|
|
55
|
+
* @property {string} target - Target locale code (e.g., 'fr')
|
|
56
|
+
* @property {string} method - Translation method name: 'llm', 'llm-coached', 'google-translate', 'api'
|
|
57
|
+
* @property {string} model - Model identifier (e.g., 'openai/gpt-4o-mini')
|
|
58
|
+
* @property {string} qualityTier - Quality tier: 'standard', 'high', 'research', 'verified'
|
|
59
|
+
* @property {number} batchSize - Max keys per API call
|
|
60
|
+
* @property {number} maxRetries - Max cascade retries on parse failure
|
|
61
|
+
* @property {string} register - Translation register / style instruction (resolved prompt text)
|
|
62
|
+
* @property {string|null} registerPreset - Preset key name (e.g., 'casual-tu') for metadata lookups, or null for custom text
|
|
63
|
+
* @property {string} name - Human-readable language name (e.g., 'French')
|
|
64
|
+
* @property {string} dir - Text direction: 'ltr' or 'rtl'
|
|
65
|
+
* @property {string|null} scripts - LEGACY: the card's scriptConverter key (misleading name, kept for JSON-output compat)
|
|
66
|
+
* @property {string|null} script - User's `script:` config value (ISO 15924), or null
|
|
67
|
+
* @property {object|null} scriptFallback - User transliteration rules for unmapped letters ({ sequence: replacement }), or null
|
|
68
|
+
* @property {{script: string|null, source: string, converterKey: string|null, choices?: Array}} [scriptResolution] - Resolved script decision (set by resolvePairs; see lib/scripts.js resolveTargetScript)
|
|
69
|
+
* @property {string|null} methodPlugin - Plugin name reference, or null
|
|
70
|
+
* @property {string|null} formalitySystem - Formality system name (e.g., 'T-V', 'speech-levels'). Used by DeepL for structured formality mapping.
|
|
71
|
+
* @property {string|null} genderGuidance - Language-specific gender guidance for LLM prompts, or null
|
|
72
|
+
* @property {Set<string>} _defaults - Fields that were filled from system defaults (used by plugin precedence)
|
|
73
|
+
*
|
|
74
|
+
* --- Plugin-injected fields (present after resolvePluginForPair) ---
|
|
75
|
+
* @property {string} [endpoint] - Plugin API endpoint URL
|
|
76
|
+
* @property {string} [pluginName] - Installed plugin name
|
|
77
|
+
* @property {string} [pluginVersion] - Plugin version string
|
|
78
|
+
* @property {string} [pluginDir] - Absolute path to plugin directory
|
|
79
|
+
* @property {object|null} [pluginBenchmarks] - Plugin quality benchmarks, or null
|
|
80
|
+
* @property {object|null} [pluginProvenance] - Plugin provenance/licensing info, or null
|
|
81
|
+
*
|
|
82
|
+
* --- Validation thresholds (used by validate.js quality gate) ---
|
|
83
|
+
* @property {number} [maxLengthRatio] - Max target/source length ratio for validation
|
|
84
|
+
* @property {number} [minLengthRatio] - Min target/source length ratio for validation
|
|
85
|
+
* @property {number} [maxRepetitionRate] - Max character repetition rate threshold
|
|
86
|
+
* @property {boolean} [requireNonLatin] - Whether to require non-Latin script in output
|
|
87
|
+
*/
|
|
88
|
+
|
|
89
|
+
// -----------------------------------------------------------------
|
|
90
|
+
// LanguageConfig — per-language settings from the user's config
|
|
91
|
+
// Produced by: config.js (from user's `languages` object)
|
|
92
|
+
// Consumed by: pairs.js:resolvePairs()
|
|
93
|
+
// -----------------------------------------------------------------
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* @typedef {object} LanguageConfig
|
|
97
|
+
* @property {string} name - Human-readable language name
|
|
98
|
+
* @property {string} register - Translation register / style instruction (resolved prompt text)
|
|
99
|
+
* @property {string|null} [registerPreset] - Preset key name for metadata lookups (e.g., DeepL formality), or null for custom text
|
|
100
|
+
* @property {string} [dir] - Text direction: 'ltr' or 'rtl'
|
|
101
|
+
* @property {string|null} [formalitySystem] - Formality system name (e.g., 'T-V', 'speech-levels', 'keigo')
|
|
102
|
+
* @property {string} [model] - Per-language model override
|
|
103
|
+
* @property {number} [batchSize] - Per-language batch size override
|
|
104
|
+
* @property {number} [maxRetries] - Per-language max retries override
|
|
105
|
+
* @property {string} [script] - Script system override (e.g., 'syllabics' for Cree)
|
|
106
|
+
*/
|
|
107
|
+
|
|
108
|
+
// -----------------------------------------------------------------
|
|
109
|
+
// LanguageCard — unified language metadata from language-cards/*.json
|
|
110
|
+
// Produced by: registers.js:getLanguageCard()
|
|
111
|
+
// Consumed by: config.js, pairs.js, init.js, status.js, deepl.js
|
|
112
|
+
//
|
|
113
|
+
// NOTE (v6 Unified Architecture):
|
|
114
|
+
// All language data lives in a single card file. The former two-tier
|
|
115
|
+
// split (v5) is eliminated. getLanguageReference() is a backward-
|
|
116
|
+
// compat alias for getLanguageCard().
|
|
117
|
+
// -----------------------------------------------------------------
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* @typedef {object} LanguageCard
|
|
121
|
+
* @property {string} code - BCP 47 locale code (e.g., 'fr', 'ko', 'x-pirate')
|
|
122
|
+
* @property {string} name - English language name
|
|
123
|
+
* @property {string} nativeName - Name in the language itself (e.g., 'Français', '한국어')
|
|
124
|
+
* @property {string} [iso639_1] - ISO 639-1 two-letter code, or null
|
|
125
|
+
* @property {string} iso639_3 - ISO 639-3 three-letter code
|
|
126
|
+
* @property {string} bcp47 - BCP 47 tag
|
|
127
|
+
* @property {string} script - ISO 15924 script code (e.g., 'Latn', 'Kore', 'Arab')
|
|
128
|
+
* @property {'ltr'|'rtl'} dir - Text directionality
|
|
129
|
+
* @property {LanguageFormality|null} formality - Formality system metadata, or null if no system
|
|
130
|
+
* @property {{ grammatical: boolean, inclusiveGuidance: string|null }} gender - Gender metadata
|
|
131
|
+
* @property {Object<string, RegisterPreset>} registers - Named register presets
|
|
132
|
+
* @property {string[]} aliases - Alternative locale codes that resolve to this card
|
|
133
|
+
* @property {{ googleTranslate: {supported: boolean}, deepl: {supported: boolean, formality?: boolean}, microsoftTranslator: {supported: boolean}, libreTranslate: {supported: boolean}, nllb: {supported: boolean, code?: string}, llm: {supported: boolean} }} methodSupport - API method support — each entry is an object with 'supported' boolean plus optional metadata
|
|
134
|
+
* @property {string|null} scriptConverter - Script converter reference (e.g., 'serbian-latin-cyrillic')
|
|
135
|
+
* @property {string[]} evalDatasets - Dataset IDs for the companion eval harness (not consumed by champollion at runtime)
|
|
136
|
+
* @property {string|null} notes - Free-text notes
|
|
137
|
+
* @property {object|null} rules - Typography, plurals, capitalization, and variable rules (used by compliance plugin)
|
|
138
|
+
* @property {{ reviewed: boolean, reviewer?: string, date?: string }|null} [humanReviewed] - Review status
|
|
139
|
+
* @property {string|null} [glottocode] - Glottolog identifier for family tree cross-referencing
|
|
140
|
+
* @property {string|null} [extends] - Parent card code for inheritance
|
|
141
|
+
* @property {string|null} [macrolanguage] - ISO 639-3 macrolanguage umbrella code (e.g., 'cre', 'ara', 'zho')
|
|
142
|
+
* @property {{ family: string, familyGlottocode: string, genus: string, genusGlottocode: string, ancestry: string[] }|null} [classification] - Genealogical classification from Glottolog + WALS
|
|
143
|
+
* @property {Array<{ source: string, sourceIso639_3?: string, type: string, domains?: string[], depth: string, period?: string, notes?: string }>|null} [contactInfluences] - Universal contact history
|
|
144
|
+
* @property {Array<{ code: string, name: string, primary: boolean }>|null} [scripts] - ISO 15924 script tracking
|
|
145
|
+
* @property {string[]|null} [dataSources] - Provenance tracking (e.g., ['glottolog-5.3', 'cldr-48'])
|
|
146
|
+
* @property {object|null} [linguisticChallenges] - MT-relevant linguistic challenges
|
|
147
|
+
* @property {object|null} [encyclopedic] - Language family, demographics, dialect info
|
|
148
|
+
* @property {object|null} [resources] - NLP corpora, models, FSTs
|
|
149
|
+
* @property {Array<{ country: string, countryCode: string, officialStatus?: string, region?: string, speakerEstimate?: string, coordinates?: [number, number] }>|null} [regions] - Geographic regions where this language is actively spoken
|
|
150
|
+
* @property {{ text: string, transliteration?: string, translation: string, literal?: string, source?: string }|null} [culturalAphorism] - Iconic proverb encapsulating the community's worldview
|
|
151
|
+
*/
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* @typedef {object} LanguageFormality
|
|
155
|
+
* @property {string} system - Formality system name (e.g., 'T-V', 'speech-levels', 'keigo', 'particles-and-pronouns')
|
|
156
|
+
* @property {string} description - Human-readable description of how formality works in this language
|
|
157
|
+
* @property {string} default - Key of the default register preset
|
|
158
|
+
*/
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* @typedef {object} RegisterPreset
|
|
162
|
+
* @property {string} label - Display label (e.g., 'Vouvoiement (Formal)', '해요체 (Polite)')
|
|
163
|
+
* @property {string} description - Short description of when to use this preset
|
|
164
|
+
* @property {string} prompt - Full register prompt text injected into LLM prompts
|
|
165
|
+
* @property {string} [deeplFormality] - DeepL API formality value: 'prefer_more', 'prefer_less', or 'default'. Only present on presets for languages where methodSupport.deepl.formality is true.
|
|
166
|
+
*/
|
|
167
|
+
|
|
168
|
+
// -----------------------------------------------------------------
|
|
169
|
+
// DiffResult — output of comparing source vs target locale
|
|
170
|
+
// Produced by: diff.js:diffLocale()
|
|
171
|
+
// Consumed by: sync.js
|
|
172
|
+
// -----------------------------------------------------------------
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* @typedef {object} DiffResult
|
|
176
|
+
* @property {string[]} missing - Keys in source but not in target
|
|
177
|
+
* @property {string[]} needsTranslation - Keys with [EN] fallback prefix in target
|
|
178
|
+
* @property {string[]} changed - Keys whose source content hash changed since last sync
|
|
179
|
+
* @property {string[]} forced - Keys explicitly requested for re-translation
|
|
180
|
+
* @property {string[]} noTranslate - Exempt keys (config `noTranslate` / auto-detected URL) whose target is not byte-identical to the source and must be copied verbatim. Never translated, gated, or billed.
|
|
181
|
+
* @property {string[]} extra - Keys in target but not in source (stale/orphaned)
|
|
182
|
+
* @property {string[]} toProcess - Deduplicated union of missing + needsTranslation + changed + forced, minus every exempt key
|
|
183
|
+
*/
|
|
184
|
+
|
|
185
|
+
// -----------------------------------------------------------------
|
|
186
|
+
// CoachingData — linguistic coaching hints for LLM-coached method
|
|
187
|
+
// Produced by: llm-coached.js:_loadCoachingData()
|
|
188
|
+
// Consumed by: llm-coached.js:buildCoachedSystemMessage/buildCoachedPrompt
|
|
189
|
+
// -----------------------------------------------------------------
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* @typedef {object} CoachingData
|
|
193
|
+
* @property {string[]} [grammar_rules] - Grammar rules (e.g., "French adjectives agree in gender/number")
|
|
194
|
+
* @property {Object<string, string>} [dictionary] - Term overrides (e.g., "dashboard" → "tableau de bord")
|
|
195
|
+
* @property {string} [style_notes] - Style guidance (e.g., "Prefer active voice. Avoid anglicisms.")
|
|
196
|
+
*/
|
|
197
|
+
|
|
198
|
+
// -----------------------------------------------------------------
|
|
199
|
+
// CLI args shape — parsed CLI arguments passed to command modules
|
|
200
|
+
// Produced by: bin/cli.js (util.parseArgs)
|
|
201
|
+
// Consumed by: lib/commands/*.js
|
|
202
|
+
// -----------------------------------------------------------------
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* @typedef {object} CLIArgs
|
|
206
|
+
* @property {string[]} [_] - Positional arguments (command, subcommand, etc.)
|
|
207
|
+
* @property {boolean} [dry] - Preview changes without writing files
|
|
208
|
+
* @property {boolean} [help] - Show help
|
|
209
|
+
* @property {boolean} [version] - Show version
|
|
210
|
+
* @property {boolean} [fallback] - Write [EN]-prefixed placeholders
|
|
211
|
+
* @property {boolean} [yes] - Skip interactive prompts
|
|
212
|
+
* @property {boolean} [undo] - Restore from backup (wrap command)
|
|
213
|
+
* @property {string} [config] - Custom config file path
|
|
214
|
+
* @property {string} [dir] - Override locales directory
|
|
215
|
+
* @property {string} [source] - Override source locale
|
|
216
|
+
* @property {string} [model] - Override translation model
|
|
217
|
+
* @property {string} [method] - Override translation method
|
|
218
|
+
* @property {string} [format] - Locale file format override
|
|
219
|
+
* @property {string} [out] - Output file path (seo sitemap)
|
|
220
|
+
* @property {string} [src] - Source directory for lint/wrap
|
|
221
|
+
* @property {string} ['content-dir'] - Hugo content directory
|
|
222
|
+
* @property {string} ['base-url'] - Site base URL override
|
|
223
|
+
* @property {string} ['min-length'] - Minimum string length to flag
|
|
224
|
+
* @property {string} ['force-keys'] - Comma-separated keys to force re-translate
|
|
225
|
+
* @property {boolean} ['warn-only'] - Exit 0 even with issues
|
|
226
|
+
*/
|
|
227
|
+
|
|
228
|
+
// Empty export so modules can import types via:
|
|
229
|
+
// /** @typedef {import('./types.js').PairConfig} PairConfig */
|
|
230
|
+
export {};
|