{"id":28892830,"url":"https://github.com/datanoisetv/translator-ai","last_synced_at":"2025-07-19T08:33:45.289Z","repository":{"id":300187108,"uuid":"1005459829","full_name":"DatanoiseTV/translator-ai","owner":"DatanoiseTV","description":"🌐 Multi-provider AI translation tool for JSON i18n files. Features: Google Gemini \u0026 Ollama support, incremental caching, multi-file deduplication, MCP server, batch processing, and cross-platform compatibility. Ideal for developers managing multilingual applications.","archived":false,"fork":false,"pushed_at":"2025-06-20T19:24:56.000Z","size":334,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-07-08T16:15:00.670Z","etag":null,"topics":["ai","cli","deepseek","gemini-api","i18n","internationalization","json","mcp","mcp-server","multi-language","nodejs","ollama","ollama-api","translation","translations","typescript-library"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/translator-ai","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DatanoiseTV.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-06-20T09:05:42.000Z","updated_at":"2025-06-20T19:24:31.000Z","dependencies_parsed_at":"2025-06-20T12:12:13.725Z","dependency_job_id":"b78283fe-f251-49f3-a744-03a1f0ea0c58","html_url":"https://github.com/DatanoiseTV/translator-ai","commit_stats":null,"previous_names":["datanoisetv/translator-gemini","datanoisetv/translator-ai"],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/DatanoiseTV/translator-ai","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DatanoiseTV%2Ftranslator-ai","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DatanoiseTV%2Ftranslator-ai/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DatanoiseTV%2Ftranslator-ai/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DatanoiseTV%2Ftranslator-ai/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DatanoiseTV","download_url":"https://codeload.github.com/DatanoiseTV/translator-ai/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DatanoiseTV%2Ftranslator-ai/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265905106,"owners_count":23846696,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","cli","deepseek","gemini-api","i18n","internationalization","json","mcp","mcp-server","multi-language","nodejs","ollama","ollama-api","translation","translations","typescript-library"],"created_at":"2025-06-21T02:06:45.171Z","updated_at":"2025-07-19T08:33:45.274Z","avatar_url":"https://github.com/DatanoiseTV.png","language":"TypeScript","funding_links":[],"categories":["Translation Services"],"sub_categories":["How to Submit"],"readme":"# translator-ai\n\n[![CI](https://github.com/DatanoiseTV/translator-ai/actions/workflows/ci.yml/badge.svg)](https://github.com/DatanoiseTV/translator-ai/actions/workflows/ci.yml)\n[![npm version](https://badge.fury.io/js/translator-ai.svg)](https://www.npmjs.com/package/translator-ai)\n[![Buy Me A Coffee](https://img.shields.io/badge/Buy%20Me%20A%20Coffee-support-yellow?style=flat-square\u0026logo=buy-me-a-coffee)](https://coff.ee/datanoisetv)\n\nFast and efficient JSON i18n translator supporting multiple AI providers (Google Gemini, OpenAI \u0026 Ollama/DeepSeek) with intelligent caching, multi-file deduplication, and MCP integration.\n\n## Features\n\n- **Multiple AI Providers**: Choose between Google Gemini, OpenAI (cloud) or Ollama/DeepSeek (local) for translations\n- **Multi-File Support**: Process multiple files with automatic deduplication to save API calls\n- **Incremental Caching**: Only translates new or modified strings, dramatically reducing API calls\n- **Batch Processing**: Intelligently batches translations for optimal performance\n- **Path Preservation**: Maintains exact JSON structure including nested objects and arrays\n- **Cross-Platform**: Works on Windows, macOS, and Linux with automatic cache directory detection\n- **Developer Friendly**: Built-in performance statistics and progress indicators\n- **Cost Effective**: Minimizes API usage through smart caching and deduplication\n- **Language Detection**: Automatically detect source language instead of assuming English\n- **Multiple Target Languages**: Translate to multiple languages in a single command\n- **Translation Metadata**: Optionally include translation details in output files for tracking\n- **Dry Run Mode**: Preview what would be translated without making API calls\n- **Format Preservation**: Maintains URLs, emails, dates, numbers, and template variables unchanged\n\n## Installation\n\n### Global Installation (Recommended)\n\n```bash\nnpm install -g translator-ai\n```\n\n### Local Installation\n\n```bash\nnpm install translator-ai\n```\n\n## Configuration\n\n### Option 1: Google Gemini API (Cloud)\n\nCreate a `.env` file in your project root or set the environment variable:\n\n```bash\nGEMINI_API_KEY=your_gemini_api_key_here\n```\n\nGet your API key from [Google AI Studio](https://aistudio.google.com/app/apikey).\n\n### Option 2: OpenAI API (Cloud)\n\nCreate a `.env` file in your project root or set the environment variable:\n\n```bash\nOPENAI_API_KEY=your_openai_api_key_here\n```\n\nGet your API key from [OpenAI Platform](https://platform.openai.com/api-keys).\n\n### Option 3: Ollama with DeepSeek-R1 (Local)\n\nFor completely local translation without API costs:\n\n1. Install [Ollama](https://ollama.ai)\n2. Pull the DeepSeek-R1 model:\n   ```bash\n   ollama pull deepseek-r1:latest\n   ```\n3. Use the `--provider ollama` flag:\n   ```bash\n   translator-ai source.json -l es -o spanish.json --provider ollama\n   ```\n\n## Usage\n\n### Basic Usage\n\n```bash\n# Translate a single file\ntranslator-ai source.json -l es -o spanish.json\n\n# Translate multiple files with deduplication\ntranslator-ai src/locales/en/*.json -l es -o \"{dir}/{name}.{lang}.json\"\n\n# Use glob patterns\ntranslator-ai \"src/**/*.en.json\" -l fr -o \"{dir}/{name}.fr.json\"\n```\n\n### Command Line Options\n\n```\ntranslator-ai \u003cinputFiles...\u003e [options]\n\nArguments:\n  inputFiles                   Path(s) to source JSON file(s) or glob patterns\n\nOptions:\n  -l, --lang \u003clangCodes\u003e      Target language code(s), comma-separated for multiple\n  -o, --output \u003cpattern\u003e      Output file path or pattern\n  --stdout                    Output to stdout instead of file\n  --stats                     Show detailed performance statistics\n  --no-cache                  Disable incremental translation cache\n  --cache-file \u003cpath\u003e         Custom cache file path\n  --provider \u003ctype\u003e           Translation provider: gemini, openai, or ollama (default: gemini)\n  --ollama-url \u003curl\u003e          Ollama API URL (default: http://localhost:11434)\n  --ollama-model \u003cmodel\u003e      Ollama model name (default: deepseek-r1:latest)\n  --gemini-model \u003cmodel\u003e      Gemini model name (default: gemini-2.0-flash-lite)\n  --openai-model \u003cmodel\u003e      OpenAI model name (default: gpt-4o-mini)\n  --list-providers            List available translation providers\n  --verbose                   Enable verbose output for debugging\n  --detect-source             Auto-detect source language instead of assuming English\n  --dry-run                   Preview what would be translated without making API calls\n  --preserve-formats          Preserve URLs, emails, numbers, dates, and other formats\n  --metadata                  Add translation metadata to output files (may break some i18n parsers)\n  --sort-keys                 Sort output JSON keys alphabetically\n  --check-keys                Verify all source keys exist in output (exit with error if keys are missing)\n  -h, --help                  Display help\n  -V, --version               Display version\n\nOutput Pattern Variables (for multiple files):\n  {dir}   - Original directory path\n  {name}  - Original filename without extension\n  {lang}  - Target language code\n```\n\n### Examples\n\n#### Translate a single file\n```bash\ntranslator-ai en.json -l es -o es.json\n```\n\n#### Translate multiple files with pattern\n```bash\n# All JSON files in a directory\ntranslator-ai locales/en/*.json -l es -o \"locales/es/{name}.json\"\n\n# Recursive glob pattern\ntranslator-ai \"src/**/en.json\" -l fr -o \"{dir}/fr.json\"\n\n# Multiple specific files\ntranslator-ai file1.json file2.json file3.json -l de -o \"{name}.de.json\"\n```\n\n#### Translate with deduplication savings\n```bash\n# Shows statistics including how many API calls were saved\ntranslator-ai src/i18n/*.json -l ja -o \"{dir}/{name}.{lang}.json\" --stats\n```\n\n#### Output to stdout (useful for piping)\n```bash\ntranslator-ai en.json -l de --stdout \u003e de.json\n```\n\n#### Parse output with jq\n```bash\ntranslator-ai en.json -l de --stdout | jq\n```\n\n#### Disable caching for fresh translation\n```bash\ntranslator-ai en.json -l ja -o ja.json --no-cache\n```\n\n#### Use custom cache location\n```bash\ntranslator-ai en.json -l ko -o ko.json --cache-file /path/to/cache.json\n```\n\n#### Use Ollama for local translation\n```bash\n# Basic usage with Ollama\ntranslator-ai en.json -l es -o es.json --provider ollama\n\n# Use a different Ollama model\ntranslator-ai en.json -l fr -o fr.json --provider ollama --ollama-model llama2:latest\n\n# Connect to remote Ollama instance\ntranslator-ai en.json -l de -o de.json --provider ollama --ollama-url http://192.168.1.100:11434\n\n# Check available providers\ntranslator-ai --list-providers\n```\n\n#### Advanced Features\n```bash\n# Detect source language automatically\ntranslator-ai content.json -l es -o spanish.json --detect-source\n\n# Translate to multiple languages at once\ntranslator-ai en.json -l es,fr,de,ja -o translations/{lang}.json\n\n# Dry run - see what would be translated without making API calls\ntranslator-ai en.json -l es -o es.json --dry-run\n\n# Preserve formats (URLs, emails, dates, numbers, template variables)\ntranslator-ai app.json -l fr -o app-fr.json --preserve-formats\n\n# Include translation metadata (disabled by default to ensure compatibility)\ntranslator-ai en.json -l fr -o fr.json --metadata\n\n# Sort keys alphabetically for consistent output\ntranslator-ai en.json -l fr -o fr.json --sort-keys\n\n# Verify all keys are present in the translation\ntranslator-ai en.json -l fr -o fr.json --check-keys\n\n# Use a different Gemini model\ntranslator-ai en.json -l es -o es.json --gemini-model gemini-2.5-flash\n\n# Combine features\ntranslator-ai src/**/*.json -l es,fr,de -o \"{dir}/{name}.{lang}.json\" \\\n  --detect-source --preserve-formats --stats --check-keys\n```\n\n### Available Gemini Models\n\nThe `--gemini-model` option allows you to choose from various Gemini models. Popular options include:\n\n- `gemini-2.0-flash-lite` (default) - Fast and efficient for most translations\n- `gemini-2.5-flash` - Enhanced performance with newer capabilities\n- `gemini-pro` - More sophisticated understanding for complex translations\n- `gemini-1.5-pro` - Previous generation pro model\n- `gemini-1.5-flash` - Previous generation fast model\n\nExample usage:\n```bash\n# Use the latest flash model\ntranslator-ai en.json -l es -o es.json --gemini-model gemini-2.5-flash\n\n# Use the default lightweight model\ntranslator-ai en.json -l fr -o fr.json --gemini-model gemini-2.0-flash-lite\n```\n\n### Available OpenAI Models\n\nThe `--openai-model` option allows you to choose from various OpenAI models. Popular options include:\n\n- `gpt-4o-mini` (default) - Cost-effective and fast for most translations\n- `gpt-4o` - Most capable model with advanced understanding\n- `gpt-4-turbo` - Previous generation flagship model\n- `gpt-3.5-turbo` - Fast and efficient for simpler translations\n\nExample usage:\n```bash\n# Use OpenAI with the default model\ntranslator-ai en.json -l es -o es.json --provider openai\n\n# Use GPT-4o for complex translations\ntranslator-ai en.json -l ja -o ja.json --provider openai --openai-model gpt-4o\n\n# Use GPT-3.5-turbo for faster, simpler translations\ntranslator-ai en.json -l fr -o fr.json --provider openai --openai-model gpt-3.5-turbo\n```\n\n### Translation Metadata\n\nWhen enabled with the `--metadata` flag, translator-ai adds metadata to help track translations:\n\n```json\n{\n  \"_translator_metadata\": {\n    \"tool\": \"translator-ai v1.1.0\",\n    \"repository\": \"https://github.com/DatanoiseTV/translator-ai\",\n    \"provider\": \"Google Gemini\",\n    \"source_language\": \"English\",\n    \"target_language\": \"fr\",\n    \"timestamp\": \"2025-06-20T12:34:56.789Z\",\n    \"total_strings\": 42,\n    \"source_file\": \"en.json\"\n  },\n  \"greeting\": \"Bonjour\",\n  \"farewell\": \"Au revoir\"\n}\n```\n\nMetadata is disabled by default to ensure compatibility with i18n parsers. Use `--metadata` to enable it.\n\n### Key Sorting\n\nUse the `--sort-keys` flag to sort all JSON keys alphabetically in the output:\n\n```bash\ntranslator-ai en.json -l es -o es.json --sort-keys\n```\n\nThis ensures consistent ordering across translations and makes diffs cleaner. Keys are sorted:\n- Case-insensitively (a, B, c, not B, a, c)\n- Recursively through all nested objects\n- Arrays maintain their element order\n\n### Key Verification\n\nUse the `--check-keys` flag to ensure translation completeness:\n\n```bash\ntranslator-ai en.json -l es -o es.json --check-keys\n```\n\nThis feature:\n- Verifies all source keys exist in the translated output\n- Reports any missing keys with their full paths\n- Exits with error code 1 if any keys are missing\n- Helps catch translation API failures or formatting issues\n- Ignores metadata keys when checking\n\n### Supported Language Codes\n\nIt should support any standardized language codes.\n\n## How It Works\n\n1. **Parsing**: Reads and flattens your JSON structure into paths\n2. **Deduplication**: When processing multiple files, identifies shared strings\n3. **Caching**: Checks cache for previously translated strings\n4. **Diffing**: Identifies new or modified strings needing translation\n5. **Batching**: Groups unique strings into optimal batch sizes for API efficiency\n6. **Translation**: Sends batches to selected provider (Gemini API or local Ollama)\n7. **Reconstruction**: Rebuilds the exact JSON structure with translations\n8. **Caching**: Updates cache with new translations for future use\n\n### Multi-File Deduplication\n\nWhen translating multiple files, translator-ai automatically:\n- Identifies duplicate strings across files\n- Translates each unique string only once\n- Applies the same translation consistently across all files\n- Saves significant API calls and ensures consistency\n\nExample: If 10 files share 50% of their strings, you save ~50% on API calls!\n\n## Cache Management\n\n### Default Cache Locations\n\n- **Windows**: `%APPDATA%\\translator-ai\\translation-cache.json`\n- **macOS**: `~/Library/Caches/translator-ai/translation-cache.json`\n- **Linux**: `~/.cache/translator-ai/translation-cache.json`\n\nThe cache file stores translations indexed by:\n- Source file path\n- Target language\n- SHA-256 hash of source string\n\nThis ensures that:\n- Modified strings are retranslated\n- Removed strings are pruned from cache\n- Multiple projects can share the same cache without conflicts\n\n## Provider Comparison\n\n### Google Gemini\n- **Pros**: Fast, accurate, handles large batches efficiently\n- **Cons**: Requires API key, has usage costs\n- **Available Models**:\n  - `gemini-2.0-flash-lite` (default) - Fastest, most cost-effective\n  - `gemini-pro` - Balanced performance\n  - `gemini-1.5-pro` - Advanced capabilities\n  - `gemini-1.5-flash` - Fast with good quality\n- **Best for**: Production use, large projects, when accuracy is critical\n\n### Ollama (Local)\n- **Pros**: Free, runs locally, no API limits, privacy-friendly\n- **Cons**: Slower, requires local resources, model download needed\n- **Best for**: Development, privacy-sensitive data, cost-conscious projects\n\n## Performance Tips\n\n1. **Use caching** (enabled by default) to minimize API calls\n2. **Batch multiple files** in the same session to leverage warm cache\n3. **Use `--stats` flag** to monitor performance and optimization opportunities\n4. **Keep source files consistent** to maximize cache hits\n5. **For Ollama**: Use a powerful machine for better performance\n\n## API Limits and Costs\n\n### Gemini API\n- Uses Gemini 2.0 Flash Lite model for optimal speed and cost\n- Chooses best batch size dynamically depending on input key count\n- Batches up to 100 strings per API call\n- Check [Google's pricing](https://ai.google.dev/pricing) for current rates\n\n### Ollama\n- No API costs - runs entirely on your hardware\n- Performance depends on your machine's capabilities\n- Supports various models with different speed/quality tradeoffs\n\n## Using with Model Context Protocol (MCP)\n\ntranslator-ai can be used as an MCP server, allowing AI assistants like Claude Desktop to translate files directly.\n\n### MCP Configuration\n\nAdd to your Claude Desktop configuration:\n\n**macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`  \n**Windows**: `%APPDATA%\\Claude\\claude_desktop_config.json`\n\n```json\n{\n  \"mcpServers\": {\n    \"translator-ai\": {\n      \"command\": \"npx\",\n      \"args\": [\n        \"-y\",\n        \"translator-ai-mcp\"\n      ],\n      \"env\": {\n        \"GEMINI_API_KEY\": \"your-gemini-api-key-here\"\n        // Or for Ollama:\n        // \"TRANSLATOR_PROVIDER\": \"ollama\"\n      }\n    }\n  }\n}\n```\n\n### MCP Usage Examples\n\nOnce configured, you can ask Claude to translate files:\n\n```\nHuman: Can you translate my English locale file to Spanish?\n\nClaude: I'll translate your English locale file to Spanish using translator-ai.\n\n\u003cuse_tool name=\"translate_json\"\u003e\n{\n  \"inputFile\": \"locales/en.json\",\n  \"targetLanguage\": \"es\",\n  \"outputFile\": \"locales/es.json\"\n}\n\u003c/use_tool\u003e\n\nSuccessfully translated! The file has been saved to locales/es.json.\n```\n\nFor multiple files with deduplication:\n\n```\nHuman: Translate all my English JSON files in the locales folder to German.\n\nClaude: I'll translate all your English JSON files to German with deduplication.\n\n\u003cuse_tool name=\"translate_multiple\"\u003e\n{\n  \"pattern\": \"locales/en/*.json\",\n  \"targetLanguage\": \"de\",\n  \"outputPattern\": \"locales/de/{name}.json\",\n  \"showStats\": true\n}\n\u003c/use_tool\u003e\n\nTranslation complete! Processed 5 files with 23% deduplication savings.\n```\n\n### MCP Tools Available\n\n1. **translate_json**: Translate a single JSON file\n   - `inputFile`: Path to source file\n   - `targetLanguage`: Target language code\n   - `outputFile`: Output file path\n\n2. **translate_multiple**: Translate multiple files with deduplication\n   - `pattern`: File pattern or paths\n   - `targetLanguage`: Target language code\n   - `outputPattern`: Output pattern with {dir}, {name}, {lang} variables\n   - `showStats`: Show deduplication statistics (optional)\n\n## Integration with Static Site Generators\n\n### Working with YAML Files (Hugo, Jekyll, etc.)\n\nSince translator-ai works with JSON files, you'll need to convert YAML to JSON and back. Here's a practical workflow:\n\n#### Setup YAML conversion tools\n\n```bash\n# Install yaml conversion tools\nnpm install -g js-yaml\n# or\npip install pyyaml\n```\n\n#### Hugo Example with YAML Conversion\n\n1. **Create a translation script** (`translate-hugo.sh`):\n\n```bash\n#!/bin/bash\n# translate-hugo.sh - Translate Hugo YAML i18n files\n\n# Function to translate YAML file\ntranslate_yaml() {\n  local input_file=$1\n  local lang=$2\n  local output_file=$3\n  \n  echo \"Translating $input_file to $lang...\"\n  \n  # Convert YAML to JSON\n  npx js-yaml $input_file \u003e temp_input.json\n  \n  # Translate JSON\n  translator-ai temp_input.json -l $lang -o temp_output.json\n  \n  # Convert back to YAML\n  npx js-yaml temp_output.json \u003e $output_file\n  \n  # Cleanup\n  rm temp_input.json temp_output.json\n}\n\n# Translate Hugo i18n files\ntranslate_yaml themes/your-theme/i18n/en.yaml es themes/your-theme/i18n/es.yaml\ntranslate_yaml themes/your-theme/i18n/en.yaml fr themes/your-theme/i18n/fr.yaml\ntranslate_yaml themes/your-theme/i18n/en.yaml de themes/your-theme/i18n/de.yaml\n```\n\n2. **Python-based converter** for more complex scenarios:\n\n```python\n#!/usr/bin/env python3\n# hugo-translate.py\n\nimport yaml\nimport json\nimport subprocess\nimport sys\nimport os\n\ndef yaml_to_json(yaml_file):\n    \"\"\"Convert YAML to JSON\"\"\"\n    with open(yaml_file, 'r', encoding='utf-8') as f:\n        data = yaml.safe_load(f)\n    return json.dumps(data, ensure_ascii=False, indent=2)\n\ndef json_to_yaml(json_str):\n    \"\"\"Convert JSON back to YAML\"\"\"\n    data = json.loads(json_str)\n    return yaml.dump(data, allow_unicode=True, default_flow_style=False)\n\ndef translate_yaml_file(input_yaml, target_lang, output_yaml):\n    \"\"\"Translate a YAML file using translator-ai\"\"\"\n    \n    # Create temp JSON file\n    temp_json_in = 'temp_in.json'\n    temp_json_out = f'temp_out_{target_lang}.json'\n    \n    try:\n        # Convert YAML to JSON\n        json_content = yaml_to_json(input_yaml)\n        with open(temp_json_in, 'w', encoding='utf-8') as f:\n            f.write(json_content)\n        \n        # Run translator-ai\n        cmd = [\n            'translator-ai',\n            temp_json_in,\n            '-l', target_lang,\n            '-o', temp_json_out\n        ]\n        subprocess.run(cmd, check=True)\n        \n        # Read translated JSON and convert back to YAML\n        with open(temp_json_out, 'r', encoding='utf-8') as f:\n            translated_json = f.read()\n        \n        yaml_content = json_to_yaml(translated_json)\n        \n        # Write YAML output\n        with open(output_yaml, 'w', encoding='utf-8') as f:\n            f.write(yaml_content)\n        \n        print(f\"✓ Translated {input_yaml} to {output_yaml}\")\n        \n    finally:\n        # Cleanup temp files\n        for f in [temp_json_in, temp_json_out]:\n            if os.path.exists(f):\n                os.remove(f)\n\n# Usage\nif __name__ == \"__main__\":\n    languages = ['es', 'fr', 'de', 'ja']\n    \n    for lang in languages:\n        translate_yaml_file(\n            'i18n/en.yaml',\n            lang,\n            f'i18n/{lang}.yaml'\n        )\n```\n\n#### Node.js Solution with Proper YAML Handling\n\nCreate `translate-yaml.js`:\n\n```javascript\n#!/usr/bin/env node\nconst fs = require('fs');\nconst yaml = require('js-yaml');\nconst { execSync } = require('child_process');\nconst path = require('path');\n\nfunction translateYamlFile(inputPath, targetLang, outputPath) {\n  console.log(`Translating ${inputPath} to ${targetLang}...`);\n  \n  // Read and parse YAML\n  const yamlContent = fs.readFileSync(inputPath, 'utf8');\n  const data = yaml.load(yamlContent);\n  \n  // Write temporary JSON\n  const tempJsonIn = `temp_${path.basename(inputPath)}.json`;\n  const tempJsonOut = `temp_${path.basename(inputPath)}_${targetLang}.json`;\n  \n  fs.writeFileSync(tempJsonIn, JSON.stringify(data, null, 2));\n  \n  try {\n    // Translate using translator-ai\n    execSync(`translator-ai ${tempJsonIn} -l ${targetLang} -o ${tempJsonOut}`);\n    \n    // Read translated JSON\n    const translatedData = JSON.parse(fs.readFileSync(tempJsonOut, 'utf8'));\n    \n    // Convert back to YAML\n    const translatedYaml = yaml.dump(translatedData, {\n      indent: 2,\n      lineWidth: -1,\n      noRefs: true\n    });\n    \n    // Write output YAML\n    fs.writeFileSync(outputPath, translatedYaml);\n    console.log(`✓ Created ${outputPath}`);\n    \n  } finally {\n    // Cleanup\n    [tempJsonIn, tempJsonOut].forEach(f =\u003e {\n      if (fs.existsSync(f)) fs.unlinkSync(f);\n    });\n  }\n}\n\n// Example usage\nconst languages = ['es', 'fr', 'de'];\nlanguages.forEach(lang =\u003e {\n  translateYamlFile(\n    'i18n/en.yaml',\n    lang,\n    `i18n/${lang}.yaml`\n  );\n});\n```\n\n### Real-world Hugo Workflow\n\nHugo supports two translation methods: by filename (`about.en.md`, `about.fr.md`) or by content directory (`content/en/`, `content/fr/`). Here's how to automate both:\n\n#### Method 1: Translation by Filename\n\nCreate `hugo-translate-files.sh`:\n\n```bash\n#!/bin/bash\n# Translate Hugo content files using filename convention\n\nSOURCE_LANG=\"en\"\nTARGET_LANGS=(\"es\" \"fr\" \"de\" \"ja\")\n\n# Find all English content files\nfind content -name \"*.${SOURCE_LANG}.md\" | while read -r file; do\n  # Extract base filename without language suffix\n  base_name=\"${file%.${SOURCE_LANG}.md}\"\n  \n  for lang in \"${TARGET_LANGS[@]}\"; do\n    output_file=\"${base_name}.${lang}.md\"\n    \n    # Skip if translation already exists\n    if [ -f \"$output_file\" ]; then\n      echo \"Skipping $output_file (already exists)\"\n      continue\n    fi\n    \n    # Extract front matter\n    awk '/^---$/{p=1; next} p\u0026\u0026/^---$/{exit} p' \"$file\" \u003e temp_frontmatter.yaml\n    \n    # Convert front matter to JSON\n    npx js-yaml temp_frontmatter.yaml \u003e temp_frontmatter.json\n    \n    # Translate front matter\n    translator-ai temp_frontmatter.json -l \"$lang\" -o \"temp_translated.json\"\n    \n    # Convert back to YAML\n    echo \"---\" \u003e \"$output_file\"\n    npx js-yaml temp_translated.json \u003e\u003e \"$output_file\"\n    echo \"---\" \u003e\u003e \"$output_file\"\n    \n    # Copy content (you might want to translate this too)\n    awk '/^---$/{p++} p==2{print}' \"$file\" | tail -n +2 \u003e\u003e \"$output_file\"\n    \n    echo \"Created $output_file\"\n  done\n  \n  # Cleanup\n  rm -f temp_frontmatter.yaml temp_frontmatter.json temp_translated.json\ndone\n```\n\n#### Method 2: Translation by Content Directory\n\n1. **Setup Hugo config** (`config.yaml`):\n\n```yaml\ndefaultContentLanguage: en\ndefaultContentLanguageInSubdir: false\n\nlanguages:\n  en:\n    contentDir: content/en\n    languageName: English\n    weight: 1\n  es:\n    contentDir: content/es\n    languageName: Español\n    weight: 2\n  fr:\n    contentDir: content/fr\n    languageName: Français\n    weight: 3\n\n# Rest of your config...\n```\n\n2. **Create translation script** (`hugo-translate-dirs.js`):\n\n```javascript\n#!/usr/bin/env node\nconst fs = require('fs-extra');\nconst path = require('path');\nconst yaml = require('js-yaml');\nconst { execSync } = require('child_process');\nconst glob = require('glob');\n\nconst SOURCE_LANG = 'en';\nconst TARGET_LANGS = ['es', 'fr', 'de'];\n\nasync function translateHugoContent() {\n  // Ensure target directories exist\n  for (const lang of TARGET_LANGS) {\n    await fs.ensureDir(`content/${lang}`);\n  }\n  \n  // Find all content files in source language\n  const files = glob.sync(`content/${SOURCE_LANG}/**/*.md`);\n  \n  for (const file of files) {\n    const relativePath = path.relative(`content/${SOURCE_LANG}`, file);\n    \n    for (const lang of TARGET_LANGS) {\n      const targetFile = path.join(`content/${lang}`, relativePath);\n      \n      // Skip if already translated\n      if (await fs.pathExists(targetFile)) {\n        console.log(`Skipping ${targetFile} (exists)`);\n        continue;\n      }\n      \n      await translateFile(file, targetFile, lang);\n    }\n  }\n}\n\nasync function translateFile(sourceFile, targetFile, targetLang) {\n  console.log(`Translating ${sourceFile} to ${targetLang}...`);\n  \n  const content = await fs.readFile(sourceFile, 'utf8');\n  const frontMatterMatch = content.match(/^---\\n([\\s\\S]*?)\\n---/);\n  \n  if (!frontMatterMatch) {\n    // No front matter, just copy\n    await fs.ensureDir(path.dirname(targetFile));\n    await fs.copyFile(sourceFile, targetFile);\n    return;\n  }\n  \n  // Parse front matter\n  const frontMatter = yaml.load(frontMatterMatch[1]);\n  const body = content.substring(frontMatterMatch[0].length);\n  \n  // Extract translatable fields\n  const translatable = {\n    title: frontMatter.title || '',\n    description: frontMatter.description || '',\n    summary: frontMatter.summary || '',\n    keywords: frontMatter.keywords || []\n  };\n  \n  // Save for translation\n  await fs.writeJson('temp_meta.json', translatable);\n  \n  // Translate\n  execSync(`translator-ai temp_meta.json -l ${targetLang} -o temp_translated.json`);\n  \n  // Read translations\n  const translated = await fs.readJson('temp_translated.json');\n  \n  // Update front matter\n  Object.assign(frontMatter, translated);\n  \n  // Write translated file\n  await fs.ensureDir(path.dirname(targetFile));\n  const newContent = `---\\n${yaml.dump(frontMatter)}---${body}`;\n  await fs.writeFile(targetFile, newContent);\n  \n  // Cleanup\n  await fs.remove('temp_meta.json');\n  await fs.remove('temp_translated.json');\n  \n  console.log(`✓ Created ${targetFile}`);\n}\n\n// Run translation\ntranslateHugoContent().catch(console.error);\n```\n\n#### Hugo i18n Files Translation\n\n1. **Install dependencies**:\n```bash\nnpm install -g translator-ai js-yaml\n```\n\n2. **Create a Makefile** for easy translation:\n\n```makefile\n# Makefile for Hugo translations\nLANGUAGES := es fr de ja zh\nSOURCE_YAML := i18n/en.yaml\nTHEME_DIR := themes/your-theme\n\n.PHONY: translate\ntranslate: $(foreach lang,$(LANGUAGES),translate-$(lang))\n\ntranslate-%:\n\t@echo \"Translating to $*...\"\n\t@npx js-yaml $(SOURCE_YAML) \u003e temp.json\n\t@translator-ai temp.json -l $* -o temp_$*.json\n\t@npx js-yaml temp_$*.json \u003e i18n/$*.yaml\n\t@rm temp.json temp_$*.json\n\t@echo \"✓ Created i18n/$*.yaml\"\n\n.PHONY: translate-theme\ntranslate-theme:\n\t@for lang in $(LANGUAGES); do \\\n\t\tmake translate-theme-$$lang; \\\n\tdone\n\ntranslate-theme-%:\n\t@echo \"Translating theme to $*...\"\n\t@npx js-yaml $(THEME_DIR)/i18n/en.yaml \u003e temp_theme.json\n\t@translator-ai temp_theme.json -l $* -o temp_theme_$*.json\n\t@npx js-yaml temp_theme_$*.json \u003e $(THEME_DIR)/i18n/$*.yaml\n\t@rm temp_theme.json temp_theme_$*.json\n\n.PHONY: clean\nclean:\n\t@rm -f temp*.json\n\n# Translate everything\n.PHONY: all\nall: translate translate-theme\n```\n\nUsage:\n```bash\n# Translate to all languages\nmake all\n\n# Translate to specific language\nmake translate-es\n\n# Translate theme files\nmake translate-theme\n```\n\n#### Complete Hugo Translation Workflow\n\nHere's a comprehensive script that handles both content and i18n translations:\n\n```javascript\n#!/usr/bin/env node\n// hugo-complete-translator.js\nconst fs = require('fs-extra');\nconst path = require('path');\nconst yaml = require('js-yaml');\nconst { execSync } = require('child_process');\nconst glob = require('glob');\n\nclass HugoTranslator {\n  constructor(targetLanguages = ['es', 'fr', 'de']) {\n    this.targetLanguages = targetLanguages;\n    this.tempFiles = [];\n  }\n\n  async translateSite() {\n    console.log('Starting Hugo site translation...\\n');\n    \n    // 1. Translate i18n files\n    await this.translateI18nFiles();\n    \n    // 2. Translate content\n    await this.translateContent();\n    \n    // 3. Update config\n    await this.updateConfig();\n    \n    console.log('\\nTranslation complete!');\n  }\n\n  async translateI18nFiles() {\n    console.log('Translating i18n files...');\n    const i18nFiles = glob.sync('i18n/en.{yaml,yml,toml}');\n    \n    for (const file of i18nFiles) {\n      const ext = path.extname(file);\n      \n      for (const lang of this.targetLanguages) {\n        const outputFile = `i18n/${lang}${ext}`;\n        \n        if (await fs.pathExists(outputFile)) {\n          console.log(`  Skipping ${outputFile} (exists)`);\n          continue;\n        }\n        \n        // Convert to JSON\n        const tempJson = `temp_i18n_${lang}.json`;\n        await this.convertToJson(file, tempJson);\n        \n        // Translate\n        const translatedJson = `temp_i18n_${lang}_translated.json`;\n        execSync(`translator-ai ${tempJson} -l ${lang} -o ${translatedJson}`);\n        \n        // Convert back\n        await this.convertFromJson(translatedJson, outputFile, ext);\n        \n        // Cleanup\n        await fs.remove(tempJson);\n        await fs.remove(translatedJson);\n        \n        console.log(`  ✓ Created ${outputFile}`);\n      }\n    }\n  }\n\n  async translateContent() {\n    console.log('\\nTranslating content...');\n    \n    // Detect translation method\n    const useContentDirs = await fs.pathExists('content/en');\n    \n    if (useContentDirs) {\n      await this.translateContentByDirectory();\n    } else {\n      await this.translateContentByFilename();\n    }\n  }\n\n  async translateContentByDirectory() {\n    const files = glob.sync('content/en/**/*.md');\n    \n    for (const file of files) {\n      const relativePath = path.relative('content/en', file);\n      \n      for (const lang of this.targetLanguages) {\n        const targetFile = path.join('content', lang, relativePath);\n        \n        if (await fs.pathExists(targetFile)) continue;\n        \n        await this.translateMarkdownFile(file, targetFile, lang);\n      }\n    }\n  }\n\n  async translateContentByFilename() {\n    const files = glob.sync('content/**/*.en.md');\n    \n    for (const file of files) {\n      const baseName = file.replace('.en.md', '');\n      \n      for (const lang of this.targetLanguages) {\n        const targetFile = `${baseName}.${lang}.md`;\n        \n        if (await fs.pathExists(targetFile)) continue;\n        \n        await this.translateMarkdownFile(file, targetFile, lang);\n      }\n    }\n  }\n\n  async translateMarkdownFile(sourceFile, targetFile, targetLang) {\n    const content = await fs.readFile(sourceFile, 'utf8');\n    const frontMatterMatch = content.match(/^---\\n([\\s\\S]*?)\\n---/);\n    \n    if (!frontMatterMatch) {\n      await fs.copy(sourceFile, targetFile);\n      return;\n    }\n    \n    const frontMatter = yaml.load(frontMatterMatch[1]);\n    const body = content.substring(frontMatterMatch[0].length);\n    \n    // Translate front matter\n    const translatable = this.extractTranslatableFields(frontMatter);\n    const tempJson = `temp_content_${path.basename(sourceFile)}.json`;\n    const translatedJson = `${tempJson}.translated`;\n    \n    await fs.writeJson(tempJson, translatable);\n    execSync(`translator-ai ${tempJson} -l ${targetLang} -o ${translatedJson}`);\n    \n    const translated = await fs.readJson(translatedJson);\n    Object.assign(frontMatter, translated);\n    \n    // Write translated file\n    await fs.ensureDir(path.dirname(targetFile));\n    const newContent = `---\\n${yaml.dump(frontMatter)}---${body}`;\n    await fs.writeFile(targetFile, newContent);\n    \n    // Cleanup\n    await fs.remove(tempJson);\n    await fs.remove(translatedJson);\n    \n    console.log(`  ✓ ${targetFile}`);\n  }\n\n  extractTranslatableFields(frontMatter) {\n    const fields = ['title', 'description', 'summary', 'keywords', 'tags'];\n    const translatable = {};\n    \n    fields.forEach(field =\u003e {\n      if (frontMatter[field]) {\n        translatable[field] = frontMatter[field];\n      }\n    });\n    \n    return translatable;\n  }\n\n  async convertToJson(inputFile, outputFile) {\n    const ext = path.extname(inputFile);\n    const content = await fs.readFile(inputFile, 'utf8');\n    let data;\n    \n    if (ext === '.yaml' || ext === '.yml') {\n      data = yaml.load(content);\n    } else if (ext === '.toml') {\n      // You'd need a TOML parser here\n      throw new Error('TOML support not implemented in this example');\n    }\n    \n    await fs.writeJson(outputFile, data, { spaces: 2 });\n  }\n\n  async convertFromJson(inputFile, outputFile, format) {\n    const data = await fs.readJson(inputFile);\n    let content;\n    \n    if (format === '.yaml' || format === '.yml') {\n      content = yaml.dump(data, { \n        indent: 2, \n        lineWidth: -1,\n        noRefs: true \n      });\n    } else if (format === '.toml') {\n      throw new Error('TOML support not implemented in this example');\n    }\n    \n    await fs.writeFile(outputFile, content);\n  }\n\n  async updateConfig() {\n    console.log('\\nUpdating Hugo config...');\n    \n    const configFile = glob.sync('config.{yaml,yml,toml,json}')[0];\n    if (!configFile) return;\n    \n    // This is a simplified example - you'd need to properly parse and update\n    console.log('  ! Remember to update your config.yaml with language settings');\n  }\n}\n\n// Run the translator\nif (require.main === module) {\n  const translator = new HugoTranslator(['es', 'fr', 'de']);\n  translator.translateSite().catch(console.error);\n}\n\nmodule.exports = HugoTranslator;\n```\n\n#### Using with Hugo Modules\n\nIf you're using Hugo Modules, you can create a translation module:\n\n```go\n// go.mod\nmodule github.com/yourusername/hugo-translator\n\ngo 1.19\n\nrequire (\n    github.com/yourusername/your-theme v1.0.0\n)\n```\n\nThen in your `package.json`:\n\n```json\n{\n  \"scripts\": {\n    \"translate\": \"node hugo-complete-translator.js\",\n    \"translate:content\": \"node hugo-complete-translator.js --content-only\",\n    \"translate:i18n\": \"node hugo-complete-translator.js --i18n-only\",\n    \"build\": \"npm run translate \u0026\u0026 hugo\"\n  }\n}\n```\n\n### Jekyll with YAML Front Matter\n\nFor Jekyll posts with YAML front matter:\n\n```python\n#!/usr/bin/env python3\n# translate-jekyll-posts.py\n\nimport os\nimport yaml\nimport json\nimport subprocess\nimport frontmatter\n\ndef translate_jekyll_post(post_path, target_lang, output_dir):\n    \"\"\"Translate Jekyll post including front matter\"\"\"\n    \n    # Load post with front matter\n    post = frontmatter.load(post_path)\n    \n    # Extract translatable front matter fields\n    translatable = {\n        'title': post.metadata.get('title', ''),\n        'description': post.metadata.get('description', ''),\n        'excerpt': post.metadata.get('excerpt', '')\n    }\n    \n    # Save as JSON for translation\n    with open('temp_meta.json', 'w', encoding='utf-8') as f:\n        json.dump(translatable, f, ensure_ascii=False, indent=2)\n    \n    # Translate\n    subprocess.run([\n        'translator-ai',\n        'temp_meta.json',\n        '-l', target_lang,\n        '-o', f'temp_meta_{target_lang}.json'\n    ])\n    \n    # Load translations\n    with open(f'temp_meta_{target_lang}.json', 'r', encoding='utf-8') as f:\n        translations = json.load(f)\n    \n    # Update post metadata\n    for key, value in translations.items():\n        if value:  # Only update if translation exists\n            post.metadata[key] = value\n    \n    # Add language to metadata\n    post.metadata['lang'] = target_lang\n    \n    # Save translated post\n    output_path = os.path.join(output_dir, os.path.basename(post_path))\n    with open(output_path, 'w', encoding='utf-8') as f:\n        f.write(frontmatter.dumps(post))\n    \n    # Cleanup\n    os.remove('temp_meta.json')\n    os.remove(f'temp_meta_{target_lang}.json')\n\n# Translate all posts\nfor lang in ['es', 'fr', 'de']:\n    os.makedirs(f'_posts/{lang}', exist_ok=True)\n    for post in os.listdir('_posts/en'):\n        if post.endswith('.md'):\n            translate_jekyll_post(\n                f'_posts/en/{post}',\n                lang,\n                f'_posts/{lang}'\n            )\n```\n\n### Tips for YAML/JSON Conversion\n\n1. **Preserve formatting**: Use `js-yaml` with proper options to maintain YAML structure\n2. **Handle special characters**: Ensure proper encoding (UTF-8) throughout\n3. **Validate output**: Some YAML features (anchors, aliases) may need special handling\n4. **Consider TOML**: For Hugo, you might also need to handle TOML config files\n\n### Alternative: Direct YAML Support (Feature Request)\n\nIf you frequently work with YAML files, consider creating a wrapper script that handles conversion automatically, or request YAML support as a feature for translator-ai.\n\n## Development\n\n### Building from source\n\n```bash\ngit clone https://github.com/DatanoiseTV/translator-ai.git\ncd translator-ai\nnpm install\nnpm run build\n```\n\n### Testing locally\n\n```bash\nnpm start -- test.json -l es -o output.json\n```\n\n## License\n\nThis project requires attribution for both commercial and non-commercial use. See [LICENSE](LICENSE) file for details.\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n## Support\n\nFor issues, questions, or suggestions, please open an issue on [GitHub](https://github.com/DatanoiseTV/translator-ai/issues).\n\nIf you find this tool useful, consider supporting the development:\n\n[![Buy Me A Coffee](https://img.shields.io/badge/Buy%20Me%20A%20Coffee-support-yellow?style=flat-square\u0026logo=buy-me-a-coffee)](https://coff.ee/datanoisetv)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdatanoisetv%2Ftranslator-ai","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdatanoisetv%2Ftranslator-ai","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdatanoisetv%2Ftranslator-ai/lists"}