UNPKG

i18n-ai-translate

Version:

AI-powered localization CLI, Node library, and GitHub Action. Translate i18next JSON, Gettext PO, Java .properties, and iOS .strings with ChatGPT, Claude, Gemini, or local Ollama models.

194 lines 9.66 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = buildDiffCommand; const constants_1 = require("./constants"); const commander_1 = require("commander"); const cache_1 = require("./cache"); const glossary_1 = require("./glossary"); const utils_1 = require("./utils"); const cli_helpers_1 = require("./cli_helpers"); const translate_directory_1 = require("./translate_directory"); const translate_file_1 = require("./translate_file"); const chat_pool_1 = __importDefault(require("./chat_pool")); const rate_limiter_1 = __importDefault(require("./rate_limiter")); const fs_1 = __importStar(require("fs")); const path_1 = __importDefault(require("path")); /** * Builds the diff command for comparing i18n files or directories. * @returns the diff command with its options and action. */ function buildDiffCommand() { return new commander_1.Command("diff") .requiredOption("-b, --before <fileOrDirectoryBefore>", "Source i18n file or directory before changes, in the jsons/ directory if a relative path is given") .requiredOption("-a, --after <fileOrDirectoryAfter>", "Source i18n file or directory after changes, in the jsons/ directory if a relative path is given") .requiredOption("-l, --input-language <inputLanguageCode>", "The input language's code, in ISO6391 (e.g. en, fr)") .requiredOption("-e, --engine <engine>", constants_1.CLI_HELP.Engine) .option("-m, --model <model>", constants_1.CLI_HELP.Model) .option("-r, --rate-limit-ms <rateLimitMs>", constants_1.CLI_HELP.RateLimit) .option("-k, --api-key <API key>", "API key") .option("-h, --host <hostIP:port>", constants_1.CLI_HELP.OllamaHost) .option("--ensure-changed-translation", constants_1.CLI_HELP.EnsureChangedTranslation, false) .option("-p, --templated-string-prefix <prefix>", "Prefix for templated strings", constants_1.DEFAULT_TEMPLATED_STRING_PREFIX) .option("-s, --templated-string-suffix <suffix>", "Suffix for templated strings", constants_1.DEFAULT_TEMPLATED_STRING_SUFFIX) .option("-n, --batch-size <batchSize>", constants_1.CLI_HELP.BatchSize) .option("--skip-translation-verification", constants_1.CLI_HELP.SkipTranslationVerification, false) .option("--skip-styling-verification", constants_1.CLI_HELP.SkipStylingVerification, false) .option("--override-prompt <path to JSON file>", constants_1.CLI_HELP.OverridePromptFile) .option("--verbose", constants_1.CLI_HELP.Verbose, false) .option("--prompt-mode <prompt-mode>", constants_1.CLI_HELP.PromptMode) .option("--batch-max-tokens <batch-max-tokens>", constants_1.CLI_HELP.MaxTokens) .option("--dry-run", constants_1.CLI_HELP.DryRun, false) .option("--no-continue-on-error", constants_1.CLI_HELP.NoContinueOnError) .option("--concurrency <concurrency>", constants_1.CLI_HELP.Concurrency) .option("--context <context>", constants_1.CLI_HELP.Context) .option("--exclude-languages [language codes...]", constants_1.CLI_HELP.ExcludeLanguages) .option("--tokens-per-minute <tpm>", constants_1.CLI_HELP.TokensPerMinute) .option("--file-format <format>", constants_1.CLI_HELP.FileFormat) .option("--cache [path]", constants_1.CLI_HELP.Cache) .option("--glossary <path>", constants_1.CLI_HELP.Glossary) .action(async (options) => { const modelArgs = (0, cli_helpers_1.processModelArgs)(options); // Shared pool + limiter mirroring cli_translate.ts. Diff // currently has a single run per invocation (no language // fan-out here), but plumbing it through keeps the option // shape symmetric and prevents surprises if diff grows a // --language-concurrency later. const sharedRateLimiter = new rate_limiter_1.default(modelArgs.rateLimitMs, Boolean(options.verbose), modelArgs.tokensPerMinute); const sharedPool = chat_pool_1.default.create({ apiKey: modelArgs.apiKey, chatParams: modelArgs.chatParams, concurrency: Math.max(1, modelArgs.concurrency), engine: options.engine, host: modelArgs.host, model: modelArgs.model, rateLimiter: sharedRateLimiter, }); let cachePath; let cache; if (options.cache) { const resolvedPath = typeof options.cache === "string" ? options.cache : cache_1.DEFAULT_CACHE_PATH; cachePath = resolvedPath; cache = (0, cache_1.loadCache)(resolvedPath); } let glossary; if (options.glossary) { try { glossary = (0, glossary_1.loadGlossary)(options.glossary); } catch (e) { (0, utils_1.printError)(`${e}`); process.exit(2); } } const sharedOptions = { ...modelArgs, cache, context: options.context, continueOnError: options.continueOnError, ensureChangedTranslation: options.ensureChangedTranslation, excludeLanguages: options.excludeLanguages, format: options.fileFormat, glossary, pool: sharedPool, rateLimiter: sharedRateLimiter, skipStylingVerification: options.skipStylingVerification, skipTranslationVerification: options.skipTranslationVerification, templatedStringPrefix: options.templatedStringPrefix, templatedStringSuffix: options.templatedStringSuffix, verbose: options.verbose, }; let overridePrompt; if (options.overridePrompt) { overridePrompt = (0, cli_helpers_1.processOverridePromptFile)(options.overridePrompt); } let dryRun; if (options.dryRun) { dryRun = { basePath: (0, fs_1.mkdtempSync)(`/tmp/i18n-ai-translate-${new Date().toISOString().replace(/[:.]/g, "-")}-`), }; } const beforeInputPath = (0, utils_1.resolveInputPath)(options.before); const afterInputPath = (0, utils_1.resolveInputPath)(options.after); if (fs_1.default.statSync(beforeInputPath).isFile() !== fs_1.default.statSync(afterInputPath).isFile()) { (0, utils_1.printError)("--before and --after arguments must be both files or both directories"); return; } if (fs_1.default.statSync(beforeInputPath).isFile()) { // Ensure they're in the same path if (path_1.default.dirname(beforeInputPath) !== path_1.default.dirname(afterInputPath)) { (0, utils_1.printError)("Input files are not in the same directory"); return; } await (0, translate_file_1.translateFileDiff)({ ...sharedOptions, dryRun, engine: options.engine, inputAfterFileOrPath: afterInputPath, inputBeforeFileOrPath: beforeInputPath, inputLanguageCode: options.inputLanguage, overridePrompt, }); } else { await (0, translate_directory_1.translateDirectoryDiff)({ ...sharedOptions, baseDirectory: path_1.default.resolve(beforeInputPath, ".."), dryRun, engine: options.engine, inputFolderNameAfter: afterInputPath, inputFolderNameBefore: beforeInputPath, inputLanguageCode: options.inputLanguage, overridePrompt, }); } // Persist the translation memory after the diff completes. // Dry-run is a no-write preview, so leave the cache untouched. if (cache && cachePath && !options.dryRun) { (0, cache_1.saveCache)(cachePath, cache); if (options.verbose) { (0, utils_1.printInfo)(`Wrote translation cache to ${cachePath}`); } } }); } //# sourceMappingURL=cli_diff.js.map