一、多语言网站概述
多语言网站(也称为国际化网站)是指能够根据用户的语言偏好提供不同语言版本的网站。ThinkPHP提供了完善的多语言支持,包括:
- 语言包管理
- 自动语言检测
- 手动语言切换
- 模板多语言支持
- 数据库内容国际化
二、环境准备与配置
首先创建ThinkPHP项目并配置多语言支持:
composer create-project topthink/think tp-multilang
配置多语言支持(config/lang.php):
return [ // 默认语言 'default_lang' => 'zh-cn', // 允许的语言列表 'allow_lang_list' => ['zh-cn', 'en-us', 'ja-jp'], // 自动侦测浏览器语言 'auto_detect' => true, // 使用Cookie记录语言 'use_cookie' => true, // 扩展语言包 'extend_list' => [], ];
三、创建语言包
ThinkPHP的语言包文件位于app/lang目录下,按语言分目录存放:
1. 中文语言包(app/lang/zh-cn.php)
return [ // 公共语言 'welcome' => '欢迎访问我们的网站', 'home' => '首页', 'about' => '关于我们', 'contact' => '联系我们', // 首页语言 'index_title' => '首页 - 多语言演示', 'index_content' => '这是一个支持多语言的网站演示', // 用户相关 'login' => '登录', 'register' => '注册', 'logout' => '退出', ];
2. 英文语言包(app/lang/en-us.php)
return [ // Common 'welcome' => 'Welcome to our website', 'home' => 'Home', 'about' => 'About Us', 'contact' => 'Contact Us', // Index page 'index_title' => 'Home - Multilingual Demo', 'index_content' => 'This is a multilingual website demo', // User related 'login' => 'Login', 'register' => 'Register', 'logout' => 'Logout', ];
3. 日文语言包(app/lang/ja-jp.php)
return [ // 共通 'welcome' => '当サイトへようこそ', 'home' => 'ホーム', 'about' => '会社概要', 'contact' => 'お問い合わせ', // トップページ 'index_title' => 'ホーム - 多言語デモ', 'index_content' => 'これは多言語対応のウェブサイトデモです', // ユーザー関連 'login' => 'ログイン', 'register' => '会員登録', 'logout' => 'ログアウト', ];
四、在控制器中使用多语言
在控制器中可以使用Lang类来获取语言文本:
// app/controller/Index.php namespace appcontroller; use thinkfacadeLang; use thinkfacadeView; class Index { public function index() { // 获取语言文本 $welcome = Lang::get('welcome'); $title = Lang::get('index_title'); // 传递到视图 View::assign([ 'title' => $title, 'welcome' => $welcome, 'content' => Lang::get('index_content') ]); return View::fetch('index'); } }
五、在模板中使用多语言
ThinkPHP模板支持多语言函数,可以直接在模板中使用:
<!-- view/index.html --> <!DOCTYPE html> <html lang="{:app('lang')}"> <head> <meta charset="UTF-8"> <title>{$title}</title> </head> <body> <header> <h1>{:lang('welcome')}</h1> <nav> <a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" >{:lang('home')}</a> <a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" >{:lang('about')}</a> <a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" >{:lang('contact')}</a> </nav> </header> <main> <p>{$content}</p> </main> <footer> <div class="language-switcher"> <a href="?lang=zh-cn" rel="external nofollow" >中文</a> <a href="?lang=en-us" rel="external nofollow" >English</a> <a href="?lang=ja-jp" rel="external nofollow" >日本語</a> </div> </footer> </body> </html>
六、实现语言切换功能
创建中间件处理语言切换:
// app/middleware/Lang.php namespace appmiddleware; use thinkfacadeLang; use thinkfacadeCookie; class Lang { public function handle($request, Closure $next) { // 获取语言参数 $lang = $request->param('lang'); // 如果URL中有语言参数 if ($lang && in_array($lang, config('lang.allow_lang_list'))) { // 设置当前语言 Lang::setLangSet($lang); // 保存到Cookie Cookie::set('think_lang', $lang, 3600 * 24 * 30); } else { // 从Cookie获取语言 $cookieLang = Cookie::get('think_lang'); if ($cookieLang && in_array($cookieLang, config('lang.allow_lang_list'))) { Lang::setLangSet($cookieLang); } } return $next($request); } }
注册中间件(app/middleware.php):
return [ // 全局中间件 appmiddlewareLang::class ];
七、数据库内容国际化
对于数据库中的动态内容,我们可以采用以下方法实现国际化:
1. 多表关联方案
创建内容表和翻译表:
// 产品表(products) CREATE TABLE products ( id INT PRIMARY KEY AUTO_INCREMENT, image VARCHAR(255), price DECIMAL(10,2), created_at DATETIME ); // 产品翻译表(product_translations) CREATE TABLE product_translations ( id INT PRIMARY KEY AUTO_INCREMENT, product_id INT, lang VARCHAR(10), name VARCHAR(255), description TEXT, FOREIGN KEY (product_id) REFERENCES products(id) );
2. 在模型中处理多语言内容
// app/model/Product.php namespace appmodel; use thinkModel; class Product extends Model { // 定义关联 public function translations() { return $this->hasMany(ProductTranslation::class); } // 获取当前语言的产品信息 public function getLocalized($lang = null) { $lang = $lang ?: app('lang'); // 获取翻译 $translation = $this->translations() ->where('lang', $lang) ->find(); if ($translation) { return [ 'id' => $this->id, 'name' => $translation->name, 'description' => $translation->description, 'image' => $this->image, 'price' => $this->price ]; } // 没有翻译时返回默认语言 $default = $this->translations() ->where('lang', config('lang.default_lang')) ->find(); return [ 'id' => $this->id, 'name' => $default->name, 'description' => $default->description, 'image' => $this->image, 'price' => $this->price ]; } }
3. 在控制器中使用
// app/controller/Product.php public function index() { $products = Product::select(); $localizedProducts = []; foreach ($products as $product) { $localizedProducts[] = $product->getLocalized(); } View::assign('products', $localizedProducts); return View::fetch('product/index'); }
八、多语言路由配置
ThinkPHP支持多语言路由,可以为不同语言创建不同的URL:
// config/route.php use thinkfacadeRoute; // 多语言路由 Route::rule(':lang/index', 'index/index') ->pattern(['lang' => 'zh-cn|en-us|ja-jp']); Route::rule(':lang/product/:id', 'product/index') ->pattern(['lang' => 'zh-cn|en-us|ja-jp', 'id' => 'd+']); // 默认路由(不带语言前缀) Route::rule('index', 'index/index');
九、最佳实践与常见问题
1. 最佳实践
- 使用语言常量代替硬编码文本
- 为所有用户可见的文本添加语言项
- 组织语言包按模块划分(如:app/lang/zh-cn/user.php)
- 提供默认语言回退机制
- 考虑语言方向(RTL/LTR)
2. 常见问题解决
问题1:语言包未生效
- 检查文件名和路径是否正确
- 确认中间件已注册
- 检查配置中的allow_lang_list
问题2:数据库内容翻译缺失
- 实现默认语言回退机制
- 添加翻译内容完整性检查
问题3:语言切换后URL不一致
- 使用统一的路由中间件处理语言参数
- 在模板中使用多语言路由函数
十、完整示例:多语言产品列表
<!-- view/product/index.html --> <div class="product-list"> <h2>{:lang('product_list')}</h2> <div class="products"> <?php foreach($products as $product): ?> <div class="product"> <img src="{$product.image}" alt="{$product.name}"> <h3>{$product.name}</h3> <p class="description">{$product.description}</p> <p class="price">{:lang('price')}: {$product.price}</p> </div> <?php endforeach; ?> </div> </div>
多语言产品列表效果
智能手机
高端配置,超长续航
价格: ¥3999.00
项目结构总结
tp-multilang/ ├─ app/ │ ├─ controller/ │ │ ├─ Index.php │ │ ├─ Product.php │ ├─ model/ │ │ ├─ Product.php │ │ ├─ ProductTranslation.php │ ├─ middleware/ │ │ ├─ Lang.php │ ├─ lang/ │ │ ├─ zh-cn.php │ │ ├─ en-us.php │ │ ├─ ja-jp.php ├─ config/ │ ├─ lang.php │ ├─ middleware.php │ ├─ route.php ├─ view/ │ ├─ index.html │ ├─ product/ │ │ ├─ index.html
// 前端语言切换演示
function switchLang(lang) {
const translations = {
‘zh-cn’: {
name: ‘智能手机’,
desc: ‘高端配置,超长续航’,
price: ‘价格: ¥3999.00’
},
‘en-us’: {
name: ‘Smartphone’,
desc: ‘High-end configuration, long battery life’,
price: ‘Price: $599.00’
},
‘ja-jp’: {
name: ‘スマートフォン’,
desc: ‘ハイエンド構成、長いバッテリー寿命’,
price: ‘価格: ¥65,800’
}
};
document.getElementById(‘product-name’).textContent = translations[lang].name;
document.getElementById(‘product-desc’).textContent = translations[lang].desc;
document.getElementById(‘product-price’).textContent = translations[lang].price;
}