Gathering detailed insights and metrics for translator-ai
Gathering detailed insights and metrics for translator-ai
Gathering detailed insights and metrics for translator-ai
Gathering detailed insights and metrics for translator-ai
@azure-rest/ai-document-translator
An isomorphic rest level client library for the Azure Document Translator service.
@azure-rest/ai-translation-text
An isomorphic client library for the Azure Cognitive Translator Service
@0xdwong/ai-translator
An AI translator
ai-ass-translator
`npm i -g ai-ass-translator`
🌐 Multi-provider AI translation tool for JSON i18n files. Features: Google Gemini & Ollama support, incremental caching, multi-file deduplication, MCP server, batch processing, and cross-platform compatibility. Ideal for developers managing multilingual applications.
npm install translator-ai
Typescript
Module System
Min. Node Version
Node Version
NPM Version
v1.1.0 - OpenAI Support & Configurable Models
Updated on Jun 20, 2025
v1.0.10 - Key Verification and Sorting
Updated on Jun 20, 2025
v1.0.9 - Advanced Translation Features
Updated on Jun 20, 2025
v1.0.8 - License field update
Updated on Jun 20, 2025
v1.0.7 - Improved Ollama robustness and validation
Updated on Jun 20, 2025
v1.0.6 - Ollama translation fixes and verbose logging
Updated on Jun 20, 2025
TypeScript (69.38%)
JavaScript (30.62%)
Total Downloads
0
Last Day
0
Last Week
0
Last Month
0
Last Year
0
NOASSERTION License
2 Stars
40 Commits
1 Branches
2 Contributors
Updated on Jul 10, 2025
Latest Version
1.1.0
Package Id
translator-ai@1.1.0
Unpacked Size
128.05 kB
Size
31.72 kB
File Count
11
NPM Version
10.8.2
Node Version
20.19.2
Published on
Jun 20, 2025
Cumulative downloads
Total Downloads
Last Day
0%
NaN
Compared to previous day
Last Week
0%
NaN
Compared to previous week
Last Month
0%
NaN
Compared to previous month
Last Year
0%
NaN
Compared to previous year
Fast and efficient JSON i18n translator supporting multiple AI providers (Google Gemini, OpenAI & Ollama/DeepSeek) with intelligent caching, multi-file deduplication, and MCP integration.
1npm install -g translator-ai
1npm install translator-ai
Create a .env
file in your project root or set the environment variable:
1GEMINI_API_KEY=your_gemini_api_key_here
Get your API key from Google AI Studio.
Create a .env
file in your project root or set the environment variable:
1OPENAI_API_KEY=your_openai_api_key_here
Get your API key from OpenAI Platform.
For completely local translation without API costs:
1ollama pull deepseek-r1:latest
--provider ollama
flag:
1translator-ai source.json -l es -o spanish.json --provider ollama
1# Translate a single file 2translator-ai source.json -l es -o spanish.json 3 4# Translate multiple files with deduplication 5translator-ai src/locales/en/*.json -l es -o "{dir}/{name}.{lang}.json" 6 7# Use glob patterns 8translator-ai "src/**/*.en.json" -l fr -o "{dir}/{name}.fr.json"
translator-ai <inputFiles...> [options]
Arguments:
inputFiles Path(s) to source JSON file(s) or glob patterns
Options:
-l, --lang <langCodes> Target language code(s), comma-separated for multiple
-o, --output <pattern> Output file path or pattern
--stdout Output to stdout instead of file
--stats Show detailed performance statistics
--no-cache Disable incremental translation cache
--cache-file <path> Custom cache file path
--provider <type> Translation provider: gemini, openai, or ollama (default: gemini)
--ollama-url <url> Ollama API URL (default: http://localhost:11434)
--ollama-model <model> Ollama model name (default: deepseek-r1:latest)
--gemini-model <model> Gemini model name (default: gemini-2.0-flash-lite)
--openai-model <model> OpenAI model name (default: gpt-4o-mini)
--list-providers List available translation providers
--verbose Enable verbose output for debugging
--detect-source Auto-detect source language instead of assuming English
--dry-run Preview what would be translated without making API calls
--preserve-formats Preserve URLs, emails, numbers, dates, and other formats
--metadata Add translation metadata to output files (may break some i18n parsers)
--sort-keys Sort output JSON keys alphabetically
--check-keys Verify all source keys exist in output (exit with error if keys are missing)
-h, --help Display help
-V, --version Display version
Output Pattern Variables (for multiple files):
{dir} - Original directory path
{name} - Original filename without extension
{lang} - Target language code
1translator-ai en.json -l es -o es.json
1# All JSON files in a directory 2translator-ai locales/en/*.json -l es -o "locales/es/{name}.json" 3 4# Recursive glob pattern 5translator-ai "src/**/en.json" -l fr -o "{dir}/fr.json" 6 7# Multiple specific files 8translator-ai file1.json file2.json file3.json -l de -o "{name}.de.json"
1# Shows statistics including how many API calls were saved 2translator-ai src/i18n/*.json -l ja -o "{dir}/{name}.{lang}.json" --stats
1translator-ai en.json -l de --stdout > de.json
1translator-ai en.json -l de --stdout | jq
1translator-ai en.json -l ja -o ja.json --no-cache
1translator-ai en.json -l ko -o ko.json --cache-file /path/to/cache.json
1# Basic usage with Ollama 2translator-ai en.json -l es -o es.json --provider ollama 3 4# Use a different Ollama model 5translator-ai en.json -l fr -o fr.json --provider ollama --ollama-model llama2:latest 6 7# Connect to remote Ollama instance 8translator-ai en.json -l de -o de.json --provider ollama --ollama-url http://192.168.1.100:11434 9 10# Check available providers 11translator-ai --list-providers
1# Detect source language automatically 2translator-ai content.json -l es -o spanish.json --detect-source 3 4# Translate to multiple languages at once 5translator-ai en.json -l es,fr,de,ja -o translations/{lang}.json 6 7# Dry run - see what would be translated without making API calls 8translator-ai en.json -l es -o es.json --dry-run 9 10# Preserve formats (URLs, emails, dates, numbers, template variables) 11translator-ai app.json -l fr -o app-fr.json --preserve-formats 12 13# Include translation metadata (disabled by default to ensure compatibility) 14translator-ai en.json -l fr -o fr.json --metadata 15 16# Sort keys alphabetically for consistent output 17translator-ai en.json -l fr -o fr.json --sort-keys 18 19# Verify all keys are present in the translation 20translator-ai en.json -l fr -o fr.json --check-keys 21 22# Use a different Gemini model 23translator-ai en.json -l es -o es.json --gemini-model gemini-2.5-flash 24 25# Combine features 26translator-ai src/**/*.json -l es,fr,de -o "{dir}/{name}.{lang}.json" \ 27 --detect-source --preserve-formats --stats --check-keys
The --gemini-model
option allows you to choose from various Gemini models. Popular options include:
gemini-2.0-flash-lite
(default) - Fast and efficient for most translationsgemini-2.5-flash
- Enhanced performance with newer capabilitiesgemini-pro
- More sophisticated understanding for complex translationsgemini-1.5-pro
- Previous generation pro modelgemini-1.5-flash
- Previous generation fast modelExample usage:
1# Use the latest flash model 2translator-ai en.json -l es -o es.json --gemini-model gemini-2.5-flash 3 4# Use the default lightweight model 5translator-ai en.json -l fr -o fr.json --gemini-model gemini-2.0-flash-lite
The --openai-model
option allows you to choose from various OpenAI models. Popular options include:
gpt-4o-mini
(default) - Cost-effective and fast for most translationsgpt-4o
- Most capable model with advanced understandinggpt-4-turbo
- Previous generation flagship modelgpt-3.5-turbo
- Fast and efficient for simpler translationsExample usage:
1# Use OpenAI with the default model 2translator-ai en.json -l es -o es.json --provider openai 3 4# Use GPT-4o for complex translations 5translator-ai en.json -l ja -o ja.json --provider openai --openai-model gpt-4o 6 7# Use GPT-3.5-turbo for faster, simpler translations 8translator-ai en.json -l fr -o fr.json --provider openai --openai-model gpt-3.5-turbo
When enabled with the --metadata
flag, translator-ai adds metadata to help track translations:
1{ 2 "_translator_metadata": { 3 "tool": "translator-ai v1.1.0", 4 "repository": "https://github.com/DatanoiseTV/translator-ai", 5 "provider": "Google Gemini", 6 "source_language": "English", 7 "target_language": "fr", 8 "timestamp": "2025-06-20T12:34:56.789Z", 9 "total_strings": 42, 10 "source_file": "en.json" 11 }, 12 "greeting": "Bonjour", 13 "farewell": "Au revoir" 14}
Metadata is disabled by default to ensure compatibility with i18n parsers. Use --metadata
to enable it.
Use the --sort-keys
flag to sort all JSON keys alphabetically in the output:
1translator-ai en.json -l es -o es.json --sort-keys
This ensures consistent ordering across translations and makes diffs cleaner. Keys are sorted:
Use the --check-keys
flag to ensure translation completeness:
1translator-ai en.json -l es -o es.json --check-keys
This feature:
It should support any standardized language codes.
When translating multiple files, translator-ai automatically:
Example: If 10 files share 50% of their strings, you save ~50% on API calls!
%APPDATA%\translator-ai\translation-cache.json
~/Library/Caches/translator-ai/translation-cache.json
~/.cache/translator-ai/translation-cache.json
The cache file stores translations indexed by:
This ensures that:
gemini-2.0-flash-lite
(default) - Fastest, most cost-effectivegemini-pro
- Balanced performancegemini-1.5-pro
- Advanced capabilitiesgemini-1.5-flash
- Fast with good quality--stats
flag to monitor performance and optimization opportunitiestranslator-ai can be used as an MCP server, allowing AI assistants like Claude Desktop to translate files directly.
Add to your Claude Desktop configuration:
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
1{ 2 "mcpServers": { 3 "translator-ai": { 4 "command": "npx", 5 "args": [ 6 "-y", 7 "translator-ai-mcp" 8 ], 9 "env": { 10 "GEMINI_API_KEY": "your-gemini-api-key-here" 11 // Or for Ollama: 12 // "TRANSLATOR_PROVIDER": "ollama" 13 } 14 } 15 } 16}
Once configured, you can ask Claude to translate files:
Human: Can you translate my English locale file to Spanish?
Claude: I'll translate your English locale file to Spanish using translator-ai.
<use_tool name="translate_json">
{
"inputFile": "locales/en.json",
"targetLanguage": "es",
"outputFile": "locales/es.json"
}
</use_tool>
Successfully translated! The file has been saved to locales/es.json.
For multiple files with deduplication:
Human: Translate all my English JSON files in the locales folder to German.
Claude: I'll translate all your English JSON files to German with deduplication.
<use_tool name="translate_multiple">
{
"pattern": "locales/en/*.json",
"targetLanguage": "de",
"outputPattern": "locales/de/{name}.json",
"showStats": true
}
</use_tool>
Translation complete! Processed 5 files with 23% deduplication savings.
translate_json: Translate a single JSON file
inputFile
: Path to source filetargetLanguage
: Target language codeoutputFile
: Output file pathtranslate_multiple: Translate multiple files with deduplication
pattern
: File pattern or pathstargetLanguage
: Target language codeoutputPattern
: Output pattern with {dir}, {name}, {lang} variablesshowStats
: Show deduplication statistics (optional)Since translator-ai works with JSON files, you'll need to convert YAML to JSON and back. Here's a practical workflow:
1# Install yaml conversion tools 2npm install -g js-yaml 3# or 4pip install pyyaml
translate-hugo.sh
):1#!/bin/bash 2# translate-hugo.sh - Translate Hugo YAML i18n files 3 4# Function to translate YAML file 5translate_yaml() { 6 local input_file=$1 7 local lang=$2 8 local output_file=$3 9 10 echo "Translating $input_file to $lang..." 11 12 # Convert YAML to JSON 13 npx js-yaml $input_file > temp_input.json 14 15 # Translate JSON 16 translator-ai temp_input.json -l $lang -o temp_output.json 17 18 # Convert back to YAML 19 npx js-yaml temp_output.json > $output_file 20 21 # Cleanup 22 rm temp_input.json temp_output.json 23} 24 25# Translate Hugo i18n files 26translate_yaml themes/your-theme/i18n/en.yaml es themes/your-theme/i18n/es.yaml 27translate_yaml themes/your-theme/i18n/en.yaml fr themes/your-theme/i18n/fr.yaml 28translate_yaml themes/your-theme/i18n/en.yaml de themes/your-theme/i18n/de.yaml
1#!/usr/bin/env python3 2# hugo-translate.py 3 4import yaml 5import json 6import subprocess 7import sys 8import os 9 10def yaml_to_json(yaml_file): 11 """Convert YAML to JSON""" 12 with open(yaml_file, 'r', encoding='utf-8') as f: 13 data = yaml.safe_load(f) 14 return json.dumps(data, ensure_ascii=False, indent=2) 15 16def json_to_yaml(json_str): 17 """Convert JSON back to YAML""" 18 data = json.loads(json_str) 19 return yaml.dump(data, allow_unicode=True, default_flow_style=False) 20 21def translate_yaml_file(input_yaml, target_lang, output_yaml): 22 """Translate a YAML file using translator-ai""" 23 24 # Create temp JSON file 25 temp_json_in = 'temp_in.json' 26 temp_json_out = f'temp_out_{target_lang}.json' 27 28 try: 29 # Convert YAML to JSON 30 json_content = yaml_to_json(input_yaml) 31 with open(temp_json_in, 'w', encoding='utf-8') as f: 32 f.write(json_content) 33 34 # Run translator-ai 35 cmd = [ 36 'translator-ai', 37 temp_json_in, 38 '-l', target_lang, 39 '-o', temp_json_out 40 ] 41 subprocess.run(cmd, check=True) 42 43 # Read translated JSON and convert back to YAML 44 with open(temp_json_out, 'r', encoding='utf-8') as f: 45 translated_json = f.read() 46 47 yaml_content = json_to_yaml(translated_json) 48 49 # Write YAML output 50 with open(output_yaml, 'w', encoding='utf-8') as f: 51 f.write(yaml_content) 52 53 print(f"✓ Translated {input_yaml} to {output_yaml}") 54 55 finally: 56 # Cleanup temp files 57 for f in [temp_json_in, temp_json_out]: 58 if os.path.exists(f): 59 os.remove(f) 60 61# Usage 62if __name__ == "__main__": 63 languages = ['es', 'fr', 'de', 'ja'] 64 65 for lang in languages: 66 translate_yaml_file( 67 'i18n/en.yaml', 68 lang, 69 f'i18n/{lang}.yaml' 70 )
Create translate-yaml.js
:
1#!/usr/bin/env node 2const fs = require('fs'); 3const yaml = require('js-yaml'); 4const { execSync } = require('child_process'); 5const path = require('path'); 6 7function translateYamlFile(inputPath, targetLang, outputPath) { 8 console.log(`Translating ${inputPath} to ${targetLang}...`); 9 10 // Read and parse YAML 11 const yamlContent = fs.readFileSync(inputPath, 'utf8'); 12 const data = yaml.load(yamlContent); 13 14 // Write temporary JSON 15 const tempJsonIn = `temp_${path.basename(inputPath)}.json`; 16 const tempJsonOut = `temp_${path.basename(inputPath)}_${targetLang}.json`; 17 18 fs.writeFileSync(tempJsonIn, JSON.stringify(data, null, 2)); 19 20 try { 21 // Translate using translator-ai 22 execSync(`translator-ai ${tempJsonIn} -l ${targetLang} -o ${tempJsonOut}`); 23 24 // Read translated JSON 25 const translatedData = JSON.parse(fs.readFileSync(tempJsonOut, 'utf8')); 26 27 // Convert back to YAML 28 const translatedYaml = yaml.dump(translatedData, { 29 indent: 2, 30 lineWidth: -1, 31 noRefs: true 32 }); 33 34 // Write output YAML 35 fs.writeFileSync(outputPath, translatedYaml); 36 console.log(`✓ Created ${outputPath}`); 37 38 } finally { 39 // Cleanup 40 [tempJsonIn, tempJsonOut].forEach(f => { 41 if (fs.existsSync(f)) fs.unlinkSync(f); 42 }); 43 } 44} 45 46// Example usage 47const languages = ['es', 'fr', 'de']; 48languages.forEach(lang => { 49 translateYamlFile( 50 'i18n/en.yaml', 51 lang, 52 `i18n/${lang}.yaml` 53 ); 54});
Hugo 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:
Create hugo-translate-files.sh
:
1#!/bin/bash 2# Translate Hugo content files using filename convention 3 4SOURCE_LANG="en" 5TARGET_LANGS=("es" "fr" "de" "ja") 6 7# Find all English content files 8find content -name "*.${SOURCE_LANG}.md" | while read -r file; do 9 # Extract base filename without language suffix 10 base_name="${file%.${SOURCE_LANG}.md}" 11 12 for lang in "${TARGET_LANGS[@]}"; do 13 output_file="${base_name}.${lang}.md" 14 15 # Skip if translation already exists 16 if [ -f "$output_file" ]; then 17 echo "Skipping $output_file (already exists)" 18 continue 19 fi 20 21 # Extract front matter 22 awk '/^---$/{p=1; next} p&&/^---$/{exit} p' "$file" > temp_frontmatter.yaml 23 24 # Convert front matter to JSON 25 npx js-yaml temp_frontmatter.yaml > temp_frontmatter.json 26 27 # Translate front matter 28 translator-ai temp_frontmatter.json -l "$lang" -o "temp_translated.json" 29 30 # Convert back to YAML 31 echo "---" > "$output_file" 32 npx js-yaml temp_translated.json >> "$output_file" 33 echo "---" >> "$output_file" 34 35 # Copy content (you might want to translate this too) 36 awk '/^---$/{p++} p==2{print}' "$file" | tail -n +2 >> "$output_file" 37 38 echo "Created $output_file" 39 done 40 41 # Cleanup 42 rm -f temp_frontmatter.yaml temp_frontmatter.json temp_translated.json 43done
config.yaml
):1defaultContentLanguage: en 2defaultContentLanguageInSubdir: false 3 4languages: 5 en: 6 contentDir: content/en 7 languageName: English 8 weight: 1 9 es: 10 contentDir: content/es 11 languageName: Español 12 weight: 2 13 fr: 14 contentDir: content/fr 15 languageName: Français 16 weight: 3 17 18# Rest of your config...
hugo-translate-dirs.js
):1#!/usr/bin/env node 2const fs = require('fs-extra'); 3const path = require('path'); 4const yaml = require('js-yaml'); 5const { execSync } = require('child_process'); 6const glob = require('glob'); 7 8const SOURCE_LANG = 'en'; 9const TARGET_LANGS = ['es', 'fr', 'de']; 10 11async function translateHugoContent() { 12 // Ensure target directories exist 13 for (const lang of TARGET_LANGS) { 14 await fs.ensureDir(`content/${lang}`); 15 } 16 17 // Find all content files in source language 18 const files = glob.sync(`content/${SOURCE_LANG}/**/*.md`); 19 20 for (const file of files) { 21 const relativePath = path.relative(`content/${SOURCE_LANG}`, file); 22 23 for (const lang of TARGET_LANGS) { 24 const targetFile = path.join(`content/${lang}`, relativePath); 25 26 // Skip if already translated 27 if (await fs.pathExists(targetFile)) { 28 console.log(`Skipping ${targetFile} (exists)`); 29 continue; 30 } 31 32 await translateFile(file, targetFile, lang); 33 } 34 } 35} 36 37async function translateFile(sourceFile, targetFile, targetLang) { 38 console.log(`Translating ${sourceFile} to ${targetLang}...`); 39 40 const content = await fs.readFile(sourceFile, 'utf8'); 41 const frontMatterMatch = content.match(/^---\n([\s\S]*?)\n---/); 42 43 if (!frontMatterMatch) { 44 // No front matter, just copy 45 await fs.ensureDir(path.dirname(targetFile)); 46 await fs.copyFile(sourceFile, targetFile); 47 return; 48 } 49 50 // Parse front matter 51 const frontMatter = yaml.load(frontMatterMatch[1]); 52 const body = content.substring(frontMatterMatch[0].length); 53 54 // Extract translatable fields 55 const translatable = { 56 title: frontMatter.title || '', 57 description: frontMatter.description || '', 58 summary: frontMatter.summary || '', 59 keywords: frontMatter.keywords || [] 60 }; 61 62 // Save for translation 63 await fs.writeJson('temp_meta.json', translatable); 64 65 // Translate 66 execSync(`translator-ai temp_meta.json -l ${targetLang} -o temp_translated.json`); 67 68 // Read translations 69 const translated = await fs.readJson('temp_translated.json'); 70 71 // Update front matter 72 Object.assign(frontMatter, translated); 73 74 // Write translated file 75 await fs.ensureDir(path.dirname(targetFile)); 76 const newContent = `---\n${yaml.dump(frontMatter)}---${body}`; 77 await fs.writeFile(targetFile, newContent); 78 79 // Cleanup 80 await fs.remove('temp_meta.json'); 81 await fs.remove('temp_translated.json'); 82 83 console.log(`✓ Created ${targetFile}`); 84} 85 86// Run translation 87translateHugoContent().catch(console.error);
1npm install -g translator-ai js-yaml
1# Makefile for Hugo translations 2LANGUAGES := es fr de ja zh 3SOURCE_YAML := i18n/en.yaml 4THEME_DIR := themes/your-theme 5 6.PHONY: translate 7translate: $(foreach lang,$(LANGUAGES),translate-$(lang)) 8 9translate-%: 10 @echo "Translating to $*..." 11 @npx js-yaml $(SOURCE_YAML) > temp.json 12 @translator-ai temp.json -l $* -o temp_$*.json 13 @npx js-yaml temp_$*.json > i18n/$*.yaml 14 @rm temp.json temp_$*.json 15 @echo "✓ Created i18n/$*.yaml" 16 17.PHONY: translate-theme 18translate-theme: 19 @for lang in $(LANGUAGES); do \ 20 make translate-theme-$$lang; \ 21 done 22 23translate-theme-%: 24 @echo "Translating theme to $*..." 25 @npx js-yaml $(THEME_DIR)/i18n/en.yaml > temp_theme.json 26 @translator-ai temp_theme.json -l $* -o temp_theme_$*.json 27 @npx js-yaml temp_theme_$*.json > $(THEME_DIR)/i18n/$*.yaml 28 @rm temp_theme.json temp_theme_$*.json 29 30.PHONY: clean 31clean: 32 @rm -f temp*.json 33 34# Translate everything 35.PHONY: all 36all: translate translate-theme
Usage:
1# Translate to all languages 2make all 3 4# Translate to specific language 5make translate-es 6 7# Translate theme files 8make translate-theme
Here's a comprehensive script that handles both content and i18n translations:
1#!/usr/bin/env node 2// hugo-complete-translator.js 3const fs = require('fs-extra'); 4const path = require('path'); 5const yaml = require('js-yaml'); 6const { execSync } = require('child_process'); 7const glob = require('glob'); 8 9class HugoTranslator { 10 constructor(targetLanguages = ['es', 'fr', 'de']) { 11 this.targetLanguages = targetLanguages; 12 this.tempFiles = []; 13 } 14 15 async translateSite() { 16 console.log('Starting Hugo site translation...\n'); 17 18 // 1. Translate i18n files 19 await this.translateI18nFiles(); 20 21 // 2. Translate content 22 await this.translateContent(); 23 24 // 3. Update config 25 await this.updateConfig(); 26 27 console.log('\nTranslation complete!'); 28 } 29 30 async translateI18nFiles() { 31 console.log('Translating i18n files...'); 32 const i18nFiles = glob.sync('i18n/en.{yaml,yml,toml}'); 33 34 for (const file of i18nFiles) { 35 const ext = path.extname(file); 36 37 for (const lang of this.targetLanguages) { 38 const outputFile = `i18n/${lang}${ext}`; 39 40 if (await fs.pathExists(outputFile)) { 41 console.log(` Skipping ${outputFile} (exists)`); 42 continue; 43 } 44 45 // Convert to JSON 46 const tempJson = `temp_i18n_${lang}.json`; 47 await this.convertToJson(file, tempJson); 48 49 // Translate 50 const translatedJson = `temp_i18n_${lang}_translated.json`; 51 execSync(`translator-ai ${tempJson} -l ${lang} -o ${translatedJson}`); 52 53 // Convert back 54 await this.convertFromJson(translatedJson, outputFile, ext); 55 56 // Cleanup 57 await fs.remove(tempJson); 58 await fs.remove(translatedJson); 59 60 console.log(` ✓ Created ${outputFile}`); 61 } 62 } 63 } 64 65 async translateContent() { 66 console.log('\nTranslating content...'); 67 68 // Detect translation method 69 const useContentDirs = await fs.pathExists('content/en'); 70 71 if (useContentDirs) { 72 await this.translateContentByDirectory(); 73 } else { 74 await this.translateContentByFilename(); 75 } 76 } 77 78 async translateContentByDirectory() { 79 const files = glob.sync('content/en/**/*.md'); 80 81 for (const file of files) { 82 const relativePath = path.relative('content/en', file); 83 84 for (const lang of this.targetLanguages) { 85 const targetFile = path.join('content', lang, relativePath); 86 87 if (await fs.pathExists(targetFile)) continue; 88 89 await this.translateMarkdownFile(file, targetFile, lang); 90 } 91 } 92 } 93 94 async translateContentByFilename() { 95 const files = glob.sync('content/**/*.en.md'); 96 97 for (const file of files) { 98 const baseName = file.replace('.en.md', ''); 99 100 for (const lang of this.targetLanguages) { 101 const targetFile = `${baseName}.${lang}.md`; 102 103 if (await fs.pathExists(targetFile)) continue; 104 105 await this.translateMarkdownFile(file, targetFile, lang); 106 } 107 } 108 } 109 110 async translateMarkdownFile(sourceFile, targetFile, targetLang) { 111 const content = await fs.readFile(sourceFile, 'utf8'); 112 const frontMatterMatch = content.match(/^---\n([\s\S]*?)\n---/); 113 114 if (!frontMatterMatch) { 115 await fs.copy(sourceFile, targetFile); 116 return; 117 } 118 119 const frontMatter = yaml.load(frontMatterMatch[1]); 120 const body = content.substring(frontMatterMatch[0].length); 121 122 // Translate front matter 123 const translatable = this.extractTranslatableFields(frontMatter); 124 const tempJson = `temp_content_${path.basename(sourceFile)}.json`; 125 const translatedJson = `${tempJson}.translated`; 126 127 await fs.writeJson(tempJson, translatable); 128 execSync(`translator-ai ${tempJson} -l ${targetLang} -o ${translatedJson}`); 129 130 const translated = await fs.readJson(translatedJson); 131 Object.assign(frontMatter, translated); 132 133 // Write translated file 134 await fs.ensureDir(path.dirname(targetFile)); 135 const newContent = `---\n${yaml.dump(frontMatter)}---${body}`; 136 await fs.writeFile(targetFile, newContent); 137 138 // Cleanup 139 await fs.remove(tempJson); 140 await fs.remove(translatedJson); 141 142 console.log(` ✓ ${targetFile}`); 143 } 144 145 extractTranslatableFields(frontMatter) { 146 const fields = ['title', 'description', 'summary', 'keywords', 'tags']; 147 const translatable = {}; 148 149 fields.forEach(field => { 150 if (frontMatter[field]) { 151 translatable[field] = frontMatter[field]; 152 } 153 }); 154 155 return translatable; 156 } 157 158 async convertToJson(inputFile, outputFile) { 159 const ext = path.extname(inputFile); 160 const content = await fs.readFile(inputFile, 'utf8'); 161 let data; 162 163 if (ext === '.yaml' || ext === '.yml') { 164 data = yaml.load(content); 165 } else if (ext === '.toml') { 166 // You'd need a TOML parser here 167 throw new Error('TOML support not implemented in this example'); 168 } 169 170 await fs.writeJson(outputFile, data, { spaces: 2 }); 171 } 172 173 async convertFromJson(inputFile, outputFile, format) { 174 const data = await fs.readJson(inputFile); 175 let content; 176 177 if (format === '.yaml' || format === '.yml') { 178 content = yaml.dump(data, { 179 indent: 2, 180 lineWidth: -1, 181 noRefs: true 182 }); 183 } else if (format === '.toml') { 184 throw new Error('TOML support not implemented in this example'); 185 } 186 187 await fs.writeFile(outputFile, content); 188 } 189 190 async updateConfig() { 191 console.log('\nUpdating Hugo config...'); 192 193 const configFile = glob.sync('config.{yaml,yml,toml,json}')[0]; 194 if (!configFile) return; 195 196 // This is a simplified example - you'd need to properly parse and update 197 console.log(' ! Remember to update your config.yaml with language settings'); 198 } 199} 200 201// Run the translator 202if (require.main === module) { 203 const translator = new HugoTranslator(['es', 'fr', 'de']); 204 translator.translateSite().catch(console.error); 205} 206 207module.exports = HugoTranslator;
If you're using Hugo Modules, you can create a translation module:
1// go.mod 2module github.com/yourusername/hugo-translator 3 4go 1.19 5 6require ( 7 github.com/yourusername/your-theme v1.0.0 8)
Then in your package.json
:
1{ 2 "scripts": { 3 "translate": "node hugo-complete-translator.js", 4 "translate:content": "node hugo-complete-translator.js --content-only", 5 "translate:i18n": "node hugo-complete-translator.js --i18n-only", 6 "build": "npm run translate && hugo" 7 } 8}
For Jekyll posts with YAML front matter:
1#!/usr/bin/env python3 2# translate-jekyll-posts.py 3 4import os 5import yaml 6import json 7import subprocess 8import frontmatter 9 10def translate_jekyll_post(post_path, target_lang, output_dir): 11 """Translate Jekyll post including front matter""" 12 13 # Load post with front matter 14 post = frontmatter.load(post_path) 15 16 # Extract translatable front matter fields 17 translatable = { 18 'title': post.metadata.get('title', ''), 19 'description': post.metadata.get('description', ''), 20 'excerpt': post.metadata.get('excerpt', '') 21 } 22 23 # Save as JSON for translation 24 with open('temp_meta.json', 'w', encoding='utf-8') as f: 25 json.dump(translatable, f, ensure_ascii=False, indent=2) 26 27 # Translate 28 subprocess.run([ 29 'translator-ai', 30 'temp_meta.json', 31 '-l', target_lang, 32 '-o', f'temp_meta_{target_lang}.json' 33 ]) 34 35 # Load translations 36 with open(f'temp_meta_{target_lang}.json', 'r', encoding='utf-8') as f: 37 translations = json.load(f) 38 39 # Update post metadata 40 for key, value in translations.items(): 41 if value: # Only update if translation exists 42 post.metadata[key] = value 43 44 # Add language to metadata 45 post.metadata['lang'] = target_lang 46 47 # Save translated post 48 output_path = os.path.join(output_dir, os.path.basename(post_path)) 49 with open(output_path, 'w', encoding='utf-8') as f: 50 f.write(frontmatter.dumps(post)) 51 52 # Cleanup 53 os.remove('temp_meta.json') 54 os.remove(f'temp_meta_{target_lang}.json') 55 56# Translate all posts 57for lang in ['es', 'fr', 'de']: 58 os.makedirs(f'_posts/{lang}', exist_ok=True) 59 for post in os.listdir('_posts/en'): 60 if post.endswith('.md'): 61 translate_jekyll_post( 62 f'_posts/en/{post}', 63 lang, 64 f'_posts/{lang}' 65 )
js-yaml
with proper options to maintain YAML structureIf 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.
1git clone https://github.com/DatanoiseTV/translator-ai.git 2cd translator-ai 3npm install 4npm run build
1npm start -- test.json -l es -o output.json
This project requires attribution for both commercial and non-commercial use. See LICENSE file for details.
Contributions are welcome! Please feel free to submit a Pull Request.
For issues, questions, or suggestions, please open an issue on GitHub.
If you find this tool useful, consider supporting the development:
No vulnerabilities found.
No security vulnerabilities found.