@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.
Files changed (65) hide show
  1. package/LICENSE +0 -0
  2. package/README.md +0 -0
  3. package/dist/cli/commands/init.d.ts +3 -0
  4. package/dist/cli/commands/init.d.ts.map +1 -0
  5. package/dist/cli/commands/init.js +82 -0
  6. package/dist/cli/commands/init.js.map +1 -0
  7. package/dist/cli/commands/translate.d.ts +0 -0
  8. package/dist/cli/commands/translate.d.ts.map +1 -1
  9. package/dist/cli/commands/translate.js +53 -26
  10. package/dist/cli/commands/translate.js.map +1 -1
  11. package/dist/cli/index.d.ts +0 -0
  12. package/dist/cli/index.d.ts.map +1 -1
  13. package/dist/cli/index.js +2 -0
  14. package/dist/cli/index.js.map +1 -1
  15. package/dist/config.d.ts +0 -0
  16. package/dist/config.d.ts.map +0 -0
  17. package/dist/config.js +0 -0
  18. package/dist/config.js.map +0 -0
  19. package/dist/errors.d.ts +0 -0
  20. package/dist/errors.d.ts.map +0 -0
  21. package/dist/errors.js +0 -0
  22. package/dist/errors.js.map +0 -0
  23. package/dist/logger.d.ts +0 -0
  24. package/dist/logger.d.ts.map +0 -0
  25. package/dist/logger.js +0 -0
  26. package/dist/logger.js.map +0 -0
  27. package/dist/provider/claude.d.ts +0 -0
  28. package/dist/provider/claude.d.ts.map +0 -0
  29. package/dist/provider/claude.js +0 -0
  30. package/dist/provider/claude.js.map +0 -0
  31. package/dist/provider/gemini.d.ts +0 -0
  32. package/dist/provider/gemini.d.ts.map +0 -0
  33. package/dist/provider/gemini.js +0 -0
  34. package/dist/provider/gemini.js.map +0 -0
  35. package/dist/provider/index.d.ts +0 -0
  36. package/dist/provider/index.d.ts.map +0 -0
  37. package/dist/provider/index.js +0 -0
  38. package/dist/provider/index.js.map +0 -0
  39. package/dist/provider/interface.d.ts +0 -0
  40. package/dist/provider/interface.d.ts.map +0 -0
  41. package/dist/provider/interface.js +0 -0
  42. package/dist/provider/interface.js.map +0 -0
  43. package/dist/provider/ollama.d.ts +0 -0
  44. package/dist/provider/ollama.d.ts.map +0 -0
  45. package/dist/provider/ollama.js +0 -0
  46. package/dist/provider/ollama.js.map +0 -0
  47. package/dist/tasks/batch-translate.d.ts +46 -0
  48. package/dist/tasks/batch-translate.d.ts.map +1 -0
  49. package/dist/tasks/batch-translate.js +174 -0
  50. package/dist/tasks/batch-translate.js.map +1 -0
  51. package/dist/tasks/stream.d.ts +0 -0
  52. package/dist/tasks/stream.d.ts.map +0 -0
  53. package/dist/tasks/stream.js +0 -0
  54. package/dist/tasks/stream.js.map +0 -0
  55. package/dist/tasks/translate.d.ts +25 -0
  56. package/dist/tasks/translate.d.ts.map +1 -1
  57. package/dist/tasks/translate.js +91 -6
  58. package/dist/tasks/translate.js.map +1 -1
  59. package/package.json +14 -8
  60. package/src/templates/fix-links.md +0 -0
  61. package/src/templates/generate-docs.md +0 -0
  62. package/src/templates/generate-tests.md +0 -0
  63. package/src/templates/research.md +0 -0
  64. package/src/templates/sync-docs.md +0 -0
  65. package/src/templates/translate.md +8 -0
package/LICENSE CHANGED
File without changes
package/README.md CHANGED
File without changes
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare const initCommand: Command;
3
+ //# sourceMappingURL=init.d.ts.map
@@ -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;AAQpC,eAAO,MAAM,gBAAgB,SA8DzB,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
- // Dry-run mode
29
- if (dryRun) {
30
- const outputPath = opts.output || `${opts.input.replace(/\.md$/, "")}.${opts.lang}.md`;
31
- process.stdout.write(`${chalk.cyan("[dry-run]")} Would translate:\n` +
32
- ` Input: ${opts.input}\n` +
33
- ` Output: ${outputPath}\n` +
34
- ` Language: ${opts.lang}\n` +
35
- ` Provider: ${config.provider}\n` +
36
- ` Model: ${config.model}\n`);
37
- return;
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":"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"}
1
+ {"version":3,"file":"translate.js","sourceRoot":"","sources":["../../../src/cli/commands/translate.ts"],"names":[],"mappings":"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"}
File without changes
@@ -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;AAoBpC,QAAA,MAAM,OAAO,SAAgB,CAAC;AAoB9B,OAAO,EAAE,OAAO,EAAE,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");
@@ -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;AAE3D,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,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"}
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
File without changes
package/dist/config.js CHANGED
File without changes
File without changes
package/dist/errors.d.ts CHANGED
File without changes
File without changes
package/dist/errors.js CHANGED
File without changes
File without changes
package/dist/logger.d.ts CHANGED
File without changes
File without changes
package/dist/logger.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
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
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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"}
File without changes
File without changes
File without changes
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;AAsED,wBAAsB,aAAa,CACjC,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,eAAe,CAAC,CAoD1B"}
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"}
@@ -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
- const systemPrompt = template
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
- // Split into chunks if needed
78
- const chunks = splitIntoChunks(content);
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
- const translatedContent = translatedParts.join("");
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":"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"}
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.0",
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": ["ai", "cli", "translation", "documentation"],
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
- "openai": "^4.77.0",
33
+ "chalk": "^5.4.0",
29
34
  "commander": "^13.0.0",
30
- "yaml": "^2.7.0",
31
35
  "dotenv": "^16.4.0",
32
- "chalk": "^5.4.0"
36
+ "fast-glob": "^3.3.3",
37
+ "openai": "^4.77.0",
38
+ "yaml": "^2.7.0"
33
39
  },
34
40
  "devDependencies": {
35
- "vitest": "^3.0.0",
36
- "typescript": "^5.7.0",
41
+ "@types/node": "^22.0.0",
37
42
  "tsx": "^4.19.0",
38
- "@types/node": "^22.0.0"
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 つの")