LoginSignup
0
1

More than 1 year has passed since last update.

【Unity】DeepLで文書翻訳してみた

Last updated at Posted at 2021-05-05

1. はじめに

ノベルワークスR&Dチームのユウガです。現在は, バーチャルオフィスの開発を行っています。
バーチャルオフィスの機能の1つとして, DeepL文書翻訳をやってみたので紹介します。

主に新しい技術のリサーチをしているため, 最新のドキュメントはだいたい英語だったりします。「このドキュメント日本語にして読みたいなぁ」と思った時に, すぐに翻訳できると便利です。また, 外国人とチャットでやりとりしていて, ファイルを共有された時に, すぐに母国語に翻訳できると便利ですよね。
DeepLを使えば日本語で作った製品案内とかを瞬時に翻訳できるので, 海外への営業・販売など比較的容易に展開できるようになるかもしれませんね。

所見としては, 文書翻訳はベータ版ではありますが, かなり精度も良くて, 全然使えると思いました。また, 画像が添付されてる場合も, レイアウトが崩れる事なく, 翻訳できた印象です。

環境
・windows10
・Unity2020.3.2f.1

2. DeepL文書翻訳

翻訳できるファイル形式は, Word(.docx), PowerPoint(.pptx), Text(.txt), HTML(.html) です。

ファイルサイズ&文字数の制限は, Word(.docx), PowerPoint(.pptx)が, 10MBまたは100万文字。Text(.txt)が, 1MBまたは100万文字。 HTML(.html)が, 5MBまたは100万文字です。

DeepL API プランの場合は, 好きなだけ文書翻訳できるそうです。
このプランは従量課金制で, 1か月の基本料金630円に加え, 1文字あたり0.0025円として料金が請求されます。

注意点としては, 文字数が少ない場合も, 1ファイルにつき最低5万文字分が請求されます。5万文字以上の場合は, 文字数分の料金が請求されます。

なので, 1ファイルにつき最低でも125円かかります.
テストしてる時, これに気が付かなくて焦りました(-_-;)

3. 実装

DeeplFileTranslator.cs
using UnityEngine;
using UnityEngine.Networking;
using System;
using System.IO;
using System.Collections.Generic;
using System.Threading.Tasks;

public static class DeeplFileTranslator
{
    [Serializable]
    public class ResponseForUpload
    {
        public string document_id;
        public string document_key;
    }

    [Serializable]
    public class ResponseForCheck
    {
        public string document_id;
        public string status;
        public string seconds_remaining;
        public string billed_characters;
    }

    private static readonly string auth_key = "XXX";

    public static async Task<string> Upload(string path, string filename, string target_lang, string content_type)
    {
        // pathにあるファイルを読み込む
        byte[] rawdata = File.ReadAllBytes(path);

        List<IMultipartFormSection> requestData = new List<IMultipartFormSection>();
        requestData.Add(new MultipartFormDataSection("auth_key",auth_key));
        requestData.Add(new MultipartFormDataSection("target_lang",target_lang));
        requestData.Add(new MultipartFormFileSection("file", rawdata, filename, content_type));

        UnityWebRequest request = UnityWebRequest.Post("https://api.deepl.com/v2/document", requestData);
        await request.SendWebRequest();

        if (request.result == UnityWebRequest.Result.ProtocolError || request.result == UnityWebRequest.Result.ConnectionError)
        {
            Debug.LogError(request.error);
            return "error";
        }

        return request.downloadHandler.text;
    }

    public static async Task<string> CheckStatus(string document_id, string document_key)
    {
        List<IMultipartFormSection> requestData = new List<IMultipartFormSection>();
        requestData.Add(new MultipartFormDataSection("auth_key",auth_key));
        requestData.Add(new MultipartFormDataSection("document_key",document_key));

        UnityWebRequest request = UnityWebRequest.Post("https://api.deepl.com/v2/document/"+document_id, requestData);
        await request.SendWebRequest();

        if (request.result == UnityWebRequest.Result.ProtocolError || request.result == UnityWebRequest.Result.ConnectionError)
        {
            Debug.LogError(request.error);
            return "error";
        }

        return request.downloadHandler.text;
    }

    public static async Task<string> Download(string document_id, string document_key, string output_path)
    {
        List<IMultipartFormSection> requestData = new List<IMultipartFormSection>();
        requestData.Add(new MultipartFormDataSection("auth_key",auth_key));
        requestData.Add(new MultipartFormDataSection("document_key",document_key));

        UnityWebRequest request = UnityWebRequest.Post("https://api.deepl.com/v2/document/"+document_id+"/result", requestData);
        await request.SendWebRequest();

        if (request.result == UnityWebRequest.Result.ProtocolError || request.result == UnityWebRequest.Result.ConnectionError)
        {
            Debug.LogError(request.error);
            return "error";
        }

        // output_pathにデータを書き込む
        byte[] rawdata = request.downloadHandler.data;
        File.WriteAllBytes(output_path, rawdata);
        return "success";
    }
}

このコードは見てもらえば分かると思いますが, UnityWebRequestを使って, DeepLとやりとりしてます。
UnityWebRequestでasync/awaitを使いたかったので, こちらのコードも使いました。
auth_keyの所は, ダッシュボードにある Authentication Key を入力してください。


FileTranlateManager.cs
using UnityEngine;
using UnityEngine.UI;
using System;
using System.IO;
using System.Threading.Tasks;

public class FileTranslateManager : MonoBehaviour
{
    [SerializeField] Button fileTranslateButton;
    private string document_id;
    private string document_key;

    void Start()
    {
        fileTranslateButton.onClick.AddListener(async () =>
        {
            string path = @"C:\Users\username\Documetns\test.docx";
            string target_lang = "EN-US";
            await DeeplFileTranslate(path, target_lang);
        });
    }

    private async Task DeeplFileTranslate(string path, string target_lang)
    {
        string ext = Path.GetExtension(path);
        string filenameWithoutExt = Path.GetFileNameWithoutExtension(path);
        string output_path = "C:\\Users\\" + Environment.UserName + "\\Downloads\\" + filenameWithoutExt + "_" + target_lang + ext;

        string jsonString = null;
        int millisecond = null;

        // Deeplにアップロード
        jsonString = await DeeplFileTranslator.Upload(path, filenameWithoutExt+ext, target_lang, GetContentType(ext));
        if (jsonString == "error") return;
        var responsForUpload = JsonUtility.FromJson<DeeplFileTranslator.ResponseForUpload>(jsonString);
        document_id = responsForUpload.document_id;
        document_key = responsForUpload.document_key;

        // 翻訳状況をチェック
        while (true)
        {
            jsonString = await DeeplFileTranslator.CheckStatus(document_id, document_key);
            if (jsonString == "error") return;

            // 翻訳完了なら, ループを抜ける
            var responseForCheck = JsonUtility.FromJson<DeeplFileTranslator.ResponseForCheck>(jsonString);
            if (responseForCheck.status == "done") break;

            if (!String.IsNullOrEmpty(responseForCheck.seconds_remaining))
            {
                millisecond = int.Parse(responseForCheck.seconds_remaining) * 1000;
                Debug.Log($"{millisecond / 1000}s remaining.");
            }
            else
            {
                millisecond = 1000;
            }

            await Task.Delay(millisecond); // ミリ秒待つ
        }

        // Deeplからダウンロード
        jsonString = await DeeplFileTranslator.Download(document_id, document_key, output_path);
        if (jsonString == "error") return;

        // 翻訳完了を知らせるために, ダウンロードフォルダを開く
        Application.OpenURL("file:C:\\Users\\" + Environment.UserName + "\\Downloads");

        document_id = null;
        document_key = null;
        return;
    }

    private static string GetContentType(string ext)
    {
        switch(ext)
        {
            case ".docx":
                return "application/vnd.openxmlformats-officedocument.wordprocessingm";
            case ".pptx":
                return "application/vnd.openxmlformats-officedocument.presentationml.presentation";
            case ".txt":
                return "text/plain";
            case ".html":
                return "text/html";
            default:
                return null;
        }
    }

}

このコードは, DeeplFileTranslatorのメソッドを呼び出して, 処理を実行してくれます。
翻訳状況をチェックの所で, 翻訳完了したらループを抜けるようにしてますが, あんまり良くないなぁと思ってるので, こう書いたら良いよって方がいたら教えてください!!

4. おわりに

個人的には, PDFも翻訳してくれたらありがたいのですが, DeeplAPIでは無理です。
なので, PDFを翻訳したければ, CloudConvertAPIを使って, word(.docx)に変換してから, DeepL翻訳すると良いかもしれません。

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