提出问题

做多语言(Internationalization,简称i18n)的页面,有多种可选方案,

  1. 制作网页模板,然后使用动态语言去解析并通过语言文件替换文字,这样要求较高,虽然大部分会有相应的支持,但是对于小型网站会提高成本。
  2. 使用JavaScript来动态更换,但是对于某些大量文字且其中夹杂多种HTML标签时会很麻烦,需要每个标签单独替换。否则就要求不同语言的编辑人员能用HTML。
  3. 使用服务器的rewrite,根据语言来重定向到不同的页面。但是这样会使页面增多,重用性降低,给维护工作带来麻烦。

分析问题

分析一下上面3种方案,可以看到2、3两种方案其实可以互补,对于大量文字夹杂大量HTML标签时,使用3方案,对于其他文字分布较开时,使用2号方案。 所以新方案的主要思想就是,使用rewrite来获取所选语言,并设置为环境变量,并根据这个变量再使用Server Side Include(SSI,mod_include)包含不同的语言文件,包括JS和HTML的。 拿最常见的产品网站来说,基本从上到下包括以下几个部分,

  1. 页头(网站logo在左,网站主要链接在右并且包含下拉列表的各种链接一组)
  2. 大banner(大图,醒目的大字,大号购买按钮之类的,有多个产品会滚动显示),展示重点产品
  3. 网格式产品描述(重点介绍功能,一个网格一个功能,有多个产品则会一个网格一个产品,然后带产品的详细页面链接,从第一条开始)
  4. 用户评价,媒体评价等等。
  5. 页脚(各种链接,权重当然没有页头重,但是也是SEO必要的一部分,然后联系方式什么的)
  6. 版权声明,ICP备案之类的其他东西

这些网页基本可以用Bootstrap的container类进行分割,然后每部分都做为单独的页面,成为SSI的一部分,而且1、5、6可以作为公共部分,在所有本网站的页面中使用。 一个一个来分析每部分应该使用哪种i18n方案,

  1. 由于自由链接,下拉列表之类的文字东西,很明显应该使用JavaScript来对每个进行替换。
  2. banner内的东西也不会太多,而且会根据不同场景进行替换,更换频率最高的一部分,而且由于banner图极大,动态加载比较好,所以应该使用灵活的JavaScript来做。
  3. 网格式产品描述,这部分带有大量文字和标签的可能性最大,不同语言显示效果会因为大量文字而不同,所以使用静态页面最合适。
  4. 这部分文字也十分多,但是不一定会带很多HTML标签,所以可以看情况使用。
  5. 同1
  6. 同1

基本想法确定,下面来实战看看,代码链接

实战

Apache设置

实现需要使用的Apache2的mod_include和mod_rewrite,打开这2个

1
2
3
4
5
$ sudo a2enmod include rewrite 
Considering dependency mime for include:
Module mime already enabled
Module include already enabled
Module rewrite already enabled

然后在项目的根目录下添加.htaccess文件,开启include

1
2
3
4
<Files "*.html">
Options +Includes
AddOutputFilter INCLUDES .html
</Files>

考虑到之后的访问路径将会是/i18nDemo/(language)/*中的language部分作为设置语言的路径,所以在.htaccess文件中添加rewrite体现出来,如果没有带该路径则设置为默认语言。如果在.htaccess中没有的语言,将会404,所以添加语言后记得添加。

1
2
3
4
5
6
7
8
9
10
# TIP: add new language
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /i18nDemo/
RewriteRule ^en/(.*) $1 \[L,env=LANG:en\] # english
RewriteRule ^zh/(.*) $1 \[L,env=LANG:zh\] # chinese
RewriteRule ^fr/(.*) $1 \[L,env=LANG:fr\] # french
</IfModule>
# TIP: change default language
SetEnv DefaultLang zh

基础信息

为了能在所有地方都能知道环境的语言设置,添加一个global.html文件,来把全站都能用到的变量设置起来。

1
2
3
4
5
6
7
8
9
10
11
<!--设置网站根目录-->
<!--#set var="site" value="/i18nDemo/"-->
<!--设置语言信息,由于有redirect,所以设置的LANG变量被改为了REDIRECT_LANG,没有显示设置语言,则使用默认语言-->
<!--同时设置网站带语言的基地址用于各种链接之间跳转时直接带语言跳转-->
<!--#if expr='-T v("REDIRECT_LANG")'-->
<!--#set var="lang" value="${REDIRECT_LANG}"-->
<!--#set var="base" value="${site}${REDIRECT_LANG}/"-->
<!--#else -->
<!--#set var="lang" value="${DefaultLang}"-->
<!--#set var="base" value="$site"-->
<!--#endif -->

然后使用index.html来测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!--#include file="common/global.html"-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>I18N DEMO</title>
</head>
<body>
<h1>hello I18N!</h1>
<h2>SITE=<!--#echo var='site'--></h2>
<h2>BASE=<!--#echo var='base'--></h2>
<h2>LANG=<!--#echo var='lang'--></h2>
</body>
</html>

打开各种语言链接看输出

  • /i18nDemo/

    1
    2
    3
    4
    hello I18N!
    SITE=/i18nDemo/
    BASE=/i18nDemo/
    LANG=zh
  • /i18nDemo/zh/

    1
    2
    3
    4
    hello I18N!
    SITE=/i18nDemo/
    BASE=/i18nDemo/zh/
    LANG=zh
  • /i18nDemo/en/

    1
    2
    3
    4
    hello I18N!
    SITE=/i18nDemo/
    BASE=/i18nDemo/en/
    LANG=en
  • /i18nDemo/jp/

    1
    404

这样就可以在页面的任意位置使用这些变量替换掉相应的链接

首页的链接应该为index.html,其他页面为../index.html,甚至是../index.html,现在可以统一为<!--#echo var='base'-->index.html,并且当前使用什么语言,跳转过来时还是同样的语言

加载语言包

就是把所有页面上带有data-lang属性的标签的文字全部用语言包里面的东西替换掉。 首先在目录下建立lang目录,里面建立zh.js和en.js,

1
2
3
4
5
6
7
8
9
10
11
12
13
//zh.js
;(function(){
window.LangConf = {
'hello': '你好,多语言'
};
})();

//en.js
;(function(){
window.LangConf = {
'hello': 'hello i18n'
};
})();

同时修改index.html,添加jquery的CDN,并且
<h1>hello I18N!</h1>替换为<h1 data-lang="hello"></h1>
然后新建js/i18n.js,把替换用到js写进去

1
2
3
4
5
6
7
8
9
10
11
12
;(function(){
if(typeof LangConf != 'undefined'){
var tmp = $('[data-lang]');
tmp.each(function(index,elem){
var e = $(elem);
var lang = LangConf[e.attr('data-lang')];
if(lang != undefined){
e.text(lang);
}
});
}
})();

把js文件添加进index.html的末尾去,并且语言包应该先于i18n.js加载

1
2
<script src="<!--#echo var='site'-->lang/<!--#echo var='lang'-->.js"></script>
<script src="<!--#echo var='site'-->js/i18n.js"></script>

换不同语言访问,即可看到效果。

当前页面直接切换语言

想象一下以下场景,用户正在访问/i18nDemo/products/product1.html,根据默认设置,此用户会正在使用中文。而此时用户想要切换至英文,那么它点击了一个下面的链接,

1
<a href="?">English</a>

问号处应该是什么呢?这些i18n的选项应该是跟随页头或者页脚的,然而并不知道当前链接。如果用SSI记录的话,也只能拿到REQUEST_URI,要插入链接十分麻烦。 所以这里有2个解决办法,

  1. <a href="<!--#echo var='REQUEST_URI'?en">English</a>,然后使用rewrite去观察request中是否有query然后去跳转。
  2. <a href="<!--#echo var='site'--><!--#echo var='lang'-->/">English</a>,这样做一个fallback然后使用JavaScript代码去替换掉这个链接。

我个人倾向于第二种,这样不用去改Apache文件,毕竟htaccess文件可读性差,交接什么的时候不方便,而且有在没有启用JavaScript时的fallback。

所以补上第二种的代码,添加到i18n.js里面即可。

1
2
3
4
$('.lang-jump [data-jump]').each(function(index,elem){
var e = $(elem);
e.attr('href', window.location.pathname.replace(BaseUrl,SiteUrl + e.attr('data-jump') + '/'));
});

只有加了.lang-jump类下面的带有data-jump属性的才会跳转。

在index.html里面加上下面2个链接测试一下,这里注意因为htaccess设置只有html文件会使用include,所以这些不能加载i18n.js里面

1
2
3
4
5
6
7
8
<div class="lang-jump">
<a href="<!--#echo var='site'-->en/" data-jump="en">English</a>
<a href="<!--#echo var='site'-->zh/">中文</a>
</div>
<script>
var BaseUrl = '<!--#echo var="base"-->';
var SiteUrl = '<!--#echo var="site"-->';
</script>