solid-translate 1.4.1 → 1.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -0
- package/dist/chunk-P6RTZPHZ.js +141 -0
- package/dist/cli.js +179 -19
- package/dist/index.d.ts +7 -0
- package/dist/{translate-M737VQHG.js → translate-SWLCBQ5Y.js} +5 -1
- package/dist/vite.d.ts +7 -0
- package/dist/vite.js +253 -37
- package/dist/vite.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-2BKJUY37.js +0 -82
package/README.md
CHANGED
|
@@ -350,9 +350,29 @@ solidTranslate({
|
|
|
350
350
|
batchSize: 50, // Keys per API call (default: 50)
|
|
351
351
|
autoExtract: true, // Auto-extract <T> and msg() strings (default: false)
|
|
352
352
|
include: ["src/**/*.tsx"], // Files to scan for extraction
|
|
353
|
+
extractImportSources: ["@/i18n"], // Extra module specifiers whose msg/<T> imports count as markers (optional)
|
|
353
354
|
})
|
|
354
355
|
```
|
|
355
356
|
|
|
357
|
+
### What extraction considers a marker
|
|
358
|
+
|
|
359
|
+
Extraction only honors `msg()` calls and `<T>`/`<Plural>` elements whose
|
|
360
|
+
identifier actually refers to solid-translate:
|
|
361
|
+
|
|
362
|
+
- Imported bindings must come from `"solid-translate"` or an accepted
|
|
363
|
+
re-export wrapper. By default any specifier whose final path segment is
|
|
364
|
+
`solid-translate` or `i18n` (e.g. `@/i18n`, `../lib/i18n`) is accepted;
|
|
365
|
+
set `extractImportSources` (plugin config) / `"extractImportSources"`
|
|
366
|
+
(CLI config) to an explicit list to override the `i18n` heuristic
|
|
367
|
+
(`"solid-translate"` itself is always accepted).
|
|
368
|
+
- Aliased imports work: `import { msg as m } from "solid-translate"`
|
|
369
|
+
extracts `m("...")`.
|
|
370
|
+
- Locally bound identifiers are never extracted — a callback parameter
|
|
371
|
+
named `msg`, a local `const msg = ...`, or a local component named `T`
|
|
372
|
+
will not pollute the catalog or emit warnings.
|
|
373
|
+
- Identifiers with no binding at all are still treated as markers by name,
|
|
374
|
+
so snippet-style sources keep working.
|
|
375
|
+
|
|
356
376
|
## CLI
|
|
357
377
|
|
|
358
378
|
For translating locale files, JSON, Markdown, and MDX outside of the Vite build.
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/translate.ts
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
async function loadGenerateObject() {
|
|
6
|
+
const { generateObject } = await import("ai");
|
|
7
|
+
return generateObject;
|
|
8
|
+
}
|
|
9
|
+
async function loadGenerateText() {
|
|
10
|
+
const { generateText } = await import("ai");
|
|
11
|
+
return generateText;
|
|
12
|
+
}
|
|
13
|
+
function extractJsonObject(text) {
|
|
14
|
+
const start = text.indexOf("{");
|
|
15
|
+
const end = text.lastIndexOf("}");
|
|
16
|
+
if (start === -1 || end <= start) {
|
|
17
|
+
throw new Error("model response contained no JSON object");
|
|
18
|
+
}
|
|
19
|
+
const parsed = JSON.parse(text.slice(start, end + 1));
|
|
20
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
21
|
+
throw new Error("model response was not a JSON object");
|
|
22
|
+
}
|
|
23
|
+
return parsed;
|
|
24
|
+
}
|
|
25
|
+
function collectBatchTranslations(parsed, requestedKeys) {
|
|
26
|
+
let dict = parsed;
|
|
27
|
+
const inner = parsed["translations"];
|
|
28
|
+
if (typeof inner === "object" && inner !== null && !Array.isArray(inner) && // Only unwrap when the envelope key is not itself a requested key
|
|
29
|
+
!requestedKeys.includes("translations")) {
|
|
30
|
+
dict = inner;
|
|
31
|
+
}
|
|
32
|
+
const translations = {};
|
|
33
|
+
const missing = [];
|
|
34
|
+
for (const key of requestedKeys) {
|
|
35
|
+
const value = dict[key];
|
|
36
|
+
if (typeof value === "string" && value.length > 0) {
|
|
37
|
+
translations[key] = value;
|
|
38
|
+
} else {
|
|
39
|
+
missing.push(key);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return { translations, missing };
|
|
43
|
+
}
|
|
44
|
+
async function translateBatch(model, entries, targetLocale, sourceLocale, systemPrompt, contexts) {
|
|
45
|
+
const keys = Object.keys(entries);
|
|
46
|
+
if (keys.length === 0) return {};
|
|
47
|
+
const defaultSystem = [
|
|
48
|
+
`You are a professional translator specializing in software localization.`,
|
|
49
|
+
`Translate text from "${sourceLocale}" to "${targetLocale}".`,
|
|
50
|
+
`Rules:`,
|
|
51
|
+
`- Preserve the original tone and meaning`,
|
|
52
|
+
`- Keep placeholders like {{variable}}, {variable}, {0}, {1} unchanged`,
|
|
53
|
+
`- Keep HTML tags unchanged`,
|
|
54
|
+
`- Do not add or remove content`,
|
|
55
|
+
`- Return natural, idiomatic translations`
|
|
56
|
+
].join("\n");
|
|
57
|
+
let contextSection = "";
|
|
58
|
+
if (contexts && Object.keys(contexts).length > 0) {
|
|
59
|
+
const contextLines = Object.entries(contexts).filter(([key]) => key in entries).map(([key, ctx]) => ` "${key}": ${ctx}`);
|
|
60
|
+
if (contextLines.length > 0) {
|
|
61
|
+
contextSection = [
|
|
62
|
+
``,
|
|
63
|
+
`Context hints for disambiguation:`,
|
|
64
|
+
...contextLines,
|
|
65
|
+
``
|
|
66
|
+
].join("\n");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const generateText = await loadGenerateText();
|
|
70
|
+
const basePrompt = [
|
|
71
|
+
`Translate each value in this JSON object from "${sourceLocale}" to "${targetLocale}".`,
|
|
72
|
+
`Respond with ONLY a JSON object \u2014 no prose, no code fences \u2014 containing the exact same keys and the translated values.`,
|
|
73
|
+
contextSection,
|
|
74
|
+
JSON.stringify(entries, null, 2)
|
|
75
|
+
].join("\n");
|
|
76
|
+
const attempt = async (prompt) => {
|
|
77
|
+
const { text } = await generateText({
|
|
78
|
+
model,
|
|
79
|
+
system: systemPrompt || defaultSystem,
|
|
80
|
+
prompt
|
|
81
|
+
});
|
|
82
|
+
return collectBatchTranslations(extractJsonObject(text), keys);
|
|
83
|
+
};
|
|
84
|
+
let { translations, missing } = await attempt(basePrompt);
|
|
85
|
+
if (missing.length > 0) {
|
|
86
|
+
const retryEntries = {};
|
|
87
|
+
for (const key of missing) retryEntries[key] = entries[key];
|
|
88
|
+
const retry = await attempt(
|
|
89
|
+
[
|
|
90
|
+
`Translate each value in this JSON object from "${sourceLocale}" to "${targetLocale}".`,
|
|
91
|
+
`Respond with ONLY a JSON object \u2014 no prose, no code fences \u2014 containing the exact same keys and the translated values.`,
|
|
92
|
+
contextSection,
|
|
93
|
+
JSON.stringify(retryEntries, null, 2)
|
|
94
|
+
].join("\n")
|
|
95
|
+
);
|
|
96
|
+
translations = { ...translations, ...retry.translations };
|
|
97
|
+
missing = retry.missing;
|
|
98
|
+
}
|
|
99
|
+
if (missing.length > 0) {
|
|
100
|
+
const sample = missing.slice(0, 3).join('", "');
|
|
101
|
+
throw new Error(
|
|
102
|
+
`model returned no translation for ${missing.length} of ${keys.length} keys (e.g. "${sample}")`
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
return translations;
|
|
106
|
+
}
|
|
107
|
+
async function translateMarkdown(model, content, targetLocale, sourceLocale, systemPrompt) {
|
|
108
|
+
const defaultSystem = [
|
|
109
|
+
`You are a professional translator specializing in documentation.`,
|
|
110
|
+
`Translate Markdown/MDX content from "${sourceLocale}" to "${targetLocale}".`,
|
|
111
|
+
`Rules:`,
|
|
112
|
+
`- Preserve all Markdown formatting (headers, lists, bold, italic, links, etc.)`,
|
|
113
|
+
`- Preserve code blocks and inline code unchanged`,
|
|
114
|
+
`- Preserve frontmatter YAML keys (only translate values)`,
|
|
115
|
+
`- Preserve MDX component syntax and JSX expressions`,
|
|
116
|
+
`- Preserve URLs and file paths unchanged`,
|
|
117
|
+
`- Return natural, idiomatic translations`
|
|
118
|
+
].join("\n");
|
|
119
|
+
const generateObject = await loadGenerateObject();
|
|
120
|
+
const { object } = await generateObject({
|
|
121
|
+
model,
|
|
122
|
+
schema: z.object({
|
|
123
|
+
translated: z.string()
|
|
124
|
+
}),
|
|
125
|
+
system: systemPrompt || defaultSystem,
|
|
126
|
+
prompt: [
|
|
127
|
+
`Translate this Markdown/MDX content from "${sourceLocale}" to "${targetLocale}".`,
|
|
128
|
+
`Return the complete translated document.`,
|
|
129
|
+
``,
|
|
130
|
+
content
|
|
131
|
+
].join("\n")
|
|
132
|
+
});
|
|
133
|
+
return object.translated;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export {
|
|
137
|
+
extractJsonObject,
|
|
138
|
+
collectBatchTranslations,
|
|
139
|
+
translateBatch,
|
|
140
|
+
translateMarkdown
|
|
141
|
+
};
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
translateBatch
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-P6RTZPHZ.js";
|
|
5
5
|
import {
|
|
6
6
|
__commonJS,
|
|
7
7
|
__toESM
|
|
@@ -14599,7 +14599,7 @@ function hashContent(content) {
|
|
|
14599
14599
|
|
|
14600
14600
|
// src/extract.ts
|
|
14601
14601
|
var import_parser = __toESM(require_lib(), 1);
|
|
14602
|
-
function extractStringsFromSource(code, filePath, warnings) {
|
|
14602
|
+
function extractStringsFromSource(code, filePath, warnings, options) {
|
|
14603
14603
|
const results = [];
|
|
14604
14604
|
const seen = /* @__PURE__ */ new Set();
|
|
14605
14605
|
const warn = (line, message) => {
|
|
@@ -14621,26 +14621,158 @@ function extractStringsFromSource(code, filePath, warnings) {
|
|
|
14621
14621
|
seen.add(entry.key);
|
|
14622
14622
|
results.push(entry);
|
|
14623
14623
|
};
|
|
14624
|
+
const bindings = collectModuleBindings(ast);
|
|
14625
|
+
const shadowStack = [];
|
|
14626
|
+
const resolveMarker = (localName) => {
|
|
14627
|
+
for (let i = shadowStack.length - 1; i >= 0; i--) {
|
|
14628
|
+
if (shadowStack[i].has(localName)) return null;
|
|
14629
|
+
}
|
|
14630
|
+
const imported = bindings.imports.get(localName);
|
|
14631
|
+
if (imported) {
|
|
14632
|
+
if (!isAcceptedImportSource(imported.source, options?.importSources)) {
|
|
14633
|
+
return null;
|
|
14634
|
+
}
|
|
14635
|
+
return MARKER_NAMES.has(imported.imported) ? imported.imported : null;
|
|
14636
|
+
}
|
|
14637
|
+
if (bindings.moduleLocals.has(localName)) return null;
|
|
14638
|
+
return MARKER_NAMES.has(localName) ? localName : null;
|
|
14639
|
+
};
|
|
14624
14640
|
const visit = (node) => {
|
|
14641
|
+
const scopeBindings = collectScopeBindings(node);
|
|
14642
|
+
if (scopeBindings) shadowStack.push(scopeBindings);
|
|
14625
14643
|
if (node.type === "JSXElement") {
|
|
14626
14644
|
const name = jsxName(node);
|
|
14627
|
-
|
|
14645
|
+
const marker = name ? resolveMarker(name) : null;
|
|
14646
|
+
if (marker === "T") {
|
|
14628
14647
|
const entry = processT(node, filePath, warn);
|
|
14629
14648
|
if (entry) push(entry);
|
|
14630
|
-
} else if (
|
|
14649
|
+
} else if (marker === "Plural") {
|
|
14631
14650
|
for (const entry of processPlural(node, filePath, warn)) {
|
|
14632
14651
|
push(entry);
|
|
14633
14652
|
}
|
|
14634
14653
|
}
|
|
14635
|
-
} else if (node.type === "CallExpression" && node.callee?.type === "Identifier" && node.callee.name === "msg") {
|
|
14654
|
+
} else if (node.type === "CallExpression" && node.callee?.type === "Identifier" && resolveMarker(node.callee.name) === "msg") {
|
|
14636
14655
|
const entry = processMsg(node, filePath, warn);
|
|
14637
14656
|
if (entry) push(entry);
|
|
14638
14657
|
}
|
|
14639
14658
|
walkChildren(node, visit);
|
|
14659
|
+
if (scopeBindings) shadowStack.pop();
|
|
14640
14660
|
};
|
|
14641
14661
|
visit(ast);
|
|
14642
14662
|
return results;
|
|
14643
14663
|
}
|
|
14664
|
+
var MARKER_NAMES = /* @__PURE__ */ new Set([
|
|
14665
|
+
"msg",
|
|
14666
|
+
"T",
|
|
14667
|
+
"Var",
|
|
14668
|
+
"Num",
|
|
14669
|
+
"Currency",
|
|
14670
|
+
"DateTime",
|
|
14671
|
+
"Plural"
|
|
14672
|
+
]);
|
|
14673
|
+
var DEFAULT_IMPORT_SOURCE_RE = /(^|\/)(solid-translate|i18n)(\.[cm]?[jt]sx?)?$/;
|
|
14674
|
+
function isAcceptedImportSource(source, importSources) {
|
|
14675
|
+
if (source === "solid-translate") return true;
|
|
14676
|
+
if (importSources) return importSources.includes(source);
|
|
14677
|
+
return DEFAULT_IMPORT_SOURCE_RE.test(source);
|
|
14678
|
+
}
|
|
14679
|
+
function collectModuleBindings(ast) {
|
|
14680
|
+
const imports = /* @__PURE__ */ new Map();
|
|
14681
|
+
const moduleLocals = /* @__PURE__ */ new Set();
|
|
14682
|
+
const body = ast.program?.body ?? [];
|
|
14683
|
+
for (const stmt of body) {
|
|
14684
|
+
if (stmt.type === "ImportDeclaration") {
|
|
14685
|
+
const source = String(stmt.source?.value ?? "");
|
|
14686
|
+
for (const spec of stmt.specifiers ?? []) {
|
|
14687
|
+
const local = spec.local?.name;
|
|
14688
|
+
if (typeof local !== "string") continue;
|
|
14689
|
+
if (spec.type === "ImportSpecifier") {
|
|
14690
|
+
const imported = spec.imported?.type === "Identifier" ? spec.imported.name : String(spec.imported?.value ?? "");
|
|
14691
|
+
imports.set(local, { imported, source });
|
|
14692
|
+
} else {
|
|
14693
|
+
imports.set(local, { imported: "*", source });
|
|
14694
|
+
}
|
|
14695
|
+
}
|
|
14696
|
+
} else {
|
|
14697
|
+
collectDeclaredNames(stmt, moduleLocals);
|
|
14698
|
+
}
|
|
14699
|
+
}
|
|
14700
|
+
return { imports, moduleLocals };
|
|
14701
|
+
}
|
|
14702
|
+
function collectDeclaredNames(stmt, into) {
|
|
14703
|
+
if (stmt.type === "ExportNamedDeclaration" || stmt.type === "ExportDefaultDeclaration") {
|
|
14704
|
+
if (stmt.declaration) collectDeclaredNames(stmt.declaration, into);
|
|
14705
|
+
return;
|
|
14706
|
+
}
|
|
14707
|
+
if (stmt.type === "VariableDeclaration") {
|
|
14708
|
+
for (const decl of stmt.declarations ?? []) {
|
|
14709
|
+
if (decl.id) collectPatternNames(decl.id, into);
|
|
14710
|
+
}
|
|
14711
|
+
return;
|
|
14712
|
+
}
|
|
14713
|
+
if ((stmt.type === "FunctionDeclaration" || stmt.type === "ClassDeclaration" || stmt.type === "TSEnumDeclaration") && stmt.id?.type === "Identifier") {
|
|
14714
|
+
addMarkerName(stmt.id.name, into);
|
|
14715
|
+
}
|
|
14716
|
+
}
|
|
14717
|
+
function collectPatternNames(pattern, into) {
|
|
14718
|
+
switch (pattern.type) {
|
|
14719
|
+
case "Identifier":
|
|
14720
|
+
addMarkerName(pattern.name, into);
|
|
14721
|
+
break;
|
|
14722
|
+
case "AssignmentPattern":
|
|
14723
|
+
collectPatternNames(pattern.left, into);
|
|
14724
|
+
break;
|
|
14725
|
+
case "RestElement":
|
|
14726
|
+
collectPatternNames(pattern.argument, into);
|
|
14727
|
+
break;
|
|
14728
|
+
case "ObjectPattern":
|
|
14729
|
+
for (const prop of pattern.properties ?? []) {
|
|
14730
|
+
if (prop.type === "ObjectProperty") {
|
|
14731
|
+
collectPatternNames(prop.value, into);
|
|
14732
|
+
} else if (prop.type === "RestElement") {
|
|
14733
|
+
collectPatternNames(prop.argument, into);
|
|
14734
|
+
}
|
|
14735
|
+
}
|
|
14736
|
+
break;
|
|
14737
|
+
case "ArrayPattern":
|
|
14738
|
+
for (const el of pattern.elements ?? []) {
|
|
14739
|
+
if (el) collectPatternNames(el, into);
|
|
14740
|
+
}
|
|
14741
|
+
break;
|
|
14742
|
+
}
|
|
14743
|
+
}
|
|
14744
|
+
function addMarkerName(name, into) {
|
|
14745
|
+
if (typeof name === "string" && MARKER_NAMES.has(name)) into.add(name);
|
|
14746
|
+
}
|
|
14747
|
+
var FUNCTION_TYPES = /* @__PURE__ */ new Set([
|
|
14748
|
+
"ArrowFunctionExpression",
|
|
14749
|
+
"FunctionExpression",
|
|
14750
|
+
"FunctionDeclaration",
|
|
14751
|
+
"ObjectMethod",
|
|
14752
|
+
"ClassMethod",
|
|
14753
|
+
"ClassPrivateMethod"
|
|
14754
|
+
]);
|
|
14755
|
+
function collectScopeBindings(node) {
|
|
14756
|
+
const bound = /* @__PURE__ */ new Set();
|
|
14757
|
+
if (FUNCTION_TYPES.has(node.type)) {
|
|
14758
|
+
if (node.id?.type === "Identifier") addMarkerName(node.id.name, bound);
|
|
14759
|
+
for (const param of node.params ?? []) {
|
|
14760
|
+
collectPatternNames(param, bound);
|
|
14761
|
+
}
|
|
14762
|
+
} else if (node.type === "CatchClause" && node.param) {
|
|
14763
|
+
collectPatternNames(node.param, bound);
|
|
14764
|
+
} else if (node.type === "BlockStatement") {
|
|
14765
|
+
for (const stmt of node.body ?? []) {
|
|
14766
|
+
collectDeclaredNames(stmt, bound);
|
|
14767
|
+
}
|
|
14768
|
+
} else if (node.type === "ForStatement" || node.type === "ForOfStatement" || node.type === "ForInStatement") {
|
|
14769
|
+
const init = node.init ?? node.left;
|
|
14770
|
+
if (init?.type === "VariableDeclaration") {
|
|
14771
|
+
collectDeclaredNames(init, bound);
|
|
14772
|
+
}
|
|
14773
|
+
}
|
|
14774
|
+
return bound.size > 0 ? bound : null;
|
|
14775
|
+
}
|
|
14644
14776
|
function walkChildren(node, visit) {
|
|
14645
14777
|
for (const key of Object.keys(node)) {
|
|
14646
14778
|
if (key === "loc" || key === "leadingComments" || key === "trailingComments" || key === "innerComments") {
|
|
@@ -14974,7 +15106,16 @@ async function syncLocaleFiles(options) {
|
|
|
14974
15106
|
delete lock.keys[key];
|
|
14975
15107
|
}
|
|
14976
15108
|
const changedCount = Object.keys(changedKeys).length;
|
|
14977
|
-
|
|
15109
|
+
const missingByLocale = {};
|
|
15110
|
+
for (const targetLocale of targetLocales) {
|
|
15111
|
+
const existing = readTargetFile(join(localesDir, `${targetLocale}.json`));
|
|
15112
|
+
const missing = Object.keys(sourceDict).filter(
|
|
15113
|
+
(key) => !(key in changedKeys) && !(key in existing)
|
|
15114
|
+
);
|
|
15115
|
+
if (missing.length > 0) missingByLocale[targetLocale] = missing;
|
|
15116
|
+
}
|
|
15117
|
+
const missingLocaleCount = Object.keys(missingByLocale).length;
|
|
15118
|
+
if (changedCount === 0 && deletedKeys.length === 0 && missingLocaleCount === 0) {
|
|
14978
15119
|
log("No changes detected in locale files.");
|
|
14979
15120
|
return {
|
|
14980
15121
|
status: "no-changes",
|
|
@@ -14983,7 +15124,7 @@ async function syncLocaleFiles(options) {
|
|
|
14983
15124
|
failures: []
|
|
14984
15125
|
};
|
|
14985
15126
|
}
|
|
14986
|
-
if (changedCount === 0) {
|
|
15127
|
+
if (changedCount === 0 && missingLocaleCount === 0) {
|
|
14987
15128
|
for (const targetLocale of targetLocales) {
|
|
14988
15129
|
const targetFilePath = join(localesDir, `${targetLocale}.json`);
|
|
14989
15130
|
const existing = readTargetFile(targetFilePath);
|
|
@@ -14996,27 +15137,43 @@ async function syncLocaleFiles(options) {
|
|
|
14996
15137
|
);
|
|
14997
15138
|
return { status: "synced", translatedKeys: [], deletedKeys, failures: [] };
|
|
14998
15139
|
}
|
|
14999
|
-
|
|
15000
|
-
|
|
15001
|
-
|
|
15002
|
-
|
|
15003
|
-
for (const key of Object.keys(changedKeys)) {
|
|
15004
|
-
const ctx = pendingEntries[key]?.context;
|
|
15005
|
-
if (ctx) changedContexts[key] = ctx;
|
|
15140
|
+
if (changedCount > 0) {
|
|
15141
|
+
log(
|
|
15142
|
+
`Translating ${changedCount} key${changedCount > 1 ? "s" : ""} to ${targetLocales.length} locale${targetLocales.length > 1 ? "s" : ""}...`
|
|
15143
|
+
);
|
|
15006
15144
|
}
|
|
15145
|
+
if (missingLocaleCount > 0) {
|
|
15146
|
+
const healTotal = Object.values(missingByLocale).reduce(
|
|
15147
|
+
(sum, keys) => sum + keys.length,
|
|
15148
|
+
0
|
|
15149
|
+
);
|
|
15150
|
+
log(
|
|
15151
|
+
`Healing ${healTotal} key${healTotal > 1 ? "s" : ""} missing from ${missingLocaleCount} locale file${missingLocaleCount > 1 ? "s" : ""}...`
|
|
15152
|
+
);
|
|
15153
|
+
}
|
|
15154
|
+
const contextFor = (key) => pendingEntries[key]?.context ?? lock.keys[key]?.context;
|
|
15007
15155
|
const failures = [];
|
|
15008
15156
|
const failedKeys = /* @__PURE__ */ new Set();
|
|
15009
15157
|
for (const targetLocale of targetLocales) {
|
|
15010
15158
|
const targetFilePath = join(localesDir, `${targetLocale}.json`);
|
|
15011
15159
|
const existing = readTargetFile(targetFilePath);
|
|
15012
|
-
const
|
|
15160
|
+
const localeEntries = { ...changedKeys };
|
|
15161
|
+
for (const key of missingByLocale[targetLocale] ?? []) {
|
|
15162
|
+
localeEntries[key] = sourceDict[key];
|
|
15163
|
+
}
|
|
15164
|
+
const localeContexts = {};
|
|
15165
|
+
for (const key of Object.keys(localeEntries)) {
|
|
15166
|
+
const ctx = contextFor(key);
|
|
15167
|
+
if (ctx) localeContexts[key] = ctx;
|
|
15168
|
+
}
|
|
15169
|
+
const entries = Object.entries(localeEntries);
|
|
15013
15170
|
for (let i = 0; i < entries.length; i += batchSize) {
|
|
15014
15171
|
const batch = Object.fromEntries(entries.slice(i, i + batchSize));
|
|
15015
15172
|
try {
|
|
15016
15173
|
const translated = await translate(
|
|
15017
15174
|
batch,
|
|
15018
15175
|
targetLocale,
|
|
15019
|
-
|
|
15176
|
+
localeContexts
|
|
15020
15177
|
);
|
|
15021
15178
|
Object.assign(existing, translated);
|
|
15022
15179
|
} catch (err) {
|
|
@@ -15229,7 +15386,8 @@ async function runExtract() {
|
|
|
15229
15386
|
const extracted = extractStringsFromSource(
|
|
15230
15387
|
code,
|
|
15231
15388
|
relative(root, file),
|
|
15232
|
-
warnings
|
|
15389
|
+
warnings,
|
|
15390
|
+
{ importSources: config.extractImportSources }
|
|
15233
15391
|
);
|
|
15234
15392
|
for (const entry of extracted) {
|
|
15235
15393
|
strings[entry.key] = entry.source;
|
|
@@ -15286,7 +15444,9 @@ async function runCheck(jsonOutput) {
|
|
|
15286
15444
|
const code = readFileSync2(file, "utf-8");
|
|
15287
15445
|
const entries = extractStringsFromSource(
|
|
15288
15446
|
code,
|
|
15289
|
-
relative(root, file)
|
|
15447
|
+
relative(root, file),
|
|
15448
|
+
void 0,
|
|
15449
|
+
{ importSources: config.extractImportSources }
|
|
15290
15450
|
);
|
|
15291
15451
|
for (const entry of entries) {
|
|
15292
15452
|
extracted[entry.key] = entry.source;
|
|
@@ -15410,7 +15570,7 @@ Run \`solid-translate translate\` to refresh ${sourceLocale} \u2192 targets.`
|
|
|
15410
15570
|
);
|
|
15411
15571
|
}
|
|
15412
15572
|
async function runTranslate() {
|
|
15413
|
-
const { translateBatch: translateBatch2, translateMarkdown: translateMarkdown2 } = await import("./translate-
|
|
15573
|
+
const { translateBatch: translateBatch2, translateMarkdown: translateMarkdown2 } = await import("./translate-SWLCBQ5Y.js");
|
|
15414
15574
|
const config = await loadConfig();
|
|
15415
15575
|
const root = process.cwd();
|
|
15416
15576
|
const sourceLocale = config.sourceLocale || "en";
|
package/dist/index.d.ts
CHANGED
|
@@ -36,6 +36,13 @@ interface SolidTranslatePluginConfig {
|
|
|
36
36
|
* Default: `["src/**\/*.tsx", "src/**\/*.ts", "src/**\/*.jsx"]`
|
|
37
37
|
*/
|
|
38
38
|
include?: string[];
|
|
39
|
+
/**
|
|
40
|
+
* Module specifiers accepted as sources of the extraction markers
|
|
41
|
+
* (`msg`, `<T>`, `<Plural>`, ...). `"solid-translate"` is always
|
|
42
|
+
* accepted. When omitted, any specifier whose final path segment is
|
|
43
|
+
* `solid-translate` or `i18n` (e.g. `"@/i18n"`) is accepted.
|
|
44
|
+
*/
|
|
45
|
+
extractImportSources?: string[];
|
|
39
46
|
}
|
|
40
47
|
/** A flat dictionary mapping keys to translated strings */
|
|
41
48
|
type TranslationDictionary = Record<string, string>;
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
collectBatchTranslations,
|
|
4
|
+
extractJsonObject,
|
|
3
5
|
translateBatch,
|
|
4
6
|
translateMarkdown
|
|
5
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-P6RTZPHZ.js";
|
|
6
8
|
import "./chunk-FYS2JH42.js";
|
|
7
9
|
export {
|
|
10
|
+
collectBatchTranslations,
|
|
11
|
+
extractJsonObject,
|
|
8
12
|
translateBatch,
|
|
9
13
|
translateMarkdown
|
|
10
14
|
};
|
package/dist/vite.d.ts
CHANGED
|
@@ -36,6 +36,13 @@ interface SolidTranslatePluginConfig {
|
|
|
36
36
|
* Default: `["src/**\/*.tsx", "src/**\/*.ts", "src/**\/*.jsx"]`
|
|
37
37
|
*/
|
|
38
38
|
include?: string[];
|
|
39
|
+
/**
|
|
40
|
+
* Module specifiers accepted as sources of the extraction markers
|
|
41
|
+
* (`msg`, `<T>`, `<Plural>`, ...). `"solid-translate"` is always
|
|
42
|
+
* accepted. When omitted, any specifier whose final path segment is
|
|
43
|
+
* `solid-translate` or `i18n` (e.g. `"@/i18n"`) is accepted.
|
|
44
|
+
*/
|
|
45
|
+
extractImportSources?: string[];
|
|
39
46
|
}
|
|
40
47
|
|
|
41
48
|
/**
|