4.3 模板多语言与 i18n 语言包

把界面文案从模板里抽出来,一套模板跑中英两种界面。

4.3.1 先分清两套语言包

X5.0 里有两套语言包,作用范围完全不同,混了就会串词。

系统语言包在 source/language/ 下,给 Discuz 自身界面用,模板里用 {lang key} 调用。这套是框架级的,你一般不碰。

模板语言包在 /template/i18n/SC_UTF8/lang_template.php,专门服务于模板作者写的文案,机制和插件语言包一致:定义一个 $lang 数组,键是英文,值是显示文字。SC_UTF8 是简体 UTF-8 的目录代号,站内语言就是它。

4.3.2 建模板语言包

新建 /template/i18n/SC_UTF8/lang_template.php:

<?php
$lang = [
    'nice'     => '不错!',
    'login_tip' => '登录后查看更多内容',
    'readmore'  => '阅读全文',
];

老式的 array() 写法同样有效:

<?php
$lang = array(
    'nice'     => '不错!',
    'login_tip' => '登录后查看更多内容',
    'readmore'  => '阅读全文',
);

想让别的代码直接引用这个文件时稳妥些,加个守卫。IN_DISCUZ 是 class_core.php 在正常加载流程里定义的系统常量,文件被浏览器直接访问时它不存在,守卫会挡住:

<?php
if (!defined('IN_DISCUZ')) {
    exit('Access Denied');
}

$lang = [
    'nice'     => '不错!',
    'readmore' => '阅读全文',
];

4.3.3 模板里怎么调用

用 {lang key} 调用,key 就是 $lang 的键:

&lt;!-- template/你的标识/forum/viewthread.htm --&gt;
&lt;div class="post-actions"&gt;
    &lt;a href="javascript:;" class="act-like"&gt;{lang nice}&lt;/a&gt;
    &lt;a href="$readmoreurl" class="act-more"&gt;{lang readmore}&lt;/a&gt;
&lt;/div&gt;

键名建议统一加前缀,避免和系统语言包撞名。撞名的后果是 {lang xxx} 拿到了别的词条,页面显示莫名其妙的中文或英文。模板叫 news,键名就全写成 news_nice、news_login_tip。

自定义描述文字也能走语言包,语言包里对应位置用 %s 占位,模板里写 {lang news_topic_count}。占位符的具体写法以你版本的语言包约定为准:

<?php
$lang = [
    'news_topic_count' => '今日主题数:%s',
];

4.3.4 哪些文本能写进语言包

不止模板正文,导出的 XML 里有一批字段同样支持写进 $lang:

字段 含义 多语言价值
name 模板名称 后台模板列表里显示
tplname 模板标识名 部分界面展示
copyright 版权信息 前台页脚、后台详情页
version 版本号 一般不用翻译
var.title 变量的标题 站长在后台看到的输入框名称
var.description 变量的说明文字 后台提示文本
var.extra 变量的附加配置 视内容而定

var.title 和 var.description 能翻译是 X5.0 很实用的改进:面向海外站长的模板包,后台设置页也是英文的,站长不用截图问你每个框是干嘛的。语言包按层级对应即可:

<?php
$lang = [
    'templatename'        => '资讯模板',
    'templatecopyright'   => '你的团队',
    'news_sitetitle'      => '站点副标题',
    'news_sitetitle_desc' => '显示在浏览器标题和页头横幅右侧',
];

4.3.5 i18n 多语言扩展

SC_UTF8 是默认简体语言。想支持英文,X5.0 提供的兼容扩展路径是在 i18n/ 下按语言代码建子目录,一个语言一个目录,各放一个 lang_template.php,键名与 SC_UTF8 下完全一致,值换成对应语言:

<?php
// /template/xxx/i18n/en/lang_template.php
$lang = [
    'nice'      => 'Nice!',
    'readmore'  => 'Read more',
];

系统的 i18n() 函数用来设置自定义语言包目录,把 mylang 指向站点下的 en 目录即可。站点切语言靠用户组语言设置加浏览器语言协商,这部分由系统处理,模板侧只需保证每个语言目录里键名齐全——漏一个键,那个词就会退回默认语言显示。日文等语言同理,建 /template/xxx/i18n/jp/ 放同名文件即可。

4.3.6 模板 JS 语言包

模板里的 JS 也要出文案,别把中文直接写进 .js。思路和插件的 JS 语言包一致:JS 里留占位符,启动时从语言包取值替换。PHP 侧把文案输出成 JSON:

<?php
$langjs = isset($lang) ? $lang : array();
?&gt;
&lt;script&gt;
window.DISCUZLANG = &lt;?php echo json_encode($langjs, JSON_UNESCAPED_UNICODE); ?&gt;;
&lt;/script&gt;

JS 侧这样用,好处是语言包换了语言文案跟着变,不用动 .js 文件本身。注意输出 JSON 时用 JSON_UNESCAPED_UNICODE,否则中文会被转成 \uXXXX,虽然能正常解析,但排查时很难看:

function t(key, fallback) {
    var dict = window.DISCUZLANG || {};
    return dict[key] || fallback || key;
}

document.getElementById('submit-btn').textContent = t('news_submit', '提交');

4.3.7 一份模板支持中英两种界面

序号 做什么 完成标志
1 建 /template/i18n/SC_UTF8/lang_template.php 键名齐全,中文
2 扫模板,把硬编码文案换成 {lang key} 模板里搜不到中文字符串
3 给键名加模板前缀 无键名与系统语言包冲突
4 建 /template/xxx/i18n/en/lang_template.php 键名与 SC_UTF8 完全一致
5 处理 XML 字段 模板名、变量标题都有译文
6 输出 JS 语言包 DISCUZLANG 可用
7 JS 侧改成取值 .js 里无硬编码文案
8 更新缓存测试 中英文各切一次
9 核对漏键 无退回默认语言的词

第 2 步最费劲。经验做法:先用编辑器的全项目搜索把 .htm 里的中文全找出来,逐条判断是文案还是内容。用户发的帖子、公告正文是内容,不该进语言包,只有界面固定文案才进。判断标准很简单——这个中文在英文站上也应该存在吗?不应该,它就是语言包条目。

第 4 步要特别小心:复制完英文包又新增了中文条目,忘了同步英文。漏键是运行时才暴露的,静态检查查不出来,建议写个十行的键名校验脚本比人工比对快得多。

到这里模板就具备了打包给海外用户的能力。但站长拿到包后第二个需求通常不是改文案,而是「把这块区域挪一挪」——这要靠 DIY,而 DIY 想让用户有东西可拖,模板就得提供页面组件和模块组件。