glotto 3.0.0 → 3.1.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 +112 -47
- package/esm/cli.js +136 -32
- package/esm/deno.d.ts +5 -0
- package/esm/deno.js +18 -6
- package/esm/src/config.d.ts +4 -0
- package/esm/src/config.d.ts.map +1 -0
- package/esm/src/config.js +95 -0
- package/esm/src/contants.d.ts +4 -0
- package/esm/src/contants.d.ts.map +1 -1
- package/esm/src/contants.js +14 -5
- package/esm/src/diff.d.ts +4 -0
- package/esm/src/diff.d.ts.map +1 -0
- package/esm/src/diff.js +53 -0
- package/esm/src/file.d.ts +4 -3
- package/esm/src/file.d.ts.map +1 -1
- package/esm/src/file.js +23 -5
- package/esm/src/providers/anthropic.d.ts +2 -2
- package/esm/src/providers/anthropic.d.ts.map +1 -1
- package/esm/src/providers/anthropic.js +9 -1
- package/esm/src/providers/gemini.d.ts +2 -2
- package/esm/src/providers/gemini.d.ts.map +1 -1
- package/esm/src/providers/gemini.js +10 -2
- package/esm/src/providers/openai.d.ts +2 -2
- package/esm/src/providers/openai.d.ts.map +1 -1
- package/esm/src/providers/openai.js +9 -1
- package/esm/src/translator.d.ts +14 -7
- package/esm/src/translator.d.ts.map +1 -1
- package/esm/src/translator.js +161 -77
- package/esm/src/types.d.ts +31 -3
- package/esm/src/types.d.ts.map +1 -1
- package/esm/src/utilites.d.ts +3 -4
- package/esm/src/utilites.d.ts.map +1 -1
- package/esm/src/utilites.js +40 -16
- package/package.json +20 -6
- package/schema/glotto.schema.json +87 -0
- package/script/cli.js +134 -30
- package/script/deno.d.ts +5 -0
- package/script/deno.js +18 -6
- package/script/src/config.d.ts +4 -0
- package/script/src/config.d.ts.map +1 -0
- package/script/src/config.js +132 -0
- package/script/src/contants.d.ts +4 -0
- package/script/src/contants.d.ts.map +1 -1
- package/script/src/contants.js +15 -6
- package/script/src/diff.d.ts +4 -0
- package/script/src/diff.d.ts.map +1 -0
- package/script/src/diff.js +57 -0
- package/script/src/file.d.ts +4 -3
- package/script/src/file.d.ts.map +1 -1
- package/script/src/file.js +28 -10
- package/script/src/providers/anthropic.d.ts +2 -2
- package/script/src/providers/anthropic.d.ts.map +1 -1
- package/script/src/providers/anthropic.js +9 -1
- package/script/src/providers/gemini.d.ts +2 -2
- package/script/src/providers/gemini.d.ts.map +1 -1
- package/script/src/providers/gemini.js +10 -2
- package/script/src/providers/openai.d.ts +2 -2
- package/script/src/providers/openai.d.ts.map +1 -1
- package/script/src/providers/openai.js +9 -1
- package/script/src/translator.d.ts +14 -7
- package/script/src/translator.d.ts.map +1 -1
- package/script/src/translator.js +168 -83
- package/script/src/types.d.ts +31 -3
- package/script/src/types.d.ts.map +1 -1
- package/script/src/utilites.d.ts +3 -4
- package/script/src/utilites.d.ts.map +1 -1
- package/script/src/utilites.js +43 -20
package/esm/src/types.d.ts
CHANGED
|
@@ -12,6 +12,9 @@ export type TranslateArgs = {
|
|
|
12
12
|
from?: string;
|
|
13
13
|
to?: string;
|
|
14
14
|
url?: string;
|
|
15
|
+
config?: string;
|
|
16
|
+
stats?: boolean;
|
|
17
|
+
incremental?: boolean;
|
|
15
18
|
'no-limit'?: boolean;
|
|
16
19
|
'no-timeout'?: boolean;
|
|
17
20
|
'max-batch-size'?: string;
|
|
@@ -21,13 +24,30 @@ export type ValidatedTranslateArgs = {
|
|
|
21
24
|
provider: Provider;
|
|
22
25
|
model?: string;
|
|
23
26
|
input: string;
|
|
24
|
-
output: string;
|
|
27
|
+
output: string[];
|
|
25
28
|
from: string;
|
|
26
|
-
to: string;
|
|
29
|
+
to: string[];
|
|
27
30
|
url?: string;
|
|
28
31
|
noLimit: boolean;
|
|
29
32
|
noTimeout: boolean;
|
|
30
33
|
maxBatchBytes: number;
|
|
34
|
+
stats: boolean;
|
|
35
|
+
incremental: boolean;
|
|
36
|
+
};
|
|
37
|
+
export type GlottoConfig = {
|
|
38
|
+
key?: string;
|
|
39
|
+
provider?: Provider;
|
|
40
|
+
model?: string;
|
|
41
|
+
input?: string;
|
|
42
|
+
output?: string | string[];
|
|
43
|
+
from?: string;
|
|
44
|
+
to?: string | string[];
|
|
45
|
+
url?: string;
|
|
46
|
+
noLimit?: boolean;
|
|
47
|
+
noTimeout?: boolean;
|
|
48
|
+
maxBatchSize?: number;
|
|
49
|
+
stats?: boolean;
|
|
50
|
+
incremental?: boolean;
|
|
31
51
|
};
|
|
32
52
|
export type JsonValue = string | number | boolean | null | JsonObject | JsonArray;
|
|
33
53
|
export interface JsonObject {
|
|
@@ -47,7 +67,15 @@ export type Batch = {
|
|
|
47
67
|
leaves: Leaf[];
|
|
48
68
|
byteSize: number;
|
|
49
69
|
};
|
|
70
|
+
export type TranslateUsage = {
|
|
71
|
+
inputTokens: number;
|
|
72
|
+
outputTokens: number;
|
|
73
|
+
};
|
|
74
|
+
export type TranslateResult = {
|
|
75
|
+
text: string;
|
|
76
|
+
usage?: TranslateUsage;
|
|
77
|
+
};
|
|
50
78
|
export interface TextTranslator {
|
|
51
|
-
translate(prompt: string): Promise<
|
|
79
|
+
translate(prompt: string): Promise<TranslateResult>;
|
|
52
80
|
}
|
|
53
81
|
//# sourceMappingURL=types.d.ts.map
|
package/esm/src/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,CAAC;AAEzD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,QAAQ,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/src/types.ts"],"names":[],"mappings":"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"}
|
package/esm/src/utilites.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { TranslateArgs, ValidatedTranslateArgs } from './types.js';
|
|
2
|
-
|
|
3
|
-
export declare
|
|
4
|
-
export declare
|
|
5
|
-
export declare const delay: (ms: number) => Promise<void>;
|
|
2
|
+
export declare function validateArgs(args: TranslateArgs): ValidatedTranslateArgs;
|
|
3
|
+
export declare function formatBytes(bytes: number): string;
|
|
4
|
+
export declare function delay(ms: number): Promise<void>;
|
|
6
5
|
//# sourceMappingURL=utilites.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utilites.d.ts","sourceRoot":"","sources":["../../src/src/utilites.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAY,aAAa,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"utilites.d.ts","sourceRoot":"","sources":["../../src/src/utilites.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAY,aAAa,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAOlF,wBAAgB,YAAY,CAAC,IAAI,EAAE,aAAa,GAAG,sBAAsB,CA0DxE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQjD;AAED,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/C"}
|
package/esm/src/utilites.js
CHANGED
|
@@ -1,17 +1,37 @@
|
|
|
1
1
|
import { DEFAULT_MAX_BATCH_BYTES } from './contants.js';
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
function splitCsv(value) {
|
|
3
|
+
return value.split(',').map((s) => s.trim()).filter((s) => s.length > 0);
|
|
4
|
+
}
|
|
5
|
+
export function validateArgs(args) {
|
|
6
|
+
if (!args.key) {
|
|
4
7
|
throw new Error('AI Key is required');
|
|
5
|
-
|
|
8
|
+
}
|
|
9
|
+
if (!args.provider) {
|
|
6
10
|
throw new Error('Provider parameter is required');
|
|
7
|
-
|
|
11
|
+
}
|
|
12
|
+
if (!args.input) {
|
|
8
13
|
throw new Error('Input parameter is required');
|
|
9
|
-
|
|
14
|
+
}
|
|
15
|
+
if (!args.output) {
|
|
10
16
|
throw new Error('Output parameter is required');
|
|
11
|
-
|
|
17
|
+
}
|
|
18
|
+
if (!args.from) {
|
|
12
19
|
throw new Error('Source language (from) parameter is required');
|
|
13
|
-
|
|
20
|
+
}
|
|
21
|
+
if (!args.to) {
|
|
14
22
|
throw new Error('Target language (to) parameter is required');
|
|
23
|
+
}
|
|
24
|
+
const toList = splitCsv(args.to);
|
|
25
|
+
const outputList = splitCsv(args.output);
|
|
26
|
+
if (toList.length === 0) {
|
|
27
|
+
throw new Error('Target language (to) must contain at least one value');
|
|
28
|
+
}
|
|
29
|
+
if (outputList.length === 0) {
|
|
30
|
+
throw new Error('Output must contain at least one value');
|
|
31
|
+
}
|
|
32
|
+
if (toList.length !== outputList.length) {
|
|
33
|
+
throw new Error(`--to (${toList.length}) and --output (${outputList.length}) must have the same number of values`);
|
|
34
|
+
}
|
|
15
35
|
let maxBatchBytes = DEFAULT_MAX_BATCH_BYTES;
|
|
16
36
|
const rawMaxBatchSize = args['max-batch-size'];
|
|
17
37
|
if (rawMaxBatchSize !== undefined) {
|
|
@@ -26,22 +46,26 @@ export const validateArgs = (args) => {
|
|
|
26
46
|
provider: args.provider,
|
|
27
47
|
model: args.model,
|
|
28
48
|
input: args.input,
|
|
29
|
-
output:
|
|
49
|
+
output: outputList,
|
|
30
50
|
from: args.from,
|
|
31
|
-
to:
|
|
51
|
+
to: toList,
|
|
32
52
|
url: args.url,
|
|
33
53
|
noLimit: args['no-limit'] ?? false,
|
|
34
54
|
noTimeout: args['no-timeout'] ?? false,
|
|
35
55
|
maxBatchBytes,
|
|
56
|
+
stats: args.stats ?? false,
|
|
57
|
+
incremental: args.incremental ?? false,
|
|
36
58
|
};
|
|
37
|
-
}
|
|
38
|
-
export
|
|
39
|
-
if (bytes < 1024)
|
|
59
|
+
}
|
|
60
|
+
export function formatBytes(bytes) {
|
|
61
|
+
if (bytes < 1024) {
|
|
40
62
|
return `${bytes} B`;
|
|
41
|
-
|
|
63
|
+
}
|
|
64
|
+
if (bytes < 1024 * 1024) {
|
|
42
65
|
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
66
|
+
}
|
|
43
67
|
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
|
44
|
-
}
|
|
45
|
-
export
|
|
68
|
+
}
|
|
69
|
+
export function delay(ms) {
|
|
46
70
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
47
|
-
}
|
|
71
|
+
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "glotto",
|
|
3
|
-
"version": "3.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "3.1.0",
|
|
4
|
+
"description": "AI-powered i18n JSON translator. Multi-target, incremental, provider-agnostic (OpenAI, Anthropic, Gemini).",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"i18n",
|
|
7
|
+
"translation",
|
|
8
|
+
"translator",
|
|
9
|
+
"ai",
|
|
10
|
+
"openai",
|
|
11
|
+
"anthropic",
|
|
12
|
+
"gemini",
|
|
13
|
+
"localization",
|
|
14
|
+
"l10n",
|
|
15
|
+
"json",
|
|
16
|
+
"cli"
|
|
17
|
+
],
|
|
5
18
|
"author": "Ibrahim Odev <me@ibrahimo.dev>",
|
|
19
|
+
"homepage": "https://github.com/ibodev1/glotto#readme",
|
|
6
20
|
"repository": {
|
|
7
21
|
"type": "git",
|
|
8
22
|
"url": "git+https://github.com/ibodev1/glotto.git"
|
|
@@ -19,13 +33,13 @@
|
|
|
19
33
|
"access": "public"
|
|
20
34
|
},
|
|
21
35
|
"engines": {
|
|
22
|
-
"node": ">=20.
|
|
36
|
+
"node": ">=20.10.0"
|
|
23
37
|
},
|
|
24
38
|
"dependencies": {
|
|
25
|
-
"@anthropic-ai/sdk": "^0.95.
|
|
26
|
-
"@google/genai": "^
|
|
39
|
+
"@anthropic-ai/sdk": "^0.95.1",
|
|
40
|
+
"@google/genai": "^2.0.0",
|
|
27
41
|
"consola": "^3.4.2",
|
|
28
|
-
"openai": "^6.
|
|
42
|
+
"openai": "^6.37.0",
|
|
29
43
|
"@deno/shim-deno": "~0.18.0"
|
|
30
44
|
},
|
|
31
45
|
"devDependencies": {
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://raw.githubusercontent.com/ibodev1/glotto/main/schema/glotto.schema.json",
|
|
4
|
+
"title": "Glotto config",
|
|
5
|
+
"description": "Configuration for the Glotto AI translator. CLI flags override values defined here.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"properties": {
|
|
9
|
+
"$schema": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "JSON Schema reference for editor autocomplete."
|
|
12
|
+
},
|
|
13
|
+
"key": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"description": "API key for the AI provider."
|
|
16
|
+
},
|
|
17
|
+
"provider": {
|
|
18
|
+
"type": "string",
|
|
19
|
+
"enum": ["openai", "gemini", "anthropic"],
|
|
20
|
+
"description": "AI translation provider."
|
|
21
|
+
},
|
|
22
|
+
"model": {
|
|
23
|
+
"type": "string",
|
|
24
|
+
"description": "Model name for the selected provider."
|
|
25
|
+
},
|
|
26
|
+
"input": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"description": "Path to source JSON file."
|
|
29
|
+
},
|
|
30
|
+
"output": {
|
|
31
|
+
"description": "Single target file path or array of paths (one per target language).",
|
|
32
|
+
"oneOf": [
|
|
33
|
+
{ "type": "string" },
|
|
34
|
+
{
|
|
35
|
+
"type": "array",
|
|
36
|
+
"items": { "type": "string" },
|
|
37
|
+
"minItems": 1
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
"from": {
|
|
42
|
+
"type": "string",
|
|
43
|
+
"description": "Source language name."
|
|
44
|
+
},
|
|
45
|
+
"to": {
|
|
46
|
+
"description": "Target language name or array of names. Length must match output array.",
|
|
47
|
+
"oneOf": [
|
|
48
|
+
{ "type": "string" },
|
|
49
|
+
{
|
|
50
|
+
"type": "array",
|
|
51
|
+
"items": { "type": "string" },
|
|
52
|
+
"minItems": 1
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
},
|
|
56
|
+
"url": {
|
|
57
|
+
"type": "string",
|
|
58
|
+
"description": "Custom base URL for OpenAI/Anthropic providers."
|
|
59
|
+
},
|
|
60
|
+
"noLimit": {
|
|
61
|
+
"type": "boolean",
|
|
62
|
+
"description": "Disable inter-batch rate-limit delay.",
|
|
63
|
+
"default": false
|
|
64
|
+
},
|
|
65
|
+
"noTimeout": {
|
|
66
|
+
"type": "boolean",
|
|
67
|
+
"description": "Disable request timeout.",
|
|
68
|
+
"default": false
|
|
69
|
+
},
|
|
70
|
+
"maxBatchSize": {
|
|
71
|
+
"type": "integer",
|
|
72
|
+
"minimum": 1,
|
|
73
|
+
"description": "Maximum source size per batch in KB.",
|
|
74
|
+
"default": 12
|
|
75
|
+
},
|
|
76
|
+
"stats": {
|
|
77
|
+
"type": "boolean",
|
|
78
|
+
"description": "Print AI usage statistics at the end of the run.",
|
|
79
|
+
"default": false
|
|
80
|
+
},
|
|
81
|
+
"incremental": {
|
|
82
|
+
"type": "boolean",
|
|
83
|
+
"description": "Translate only missing or empty keys when target file already exists.",
|
|
84
|
+
"default": false
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
package/script/cli.js
CHANGED
|
@@ -43,6 +43,8 @@ const unstable_spinner_js_1 = require("./deps/jsr.io/@std/cli/1.0.29/unstable_sp
|
|
|
43
43
|
const mod_js_1 = require("./deps/jsr.io/@std/cli/1.0.29/mod.js");
|
|
44
44
|
const file_js_1 = require("./src/file.js");
|
|
45
45
|
const translator_js_1 = require("./src/translator.js");
|
|
46
|
+
const diff_js_1 = require("./src/diff.js");
|
|
47
|
+
const config_js_1 = require("./src/config.js");
|
|
46
48
|
const contants_js_1 = require("./src/contants.js");
|
|
47
49
|
const utilites_js_1 = require("./src/utilites.js");
|
|
48
50
|
const logger_js_1 = require("./src/logger.js");
|
|
@@ -51,7 +53,7 @@ const gemini_js_1 = __importDefault(require("./src/providers/gemini.js"));
|
|
|
51
53
|
const openai_js_1 = __importDefault(require("./src/providers/openai.js"));
|
|
52
54
|
const anthropic_js_1 = __importDefault(require("./src/providers/anthropic.js"));
|
|
53
55
|
const spinner = new unstable_spinner_js_1.Spinner({ message: 'AI Thinks...', color: 'cyan' });
|
|
54
|
-
|
|
56
|
+
function createTranslator(args, options) {
|
|
55
57
|
switch (args.provider) {
|
|
56
58
|
case 'gemini':
|
|
57
59
|
return new gemini_js_1.default(args.key, args.model, options);
|
|
@@ -62,12 +64,99 @@ const createTranslator = (args, options) => {
|
|
|
62
64
|
default:
|
|
63
65
|
throw new Error(`Unknown provider: ${args.provider}`);
|
|
64
66
|
}
|
|
65
|
-
}
|
|
67
|
+
}
|
|
68
|
+
function printBanner() {
|
|
69
|
+
logger_js_1.logger.box(`Glotto AI Translator v${deno_js_1.default.version}\n${contants_js_1.GITHUB_REPO_URL}`);
|
|
70
|
+
}
|
|
71
|
+
function printStats(args, perTarget) {
|
|
72
|
+
const totalInput = perTarget.reduce((sum, t) => sum + t.usage.inputTokens, 0);
|
|
73
|
+
const totalOutput = perTarget.reduce((sum, t) => sum + t.usage.outputTokens, 0);
|
|
74
|
+
const totalCalls = perTarget.reduce((sum, t) => sum + t.calls, 0);
|
|
75
|
+
const totalBytes = perTarget.reduce((sum, t) => sum + t.bytes, 0);
|
|
76
|
+
const totalTranslated = perTarget.reduce((sum, t) => sum + t.translatedCount, 0);
|
|
77
|
+
const lines = [];
|
|
78
|
+
lines.push(`Provider: ${args.provider}${args.model ? ` (${args.model})` : ''}`);
|
|
79
|
+
lines.push(`Targets: ${perTarget.length}`);
|
|
80
|
+
lines.push('');
|
|
81
|
+
for (const t of perTarget) {
|
|
82
|
+
const mode = t.incrementalApplied ? ' [incremental]' : '';
|
|
83
|
+
lines.push(`→ ${t.to} (${t.output})${mode}: ${t.translatedCount}/${t.fullCount} entries, ${t.batchCount} batch(es), ${(0, utilites_js_1.formatBytes)(t.bytes)}, ${t.calls} call(s)`);
|
|
84
|
+
lines.push(` tokens — in: ${t.usage.inputTokens}, out: ${t.usage.outputTokens}`);
|
|
85
|
+
}
|
|
86
|
+
lines.push('');
|
|
87
|
+
lines.push(`Total: ${totalTranslated} entries translated, ${totalCalls} call(s), ${(0, utilites_js_1.formatBytes)(totalBytes)}`);
|
|
88
|
+
lines.push(`Total tokens — in: ${totalInput}, out: ${totalOutput}, sum: ${totalInput + totalOutput}`);
|
|
89
|
+
logger_js_1.logger.box(lines.join('\n'));
|
|
90
|
+
}
|
|
91
|
+
async function translateForTarget(validatedArgs, fileContent, allLeaves, to, outputRel, translator, translateOptions, targetIndex, totalTargets) {
|
|
92
|
+
const outputPath = (0, file_js_1.resolvePath)(outputRel);
|
|
93
|
+
const targetLabel = `[Target ${targetIndex + 1}/${totalTargets}: ${to}]`;
|
|
94
|
+
logger_js_1.logger.info(`${targetLabel} → ${outputRel}`);
|
|
95
|
+
const translatableLeaves = allLeaves.filter((leaf) => leaf.translatable);
|
|
96
|
+
let leavesToTranslate = translatableLeaves;
|
|
97
|
+
let existingTarget = null;
|
|
98
|
+
let incrementalApplied = false;
|
|
99
|
+
if (validatedArgs.incremental) {
|
|
100
|
+
existingTarget = await (0, file_js_1.readJsonIfExists)(outputPath);
|
|
101
|
+
if (existingTarget !== null) {
|
|
102
|
+
leavesToTranslate = (0, diff_js_1.findMissingLeaves)(allLeaves, existingTarget);
|
|
103
|
+
incrementalApplied = true;
|
|
104
|
+
logger_js_1.logger.info(`${targetLabel} Incremental: ${leavesToTranslate.length}/${translatableLeaves.length} entries missing in existing target`);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
logger_js_1.logger.info(`${targetLabel} Incremental: target file not found, doing full translation`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (leavesToTranslate.length === 0) {
|
|
111
|
+
logger_js_1.logger.info(`${targetLabel} Nothing to translate, writing existing/source content`);
|
|
112
|
+
const content = existingTarget !== null ? existingTarget : fileContent;
|
|
113
|
+
await (0, file_js_1.writeOutput)(outputPath, JSON.stringify(content, null, 2));
|
|
114
|
+
return {
|
|
115
|
+
to,
|
|
116
|
+
output: outputRel,
|
|
117
|
+
fullCount: translatableLeaves.length,
|
|
118
|
+
translatedCount: 0,
|
|
119
|
+
batchCount: 0,
|
|
120
|
+
bytes: 0,
|
|
121
|
+
calls: 0,
|
|
122
|
+
usage: { inputTokens: 0, outputTokens: 0 },
|
|
123
|
+
incrementalApplied,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
const batches = (0, translator_js_1.groupIntoBatches)(leavesToTranslate, validatedArgs.maxBatchBytes);
|
|
127
|
+
const totalBytes = batches.reduce((sum, b) => sum + b.byteSize, 0);
|
|
128
|
+
logger_js_1.logger.info(`${targetLabel} ${leavesToTranslate.length} translatable entries, ${(0, utilites_js_1.formatBytes)(totalBytes)}, split into ${batches.length} batch(es)`);
|
|
129
|
+
for (const batch of batches) {
|
|
130
|
+
logger_js_1.logger.info(` Batch ${batch.index + 1}: ${batch.leaves.length} entries, ${(0, utilites_js_1.formatBytes)(batch.byteSize)}`);
|
|
131
|
+
}
|
|
132
|
+
spinner.start();
|
|
133
|
+
const result = await (0, translator_js_1.runBatches)(batches, translator, validatedArgs.from, to, translateOptions);
|
|
134
|
+
spinner.stop();
|
|
135
|
+
let finalContent;
|
|
136
|
+
if (incrementalApplied && existingTarget !== null) {
|
|
137
|
+
finalContent = (0, diff_js_1.mergeTranslations)(existingTarget, allLeaves, result.translations);
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
finalContent = (0, translator_js_1.reconstruct)(allLeaves, result.translations);
|
|
141
|
+
}
|
|
142
|
+
await (0, file_js_1.writeOutput)(outputPath, JSON.stringify(finalContent, null, 2));
|
|
143
|
+
return {
|
|
144
|
+
to,
|
|
145
|
+
output: outputRel,
|
|
146
|
+
fullCount: translatableLeaves.length,
|
|
147
|
+
translatedCount: result.translations.size,
|
|
148
|
+
batchCount: batches.length,
|
|
149
|
+
bytes: totalBytes,
|
|
150
|
+
calls: result.calls,
|
|
151
|
+
usage: result.usage,
|
|
152
|
+
incrementalApplied,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
66
155
|
async function main() {
|
|
67
156
|
try {
|
|
68
157
|
const args = (0, mod_js_1.parseArgs)(dntShim.Deno.args, {
|
|
69
|
-
string: ['key', 'provider', 'model', 'input', 'output', 'from', 'to', 'url', 'max-batch-size'],
|
|
70
|
-
boolean: ['help', 'version', 'no-limit', 'no-timeout'],
|
|
158
|
+
string: ['key', 'provider', 'model', 'input', 'output', 'from', 'to', 'url', 'max-batch-size', 'config'],
|
|
159
|
+
boolean: ['help', 'version', 'no-limit', 'no-timeout', 'stats', 'incremental'],
|
|
71
160
|
alias: {
|
|
72
161
|
provider: 'p',
|
|
73
162
|
model: 'm',
|
|
@@ -79,7 +168,6 @@ async function main() {
|
|
|
79
168
|
help: 'h',
|
|
80
169
|
version: 'v',
|
|
81
170
|
},
|
|
82
|
-
default: { provider: contants_js_1.DEFAULT_PROVIDER },
|
|
83
171
|
});
|
|
84
172
|
const help = args.help || dntShim.Deno.args.length === 0;
|
|
85
173
|
const version = args.version;
|
|
@@ -88,38 +176,53 @@ async function main() {
|
|
|
88
176
|
dntShim.Deno.exit(0);
|
|
89
177
|
}
|
|
90
178
|
if (help) {
|
|
179
|
+
printBanner();
|
|
91
180
|
logger_js_1.logger.box(contants_js_1.HELP_TEXT);
|
|
92
181
|
dntShim.Deno.exit(0);
|
|
93
182
|
}
|
|
94
|
-
|
|
183
|
+
printBanner();
|
|
184
|
+
const config = await (0, config_js_1.loadConfig)(args.config);
|
|
185
|
+
const merged = (0, config_js_1.applyConfig)(args, config, dntShim.Deno.args);
|
|
186
|
+
if (!merged.provider) {
|
|
187
|
+
merged.provider = contants_js_1.DEFAULT_PROVIDER;
|
|
188
|
+
}
|
|
189
|
+
const validatedArgs = (0, utilites_js_1.validateArgs)(merged);
|
|
95
190
|
const fileContent = await (0, file_js_1.getImportJson)(validatedArgs.input);
|
|
96
191
|
const allLeaves = (0, translator_js_1.extractLeaves)(fileContent);
|
|
97
192
|
const translatableLeaves = allLeaves.filter((leaf) => leaf.translatable);
|
|
98
|
-
const batches = (0, translator_js_1.groupIntoBatches)(allLeaves, validatedArgs.maxBatchBytes);
|
|
99
|
-
const totalBytes = batches.reduce((sum, b) => sum + b.byteSize, 0);
|
|
100
193
|
logger_js_1.logger.info('Provider: ', validatedArgs.provider);
|
|
101
194
|
logger_js_1.logger.info('Input: ', validatedArgs.input);
|
|
102
|
-
logger_js_1.logger.info('
|
|
195
|
+
logger_js_1.logger.info('Targets: ', validatedArgs.to.map((to, i) => `${to} → ${validatedArgs.output[i]}`).join(', '));
|
|
103
196
|
logger_js_1.logger.info('From: ', validatedArgs.from);
|
|
104
|
-
|
|
105
|
-
if (validatedArgs.model)
|
|
197
|
+
if (validatedArgs.model) {
|
|
106
198
|
logger_js_1.logger.info('Model: ', validatedArgs.model);
|
|
107
|
-
|
|
199
|
+
}
|
|
200
|
+
if (validatedArgs.url) {
|
|
108
201
|
logger_js_1.logger.info('URL: ', validatedArgs.url);
|
|
109
|
-
|
|
202
|
+
}
|
|
203
|
+
if (validatedArgs.noLimit) {
|
|
110
204
|
logger_js_1.logger.info('Rate limit protection: disabled (--no-limit)');
|
|
111
|
-
|
|
205
|
+
}
|
|
206
|
+
if (validatedArgs.noTimeout) {
|
|
112
207
|
logger_js_1.logger.info('Request timeout: disabled (--no-timeout)');
|
|
208
|
+
}
|
|
209
|
+
if (validatedArgs.incremental) {
|
|
210
|
+
logger_js_1.logger.info('Incremental mode: enabled (--incremental)');
|
|
211
|
+
}
|
|
212
|
+
if (validatedArgs.stats) {
|
|
213
|
+
logger_js_1.logger.info('Stats: enabled (--stats)');
|
|
214
|
+
}
|
|
113
215
|
if (validatedArgs.maxBatchBytes !== contants_js_1.DEFAULT_MAX_BATCH_BYTES) {
|
|
114
216
|
logger_js_1.logger.info(`Max batch size: ${(0, utilites_js_1.formatBytes)(validatedArgs.maxBatchBytes)}`);
|
|
115
217
|
}
|
|
116
|
-
logger_js_1.logger.info(`
|
|
117
|
-
|
|
118
|
-
logger_js_1.logger.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
218
|
+
logger_js_1.logger.info(`Source: ${translatableLeaves.length} translatable entries (of ${allLeaves.length} leaves)`);
|
|
219
|
+
if (translatableLeaves.length === 0 && !validatedArgs.incremental) {
|
|
220
|
+
logger_js_1.logger.warn('No translatable entries found, copying input to output(s)');
|
|
221
|
+
for (const outputRel of validatedArgs.output) {
|
|
222
|
+
await (0, file_js_1.writeOutput)((0, file_js_1.resolvePath)(outputRel), JSON.stringify(fileContent, null, 2));
|
|
223
|
+
}
|
|
224
|
+
logger_js_1.logger.success('Translation completed');
|
|
225
|
+
logger_js_1.logger.info(`★ Glotto faydalı olduysa GitHub'da yıldızlamayı unutma: ${contants_js_1.GITHUB_REPO_URL}`);
|
|
123
226
|
dntShim.Deno.exit(0);
|
|
124
227
|
}
|
|
125
228
|
const translateOptions = {
|
|
@@ -127,22 +230,23 @@ async function main() {
|
|
|
127
230
|
noTimeout: validatedArgs.noTimeout,
|
|
128
231
|
};
|
|
129
232
|
const translator = createTranslator(validatedArgs, translateOptions);
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
233
|
+
const perTarget = [];
|
|
234
|
+
for (let i = 0; i < validatedArgs.to.length; i++) {
|
|
235
|
+
const stat = await translateForTarget(validatedArgs, fileContent, allLeaves, validatedArgs.to[i], validatedArgs.output[i], translator, translateOptions, i, validatedArgs.to.length);
|
|
236
|
+
perTarget.push(stat);
|
|
237
|
+
}
|
|
135
238
|
spinner.stop();
|
|
136
239
|
logger_js_1.logger.success('Translation completed');
|
|
240
|
+
if (validatedArgs.stats) {
|
|
241
|
+
printStats(validatedArgs, perTarget);
|
|
242
|
+
}
|
|
243
|
+
logger_js_1.logger.info(`★ Glotto faydalı olduysa GitHub'da yıldızlamayı unutma: ${contants_js_1.GITHUB_REPO_URL}`);
|
|
244
|
+
dntShim.Deno.exit(0);
|
|
137
245
|
}
|
|
138
246
|
catch (error) {
|
|
139
247
|
spinner.stop();
|
|
140
248
|
logger_js_1.logger.error(error);
|
|
141
249
|
dntShim.Deno.exit(1);
|
|
142
250
|
}
|
|
143
|
-
finally {
|
|
144
|
-
spinner.stop();
|
|
145
|
-
dntShim.Deno.exit(0);
|
|
146
|
-
}
|
|
147
251
|
}
|
|
148
252
|
main();
|
package/script/deno.d.ts
CHANGED
|
@@ -11,17 +11,22 @@ declare namespace _default {
|
|
|
11
11
|
publish: string;
|
|
12
12
|
"publish:npm": string;
|
|
13
13
|
check: string;
|
|
14
|
+
test: string;
|
|
14
15
|
};
|
|
15
16
|
namespace fmt {
|
|
16
17
|
let semiColons: boolean;
|
|
17
18
|
let singleQuote: boolean;
|
|
18
19
|
let lineWidth: number;
|
|
19
20
|
}
|
|
21
|
+
namespace publish {
|
|
22
|
+
let exclude: string[];
|
|
23
|
+
}
|
|
20
24
|
let imports: {
|
|
21
25
|
"@anthropic-ai/sdk": string;
|
|
22
26
|
"@deno/dnt": string;
|
|
23
27
|
"@google/genai": string;
|
|
24
28
|
"@openai/openai": string;
|
|
29
|
+
"@std/assert": string;
|
|
25
30
|
"@std/cli": string;
|
|
26
31
|
"@std/path": string;
|
|
27
32
|
consola: string;
|
package/script/deno.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.default = {
|
|
4
4
|
"name": "@ibodev/glotto",
|
|
5
|
-
"version": "3.
|
|
5
|
+
"version": "3.1.0",
|
|
6
6
|
"exports": "./cli.ts",
|
|
7
7
|
"lock": false,
|
|
8
8
|
"nodeModulesDir": "auto",
|
|
@@ -12,21 +12,33 @@ exports.default = {
|
|
|
12
12
|
"build:npm": "deno run -A scripts/build_npm.ts",
|
|
13
13
|
"publish": "deno publish",
|
|
14
14
|
"publish:npm": "cd ./npm && npm publish",
|
|
15
|
-
"check": "deno check **/*.ts"
|
|
15
|
+
"check": "deno check **/*.ts",
|
|
16
|
+
"test": "deno test -A --no-check tests/"
|
|
16
17
|
},
|
|
17
18
|
"fmt": {
|
|
18
19
|
"semiColons": true,
|
|
19
20
|
"singleQuote": true,
|
|
20
21
|
"lineWidth": 160
|
|
21
22
|
},
|
|
23
|
+
"publish": {
|
|
24
|
+
"exclude": [
|
|
25
|
+
"tests/",
|
|
26
|
+
"scripts/",
|
|
27
|
+
"npm/",
|
|
28
|
+
"assets/",
|
|
29
|
+
"glotto.exe",
|
|
30
|
+
"CLAUDE.md"
|
|
31
|
+
]
|
|
32
|
+
},
|
|
22
33
|
"imports": {
|
|
23
|
-
"@anthropic-ai/sdk": "npm:@anthropic-ai/sdk@^0.95.
|
|
34
|
+
"@anthropic-ai/sdk": "npm:@anthropic-ai/sdk@^0.95.1",
|
|
24
35
|
"@deno/dnt": "jsr:@deno/dnt@^0.42.3",
|
|
25
|
-
"@google/genai": "npm:@google/genai@^
|
|
26
|
-
"@openai/openai": "npm:openai@^6.
|
|
36
|
+
"@google/genai": "npm:@google/genai@^2.0.0",
|
|
37
|
+
"@openai/openai": "npm:openai@^6.37.0",
|
|
38
|
+
"@std/assert": "jsr:@std/assert@^1.0.19",
|
|
27
39
|
"@std/cli": "jsr:@std/cli@^1.0.29",
|
|
28
40
|
"@std/path": "jsr:@std/path@^1.1.4",
|
|
29
41
|
"consola": "npm:consola@^3.4.2"
|
|
30
42
|
},
|
|
31
|
-
"allowScripts": ["npm:@google/genai@1.52.0", "npm:protobufjs@7.5.6"]
|
|
43
|
+
"allowScripts": ["npm:@google/genai@1.52.0", "npm:@google/genai@2.0.0", "npm:protobufjs@7.5.6"]
|
|
32
44
|
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { GlottoConfig, TranslateArgs } from './types.js';
|
|
2
|
+
export declare function loadConfig(explicitPath?: string): Promise<GlottoConfig>;
|
|
3
|
+
export declare function applyConfig(cli: TranslateArgs, config: GlottoConfig, rawArgs: string[]): TranslateArgs;
|
|
4
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/src/config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAuB9D,wBAAsB,UAAU,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAc7E;AAkBD,wBAAgB,WAAW,CAAC,GAAG,EAAE,aAAa,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,aAAa,CA0CtG"}
|