gettext能运行的前提是环境对齐:需确认系统locale存在且setlocale()返回true、bindtextdomain路径正确指向.mo父目录、lc_messages目录结构严格匹配、.mo为utf-8编码,apache/php-fpm需重启生效;否则应改用php-gettext纯php方案。

别从“学国际化”开始,先确认你当前项目能不能跑通 gettext() —— 90% 的 PHP 国际化卡点不在代码,而在环境没对齐。
setlocale() 返回 false 怎么办
这是最常遇到的拦路虎。不是函数写错了,是系统压根不认你传的 locale 字符串。
- Linux 上运行
locale -a | grep 'en_US.utf8',Mac 上用locale -a | grep 'en_US.UTF-8',确认目标 locale 确实存在 -
setlocale(LC_MESSAGES, 'en_US.UTF-8')必须检查返回值:if (false === setlocale(LC_MESSAGES, 'en_US.UTF-8')) { die('locale not available'); } - Docker 容器默认 locale 是空的,得在
Dockerfile里加RUN locale-gen en_US.UTF-8 && update-locale - Apache 下改完 locale 要
sudo systemctl restart apache2;PHP-FPM 则需sudo systemctl reload php*-fpm,否则缓存不刷新
bindtextdomain() 路径为什么总找不到 .mo 文件
路径错一个层级、大小写差一点、LC_MESSAGES 拼错,gettext() 就静默失败,不报错也不翻译。
- 目录结构必须严格为:
/path/to/locales/zh_CN/LC_MESSAGES/messages.mo(注意LC_MESSAGES全大写、固定名) -
bindtextdomain('messages', '/var/www/myapp/locale')的第二个参数是父目录,不是 .mo 所在目录,更不能是 .mo 文件路径 - .mo 文件编码必须是 UTF-8,用
file messages.mo检查,非 UTF-8 会导致乱码或完全不加载 - 开发时建议用 Poedit 编译 .mo,避免手写 msgfmt 命令出错;编译后用
msgunfmt messages.mo反向验证内容是否正常
为什么 _('Hello') 没反应,还是输出英文
gettext 不是“设一次就全局生效”,它依赖三个调用顺序和作用域,漏一步就白配。
- 必须每请求都重设:先
setlocale(),再bindtextdomain(),最后textdomain();不能只在入口文件顶部执行一次 -
textdomain('messages')的参数要和bindtextdomain()第一个参数一致,且和 .mo 文件名前缀一致(如messages.mo) - 确保 PHP 启用了 gettext 扩展:
php -m | grep gettext,Ubuntu 装php-gettext,Alpine 装php7-gettext - 不要在
_()里拼接变量:_("Hello " . $name)会破坏词条提取,应改用占位符 +sprintf()或框架的上下文替换机制
不想碰系统 locale 怎么办
共享主机、精简 Docker 镜像、CI 环境里装不了 locale —— 这时候硬上 gettext() 只会浪费时间。
- 用
composer require php-gettext/gettext,纯 PHP 实现,不依赖系统库 - 导出 .po 为 PHP 数组(Poedit → Export → PHP Array),比读 .mo 更快、无编码风险
- 手动加载:
$t = \Gettext\Translations::fromPhpArray($array); echo $t->translate('Welcome'); - 注意:它不支持复数自动判断(
ngettext()),$n === 1 ? 'item' : 'items'得自己写分支
真正难的不是写 _(...),而是让所有环境(本地、测试、生产、Docker、CI)的 locale + 目录结构 + 扩展状态全部对齐;一旦某个环节是“差不多”,gettext 就会沉默失效,连 warning 都不抛。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











