カテゴリー: Discordボット開発

Discordボット開発#10多言語(i18n)に対応する! (Pythonサンプル付き)

この記事は韓国語の原文から翻訳したものです。

目次10項目

こんにちは!Mojangcamです 😎

前回の#9では、ボットにAI(Claude・ChatGPT API)を連携して会話できるボットを作りましたね。今回はボットをグローバルボットにアップグレードします。そう、多言語(i18n)対応です!

海外のユーザーが混ざっているゲームサーバーを運営していると、韓国語でしか答えないボットが物足りなく感じることがあります。Discordはユーザーごとにクライアントの言語設定を持っているので、これを読み取れば、韓国語ユーザーには韓国語で、海外ユーザーには英語で自動的に応答するボットが作れます。さらにスラッシュコマンドの名前や説明まで言語ごとに切り替えて表示できるんです!

🧠 i18nって何?なぜ必要なの?

i18nはinternationalization(国際化)の略です。iとnの間に18文字あるので、i18nと呼ばれています 💡

Discordボットでi18nが必要になる場面は、思っているより多いです。

  • 海外ユーザーが混ざったゲームサーバー(Minecraft・ARKなど)の案内ボット
  • いろいろな国のサーバーに配布する公開ボット
  • コマンドの説明をユーザーの言語で表示して、使い始めのハードルを下げたいとき

discord.pyには、そのための機能がすでに組み込まれています。interaction.locale(ユーザーの言語)とTranslator(コマンドの翻訳機)の2つさえ知っていればOKです!

📌 準備するもの

  • Python 3.8以上
  • discord.py 2.x (この記事は最新バージョンの2.7.1を基準にしています)
  • ボットトークン + ボットを招待したテスト用サーバー (#1を参照)
ターミナル
pip install -U discord.py

📌 ステップ1 - ユーザーの言語を調べる (interaction.locale)

スラッシュコマンドが実行されると、Discordはそのユーザーの言語情報も一緒に送ってくれます。まずはどんな値が届くのか確認してみましょう。

Python
@bot.tree.command(name="lang", description="自分の言語設定を確認")
async def lang(interaction: discord.Interaction):
    await interaction.response.send_message(
        f"クライアントの言語: {interaction.locale}"
    )

ひとつずつ見ていきましょう 💡

  • interaction.locale : コマンドを実行したユーザーのDiscordアプリの言語設定です。韓国語クライアントならko、英語(アメリカ)ならen-US、日本語ならjaが届きます。
  • interaction.guild_locale : こちらはユーザーではなく、サーバー(ギルド)のデフォルト言語です。お知らせのようなサーバー単位のメッセージに使うと便利です。
  • 戻り値の型はdiscord.Localeという列挙型(enum)ですが、str()で囲むと'ko'のような文字列になります。

📌 ステップ2 - 翻訳辞書で応答メッセージを切り替える

次は、言語ごとの応答をまとめた翻訳辞書(dict)を作り、interaction.localeに合ったメッセージを選んで送ってみましょう。

Python
RESPONSES = {
    "ko": "안녕하세요, {user}님! 만나서 반가워요 🙌",
    "ja": "こんにちは、{user}さん!",
    "en-US": "Hello, {user}! Nice to meet you 🙌",
}
DEFAULT_LOCALE = "en-US"

@bot.tree.command(name="hello", description="Send a friendly greeting")
async def hello(interaction: discord.Interaction):
    lang = str(interaction.locale)
    template = RESPONSES.get(lang, RESPONSES[DEFAULT_LOCALE])
    await interaction.response.send_message(
        template.format(user=interaction.user.display_name)
    )

ひとつずつ見ていきましょう 💡

  • RESPONSES : 言語コードをキーにした翻訳辞書です。言語を追加したいときは、ここに1行足すだけです。
  • RESPONSES.get(lang, デフォルト値) : 辞書にない言語(例: フランス語のfr)で来た場合は、デフォルト言語の英語で代わりに応答します。多言語処理のいちばん大事な安全装置です!
  • {user} + format() : メッセージの中にニックネームなどの変数を差し込むためのプレースホルダーです。

📌 ステップ3 - コマンド名・説明まで翻訳する (Translator)

応答だけ切り替えても、まだ半分です。韓国のユーザーの画面では、コマンド自体が/인사(「あいさつ」の意味)と表示されたほうがうれしいですよね?そんなときに使うのがapp_commands.Translatorです。

Python
from discord import app_commands
from discord.app_commands import locale_str as _T

COMMAND_I18N = {
    "ko": {
        "hello": "인사",
        "Send a friendly greeting": "봇이 반갑게 인사를 건네요",
    },
    "ja": {
        "hello": "あいさつ",
        "Send a friendly greeting": "ボットがあいさつします",
    },
}

class MyTranslator(app_commands.Translator):
    async def translate(self, string, locale, context):
        # 翻訳があれば翻訳文を、なければNone(原文のまま)を返します
        return COMMAND_I18N.get(str(locale), {}).get(string.message)

ひとつずつ見ていきましょう 💡

  • locale_str (_T) : 「この文字列は翻訳対象です」と示すマーカーです。コマンドを作るときにname=_T("hello")のように囲みます。
  • Translator : コマンドをサーバーに登録(sync)するとき、Discordがサポートしているすべての言語に対してtranslate()を呼び出します。翻訳がなければNoneを返すと、原文がそのまま使われます。
  • string.message : _T()で囲んだ元の文字列です。これをキーにして翻訳辞書から探します。
  • ⚠️ コマンド名のルールは翻訳文にもそのまま適用されます。スペース禁止、英字は小文字のみ! (ハングル・日本語はそのままでOK)

最後に、ボットの起動時に翻訳機をsyncより先に登録します。

Python
@bot.event
async def on_ready():
    await bot.tree.set_translator(MyTranslator())  # 必ずsyncより先に!
    await bot.tree.sync()
    print(f"{bot.user} ログイン完了!")

📌 全体のコード (コピペOK)

Python
# discord.py 2.7.x / Python 3.8+
from typing import Optional

import discord
from discord import app_commands
from discord.app_commands import locale_str as _T
from discord.ext import commands

intents = discord.Intents.default()
bot = commands.Bot(command_prefix="!", intents=intents)

# コマンド名・説明の翻訳表 (Translatorが使用)
COMMAND_I18N = {
    "ko": {
        "hello": "인사",
        "Send a friendly greeting": "봇이 반갑게 인사를 건네요",
    },
    "ja": {
        "hello": "あいさつ",
        "Send a friendly greeting": "ボットがあいさつします",
    },
}

# 応答メッセージの翻訳表 (interaction.localeで選択)
RESPONSES = {
    "ko": "안녕하세요, {user}님! 만나서 반가워요 🙌",
    "ja": "こんにちは、{user}さん!",
    "en-US": "Hello, {user}! Nice to meet you 🙌",
}
DEFAULT_LOCALE = "en-US"

class MyTranslator(app_commands.Translator):
    async def translate(
        self,
        string: app_commands.locale_str,
        locale: discord.Locale,
        context: app_commands.TranslationContext,
    ) -> Optional[str]:
        return COMMAND_I18N.get(str(locale), {}).get(string.message)

@bot.tree.command(name=_T("hello"), description=_T("Send a friendly greeting"))
async def hello(interaction: discord.Interaction):
    lang = str(interaction.locale)  # 例: 'ko', 'ja', 'en-US'
    template = RESPONSES.get(lang, RESPONSES[DEFAULT_LOCALE])
    await interaction.response.send_message(
        template.format(user=interaction.user.display_name)
    )

@bot.event
async def on_ready():
    await bot.tree.set_translator(MyTranslator())  # 必ずsyncより先に!
    await bot.tree.sync()
    print(f"{bot.user} ログイン完了!")

bot.run("ここにボットトークンを入力")

📌 実行結果

ボットを起動してコマンドを入力してみると...

  • 韓国語クライアント: コマンド一覧に/인사 - 봇이 반갑게 인사를 건네요(「/あいさつ - ボットが元気にあいさつします」)と表示され、実行すると「안녕하세요, ○○님! 만나서 반가워요 🙌」(「こんにちは、○○さん!会えてうれしいです 🙌」)
  • 英語クライアント: /hello - Send a friendly greetingと表示され、「Hello, ○○! Nice to meet you 🙌」
  • 日本語クライアント: /あいさつと表示されます!同じコマンドなのに、言語ごとにまったく違って見えます。

翻訳辞書にない言語(フランス語など)で実行すると、デフォルトの英語で応答することもぜひ確認してみてください!

📌 よくあるエラー

  1. コマンド名が翻訳されず、英語のままになる 原因: set_translator()をsync()の後に呼び出したか、翻訳機を登録したあとに再同期していない場合です。翻訳はsyncのタイミングで反映されるからです。 解決: await bot.tree.set_translator(...) → await bot.tree.sync()の順番を守り、DiscordクライアントでCtrl+Rを押して再読み込みしてみてください。反映まで数分かかることもあります。
  2. sync中に400 Bad Request (CommandSyncFailure) エラーが出る 原因: 翻訳したコマンド名がDiscordのルールに違反している場合です。コマンド名にはスペースを入れられず、英字の大文字も使えません。 解決: 翻訳辞書のコマンド名の部分を確認してください。"あいさつ する"(スペース ❌) → "あいさつする"(⭕)、"Hello"(大文字 ❌) → "hello"(⭕)
  3. interaction.localeがいつもen-USになる 原因: localeはサーバーの言語ではなく、各ユーザーのDiscordアプリの言語設定が基準です。自分のアプリが英語に設定されているなら、en-USで正常です。 解決: Discordの設定 → 言語で日本語(または韓国語)に変えて、もう一度テストしてみてください。サーバー基準の言語が必要ならinteraction.guild_localeを使えばOKです。

📌 まとめ

今回はinteraction.localeでユーザーの言語を読み取り、翻訳辞書で応答を切り替え、Translatorでコマンド名・説明まで翻訳する3ステップの多言語ボットを作ってみました。翻訳辞書に言語を1行追加するだけでどんな言語にも対応できるので、みなさんのサーバーに合わせて広げてみてください!

次回は、コマンドの連打を防ぐクールダウン(Cooldown)と権限管理を扱う予定です。お楽しみに 🙌

📚 Discordボット開発シリーズ

  1. Discordボット開発 - スラッシュコマンド(Slash Command)完全攻略!
  2. Discordボット開発 #2 - スラッシュコマンドにオプション(Arguments)を追加しよう!
  3. Discordボット開発 #3 - スラッシュコマンドにサブコマンド(Subcommand)を追加しよう!
  4. Discordボット開発 #4 - スラッシュコマンドで埋め込み(Embed)を送信しよう!
  5. Discordボット開発 #5 - ボタン(Button)のインタラクションを実装しよう
  6. Discordボット開発 #6 - ドロップダウン(Select Menu)メニューを実装しよう
  7. Discordボット開発 #7 - モーダル(Modal)で入力を受け取る
  8. Discordボット開発 #8 - 埋め込み(Embed)を画像・フィールドで飾る
  9. Discordボット開発 #9 - AI(Claude・ChatGPT API)と連携する
  10. Discordボット開発 #10 - 多言語(i18n)に対応する ← 今読んでいる記事!

連載 · 全32回中10回目

Discordボット開発

  1. #1Discordボット開発 - スラッシュコマンド(Slash Command)完全攻略!
  2. #2スラッシュコマンドにオプション(Arguments)を追加しよう!
  3. #3スラッシュコマンドにサブコマンド(Subcommand)を追加しよう!
  4. #4スラッシュコマンドで埋め込み(Embed)を送信しよう!
  5. #5ボタン(Button)のインタラクションを実装しよう!
  6. #6ドロップダウン(Select Menu)メニューを実装しよう!
  7. #7モーダル(Modal)で入力を受け取る方法 (Pythonサンプル付き)
  8. #8埋め込み(Embed)を画像・フィールドで飾る (Pythonサンプル付き)
  9. #9AI(Claude・ChatGPT API)と連携する! (Pythonサンプル付き)
  10. #10多言語(i18n)に対応する! (Pythonサンプル付き)現在の記事
  11. #11FlaskでWebダッシュボードを作る! (Pythonサンプル付き)
  12. #12コマンドのクールダウン(Cooldown)・権限(Permission)を管理しよう! (Pythonサンプル付き)
  13. #13ロール・チャンネルの自動化でサーバーを管理しよう! (Pythonサンプル付き)
  14. #14データを保存しよう!JSON vs SQLite (Pythonサンプル付き)
  15. #15슬래시 명령어 그룹(Group)으로 명령어 정리하기! (파이썬 예제) (韓国語)
  16. #16리마인더·알림 기능 만들기! 봇이 먼저 말 걸게 하기 (파이썬 예제) (韓国語)
  17. #17RAG로 문서 기반 답변봇 만들기! 우리 서버 전용 FAQ 봇 (파이썬 예제) (韓国語)
  18. #18투표(Poll) 기능 만들기! 다수결로 정하는 서버 규칙 (파이썬 예제) (韓国語)
  19. #19Cog로 코드 쪼개기! 1000줄 봇도 안 더러워지는 구조 (파이썬 예제) (韓国語)
  20. #20자동 환영 메시지·온보딩 만들기! 규칙 동의 버튼으로 자동 역할 부여 (파이썬 예제) (韓国語)
  21. #21키워드 자동응답·금지어 필터 만들기! 경고 누적으로 자동 제재까지 (파이썬 예제) (韓国語)
  22. #22슬래시 명령어 자동완성(Autocomplete) 만들기! 금지어를 골라서 추가·삭제 (파이썬 예제) (韓国語)
  23. #23aiosqlite로 비동기 DB 제대로 설계하기! JSON 탈출 (파이썬 예제) (韓国語)
  24. #24채널 난독화(Channel Obfuscation) 대응하기! 11/16부터 안 보이는 채널이 생겨요 (파이썬 예제) (韓国語)
  25. #25봇이 죽지 않게! 에러 핸들링·로깅 체계 잡기 (파이썬 예제) (韓国語)
  26. #26티켓(Ticket) 문의 시스템 만들기! 버튼 하나로 1:1 상담 채널 자동 생성 (파이썬 예제) (韓国語)
  27. #27티켓 담당자 배정·우선순위 만들기! 스태프가 여러 명이어도 안 꼬이게 (파이썬 예제) (韓国語)
  28. #28레벨·경험치(XP) 시스템 만들기! 채팅할수록 레벨업, 랭킹판까지 (파이썬 예제) (韓国語)
  29. #29화폐·상점(Economy) 시스템 만들기! 코인 벌어서 아이템 사기 (파이썬 예제) (韓国語)
  30. #30投票・アンケート(Poll)ボットを作ろう!ネイティブ投票+ボタンでリアルタイム集計まで(Pythonサンプル) 🗳️
  31. #32サーバーログ・監査ログ(Audit Log)チャンネルを作ろう!メッセージの削除・編集・入退室・キックまで自動記録(Pythonサンプル) 🕵️
  32. #33임시 음성방 확장, 파티방 만들기! 방장 위임·강퇴·초대 전용(비공개) 방 + 전용 채팅방 자동 생성 (파이썬 예제) 🎉 (韓国語)
全32回を表示閉じる

著者

Mojangcam

自分でコードを書き、サーバーを動かし、詰まったエラーを解決しながら学んだことを記録する開発・IT実践ブログ