📝
Unity LocalizationのCSVデータをopenaiとgithub actionsを使って自動翻訳を行う
はじめに
OSSのデスクトップマスコット「uDesktopMascot」を開発しています。こちらは多言語に対応したリポジトリおよびプロジェクトにしていきたいという思いで開発を行っています。
以下の記事のようにReadMeはOpenAIのgpt-4o-miを使って日本語から多言語に自動翻訳対応を行っています。
今回は、アプリ内で使用するテキストをOpenAIを使ってGitHub Actionsで自動翻訳対応を行っていきます。
デモ
実際の翻訳処理の差分は以下のようになります。
以下が実際の処理されたコミット差分です
上記のデータをLocalization Tablesにcsvからimportした結果です。すべての言語の翻訳結果が埋まっています。
開発環境
unity
- Unity 6000.0.31f1(IL2CPP)
- Localization 1.5.4
python
- openai 1.60.1
- python 3.11
大まかな処理の流れ
- ローカル環境でLocalization TablesでNew Entryで新規登録を行う(keyと日本語のvalue)
- CSVにexportする
- コミットする(ただし1,2はCSVで作業をすることで省略することはできます.Key Idはルールがあるので、こちらはunity側に従ってくださいテーブルキーの生成)
- github actionsでcsvに差分があるときだけjobを実行
- job内から翻訳処理のpythonスクリプトの実行
- コミット(PRにもコメント返信)
unity側の対応
パッケージを入れる以外には対応は必要ないです.
github actionsの対応
PRに対してcsvがコミットされたときに処理を行いたいため、以下のようなjobを定義します
name: Auto Localization
on:
pull_request:
branches:
- develop
paths:
- 'Assets/uDesktopMascot/LocalizationTable/LocalizationTable.csv'
jobs:
translate:
runs-on: ubuntu-latest
steps:
- name: リポジトリをチェックアウト
uses: actions/checkout@v4.2.2
with:
fetch-depth: 0
- name: Pythonをセットアップ
uses: actions/setup-python@v5.3.0
with:
python-version: '3.11'
- name: 依存関係のインストール
run: |
pip install openai pandas
- name: 翻訳スクリプトを実行
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: python .github/scripts/translate_localization.py
- name: 変更をコミット
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
# 変更をステージしてコミット
git add Assets/uDesktopMascot/LocalizationTable/LocalizationTable.csv
if git commit -m "自動翻訳を追加(GitHub Actionsによる)"; then
echo "Changes committed successfully."
# リモートの最新の変更を取り込み、リベース
if git pull --rebase origin ${{ github.head_ref }}; then
echo "Successfully rebased."
else
echo "Rebase failed. Please review the errors."
exit 1 # エラーが発生した場合、処理を中止
fi
# 最後にプッシュ
git push origin HEAD:${{ github.head_ref }}
else
echo "No changes to commit"
fi
- name: PRにコメントを追加
if: success() || failure()
uses: actions/github-script@v6
with:
script: |
github.rest.issues.createComment({
issue_number: ${{ github.event.pull_request.number }},
owner: context.repo.owner,
repo: context.repo.repo,
body: "自動翻訳が完了し、ローカライズCSVに反映されました。"
})
また翻訳処理は、pythondで以下のように行いました
import os
import pandas as pd
from openai import OpenAI
import csv
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
# CSVファイルのパス
csv_path = 'Assets/uDesktopMascot/LocalizationTable/LocalizationTable.csv'
# 現在のCSVを読み込み
df = pd.read_csv(csv_path)
# 対象の言語とその表示名を設定(言語名は英語に変更)
target_languages = {
'English(en)': 'English',
'French(fr)': 'French',
'Italian(it)': 'Italian',
'Korean(ko)': 'Korean'
}
# 翻訳用の関数を定義
def translate_text(text, target_language):
try:
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": f"Translate the following Japanese text to {target_language}. Return only the translated text without any additional explanations or notes.\n\n{text}"}
]
)
# 翻訳結果を取得
translation = response.choices[0].message.content.strip()
return translation
except Exception as e:
print(f"翻訳中にエラーが発生しました:{e}")
return None
# 各行に対して翻訳を実行
for idx, row in df.iterrows():
japanese_text = row.get('Japanese(ja)', '')
key = row.get('Key', '')
# 日本語テキストが存在する場合のみ翻訳を実行
if pd.notnull(japanese_text) and japanese_text.strip() != '':
print(f"キー '{key}' の翻訳を実行します。")
for column, target_language in target_languages.items():
current_translation = row.get(column, '')
# 翻訳列が存在しない場合、列を追加
if column not in df.columns:
df[column] = ''
# 翻訳が未実行または空欄の場合のみ翻訳を実行
if pd.isnull(current_translation) or current_translation.strip() == '':
translation = translate_text(japanese_text, target_language)
if translation:
df.at[idx, column] = translation
print(f"{target_language}への翻訳結果:{translation}")
else:
print(f"キー '{key}' の {target_language} への翻訳に失敗しました。")
else:
print(f"'{column}' 列には既に翻訳が存在します。翻訳をスキップします。")
else:
print(f"キー '{key}' に日本語テキストが存在しないため、翻訳をスキップします。")
# カスタムCSV書き込み関数を定義
# カスタムCSV書き込み関数を定義
def write_custom_csv(df, csv_path):
columns = df.columns.tolist()
with open(csv_path, 'w', newline='', encoding='utf-8') as csvfile:
# ヘッダーを書き込む(ダブルクォーテーションを付けない)
csvfile.write(','.join(columns) + '\n')
# データ行を書き込む(全てのフィールドをダブルクォーテーションで囲む。ただし 'Key' と 'Id' 列を除く)
for idx, row in df.iterrows():
data_row = []
for col in columns:
value = row[col]
if pd.isnull(value):
value = ''
else:
value = str(value).replace('\n', '\\n').replace('\r', '\\r')
if col in ['Id']:
# Id 列はそのまま
value = value.replace(',', '\\,')
data_row.append(value)
else:
# ダブルクォーテーションで囲み、内部のダブルクォーテーションをエスケープ
value = value.replace('"', '""')
data_row.append(f'"{value}"')
csvfile.write(','.join(data_row) + '\n')
# カスタム関数でCSVを保存
write_custom_csv(df, csv_path)
print("翻訳が完了し、CSVファイルが更新されました。")

midra-lab.notion.site/MidraLab-dd08b86fba4e4041a14e09a1d36f36ae 個人が興味を持ったこと × チームで面白いものや興味を持ったものを試していくコミュニティ
Discussion