@geolonia/yuuhitsu 0.1.0 → 0.1.1
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 +0 -0
- package/README.md +0 -0
- package/dist/cli/commands/init.d.ts +3 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +82 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/translate.d.ts +0 -0
- package/dist/cli/commands/translate.d.ts.map +1 -1
- package/dist/cli/commands/translate.js +53 -26
- package/dist/cli/commands/translate.js.map +1 -1
- package/dist/cli/index.d.ts +0 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/config.d.ts +0 -0
- package/dist/config.d.ts.map +0 -0
- package/dist/config.js +0 -0
- package/dist/config.js.map +0 -0
- package/dist/errors.d.ts +0 -0
- package/dist/errors.d.ts.map +0 -0
- package/dist/errors.js +0 -0
- package/dist/errors.js.map +0 -0
- package/dist/logger.d.ts +0 -0
- package/dist/logger.d.ts.map +0 -0
- package/dist/logger.js +0 -0
- package/dist/logger.js.map +0 -0
- package/dist/provider/claude.d.ts +0 -0
- package/dist/provider/claude.d.ts.map +0 -0
- package/dist/provider/claude.js +0 -0
- package/dist/provider/claude.js.map +0 -0
- package/dist/provider/gemini.d.ts +0 -0
- package/dist/provider/gemini.d.ts.map +0 -0
- package/dist/provider/gemini.js +0 -0
- package/dist/provider/gemini.js.map +0 -0
- package/dist/provider/index.d.ts +0 -0
- package/dist/provider/index.d.ts.map +0 -0
- package/dist/provider/index.js +0 -0
- package/dist/provider/index.js.map +0 -0
- package/dist/provider/interface.d.ts +0 -0
- package/dist/provider/interface.d.ts.map +0 -0
- package/dist/provider/interface.js +0 -0
- package/dist/provider/interface.js.map +0 -0
- package/dist/provider/ollama.d.ts +0 -0
- package/dist/provider/ollama.d.ts.map +0 -0
- package/dist/provider/ollama.js +0 -0
- package/dist/provider/ollama.js.map +0 -0
- package/dist/tasks/batch-translate.d.ts +46 -0
- package/dist/tasks/batch-translate.d.ts.map +1 -0
- package/dist/tasks/batch-translate.js +174 -0
- package/dist/tasks/batch-translate.js.map +1 -0
- package/dist/tasks/stream.d.ts +0 -0
- package/dist/tasks/stream.d.ts.map +0 -0
- package/dist/tasks/stream.js +0 -0
- package/dist/tasks/stream.js.map +0 -0
- package/dist/tasks/translate.d.ts +25 -0
- package/dist/tasks/translate.d.ts.map +1 -1
- package/dist/tasks/translate.js +91 -6
- package/dist/tasks/translate.js.map +1 -1
- package/package.json +14 -8
- package/src/templates/fix-links.md +0 -0
- package/src/templates/generate-docs.md +0 -0
- package/src/templates/generate-tests.md +0 -0
- package/src/templates/research.md +0 -0
- package/src/templates/sync-docs.md +0 -0
- package/src/templates/translate.md +8 -0
package/LICENSE
CHANGED
|
File without changes
|
package/README.md
CHANGED
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAiDpC,eAAO,MAAM,WAAW,SA6CpB,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { writeFileSync, existsSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
const DEFAULT_CONFIG_TEMPLATE = `# Yuuhitsu Configuration File
|
|
6
|
+
# AI-powered document operations CLI
|
|
7
|
+
# See: https://github.com/geolonia/yuuhitsu
|
|
8
|
+
|
|
9
|
+
# AI Provider Selection
|
|
10
|
+
# Choose one: claude, gemini, or ollama
|
|
11
|
+
provider: claude
|
|
12
|
+
|
|
13
|
+
# Model Configuration
|
|
14
|
+
# Claude models: claude-sonnet-4-5-20250929, claude-opus-4-6-20250929, claude-haiku-4-5-20251001
|
|
15
|
+
# Gemini models: gemini-2.0-flash, gemini-1.5-pro
|
|
16
|
+
# Ollama models: llama3.2, mistral, etc. (requires local Ollama server)
|
|
17
|
+
model: claude-sonnet-4-5-20250929
|
|
18
|
+
|
|
19
|
+
# Example configurations for other providers:
|
|
20
|
+
#
|
|
21
|
+
# --- Claude (Anthropic) ---
|
|
22
|
+
# provider: claude
|
|
23
|
+
# model: claude-sonnet-4-5-20250929
|
|
24
|
+
# Requires: ANTHROPIC_API_KEY environment variable
|
|
25
|
+
#
|
|
26
|
+
# --- Gemini (Google) ---
|
|
27
|
+
# provider: gemini
|
|
28
|
+
# model: gemini-2.0-flash
|
|
29
|
+
# Requires: GOOGLE_API_KEY environment variable
|
|
30
|
+
#
|
|
31
|
+
# --- Ollama (Local) ---
|
|
32
|
+
# provider: ollama
|
|
33
|
+
# model: llama3.2
|
|
34
|
+
# Requires: Ollama server running locally (no API key needed)
|
|
35
|
+
# Install: https://ollama.ai
|
|
36
|
+
|
|
37
|
+
# Optional: Custom template directory
|
|
38
|
+
# templates: ./templates
|
|
39
|
+
|
|
40
|
+
# Optional: Default output directory
|
|
41
|
+
# outputDir: ./output
|
|
42
|
+
|
|
43
|
+
# Optional: Logging configuration
|
|
44
|
+
# log:
|
|
45
|
+
# enabled: true
|
|
46
|
+
# path: ./yuuhitsu.log
|
|
47
|
+
`;
|
|
48
|
+
export const initCommand = new Command("init")
|
|
49
|
+
.description("Initialize a yuuhitsu config file")
|
|
50
|
+
.option("--force", "Overwrite existing config file")
|
|
51
|
+
.action(async (opts) => {
|
|
52
|
+
const configPath = join(process.cwd(), "yuuhitsu.config.yaml");
|
|
53
|
+
// Check if config already exists
|
|
54
|
+
if (existsSync(configPath) && !opts.force) {
|
|
55
|
+
process.stderr.write(chalk.red("Error:") + " Config file already exists: " + configPath + "\n" +
|
|
56
|
+
chalk.yellow("Hint:") + " Use --force to overwrite the existing file\n");
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
// Write config file
|
|
60
|
+
try {
|
|
61
|
+
writeFileSync(configPath, DEFAULT_CONFIG_TEMPLATE, "utf-8");
|
|
62
|
+
if (opts.force && existsSync(configPath)) {
|
|
63
|
+
process.stdout.write(chalk.green("✓") + " Config file overwritten: " + configPath + "\n");
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
process.stdout.write(chalk.green("✓") + " Config file created: " + configPath + "\n");
|
|
67
|
+
}
|
|
68
|
+
process.stdout.write("\n" +
|
|
69
|
+
"Next steps:\n" +
|
|
70
|
+
"1. Set your API key:\n" +
|
|
71
|
+
" - For Claude: export ANTHROPIC_API_KEY='your-key'\n" +
|
|
72
|
+
" - For Gemini: export GOOGLE_API_KEY='your-key'\n" +
|
|
73
|
+
" - For Ollama: Start Ollama server (ollama serve)\n" +
|
|
74
|
+
"2. Run a command: yuuhitsu translate --input file.md --lang ja\n");
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
process.stderr.write(chalk.red("Error:") + " Failed to create config file\n" +
|
|
78
|
+
chalk.yellow("Hint:") + " " + (err instanceof Error ? err.message : String(err)) + "\n");
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../../src/cli/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,uBAAuB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0C/B,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC;KAC3C,WAAW,CAAC,mCAAmC,CAAC;KAChD,MAAM,CAAC,SAAS,EAAE,gCAAgC,CAAC;KACnD,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;IACrB,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,sBAAsB,CAAC,CAAC;IAE/D,iCAAiC;IACjC,IAAI,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;QAC1C,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,+BAA+B,GAAG,UAAU,GAAG,IAAI;YACzE,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,+CAA+C,CACxE,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,oBAAoB;IACpB,IAAI,CAAC;QACH,aAAa,CAAC,UAAU,EAAE,uBAAuB,EAAE,OAAO,CAAC,CAAC;QAE5D,IAAI,IAAI,CAAC,KAAK,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YACzC,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,4BAA4B,GAAG,UAAU,GAAG,IAAI,CACpE,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,wBAAwB,GAAG,UAAU,GAAG,IAAI,CAChE,CAAC;QACJ,CAAC;QAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,IAAI;YACJ,eAAe;YACf,wBAAwB;YACxB,wDAAwD;YACxD,qDAAqD;YACrD,uDAAuD;YACvD,kEAAkE,CACnE,CAAC;IACJ,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,iCAAiC;YACvD,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CACxF,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC"}
|
|
File without changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"translate.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/translate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"translate.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/translate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASpC,eAAO,MAAM,gBAAgB,SA4FzB,CAAC"}
|
|
@@ -4,49 +4,76 @@ import chalk from "chalk";
|
|
|
4
4
|
import { loadConfig } from "../../config.js";
|
|
5
5
|
import { createProvider } from "../../provider/index.js";
|
|
6
6
|
import { translateFile } from "../../tasks/translate.js";
|
|
7
|
+
import { batchTranslate, isGlobPattern } from "../../tasks/batch-translate.js";
|
|
7
8
|
import { formatError, AppError } from "../../errors.js";
|
|
8
9
|
export const translateCommand = new Command("translate")
|
|
9
10
|
.description("Translate a Markdown document to another language")
|
|
10
|
-
.requiredOption("--input <file>", "Input Markdown file")
|
|
11
|
+
.requiredOption("--input <file>", "Input Markdown file or glob pattern")
|
|
11
12
|
.requiredOption("--lang <code>", "Target language code (e.g., ja, en, zh, ko)")
|
|
12
13
|
.option("--output <file>", "Output file path (default: <input>.<lang>.md)")
|
|
14
|
+
.option("--output-dir <dir>", "Output directory for batch translation (preserves directory structure)")
|
|
13
15
|
.action(async (opts, cmd) => {
|
|
14
16
|
const globalOpts = cmd.parent?.opts() ?? {};
|
|
15
17
|
const configPath = globalOpts.config ?? "./yuuhitsu.config.yaml";
|
|
16
18
|
const dryRun = globalOpts.dryRun ?? false;
|
|
17
19
|
const verbose = globalOpts.verbose ?? false;
|
|
18
20
|
try {
|
|
19
|
-
// Validate input file exists
|
|
20
|
-
if (!existsSync(opts.input)) {
|
|
21
|
-
throw new AppError(`Input file not found: ${opts.input}`, "Check the file path and try again.");
|
|
22
|
-
}
|
|
23
21
|
// Load config
|
|
24
22
|
const config = await loadConfig(configPath);
|
|
25
23
|
if (verbose) {
|
|
26
24
|
process.stderr.write(`${chalk.gray(`Provider: ${config.provider}, Model: ${config.model}`)}\n`);
|
|
27
25
|
}
|
|
28
|
-
//
|
|
29
|
-
if (
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
26
|
+
// Check if input is a glob pattern
|
|
27
|
+
if (isGlobPattern(opts.input)) {
|
|
28
|
+
// Batch translation mode
|
|
29
|
+
const provider = dryRun ? undefined : createProvider(config.provider, config.model);
|
|
30
|
+
if (dryRun) {
|
|
31
|
+
process.stdout.write(`${chalk.cyan("[dry-run]")} Batch translate mode:\n` +
|
|
32
|
+
` Pattern: ${opts.input}\n` +
|
|
33
|
+
` Language: ${opts.lang}\n` +
|
|
34
|
+
` Provider: ${config.provider}\n` +
|
|
35
|
+
` Model: ${config.model}\n` +
|
|
36
|
+
(opts.outputDir ? ` Output dir: ${opts.outputDir}\n` : "") +
|
|
37
|
+
`\n`);
|
|
38
|
+
}
|
|
39
|
+
await batchTranslate({
|
|
40
|
+
pattern: opts.input,
|
|
41
|
+
targetLang: opts.lang,
|
|
42
|
+
provider,
|
|
43
|
+
outputDir: opts.outputDir,
|
|
44
|
+
dryRun,
|
|
45
|
+
verbose,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
// Single file translation mode (original behavior)
|
|
50
|
+
// Validate input file exists
|
|
51
|
+
if (!existsSync(opts.input)) {
|
|
52
|
+
throw new AppError(`Input file not found: ${opts.input}`, "Check the file path and try again.");
|
|
53
|
+
}
|
|
54
|
+
// Dry-run mode
|
|
55
|
+
if (dryRun) {
|
|
56
|
+
const outputPath = opts.output || `${opts.input.replace(/\.md$/, "")}.${opts.lang}.md`;
|
|
57
|
+
process.stdout.write(`${chalk.cyan("[dry-run]")} Would translate:\n` +
|
|
58
|
+
` Input: ${opts.input}\n` +
|
|
59
|
+
` Output: ${outputPath}\n` +
|
|
60
|
+
` Language: ${opts.lang}\n` +
|
|
61
|
+
` Provider: ${config.provider}\n` +
|
|
62
|
+
` Model: ${config.model}\n`);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
// Create provider
|
|
66
|
+
const provider = createProvider(config.provider, config.model);
|
|
67
|
+
// Execute translation
|
|
68
|
+
const result = await translateFile({
|
|
69
|
+
provider,
|
|
70
|
+
inputPath: opts.input,
|
|
71
|
+
outputPath: opts.output,
|
|
72
|
+
targetLang: opts.lang,
|
|
73
|
+
});
|
|
74
|
+
process.stdout.write(`${chalk.green("✓")} Translated to ${result.outputPath}\n` +
|
|
75
|
+
` Tokens: ${result.usage.totalTokens} (${result.chunks} chunk${result.chunks > 1 ? "s" : ""})\n`);
|
|
38
76
|
}
|
|
39
|
-
// Create provider
|
|
40
|
-
const provider = createProvider(config.provider, config.model);
|
|
41
|
-
// Execute translation
|
|
42
|
-
const result = await translateFile({
|
|
43
|
-
provider,
|
|
44
|
-
inputPath: opts.input,
|
|
45
|
-
outputPath: opts.output,
|
|
46
|
-
targetLang: opts.lang,
|
|
47
|
-
});
|
|
48
|
-
process.stdout.write(`${chalk.green("✓")} Translated to ${result.outputPath}\n` +
|
|
49
|
-
` Tokens: ${result.usage.totalTokens} (${result.chunks} chunk${result.chunks > 1 ? "s" : ""})\n`);
|
|
50
77
|
}
|
|
51
78
|
catch (err) {
|
|
52
79
|
process.stderr.write(formatError(err) + "\n");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"translate.js","sourceRoot":"","sources":["../../../src/cli/commands/translate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAExD,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,OAAO,CAAC,WAAW,CAAC;KACrD,WAAW,CAAC,mDAAmD,CAAC;KAChE,cAAc,CAAC,gBAAgB,EAAE,
|
|
1
|
+
{"version":3,"file":"translate.js","sourceRoot":"","sources":["../../../src/cli/commands/translate.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/index.d.ts
CHANGED
|
File without changes
|
package/dist/cli/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAqBpC,QAAA,MAAM,OAAO,SAAgB,CAAC;AAqB9B,OAAO,EAAE,OAAO,EAAE,CAAC"}
|
package/dist/cli/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { join, dirname } from "path";
|
|
|
5
5
|
import { fileURLToPath } from "url";
|
|
6
6
|
import { formatError } from "../errors.js";
|
|
7
7
|
import { translateCommand } from "./commands/translate.js";
|
|
8
|
+
import { initCommand } from "./commands/init.js";
|
|
8
9
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
10
|
function getVersion() {
|
|
10
11
|
try {
|
|
@@ -24,6 +25,7 @@ program
|
|
|
24
25
|
.option("--dry-run", "Show what would be done without making API calls")
|
|
25
26
|
.option("--verbose", "Enable verbose output");
|
|
26
27
|
// Register commands
|
|
28
|
+
program.addCommand(initCommand);
|
|
27
29
|
program.addCommand(translateCommand);
|
|
28
30
|
program.parseAsync(process.argv).catch((err) => {
|
|
29
31
|
process.stderr.write(formatError(err) + "\n");
|
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAE1D,SAAS,UAAU;IACjB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CACpB,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,oBAAoB,CAAC,EAAE,OAAO,CAAC,CAC7D,CAAC;QACF,OAAO,GAAG,CAAC,OAAO,CAAC;IACrB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAC;IACjB,CAAC;AACH,CAAC;AAED,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,UAAU,CAAC;KAChB,WAAW,CACV,oDAAoD,CACrD;KACA,OAAO,CAAC,UAAU,EAAE,CAAC;KACrB,MAAM,CAAC,iBAAiB,EAAE,kBAAkB,EAAE,wBAAwB,CAAC;KACvE,MAAM,CAAC,WAAW,EAAE,kDAAkD,CAAC;KACvE,MAAM,CAAC,WAAW,EAAE,uBAAuB,CAAC,CAAC;AAEhD,oBAAoB;AACpB,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;AAErC,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IAC7C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;IAC9C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC;AAEH,OAAO,EAAE,OAAO,EAAE,CAAC"}
|
package/dist/config.d.ts
CHANGED
|
File without changes
|
package/dist/config.d.ts.map
CHANGED
|
File without changes
|
package/dist/config.js
CHANGED
|
File without changes
|
package/dist/config.js.map
CHANGED
|
File without changes
|
package/dist/errors.d.ts
CHANGED
|
File without changes
|
package/dist/errors.d.ts.map
CHANGED
|
File without changes
|
package/dist/errors.js
CHANGED
|
File without changes
|
package/dist/errors.js.map
CHANGED
|
File without changes
|
package/dist/logger.d.ts
CHANGED
|
File without changes
|
package/dist/logger.d.ts.map
CHANGED
|
File without changes
|
package/dist/logger.js
CHANGED
|
File without changes
|
package/dist/logger.js.map
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/dist/provider/claude.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/dist/provider/gemini.js
CHANGED
|
File without changes
|
|
File without changes
|
package/dist/provider/index.d.ts
CHANGED
|
File without changes
|
|
File without changes
|
package/dist/provider/index.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/dist/provider/ollama.js
CHANGED
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { AIProvider } from "../provider/index.js";
|
|
2
|
+
export interface BatchProgress {
|
|
3
|
+
total: number;
|
|
4
|
+
current: number;
|
|
5
|
+
succeeded: number;
|
|
6
|
+
failed: number;
|
|
7
|
+
skipped: number;
|
|
8
|
+
}
|
|
9
|
+
export interface BatchTranslateOptions {
|
|
10
|
+
pattern: string;
|
|
11
|
+
targetLang: string;
|
|
12
|
+
provider?: AIProvider;
|
|
13
|
+
outputDir?: string;
|
|
14
|
+
inputBase?: string;
|
|
15
|
+
onProgress?: (progress: BatchProgress) => void;
|
|
16
|
+
dryRun: boolean;
|
|
17
|
+
verbose?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface BatchResult {
|
|
20
|
+
total: number;
|
|
21
|
+
succeeded: number;
|
|
22
|
+
failed: number;
|
|
23
|
+
errors: Array<{
|
|
24
|
+
file: string;
|
|
25
|
+
error: string;
|
|
26
|
+
}>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Check if a string contains glob pattern characters
|
|
30
|
+
*/
|
|
31
|
+
export declare function isGlobPattern(input: string): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Generate output path for a translated file
|
|
34
|
+
*/
|
|
35
|
+
export declare function generateOutputPath(opts: {
|
|
36
|
+
inputPath: string;
|
|
37
|
+
targetLang: string;
|
|
38
|
+
outputDir?: string;
|
|
39
|
+
inputBase?: string;
|
|
40
|
+
explicitOutput?: string;
|
|
41
|
+
}): string;
|
|
42
|
+
/**
|
|
43
|
+
* Execute batch translation
|
|
44
|
+
*/
|
|
45
|
+
export declare function batchTranslate(opts: BatchTranslateOptions): Promise<BatchResult>;
|
|
46
|
+
//# sourceMappingURL=batch-translate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batch-translate.d.ts","sourceRoot":"","sources":["../../src/tasks/batch-translate.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAGvD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,UAAU,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,aAAa,KAAK,IAAI,CAAC;IAC/C,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,GAAG,MAAM,CAsCT;AA4CD;;GAEG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,qBAAqB,GAC1B,OAAO,CAAC,WAAW,CAAC,CAqHtB"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import fg from "fast-glob";
|
|
2
|
+
import { dirname, join, relative, parse, isAbsolute, sep } from "path";
|
|
3
|
+
import { mkdirSync } from "fs";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import { translateFile } from "./translate.js";
|
|
6
|
+
/**
|
|
7
|
+
* Check if a string contains glob pattern characters
|
|
8
|
+
*/
|
|
9
|
+
export function isGlobPattern(input) {
|
|
10
|
+
return /[*?[\]{}]/.test(input);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Generate output path for a translated file
|
|
14
|
+
*/
|
|
15
|
+
export function generateOutputPath(opts) {
|
|
16
|
+
const { inputPath, targetLang, outputDir, inputBase, explicitOutput } = opts;
|
|
17
|
+
// If explicit output is provided, use it
|
|
18
|
+
if (explicitOutput) {
|
|
19
|
+
return explicitOutput;
|
|
20
|
+
}
|
|
21
|
+
// If outputDir is provided, preserve directory structure
|
|
22
|
+
if (outputDir && inputBase) {
|
|
23
|
+
const relativePath = relative(inputBase, inputPath);
|
|
24
|
+
// Security: Prevent path traversal
|
|
25
|
+
// Reject paths that are absolute or try to escape via ".."
|
|
26
|
+
if (isAbsolute(relativePath)) {
|
|
27
|
+
throw new Error(`Path traversal detected: ${inputPath} is outside ${inputBase} (absolute path)`);
|
|
28
|
+
}
|
|
29
|
+
if (relativePath.startsWith(`..${sep}`) || relativePath === "..") {
|
|
30
|
+
throw new Error(`Path traversal detected: ${inputPath} tries to escape ${inputBase}`);
|
|
31
|
+
}
|
|
32
|
+
// Check for ".." in path segments
|
|
33
|
+
const segments = relativePath.split(sep);
|
|
34
|
+
if (segments.includes("..")) {
|
|
35
|
+
throw new Error(`Path traversal detected: ${inputPath} contains ".." segments`);
|
|
36
|
+
}
|
|
37
|
+
return join(outputDir, relativePath);
|
|
38
|
+
}
|
|
39
|
+
// Default: add language code before extension
|
|
40
|
+
const parsed = parse(inputPath);
|
|
41
|
+
const ext = parsed.ext || "";
|
|
42
|
+
const base = parsed.name;
|
|
43
|
+
const dir = parsed.dir;
|
|
44
|
+
return join(dir, `${base}.${targetLang}${ext}`);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Determine the base path for --input-dir calculation
|
|
48
|
+
*/
|
|
49
|
+
function determineInputBase(pattern, matchedFiles) {
|
|
50
|
+
// Extract the static prefix from the glob pattern
|
|
51
|
+
// e.g., "docs/en/**/*.md" -> "docs/en"
|
|
52
|
+
const staticPrefix = pattern.split(/[*?[\]{}]/)[0];
|
|
53
|
+
// If there's a static directory prefix, use it
|
|
54
|
+
if (staticPrefix && staticPrefix.includes("/")) {
|
|
55
|
+
// Remove trailing slash and return as-is (it's already a directory path)
|
|
56
|
+
return staticPrefix.replace(/\/$/, "");
|
|
57
|
+
}
|
|
58
|
+
// If no static prefix, find common base from matched files
|
|
59
|
+
if (matchedFiles.length === 0) {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
// Find the common directory prefix of all matched files
|
|
63
|
+
const dirs = matchedFiles.map(f => dirname(f));
|
|
64
|
+
const commonDir = dirs.reduce((acc, dir) => {
|
|
65
|
+
if (!acc)
|
|
66
|
+
return dir;
|
|
67
|
+
const accParts = acc.split("/");
|
|
68
|
+
const dirParts = dir.split("/");
|
|
69
|
+
const common = [];
|
|
70
|
+
for (let i = 0; i < Math.min(accParts.length, dirParts.length); i++) {
|
|
71
|
+
if (accParts[i] === dirParts[i]) {
|
|
72
|
+
common.push(accParts[i]);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return common.join("/") || ".";
|
|
79
|
+
});
|
|
80
|
+
return commonDir === "." ? undefined : commonDir;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Execute batch translation
|
|
84
|
+
*/
|
|
85
|
+
export async function batchTranslate(opts) {
|
|
86
|
+
const { pattern, targetLang, provider, outputDir, onProgress, dryRun, verbose } = opts;
|
|
87
|
+
// Match files using fast-glob
|
|
88
|
+
const matchedFiles = await fg(pattern, {
|
|
89
|
+
onlyFiles: true,
|
|
90
|
+
absolute: false,
|
|
91
|
+
});
|
|
92
|
+
if (matchedFiles.length === 0) {
|
|
93
|
+
throw new Error(`No files matched pattern: ${pattern}`);
|
|
94
|
+
}
|
|
95
|
+
// Determine input base for directory structure preservation
|
|
96
|
+
const inputBase = outputDir ? determineInputBase(pattern, matchedFiles) : undefined;
|
|
97
|
+
// Initialize progress
|
|
98
|
+
const progress = {
|
|
99
|
+
total: matchedFiles.length,
|
|
100
|
+
current: 0,
|
|
101
|
+
succeeded: 0,
|
|
102
|
+
failed: 0,
|
|
103
|
+
skipped: 0,
|
|
104
|
+
};
|
|
105
|
+
const errors = [];
|
|
106
|
+
// Process each file
|
|
107
|
+
for (let i = 0; i < matchedFiles.length; i++) {
|
|
108
|
+
const inputPath = matchedFiles[i];
|
|
109
|
+
progress.current = i + 1;
|
|
110
|
+
const outputPath = generateOutputPath({
|
|
111
|
+
inputPath,
|
|
112
|
+
targetLang,
|
|
113
|
+
outputDir,
|
|
114
|
+
inputBase,
|
|
115
|
+
});
|
|
116
|
+
// Show progress
|
|
117
|
+
const progressPrefix = chalk.cyan(`[${progress.current}/${progress.total}]`);
|
|
118
|
+
if (dryRun) {
|
|
119
|
+
process.stdout.write(`${progressPrefix} Would translate:\n` +
|
|
120
|
+
` Input: ${inputPath}\n` +
|
|
121
|
+
` Output: ${outputPath}\n`);
|
|
122
|
+
progress.succeeded++;
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
if (!provider) {
|
|
126
|
+
throw new Error("Provider is required when dryRun is false");
|
|
127
|
+
}
|
|
128
|
+
try {
|
|
129
|
+
process.stdout.write(`${progressPrefix} Translating ${inputPath}...\n`);
|
|
130
|
+
// Ensure output directory exists
|
|
131
|
+
const outputDirPath = dirname(outputPath);
|
|
132
|
+
mkdirSync(outputDirPath, { recursive: true });
|
|
133
|
+
// Translate the file
|
|
134
|
+
const result = await translateFile({
|
|
135
|
+
provider,
|
|
136
|
+
inputPath,
|
|
137
|
+
outputPath,
|
|
138
|
+
targetLang,
|
|
139
|
+
});
|
|
140
|
+
process.stdout.write(`${chalk.green("✓")} Translated to ${result.outputPath}\n` +
|
|
141
|
+
(verbose ? ` Tokens: ${result.usage.totalTokens} (${result.chunks} chunk${result.chunks > 1 ? "s" : ""})\n` : ""));
|
|
142
|
+
progress.succeeded++;
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
146
|
+
process.stderr.write(`${chalk.red("✗")} Failed to translate ${inputPath}: ${errorMsg}\n`);
|
|
147
|
+
errors.push({ file: inputPath, error: errorMsg });
|
|
148
|
+
progress.failed++;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// Call progress callback
|
|
152
|
+
if (onProgress) {
|
|
153
|
+
onProgress(progress);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// Print summary
|
|
157
|
+
const summaryColor = progress.failed > 0 ? chalk.yellow : chalk.green;
|
|
158
|
+
process.stdout.write(`\n` +
|
|
159
|
+
summaryColor("Summary:\n") +
|
|
160
|
+
` Total: ${progress.total}\n` +
|
|
161
|
+
` Succeeded: ${progress.succeeded}\n` +
|
|
162
|
+
` Failed: ${progress.failed}\n`);
|
|
163
|
+
if (errors.length > 0) {
|
|
164
|
+
process.stderr.write(`\n${chalk.red("Errors:")}\n` +
|
|
165
|
+
errors.map(e => ` ${e.file}: ${e.error}`).join("\n") + "\n");
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
total: progress.total,
|
|
169
|
+
succeeded: progress.succeeded,
|
|
170
|
+
failed: progress.failed,
|
|
171
|
+
errors,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=batch-translate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batch-translate.js","sourceRoot":"","sources":["../../src/tasks/batch-translate.ts"],"names":[],"mappings":"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"}
|
package/dist/tasks/stream.d.ts
CHANGED
|
File without changes
|
|
File without changes
|
package/dist/tasks/stream.js
CHANGED
|
File without changes
|
package/dist/tasks/stream.js.map
CHANGED
|
File without changes
|
|
@@ -1,4 +1,28 @@
|
|
|
1
1
|
import type { AIProvider } from "../provider/interface.js";
|
|
2
|
+
interface FrontmatterSeparation {
|
|
3
|
+
frontmatter: string | null;
|
|
4
|
+
body: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Separate frontmatter from Markdown content
|
|
8
|
+
* Handles: LF, CRLF, no trailing newline after closing ---, trailing spaces, empty frontmatter
|
|
9
|
+
* @param content - Full Markdown content
|
|
10
|
+
* @returns Object with separated frontmatter and body
|
|
11
|
+
*/
|
|
12
|
+
export declare function separateFrontmatter(content: string): FrontmatterSeparation;
|
|
13
|
+
interface CodeProtection {
|
|
14
|
+
text: string;
|
|
15
|
+
map: Map<string, string>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Replace fenced code blocks and inline code with placeholders.
|
|
19
|
+
* Fenced blocks (``` or ````+) take priority over inline code.
|
|
20
|
+
*/
|
|
21
|
+
export declare function protectCodeBlocks(content: string): CodeProtection;
|
|
22
|
+
/**
|
|
23
|
+
* Restore placeholders back to original code blocks/inline code.
|
|
24
|
+
*/
|
|
25
|
+
export declare function restoreCodeBlocks(content: string, map: Map<string, string>): string;
|
|
2
26
|
export interface TranslateOptions {
|
|
3
27
|
provider: AIProvider;
|
|
4
28
|
inputPath: string;
|
|
@@ -16,4 +40,5 @@ export interface TranslateResult {
|
|
|
16
40
|
chunks: number;
|
|
17
41
|
}
|
|
18
42
|
export declare function translateFile(options: TranslateOptions): Promise<TranslateResult>;
|
|
43
|
+
export {};
|
|
19
44
|
//# sourceMappingURL=translate.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"translate.d.ts","sourceRoot":"","sources":["../../src/tasks/translate.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAe,MAAM,0BAA0B,CAAC;AAIxE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,UAAU,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE;QACL,YAAY,EAAE,MAAM,CAAC;QACrB,gBAAgB,EAAE,MAAM,CAAC;QACzB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,MAAM,EAAE,MAAM,CAAC;CAChB;
|
|
1
|
+
{"version":3,"file":"translate.d.ts","sourceRoot":"","sources":["../../src/tasks/translate.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAe,MAAM,0BAA0B,CAAC;AAIxE,UAAU,qBAAqB;IAC7B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,qBAAqB,CAmB1E;AAED,UAAU,cAAc;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC1B;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAqBjE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CASnF;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,UAAU,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE;QACL,YAAY,EAAE,MAAM,CAAC;QACrB,gBAAgB,EAAE,MAAM,CAAC;QACzB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,MAAM,EAAE,MAAM,CAAC;CAChB;AAuFD,wBAAsB,aAAa,CACjC,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,eAAe,CAAC,CAkE1B"}
|
package/dist/tasks/translate.js
CHANGED
|
@@ -1,6 +1,65 @@
|
|
|
1
1
|
import { readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
2
2
|
import { dirname, basename, extname, join } from "path";
|
|
3
3
|
const CHUNK_SIZE = 50 * 1024; // 50KB
|
|
4
|
+
/**
|
|
5
|
+
* Separate frontmatter from Markdown content
|
|
6
|
+
* Handles: LF, CRLF, no trailing newline after closing ---, trailing spaces, empty frontmatter
|
|
7
|
+
* @param content - Full Markdown content
|
|
8
|
+
* @returns Object with separated frontmatter and body
|
|
9
|
+
*/
|
|
10
|
+
export function separateFrontmatter(content) {
|
|
11
|
+
// Normalize CRLF to LF for regex matching
|
|
12
|
+
const normalized = content.replace(/\r\n/g, "\n");
|
|
13
|
+
// Match frontmatter (two alternations to keep closing --- on its own line):
|
|
14
|
+
// Case 1: non-empty body: ^---\n ... \n---[ \t]*(\n|$)
|
|
15
|
+
// Case 2: empty body: ^---\n---[ \t]*(\n|$)
|
|
16
|
+
// Using alternation avoids the \n? ambiguity that allows --- to match mid-line.
|
|
17
|
+
const frontmatterRegex = /^---\n([\s\S]*?)\n---[ \t]*(\n|$)|^---\n---[ \t]*(\n|$)/;
|
|
18
|
+
const match = normalized.match(frontmatterRegex);
|
|
19
|
+
if (match) {
|
|
20
|
+
const matchedLength = match[0].length;
|
|
21
|
+
const frontmatter = normalized.slice(0, matchedLength);
|
|
22
|
+
const body = normalized.slice(matchedLength);
|
|
23
|
+
return { frontmatter, body };
|
|
24
|
+
}
|
|
25
|
+
return { frontmatter: null, body: normalized };
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Replace fenced code blocks and inline code with placeholders.
|
|
29
|
+
* Fenced blocks (``` or ````+) take priority over inline code.
|
|
30
|
+
*/
|
|
31
|
+
export function protectCodeBlocks(content) {
|
|
32
|
+
const map = new Map();
|
|
33
|
+
let blockIndex = 0;
|
|
34
|
+
let inlineIndex = 0;
|
|
35
|
+
// Step 1: Replace fenced code blocks (4+ backticks before 3-backtick blocks)
|
|
36
|
+
// Matches ````+ ... ```` or ``` ... ``` (non-greedy, multiline)
|
|
37
|
+
let result = content.replace(/(`{3,})[^\n]*\n[\s\S]*?\1[ \t]*(\n|$)/g, (match) => {
|
|
38
|
+
const placeholder = `__CODE_BLOCK_${blockIndex++}__`;
|
|
39
|
+
map.set(placeholder, match);
|
|
40
|
+
return placeholder + "\n";
|
|
41
|
+
});
|
|
42
|
+
// Step 2: Replace inline code (single backtick, not within code blocks)
|
|
43
|
+
result = result.replace(/`([^`\n]+)`/g, (match) => {
|
|
44
|
+
const placeholder = `__INLINE_CODE_${inlineIndex++}__`;
|
|
45
|
+
map.set(placeholder, match);
|
|
46
|
+
return placeholder;
|
|
47
|
+
});
|
|
48
|
+
return { text: result, map };
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Restore placeholders back to original code blocks/inline code.
|
|
52
|
+
*/
|
|
53
|
+
export function restoreCodeBlocks(content, map) {
|
|
54
|
+
let result = content;
|
|
55
|
+
// Restore fenced code blocks (may have trailing newline added by protect)
|
|
56
|
+
for (const [placeholder, original] of map.entries()) {
|
|
57
|
+
// The placeholder may appear with or without trailing newline
|
|
58
|
+
result = result.split(placeholder + "\n").join(original);
|
|
59
|
+
result = result.split(placeholder).join(original);
|
|
60
|
+
}
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
4
63
|
const DEFAULT_TEMPLATE = `You are a professional translator. Translate the following Markdown document to {{targetLanguage}}.
|
|
5
64
|
|
|
6
65
|
Rules:
|
|
@@ -10,16 +69,31 @@ Rules:
|
|
|
10
69
|
- Maintain the same document structure
|
|
11
70
|
- Produce natural, fluent text in the target language
|
|
12
71
|
|
|
72
|
+
CRITICAL - Link and URL preservation:
|
|
73
|
+
- NEVER modify any URLs or link paths. Keep all href/src values exactly as-is.
|
|
74
|
+
- NEVER change internal link paths (e.g., /ja/..., /en/..., ./relative-path). Preserve them verbatim.
|
|
75
|
+
- NEVER convert external URLs to different language versions.
|
|
76
|
+
- If the source has [text](/ja/changelog), the output must keep the same path, only translate the link text if needed.
|
|
77
|
+
- Example: [紹介](/ja/intro) → translate "紹介" but keep "/ja/intro" unchanged
|
|
78
|
+
- Example: [MDN](https://developer.mozilla.org/ja/) → keep the /ja/ in URL, translate "MDN" if needed
|
|
79
|
+
|
|
13
80
|
Additional rules for Japanese translation:
|
|
14
81
|
- Use full-width punctuation: 。、?! (not .,?!)
|
|
15
82
|
- Add half-width spaces around English words and numbers (e.g., "Vela とは", "NGSIv2 は", "3 つの")
|
|
16
83
|
- Use natural Japanese terms for technical words where appropriate (e.g., "registration" → "登録", "subscription" → "サブスクリプション")
|
|
17
84
|
- Keep product names, proper nouns, and abbreviations unchanged (e.g., Vela, FIWARE, NGSIv2, NGSI-LD, MCP)`;
|
|
18
|
-
function buildPrompt(content, targetLang, templateContent) {
|
|
85
|
+
function buildPrompt(content, targetLang, hasPlaceholders, templateContent) {
|
|
19
86
|
const template = templateContent || DEFAULT_TEMPLATE;
|
|
20
|
-
|
|
87
|
+
let systemPrompt = template
|
|
21
88
|
.replace(/\{\{targetLanguage\}\}/g, targetLang)
|
|
22
89
|
.replace(/\{\{content\}\}/g, "");
|
|
90
|
+
if (hasPlaceholders) {
|
|
91
|
+
systemPrompt +=
|
|
92
|
+
"\n\nIMPORTANT - Placeholder preservation:\n" +
|
|
93
|
+
"- Tokens matching __CODE_BLOCK_N__ or __INLINE_CODE_N__ are placeholders for code blocks/inline code.\n" +
|
|
94
|
+
"- Output them VERBATIM and UNCHANGED. Do NOT translate, modify, or remove them.\n" +
|
|
95
|
+
"- Example: if input has __CODE_BLOCK_0__, output must contain __CODE_BLOCK_0__ exactly.";
|
|
96
|
+
}
|
|
23
97
|
return [
|
|
24
98
|
{ role: "system", content: systemPrompt },
|
|
25
99
|
{ role: "user", content },
|
|
@@ -74,12 +148,17 @@ export async function translateFile(options) {
|
|
|
74
148
|
const resolvedOutput = resolveOutputPath(inputPath, targetLang, options.outputPath);
|
|
75
149
|
// Ensure output directory exists
|
|
76
150
|
mkdirSync(dirname(resolvedOutput), { recursive: true });
|
|
77
|
-
//
|
|
78
|
-
const
|
|
151
|
+
// Separate frontmatter from body
|
|
152
|
+
const { frontmatter, body } = separateFrontmatter(content);
|
|
153
|
+
// Protect code blocks: replace with placeholders before sending to LLM
|
|
154
|
+
const { text: protectedBody, map: codeMap } = protectCodeBlocks(body);
|
|
155
|
+
const hasPlaceholders = codeMap.size > 0;
|
|
156
|
+
// Split body into chunks if needed (frontmatter is never sent to LLM)
|
|
157
|
+
const chunks = splitIntoChunks(protectedBody);
|
|
79
158
|
const translatedParts = [];
|
|
80
159
|
let totalUsage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
|
|
81
160
|
for (const chunk of chunks) {
|
|
82
|
-
const messages = buildPrompt(chunk, targetLang, templateContent);
|
|
161
|
+
const messages = buildPrompt(chunk, targetLang, hasPlaceholders, templateContent);
|
|
83
162
|
const response = await provider.chat({
|
|
84
163
|
model: "",
|
|
85
164
|
messages,
|
|
@@ -89,7 +168,13 @@ export async function translateFile(options) {
|
|
|
89
168
|
totalUsage.completionTokens += response.usage.completionTokens;
|
|
90
169
|
totalUsage.totalTokens += response.usage.totalTokens;
|
|
91
170
|
}
|
|
92
|
-
|
|
171
|
+
// Restore code blocks from placeholders
|
|
172
|
+
const translatedBodyWithPlaceholders = translatedParts.join("");
|
|
173
|
+
const translatedBody = restoreCodeBlocks(translatedBodyWithPlaceholders, codeMap);
|
|
174
|
+
// Recombine frontmatter with translated body
|
|
175
|
+
const translatedContent = frontmatter
|
|
176
|
+
? frontmatter + translatedBody
|
|
177
|
+
: translatedBody;
|
|
93
178
|
// Write output
|
|
94
179
|
writeFileSync(resolvedOutput, translatedContent, "utf-8");
|
|
95
180
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"translate.js","sourceRoot":"","sources":["../../src/tasks/translate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAGxD,MAAM,UAAU,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,OAAO;
|
|
1
|
+
{"version":3,"file":"translate.js","sourceRoot":"","sources":["../../src/tasks/translate.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geolonia/yuuhitsu",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "右筆 (Yuuhitsu) - AI-powered document operations CLI. Translate, generate, and sync documents using Claude, Gemini, or Ollama.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -14,7 +14,12 @@
|
|
|
14
14
|
"test:watch": "vitest",
|
|
15
15
|
"lint": "tsc --noEmit"
|
|
16
16
|
},
|
|
17
|
-
"keywords": [
|
|
17
|
+
"keywords": [
|
|
18
|
+
"ai",
|
|
19
|
+
"cli",
|
|
20
|
+
"translation",
|
|
21
|
+
"documentation"
|
|
22
|
+
],
|
|
18
23
|
"license": "MIT",
|
|
19
24
|
"files": [
|
|
20
25
|
"dist",
|
|
@@ -25,16 +30,17 @@
|
|
|
25
30
|
"dependencies": {
|
|
26
31
|
"@anthropic-ai/sdk": "^0.39.0",
|
|
27
32
|
"@google/genai": "^0.7.0",
|
|
28
|
-
"
|
|
33
|
+
"chalk": "^5.4.0",
|
|
29
34
|
"commander": "^13.0.0",
|
|
30
|
-
"yaml": "^2.7.0",
|
|
31
35
|
"dotenv": "^16.4.0",
|
|
32
|
-
"
|
|
36
|
+
"fast-glob": "^3.3.3",
|
|
37
|
+
"openai": "^4.77.0",
|
|
38
|
+
"yaml": "^2.7.0"
|
|
33
39
|
},
|
|
34
40
|
"devDependencies": {
|
|
35
|
-
"
|
|
36
|
-
"typescript": "^5.7.0",
|
|
41
|
+
"@types/node": "^22.0.0",
|
|
37
42
|
"tsx": "^4.19.0",
|
|
38
|
-
"
|
|
43
|
+
"typescript": "^5.9.3",
|
|
44
|
+
"vitest": "^3.0.0"
|
|
39
45
|
}
|
|
40
46
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -7,6 +7,14 @@ Rules:
|
|
|
7
7
|
- Maintain the same document structure
|
|
8
8
|
- Produce natural, fluent text in the target language
|
|
9
9
|
|
|
10
|
+
CRITICAL - Link and URL preservation:
|
|
11
|
+
- NEVER modify any URLs or link paths. Keep all href/src values exactly as-is.
|
|
12
|
+
- NEVER change internal link paths (e.g., /ja/..., /en/..., ./relative-path). Preserve them verbatim.
|
|
13
|
+
- NEVER convert external URLs to different language versions.
|
|
14
|
+
- If the source has [text](/ja/changelog), the output must keep the same path, only translate the link text if needed.
|
|
15
|
+
- Example: [紹介](/ja/intro) → translate "紹介" but keep "/ja/intro" unchanged
|
|
16
|
+
- Example: [MDN](https://developer.mozilla.org/ja/) → keep the /ja/ in URL, translate "MDN" if needed
|
|
17
|
+
|
|
10
18
|
Additional rules for Japanese translation:
|
|
11
19
|
- Use full-width punctuation: 。、?! (not .,?!)
|
|
12
20
|
- Add half-width spaces around English words and numbers (e.g., "Vela とは", "NGSIv2 は", "3 つの")
|