Azure AI 服务之文本翻译

时间:2022-11-30 19:24:35

当下人工智能可谓是风头正劲,几乎所有的大厂都有相关的技术栈。微软在 AI 领域自然也是投入了重注,并且以 Azure 认知服务的方式投入了市场:

Azure AI 服务之文本翻译

也就是说作为开发者我们不需要学习太多 AI 的理论知识,直接使用 Azure 提供的认知服务 API 就可以在程序中实现 AI 的功能了!

本文作为介绍 Azure AI 服务系列的第一篇,将通过 demo 介绍 Azure 认识服务中 Language 分类中的文本翻译服务(Translator Text API Azure AI 服务之文本翻译)。

Microsoft 文本翻译 API 是一种基于云的机器翻译服务, 支持多种语言。使用者可用于构建应用程序、网站、工具或任何需要多语言支持的解决方案。该服务是通过 REST API 提供的,所以我们可以以任何语言来调用它们。本文笔者使用 C# 通过构建一个 WPF 程序来演示如何通过简单的几步就能创建一个像模像样的翻译程序:

Azure AI 服务之文本翻译

本文的完整 demo 请从这里下载

创建 Azure 服务

要使用 Azure 的翻译服务需要先在 Azure 上创建对应的实例,比如我们需要先创建一个 "Translator Text API" 服务实例:

Azure AI 服务之文本翻译

在本文的 demo 程序中我们还会用到拼写检查的服务,所以还需要创建一个 "Bing Spell Check v7 API" 服务的实例:

Azure AI 服务之文本翻译

说明:对于学习和练习来说,你可以创建免费的 Azure 账号并创建免费版的上述实例,详细信息请参考 Azure 官网。

创建 WPF 应用程序

先在 VS 中创建 WPF 程序并简单的布局。

既然是 REST API,那么我们肯定是以 url 的方式访问服务,下面分别是访问文本翻译服务和拼写检查服务的 url:

const string TEXT_TRANSLATION_API_ENDPOINT = "https://api.microsofttranslator.com/v2/Http.svc/";
const string BING_SPELL_CHECK_API_ENDPOINT = "https://api.cognitive.microsoft.com/bing/v7.0/spellcheck/";

在访问相应的服务时,我们用这两个常量再拼接上合适的参数就可以了。

需要注意的是,Azure 提供的认知服务 API 都是需要认证信息的。具体的方式就是把我们创建的服务的 key 随 API 发送的服务器端进行认证,比如把 key 添加到 http 请求的 header 中:

WebRequest.Headers.Add("Ocp-Apim-Subscription-Key", "your key");

你可以在创建的服务实例的详情界面获得对应的 key,我们在程序中通过定义的常量来保存它们:

const string TEXT_TRANSLATION_API_SUBSCRIPTION_KEY = "your translator key";
const string BING_SPELL_CHECK_API_SUBSCRIPTION_KEY = "your spell check key";

由于 demo 的代码比较长,为了能集中精力介绍 Azure AI 相关的内容,本文中只贴出相关的代码。完整的 demo 代码在这里

获取支持的语言列表

在进行任何的文本翻译之前,我们需要搞清楚 Azure 提供的翻译服务究竟支持哪些语言!下面的请求能够返回翻译服务支持的语言列表:

string uri = TEXT_TRANSLATION_API_ENDPOINT + "GetLanguagesForTranslate?scope=text";

我们把代码封装到下面的函数中:

private string[] languageCodes;
private void GetLanguagesForTranslate()
{
// 获得翻译服务支持的语言
string uri = TEXT_TRANSLATION_API_ENDPOINT + "GetLanguagesForTranslate?scope=text";
WebRequest WebRequest = WebRequest.Create(uri);
// 在 http 请求中添加认证信息
WebRequest.Headers.Add("Ocp-Apim-Subscription-Key", TEXT_TRANSLATION_API_SUBSCRIPTION_KEY);
WebResponse response = null; // 把返回的 xml 信息抽取到数组中
response = WebRequest.GetResponse();
using (Stream stream = response.GetResponseStream())
{
DataContractSerializer dcs = new DataContractSerializer(typeof(List<string>));
List<string> languagesForTranslate = (List<string>)dcs.ReadObject(stream);
languageCodes = languagesForTranslate.ToArray();
}
}

执行这个函数后,languageCodes 中的内容如下图所示:

Azure AI 服务之文本翻译

虽然取到了可以翻译的语言列表,但是像图中的内容是无法显示给用户的,还需要把它们转换成对用户友好的名称,因此我们定义 GetLanguageNames 函数完成这个功能:

private SortedDictionary<string, string> languageCodesAndTitles =
new SortedDictionary<string, string>(Comparer<string>.Create((a, b) => string.Compare(a, b, true)));
private void GetLanguageNames()
{
// 获得简体中文的语言名称
string uri = TEXT_TRANSLATION_API_ENDPOINT + "GetLanguageNames?locale=zh-CHS";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.Headers.Add("Ocp-Apim-Subscription-Key", TEXT_TRANSLATION_API_SUBSCRIPTION_KEY);
request.ContentType = "text/xml";
request.Method = "POST";
DataContractSerializer dcs = new DataContractSerializer(Type.GetType("System.String[]"));
using (Stream stream = request.GetRequestStream())
{
dcs.WriteObject(stream, languageCodes);
} // 把返回的 xml 信息抽取到数组中
var response = request.GetResponse();
string[] languageNames;
using (Stream stream = response.GetResponseStream())
{
languageNames = (string[])dcs.ReadObject(stream);
} // 把支持的语言列表及其友好名称保存到字典数据结构中,
// 随后会把它们绑定给 combo box 控件进行显示
for (int i = ; i < languageNames.Length; i++)
{
languageCodesAndTitles.Add(languageNames[i], languageCodes[i]);
}
}

这次我们拿到了用中文显示的语言名称:

Azure AI 服务之文本翻译

初始化源和目标语言列表

当获得了支持翻译的语言列表后,就可以通过 UI 控件把它们显示出来:

private void PopulateLanguageMenus()
{
int count = languageCodesAndTitles.Count;
foreach (string menuItem in languageCodesAndTitles.Keys)
{
FromLanguageComboBox.Items.Add(menuItem);
ToLanguageComboBox.Items.Add(menuItem);
} // 设置默认的源语言和目标语言
FromLanguageComboBox.SelectedItem = "英语";
ToLanguageComboBox.SelectedItem = "简体中文";
}

在我们的使用场景中,把默认的翻译文本设置为 "英语",翻译的目标语言为 "简体中文":

Azure AI 服务之文本翻译

翻译文本

接下来介绍文本翻译的 API,其核心是下面的 url 请求:

TEXT_TRANSLATION_API_ENDPOINT + "Translate?text=" + 待翻译文本 + "&from=" + 源语言 + "&to=" + 目标语言

同样,我们把它封装成一个具有完整功能的函数:

private void TranslateButton_Click(object sender, EventArgs e)
{
string textToTranslate = TextToTranslate.Text.Trim();
string fromLanguage = FromLanguageComboBox.SelectedValue.ToString();
string fromLanguageCode = languageCodesAndTitles[fromLanguage];
string toLanguageCode = languageCodesAndTitles[ToLanguageComboBox.SelectedValue.ToString()]; // 如果要翻译的文本是英语,还可以进行拼写检查
if (fromLanguageCode == "en")
{
textToTranslate = CorrectSpelling(textToTranslate);
// 把更新后的文本保存到 UI 控件上
TextToTranslate.Text = textToTranslate;
} // 处理文本为空和不需要翻译的情况
if (textToTranslate == "" || fromLanguageCode == toLanguageCode)
{
TranslatedText.Text = textToTranslate;
return;
} // 通过 http 请求执行翻译任务
string uri = string.Format(TEXT_TRANSLATION_API_ENDPOINT + "Translate?text=" +
System.Web.HttpUtility.UrlEncode(textToTranslate) + "&from={0}&to={1}", fromLanguageCode, toLanguageCode);
var translationWebRequest = HttpWebRequest.Create(uri);
translationWebRequest.Headers.Add("Ocp-Apim-Subscription-Key", TEXT_TRANSLATION_API_SUBSCRIPTION_KEY);
WebResponse response = null;
response = translationWebRequest.GetResponse(); // 把返回的翻译结果抽取到 UI 控件中
Stream stream = response.GetResponseStream();
StreamReader translatedStream = new StreamReader(stream, Encoding.GetEncoding("utf-8"));
System.Xml.XmlDocument xmlResponse = new System.Xml.XmlDocument();
xmlResponse.LoadXml(translatedStream.ReadToEnd());
TranslatedText.Text = xmlResponse.InnerText;
}

在调用翻译文本的 API 前,需要先从 UI 控件中取得用户设置的源语言和目标语言,并且还要对放在 url 中传输的文本内容进行编码:

string uri = string.Format(TEXT_TRANSLATION_API_ENDPOINT + "Translate?text=" +
System.Web.HttpUtility.UrlEncode(textToTranslate) + "&from={0}&to={1}", fromLanguageCode, toLanguageCode);

拼写检查

对于英语,我们可以通过 Bing Spell Check 服务进行翻译前的拼写检查。比如 TranslateButton_Click 函数中的:

// 如果要翻译的文本是英语,还可以进行拼写检查
if (fromLanguageCode == "en")
{
textToTranslate = CorrectSpelling(textToTranslate);
// 把更新后的文本保存到 UI 控件上
TextToTranslate.Text = textToTranslate;
}

主要的拼写检查逻辑被封装在了 CorrectSpelling 函数中:

private string CorrectSpelling(string text)
{
string uri = BING_SPELL_CHECK_API_ENDPOINT + "?mode=spell&mkt=en-US";
// 创建拼写检查的请求
HttpWebRequest spellCheckWebRequest = (HttpWebRequest)WebRequest.Create(uri);
spellCheckWebRequest.Headers.Add("Ocp-Apim-Subscription-Key", BING_SPELL_CHECK_API_SUBSCRIPTION_KEY);
spellCheckWebRequest.Method = "POST";
spellCheckWebRequest.ContentType = "application/x-www-form-urlencoded"; // 这个设置是必须的! // 把文本内容放在请求的 body 中
string body = "text=" + System.Web.HttpUtility.UrlEncode(text);
byte[] data = Encoding.UTF8.GetBytes(body);
spellCheckWebRequest.ContentLength = data.Length;
using (var requestStream = spellCheckWebRequest.GetRequestStream())
requestStream.Write(data, , data.Length);
HttpWebResponse response = (HttpWebResponse)spellCheckWebRequest.GetResponse(); // 从返回中取出 json 格式的拼写检查结果
var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
var responseStream = response.GetResponseStream();
var jsonString = new StreamReader(responseStream, Encoding.GetEncoding("utf-8")).ReadToEnd();
dynamic jsonResponse = serializer.DeserializeObject(jsonString);
var flaggedTokens = jsonResponse["flaggedTokens"]; // 我们定义一个规则来应用拼写检查的结果,
// 比如:当拼写检查的权值大于 0.7 时就用建议的值替换掉文本中的值。
var corrections = new SortedDictionary<int, string[]>(Comparer<int>.Create((a, b) => b.CompareTo(a)));
for (int i = ; i < flaggedTokens.Length; i++)
{
var correction = flaggedTokens[i];
var suggestion = correction["suggestions"][];
if (suggestion["score"] > (decimal)0.7)
corrections[(int)correction["offset"]] = new string[]
{ correction["token"], suggestion["suggestion"] };
} foreach (int i in corrections.Keys)
{
var oldtext = corrections[i][];
var newtext = corrections[i][];
if (text.Substring(i, oldtext.Length).All(char.IsUpper)) newtext = newtext.ToUpper();
else if (char.IsUpper(text[i])) newtext = newtext[].ToString().ToUpper() + newtext.Substring();
text = text.Substring(, i) + newtext + text.Substring(i + oldtext.Length);
}
return text;
}

从上面的代码可以看出,拼写检查只是给出一些建议,具体怎么做还是由用户决定的。比如上面的代码中当拼写检查的权值大于 0.7 时就用建议的值替换掉文本中的值。下面我们来测试一下拼写检查的逻辑,运行程序,并输入 "helo world!" 进行翻译:

Azure AI 服务之文本翻译

执行翻译操作,代码逻辑在检测到待翻译的语言为英语时,会先进行代码的拼写检查:

Azure AI 服务之文本翻译

上图显示拼写检查函数 CorrectSpelling 纠正了我们的拼写错误,下面是翻译的结果:

Azure AI 服务之文本翻译

有了代码的拼写检查,是不是感觉这个程序有点 "智能" 的味道啦!

总结

就像 azure 提供的其它服务一样,入门和上手非常的容易。我们简单的搞了几下就能够运行一个简单的文本翻译程序了。
当然这只是一个开始,希望大家和笔者一道通过本文开启 Azure AI 的一段旅程。

参考:
Microsoft Translator WPF application in C#