26
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

Pleasanterは日本語のほか、「英語 / 中国語 / ドイツ語 / 韓国語 / スペイン語 / ベトナム語」に対応しており、アプリ上で表示しているテキストはDisplaysディレクトリ内のJSONファイルで管理しています。

新しい機能を開発したときなど、ラベルや説明文といったすべてのテキストを多言語化対応するのですが、10個・20個と多くのテキストを翻訳してJSONを作るのは、意外と作業ボリュームがあるのです。

そこで思いついたのが「DiscordのBotを使えば多言語化JSONを一瞬で作れるのでは?」という方法。
本記事では、「Discordの翻訳Bot」を作り、Pleasanterの多言語化用JSONを簡単に生成する方法をご紹介します。


構成・特徴

こちらの記事では以下の構成で作っていきます。

  • Python + discord.py
  • 翻訳にはGoogle Translate API利用
  • .envでBotトークン&APIキー管理
  • Pleasanterがサポートしている主要7ヶ国語対応(追加も自由)

動作イメージ

Discordにポストしたメッセージに絵文字でリアクションすればPleasanterの多言語化JSONをリプライ

こんにちは

これに何かしらの絵文字でリアクションすると以下のjsonがリプライされるようにしたい。

{
    "Id": "Hello",
    "Type": 110,
    "Languages": [
        {
            "Body": "Hello"
        },
        {
            "Language": "zh",
            "Body": "你好"
        },
        {
            "Language": "ja",
            "Body": "こんにちは"
        },
        {
            "Language": "de",
            "Body": "Hallo"
        },
        {
            "Language": "ko",
            "Body": "안녕하세요"
        },
        {
            "Language": "es",
            "Body": "Hola"
        },
        {
            "Language": "vn",
            "Body": "Xin chào"
        }
    ]
}

データの内容を見て問題なければコードブロックをコピーしてJSONファイルにし、Pleasanterに登録。


事前準備

コーディングの前にBot作成に必要な準備をします。

Discord Botの作成・トークン取得

  1. Discord開発者ポータルでアプリを新規作成
  2. 「Bot」タブから「Add Bot」でBotを作成
  3. 「Token」→「Copy」でBotトークンを控える
  4. Authorization Flowから「Message Content Intent」を有効化

BotをDiscordサーバーへ招待

  1. Discord開発者ポータルから先ほど作ったアプリを選択
  2. 「OAuth2」-「URL Generator」の「bot」にチェック
  3. 「Bot Permissions」から以下の権限をチェック
    • Read Messages / View Channels
    • Read Message History
    • Send Messages
    • Add Reactions
  4. 「Generated URL」をコピーし、ブラウザで開く
  5. Botを招待サーバーを選択。
  6. サーバーのメンバーリストに表示があれば成功。

GOOGLE_TRANSLATE_API_KEY取得

  1. Google Cloud Platformでプロジェクトを作成
  2. 「APIとサービス」-「ライブラリ」から「Cloud Translation API」を有効化
  3. 「APIとサービス」-「認証情報」から「APIキー」を作成
  4. 「APIキー」表示→控える

毎月、処理のためにAPIに送信された 500,000 文字までは無料です(LLM には適用されません)。
https://cloud.google.com/translate?hl=ja

※ 登録は自己責任でお願いします。

ディレクトリ・ファイル構成

以下のように適当なプロジェクトフォルダを用意して2つのファイルを用意します。

[project]
├ bot.py // bot用のpythonファイル
└ .env  // 環境変数ファイル

先ほど取得したトークンやAPIキーは .env に環境変数として定義します。
他人が知ると外部APIやBotアカウントを勝手に使われるリスクがありますので中身は公開しないでください。

# Discord Botの認証トークン
DISCORD_BOT_TOKEN=XXXXXXXXXX

# Google Cloud Translation APIキー
GOOGLE_TRANSLATE_API_KEY=XXXXXXXXXX

.envの内容が正しくない場合、Botや翻訳が動作しません。必ず発行した値を正確にコピペしてください。


必要なパッケージ

Discord翻訳Botを動かすために、以下のPythonパッケージが必要です。

パッケージ名 用途
discord.py Discord Bot API用ライブラリ
python-dotenv .envファイルから環境変数(トークン等)を読み込む
requests Google翻訳APIへのHTTPリクエスト送信

インストールコマンド

コマンドプロンプトやターミナルで下記を実行します:

pip install discord.py python-dotenv requests

補足

  • すでにインストール済みの場合はスキップしてOKです
  • パッケージが見つからない等のエラーが出る場合は「Python の PATH 設定」「pip バージョン」の確認も

インストール後の確認

pip show discord.py
pip show python-dotenv
pip show requests

これでパッケージ情報が表示されれば導入成功です。


まずはリプライまで

まずは特定のコマンドを含むメッセージに対してリプライするところまで実装します。

コマンドは「translate」の頭文字のtにし、以下のメッセージで翻訳できるように。

!t {target_lang} {text}

コード:bot.py

import discord
import os
import requests
from dotenv import load_dotenv

# .envファイルから環境変数を読み込み
load_dotenv()

# Discord Botのトークン・Google翻訳APIキーを取得
TOKEN = os.getenv("DISCORD_BOT_TOKEN")
GOOGLE_TRANSLATE_API_KEY = os.getenv("GOOGLE_TRANSLATE_API_KEY")

# メッセージ内容取得のためインテントを有効化
intents = discord.Intents.default()
intents.message_content = True

# クライアント作成
client = discord.Client(intents=intents)

# Google翻訳APIラッパー関数
def translate_text(text, target_lang='en'):
    """
    Google Cloud Translation API を利用してテキストを指定言語に翻訳する関数
    :param text: 翻訳したい元のテキスト
    :param target_lang: 翻訳先の言語(例: 'en', 'ja', 'ko')
    :return: 翻訳されたテキスト(失敗時は「翻訳エラー」)
    """
    url = "https://translation.googleapis.com/language/translate/v2"
    params = {
        "q": text,
        "target": target_lang,
        "key": GOOGLE_TRANSLATE_API_KEY
    }
    try:
        # POSTリクエストでAPIへ翻訳依頼
        response = requests.post(url, data=params)
        result = response.json()
        return result['data']['translations'][0]['translatedText']
    except Exception:
        return "翻訳エラー"

@client.event
async def on_message(message):
    # Bot自身の発言には反応しない
    if message.author == client.user:
        return

    # "!t <言語コード> <テキスト>" で翻訳開始
    if message.content.startswith('!t '):
        try:
            # 空白で分割 → !t, lang, text
            _, lang, text = message.content.split(' ', 2)
        except ValueError:
            # 書式ミス時は説明を返す
            await message.channel.send("書式が間違っています。例: !t ja Hello")
            return
        # 翻訳APIを呼び出し
        translated = translate_text(text, lang)
        # 元メッセージに対してコードブロック形式でリプライ
        await message.reply(f"```{translated}```")

# Bot起動
client.run(TOKEN)

さっそく起動してみましょう。


Botの起動・動作確認

1. 起動コマンド

ターミナル/コマンドプロンプトで下記を実行します:

python bot.py

2. BotがDiscordにオンライン表示されることを確認

  • 起動に成功すると、Botが参加しているDiscordサーバーでBotアカウントが「オンライン」になります。
    image.png

3. 動作テスト

  • Botが参加中のチャンネルで !t en こんにちは とポスト
  • Botから返信(Helloなど)がリプライとして返されれば成功です
    image.png

絵文字リアクションでJSONをリプライ

次に本記事のメインである絵文字リアクションでJSONをリプライする機能を実装します。
ここでも絵文字は translate に。

コード:bot.py

import discord
import os
import requests
import json
from dotenv import load_dotenv
import re

load_dotenv()
TOKEN = os.getenv("DISCORD_BOT_TOKEN")
GOOGLE_TRANSLATE_API_KEY = os.getenv("GOOGLE_TRANSLATE_API_KEY")

intents = discord.Intents.default()
intents.message_content = True
intents.reactions = True
client = discord.Client(intents=intents)

def translate_text(text, target_lang='en'):
    url = "https://translation.googleapis.com/language/translate/v2"
    params = {
        "q": text,
        "target": target_lang,
        "key": GOOGLE_TRANSLATE_API_KEY
    }
    try:
        response = requests.post(url, data=params)
        result = response.json()
        return result['data']['translations'][0]['translatedText']
    except Exception:
        return "翻訳エラー"

@client.event
async def on_message(message):
    if message.author == client.user:
        return

    if message.content.startswith('!t '):
        try:
            _, lang, text = message.content.split(' ', 2)
        except ValueError:
            await message.channel.send("書式が間違っています。例: !t ja Hello")
            return
        translated = translate_text(text, lang)
        await message.reply(f"```{translated}```")

### [追加] ---------------------------------------------------
@client.event
async def on_raw_reaction_add(payload):
    # リアクションが付いたチャンネルの取得
    channel = client.get_channel(payload.channel_id)
    if channel is None:
        return
    try:
        # 該当メッセージの取得
        message = await channel.fetch_message(payload.message_id)
    except Exception:
        return

    emoji = payload.emoji.name

    # 🇹リアクションの場合はJSON形式で多言語翻訳を返す
    if emoji == "🇹":
        original = message.content

        # jsonのIDをアッパーキャメルケースで定義
        json_id = translate_text(original, "en")
        id_value = ''.join(word.capitalize() for word in re.split(r'\W+', json_id) if word)

        # 多言語翻訳リスト(JSON出力用)
        target_languages = [
            ("", "en"),      # 英語。Languageキーなし
            ("zh", "zh-CN"), # 中国語(簡体)
            ("ja", "ja"),    # 日本語
            ("de", "de"),    # ドイツ語
            ("ko", "ko"),    # 韓国語
            ("es", "es"),    # スペイン語
            ("vn", "vi"),    # ベトナム語(JSONは"vn"/APIは"vi")
        ]

        body_results = []
        for lang_key, lang_code in target_languages:
            # 日本語は原文そのまま。それ以外は翻訳APIで取得
            if lang_code == "ja":
                body = original
            else:
                body = translate_text(original, lang_code)
            # JSONの構成:Languageがある場合は先頭に、それ以外はBodyのみ
            if lang_key != "":
                entry = {"Language": lang_key, "Body": body}
            else:
                entry = {"Body": body}
            body_results.append(entry)

        # 最終JSON構造を構築
        json_data = {
            "Id": id_value,
            "Type": 110,
            "Languages": body_results
        }

        # 整形済みJSONをコードブロックでreply
        json_str = json.dumps(json_data, ensure_ascii=False, indent=4)
        await message.reply(f"```json\n{json_str}\n```")
### [/追加] ---------------------------------------------------

# Botを起動
client.run(TOKEN)

動作確認

先ほどと同じように起動コマンド実行します。

python bot.py

まずは翻訳したい日本語テキストをポスト

image.png

絵文字の でリアクション する

image.png

コードブロックでJSONがリプライ

image.png

無事に多言語用JSONを出力できました。
コードブロックなので、そのままコピーしてあとは登録するだけです。

おまけ:国旗リアクションで翻訳

目的は達成できたのですが、もう一つ機能を追加したいと思います。
ポストされたメッセージに対して、国旗でリアクションすることで、対応する言語に翻訳する機能です。
こちら、あの有名翻訳Botの機能を再現してみようという試みになります。

仕様

メッセージに国旗リアクションが付いたら、その国の言語で翻訳して返信。

例)
- スペイン語で翻訳
- 日本語で翻訳

コード:bot.py

import discord
import os
import requests
import json
from dotenv import load_dotenv
import re

load_dotenv()

TOKEN = os.getenv("DISCORD_BOT_TOKEN")
GOOGLE_TRANSLATE_API_KEY = os.getenv("GOOGLE_TRANSLATE_API_KEY")

intents = discord.Intents.default()
intents.message_content = True
intents.reactions = True

client = discord.Client(intents=intents)

def translate_text(text, target_lang='en'):
    url = "https://translation.googleapis.com/language/translate/v2"
    params = {
        "q": text,
        "target": target_lang,
        "key": GOOGLE_TRANSLATE_API_KEY
    }
    try:
        response = requests.post(url, data=params)
        result = response.json()
        return result['data']['translations'][0]['translatedText']
    except Exception:
        return "翻訳エラー"

### [追加] ---------------------------------------------------
# 国旗と言語の値が違うのでマッピング
FLAG_LANG_MAP = {
    "🇺🇸": "en",     # アメリカ国旗→英語
    "🇨🇳": "zh-CN",  # 中国国旗→中国語(簡体)
    "🇯🇵": "ja",     # 日本国旗→日本語
    "🇩🇪": "de",     # ドイツ国旗→ドイツ語
    "🇰🇷": "ko",     # 韓国国旗→韓国語
    "🇪🇸": "es",     # スペイン国旗→スペイン語
    "🇻🇳": "vi",     # ベトナム国旗→ベトナム語
}
### [/追加] ---------------------------------------------------

@client.event
async def on_message(message):
    if message.author == client.user:
        return

    if message.content.startswith('!t '):
        try:
            _, lang, text = message.content.split(' ', 2)
        except ValueError:
            await message.channel.send("書式が間違っています。例: !t ja Hello")
            return
        translated = translate_text(text, lang)
        await message.reply(f"```{translated}```")

@client.event
async def on_raw_reaction_add(payload):
    channel = client.get_channel(payload.channel_id)
    if channel is None:
        return
    try:
        message = await channel.fetch_message(payload.message_id)
    except Exception:
        return

    emoji = payload.emoji.name

### [追加] ---------------------------------------------------
    # --- 国旗の場合は1言語で返信 ---
    if emoji in FLAG_LANG_MAP:
        lang = FLAG_LANG_MAP[emoji]
        translated = translate_text(message.content, lang)
        await message.reply(f"```{translated}```")
        return
### [/追加] ---------------------------------------------------

    if emoji == "🇹":
        original = message.content
        json_id = translate_text(original, "en")
        id_value = ''.join(word.capitalize() for word in re.split(r'\W+', json_id) if word)

        target_languages = [
            ("", "en"),
            ("zh", "zh-CN"),
            ("ja", "ja"),
            ("de", "de"),
            ("ko", "ko"),
            ("es", "es"),
            ("vn", "vi"),
        ]

        body_results = []
        for lang_key, lang_code in target_languages:
            if lang_code == "ja":
                body = original
            else:
                body = translate_text(original, lang_code)
            if lang_key != "":
                entry = {"Language": lang_key, "Body": body}
            else:
                entry = {"Body": body}
            body_results.append(entry)

        json_data = {
            "Id": id_value,
            "Type": 110,
            "Languages": body_results
        }

        json_str = json.dumps(json_data, ensure_ascii=False, indent=4)
        await message.reply(f"```json\n{json_str}\n```")

client.run(TOKEN)

では起動コマンドを実行して確認してみましょう。

「ありがとうございます」にスペイン国旗 でリアクション

image.png

スペイン語でリプライ

image.png

「gracias」とポストし、日本国旗 でリアクション

image.png

日本語でリプライ

image.png

期待通りスペイン語・日本語でリプライされました。
これでいろんな国の人とお話することが出来ますね!


翻訳Bot機能まとめ

最後にこのBotの翻訳機能をおさらい。

1. コマンド翻訳

  • !t <言語コード> <テキスト> で即時翻訳

2. リアクション

  • 主要言語まとめてJSON生成
  • キャメルケースID自動生成
  • メッセージへのリプライ形式で返信

3. 国旗リアクション

  • 国旗アイコンでその国の言語に自動翻訳
  • マッピング辞書で柔軟に拡張可能
  • 国と言語は FLAG_LANG_MAP に追加可能

拡張アイディア

こちらのBotですが、ターミナルで起動し続けないとBotがオフラインになってしまいます。
今回は必要な時だけ起こすのでいつもは熟睡していても問題なのですが、もしこのBotを常駐させたい場合はVMなどを使い、常に起動し続けなければいけません。

翻訳に使ったGoogleCloudには仮想マシンもあるのでそちらを使えば常時起動も可能になります。
https://cloud.google.com/products/compute?hl=ja

設定次第で無料枠でも使えるので興味のある方はお試しください。
https://docs.cloud.google.com/free/docs/free-cloud-features?hl=ja#compute

ただし、こちらも自己責任で。


さいごに

DiscordBot × Google翻訳で、多言語化JSONが簡単に作れるようになり、 更に汎用的な翻訳Botとして使えるようにしてみました。
たまにDiscordで外国の方と話すことがあるのですが毎回翻訳するのも手間なので、これがあればお互いに翻訳しながら会話できるので便利かなと思います。

DiscordのBotは他にも様々な用途に使えるので、いろいろ考えてみるのも面白いですね。

それでは良いゲームライフを!


参考リンク


26
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
26
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?