5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Qiitanがほしい人の一人アドカレAdvent Calendar 2024

Day 21

逆翻訳して遊びたい!でも翻訳APIは有料なこと多いし・・・え?Pythonでローカルで動く翻訳ライブラリが?

Last updated at Posted at 2024-12-21

いつもの読み飛ばしポイント

どうも限界派遣SESのnikawamikanです。
アドカレもあと5日で終わりますね。25記事書くの頑張ります。
ちなみに本日も遅れています。

本日はクソアプリを作っていこうと思います。

逆翻訳ってクソおもろい

逆翻訳といえば何を思い浮かべますか?
私はバイヤー高橋さんです。

くっそ面白いですよねw

現代ではGoogle翻訳やDeepLなど真面目に使えるWeb翻訳などが充実していますが、これらをAPIとして使おうとすると有料なことが多いです。
仕方ないことですね。ディープラーニングのコストなどを考えると、Web翻訳を無料で使えるのはありがたいことです。

しかし、Pythonにはローカルで使える翻訳ライブラリがあります。

今回はargostranslateを使って逆翻訳APIを作っていきます。

環境構築

今回は以下の環境で逆翻訳APIを作っていきます。

  • Python 3.12

ライブラリのインストール

まずは以下のライブラリをインストールします。

ライブラリ 用途
argostranslate 翻訳ライブラリ
uvicorn サーバー
fastapi APIフレームワーク
pip install argostranslate uvicorn fastapi

次にargostranslateのモデルをダウンロードしておきます。

argospm install translate

これを実行すると${HOME}/.local/cache/argos-translateにモデルがダウンロードされます。

逆翻訳APIの作成

それでは逆翻訳APIを作成していきます。

翻訳関数

まずは翻訳関数を作成します。

from argostranslate import translate
from functools import lru_cache

@lru_cache(2 ** 10) # キャッシュを使って同じ組み合わせの翻訳を高速化
def translate_text(text: str, source_lang: str, target_lang: str) -> str:
    translated_text = translate.translate(
        text, # 翻訳したいテキスト
        source_lang, # 翻訳前の言語
        target_lang # 翻訳後の言語
    )
    return translated_text

この翻訳ですが、CPUで動作させる場合結構時間がかかります。
私のサーバーマシンはGPUを搭載していないので、残念ですがCPUで動かします。

なので、少しでも高速化するためにfunctools.lru_cacheを使ってキャッシュを使います。
これは引数が同じ場合、結果をキャッシュしたものを返すことの出来るデコレータです。

次に逆翻訳関数を作成します。

def re_translate_text(text: str, source_lang: str, via_langs: list[str]) -> str:

    # 元の言語から中間言語へ翻訳
    translated_text = translate_text(
        text,
        source_lang,
        via_langs[0]
    )

    # 中間言語から中間言語へ翻訳
    for i in range(len(via_langs) - 1):
        translated_text = translate_text(
            translated_text,
            via_langs[i],
            via_langs[i + 1]
        )

    # 中間言語から元の言語へ翻訳
    translated_text = translate_text(
        translated_text,
        via_langs[-1],
        source_lang
    )
    return translated_text

この関数は、元の言語から中間言語へ翻訳し、中間言語から中間言語へ翻訳し、中間言語から元の言語へ翻訳する関数です。
via_langsには中間言語のリストを渡します。
中間言語が1個の場合は、元の言語から中間言語へ翻訳し、中間言語から元の言語へ翻訳するだけです。
2個以上の場合は、中間言語から中間言語へ翻訳を行います。

FastAPIの作成

次にエンドポイントを定義します。

from fastapi.responses import PlainTextResponse

@app.get("/re_translate")
def re_translate(text: str, source_lang: str, via_langs: str):
    """再翻訳して人間が理解できないテキストに翻訳します。
    """
    return PlainTextResponse(
        re_translate_text(
            text,
            source_lang,
            via_langs.split(",")))

@app.getデコレータを使って、GETリクエストを受け取るエンドポイントを作成します。
/re_translateエンドポイントにTranslateModelを受け取り、re_translate_text関数を呼び出して結果を返します。

この手軽さがFastAPIのいいところですね。

サーバーの起動

サーバーを起動して、APIを叩いてみます。

uvicorn main:app --reload

--reloadオプションをつけると、ファイルの変更を検知してサーバーを再起動してくれます。

これで実行しhttp://127.0.0.1:8000/docsにアクセスすると、SwaggerUIが表示されます。

ここではエンドポイントを試すことができるので、試してみましょう。

textに今日のご飯何にする?おでん?みかん?と入力し、source_langにja、via_langsにen,ruを入力してExecuteをクリックします。

image.png

すると、ペットと一緒に何をしますか?という結果が返ってきました。

いいですねw

Discord Botに組み込む

汎用的に利用することを考えるとAPIのほうが便利ですが、人に見せたりするにはDiscord Botにすると見せやすくて面白いですよね。

Botの作成

まず追加のライブラリをインストールします。

ライブラリ 用途
py-cord Discord Botライブラリ
aiocache キャッシュライブラリ
pip install py-cord aiocache

次にBotを作成します。

main.py
import discord
from discord import option
import aiohttp
from aiocache import cached

intents = discord.Intents.default()

bot = discord.Bot(intents=intents)

# 翻訳関数
@cached(600) # キャッシュを使ってBOT側で再問い合わせを減らす
async def translate_text(text: str, source_lang: str, via_langs: str) -> str:
    encoded_text = quote(text)
    encoded_source_lang = quote(source_lang)
    encoded_via_langs = quote(via_langs)

    async with aiohttp.ClientSession() as session:
        async with session.get(
            f"http://127.0.0.1:8000/re_translate?text={encoded_text}&source_lang={
                encoded_source_lang}&via_langs={encoded_via_langs}"
        ) as response:
            translated_text = await response.text()
    return translated_text

# 逆翻訳コマンド
@bot.slash_command(name="re_translate", description="逆翻訳します")
@option("text", description="翻訳するテキスト")
@option("source_lang", description="翻訳前の言語")
@option("via_langs", description="翻訳する言語 ex) en,ja")
async def re_translate(
    ctx: discord.ApplicationContext,
    text: str,
    source_lang: str,
    via_langs: str,
):  # Takes one integer parameter
    await ctx.defer() # 返信まで時間がかかるので、待機状態にする

    try:
        translated_text = await translate_text(text, source_lang, via_langs)
        # followupで待機状態のメッセージを消して、結果を返す
        await ctx.interaction.followup.send(translated_text)
    except Exception as e:
        await ctx.interaction.followup.send("エラーが発生しました")
        raise e


bot.run("TOKEN")

TOKENにはBOTのトークンを入力してください。
できれば、.envファイルなどを使って環境変数にしたほうがセキュリティ的にはいいです。

軽く説明すると、
翻訳関数を定義し、翻訳関数にはAPIの呼び出しキャッシュを取得するためにcachedアノテーションを使っています。600秒間キャッシュを保持することで、再問い合わせのコストを下げます。

@bot.slash_commandデコレータを使って、/re_translateコマンドを作成しています。
@optionデコレータを使って、コマンドのオプションを設定しています。
この@optionデコレーターはなくてもいいですが、使うことで説明などを設定することができます。

Botの起動

botを起動は普通にmain.pyを実行するだけです。

python main.py

これでBotが起動します。

DiscordにBotを追加して、/re_translateコマンドを実行してみましょう。

image-2.png

image-3.png

こんな感じで逆翻訳ができました。

なんかインパクトに欠けますね。

画像に変換する

インパクトの強い画像を生成するAPIといえば5000choyen-apiというものがあります。
これを使って、逆翻訳したテキストを画像に変換してみましょう。

まずはAPIを使って画像を生成する関数を作成します。

from io import BytesIO
import discord
from discord import option
import aiohttp
from urllib.parse import quote
from aiocache import cached

@cached(600)
async def text_to_image(text: str):
    escaped_text = escape(text)
    async with aiohttp.ClientSession() as session:
        async with session.get(
            f"https://gsapi.cbrx.io/image?top={escaped_text}&single=true"
        ) as response:
            image = await response.read()

            return image

ここで返却される画像はbytesオブジェクトなので、これをdiscord.Fileに変換して送信します。

@bot.slash_command(name="re_translate_image", description="逆翻訳して画像にします")
@option("text", description="翻訳するテキスト")
@option("source_lang", description="翻訳前の言語")
@option("via_langs", description="翻訳する言語 ex) en,ja")
async def re_translate_image(
    ctx: discord.ApplicationContext,
    text: str,
    source_lang: str,
    via_langs: str,
):  # Takes one integer parameter
    await ctx.defer()

    try:
        translated_text = await translate_text(text, source_lang, via_langs)
        image = await text_to_image(translated_text)
        byteio = BytesIO(image)
        byteio.seek(0)
        await ctx.interaction.followup.send(file=discord.File(byteio, "translated_image.png"))
    except Exception as e:
        await ctx.interaction.followup.send("エラーが発生しました")
        raise e

これで/re_translate_imageコマンドを使って、逆翻訳したテキストを画像に変換することができます。

ByteIOを使って、discord.Fileに変換しています。

実行すると以下のような画像が生成されます。

image-5.png

まとめ

今回はargostranslateを使って逆翻訳APIを作成しました。
また、この翻訳精度ですが、Google翻訳やDeepLに比べると精度は低いですが、逆翻訳として使う分には逆に面白い気がします。

クソアプリ作成の参考にしてみてください。

それでは。

5
1
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
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?