symfony 4 可手动接入 gettext,需启用 php gettext 扩展、安装 msgfmt 工具并遵循 locale/zh_cn/lc_messages/messages.{po,mo} 目录结构,通过 setlocale、bindtextdomain、textdomain 及 _() 函数调用,建议封装为服务并与 twig 自定义函数集成。

Symfony 4 本身不原生集成 gettext,但可以手动接入,前提是系统已启用 PHP 的 gettext 扩展,并配合标准的 .po/.mo 文件流程。这不是 Symfony 官方推荐路径(官方主推 symfony/translation + XLIFF/YAML),但对已有 gettext 项目、或追求 POSIX 标准兼容与编译后性能的场景仍具实用价值。
确认环境支持 gettext
必须确保运行环境满足以下三项:
- PHP 已加载
gettext扩展:检查php -m | grep gettext或extension=gettext在php.ini中已启用(PHP 8.0+ 需编译时包含) - 系统安装了 GNU
msgfmt工具:用于将.po编译为.mo,执行msgfmt --version验证 - 项目目录结构符合 gettext 约定:通常为
locale/zh_CN/LC_MESSAGES/messages.mo,其中zh_CN是 locale 名,messages是 textdomain
准备 PO/MO 翻译资源
在项目根目录下创建 locale/ 目录,按标准层级组织:
locale/
├── en_US/
│ └── LC_MESSAGES/
│ └── messages.po
├── zh_CN/
│ └── LC_MESSAGES/
│ └── messages.po
└── fr_FR/
└── LC_MESSAGES/
└── messages.po
每个 .po 文件内容示例如下(locale/zh_CN/LC_MESSAGES/messages.po):
msgid "Welcome" msgstr "欢迎" msgid "Edit profile" msgstr "编辑个人资料"
用 msgfmt 编译为二进制 .mo:
msgfmt locale/zh_CN/LC_MESSAGES/messages.po -o locale/zh_CN/LC_MESSAGES/messages.mo
在 Symfony 4 中调用 gettext
不能直接依赖 Symfony 的 Translator 服务——gettext 是独立的 PHP 内置函数族。需在控制器、模板或服务中显式设置 locale 并调用:
- 在控制器方法开头设置当前语言环境:
setlocale(LC_ALL, 'zh_CN.UTF-8'); - 绑定文本域路径:
bindtextdomain('messages', __DIR__.'/../../locale');
(注意路径需指向locale/上级,即bindtextdomain的第二个参数是locale所在父目录) - 激活文本域:
textdomain('messages'); - 使用
_()或gettext()输出翻译:echo _('Welcome'); // 输出“欢迎”
⚠️ 注意:每次请求需重新调用 setlocale() 和 bindtextdomain(),建议封装成一个 GettextTranslator 服务,注入 RequestStack 动态读取 _locale 参数。
与 Symfony 路由和 Twig 协同
若已配置带 locale 前缀的路由(如 /zh_CN/about),可在控制器中提取并设置:
$locale = $request->get('_locale', 'en_US');
setlocale(LC_ALL, $locale.'UTF-8');
bindtextdomain('messages', $this->getParameter('kernel.project_dir').'/locale');
textdomain('messages');
Twig 模板中不能直接用 {{ 'Welcome'|trans }}(那是 Symfony Translator 的语法)。需在模板中传入已翻译的字符串,或注册自定义 Twig 函数:
// 在 Twig 扩展中添加
public function getFunctions(): array
{
return [
new TwigFunction('_', [$this, 'gettext']),
];
}
public function gettext(string $msgid): string { return _($msgid); }
之后模板可写:{{ _('Welcome') }} —— 但要注意此方式绕过 Symfony 的缓存与域管理机制。











