在PHP项目中,将界面文案与代码分离是常见的做法,其中使用JSON格式保存语言包因其结构清晰、易维护而广受欢迎。不过JSON本身只是静态数据,当文案中存在“亲爱的{{user}},您有{{count}}条新消息”这类需要插入运行时数据的占位符时,就必须在PHP侧完成动态替换。下面我们看一个典型的处理流程。

一、JSON语言文件的结构设计
为了让占位符容易被识别和替换,通常会在JSON里使用双大括号包裹变量名,例如{{username}}。这种写法直观且不易与正常标点冲突。一个简中的语言文件可以如下组织,把不同页面的文本按模块归类。
需要注意,JSON中的值就是普通字符串,PHP通过json_decode解析后得到的仍是字符串,因此替换逻辑完全建立在字符串操作之上。如果占位符设计得过于复杂,比如支持条件判断,那就超出了简单替换的范畴,应当考虑模板引擎而非手动处理。
{
"welcome": "欢迎你,{{username}}!",
"message": "您当前有{{count}}条未读消息。",
"farewell": "再见,{{username}},期待再次相见。"
}
二、使用strtr进行安全替换
PHP内置的strtr函数非常适合做批量字符映射替换。它接收待处理字符串和“原字符=>新字符”的关联数组,能够一次性把所有的{{key}}换成对应的值。相比str_replace循环调用,strtr只遍历一次字符串,效率更高,且不会因为替换后的内容里又包含占位符而产生意外二次替换。
下面示例展示如何读取上述JSON并替换 welcome 文案中的用户名。我们先将占位符写成带双大括号的形式作为数组键,值则为实际变量。这种方式代码可读性很好,新增变量只需往映射数组里加一项。
<?php
$langJson = file_get_contents('zh-CN.json');
$lang = json_decode($langJson, true);
$username = '张三';
$replaceMap = [
'{{username}}' => $username
];
$welcomeText = strtr($lang['welcome'], $replaceMap);
echo $welcomeText; // 输出:欢迎你,张三!
?>
如果同一段文案有多个占位符,只需在映射数组中继续补充。例如 message 字段同时有{{count}},我们可以合并变量后统一替换,避免多次函数调用。
<?php
$count = 5;
$replaceMap['{{count}}'] = $count;
$messageText = strtr($lang['message'], $replaceMap);
echo $messageText; // 输出:您当前有5条未读消息。
?>
三、封装通用的语言助手函数
在真实项目中,每次手写strtr和映射数组会很繁琐。我们可以封装一个函数,接收语言键名和变量数组,自动拼接占位符并返回结果。这样控制器里只需一行调用,也方便集中处理缺失变量等异常。
以下实现中,函数遍历传入的变量数组,把键名加工成{{键}}形态。若某个占位符在语言文件中存在但调用时未提供变量,可选择保留原样或记录日志,这里采用保留原样以便排查。
<?php
function trans($key, $vars = []) {
static $lang = null;
if ($lang === null) {
$json = file_get_contents('zh-CN.json');
$lang = json_decode($json, true);
}
if (!isset($lang[$key])) {
return $key;
}
$map = [];
foreach ($vars as $k => $v) {
$map['{{' . $k . '}}'] = $v;
}
return strtr($lang[$key], $map);
}
echo trans('farewell', ['username' => '李四']);
// 输出:再见,李四,期待再次相见。
?>
四、与sprintf方案的对比
有些开发者习惯用sprintf和%s占位符处理字符串,但在JSON语言包场景下并不推荐。首先,sprintf要求占位符顺序固定,翻译人员调整文案时若变换参数位置,PHP代码就得同步修改;其次,若变量值里本身含有百分号,还会引发格式化错误。
而双大括号方案对翻译友好,语序自由,且strtr不做任何格式解析,值里包含什么符号都不会报错。下表简要对比两者在JSON语言文件中的表现。
| 维度 | strtr+双大括号 | sprintf+%s |
|---|---|---|
| 翻译调序 | 无需改代码 | 必须改参数顺序 |
| 特殊字符 | 原样替换安全 | 百分号需转义 |
| 可读性 | 占位符语义清晰 | 仅位置含义 |
五、常见误区与注意事项
一个容易踩的坑是直接在JSON里写PHP变量如$username,然后试图用eval执行,这会带来严重安全问题且破坏数据结构。语言文件应只存纯文本,所有动态性由PHP在运行时注入。
另外,若文案中包含字面意义的双大括号而非占位符,替换时会误伤。此时可约定使用更少见的标记如[[[key]]],或在替换前用正则精确匹配{{字母数字}}形式。总之,明确约定占位符规则并统一落地,才能让PHP动态替换JSON语言文件稳定可控。