QtQuickアプリでMicrosoft Translator APIを使ってみた


下に紹介してる通りMicrosoft Translator APIの使い方を紹介した記事はたくさんあるんだけど、とりあえずなんかAzure DataMarketでのTranslatorのサービスは2017年3月末までとかなんとかで、2017年1月以降にMicrosoft Cognitive Servicesの方に移行してねみたいな話らしい。
で、Microsoft Cognitive ServicesのAPIに移行すると(当たり前なのかもだけど)EndPointとかがちょっと変わるから、そっちを使うサンプルを作ってみた。

参考にした記事

以上、誠に勝手ながら参考にさせてイタダイタ。

Azure

https://portal.azure.com/ からMicrosoftアカウントでログインして、「新規」→「Intelligence + analytics」→「Cognitive Service API」と選択して、必要項目を入力する。

  • Account Name: アプリの名前とか?
  • サブスクリプション: 「無料試用版」
  • API Type: Translator Text API
  • Pricing Tier: F0 Free
  • Resource group: 適当に名前つける
  • Resouce group location: 東日本 or 西日本

これで「作成」押して必要な確認を済ませるとダッシュボードから確認ができるようになる。
「すべてのリソース」からさっき作ったサブスクリプションを選択し、RESOUCE MANAGEMENTの「KEY」を開くとACCOUNT NAMEとKEY 1、KEY 2が確認できるはず。
なんでキーは2つあるのかよく知らないけど(ぉぃ)、これが下でAccessTokenを取得するときに必要になる。

APIのドキュメント

一つ目がトークン取得用のAPIについてのドキュメント(Cognitive Serviceは共通してこのAPIでTokenを発行するらしい)、二つ目がTranslation APIを使うためのドキュメントになる。(まぁ、見ればわかるか)

使い方らしきもの

基本的にまずSubscription-Keyを使ってAccess Tokenを発行し、Tokenを使ってTranslator APIにアクセスするという手順自体は変わっていないらしい。

に対して

Ocp-Apim-Subscription-Key: <your-key>

というヘッダを定義するか、もしくはurlに

Subscription-Key=<your-key>

をつけて投げるとTokenが返ってくるという寸法。

// Pass key using header
curl --header 'Ocp-Apim-Subscription-Key: <your-key>' --data "" 'https://api.cognitive.microsoft.com/sts/v1.0/issueToken'
// Pass key using query string parameter
curl --data "" 'https://api.cognitive.microsoft.com/sts/v1.0/issueToken?Subscription-Key=<your-key>'

返って来たTokenをurlに

appid=Bearer <access_token>

の形で付与するか、もしくは

Authorization: Bearer <access_token>

というヘッダーを付与した上で、必要なパラメータをつけて

に投げれば、xml形式で訳語が返ってくる。

書いてみた

サーバからエラーが返った時の処理とかは一切書いてないけど、API使う部分についてはこんな感じかなぁ。

translate.js
function getToken(callback) {
    var xhr = new XMLHttpRequest();
    xhr.open('POST', 'https://api.cognitive.microsoft.com/sts/v1.0/issueToken', true);
    xhr.setRequestHeader('Ocp-Apim-Subscription-Key', subscriptionKey);
    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
            if (xhr.status === 200) {
                callback(xhr.responseText);
            } else {
                console.log(xhr.responseText);
            }
        }
    }
    xhr.send();
}

function translate(token, text, callback) {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', 'https://api.microsofttranslator.com/v2/http.svc/Translate?text=%1&to=%2&category=%3'.arg(text).arg('ja').arg('generalnn'), true);
    xhr.setRequestHeader('Authorization', 'Bearer %1'.arg(token));
    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
            if (xhr.status === 200 && xhr.responseText.match(/<string .*>(.*)<\/string>/)) {
                callback(RegExp.$1);
            } else {
                console.log(xhr.responseText);
            }
        }
    }
    xhr.send();
}

XMLなんだからちゃんとパースしてstringタグからデータ取れよって話なんだけど、今のところQML上ではgetElementsByTagNameとかいろいろ未実装な部分があってよくわからないから正規表現で取り出すことにした(笑)
あと、去年の11月の発表によるとcategoryに"generalnn"を指定するとニューラル ネットワークに対応した翻訳ができるっていうことらしいので指定しておいた。(デフォルトは"general")

んでもって、とりあえずQMLからこんな感じで使ってみた。

main.qml
import QtQuick 2.7
import QtQuick.Window 2.2
import 'translate.js' as Translate

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    Row {
        anchors.fill: parent
        Rectangle {
            width: parent.width / 2
            height: parent.height
            border.width: 1
            TextInput {
                id: translate
                anchors.fill: parent
                padding: 10
                text: 'Please input the texts.'
                wrapMode: Text.WordWrap
                onTextChanged: {
                    Translate.getToken(function (token){
                        Translate.translate(token, translate.text, function (translated_text) {
                            translated.text = translated_text;
                        });
                    });
                }
            }
        }
        Rectangle {
            width: parent.width / 2
            height: parent.height
            border.width: 1
            Text {
                id: translated
                anchors.fill: parent
                padding: 10
                text: ''

                wrapMode: Text.WordWrap
            }
        }
    }
}

実行結果はこんな感じ。

まぁいいんじゃないだろうか。

翻訳の精度的にはなんとなくGoogle翻訳の方がいい感じに見えるんだけど、そこそこ使える感じの訳語が返ってくるし、何より2,000,000文字まで無料で使えるのがありがたい。
あと、なんかお題目的に「QtQcuikで使う」とは書いたんだけど、よくよく見たらほとんどJavascriptじゃん(笑)

一応、今回のソースはGitHubで公開しとくのでなんかこれはダメだろ的なものがあったら教えてください m(_ _)m
https://github.com/helicalgear/sample-ms-translation-api