#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. 実装
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
を入力してください。
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翻訳すると良いかもしれません。