在PHP项目里做多语言支持,本质上就是把界面文案从代码里抽出来,再根据用户语言偏好动态替换。不同架构规模下,实现思路差异很大,选错方案会导致后期翻译文件混乱、性能下降。下面汇总三种主流做法,并给出可直接落地的代码。

一、数组常量方案:最轻量的自研实现
小型网站或内部系统常直接用PHP数组存文案。每种语言一个文件,返回键值对,运行时根据会话里的语言标识引入对应文件。这种做法零依赖,任何环境都能跑。
目录结构可以很简单,例如在 lang/ 下放 zh_CN.php 和 en_US.php,每个文件返回数组。切换时仅需改 $_SESSION['lang']。下面是一段示例:
<?php
// lang/zh_CN.php
return [
'welcome' => '欢迎访问',
'logout' => '退出登录',
];
// lang/en_US.php
return [
'welcome' => 'Welcome',
'logout' => 'Logout',
];
// 使用方式
session_start();
$lang = $_SESSION['lang'] ?? 'zh_CN';
$messages = require __DIR__ . "/lang/{$lang}.php";
echo $messages['welcome'];
优点是实现直观,新人也能秒懂;缺点是翻译散落在多个数组里,没有复数、上下文等专业处理。当语言超过三种、文案过千条时,很容易出现漏翻或键名不一致。
另外,该方案每次请求都要 require 整个语言文件,若文件很大,会稍微拖慢响应。可通过OPcache缓解,但不如编译型方案高效。
二、gettext扩展:PHP原生的国际化标准
gettext是GNU推出的国际化工具,PHP通过 gettext 扩展原生支持。它用 .po 存源码、.mo 存编译二进制,由专业翻译工具编辑,性能和规范性都更好。
常见误区是以为gettext只能在Linux用。其实Windows下开启扩展后,把语言目录按 locale/LC_MESSAGES/domain.mo 放好即可。代码里用 _() 或 gettext() 取词:
<?php
// 设置语言环境
putenv('LANG=en_US.UTF-8');
setlocale(LC_ALL, 'en_US.UTF-8');
bindtextdomain('messages', __DIR__ . '/locale');
textdomain('messages');
// 输出翻译,未命中则返回原文
echo gettext('Welcome');
echo _('Logout');
gettext支持复数规则(如英文单复数、俄文三形式),也支持上下文区分同形异义词。配合Poedit这类工具,翻译流程非常规范,适合中大型产品。
它的短板在于部署略复杂,要生成mo文件且注意权限;另外部分共享主机可能未装扩展。但从架构清晰度看,它是将代码与文案彻底解耦的最佳原生方案。
三、框架内置语言包:以Laravel为例
现代PHP框架大多自带多语言组件。Laravel用 resources/lang/ 下的PHP或JSON文件,配合 app()->setLocale() 和中间件自动识别。
下面用中间件根据请求头切换语言,并在模板里用 __() 函数取词:
<?php
// 中间件 Localization.php
public function handle($request, $next) {
$locale = $request->segment(1);
if (in_array($locale, ['en', 'zh'])) {
app()->setLocale($locale);
}
return $next($request);
}
// resources/lang/en/auth.php
return ['login' => 'Login', 'register' => 'Register'];
// 控制器或模板中
echo __('auth.login');
框架方案胜在开发体验,语言包可嵌套、支持替换参数,还有第三方包做数据库驱动翻译。缺点是与框架强绑定,脱离框架就无法复用。
对于已经用Laravel、Symfony的项目,直接用内置组件最省心;若是裸写PHP,引入整套框架只为多语言则不划算。
四、方案对比与选型建议
从维护成本、性能、专业性三个维度看,三者定位不同。下表给出直观比较:
| 方案 | 适用规模 | 专业翻译支持 | 部署难度 |
|---|---|---|---|
| 数组常量 | 小型/原型 | 弱 | 极低 |
| gettext | 中大型 | 强 | 中 |
| 框架语言包 | 框架项目 | 中 | 低 |
如果项目文案少、上线急,数组常量最稳;若面向多国长期运营,gettext在翻译管理和运行效率上优势明显;框架内则不必重复造轮子。
实际架构中也可混合:用gettext做核心文案,数组做动态提示,只要封装统一取词接口,就不会让调用方感知差异。多语言不是功能点缀,而是早期就该规划的基础架构。