PHP项目多时区与本地化

wen PHP项目 4

本文目录导读:

PHP项目多时区与本地化

  1. 时区处理
  2. 国际化/本地化
  3. 多语言支持
  4. 中间件处理
  5. 数据库存储策略
  6. 前端本地化集成
  7. 完整示例
  8. 最佳实践建议

在PHP项目中处理多时区和本地化是一个常见且重要的需求,以下是一个全面的解决方案指南:

时区处理

1 基础配置

// 在项目启动时设置默认时区
date_default_timezone_set('UTC');
// 或在php.ini中配置
// date.timezone = UTC
// 使用Carbon库(推荐)
use Carbon\Carbon;
// 设置默认时区
Carbon::setToStringFormat('Y-m-d H:i:s');

2 用户时区处理

class TimezoneManager {
    private $userTimezone;
    public function __construct($userTimezone = 'Asia/Shanghai') {
        $this->userTimezone = $userTimezone;
    }
    // 存储时转换为UTC
    public function toUTC($datetime) {
        return Carbon::parse($datetime, $this->userTimezone)
                     ->setTimezone('UTC')
                     ->format('Y-m-d H:i:s');
    }
    // 显示时转换为用户时区
    public function toUserTimezone($utcDatetime) {
        return Carbon::parse($utcDatetime, 'UTC')
                     ->setTimezone($this->userTimezone)
                     ->format('Y-m-d H:i:s');
    }
    // 获取所有支持的时区
    public static function getTimezoneList() {
        return DateTimeZone::listIdentifiers();
    }
}

3 检测用户时区

// 通过JavaScript获取用户时区
// 在前端发送时区信息
?>
<script>
// 获取用户时区
fetch('/api/set-timezone', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({
        timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
        offset: new Date().getTimezoneOffset()
    })
});
</script>
<?php
// 后端存储用户时区
class UserController {
    public function setTimezone(Request $request) {
        $user = auth()->user();
        $user->timezone = $request->timezone;
        $user->save();
        // 将时区存入Session
        session(['user_timezone' => $request->timezone]);
    }
}

国际化/本地化

1 使用gettext扩展

// 初始化本地化
class Localization {
    public static function init($locale = 'zh_CN') {
        // 设置语言环境
        putenv("LANG=$locale");
        setlocale(LC_ALL, $locale);
        // 设置文本域
        bindtextdomain('messages', __DIR__ . '/locales');
        bind_textdomain_codeset('messages', 'UTF-8');
        textdomain('messages');
    }
    // 翻译函数
    public static function t($text) {
        return gettext($text);
    }
}
// 使用示例
Localization::init('zh_CN');
echo Localization::t('Hello World');

2 使用Symfony Translation组件

use Symfony\Component\Translation\Translator;
use Symfony\Component\Translation\Loader\ArrayLoader;
class TranslationManager {
    private $translator;
    public function __construct($locale = 'zh_CN') {
        $this->translator = new Translator($locale);
        // 加载翻译文件
        $this->translator->addLoader('array', new ArrayLoader());
        // 添加中文翻译
        $this->translator->addResource('array', [
            'hello' => '你好',
            'welcome' => '欢迎来到 {name}',
        ], 'zh_CN');
        // 添加英文翻译
        $this->translator->addResource('array', [
            'hello' => 'Hello',
            'welcome' => 'Welcome to {name}',
        ], 'en_US');
    }
    public function trans($key, $params = []) {
        return $this->translator->trans($key, $params);
    }
    public function setLocale($locale) {
        $this->translator->setLocale($locale);
    }
}

3 日期和数字格式化

use NumberFormatter;
use IntlDateFormatter;
class FormattingService {
    public static function formatDate($datetime, $locale = 'zh_CN') {
        $formatter = new IntlDateFormatter(
            $locale,
            IntlDateFormatter::LONG,
            IntlDateFormatter::NONE
        );
        return $formatter->format(new DateTime($datetime));
    }
    public static function formatCurrency($amount, $currency = 'CNY', $locale = 'zh_CN') {
        $formatter = new NumberFormatter($locale, NumberFormatter::CURRENCY);
        return $formatter->formatCurrency($amount, $currency);
    }
    public static function formatNumber($number, $locale = 'zh_CN') {
        $formatter = new NumberFormatter($locale, NumberFormatter::DECIMAL);
        return $formatter->format($number);
    }
}

多语言支持

1 定义语言包

// app/Locales/zh_CN.php
return [
    'welcome' => '欢迎来到我们的网站',
    'login' => '登录',
    'logout' => '退出',
];
// app/Locales/en_US.php
return [
    'welcome' => 'Welcome to our website',
    'login' => 'Login',
    'logout' => 'Logout',
];

2 语言切换

class LocaleManager {
    private $defaultLocale = 'en';
    private $supportedLocales = ['en', 'zh', 'ja', 'ko'];
    public function setLocale($locale) {
        if (in_array($locale, $this->supportedLocales)) {
            session(['locale' => $locale]);
            app()->setLocale($locale);
        }
    }
    public function getLocale() {
        return session('locale', $this->defaultLocale);
    }
    public function getTranslations($locale = null) {
        $locale = $locale ?? $this->getLocale();
        $file = __DIR__ . "/Locales/{$locale}.php";
        return file_exists($file) ? require $file : [];
    }
}

中间件处理

use Closure;
class LocalizationMiddleware {
    public function handle($request, Closure $next) {
        // 从请求中获取语言
        $locale = $request->get('locale') ?? 
                  $request->cookie('locale') ?? 
                  session('locale') ?? 
                  $this->detectFromBrowser();
        // 设置应用语言
        app()->setLocale($locale);
        // 设置用户时区
        if ($userTimeZone = session('user_timezone')) {
            date_default_timezone_set($userTimeZone);
        }
        return $next($request);
    }
    private function detectFromBrowser() {
        $browserLang = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? 'en', 0, 2);
        return in_array($browserLang, ['zh', 'en', 'ja']) ? $browserLang : 'en';
    }
}

数据库存储策略

// 数据库表结构
CREATE TABLE `users` (
    `id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    `timezone` VARCHAR(50) DEFAULT 'UTC',
    `locale` VARCHAR(10) DEFAULT 'en',
    `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
// 统一存储UTC时间戳
class DateTimeHelper {
    public static function saveDateTime($datetime, $timezone = 'UTC') {
        return Carbon::parse($datetime, $timezone)
                     ->setTimezone('UTC')
                     ->format('Y-m-d H:i:s');
    }
    public static function displayDateTime($datetime, $format = 'Y-m-d H:i:s') {
        $userTimezone = session('user_timezone', 'UTC');
        return Carbon::parse($datetime, 'UTC')
                     ->setTimezone($userTimezone)
                     ->format($format);
    }
}

前端本地化集成

<!-- 在前端传递翻译 -->
<script>
    var i18n = {
        'hello': '<?php echo __('hello'); ?>',
        'date_format': 'Y-m-d',
    };
    // 日期格式化
    const formatDate = (date, timezone) => {
        return new Intl.DateTimeFormat('zh-CN', {
            timeZone: timezone,
            dateStyle: 'medium',
            timeStyle: 'short'
        }).format(new Date(date));
    };
</script>

完整示例

// 综合使用示例
class InternationalizationService {
    public function initialize() {
        // 1. 设置默认时区
        date_default_timezone_set('UTC');
        // 2. 初始化本地化
        $locale = session('locale', 'zh_CN');
        putenv("LANG=$locale");
        setlocale(LC_ALL, $locale);
        // 3. 设置翻译
        $translator = app('translator');
        $translator->setLocale($locale);
        // 4. 配置formatter
        $this->formatter = new IntlDateFormatter(
            $locale,
            IntlDateFormatter::MEDIUM,
            IntlDateFormatter::SHORT
        );
    }
    public function displayContent($content) {
        $userTimezone = session('user_timezone', 'Asia/Shanghai');
        return [
            'translated' => $this->translate($content),
            'localized_date' => $this->formatDate(
                $content->created_at,
                $userTimezone
            ),
            'localized_currency' => $this->formatCurrency(
                $content->price,
                $content->currency
            )
        ];
    }
}

最佳实践建议

  1. 统一存储UTC:数据库统一存储UTC时间
  2. 前端获取时区:首次访问时获取并存储用户时区
  3. 缓存翻译文件:生产环境缓存翻译文件以提高性能
  4. 使用Composer包:考虑使用Carbon, Symfony Translation等成熟库
  5. 测试多个时区:开发时测试不同时区边界情况
  6. 分发:对静态资源使用CDN并考虑地理位置

这个方案涵盖了PHP项目中多时区和本地化的核心需求,可以根据实际项目规模选择合适的实现方式。

抱歉,评论功能暂时关闭!