https://d226lax1qjow5r.cloudfront.net/blog/blogposts/build-a-speech-translation-app-on-deno-with-azure-and-vonage/Blog_Speach-Translation_Deno-Azure_1200x600-1.png

AzureとVonageでDeno上に音声翻訳アプリを構築する

最終更新日 May 4, 2021

所要時間:1 分

Vonageは最近 自動音声認識 (ASR)をVoice APIの新機能としてリリースしました。これは、この新機能を活用するために楽しい新しい音声アプリケーションを構築する素晴らしい理由です!

このチュートリアルでは、Deno上で動作するVoiceアプリケーションを構築します。 チュートリアル上で動作する音声アプリケーションを構築します:

  1. 電話を受ける

  2. プロンプトで発信者が言ったスピーチを受け入れる。

  3. Vonage ASRを使って音声をテキストに変換

  4. Microsoft Azureを使用して、ランダムに選択した言語に翻訳します。

  5. 元の英文と新しく翻訳された英文の両方を言い返す

  6. 新しく翻訳されたテキストに Vonage Voiceが利用可能な場合その音声が使用されます。

私たちがDenoを実行環境として構築しているのは、Denoによって軽量な依存関係でTypeScriptのサーバーサイドアプリを構築できるからだ。実際に必要な外部コードだけを統合し、必要な実行権限だけを与えることができる。

テキスト翻訳を提供するために統合できるプロバイダーはいくつかあります。このチュートリアルでは Microsoft Azure音声翻訳API.

始めよう!

tl;dr もしスキップしてアプリだけを実行したいのであれば、完全に動作するバージョンを GitHub.

前提条件

このアプリケーションを作るには、実装を始める前にいくつかのアイテムが必要です:

それがすべて片付いたら、アプリケーションの実装に取りかかろう。

Vonage API アカウント

このチュートリアルを完了するには Vonage APIアカウント.まだお持ちでない方は 今すぐサインアップをクリックし、無料のクレジットを使用して構築を開始することができます。アカウントを取得すると、APIキーとAPIシークレットが Vonage APIダッシュボード.

このチュートリアルではバーチャル電話番号も使用します。購入するには Numbers> 番号を購入するにアクセスし、ニーズに合ったものを検索してください。

Start building with Vonage

フォルダ構造の作成

最初のステップは、アプリケーションのフォルダ構造を作成することです。最終的にはこのようになる:

.
+-- data/
|   +-- languages.ts
|   +-- voices.ts
+-- services/
|   +-- auth/
|     +-- token.ts
|   +-- translate.ts
|   +-- language_picker.ts
|   +-- voice_picker.ts
+-- server.ts
+-- .env

このチュートリアルでは、アプリケーションのルート・フォルダーと呼ぶことにします、 speech-translation-appと呼びますが、お好きな名前を付けてください。ルート・フォルダーを作成したら、その中にディレクトリを移動して data, servicesservices/authサブフォルダーを作成します。

ルート・フォルダー内に server.ts.envファイルを作成する。 touch server.ts .envを実行してファイルを作成する。

の中で同様の操作を行う。 data, servicesおよび services/authフォルダー内でも同様の操作を行い touchを実行して、上のディレクトリツリーに示したファイルを作成する。

Denoサーバーの作成

お好みのコード・エディターで server.tsファイルを開いてください。

このファイルの中で、HTTPサーバーをインスタンス化し、ルートを提供し、アプリケーションのフローを制御する。

私たちは Opineを使うことにする。Opineは、ExpressJSから移植されたDenoのために作られたミニマリスト・フレームワークだ。ExpressJSに慣れている人なら、Opineの構成はなじみのあるものに感じられるだろう。

Opineを使用するには、ファイルの先頭でインポートする必要があります。DenoはNodeJSとは異なり、node_modulesや他の同様のパッケージ管理システムを使用しません。その結果、アプリケーションに取り込まれる各パッケージは、そのソースから直接インポートされます:

import { opine } from "https://deno.land/x/opine@master/mod.ts";

opineを使えるようにしたら、そのインスタンスをインスタンス化して、ルートのスケルトン構造を作ることができる:

const app = opine();
 app.get("/webhooks/answer", async function (req, res) {
  // Do something on a GET request to /webhooks/answer
});
 app.get("/webhooks/asr", async function (req, res) {
  // Do something on a GET request to /webhooks/asr
});
 app.get("/webhooks/event", async function (req, res) {
  // Do something on a GET request to /webhooks/event
  res.status = 204
})
 app.listen({ port: 8000 });
 console.log("Server is running on port 8000");

サーバーに列挙された3つの GETサーバーに列挙された3つのリクエストは、Vonage Voice APIからの3つのユニークなウェブフックに対応している。最初のものは、APIが着信コールを送信するものである。2つ目は、APIがVonageの自動音声認識機能を使用してテキストに変換された音声を送信する場所である。最後に、3つ目のルートは、通話のライフサイクルのすべてのイベントデータが送信される場所です。

アプリケーションの機能を制御するために、これら3つのルートそれぞれにロジックを提供する必要があります:この会話はNJalal7によって解決済みとしてマークされました。

  • 着信ルートは、発信者の音声入力を取り込み、テキスト変換のためにVonage APIに送信する処理を行う。

  • 2つ目のルートはテキストを受信し、Azure Speech Translation APIに送信して第二言語に翻訳します。また、元のメッセージと翻訳されたメッセージを発信者に再生します。

  • 最終ルートは、すべての呼のライフサイクルイベントデータを受け取り、デー タの受信を確認する。

ルートの定義

着信コールのロジックを構築してみよう。 /webhooks/answerルート

ルート内部では、発信者ID(UUID)を変数に代入する必要があります。変数 UUIDはASRリクエストに必要なコンポーネントです:

const uuid = req.query.uuid

次に、HTTPステータスコード 200で応答し、Nexmo Call Control Object (NCCO)を送り返す必要がある。NCCOは、Vonage APIに実行させたい命令のセットを含むJSONオブジェクトである:

res.json([
  {
    action: 'talk',
    text: 'Welcome to the Vonage Universal Translator Randomizer brought to you by Vonage Automatic Speech Recognition run on Deno. Please say something.',
    bargeIn: true
  },
  {
    eventUrl: [asrWebhook],
    eventMethod: 'GET',
    action: 'input',
    speech: {
      uuid: [uuid],
      language: 'en-us'
    }
  }
]);

ご覧の通り、NCCOは2つのアクションで構成されている: talkinputそれぞれ

その talkアクションは呼び出し元を歓迎し、何かを言うように求める。また、パラメータ bargeInに等しい trueに等しいパラメータを設定します。

アクションは inputアクションは、発信者のVoice入力を受け付ける場所です。このアクションでは、いくつかのユニークなパラメータを定義します:

  • eventUrl:テキストに変換された音声をどこに送るか。アクションでは、URLを asrWebhook.これは後で作成します。

  • eventMethod:どのHTTP動詞を使って、完成した音声テキストを送信するか。この場合は GET.

  • action:すべてのNCCOアクションの基本パラメータ。この値は実行したいアクションに等しい。 input.

  • speech:を含むオブジェクトに等しい値を持つパラメータ。 UUIDを含むオブジェクトに等しい。 languageを含むオブジェクト。

この最初の GETルートは次のようになる:

app.get("/webhooks/answer", async function (req, res) {
  const uuid = req.query.uuid
  res.status = 200
  res.json([
    {
      action: 'talk',
      text: 'Welcome to the Vonage Universal Translator Randomizer brought to you by Vonage Automatic Speech Recognition run on Deno. Please say something.',
      bargeIn: true
    },
    {
      eventUrl: [asrWebhook],
      eventMethod: 'GET',
      action: 'input',
      speech: {
        uuid: [uuid],
        language: 'en-us'
      }
    }
  ]);
});

次に定義する必要があるのは /webhooks/asrこのルートは Vonage API からテキストに変換された音声を受け取り、それを処理します。

使用する変数に割り当てたい値がいくつかある。1つ目はASR変換の結果で、オブジェクトの配列という形で私たちに送られてくる。オブジェクトは精度の高い順に並んでいる。番目の変数には、最も精度の高いオブジェクトのテキストが格納されます。

2番目の変数を空の変数としてインスタンス化し、Vonage ASRが発信者の発話を拾えたかどうかという条件に基づいて値を代入する。発話が認識された場合、その値が使用される。しかし、音声が認識されなかった場合は、デフォルト値が提供され、その理由を示すメッセージがコンソールに表示される。

最後に作成する2つの変数には、翻訳する音声のランダムな言語選択の値を代入し、翻訳を話すVoiceを選択します。そして、言語とVoiceの情報をコンソールで共有します:

const data = await JSON.parse(req.query.speech)
var mostConfidentResultsText;
if (!data.results) {
  console.log("Vonage ASR did not pick up what you tried to say");
  mostConfidentResultsText = 'Vonage ASR did not pick up your speech. Please call back and try again.';
} else {
  mostConfidentResultsText = data.results[0].text;
};
const languageChoice = languagePicker(languageList);
const voiceChoice = voicePicker(voicesList, languageChoice);
console.log(`Language to translate into: ${languageChoice.name} and Vonage language voice being used: ${voiceChoice}`);

そして、最初のルートと同じように、レスポンスのHTTPステータスコードを 200に設定し、別のNCCO JSONオブジェクトで応答します:

res.status = 200
res.json([
  {
    action: 'talk',
    text: `This is what you said in English: ${mostConfidentResultsText}`
  },
  {
    action: 'talk',
    text: `This is your text translated into ${languageChoice.name}`
  },
  {
    action: 'talk',
    text: `${await translateText(languageChoice.code.split('-')[0], mostConfidentResultsText)}`,
    voiceName: voiceChoice
  }
])

このNCCOオブジェクトには3つのアクションが含まれている。 talkアクションがあり、それぞれ変数と関数を作成する必要があります。ルートの定義が終わったら、それを行います。

最初の talkアクションは、自動音声認識変換中に理解されたように、発信者に元のメッセージを英語で言い返す。

2番目の talkアクションは、発信者にメッセージがどの言語に翻訳されたかを伝えます。

3番目の talkアクションは、発信者に新しく翻訳されたメッセージを言う。また voiceNameパラメータも活用し、その言語で指定されたVoiceがあれば、その言語で翻訳されたメッセージを言う。

最後に定義する必要があるのは、短いルートです。これは、コールの残りのイベントウェブフックデータを受け取るものです。このチュートリアルでは、受け取ったことを確認する以外に、そのデータで何かをするつもりはありません。HTTPステータスコード 204HTTPステータスコードを送り返すことで確認します。これは、メッセージが成功したことと同じで、応答するコンテンツはありません:

app.get("/webhooks/event", async function (req, res) {
  res.status = 204
})

サーバーが定義されたので、サーバールートで呼び出したヘルパー関数を構築する準備ができました。

サービスとデータの作成

もう一度ファイルの先頭に移動し、関数とデータのインポート文をいくつか追加しよう。 server.tsファイルの一番上に移動して、定義する関数やデータのimport文をいくつか追加しよう:

import { languageList } from "./data/languages.ts";
import { voicesList } from "./data/voices.ts";
import { translateText } from "./services/translate.ts";
import { voicePicker } from "./services/voice_picker.ts";
import { languagePicker } from "./services/language_picker.ts";

上のスニペットが示すように、以下の5つの項目を作成する必要がある:

  • languageList:メッセージを翻訳する言語の配列。

  • voicesList:翻訳されたメッセージを話す声の配列

  • translateText:テキストを第2言語に翻訳する機能

  • voicePicker:翻訳されたテキストを話す音声を選択する機能。

  • languagePicker:テキストを翻訳する言語を選択する機能

それぞれをこれから作っていく。

データの定義

まず、アプリケーションにデータを追加してみよう。

言語のリストと、その言語を話す声のリストだ。

サポートされている言語のリストは Vonage ASRガイド.音声名のリストも同様に 音声APIガイド.

ファイルを開き data/languages.tsファイルを開き、そこにオブジェクトの配列を追加する:

export const languageList = [
  { "name": "Afrikaans (South Africa)", "code": "af-ZA" },
  { "name": "Albanian (Albania)", "code": "sq-AL" },
  { "name": "Amharic (Ethiopia)", "code": "am-ET" },
  { "name": "Arabic (Algeria)", "code": "ar-DZ" },
  { "name": "Arabic (Bahrain)", "code": "ar-BH" },
  { "name": "Arabic (Egypt)", "code": "ar-EG" },
  { "name": "Arabic (Iraq)", "code": "ar-IQ" },
  { "name": "Arabic (Israel)", "code": "ar-IL" },
  { "name": "Arabic (Jordan)", "code": "ar-JO" },
  { "name": "Arabic (Kuwait)", "code": "ar-KW" },
  { "name": "Arabic (Lebanon)", "code": "ar-LB" },
  { "name": "Arabic (Morocco)", "code": "ar-MA" },
  { "name": "Arabic (Oman)", "code": "ar-OM" },
  { "name": "Arabic (Qatar)", "code": "ar-QA" },
  { "name": "Arabic (Saudi Arabia)", "code": "ar-SA" },
  { "name": "Arabic (State of Palestine)", "code": "ar-PS" },
  { "name": "Arabic (Tunisia)", "code": "ar-TN" },
  { "name": "Arabic (United Arab Emirates)", "code": "ar-AE" },
  { "name": "Armenian (Armenia)", "code": "hy-AM" },
  { "name": "Azerbaijani (Azerbaijan)", "code": "az-AZ" },
  { "name": "Basque (Spain)", "code": "eu-ES" },
  { "name": "Bengali (Bangladesh)", "code": "bn-BD" },
  { "name": "Bengali (India)", "code": "bn-IN" },
  { "name": "Bulgarian (Bulgaria)", "code": "bg-BG" },
  { "name": "Catalan (Spain)", "code": "ca-ES" },
  { "name": "Chinese, Mandarin (Simplified, China)", "code": "zh" },
  { "name": "Croatian (Croatia)", "code": "hr-HR" },
  { "name": "Czech (Czech Republic)", "code": "cs-CZ" },
  { "name": "Danish (Denmark)", "code": "da-DK" },
  { "name": "Dutch (Netherlands)", "code": "nl-NL" },
  { "name": "English (Australia)", "code": "en-AU" },
  { "name": "English (Canada)", "code": "en-CA" },
  { "name": "English (Ghana)", "code": "en-GH" },
  { "name": "English (India)", "code": "en-IN" },
  { "name": "English (Ireland)", "code": "en-IE" },
  { "name": "English (Kenya)", "code": "en-KE" },
  { "name": "English (New Zealand)", "code": "en-NZ" },
  { "name": "English (Nigeria)", "code": "en-NG" },
  { "name": "English (Philippines)", "code": "en-PH" },
  { "name": "English (South Africa)", "code": "en-ZA" },
  { "name": "English (Tanzania)", "code": "en-TZ" },
  { "name": "English (United Kingdom)", "code": "en-GB" },
  { "name": "English (United States)", "code": "en-US" },
  { "name": "Finnish (Finland)", "code": "fi-FI" },
  { "name": "French (Canada)", "code": "fr-CA" },
  { "name": "French (France)", "code": "fr-FR" },
  { "name": "Galician (Spain)", "code": "gl-ES" },
  { "name": "Georgian (Georgia)", "code": "ka-GE" },
  { "name": "German (Germany)", "code": "de-DE" },
  { "name": "Greek (Greece)", "code": "el-GR" },
  { "name": "Gujarati (India)", "code": "gu-IN" },
  { "name": "Hebrew (Israel)", "code": "he-IL" },
  { "name": "Hindi (India)", "code": "hi-IN" },
  { "name": "Hungarian (Hungary)", "code": "hu-HU" },
  { "name": "Icelandic (Iceland)", "code": "is-IS" },
  { "name": "Indonesian (Indonesia)", "code": "id-ID" },
  { "name": "Italian (Italy)", "code": "it-IT" },
  { "name": "Japanese (Japan)", "code": "ja-JP" },
  { "name": "Javanese (Indonesia)", "code": "jv-ID" },
  { "name": "Kannada (India)", "code": "kn-IN" },
  { "name": "Khmer (Cambodia)", "code": "km-KH" },
  { "name": "Korean (South Korea)", "code": "ko-KR" },
  { "name": "Lao (Laos)", "code": "lo-LA" },
  { "name": "Latvian (Latvia)", "code": "lv-LV" },
  { "name": "Lithuanian (Lithuania)", "code": "lt-LT" },
  { "name": "Malay (Malaysia)", "code":  "ms-MY" },
  { "name": "Malayalam (India)", "code": "ml-IN" }, 
  { "name": "Marathi (India)", "code": "mr-IN" },
  { "name": "Nepali (Nepal)", "code":  "ne-NP"},
  { "name": "Norwegian Bokmål (Norway)",  "code": "nb-NO"},
  { "name": "Persian (Iran)", "code":  "fa-IR"},
  { "name": "Polish (Poland)", "code":  "pl-PL"},
  { "name": "Portuguese (Brazil)", "code": "pt-BR"},
  { "name": "Portuguese (Portugal)", "code": "pt-PT"},
  { "name": "Romanian (Romania)", "code": "ro-RO"} ,
  { "name": "Russian (Russia)", "code": "ru-RU" },
  { "name": "Serbian (Serbia)", "code": "sr-RS" },
  { "name": "Sinhala (Sri Lanka)", "code": "si-LK" },
  { "name": "Slovak (Slovakia)", "code": "sk-SK" },
  { "name": "Slovenian (Slovenia)", "code": "sl-SI" },
  { "name": "Spanish (Argentina)", "code": "es-AR" },
  { "name": "Spanish (Bolivia)", "code": "es-BO" },
  { "name": "Spanish (Chile)", "code": "es-CL" },
  { "name": "Spanish (Colombia)", "code": "es-CO" },
  { "name": "Spanish (Costa Rica)", "code":  "es-CR" },
  { "name": "Spanish (Dominican Republic)", "code": "es-DO" },
  { "name": "Spanish (Ecuador)", "code": "es-EC" },
  { "name": "Spanish (El Salvador)", "code": "es-SV" },
  { "name": "Spanish (Guatemala)", "code": "es-GT" },
  { "name": "Spanish (Honduras)", "code": "es-HN" },
  { "name": "Spanish (Mexico)", "code": "es-MX" },
  { "name": "Spanish (Nicaragua)", "code": "es-NI" },
  { "name": "Spanish (Panama)", "code": "es-PA" },
  { "name": "Spanish (Paraguay)", "code": "es-PY" },
  { "name": "Spanish (Peru)", "code": "es-PE" },
  { "name": "Spanish (Puerto Rico)", "code": "es-PR" },
  { "name": "Spanish (Spain)", "code": "es-ES" },
  { "name": "Spanish (United States)", "code": "es-US" },
  { "name": "Spanish (Uruguay)", "code": "es-UY" },
  { "name": "Spanish (Venezuela)", "code": "es-VE" },
  { "name": "Sundanese (Indonesia)", "code": "su-ID" },
  { "name": "Swahili (Kenya)", "code": "sw-KE" },
  { "name": "Swahili (Tanzania)", "code": "sw-TZ" },
  { "name": "Swedish (Sweden)", "code": "sv-SE" },
  { "name": "Tamil (India)", "code": "ta-IN" },
  { "name": "Tamil (Malaysia)", "code": "ta-MY" },
  { "name": "Tamil (Singapore)", "code": "ta-SG" },
  { "name": "Tamil (Sri Lanka)", "code": "ta-LK" },
  { "name": "Telugu (India)", "code": "te-IN" },
  { "name": "Thai (Thailand)", "code": "th-TH" },
  { "name": "Turkish (Turkey)", "code": "tr-TR" },
  { "name": "Ukrainian (Ukraine)", "code": "uk-UA" },
  { "name": "Urdu (India)", "code": "ur-IN" },
  { "name": "Urdu (Pakistan)", "code": "ur-PK" },
  { "name": "Vietnamese (Vietnam)", "code": "vi-VN" },
  { "name": "Zulu (South Africa)", "code": "zu-ZA" }
]

これは、このチュートリアルが発行された時点でサポートされている言語のリストです。このリストは変更されることがありますので、最新の情報についてはウェブサイトのガイドを参照してください。

次に data/voices.tsファイルを開き、このファイルにもオブジェクトの配列を追加する。言語リストと同様、ここでのデータは、発表時点でのVoice名のリストを表しています。1つの言語につき複数のVoiceが存在することがよくあります。このチュートリアルでは、重複する言語の Voice を削除し、1 言語につき 1 つの Voice に収まるようにしています:

export const voicesList = [
  { "name": "Salli", "code": "en-US" },
  { "name": "Marlene", "code": "de-DE" },
  { "name": "Nicole", "code": "en-AU" },
  { "name": "Gwyneth", "code": "en-GB" },
  { "name": "Geraint", "code": "cy-GB" },
  { "name": "Raveena", "code": "en-IN" },
  { "name": "Conchita", "code": "es-ES" },
  { "name": "Penelope", "code": "es-US" },
  { "name": "Chantal", "code": "fr-CA" },
  { "name": "Mathieu", "code": "fr-FR" },
  { "name": "Aditi", "code": "hi-IN" },
  { "name": "Dora", "code": "is-IS" },
  { "name": "Carla", "code": "it-IT" },
  { "name": "Liv", "code": "nb-NO" },
  { "name": "Lotte", "code": "nl-NL" },
  { "name": "Jacek", "code": "pl-PL" },
  { "name": "Vitoria", "code": "pt-BR" },
  { "name": "Ines", "code": "pt-PT" },
  { "name": "Carmen", "code": "ro-RO" },
  { "name": "Tatyana", "code": "ru-RU" },
  { "name": "Astrid", "code": "sv-SE" },
  { "name": "Filiz", "code": "tr-TR" },
  { "name": "Mizuki", "code": "ja-JP" },
  { "name": "Seoyeon", "code": "ko-KR" },
  { "name": "Laila", "code": "ara-XWW" },
  { "name": "Damayanti", "code": "ind-IDN" },
  { "name": "Miren", "code": "baq-ESP" },
  { "name": "Sin-Ji", "code": "yue-CHN" },
  { "name": "Jordi", "code": "cat-ESP" },
  { "name": "Montserrat", "code": "cat-ESP" },
  { "name": "Iveta", "code": "ces-CZE" },
  { "name": "Tessa", "code": "eng-ZAF" },
  { "name": "Satu", "code": "fin-FIN" },
  { "name": "Melina", "code": "ell-GRC" },
  { "name": "Carmit", "code": "heb-ISR" },
  { "name": "Lekha", "code": "hin-IND" },
  { "name": "Mariska", "code": "hun-HUN" },
  { "name": "Sora", "code": "kor-KOR" },
  { "name": "Tian-Tian", "code": "cmn-CHN" },
  { "name": "Mei-Jia", "code": "cmn-TWN" },
  { "name": "Nora", "code": "nor-NOR" },
  { "name": "Henrik", "code": "nor-NOR" },
  { "name": "Felipe", "code": "por-BRA" },
  { "name": "Joana", "code": "por-PRT" },
  { "name": "Ioana", "code": "ron-ROU" },
  { "name": "Laura", "code": "slk-SVK" },
  { "name": "Alva", "code": "swe-SWE" },
  { "name": "Kanya", "code": "tha-THA" },
  { "name": "Yelda", "code": "tur-TUR" },
  { "name": "Empar", "code": "spa-ESP" }
]

サービス機能の定義

私たちのアプリケーションは services/で定義されたいくつかの関数を使います。この時点で、それぞれの関数を作り上げることにする。

Microsoft Azure Speech Translation APIを利用するには、2段階のプロセスを使用してAPIを認証する必要があります。最初のステップは、トークン作成APIエンドポイントからJSON Web Token (JWT)を取得することで、次に翻訳APIエンドポイントにHTTPコールを行う際に2番目のステップで使用します。

ファイルを開き services/auth/token.tsファイルを開き、その中でAzureからJWTを取得する機能を作成します。これは、Microsoft Azureのアカウント作成に成功し、APIキーを受け取ったかどうかに依存することに注意してください。この関数は、APIキーをファイル内の環境変数から読み込みます。 .envファイル内の環境変数からAPIキーを読み取ります:

import "https://deno.land/x/dotenv/load.ts";
const azureEndpoint: any = Deno.env.get("AZURE_ENDPOINT");
var data;
 export const getToken = async (key: string | undefined) => {
  if (!key) {
    console.log("You are missing your Azure Subscription Key. You must add it as an environment variable.");
    return;
  };
  if (!azureEndpoint) {
    console.log("You are missing your Azure endpoint definition. You must add it as an environment variable.");
  };
  data = await fetch(`${azureEndpoint.toString()}sts/v1.0/issuetoken`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
      'Content-length': '0',
      'Ocp-Apim-Subscription-Key':key.toString()
    }
  })
  var text = await data.text();
  return text; 
};

この getToken()関数は keyパラメータを受け取り、dotenvファイルで定義されたMicrosoft Azure URLエンドポイントとともに、APIキーを送信するリクエストを作成する。 fetch()リクエストを行います。返ってくる値は、関数からの値として明示的に返されるJWTです。ファイルの最上部で、Denoからdotenvローダーモジュールをインポートしています。 .envファイルの値を読み込むことができます。

もし keyundefinedである場合、あるいは azureEndpointに値がない場合、この関数は早めに戻り、何が足りなかったのかをコンソールで説明します。

からトークンを取得したら、それを使って getToken()を取得したら、それを使って翻訳APIを呼び出して翻訳されたテキストを返すヘルパー関数を作る準備ができた。

ファイルを開き services/translate.tsファイルを開き、その中に translateText()関数を作成します:

import { getToken } from './auth/token.ts';
import "https://deno.land/x/dotenv/load.ts";
const azureSubscriptionKey: string | undefined = Deno.env.get("AZURE_SUBSCRIPTION_KEY");
 export const translateText = async (languageCode: string, text: string) => {
  const token =  await getToken(azureSubscriptionKey);
  const response = await fetch(`https://api.cognitive.microsofttranslator.com/translate?api-version=3.0&from=en&to=${languageCode}`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${token}`
    },
    body: JSON.stringify([{"text": text}])
  });
  var translation = await response.json();
  return translation[0][<any>"translations"][0][<any>"text"]
};

この関数は、その前の関数と同様に、定義した Azure API キーを取得するために .envファイルから読み込んで、定義したAzure APIキーを取得します。この関数は2つの引数を取ります:2文字の言語コードと、変換された音声テキストです。

そして、この関数は2つの変数を作成する: tokenそして response.前者は getToken()関数を呼び出す。後者は fetch() POSTクエリパラメータの一部として2文字の言語コードを使用して、Azure音声翻訳APIエンドポイントへのリクエストを呼び出します。関数によって生成されたJWTは getToken()関数によって生成されたJWTは Authorizationヘッダーに渡される。ヘッダーの bodyリクエストの POSTリクエストは、JSON文字列に変換されたテキストに音声変換されます。

リクエストからのレスポンスは translation変数に保持され、実際の翻訳テキストは関数によって返されます。 translation[0]["translations][0]["text].

環境変数の定義に移る前に、残り2つの関数を作成しなければならない。 .env環境変数の定義に移る前に

残りの2つの関数のうち、最初に作る関数は、翻訳するテキストの言語を言語リストからランダムに選びます。

以下を開き services/language_picker.tsを開き、以下のコードを追加する:

export const languagePicker = (languages: any) => {
 const language = languages[Math.floor(Math.random() * languages.length)];
 return language;
}

この関数は、言語リストからランダムにインデックスを選び、そのインデックスに含まれるオブジェクトの値を返すために、ちょっとした数学を使う。

最後に作る関数は、翻訳された言語を話すVonage Voiceが存在すれば、それを選ぶ。存在しない場合は、アメリカ英語を表す Salli音声はアメリカ英語を表します。また、選択された言語がアラビア語の地域方言の一つである場合、選択されたVoiceはVonageアラビア語ボイスの一つであることを保証します。

を開き services/voice_picker.tsを開き、そこに以下を追加する:

var voiceChoice: any = { "name": "Salli", "code": "en-US" }
 export const voicePicker = (voices: Array<object>, language: any) => {
  voiceChoice = voices.find((voice: any) => voice.code === language.code)
  if (language.code.split('-')[0] === 'ar') {
    voiceChoice = { "name": "Laila", "code": "ara-XWW" }
  };
  if (voiceChoice === undefined) {
    voiceChoice = { "name": "Salli", "code": "en-US" }
  };
  return voiceChoice.name;
};

これですべての機能が終了した!ここまでくれば、ゴールまであと少し。

最後に必要なのは、環境変数に値を割り当てて .env環境変数に値を割り当てることと、Vonageのバーチャル電話番号をプロビジョニングすることだ。

環境変数の定義

ファイルで割り当てる必要のある値は3つある。 .envファイルで割り当てる必要がある値は3つある:

  • azure_subscription_key

  • AZURE_ENDPOINT

  • vonage_asr_webhook

最初の2つはそれぞれ、Azure APIキーとAzure URLエンドポイントだ。

後者は、Vonage自動音声認識機能によって返されるデータのWebhook URLです。この後者の値は、外部からアクセス可能なURLである必要があります。開発中に使用する良いツールは、ローカル環境を外部から利用可能にするngrokです。ngrokをローカルにセットアップする方法は 開発者向けウェブサイト.

Vonageバーチャル電話番号のプロビジョニング

Vonageバーチャル電話番号のプロビジョニングには2つの方法があります。一度 Vonage 開発者アカウントを取得したら、ダッシュボードから電話番号を購入するか、Vonage CLI を使うかのどちらかです。ここでは CLI を使って行います。

CLIをインストールするには、yarnかnpmを使う: yarn global add @vonage/cliまたは npm install @vonage/cli -g.インストール後、ダッシュボードから取得したAPI認証情報を提供する必要があります:

vonage config:set --apiKey=VONAGE_API_KEY --apiSecret=VONAGE_API_SECRET

CLIがセットアップされたので、それを使ってあなたの国で利用可能なNumbersを検索することができます。これを行うには、2文字の国コードを使用して以下を実行します。以下の例は、米国での番号検索を示しています。必ず --features=VOICEフラグを追加して、Voice対応のNumbersのみを返すようにしてください:

vonage numbers:search US --features=VOICE

欲しい番号を見つけたら、CLIで購入することもできる:

vonage numbers:buy NUMBER COUNTRYCODE

コマンドを送信した後に confirmを入力してください。

Voiceアプリを作成するので、Vonage Applicationも作成する必要がある。これもCLIで行うことができ、完了したら、最近プロビジョニングされた電話番号をアプリケーションにリンクさせることができる。アプリケーションの作成を利用して、アンサーウェブフックとイベントウェブフックのURLを提供することもできます。開発中に作成する場合は、今が ngrok サーバーを作成し、ngrok URL を提供する良い機会です:

vonage apps:create APP_NAME --voice_answer_url=https://www.example.com/answer --voice_event_url=https://www.example.com/event

コマンドはアプリケーションIDを返す: Application ID: asdasdas-asdd-2344-2344-asdasdasd345.このIDを使って、アプリケーションを電話番号にリンクさせます:

vonage apps:link APP_ID --number=YOUR_VONAGE_NUMBER

これらのコマンドが完了したら、アプリケーションを実行する準備が整いました!

アプリケーションの実行

アプリケーションを使用するには、ngrok サーバーと Deno ウェブサーバーの両方を起動します。Deno アプリケーションを起動するには、ルートフォルダから以下を実行します:

deno run --allow-read --allow-env --allow-net server.ts

これで、Vonageのプロビジョニングされた電話番号に電話をかけ、プロンプトに従ってメッセージを言うことができます。あなたのメッセージは、Vonageの自動音声認識機能を使用してテキストに変換され、Microsoft Azureを使用してランダムな第二言語に翻訳された後、あなたに言い返されます。お楽しみください!

シェア:

https://a.storyblok.com/f/270183/384x384/e5480d2945/ben-greenberg.png
Ben Greenbergヴォネージの卒業生

ベンはセカンドキャリアの開発者で、以前は成人教育、コミュニティ組織化、非営利団体運営の分野で10年を過ごした。彼はVonageの開発者支援者として働いていた。コミュニティ開発とテクノロジーの交差点について定期的に執筆している。南カリフォルニア出身で、長年ニューヨークに住んでいたが、現在はイスラエルのテルアビブ近郊に在住。