gettext易失败主因是强依赖系统locale设置:若系统未安装zh_CN.UTF-8等locale、路径错误或.mo结构不符,会导致setlocale()或bindtextdomain()失败;可控环境且需复数/长期维护选gettext,否则数组方案更省事。

直接用 gettext 扩展是最稳妥的选择,前提是服务器环境可控、能装扩展、且项目需要长期维护多语言——否则数组语言包更省事,别硬上 gettext。
为什么 gettext 容易失败?
它不只依赖 PHP 扩展,还强依赖系统级 locale 设置。常见报错如 bindtextdomain(): Unable to bind text domain 或 setlocale(): Invalid locale,基本都卡在这儿。
-
setlocale(LC_ALL, 'zh_CN.UTF-8')失败,大概率是系统根本没装这个 locale,得先运行locale -a | grep zh_CN确认存在;Ubuntu/Debian 要sudo locale-gen zh_CN.UTF-8,CentOS 要改/etc/locale.conf -
bindtextdomain('messages', './locale')的路径必须是绝对路径或相对于当前工作目录的正确路径,./locale在 CLI 下和 Web 下的“当前目录”可能不同,建议用__DIR__ . '/locale' -
.mo文件权限要可读,且目录结构必须严格为locale/zh_CN/LC_MESSAGES/messages.mo,少一层或名字不对就加载失败
gettext 和数组语言包怎么选?
看三点:部署环境、语言数量、是否需要复数/日期格式化。
- 共享主机或 Docker 里没法改系统 locale?→ 别碰
gettext,用数组语言包 - 只支持中/英两种语言,翻译条目不到 200 条?→ 数组文件(
lang/zh.php、lang/en.php)加一个trans($key)函数足够 - 要处理 “您有
{count}条消息” 这类带变量、或英文里 “1 message / 2 messages” 这种复数?→ 必须用intl扩展 +MessageFormatter,gettext原生支持复数但配置麻烦,数组方案得自己写逻辑
语言切换怎么保持状态?
URL 参数最简单,但别只靠它。用户点一次“中文”,下次打开还是英文,体验就断了。
立即学习“PHP免费学习笔记(深入)”;
- 优先写入
$_SESSION['lang']:在语言切换入口处session_start(); $_SESSION['lang'] = 'zh'; - 再 fallback 到
$_COOKIE['lang']:设置时加setcookie('lang', 'zh', time() + 3600*24*30, '/', '', false, true) - 最后才解析
$_SERVER['HTTP_ACCEPT_LANGUAGE']:它返回类似zh-CN,zh;q=0.9,en;q=0.8,需用explode()+substr()提取前缀,且只匹配你实际支持的语言列表(比如['zh', 'en']),别无脑用zh-CN
真正难的不是写翻译函数,而是让所有模板里的文字都过一遍翻译层,且漏掉的字符串要有兜底(比如返回原 key 或默认语言值)。这点数组方案容易做,gettext 默认遇到未翻译 key 就输出原文,但你得确保 .po 文件持续更新,否则上线后才发现“提交”按钮没翻,就得重跑 xgettext + 人工补译。



















