.mo 二進制格式,查找極快(雜湊表/O(1))。xgettext 提取、msgmerge 合併、msgfmt 編譯、poedit 圖形化編輯。# 系統庫
apt-get install -y gettext libgettextpo-dev
# PHP 擴展(通常內建,檢查 php -m | grep gettext)
# 若無:docker-php-ext-install gettext 或 pecl install gettext
# 重啟 PHP-FPM
systemctl reload php8.2-fpm
php_gettext.dll(php.ini 取消註釋 extension=gettext)。libintl.dll 放入 PATH(PHP 目錄或 System32)。<?php phpinfo(); ?>
// 搜尋 "gettext" 區塊,顯示版本即成功
| 函數 | 用途 | 範例 |
|---|---|---|
_($msg) / gettext($msg) | 單數翻譯 | echo _('Hello World'); |
ngettext($single, $plural, $count) | 複數形式 | printf(ngettext('%d apple', '%d apples', $n), $n); |
pgettext($context, $msg) | 上下文區分 | echo pgettext('verb', 'View'); // 動詞 vs pgettext('noun', 'View'); // 名詞 |
慣例:function __($msg) { return gettext($msg); } 簡寫,配合 IDE 自動提取。
<?php
// bootstrap.php 或公共入口
$locale = $_GET['lang'] ?? 'zh_TW';
putenv("LANG=$locale");
setlocale(LC_ALL, $locale);
// 綁定域 -> 目錄
bindtextdomain('messages', __DIR__ . '/locale');
bind_textdomain_codeset('messages', 'UTF-8'); // 關鍵:避免亂碼
textdomain('messages');
目錄結構:
locale/
├── zh_TW/
│ └── LC_MESSAGES/
│ ├── messages.po
│ └── messages.mo
├── en_US/
│ └── LC_MESSAGES/
│ ├── messages.po
│ └── messages.mo
└── ja_JP/
└── LC_MESSAGES/
├── messages.po
└── messages.mo
xgettext --language=PHP \
--keyword=_ \
--keyword=__ \
--keyword=ngettext:1,2 \
--keyword=pgettext:1c,2 \
--from-code=UTF-8 \
--output=locale/messages.pot \
-r src/
--keyword 定義提取函數名,支援自定義封裝函數。--keyword=ngettext:1,2 表示第 1、2 參數為單複數字串。--keyword=pgettext:1c,2 表示第 1 參數為 context,第 2 為字串。msginit --locale=zh_TW --input=locale/messages.pot --output=locale/zh_TW/LC_MESSAGES/messages.po
# messages.po 片段
msgid "Hello World"
msgstr "你好,世界"
msgid "%d apple"
msgid_plural "%d apples"
msgstr[0] "%d 蘋果"
msgstr[1] "%d 蘋果" # 中文無複數變化,複製即可
msgfmt locale/zh_TW/LC_MESSAGES/messages.po -o locale/zh_TW/LC_MESSAGES/messages.mo
# 重新提取
xgettext ... -o locale/messages.pot.new
# 合併保留舊翻譯
msgmerge --update locale/zh_TW/LC_MESSAGES/messages.po locale/messages.pot.new
# 重新編譯
msgfmt locale/zh_TW/LC_MESSAGES/messages.po -o locale/zh_TW/LC_MESSAGES/messages.mo
.po 標頭需宣告 Plural-Forms:
# 中文
"Plural-Forms: nplurals=1; plural=0;\n"
# 英文
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
# 俄文 (3 種)
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
| 框架 | 整合方式 |
|---|---|
| Laravel | __()、__n() 封裝 gettext,語言檔放 lang/{locale}/,php artisan lang:publish |
| Symfony | Translation 組件原生支援 gettext,translator.add_resource('gettext', ...) |
| Yii2 | Yii::t() 支援 GettextMessageSource |
| WordPress | __()、_n()、_x(),語言包放 wp-content/languages/plugins/ |
opcache_reset(),否則舊快取生效。bind_textdomain_codeset('messages', 'UTF-8') 少不了。admin、frontend、api)。apt-get install -y locales && locale-gen zh_TW.UTF-8 en_US.UTF-8。| 方案 | 優點 | 缺點 | 適用 |
|---|---|---|---|
| gettext | 標準、快、工具成熟、支援複數/上下文 | 需編譯、部署稍繁瑣 | 中大型專案、多語言、長期維護 |
| PHP 陣列回傳 | 簡單、無編譯、熱重載 | 記憶體佔大、無複數支援、難合併 | 小專案、少語言、快速原型 |
| JSON 檔案 | 前後端共用、易解析 | 無標準複數、無上下文 | SPA 專案、前端主導翻譯 |
| 資料庫儲存 | 動態管理、後台編輯 | 查詢慢、需快取、一致性複雜 | SaaS 多租戶、用戶自定義語言 |
原文《利用gettext來實現PHP的國際化編程》重構,補充完整工具鏈流程、複數形式、框架整合、效能優化與替代方案對比。