4.2 插件模板与语言包的整合
模板放哪、怎么渲染,以及中文硬编码这个最大的坑怎么填。
4.2.1 插件的模板放在哪
插件页面也需要界面。X 系列的惯例是:脚本放 source/plugin/{标识}/,模板统一放它下面的 template/ 子目录,文件名和脚本同名,扩展名是 .htm 或 .php。
| 用途 | 位置 |
|---|---|
| 普通版模板 | source/plugin/{标识}/template/xxx.htm |
| 手机版模板 | source/plugin/{标识}/template/mobile/xxx.htm |
这里说的是「扩展项目」类型的模块——脚本以 .inc.php 结尾、模板放固定文件名那一类。「程序脚本」类型(.class.php)走页面嵌入,模板在模板套系里,不受这套规则约束。记住「同名」这条:脚本 myplugin.inc.php 对应的模板就得叫 myplugin.htm。系统按这个约定去找,找不到就白屏。
4.2.2 渲染:include template('xxx')
模板不能被浏览器直接访问,所以要用系统的模板函数渲染,插件脚本里最常见的一行是:
<?php
// source/plugin/你的标识/myplugin.inc.php
global $_G, $discuz;
$pluginid = 'myplugin'; // 与目录名一致
// ... 业务处理 ...
include template($pluginid);
template() 的参数就是不带路径和扩展名的标识。它会自动去 source/plugin/{标识}/template/ 下找同名文件,编译后输出。手机版单独指定:
include template($pluginid.'_mobile');
比 include template($pluginid) 更省事的是直接写标识字符串:include template('myplugin');。两种都对,$pluginid 变量的好处是脚本改名时只改一处。
改完模板记得更新缓存。模板不是每次都解析,先编译成 PHP 缓存到 data/cache/,缓存失效才重新编译——不更新缓存你会盯着没改动的页面怀疑人生。
4.2.3 模板里用什么标签
插件模板支持的是全套模板语言,和模板套系里的一致。下面这段可以直接放 template/myplugin.htm:
<!-- source/plugin/你的标识/template/myplugin.htm -->
<div class="mp-wrap">
<h2 class="mp-title">{lang myplugin_title}</h2>
{if $count}
<p>{lang myplugin_summary}({$count})</p>
{loop $list $item}
<li class="mp-item">
<a href="plugin.php?id=myplugin:detail&id={$item[cid]}">{$item[title]}</a>
<span class="mp-time">{$item[dateline]}</span>
</li>
{/loop}
{else}
<p class="mp-empty">{lang myplugin_empty}</p>
{/if}
</div>
{lang myplugin_title} 就是语言包调用,myplugin_title 是 key。注意 key 前面不带 lang_ 前缀——插件语言包文件里也不带,两边保持一致。
模板里的逻辑最终编译成 PHP,所以能写 PHP 语法。但别在模板里写业务查询——调试时要在编译后的缓存文件里翻 PHP 代码,排查成本高。查询放脚本里做,模板只负责显示。
4.2.4 插件语言包
插件语言包文件在开发阶段放 data/plugindata/{标识}.lang.php,安装时才被搬到插件目录,内容是一个 $lang 数组:
<?php
// data/plugindata/myplugin.lang.php
$lang = array(
'title' => '我的插件',
'summary' => '当前共有',
'empty' => '暂无数据',
'ok' => '操作成功',
'fail' => '操作失败',
);
在脚本里读取就是 $lang['title'],模板里读取是 {lang myplugin_title}。脚本和模板共用同一份语言包,这也是为什么 key 不能带 lang_ 前缀——系统会自动拼。
语言包的价值在后台更明显:广告类、道具类这些挂载式扩展的 $name、$description 都填语言包 key,切语言时菜单名跟着变。硬编码中文的话,中文站看不出问题,一做英文站菜单就永远改不掉。
4.2.5 模板语言包:X5 的 i18n 扩展
X5.0 给模板加了语言包扩展能力,文件位置是 /template/i18n/SC_UTF8/lang_template.php:
<?php
// template/i18n/SC_UTF8/lang_template.php
$lang = [
'nice' => '不错!',
];
这里可以写进 $lang 的内容比纯文案广:name、tplname、copyright、version,以及变量定义里的 var.title、var.description、var.extra——也就是模板列表页显示的那些文字。兼容扩展的路径是 /template/{标识}/i18n/en/lang_template.php,给某个具体套系补语言。
模板里用 {lang xxx} 调用。系统语言包在 source/language/ 下,模板语言包在 template/i18n/ 下,两个来源合并。
X5 的 default 套系默认启用 .php 后缀的模板文件。做多语言交付时要留意:官方允许同时兼容 X3.5 与 X5.0,但两种后缀并存时继承关系要逐一核对。
4.2.6 硬编码中文对照表
多语言站点里,硬编码中文是最大的坑——因为它编译进缓存后就不变了,改代码还容易漏掉散落各处的一两句话。
| 位置 | 硬编码写法 | 整改做法 |
|---|---|---|
| 模板静态文案 | <h2>我的插件</h2> |
{lang myplugin_title} |
| 脚本提示语 | showmessage('保存成功'); |
showmessage($lang['ok']); |
| 后台设置标题 | getsetting() 里 'label' => '推广标识' |
改用语言包 key |
| 挂载式扩展名称 | var $name = '我的广告'; |
var $name = 'lang_promo_name'; |
| 校验失败提示 | return '请选择对象'; |
从语言包取,缺失时兜底 |
整改的实际做法是先写完功能,再全局搜一遍中文字符串,搬进语言包。搬完把语言包文件本身排除在搜索外——那是它该待的地方。
// 脚本里统一的取值写法,带兜底
function mp_lang($key, $default = '') {
global $lang;
return isset($lang[$key]) ? $lang[$key] : $default;
}
showmessage(mp_lang('fail', '操作失败'));
兜底参数别省。语言包漏发一个 key,页面直接白屏比显示英文更让人抓狂。
生活里给产品做多语言包装有个经验:先按一种语言把所有标签贴齐,再贴第二种,别一边做一边翻——翻出来的语言永远漏项。代码里的语言包就是同一个道理。
4.2.7 收尾检查
- 模板在
source/plugin/{标识}/template/下,文件名与.inc.php脚本同名。 - 手机版模板在
template/mobile/下单独一份。 - 改完模板更新过缓存。
- 脚本和模板都从语言包取值,没有硬编码中文。
- 语言包 key 不带
lang_前缀,两边保持一致。 - 取语言包值的地方有兜底默认值。
模板和语言包这条路走通,插件就能带着完整界面打包给别人。下一节换一个量级——不是扩展系统,而是替换系统的某一段逻辑。
