MCPサーバーとは?
MCPサーバーは、AIアシスタント(Claudeなど)が直接JSONファイルを翻訳できる特殊なサービスです。特定のインターフェースを通じてAIモデルと通信し、ファイルの自動翻訳を実現します。MCPサーバーの使用方法は?
MCPサーバーを使用するには、まずAIプロバイダー(Google GeminiやOllamaなど)を設定する必要があります。その後、AIアシスタント(Claudeなど)でこのサービスを呼び出すと、直接JSONファイルを翻訳できます。適用シナリオ
MCPサーバーは、多言語のJSONファイルを頻繁に翻訳する必要がある開発者やチームに非常に適しています。大量の手動翻訳時間を節約し、作業効率を向上させます。主な機能
利点と制限
使い方
使用例
よくある質問
関連リソース
🚀 translator-ai
Fast and efficient JSON i18n translator supporting multiple AI providers (Google Gemini, OpenAI & Ollama/DeepSeek) with intelligent caching, multi-file deduplication, and MCP integration.
🚀 クイックスタート
このツールは、JSON形式のi18nファイルを高速かつ効率的に翻訳することができます。複数のAIプロバイダー(Google Gemini、OpenAI、Ollama/DeepSeek)をサポートし、インテリジェントなキャッシュ、マルチファイルの重複排除、MCP統合機能を備えています。
✨ 主な機能
- 複数のAIプロバイダー: Google Gemini、OpenAI(クラウド)、Ollama/DeepSeek(ローカル)から選択して翻訳を行えます。
- マルチファイルサポート: 複数のファイルを自動的に重複排除して処理し、API呼び出しを節約します。
- インクリメンタルキャッシュ: 新しいまたは変更された文字列のみを翻訳するため、API呼び出しを大幅に削減します。
- バッチ処理: 翻訳を最適なバッチサイズでグループ化し、パフォーマンスを向上させます。
- パスの保持: ネストされたオブジェクトや配列を含む正確なJSON構造を維持します。
- クロスプラットフォーム: Windows、macOS、Linuxで動作し、自動的にキャッシュディレクトリを検出します。
- 開発者にやさしい: 組み込みのパフォーマンス統計と進捗インジケーターがあります。
- コスト効率が高い: スマートなキャッシュと重複排除により、APIの使用を最小限に抑えます。
- 言語検出: 英語を前提とせずに、自動的にソース言語を検出します。
- 複数のターゲット言語: 1つのコマンドで複数の言語に翻訳できます。
- 翻訳メタデータ: オプションで、出力ファイルに翻訳の詳細を含めることができ、追跡に役立ちます。
- ドライランモード: API呼び出しを行わずに、どの部分が翻訳されるかをプレビューできます。
- フォーマットの保持: URL、メールアドレス、日付、数字、テンプレート変数などを変更せずに維持します。
📦 インストール
グローバルインストール(推奨)
npm install -g translator-ai
ローカルインストール
npm install translator-ai
💻 使用例
基本的な使用法
# 単一のファイルを翻訳する
translator-ai source.json -l es -o spanish.json
# 重複排除を行いながら複数のファイルを翻訳する
translator-ai src/locales/en/*.json -l es -o "{dir}/{name}.{lang}.json"
# グロブパターンを使用する
translator-ai "src/**/*.en.json" -l fr -o "{dir}/{name}.fr.json"
コマンドラインオプション
translator-ai <inputFiles...> [options]
引数:
inputFiles ソースJSONファイルのパスまたはグロブパターン
オプション:
-l, --lang <langCodes> ターゲット言語コード(複数の場合はカンマ区切り)
-o, --output <pattern> 出力ファイルのパスまたはパターン
--stdout ファイルではなく標準出力に出力する
--stats 詳細なパフォーマンス統計を表示する
--no-cache インクリメンタル翻訳キャッシュを無効にする
--cache-file <path> カスタムキャッシュファイルのパス
--provider <type> 翻訳プロバイダー: gemini, openai, または ollama (デフォルト: gemini)
--ollama-url <url> Ollama APIのURL (デフォルト: http://localhost:11434)
--ollama-model <model> Ollamaモデル名 (デフォルト: deepseek-r1:latest)
--gemini-model <model> Geminiモデル名 (デフォルト: gemini-2.0-flash-lite)
--openai-model <model> OpenAIモデル名 (デフォルト: gpt-4o-mini)
--list-providers 利用可能な翻訳プロバイダーをリストする
--verbose デバッグ用の詳細出力を有効にする
--detect-source 英語を前提とせずに、自動的にソース言語を検出する
--dry-run API呼び出しを行わずに、どの部分が翻訳されるかをプレビューする
--preserve-formats URL、メールアドレス、数字、日付、その他のフォーマットを維持する
--metadata 出力ファイルに翻訳メタデータを追加する(一部のi18nパーサーで破損する可能性があります)
--sort-keys 出力JSONのキーをアルファベット順にソートする
--check-keys すべてのソースキーが出力に存在することを検証する(キーが欠落している場合はエラーで終了する)
-h, --help ヘルプを表示する
-V, --version バージョンを表示する
出力パターン変数(複数のファイル用):
{dir} - 元のディレクトリパス
{name} - 拡張子を除いた元のファイル名
{lang} - ターゲット言語コード
具体的な使用例
単一のファイルを翻訳する
translator-ai en.json -l es -o es.json
パターンを使用して複数のファイルを翻訳する
# ディレクトリ内のすべてのJSONファイル
translator-ai locales/en/*.json -l es -o "locales/es/{name}.json"
# 再帰的なグロブパターン
translator-ai "src/**/en.json" -l fr -o "{dir}/fr.json"
# 複数の特定のファイル
translator-ai file1.json file2.json file3.json -l de -o "{name}.de.json"
重複排除による節約を伴う翻訳
# API呼び出しが何回節約されたかを含む統計を表示する
translator-ai src/i18n/*.json -l ja -o "{dir}/{name}.{lang}.json" --stats
標準出力に出力する(パイプに使用するのに便利)
translator-ai en.json -l de --stdout > de.json
jqで出力を解析する
translator-ai en.json -l de --stdout | jq
キャッシュを無効にして新しい翻訳を行う
translator-ai en.json -l ja -o ja.json --no-cache
カスタムキャッシュの場所を使用する
translator-ai en.json -l ko -o ko.json --cache-file /path/to/cache.json
Ollamaを使用してローカルで翻訳する
# Ollamaを使用した基本的な使用法
translator-ai en.json -l es -o es.json --provider ollama
# 別のOllamaモデルを使用する
translator-ai en.json -l fr -o fr.json --provider ollama --ollama-model llama2:latest
# リモートのOllamaインスタンスに接続する
translator-ai en.json -l de -o de.json --provider ollama --ollama-url http://192.168.1.100:11434
# 利用可能なプロバイダーを確認する
translator-ai --list-providers
高度な機能
# ソース言語を自動的に検出する
translator-ai content.json -l es -o spanish.json --detect-source
# 一度に複数の言語に翻訳する
translator-ai en.json -l es,fr,de,ja -o translations/{lang}.json
# ドライラン - API呼び出しを行わずに、どの部分が翻訳されるかを確認する
translator-ai en.json -l es -o es.json --dry-run
# フォーマットを維持する(URL、メールアドレス、日付、数字、テンプレート変数)
translator-ai app.json -l fr -o app-fr.json --preserve-formats
# 翻訳メタデータを含める(互換性を確保するため、デフォルトでは無効)
translator-ai en.json -l fr -o fr.json --metadata
# キーをアルファベット順にソートして、出力を一貫性のあるものにする
translator-ai en.json -l fr -o fr.json --sort-keys
# すべてのキーが翻訳に含まれていることを検証する
translator-ai en.json -l fr -o fr.json --check-keys
# 別のGeminiモデルを使用する
translator-ai en.json -l es -o es.json --gemini-model gemini-2.5-flash
# 機能を組み合わせる
translator-ai src/**/*.json -l es,fr,de -o "{dir}/{name}.{lang}.json" \
--detect-source --preserve-formats --stats --check-keys
利用可能なGeminiモデル
--gemini-model
オプションを使用すると、さまざまなGeminiモデルから選択できます。人気のあるオプションは次のとおりです。
gemini-2.0-flash-lite
(デフォルト) - ほとんどの翻訳に高速かつ効率的gemini-2.5-flash
- 新しい機能を備えた高性能モデルgemini-pro
- 複雑な翻訳に対する高度な理解能力gemini-1.5-pro
- 前世代のプロモデルgemini-1.5-flash
- 前世代の高速モデル
使用例:
# 最新のフラッシュモデルを使用する
translator-ai en.json -l es -o es.json --gemini-model gemini-2.5-flash
# デフォルトの軽量モデルを使用する
translator-ai en.json -l fr -o fr.json --gemini-model gemini-2.0-flash-lite
利用可能なOpenAIモデル
--openai-model
オプションを使用すると、さまざまなOpenAIモデルから選択できます。人気のあるオプションは次のとおりです。
gpt-4o-mini
(デフォルト) - ほとんどの翻訳にコスト効率が高く、高速gpt-4o
- 高度な理解能力を備えた最も強力なモデルgpt-4-turbo
- 前世代のフラッグシップモデルgpt-3.5-turbo
- 単純な翻訳に高速かつ効率的
使用例:
# デフォルトのモデルでOpenAIを使用する
translator-ai en.json -l es -o es.json --provider openai
# 複雑な翻訳にGPT-4oを使用する
translator-ai en.json -l ja -o ja.json --provider openai --openai-model gpt-4o
# 高速で単純な翻訳にGPT-3.5-turboを使用する
translator-ai en.json -l fr -o fr.json --provider openai --openai-model gpt-3.5-turbo
翻訳メタデータ
--metadata
フラグを使用して有効にすると、translator-aiは翻訳の追跡に役立つメタデータを追加します。
{
"_translator_metadata": {
"tool": "translator-ai v1.1.0",
"repository": "https://github.com/DatanoiseTV/translator-ai",
"provider": "Google Gemini",
"source_language": "English",
"target_language": "fr",
"timestamp": "2025-06-20T12:34:56.789Z",
"total_strings": 42,
"source_file": "en.json"
},
"greeting": "Bonjour",
"farewell": "Au revoir"
}
メタデータは、i18nパーサーとの互換性を確保するため、デフォルトでは無効になっています。--metadata
を使用して有効にしてください。
キーのソート
--sort-keys
フラグを使用すると、出力のすべてのJSONキーをアルファベット順にソートできます。
translator-ai en.json -l es -o es.json --sort-keys
これにより、翻訳間で一貫した順序が保たれ、差分がクリーンになります。キーは次のようにソートされます。
- 大文字と小文字を区別せずに(a, B, c、B, a, cではなく)
- すべてのネストされたオブジェクトを再帰的に
- 配列は要素の順序を維持します
キーの検証
--check-keys
フラグを使用すると、翻訳の完全性を確認できます。
translator-ai en.json -l es -o es.json --check-keys
この機能は次のことを行います。
- すべてのソースキーが翻訳された出力に存在することを検証する
- 欠落しているキーとその完全なパスを報告する
- キーが欠落している場合はエラーコード1で終了する
- 翻訳APIの失敗やフォーマットの問題を検出するのに役立つ
- 検証時にメタデータキーは無視する
サポートされている言語コード
標準化された言語コードであれば、すべてサポートされています。
📚 ドキュメント
動作原理
- パース: JSON構造を読み取り、パスに平坦化します。
- 重複排除: 複数のファイルを処理する際に、共有される文字列を識別します。
- キャッシュ: 以前に翻訳された文字列のキャッシュをチェックします。
- 差分検出: 翻訳が必要な新しいまたは変更された文字列を識別します。
- バッチ処理: 一意の文字列を最適なバッチサイズにグループ化し、APIの効率を向上させます。
- 翻訳: バッチを選択したプロバイダー(Gemini APIまたはローカルのOllama)に送信します。
- 再構築: 翻訳を含む正確なJSON構造を再構築します。
- キャッシュ更新: 新しい翻訳をキャッシュに保存し、将来の使用に備えます。
マルチファイルの重複排除
複数のファイルを翻訳する際、translator-aiは自動的に次のことを行います。
- ファイル間の重複する文字列を識別する
- 各一意の文字列を一度だけ翻訳する
- すべてのファイルに一貫して同じ翻訳を適用する
- 大幅なAPI呼び出しの節約と一貫性の確保
例: 10個のファイルが50%の文字列を共有する場合、API呼び出しを約50%節約できます!
キャッシュ管理
デフォルトのキャッシュ場所
- Windows:
%APPDATA%\translator-ai\translation-cache.json
- macOS:
~/Library/Caches/translator-ai/translation-cache.json
- Linux:
~/.cache/translator-ai/translation-cache.json
キャッシュファイルは、次の要素で索引付けされた翻訳を保存します。
- ソースファイルのパス
- ターゲット言語
- ソース文字列のSHA-256ハッシュ
これにより、次のことが保証されます。
- 変更された文字列は再翻訳される
- 削除された文字列はキャッシュから削除される
- 複数のプロジェクトが同じキャッシュを共有しても競合しない
プロバイダーの比較
Google Gemini
- 利点: 高速で正確で、大規模なバッチを効率的に処理します。
- 欠点: APIキーが必要で、使用料が発生します。
- 利用可能なモデル:
gemini-2.0-flash-lite
(デフォルト) - 最も高速でコスト効率が高いgemini-pro
- バランスの良いパフォーマンスgemini-1.5-pro
- 高度な機能gemini-1.5-flash
- 高速で品質が良い
- 最適な用途: 本番環境での使用、大規模なプロジェクト、精度が重要な場合
Ollama (ローカル)
- 利点: 無料で、ローカルで実行され、API制限がなく、プライバシーに配慮されています。
- 欠点: 速度が遅く、ローカルリソースが必要で、モデルのダウンロードが必要です。
- 最適な用途: 開発、プライバシーに敏感なデータ、コスト意識の高いプロジェクト
パフォーマンスのヒント
- キャッシュを使用する(デフォルトで有効): API呼び出しを最小限に抑えます。
- 同じセッションで複数のファイルをバッチ処理する: ウォームキャッシュを活用します。
--stats
フラグを使用する: パフォーマンスを監視し、最適化の機会を見つけます。- ソースファイルを一貫性を保つ: キャッシュヒットを最大化します。
- Ollamaの場合: パフォーマンスを向上させるために、高性能なマシンを使用します。
API制限とコスト
Gemini API
- 最適な速度とコストのために、Gemini 2.0 Flash Liteモデルを使用します。
- 入力キーの数に応じて、最適なバッチサイズを動的に選択します。
- API呼び出しごとに最大100個の文字列をバッチ処理します。
- 現在の料金については、Googleの料金表を確認してください。
Ollama
- APIコストがかからず、すべての処理があなたのハードウェア上で行われます。
- パフォーマンスはあなたのマシンの能力に依存します。
- さまざまな速度と品質のトレードオフを持つモデルをサポートしています。
モデルコンテキストプロトコル (MCP) との連携
translator-aiはMCPサーバーとして使用でき、Claude DesktopなどのAIアシスタントが直接ファイルを翻訳できるようになります。
MCPの設定
Claude Desktopの設定に追加します。
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"translator-ai": {
"command": "npx",
"args": [
"-y",
"translator-ai-mcp"
],
"env": {
"GEMINI_API_KEY": "your-gemini-api-key-here"
// またはOllamaの場合:
// "TRANSLATOR_PROVIDER": "ollama"
}
}
}
}
MCPの使用例
設定が完了すると、Claudeにファイルを翻訳するように依頼できます。
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.
複数のファイルを重複排除して翻訳する場合:
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.
利用可能なMCPツール
-
translate_json: 単一のJSONファイルを翻訳する
inputFile
: ソースファイルのパスtargetLanguage
: ターゲット言語コードoutputFile
: 出力ファイルのパス
-
translate_multiple: 重複排除を行いながら複数のファイルを翻訳する
pattern
: ファイルパターンまたはパスtargetLanguage
: ターゲット言語コードoutputPattern
: {dir}, {name}, {lang}変数を含む出力パターンshowStats
: 重複排除の統計を表示する(オプション)
静的サイトジェネレーターとの統合
YAMLファイル(Hugo、Jekyllなど)の操作
translator-aiはJSONファイルで動作するため、YAMLをJSONに変換し、その後戻す必要があります。以下は実用的なワークフローです。
YAML変換ツールのセットアップ
# yaml変換ツールをインストールする
npm install -g js-yaml
# または
pip install pyyaml
HugoのYAML変換の例
- 翻訳スクリプトを作成する (
translate-hugo.sh
):
#!/bin/bash
# translate-hugo.sh - HugoのYAML i18nファイルを翻訳する
# YAMLファイルを翻訳する関数
translate_yaml() {
local input_file=$1
local lang=$2
local output_file=$3
echo "Translating $input_file to $lang..."
# YAMLをJSONに変換する
npx js-yaml $input_file > temp_input.json
# JSONを翻訳する
translator-ai temp_input.json -l $lang -o temp_output.json
# JSONをYAMLに戻す
npx js-yaml temp_output.json > $output_file
# 一時ファイルを削除する
rm temp_input.json temp_output.json
}
# Hugoのi18nファイルを翻訳する
translate_yaml themes/your-theme/i18n/en.yaml es themes/your-theme/i18n/es.yaml
translate_yaml themes/your-theme/i18n/en.yaml fr themes/your-theme/i18n/fr.yaml
translate_yaml themes/your-theme/i18n/en.yaml de themes/your-theme/i18n/de.yaml
- より複雑なシナリオ用のPythonベースのコンバーター:
#!/usr/bin/env python3
# hugo-translate.py
import yaml
import json
import subprocess
import sys
import os
def yaml_to_json(yaml_file):
"""YAMLをJSONに変換する"""
with open(yaml_file, 'r', encoding='utf-8') as f:
data = yaml.safe_load(f)
return json.dumps(data, ensure_ascii=False, indent=2)
def json_to_yaml(json_str):
"""JSONをYAMLに戻す"""
data = json.loads(json_str)
return yaml.dump(data, allow_unicode=True, default_flow_style=False)
def translate_yaml_file(input_yaml, target_lang, output_yaml):
"""translator-aiを使用してYAMLファイルを翻訳する"""
# 一時JSONファイルを作成する
temp_json_in = 'temp_in.json'
temp_json_out = f'temp_out_{target_lang}.json'
try:
# YAMLをJSONに変換する
json_content = yaml_to_json(input_yaml)
with open(temp_json_in, 'w', encoding='utf-8') as f:
f.write(json_content)
# translator-aiを実行する
cmd = [
'translator-ai',
temp_json_in,
'-l', target_lang,
'-o', temp_json_out
]
subprocess.run(cmd, check=True)
# 翻訳されたJSONを読み取り、YAMLに戻す
with open(temp_json_out, 'r', encoding='utf-8') as f:
translated_json = f.read()
yaml_content = json_to_yaml(translated_json)
# YAML出力を書き込む
with open(output_yaml, 'w', encoding='utf-8') as f:
f.write(yaml_content)
print(f"✓ Translated {input_yaml} to {output_yaml}")
finally:
# 一時ファイルを削除する
for f in [temp_json_in, temp_json_out]:
if os.path.exists(f):
os.remove(f)
# 使用例
if __name__ == "__main__":
languages = ['es', 'fr', 'de', 'ja']
for lang in languages:
translate_yaml_file(
'i18n/en.yaml',
lang,
f'i18n/{lang}.yaml'
)
適切なYAML処理を行うNode.jsソリューション
translate-yaml.js
を作成します。
#!/usr/bin/env node
const fs = require('fs');
const yaml = require('js-yaml');
const { execSync } = require('child_process');
const path = require('path');
function translateYamlFile(inputPath, targetLang, outputPath) {
console.log(`Translating ${inputPath} to ${targetLang}...`);
// YAMLを読み取り、解析する
const yamlContent = fs.readFileSync(inputPath, 'utf8');
const data = yaml.load(yamlContent);
// 一時JSONファイルを書き込む
const tempJsonIn = `temp_${path.basename(inputPath)}.json`;
const tempJsonOut = `temp_${path.basename(inputPath)}_${targetLang}.json`;
fs.writeFileSync(tempJsonIn, JSON.stringify(data, null, 2));
try {
// translator-aiを使用して翻訳する
execSync(`translator-ai ${tempJsonIn} -l ${targetLang} -o ${tempJsonOut}`);
// 翻訳されたJSONを読み取る
const translatedData = JSON.parse(fs.readFileSync(tempJsonOut, 'utf8'));
// JSONをYAMLに戻す
const translatedYaml = yaml.dump(translatedData, {
indent: 2,
lineWidth: -1,
noRefs: true
});
// 出力YAMLを書き込む
fs.writeFileSync(outputPath, translatedYaml);
console.log(`✓ Created ${outputPath}`);
} finally {
// 一時ファイルを削除する
[tempJsonIn, tempJsonOut].forEach(f => {
if (fs.existsSync(f)) fs.unlinkSync(f);
});
}
}
// 使用例
const languages = ['es', 'fr', 'de'];
languages.forEach(lang => {
translateYamlFile(
'i18n/en.yaml',
lang,
`i18n/${lang}.yaml`
);
});
実際のHugoのワークフロー
Hugoは、ファイル名による翻訳(about.en.md
, about.fr.md
)またはコンテンツディレクトリによる翻訳(content/en/
, content/fr/
)の2つの方法をサポートしています。以下は両方を自動化する方法です。
方法1: ファイル名による翻訳
hugo-translate-files.sh
を作成します。
#!/bin/bash
# ファイル名の規則を使用してHugoのコンテンツファイルを翻訳する
SOURCE_LANG="en"
TARGET_LANGS=("es" "fr" "de" "ja")
# すべての英語のコンテンツファイルを見つける
find content -name "*.${SOURCE_LANG}.md" | while read -r file; do
# 言語サフィックスを除いた基本ファイル名を抽出する
base_name="${file%.${SOURCE_LANG}.md}"
for lang in "${TARGET_LANGS[@]}"; do
output_file="${base_name}.${lang}.md"
# 翻訳がすでに存在する場合はスキップする
if [ -f "$output_file" ]; then
echo "Skipping $output_file (already exists)"
continue
fi
# フロントマターを抽出する
awk '/^---$/{p=1; next} p&&/^---$/{exit} p' "$file" > temp_frontmatter.yaml
# フロントマターをJSONに変換する
npx js-yaml temp_frontmatter.yaml > temp_frontmatter.json
# フロントマターを翻訳する
translator-ai temp_frontmatter.json -l "$lang" -o "temp_translated.json"
# JSONをYAMLに戻す
echo "---" > "$output_file"
npx js-yaml temp_translated.json >> "$output_file"
echo "---" >> "$output_file"
# コンテンツをコピーする(これも翻訳したい場合は)
awk '/^---$/{p++} p==2{print}' "$file" | tail -n +2 >> "$output_file"
echo "Created $output_file"
done
# 一時ファイルを削除する
rm -f temp_frontmatter.yaml temp_frontmatter.json temp_translated.json
done
方法2: コンテンツディレクトリによる翻訳
- Hugoの設定をセットアップする (
config.yaml
):
defaultContentLanguage: en
defaultContentLanguageInSubdir: false
languages:
en:
contentDir: content/en
languageName: English
weight: 1
es:
contentDir: content/es
languageName: Español
weight: 2
fr:
contentDir: content/fr
languageName: Français
weight: 3
# 残りの設定...
- 翻訳スクリプトを作成する (
hugo-translate-dirs.js
):
#!/usr/bin/env node
const fs = require('fs-extra');
const path = require('path');
const yaml = require('js-yaml');
const { execSync } = require('child_process');
const glob = require('glob');
const SOURCE_LANG = 'en';
const TARGET_LANGS = ['es', 'fr', 'de'];
async function translateHugoContent() {
// ターゲットディレクトリが存在することを確認する
for (const lang of TARGET_LANGS) {
await fs.ensureDir(`content/${lang}`);
}
// ソース言語のすべてのコンテンツファイルを見つける
const files = glob.sync(`content/${SOURCE_LANG}/**/*.md`);
for (const file of files) {
const relativePath = path.relative(`content/${SOURCE_LANG}`, file);
for (const lang of TARGET_LANGS) {
const targetFile = path.join(`content/${lang}`, relativePath);
// すでに翻訳されている場合はスキップする
if (await fs.pathExists(targetFile)) {
console.log(`Skipping ${targetFile} (exists)`);
continue;
}
await translateFile(file, targetFile, lang);
}
}
}
async function translateFile(sourceFile, targetFile, targetLang) {
console.log(`Translating ${sourceFile} to ${targetLang}...`);
const content = await fs.readFile(sourceFile, 'utf8');
const frontMatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
if (!frontMatterMatch) {
// フロントマターがない場合は、単にコピーする
await fs.ensureDir(path.dirname(targetFile));
await fs.copyFile(sourceFile, targetFile);
return;
}
// フロントマターを解析する
const frontMatter = yaml.load(frontMatterMatch[1]);
const body = content.substring(frontMatterMatch[0].length);
// 翻訳可能なフィールドを抽出する
const translatable = {
title: frontMatter.title || '',
description: frontMatter.description || '',
summary: frontMatter.summary || '',
keywords: frontMatter.keywords || []
};
// 翻訳用に保存する
await fs.writeJson('temp_meta.json', translatable);
// 翻訳する
execSync(`translator-ai temp_meta.json -l ${targetLang} -o temp_translated.json`);
// 翻訳を読み取る
const translated = await fs.readJson('temp_translated.json');
// フロントマターを更新する
Object.assign(frontMatter, translated);
// 翻訳されたファイルを書き込む
await fs.ensureDir(path.dirname(targetFile));
const newContent = `---\n${yaml.dump(frontMatter)}---${body}`;
await fs.writeFile(targetFile, newContent);
// 一時ファイルを削除する
await fs.remove('temp_meta.json');
await fs.remove('temp_translated.json');
console.log(`✓ Created ${targetFile}`);
}
// 翻訳を実行する
if (require.main === module) {
const translator = new HugoTranslator(['es', 'fr', 'de']);
translator.translateSite().catch(console.error);
}
module.exports = HugoTranslator;
Hugoのi18nファイルの翻訳
- 依存関係をインストールする:
npm install -g translator-ai js-yaml
- 簡単な翻訳用のMakefileを作成する:
# Makefile for Hugo translations
LANGUAGES := es fr de ja zh
SOURCE_YAML := i18n/en.yaml
THEME_DIR := themes/your-theme
.PHONY: translate
translate: $(foreach lang,$(LANGUAGES),translate-$(lang))
translate-%:
@echo "Translating to $*..."
@npx js-yaml $(SOURCE_YAML) > temp.json
@translator-ai temp.json -l $* -o temp_$*.json
@npx js-yaml temp_$*.json > i18n/$*.yaml
@rm temp.json temp_$*.json
@echo "✓ Created i18n/$*.yaml"
.PHONY: translate-theme
translate-theme:
@for lang in $(LANGUAGES); do \
make translate-theme-$$lang; \
done
translate-theme-%:
@echo "Translating theme to $*..."
@npx js-yaml $(THEME_DIR)/i18n/en.yaml > temp_theme.json
@translator-ai temp_theme.json -l $* -o temp_theme_$*.json
@npx js-yaml temp_theme_$*.json > $(THEME_DIR)/i18n/$*.yaml
@rm temp_theme.json temp_theme_$*.json
.PHONY: clean
clean:
@rm -f temp*.json
# すべてを翻訳する
.PHONY: all
all: translate translate-theme
使用例:
# すべての言語に翻訳する
make all
# 特定の言語に翻訳する
make translate-es
# テーマファイルを翻訳する
make translate-theme
完全なHugoの翻訳ワークフロー
以下は、コンテンツとi18nの翻訳の両方を処理する包括的なスクリプトです。
#!/usr/bin/env node
// hugo-complete-translator.js
const fs = require('fs-extra');
const path = require('path');
const yaml = require('js-yaml');
const { execSync } = require('child_process');
const glob = require('glob');
class HugoTranslator {
constructor(targetLanguages = ['es', 'fr', 'de']) {
this.targetLanguages = targetLanguages;
this.tempFiles = [];
}
async translateSite() {
console.log('Starting Hugo site translation...\n');
// 1. i18nファイルを翻訳する
await this.translateI18nFiles();
// 2. コンテンツを翻訳する
await this.translateContent();
// 3. 設定を更新する
await this.updateConfig();
console.log('\nTranslation complete!');
}
async translateI18nFiles() {
console.log('Translating i18n files...');
const i18nFiles = glob.sync('i18n/en.{yaml,yml,toml}');
for (const file of i18nFiles) {
const ext = path.extname(file);
for (const lang of this.targetLanguages) {
const outputFile = `i18n/${lang}${ext}`;
if (await fs.pathExists(outputFile)) {
console.log(` Skipping ${outputFile} (exists)`);
continue;
}
// JSONに変換する
const tempJson = `temp_i18n_${lang}.json`;
await this.convertToJson(file, tempJson);
// 翻訳する
const translatedJson = `temp_i18n_${lang}_translated.json`;
execSync(`translator-ai ${tempJson} -l ${lang} -o ${translatedJson}`);
// 戻す
await this.convertFromJson(translatedJson, outputFile, ext);
// 一時ファイルを削除する
await fs.remove(tempJson);
await fs.remove(translatedJson);
console.log(` ✓ Created ${outputFile}`);
}
}
}
async translateContent() {
console.log('\nTranslating content...');
// 翻訳方法を検出する
const useContentDirs = await fs.pathExists('content/en');
if (useContentDirs) {
await this.translateContentByDirectory();
} else {
await this.translateContentByFilename();
}
}
async translateContentByDirectory() {
const files = glob.sync('content/en/**/*.md');
for (const file of files) {
const relativePath = path.relative('content/en', file);
for (const lang of this.targetLanguages) {
const targetFile = path.join('content', lang, relativePath);
if (await fs.pathExists(targetFile)) continue;
await this.translateMarkdownFile(file, targetFile, lang);
}
}
}
async translateContentByFilename() {
const files = glob.sync('content/**/*.en.md');
for (const file of files) {
const baseName = file.replace('.en.md', '');
for (const lang of this.targetLanguages) {
const targetFile = `${baseName}.${lang}.md`;
if (await fs.pathExists(targetFile)) continue;
await this.translateMarkdownFile(file, targetFile, lang);
}
}
}
async translateMarkdownFile(sourceFile, targetFile, targetLang) {
const content = await fs.readFile(sourceFile, 'utf8');
const frontMatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
if (!frontMatterMatch) {
await fs.copy(sourceFile, targetFile);
return;
}
const frontMatter = yaml.load(frontMatterMatch[1]);
const body = content.substring(frontMatterMatch[0].length);
// フロントマターを翻訳する
const translatable = this.extractTranslatableFields(frontMatter);
const tempJson = `temp_content_${path.basename(sourceFile)}.json`;
const translatedJson = `${tempJson}.translated`;
await fs.writeJson(tempJson, translatable);
execSync(`translator-ai ${tempJson} -l ${targetLang} -o ${translatedJson}`);
const translated = await fs.readJson(translatedJson);
Object.assign(frontMatter, translated);
// 翻訳されたファイルを書き込む
await fs.ensureDir(path.dirname(targetFile));
const newContent = `---\n${yaml.dump(frontMatter)}---${body}`;
await fs.writeFile(targetFile, newContent);
// 一時ファイルを削除する
await fs.remove(tempJson);
await fs.remove(translatedJson);
console.log(` ✓ ${targetFile}`);
}
extractTranslatableFields(frontMatter) {
const fields = ['title', 'description', 'summary', 'keywords', 'tags'];
const translatable = {};
fields.forEach(field => {
if (frontMatter[field]) {
translatable[field] = frontMatter[field];
}
});
return translatable;
}
async convertToJson(inputFile, outputFile) {
const ext = path.extname(inputFile);
const content = await fs.readFile(inputFile, 'utf8');
let data;
if (ext === '.yaml' || ext === '.yml') {
data = yaml.load(content);
} else if (ext === '.toml') {
// この例ではTOMLサポートは実装されていません
throw new Error('TOML support not implemented in this example');
}
await fs.writeJson(outputFile, data, { spaces: 2 });
}
async convertFromJson(inputFile, outputFile, format) {
const data = await fs.readJson(inputFile);
let content;
if (format === '.yaml' || format === '.yml') {
content = yaml.dump(data, {
indent: 2,
lineWidth: -1,
noRefs: true
});
} else if (format === '.toml') {
throw new Error('TOML support not implemented in this example');
}
await fs.writeFile(outputFile, content);
}
async updateConfig() {
console.log('\nUpdating Hugo config...');
const configFile = glob.sync('config.{yaml,yml,toml,json}')[0];
if (!configFile) return;
// これは簡略化された例です - 適切に解析して更新する必要があります
console.log(' ! Remember to update your config.yaml with language settings');
}
}
// 翻訳を実行する
if (require.main === module) {
const translator = new HugoTranslator(['es', 'fr', 'de']);
translator.translateSite().catch(console.error);
}
module.exports = HugoTranslator;
Hugoモジュールとの連携
Hugoモジュールを使用している場合は、翻訳モジュールを作成できます。
// go.mod
module github.com/yourusername/hugo-translator
go 1.19
require (
github.com/yourusername/your-theme v1.0.0
)
次に、package.json
に以下を追加します。
{
"scripts": {
"translate": "node hugo-complete-translator.js",
"translate:content": "node hugo-complete-translator.js --content-only",
"translate:i18n": "node hugo-complete-translator.js --i18n-only",
"build": "npm run translate && hugo"
}
}
YAMLフロントマターを持つJekyllの投稿
YAMLフロントマターを持つJekyllの投稿については、以下のPythonスクリプトを使用できます。
#!/usr/bin/env python3
# translate-jekyll-posts.py
import os
import yaml
import json
import subprocess
import frontmatter
def translate_jekyll_post(post_path, target_lang, output_dir):
"""Jekyllの投稿を翻訳する(フロントマターも含む)"""
# フロントマター付きで投稿を読み込む
post = frontmatter.load(post_path)
# 翻訳可能なフロントマターフィールドを抽出する
translatable = {
'title': post.metadata.get('title', ''),
'description': post.metadata.get('description', ''),
'excerpt': post.metadata.get('excerpt', '')
}
# 翻訳用にJSONとして保存する
with open('temp_meta.json', 'w', encoding='utf-8') as f:
json.dump(translatable, f, ensure_ascii=False, indent=2)
# 翻訳する
subprocess.run([
'translator-ai',
'temp_meta.json',
'-l', target_lang,
'-o', f'temp_meta_{target_lang}.json'
])
# 翻訳を読み取る
with open(f'temp_meta_{target_lang}.json', 'r', encoding='utf-8') as f:
translations = json.load(f)
# 投稿のメタデータを更新する
for key, value in translations.items():
if value: # 翻訳が存在する場合のみ更新する
post.metadata[key] = value
# メタデータに言語を追加する
post.metadata['lang'] = target_lang
# 翻訳された投稿を保存する
output_path = os.path.join(output_dir, os.path.basename(post_path))
with open(output_path, 'w', encoding='utf-8') as f:
f.write(frontmatter.dumps(post))
# 一時ファイルを削除する
os.remove('temp_meta.json')
os.remove(f'temp_meta_{target_lang}.json')
# すべての投稿を翻訳する
for lang in ['es', 'fr', 'de']:
os.makedirs(f'_posts/{lang}', exist_ok=True)
for post in os.listdir('_posts/en'):
if post.endswith('.md'):
translate_jekyll_post(
f'_posts/en/{post}',
lang,
f'_posts/{lang}'
)
YAML/JSON変換のヒント
- フォーマットを保持する:
js-yaml
を適切なオプションで使用して、YAML構造を維持します。 - 特殊文字を処理する: 全体を通して適切なエンコーディング(UTF-8)を確保します。
- 出力を検証する: 一部のYAML機能(アンカー、エイリアス)は特別な処理が必要な場合があります。
- TOMLを考慮する: Hugoの場合、TOML設定ファイルも処理する必要があるかもしれません。
代替案: 直接のYAMLサポート(機能リクエスト)
YAMLファイルを頻繁に扱う場合は、自動的に変換を処理するラッパースクリプトを作成するか、translator-aiにYAMLサポートを機能としてリクエストしてください。
🔧 開発
ソースからビルドする
git clone https://github.com/DatanoiseTV/translator-ai.git
cd translator-ai
npm install
npm run build
ローカルでテストする
npm start -- test.json -l es -o output.json
📄 ライセンス
このプロジェクトは、商用および非商用の両方の使用において、帰属表示が必要です。詳細については、LICENSEファイルを参照してください。
貢献
貢献は大歓迎です!プルリクエストを送信してください。
サポート
問題、質問、または提案がある場合は、GitHubでイシューを開いてください。
このツールが役に立った場合は、開発をサポートすることを検討してください。













