Windows Translatorで "ArgumentOutOfRangeException: 'to' must be a valid languageというエラーが出るときの対処


症状

Microsoft Translator APIを使おうとしたら、タイトルどおりのエラーメッセージが出た。

APIに渡すクエリは
?text=翻訳したいテキスト&from=ja&to=en
のようになるのだが、何度ソースをみても&to=enは間違いなく入っている。

ググると結構はまっている人がいるみたいだが、わかりやすい解決方法はみつからない。
半日以上かけてなんとか解決したので記録しておく。

原因:URLエンコードしすぎ

いろいろ確認すると、

?text=hello&from=en&to=ja

と手で入力したときにはエラーがでない。

コードを見直してみると、クエリ全体をURLエンコードしていた。
どうやらそれが原因らしく、上記のクエリのエンコードされた後の文字列を確認してみると、

%3Ftext%3Dhello%26from%3Den%26to%3Dja

みたいになっていた。

'&'や'='などもエンコードされていて、そりゃエラーがでるよね…。

URLエンコードは翻訳したいテキストだけすることにしたら解決しました。

いちおう翻訳部分だけコードを載せておきます。

アクセストークンは別に用意してください。

TypeScript
import qs = require('querystring');

// 略

    translate(token, text, from_lang, to_lang, callback) {
        const options = "text=" + qs.escape(text) + // ここだけエンコード
                        "&from=" + from_lang +
                        "&to=" + to_lang +
                        "&oncomplete=callback";

        const request_options = {
            host: 'api.microsofttranslator.com',
            path: '/V2/Ajax.svc/Translate?' + options,
            method: 'GET',
            headers: {
                "Authorization": 'Bearer ' + token
            }
        };

        const req = https.request(request_options, function(res) {
            var body = '';
            res.setEncoding('utf8');

            res.on('data', function(chunk) {
                body += chunk;
            });

            res.on('end', function() {
                // body の中身は "callback(translated_text)" という形になっているはず。
                eval(body); // callback関数に翻訳されたテキストだけ渡す。
            });

        });
        req.on('error', function(e) {
            console.log(e.message);
        });
        req.end();
    }

参考

Microsoft Translator APIを使ってみる