Hello World
Spiga

五十种语言的“圣诞快乐”(上):分析与实现

2009-12-25 11:17 by 老赵, 7006 visits

圣诞节到了,于是在某个邮件列表上收到了这样一封信,“五种语言的圣诞快乐”:

  • 汉语版:圣诞快乐!
  • 英语版:麦瑞克瑞死没死!
  • 俄语版:买个萝卜切吧切吧炖了吧!
  • 韩语版:空起哇撒起哇, 米死搭!
  • 日语版:锅你得洗哇,碗你得洗哇,盆你得洗哇,锅碗盆你都得洗了哇!

后来有人贴了个更全的,于是有人猜测“这不是使用Google翻译做的吧?”哗,有意思的,我心想。以前也用过一个别人封装好的程序集,可以调用在线的Google Translate服务进行翻译。那么,我们现在自己来试试看吧。

使用Google Translate

Google Translate是一个在线工具,可以翻译五十多种语言。可惜的是,Google Translate并没有像Bing翻译那样直接提供RESTfulSOAP形式的API,不过这也给了我们一些探索的乐趣。目前Google Translate只提供一个脚本,您可以把它嵌入到网页中,这样便可以使用其翻译功能了。例如,您可以在页面上放置这样的内容:

<div id="google_translate_element">圣诞快乐</div>

<script>
    function googleTranslateElementInit() {
        new google.translate.TranslateElement({
            pageLanguage: 'zh-CN'
        }, 'google_translate_element');
    }
</script>

<script src="http://translate.google.com/translate_a/element.js?cb=googleTranslateElementInit"></script>

于是乎:

以上是我选择将其翻译为英语后的结果——当然Google Translate可以让你选择五十多种目标语言。

获取语言信息

使用Fiddler抓包后便可以发现,在页面加载时Google Translate的脚本首先会去加载所有它支持的语言:

GET /translate_a/l?client=te&hl=en&cb=_callbacks_._0g3matv3f HTTP/1.1
Accept: */*
Referer: http://localhost:46714/translate.html
Accept-Language: en-us
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; .NET4.0C; .NET4.0E; OfficeLiveConnector.1.3; OfficeLivePatch.0.0)
Accept-Encoding: gzip, deflate
Host: translate.googleapis.com
Connection: Keep-Alive

这样便会得到这样的结果:

_callbacks_._0g3matv3f({'sl':{'auto':'Detect language','af':'Afrikaans','sq':'Albanian','ar':'Arabic','be':'Belarusian','bg':'Bulgarian','ca':'Catalan','zh-CN':'Chinese','hr':'Croatian','cs':'Czech','da':'Danish','nl':'Dutch','en':'English','et':'Estonian','tl':'Filipino','fi':'Finnish','fr':'French','gl':'Galician','de':'German','el':'Greek','iw':'Hebrew','hi':'Hindi','hu':'Hungarian','is':'Icelandic','id':'Indonesian','ga':'Irish','it':'Italian','ja':'Japanese','ko':'Korean','lv':'Latvian','lt':'Lithuanian','mk':'Macedonian','ms':'Malay','mt':'Maltese','no':'Norwegian','fa':'Persian','pl':'Polish','pt':'Portuguese','ro':'Romanian','ru':'Russian','sr':'Serbian','sk':'Slovak','sl':'Slovenian','es':'Spanish','sw':'Swahili','sv':'Swedish','th':'Thai','tr':'Turkish','uk':'Ukrainian','vi':'Vietnamese','cy':'Welsh','yi':'Yiddish'},'tl':{'af':'Afrikaans','sq':'Albanian','ar':'Arabic','be':'Belarusian','bg':'Bulgarian','ca':'Catalan','zh-CN':'Chinese (Simplified)','zh-TW':'Chinese (Traditional)','hr':'Croatian','cs':'Czech','da':'Danish','nl':'Dutch','en':'English','et':'Estonian','tl':'Filipino','fi':'Finnish','fr':'French','gl':'Galician','de':'German','el':'Greek','iw':'Hebrew','hi':'Hindi','hu':'Hungarian','is':'Icelandic','id':'Indonesian','ga':'Irish','it':'Italian','ja':'Japanese','ko':'Korean','lv':'Latvian','lt':'Lithuanian','mk':'Macedonian','ms':'Malay','mt':'Maltese','no':'Norwegian','fa':'Persian','pl':'Polish','pt':'Portuguese','ro':'Romanian','ru':'Russian','sr':'Serbian','sk':'Slovak','sl':'Slovenian','es':'Spanish','sw':'Swahili','sv':'Swedish','th':'Thai','tr':'Turkish','uk':'Ukrainian','vi':'Vietnamese','cy':'Welsh','yi':'Yiddish'}})

不过,您一定发现了,这些数据为什么是英文的呢?根据经验,这是由浏览器的语言首选项决定的。因此,我将zh-CN加入语言列表中的最上方,在IE里可以在Tools – Internet Options的General标签中设置Languages:

此时再次刷新页面,就会发现界面变成了中文,而Google Translate也会使用另外的地址来加载语言信息:

GET /translate_a/l?client=te&hl=zh-CN&cb=_callbacks_._0g3mb650r HTTP/1.1
Accept: */*
Referer: http://localhost:46714/translate.html
Accept-Language: zh-CN,en-US;q=0.5
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; .NET4.0C; .NET4.0E; OfficeLiveConnector.1.3; OfficeLivePatch.0.0)
Accept-Encoding: gzip, deflate
Host: translate.googleapis.com
Connection: Keep-Alive

其得到的结果是:

_callbacks_._0g3mb650r({'sl':{'auto':'检测语言','sq':'阿尔巴尼亚语','ar':'阿拉伯语','ga':'爱尔兰语','et':'爱沙尼亚语','be':'白俄罗斯语','bg':'保加利亚语','is':'冰岛语','pl':'波兰语','fa':'波斯语','af':'布尔文(南非荷兰语)','da':'丹麦语','de':'德语','ru':'俄语','fr':'法语','tl':'菲律宾语','fi':'芬兰语','ko':'韩语','nl':'荷兰语','gl':'加利西亚语','ca':'加泰罗尼亚语','cs':'捷克语','hr':'克罗地亚语','lv':'拉脱维亚语','lt':'立陶宛语','ro':'罗马尼亚语','mt':'马耳他语','ms':'马来语','mk':'马其顿语','no':'挪威语','pt':'葡萄牙语','ja':'日语','sv':'瑞典语','sr':'塞尔维亚语','sk':'斯洛伐克语','sl':'斯洛文尼亚语','sw':'斯瓦希里语','th':'泰语','tr':'土耳其语','cy':'威尔士语','uk':'乌克兰语','es':'西班牙语','iw':'希伯来语','el':'希腊语','hu':'匈牙利语','it':'意大利语','yi':'意第绪语','hi':'印地语','id':'印尼语','en':'英语','vi':'越南语','zh-CN':'中文'},'tl':{'sq':'阿尔巴尼亚语','ar':'阿拉伯语','ga':'爱尔兰语','et':'爱沙尼亚语','be':'白俄罗斯语','bg':'保加利亚语','is':'冰岛语','pl':'波兰语','fa':'波斯语','af':'布尔文(南非荷兰语)','da':'丹麦语','de':'德语','ru':'俄语','fr':'法语','tl':'菲律宾语','fi':'芬兰语','ko':'韩语','nl':'荷兰语','gl':'加利西亚语','ca':'加泰罗尼亚语','cs':'捷克语','hr':'克罗地亚语','lv':'拉脱维亚语','lt':'立陶宛语','ro':'罗马尼亚语','mt':'马耳他语','ms':'马来语','mk':'马其顿语','no':'挪威语','pt':'葡萄牙语','ja':'日语','sv':'瑞典语','sr':'塞尔维亚语','sk':'斯洛伐克语','sl':'斯洛文尼亚语','sw':'斯瓦希里语','th':'泰语','tr':'土耳其语','cy':'威尔士语','uk':'乌克兰语','es':'西班牙语','iw':'希伯来语','el':'希腊语','hu':'匈牙利语','it':'意大利语','yi':'意第绪语','hi':'印地语','id':'印尼语','en':'英语','vi':'越南语','zh-TW':'中文(繁体)','zh-CN':'中文(简体)'}})

而其中标红的部分,便是tl(我猜测是Target Language的意思)所对应的JSON格式,我们可以使用以下代码来获取所有的语言信息:

private static Dictionary<string, string> GetLanguages()
{
    var url =
        "http://translate.googleapis.com" +
        "/translate_a/l?client=te&hl=zh-CN&cb=_callbacks_._0g3mb650r";

    var webClient = new WebClient();
    var script = webClient.DownloadString(url);

    var json = Regex.Match(script, @"'tl':({.+})}\)").Groups[1].Value;
    var serializer = new JavaScriptSerializer();
    return serializer.Deserialize<Dictionary<string, string>>(json);
}

进行翻译

点击页面上的下拉列表可以进行翻译。例如我们选择“英语”之后,Fiddler便会捕获到一个POST请求:

POST /translate_a/t?client=te&format=html&v=1.0 HTTP/1.1
Accept: */*
Accept-Language: zh-CN
Referer: http://translate.googleapis.com/translate_static/js/element/hrs.swf
x-flash-version: 10,0,32,18
Content-Type: application/x-www-form-urlencoded
Google-Translate-Referer: http://localhost:46714/translate.html
Content-Length: 58
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; .NET4.0C; .NET4.0E; OfficeLiveConnector.1.3; OfficeLivePatch.0.0)
Host: translate.googleapis.com
Connection: Keep-Alive
Pragma: no-cache

q=%E5%9C%A3%E8%AF%9E%E5%BF%AB%E4%B9%90&sl=zh-CN&tl=en&tc=1

可以看到,需要翻译的文字将作为q被传输到服务器端。这个请求会得到翻译后的结果:

"Merry Christmas"

于是代码也就很容易写出了:

private static string Translate(string source, string sl, string tl)
{
    var url =
        "https://translate.googleapis.com" +
        "/translate_a/t?client=te&format=html&v=1.0";

    var data = String.Format(
        "q={0}&sl={1}&tl={2}&tc=1",
        HttpUtility.UrlEncode(source), sl, tl);

    var webClient = new WebClient();
    webClient.Encoding = Encoding.UTF8;
    webClient.Headers.Add(
        HttpRequestHeader.UserAgent,
        "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0;)");

    var json = webClient.UploadString(url, data);
    var serializer = new JavaScriptSerializer();
    return serializer.Deserialize<string>(json);
}

在试验过程中,您可以使用Fiddler的Request Builder功能来发起一些测试请求,这样便可以得知怎么样的Header信息可以获得正确的结果。例如在这里,我发现如果不添加User Agent信息,Google Translate便会返回一些非常奇怪的内容。

组合

既然有了以上两个方法,输出翻译结果也再简单不过了:

var languages = GetLanguages();

var targetLanguages = languages.Select(
    p => String.Format(
        "{0}: {1}",
        p.Value,
        Translate("圣诞快乐", "zh-CN", p.Key)));

File.WriteAllLines("output.txt", targetLanguages.ToArray());

由于语言非常古怪,因此无法在Console上输出,我只能将其直接写到UTF-8的文件中去——最后的结果还是相当有趣的。

完整代码

相关文章

Creative Commons License

本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名赵劼(包含链接),具体操作方式可参考此处。如您有任何疑问或者授权方面的协商,请给我留言

Add your comment

29 条回复

  1. chy710
    *.*.*.*
    链接

    chy710 2009-12-25 11:20:00

    圣诞快乐

  2. 老赵
    admin
    链接

    老赵 2009-12-25 11:22:00

    @chy710
    同乐同乐

  3. 温景良(Jason)
    *.*.*.*
    链接

    温景良(Jason) 2009-12-25 11:24:00

    老赵够闲的,还玩这个

  4. 温景良(Jason)
    *.*.*.*
    链接

    温景良(Jason) 2009-12-25 11:24:00

    把生活的每一个点滴到嵌入到程序中,高人

  5. 老赵
    admin
    链接

    老赵 2009-12-25 11:25:00

    @温景良(Jason)
    上是铺垫,下比较好玩。
    这不是趁此机会借一个好的题材么,否则干巴巴都没有意思,姆哈哈……

  6. Old
    *.*.*.*
    链接

    Old 2009-12-25 11:25:00

    同上

  7. lovecindywang
    *.*.*.*
    链接

    lovecindywang 2009-12-25 11:28:00

  8. 老赵
    admin
    链接

    老赵 2009-12-25 11:32:00

    @lovecindywang
    不错不错……可惜是封装好的JavaScript API……

  9. 温景良(Jason)
    *.*.*.*
    链接

    温景良(Jason) 2009-12-25 11:35:00

    Jeffrey Zhao:
    @温景良(Jason)
    上是铺垫,下比较好玩。
    这不是趁此机会借一个好的题材么,否则干巴巴都没有意思,姆哈哈……


    有道理,一直想做点东西,但是总是认为没有应用场合,看样子以后结合生活.

  10. tshao[未注册用户]
    *.*.*.*
    链接

    tshao[未注册用户] 2009-12-25 11:39:00

    [可以看到,需要翻译的文字将作为q被传输到客户端。]
    应该是服务器端吧。

    原来这request是flash里发出去的啊……

  11. 老赵
    admin
    链接

    老赵 2009-12-25 11:42:00

    @tshao
    为了跨域名吧

  12. 1-2-3
    *.*.*.*
    链接

    1-2-3 2009-12-25 11:54:00

    “最后的结果”链接好像不好用啊?
    另外,能按读音翻译成类似于“买个萝卜切吧切吧炖了吧”这样有趣的汉字不?

  13. 老赵
    admin
    链接

    老赵 2009-12-25 12:06:00

    @1-2-3
    再试试看?

  14. ITniao
    *.*.*.*
    链接

    ITniao 2009-12-25 12:28:00

    期待 情人節快樂!

  15. Mr.d
    *.*.*.*
    链接

    Mr.d 2009-12-25 12:49:00

    老赵挺喜欢玩的···

  16. canbeing
    *.*.*.*
    链接

    canbeing 2009-12-25 13:01:00

    老赵圣诞节的特别礼物,圣诞快乐

  17. 1-2-3
    *.*.*.*
    链接

    1-2-3 2009-12-25 13:10:00

    @Jeffrey Zhao
    现在能看了。要是能变成汉字音的就算你厉害!!

  18. winter-cn
    *.*.*.*
    链接

    winter-cn 2009-12-25 13:18:00

    @1-2-3
    哈哈 那可不一定实现不了哦
    说不定老赵又要找一汉字拼音服务调用下了

  19. 老赵
    admin
    链接

    老赵 2009-12-25 13:19:00

    @winter-cn
    但这难道不是外文么……

  20. 徐少侠
    *.*.*.*
    链接

    徐少侠 2009-12-25 13:20:00

    lovecindywang:
    http://code.google.com/intl/zh-CN/apis/ajaxlanguage/documentation/reference.html

    这个不是吗?



    感觉这年头Google越来越厉害了

    Java那里,Sun倒下后,Google其实可以成为Java的精神归宿
    Google已经提供了一系列服务器产品
    老实说,差不多已经可以涵盖90%以上的开发生命周期了
    网速足够的话,Web应用的开发可以不要IDE了,浏览器就可以了!!

    .net这里,至少在WebService概念上,Google也提供大量的东西

    Google离开霸主的目标越来越近
    而且不光是软件一个领域的霸权
    比MS还狠

  21. 老赵
    admin
    链接

    老赵 2009-12-25 13:25:00

    @徐少侠
    Google是牛,但是你举出的例子我真不觉得是Google牛的地方……
    例如翻译这东西,Bing也有,比Google提供的还方便,呵呵。

    Google和Java的距离还是很远的,不然你说说它对Java有什么贡献呢?
    Java这块,还是Sun和IBM两家的贡献无可比拟。

    你说Web的IDE,假设已经网速够了,那么有没有成熟的东西可以看看呢?
    还有,你说的Google提供的服务器产品是哪些呢?
    而且要说领域,微软比Google的战线要长太多了……

    总之一句话,Google是牛,但也不用神化Google。
    神化Google是媒体喜欢干的事情,人家要吸引眼球,其实他们心里都明镜似的呢,呵呵……

  22. winter-cn
    *.*.*.*
    链接

    winter-cn 2009-12-25 13:27:00

    @Jeffrey Zhao
    外文=>音标=>相近拼音=>汉字

    外文=>音标
    这一步还没思路 拭目以待看老赵你了 哈哈哈

  23. Crayon
    *.*.*.*
    链接

    Crayon 2009-12-25 14:15:00

  24. MyCoolDog
    *.*.*.*
    链接

    MyCoolDog 2009-12-25 17:08:00

    学习!

  25. 王宁
    *.*.*.*
    链接

    王宁 2009-12-25 17:52:00

    这个貌似翻译“你好”的时候会有问题..。^_^

  26. 王宁
    *.*.*.*
    链接

    王宁 2009-12-25 18:25:00

    这个可能是由于翻译有多个结果,如你好的英文翻译为:"[\"Hello\",[[\"interjection\",\"hallo!\",\"hello!\",\"hi!\"]]]"
    然后中间有些符号在JavaScriptSerializer实例的Deserialize的方法中有异常。
    这里需要处理一下。

  27. 老赵
    admin
    链接

    老赵 2009-12-25 18:26:00

    @王宁
    原来如此,我还以为总是只有一个结果呢。
    也真是的,就算只有一个结果,也放在数组里不好嘛。
    一点也不统一……

  28. funny zak
    *.*.*.*
    链接

    funny zak 2009-12-26 00:07:00

    写的好,顶了

  29. 斯克迪亚
    *.*.*.*
    链接

    斯克迪亚 2009-12-28 13:44:00

    以前就一直特纠结Google翻译怎么调用,现在终于被老赵解惑了,内牛满面。

发表回复

登录 / 登录并记住我 ,登陆后便可删除或修改已发表的评论 (请注意保留评论内容)

昵称:(必填)

邮箱:(必填,仅用于Gavatar

主页:(可选)

评论内容(大于5个字符):

  1. Your Name yyyy-MM-dd HH:mm:ss

使用Live Messenger联系我