本文目录导读:

在 PHP 中进行本地化(L10n,Localization)有几种不同的方式,从简单的内置函数到完整的框架解决方案,以下是详细的指南:
使用 gettext 扩展(最常用)
安装和启用
# Ubuntu/Debian sudo apt-get install php-gettext php-intl # CentOS/RHEL sudo yum install php-gettext php-intl
基本用法
<?php
// 设置语言环境
$locale = 'zh_CN.UTF-8'; // 中文
// $locale = 'en_US.UTF-8'; // 英文
putenv("LC_ALL=$locale");
setlocale(LC_ALL, $locale);
// 指定文本域(domain)
$domain = 'messages';
bindtextdomain($domain, './locale');
bind_textdomain_codeset($domain, 'UTF-8');
textdomain($domain);
// 翻译字符串
echo gettext("Hello World");
// 或简写
echo _("Hello World");
// 复数形式
echo ngettext("item", "items", $count);
?>
创建翻译文件
-
创建 POT 文件(模板文件):
xgettext -n -o messages.pot *.php
-
生成 PO 文件(每种语言一个):
msginit -i messages.pot -o locale/zh_CN/LC_MESSAGES/messages.po -l zh_CN.UTF-8
-
翻译 PO 文件,然后编译为 MO 文件:
msgfmt messages.po -o messages.mo
使用数组/JSON 文件(简单方案)
语言文件结构
// lang/en.php
return [
'welcome' => 'Welcome to our site',
'goodbye' => 'Goodbye',
'items' => 'You have {count} items',
];
// lang/zh.php
return [
'welcome' => '欢迎来到我们的网站',
'goodbye' => '再见',
'items' => '您有 {count} 个项目',
];
加载和使用
<?php
class Translator {
private $translations = [];
public function __construct($locale) {
$file = __DIR__ . "/lang/{$locale}.php";
if (file_exists($file)) {
$this->translations = require $file;
}
}
public function translate($key, $params = []) {
$text = $this->translations[$key] ?? $key;
// 替换参数
foreach ($params as $param => $value) {
$text = str_replace("{{$param}}", $value, $text);
}
return $text;
}
}
// 使用
$t = new Translator('zh');
echo $t->translate('welcome');
echo $t->translate('items', ['count' => 5]);
?>
使用国际化框架
Symfony Translation 组件
composer require symfony/translation
<?php
use Symfony\Component\Translation\Translator;
use Symfony\Component\Translation\Loader\ArrayLoader;
use Symfony\Component\Translation\Loader\YamlFileLoader;
$translator = new Translator('zh');
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', [
'hello' => '你好',
'items' => '%count% 个项目',
], 'zh');
echo $translator->trans('hello');
echo $translator->trans('items', ['%count%' => 3]);
?>
Laravel 的本地化
// resources/lang/zh/messages.php
return [
'welcome' => '欢迎',
];
// 使用
echo trans('messages.welcome');
// 或
echo __('messages.welcome');
// 带参数
echo trans('messages.welcome_user', ['name' => '张三']);
// 复数
echo trans_choice('messages.apples', 10);
自动检测语言
<?php
function detectLocale() {
// 1. 检查 session
if (isset($_SESSION['locale'])) {
return $_SESSION['locale'];
}
// 2. 检查 cookie
if (isset($_COOKIE['locale'])) {
return $_COOKIE['locale'];
}
// 3. 从浏览器头检测
if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
$languages = array_map('trim', explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']));
foreach ($languages as $lang) {
$lang = substr($lang, 0, 2);
if (in_array($lang, ['zh', 'en', 'ja', 'ko'])) {
return $lang;
}
}
}
// 4. 默认语言
return 'zh';
}
// 切换语言
function setLocale($locale) {
$_SESSION['locale'] = $locale;
setcookie('locale', $locale, time() + 3600 * 24 * 30);
}
?>
最佳实践建议
完整示例类
<?php
class I18n {
private static $instance = null;
private $locale = 'en';
private $translator;
private $translations = [];
private function __construct() {}
public static function getInstance() {
if (self::$instance === null) {
self::$instance = new self();
self::$instance->init();
}
return self::$instance;
}
private function init() {
$this->locale = $this->detectLocale();
// 选择翻译方式
if (extension_loaded('gettext')) {
$this->initGettext();
} else {
$this->loadArrays();
}
}
private function detectLocale() {
// 实现语言检测逻辑
return 'zh';
}
private function initGettext() {
$locale = $this->locale . '.UTF-8';
putenv("LC_ALL=$locale");
setlocale(LC_ALL, $locale);
bindtextdomain('messages', __DIR__ . '/locale');
bind_textdomain_codeset('messages', 'UTF-8');
textdomain('messages');
}
private function loadArrays() {
$file = __DIR__ . "/lang/{$this->locale}.php";
if (file_exists($file)) {
$this->translations = require $file;
}
}
public function translate($key, $params = []) {
if (extension_loaded('gettext')) {
$text = gettext($key);
} else {
$text = $this->translations[$key] ?? $key;
}
foreach ($params as $param => $value) {
$text = str_replace("{{$param}}", $value, $text);
}
return $text;
}
public function locale() {
return $this->locale;
}
}
// 使用
I18n::getInstance()->translate('welcome');
?>
目录结构参考
project/
├── lang/
│ ├── en.php
│ ├── zh.php
│ └── ja.php
├── locale/
│ ├── en_US/
│ │ └── LC_MESSAGES/
│ │ └── messages.mo
│ └── zh_CN/
│ └── LC_MESSAGES/
│ └── messages.mo
├── scripts/
│ ├── update-po.sh
│ └── compile-mo.sh
└── src/
└── I18n.php
选择哪种方案取决于:
- 项目规模:大项目推荐 gettext 或框架方案
- 部署环境:检查 gettext 扩展是否可用
- 翻译频率:需要频繁修改时使用数组方案更方便
- 团队工作流:考虑使用 Poedit 或 Weblate 等工具