看到thinkphp说 支持多语言 还是挺开心的。之前做过一个日本的工地管理项目,前期不知道要翻译这事(也没想过),到了后面突然被交待这项任务,瞬间懵了。当时用的ci框架,ci本身是有一个 语言类 设置,加载System/language目录下的语言包文件。但是之前没有使用语言包的习惯,一般提示或错误信息都是直接定义使用的。还有后台页面也是中文页面,这些因素使得后期的翻译工作异常繁重,没有尽头。

相比ci框架中的语言类支持,thinkphp的多语言支持更加灵活多变。首先就是语言包的使用问题。ci可以一次加载多个语言包,但并不能针对客户端做出灵活反应。一般都是定义一个默认语言,然后所有信息都从这个包里面去取。而thinkphp可以根据浏览器信息自动判断当前用户的浏览器支持语言,动态赋值,多态展现。其次就是thinkphp默认比ci多了一级模块,相比于ci只能从系统语言目录加载,thinkphp有了更加多的选择,加载顺序如下:

ThinkPHP/Lang/zh-cn.php 框架底层语言包
Application/Common/Lang/zh-cn.php 应用公共语言包
Application/Home/Lang/zh-cn.php Home模块语言包
Application/Home/Lang/zh-cn/user.php Home模块的User控制器语言包

按照thinkphp文档,先创建tags.php配置文件,配置行为定义

return array(
    // 添加下面一行定义即可
    'app_begin' => array('Behavior\CheckLangBehavior'),
);

之后再应用配置文件中添加多语言支持

'LANG_SWITCH_ON' => true,   // 开启语言包功能
'LANG_AUTO_DETECT' => true, // 自动侦测语言 开启多语言功能后有效
'LANG_LIST'        => 'zh-cn,zh-tw', // 允许切换的语言列表 用逗号分隔
'VAR_LANGUAGE'     => 'l', // 默认语言切换变量

系统默认只有简体中文语言包文件zh-cn.php和英文语言包en-us.php,如果要增加繁体中文zh-tw或者其他语言支持,只要增加相应的语言定义文件。ThinkPHP语言文件定义采用返回数组方式:

return array(
     'lan_define'=>'欢迎使用ThinkPHP',
);

也可以在程序里面动态设置语言定义的值,使用下面的方式:

L('lan_define','语言定义');
$value = L('lan_define');

可以直接再模版引擎中直接输出:

{$Think.lang.lan_define}

我这边在ThinkPHP/Lang添加了zh-tw.php繁体中文包。在zh-cn和zh-tw同时定义了变量lang_define,展示各自的语言。现在要测试是否能够根据浏览器支持语言来展示不同的语言包,只需要将浏览器默认语言设置成繁体即可(不会可以百度)。

但是结果并没有如我所愿。输出的一直是简体中文。我甚至去掉了浏览器语言偏好设置中的其他选项,只留了zh-tw一个,仍然没有效果。从浏览器控制台的Request Headers中找到Accept-language字段:Accept-Language: zh-TW;q=0.9,zh;q=0.8,也就是说浏览器默认首选语言确实是中文繁体,后面的几项应该是浏览器默认支持的语言(包含了中文)。无解。但到了第二天再次查看的时候,输出的内容竟然变成了繁体。这是不是说明,之前是设置成功了的呢?

查看多语言支持行为源码:ThinkPHP\Library\Behavior\CheckLangBehavior.class.php。该行为绑定在app_begain标签上,在app_begain的时候,会回调run()方法。run()方法中执行了checkLanguage()方法,主要函数体如下:

private function checkLanguage() {
        // 不开启语言包功能,仅仅加载框架语言文件直接返回
        if (!C('LANG_SWITCH_ON',null,false)){
            return;
        }
        $langSet = C('DEFAULT_LANG');
        $varLang =  C('VAR_LANGUAGE',null,'l');
        $langList = C('LANG_LIST',null,'zh-cn');
        // 启用了语言包功能
        // 根据是否启用自动侦测设置获取语言选择
        if (C('LANG_AUTO_DETECT',null,true)){
            if(isset($_GET[$varLang])){
                $langSet = $_GET[$varLang];// url中设置了语言变量
                cookie('think_language',$langSet,3600);
            }elseif(cookie('think_language')){// 获取上次用户的选择
                $langSet = cookie('think_language');
            }elseif(isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])){// 自动侦测浏览器语言
                preg_match('/^([a-z\d\-]+)/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $matches);
                $langSet = $matches[1];
                cookie('think_language',$langSet,3600);
            }
            if(false === stripos($langList,$langSet)) { // 非法语言参数
                $langSet = C('DEFAULT_LANG');
            }
        }
        // 定义当前语言
        define('LANG_SET',strtolower($langSet));

        // 按照优先级顺序读取语言包
        ...
}

从代码中可以发现,tp对选择使用的语言做了cookie缓存。通过清除浏览器中的think_languagecookie变量,可以查看到实时代码变化结果。

首先是从默认配置项DEFAULT_LANG获取值。

接着是get参数,key值为之前定义的VAR_LANGUAGE的值。试了一下l=zh-tw传参是ok的。

接着是think_languagecookie缓存,通过清除对应的缓存可以绕过这一选择分支。

之后就是客户端请求头中的Accept-Language变量,在服务器端获取为$_SERVER['HTTP_ACCEPT_LANGUAGE']。匹配规则不太理解,但从结果来看,$matches有两项,都是Accept-Language中的第一项,也是浏览器默认语言中文繁体zh-TW

最后是一个规则补充。在配置文件中配置过LANG_LIST常量,此时检查得到的语言是否在可用语言列表中,如果不在,则仍然加载默认语言。

如此,便真相大白了!

结尾:用的是chrome浏览器,在默认语言切到繁体后,总会出现自动翻译的弹框。在去除了view文件中html便签的lang=zh-CN后好了。这是因为文档定义的语言类型与浏览器默认语言类型不一致。