使用 HTTPModules 和机器翻译 (MT) 自动翻译站点






4.60/5 (9投票s)
使用 HTTP 模块和机器翻译实现网站自动翻译。
引言
我们大约有 20 个网站。管理层正在向海外扩张,并希望所有网站都能多语言化,最好是昨天就完成。而且,棘手的是,有些内容是由用户生成的。面对永无止境的资源文件标记的未来,我需要找到一个更好的解决方案。作为一名程序员,我想创造一些可以解决我所有问题的东西。我需要一种可以用于任何网站、并且不需要太多修改源代码的方案。它还需要能够自行翻译页面,但同时又需要学习用户更新内容时产生的生词。
老师
这些网站碰巧位于防火墙后面,因此使用 Google 或 BabelFish 的翻译服务实际上是行不通的。但是,使用机器翻译听起来是个好主意(总比处理所有那些资源文件要好)。机器翻译的效果相当不错,但并非一门精确的科学,因此我们需要一种方法来修改它的翻译结果。我找到一家名为 SysTran 的公司,他们提供 API。这样,我就有了一个“老师”,可以用来学习新语言的页面。大多数机器翻译程序都有一个函数,你可以传递一个 URL 给它,它就会一次性翻译整个页面。问题是它太慢了,而且经常会搞乱你的 JavaScript 或页面上的其他东西。这种方法对我来说太笨重了。我需要一种方法来精确地修改内容:标签、组合框数据、列表数据等等……
通用化
我决定不想为我们使用的每个控件编写新的控件或包装器。我不想修改网站上的代码。我们在 ASP.NET 中做的所有这些最终都会在某个时候被转换成纯粹的 HTML,而 HTML 中需要处理的控件要少得多。所以,如果我在 ASP.NET 生成 HTML 后,在它发送给客户端之前进行翻译,我就可以在不搞乱 JavaScript 的情况下做到这一点,只翻译我想要翻译的内容。HTTP 模块非常适合这个任务。我不仅可以翻译原始 HTML,还可以在任何网站的前端添加它,而不会破坏网站的内部运作。
就喜欢原始的
实际上,获取原始输出有点棘手。唯一能获取它的地方是创建一个 Filter
对象并将其分配给 Response
对象,如下所示……
Dim f = _Context.Response.Filter
Dim sr As TranslateFilterToDom = New TranslateFilterToDom(f)
sr.Language = GetUserLanguage()
_Context.Response.Filter = sr
你创建一个继承自 Stream
的对象。大部分内容你都可以保持不变,只需填充必要的部分。不变的是 Write
方法。这是一个流,所以 HTML 是分块填充的。我们寻找的是结束 HTML 标签……
Public Overrides Sub Write(ByVal Buffer() As Byte, _
ByVal offset As Integer, ByVal count As Integer)
Dim sBuffer As String = System.Text.UTF8Encoding.UTF8.GetString(Buffer, offset, count)
Dim rHTML As Regex = New Regex("</html>", RegexOptions.IgnoreCase)
If (Not rHTML.IsMatch(sBuffer)) Then
responseHtml.Append(sBuffer)
Else
responseHtml.Append(sBuffer)
Dim finalHtml As String = responseHtml.ToString()
' Transform the response and write it back out
If Language <> "en" Then ' Don't do anything if its English
xml = New XmlDocument 'Create a place to put the XHTML
Dim xmldecl As XmlDeclaration
xmldecl = xml.CreateXmlDeclaration("1.0", "UTF-8", Nothing)
finalHtml = finalHtml.Substring(finalHtml.ToLower.IndexOf("<html"))
'Help out the HTML a bit to make it XHTML
finalHtml = finalHtml.Replace(" ", " ")
finalHtml = finalHtml.Replace("<br>", "<br/>")
finalHtml = finalHtml.Replace("<hr>", "<hr/>")
finalHtml = finalHtml.Replace("&", "&")
finalHtml = finalHtml.Replace("<head>", "<head>" & META)
'Add a META tag to make sure its UTF-8
xml.LoadXml(finalHtml)
xml.InsertBefore(xmldecl, xml.DocumentElement)
finalHtml = Translate(xml) 'Start the Translation
finalHtml = finalHtml.Replace("&", "&") 'Put the & back in
End If
Dim data As Byte() = System.Text.UTF8Encoding.UTF8.GetBytes(finalHtml)
_sink.Write(data, 0, data.Length)
End If
End Sub
这里的想法是在 HTML 流向客户端的过程中解析 HTML 标签。这样,我们就无需担心服务器控件,也无需更改应用程序中的代码。问题是如何进行解析。如果 HTML 符合 XHTML 标准,我们可以直接将其加载到 XMLDocument
中并进行解析。然后,我们将其传递给 Translate
方法……
Private Function Translate(ByVal poxml As XmlDocument) As String
Dim loNode As XmlNode = DirectCast(poxml.DocumentElement, XmlNode)
TranslateNode(poxml.DocumentElement)
If _IsManaul Then
'The manual switch will put the script on the page to allow for user translation.
Dim xmlNode As XmlNode = poxml.GetElementsByTagName("head")(0)
Dim xmlScript As XmlElement = poxml.CreateElement("Script")
xmlScript.InnerXml() = GetManualScript()
xmlNode.AppendChild(xmlScript)
End If
Return poxml.OuterXml
End Function
所以,我们在这里启动递归来解析 XMLDocument
。此外,我们将处理 MT 程序无法翻译用户所选语言的情况,这正是 IsManual
(我知道代码中拼写错了)的用途。
所以,这是外科手术式处理的部分。我们需要获取用户可以看到的文本。我们关心 HTML 标签之间的文本。我们关心 value
属性中的文本。我们不关心文本框或文本区域,因为用户将在其中输入内容。我们不关心 script
或 style
标签中的内容。
Private Sub TranslateNode(ByVal poNode As XmlNode)
'Find where the text we want to translate is
Dim lbTraverse As Boolean = True
Select Case poNode.Name.ToLower
Case "#text" 'catches text between tags
poNode.Value = TranslateText(poNode.Value, Nothing)
Case "input"
Dim lsType As String
If poNode.Attributes.ItemOf("type") Is Nothing Then
lsType = "text"
Else
lsType = poNode.Attributes.ItemOf("type").Value.ToLower
End If
If Not poNode.Attributes.ItemOf("value") Is Nothing Then
Select Case lsType
Case "button", "submit"
poNode.Attributes.ItemOf("value").Value = _
TranslateText(poNode.Attributes.ItemOf("value").Value, poNode
End Select
End If
Case "script", "style", "textarea" 'don't care about anything in here.
lbTraverse = False
End Select
If lbTraverse Then 'Go on down the the recursion
For Each loNode As XmlNode In poNode.ChildNodes
TranslateNode(loNode)
Next
End If
End Sub
那么,既然我们已经获得了需要翻译的文本,我们如何翻译它呢?使用 MT 的缺点是它速度慢,至少比从数据库中查找单词要慢。我们的做法是,一旦我们翻译了一个单词或短语,我们就会将其保存起来,供我们的网站或任何使用此 HTTP 模块的网站使用。
Private Function TranslateText(ByVal psString As String, _
ByVal pnode As XmlNode) As String
Dim result As String = String.Empty
'clean the text up so we can translate it
psString = psString.Replace(vbCr, "")
psString = psString.Replace(vbCrLf, "")
psString = psString.Replace(vbLf, "")
psString = psString.Replace(vbTab, "")
psString = psString.Trim
result = Lookup(psString) 'Try looking it up out of the database
If result.Trim = String.Empty Then 'If its not there then translate it
Dim loTranslator As ITranslator = New Systran 'Set up your own translator interface
'Make sure you have a translator and that translator will translate the users language
If Not loTranslator Is Nothing AndAlso _
Array.Exists(loTranslator.AvailableLanguages, AddressOf HasLanguage) Then
result = loTranslator.Translate(psString, "en", Language) 'Get the word
SaveWord(psString, result) 'And save it off so you don't ever have
'to go get it again
Else
'If you don't support the language or don't have a translator,
'set the Manual switch so users can translate it themselves
_IsManaul = True
result = psString
End If
End If
Return result
End Function
魔术就发生在这里。我设置了它,以便你只需实现 ITranslator
即可添加自己的翻译器。我们正在考虑使用 Systran,它有一个 Web 服务 API。
Public Function Translate(ByVal psString As String, _
ByVal psFromLanguage As String, ByVal psToLanguage As String) _
As String Implements ITranslator.Translate
Dim lsURL As String = My.Settings.TransaltionURL.Trim.Replace("@Language", psToLanguage)
Dim loWebclient As WebClient = New WebClient
loWebclient.Encoding = Encoding.UTF8
'Make sure you tell the web client to be unicode compliant
Dim result As String = loWebclient.UploadString(lsURL, psString).Trim
Return result.Replace("body=", "") 'Got to get rid of this
End Function
那么,如果……
如果你没有 MT 服务器,或者用户使用的语言 MT 无法翻译怎么办?你需要一种方法让用户为你翻译页面。这是网站翻译的 Wikipedia 方法。通过创建一个列表中常用短语和网页控件中使用的元素,你可以通过使用在线翻译器之一来翻译所有内容,或者让用户填写翻译。这足以处理静态内容。然后,还有用户生成的内容。我们需要一种方法让用户选择以英语显示的内容并为我们翻译它。所以,如果手动翻译开关打开,我们就会在 HTML 中加载必要的 JavaScript,让用户选择内容,并在鼠标抬起时,弹出一个对话框,允许他们提供建议。在我们的网站上,我们捕获用户的登录信息,因为我们是单点登录,所以如果他们输入任何不当内容,我们可以对其进行处理。
这是演示的英文版本。不进行翻译。
使用 Systran MT 已翻译成日语。
请原谅拼写错误。Systran 不翻译越南语。所以,用户需要提供翻译。他们只需选择英文文本,然后会弹出一个对话框询问翻译。然后,该翻译将被存储在数据库中供所有网站使用。
这是数据库表。对于任何大小的网站或多个网站,它都会变得相当大,但所有网站都可以使用它。一旦一个网站学会了一个单词,所有网站在那个时候都知道了,并且不需要再查找了。
Systran 不翻译越南语。所以,用户需要提供翻译。他们只需选择英文文本,然后会弹出一个对话框询问翻译。然后,该翻译将被存储在数据库中供所有网站使用。当然,如果用户拼写错误,正如这里所示,它会显示拼写错误,因此添加一个多语言拼写检查器会很有帮助。
结论
抛弃 RESX 文件,让你的网站通过 MT 作为老师相互学习。我希望这是一个你可以扩展的想法。代码可能需要一些调整才能集成到你的网站中,但希望这是一个你可以使用的想法。