この前、会社のSlackのとあるチャンネルでKiaraという入力した言語によって
「英語から日本語へ」もしくは「日本語から英語へ」よしなに翻訳して投稿してくれるサービスを見掛けました。
Kiaraは100の言語に対応しているみたいですが、英語と日本語だけでも同じようなことが無料で出来ないかなと思ってやってみました。
完成イメージ
実装イメージ
- 投稿したスレッドに対して翻訳結果が投稿する
- なんちゃって日本語判定機能で投稿された文書が日本語か判断したい
- 日本語で投稿すれば「日本語から英語」へ翻訳する
- 英語で投稿すれば「英語から日本語」へ翻訳する
- オプションで翻訳結果を別の新しいスレッドへ投稿できるように
やり方
DeepL APIの登録
今回翻訳に使うのは皆んなお世話になりまくっている「DeepL」です。
DeepLには無料で使えるAPIが提供されているので、今回はそちらを利用します。
最初に言っておくとDeepLのAPIを利用するにあたって、クレジットカードが必須なのでご準備ください。これ不正利用の対策で登録させられるだけで、無料版を使い続けている限り課金されることはありません。
無料で使えるAPIの特徴はこちら。
- 1か月に500,000文字まで翻訳できる
- セキュリティ対策が有料版より弱い
- 翻訳される際、有料版の方が優先される
会話の少ないチャンネルや個人のワークスペースとかであれば全然問題ない範囲だと思いますが、会話が頻繁に行われる場所であれば、1ヶ月500,000文字制限がネックになってくるかも知れません。
が、それは実際に制限かかった時に有料版にするか考えましょう。
ちなみに有料版は毎月630円は固定でかかり、そこから1,000,000文字あたり2,500円課金されていきます。
登録方法
こちらのDeepL APIページから「無料で登録する」を選択し後は適当に入力していくだけです。
ここで先ほど準備したクレジットカード情報を入力する必要があります。
最後まで登録が完了したらアカウント画面にある「アカウント」をクリックすると、画面下あたりに認証キーが表示されていると思うので、このキーをどこかにメモっておいて下さい。
無料版アカウントだと、通常のWeb画面上で行う翻訳が使えなくなってしまうので、そちらも利用する場合はログアウトしておくこと!!
Slack Appの作成
Slack上で実際に翻訳後のテキストを投稿してくれるAppを作成します。
作成方法はこちらの記事を参考に、
まで実施して、WebhookのURLをどこかに保存しておいて下さい。
GoogleAppScript(GAS)の作成
今回の肝となる、DeepL APIを実行して翻訳を行うロジックをGASで作成していきます。
GASトップページの「新しいプロジェクト」から適当にプロジェクトを作成して下さい。
コードを追加
コードを追加していくファイルを作成します。
最初からある「コード.gs」の他にもう1つファイルを新しく作成して下さい。
また、「コード.gs」も一緒に名前を変えちゃって、合計2つのファイルを用意して下さい。
それぞれのファイル名と、処理内容のイメージは以下。
- Slackからリクエスト受け取る
- Slackに投稿されたメッセージが日本語か英語か、checkJapanese.gsを呼び出して判定する
- 判定の結果、日本語でなければ英語と判断する
- DeepL APIを使って翻訳結果を受け取る
- SlackのWebhook URLを叩いて翻訳結果をSlack投稿する
- main.gsから呼び出される
- 渡されたメッセージが日本語か判定する
- なんちゃって日本語判定で100%の精度ではないのでご注意を
実際のコードはこちら。
function doPost(e) {
// スレッドに投稿するフラグ
// true : 投稿されたスレッド内に翻訳結果を投稿
// false: 別の新しいスレッドに翻訳結果を投稿
const THREAD_POST_FLG = true
// スクリプトプロパティの取得
let scriptProperties = PropertiesService.getScriptProperties()
let AUTH_KEY = scriptProperties.getProperty('DEEPL_AUTH_KEY')
let WEBHOOK_URL = scriptProperties.getProperty('WEBHOOK_URL')
// 翻訳対象テキスト
// var translationText = 'Anya, I like peanuts.'
var translationText = 'アーニャを知ると世界が平和に'
var ts = ''
// 実行用にnullの場合はSlack APIの仕様をスキップ
if(e != null) {
// 疎通確認
var params = JSON.parse(e.postData.getDataAsString());
if('challenge' in params){
return ContentService.createTextOutput(params.challenge);
}
// Botの投稿に反応しないように修正
if('subtype' in params.event) {
return
}
// Slack投稿内容をパース
var params = JSON.parse(e.postData.getDataAsString())
if('text' in params.event) {
translationText = params.event.text
}
// スレッドを取得
ts = params.event.ts
}
var targetLang = ''
var sourceLang = ''
// 言語判定
if (checkJapanese(translationText)) {
// 日本語->英語翻訳
targetLang = 'EN-US'
sourceLang = 'JA'
Logger.log('日本語から英語に翻訳します')
} else {
// 英語->日本語翻訳
targetLang = 'JA'
sourceLang = 'EN'
Logger.log('英語から日本語に翻訳します')
}
// DeepL
// API Document: https://www.deepl.com/ja/docs-api/translating-text/request/
// NOTE: 有料版を使ってる人はURLの向き先を有料版URLへ変更して下さい
let DEEPL_API_URL = 'https://api-free.deepl.com/v2/translate?auth_key=' + AUTH_KEY
+ '&text=' + translationText + '&target_lang=' + targetLang + '&source_lang=' + sourceLang
// DeepL APIの実行
let deeplResponse = UrlFetchApp.fetch(DEEPL_API_URL).getContentText("UTF-8")
// DeepL APIのレスポンスをパース
let deeplResponseObject = JSON.parse(deeplResponse)
// 翻訳後テキストを取得
let afterTranslationText = deeplResponseObject.translations[0].text
// リクエスト内容を整形
var options = {}
if (ts && THREAD_POST_FLG) {
// スレッドがあって、スレッド内投稿する設定だったらスレッド内に投稿
options = {
"method" : "post",
"contentType" : "application/json",
"payload" : JSON.stringify(
{
"text" : afterTranslationText,
"thread_ts" : ts
}
)
}
} else {
// それ以外は新しいスレッドに投稿
options = {
"method" : "post",
"contentType" : "application/json",
"payload" : JSON.stringify(
{
"text" : afterTranslationText
}
)
}
}
// 投稿
UrlFetchApp.fetch(WEBHOOK_URL, options);
}
function checkJapanese(text) {
let divisionText = []
// 文字数判定
if (text.length <= 10) {
// 10文字以下ならそのまま判定へ回す
divisionText.push(text)
} else {
// 10文字以上なら、雑に入力文字列を10分割
let divisionNumber = Math.trunc(text.length / 10)
for (let i = 0; i < 10; i++) {
divisionText.push(text.substr(i * divisionNumber, divisionNumber));
}
}
// 分割した入力文字を日本語か判定
var jConunt = 0
var eCount = 0
for (const element of divisionText) {
// 日本語か判定
if (check(element)) {
// 日本語
jConunt++
} else {
eCount++
}
}
// 日本語の方が多ければ、入力文字列は日本語として扱う
return jConunt > eCount
}
// textが日本語のみで構成されているかチェック
function check(text) {
// カタカナ(\u30a0-\u30ff), ひらがな(\u3040-\u309f), 々-〆(\u3005-\u3006), CJK統合漢字(\u30e0-\u9fcf)
return (text.match(/^[\u30a0-\u30ff\u3040-\u309f\u3005-\u3006\u30e0-\u9fcf]+$/) )? true : false
}
同じスレッドで翻訳結果を投稿してほしくない場合はTHREAD_POST_FLG
の値をtrue
からfalse
に変更して下さい。
スクリプト プロパティの設定
今回設定するプロパティは以下です。
プロパティ名 | 内容 | サンプル値 |
---|---|---|
DEEPL_AUTH_KEY | 最初に発行したDeepL APIの「認証キー」 https://www.deepl.com/ja/account/summary | 12345678-abcd-12a3-456b-789def012gh3:xx |
WEBHOOK_URL | 先ほど作成したSlack Appで設定されたWebhook URL https://api.slack.com/apps | https://hooks.slack.com/services/XXXXX/XXXX/xxxxxxxxx |
開いているGASのプロジェクト画面左側に歯車アイコンの「プロジェクトの設定」があると思うので、そこから設定画面へ移り「スクリプト プロパティ」で上記の値を設定して下さい。
デプロイ
ここまで設定できたら、デプロイしてアプリケーションにしてみましょう!
① 右上の「デプロイ」をクリックしたら3つ選択肢が出てくるので「新しいデプロイ」を選択
② 「デプロイタイプを選択してくだと」と出てくるので歯車マークから「ウェブアプリ」を選択
③ 「アクセスできるユーザー」を「全員」にしてSlackからアクセスできるように
④ ここまでできたら「デプロイ」をクリックして完了!
ウェブアプリ URLが作成されるのでコピーしておく。
ここのURLへリクエストするとさっきのコードが実行されるので、今度はSlackからここへリクエストする設定を追加してあげます。
Slackで投稿されたらGASへリクエストする
GASでランダムに選んでくれるアプリケーションが完成したので、
Slack Appで特定のチャンネルへ投稿されたら、GASを呼び出してあげるようにします。
Event Subscriptionsの設定
これを設定すると、Appが許可されたチャンネルで特定のイベント(メッセージが投稿されたら、ファイルが作られたらなど)が起きると、意図したURLへリクエストを送れるようになります。
① 左側のメニューから「Event Subscriptions」を選択
② 右上のトグルを「Off」から「On」へ
③ 「Request URL」にさっき作成したGASのアプリケーション URLを入力
④ 「Subscribe to bot events」で「message.channels」(検知したいイベント)を選択
⑤ ④までできたら「Save Chanes」から設定保存!
Appをチャンネルに追加
さっき設定したイベントを検知するにはApp(Bot)がチャンネルに入っている必要があります。
なので追加してあげましょう。
私はいつもメンション付けてチャンネルに入れてますが、好きな方法で大丈夫です。
これでやる事は以上です!お疲れ様でした!
動作確認してみる
実際にAppを追加したチャンネルで適当に日本語と英語で投稿してみましょう!
英語で登校した時は日本語で、日本語で登校した時は英語で投稿されていると思います。
最後に
今回はSlackで無料自動翻訳Botを作成しました!
外国の方と一緒に行っているプロジェクトなどで導入すると会話がスムーズになると思うので是非やってみて下さい。
まずは無料でやってみて、もっと沢山の機能が欲しくなったり、複数の言語に簡単に対応したくなったらKiaraなどの有料サービスを使ってみると良いかと!!
MATTSU