欢迎来到 Xiuno BBS

Xiuno BBS 重构记录贴(十)多语言系统文档

# Xiuno BBS 4.5重构版 多语言系统文档

## 目录

1. [架构概述](#1-架构概述)
2. [文件结构](#2-文件结构)
3. [核心 API](#3-核心-api)
4. [插件开发](#4-插件开发)
5. [模板开发](#5-模板开发)
6. [语言切换机制](#6-语言切换机制)
7. [后台多语言](#7-后台多语言)
8. [安装程序多语言](#8-安装程序多语言)
9. [同步检测工具](#9-同步检测工具)
10. [API 多语言支持](#10-api-多语言支持)
11. [翻译协作流程](#11-翻译协作流程)

---

## 1. 架构概述

Xiuno BBS 采用**关联数组**方式实现多语言,核心是 PHP 原生的 `return array()` 结构。

### 设计原则

- **零依赖**:无需第三方库,PHP 原生支持
- **模块化拆分**:按功能模块将语言包拆分为多个子文件,便于维护
- **插件友好**:通过 hook 机制允许插件扩展语言包
- **用户级切换**:支持前台用户选择语言偏好,存储于 Cookie
- **自动检测**:首次访问时根据浏览器 `Accept-Language` 自动选择语言

### 语言列表

| 代码 | 语言 | 目录 |
|------|------|------|
| zh-cn | 简体中文 | lang/zh-cn/ |
| zh-tw | 繁体中文 | lang/zh-tw/ |
| en-us | English | lang/en-us/ |
| ru-ru | Русский | lang/ru-ru/ |
| th-th |  | lang/th-th/ |

---

## 2. 文件结构

```
lang/
├── zh-cn/
│   ├── bbs.php              ← 前台语言统一入口(include 子模块)
│   ├── bbs_common.php       ← 通用词汇(登录、提交、删除等)
│   ├── bbs_user.php         ← 用户相关(注册、个人资料等)
│   ├── bbs_thread.php       ← 帖子相关(发表、回复、收藏等)
│   ├── bbs_forum.php        ← 版块相关
│   ├── bbs_search.php       ← 搜索相关
│   ├── bbs_theme.php        ← 主题/外观相关
│   ├── bbs_social.php       ← 社交相关(关注、通知、动态等)
│   ├── bbs_friendlink.php   ← 友情链接
│   ├── bbs_misc.php         ← 其他杂项
│   ├── bbs_admin.php        ← 后台管理
│   └── bbs_install.php       ← 安装程序
├── zh-tw/
│   └── ...                  ← 同上结构
├── en-us/
│   └── ...
├── ru-ru/
│   └── ...
└── th-th/
    └── ...
```

### bbs.php 入口文件

```php
<?php
$lang = array();

$_lang_dir = APP_PATH . "lang/{$conf['lang']}";

// 按模块 include 子文件
$lang += include _include("$_lang_dir/bbs_common.php");
$lang += include _include("$_lang_dir/bbs_user.php");
$lang += include _include("$_lang_dir/bbs_thread.php");
// ... 其他模块

// hook lang_zh_cn_bbs.php    ← 插件语言注入点
// 编译时,插件的 hook 文件内容会追加到这里

return $lang;
```

**注意**:必须使用 `_include()` 而非 `include`,确保:
1. 子模块文件也被 Xiuno 编译到 `tmp/` 目录
2. 插件 hook 能正确注入

### 模块划分建议

| 文件 | 内容 | 示例 key |
|------|------|----------|
| bbs_common.php | 通用操作 | `login`, `logout`, `submit`, `delete`, `success`, `error` |
| bbs_user.php | 用户功能 | `register`, `user_center`, `edit_profile`, `change_password` |
| bbs_thread.php | 帖子功能 | `create_thread`, `reply`, `digest`, `top` |
| bbs_forum.php | 版块功能 | `forum`, `category`, `sub_forum` |
| bbs_search.php | 搜索功能 | `search`, `search_result`, `no_result` |
| bbs_theme.php | 主题功能 | `theme_settings`, `light_mode`, `dark_mode` |
| bbs_social.php | 社交功能 | `follow`, `followers`, `my_feed`, `notification` |
| bbs_friendlink.php | 友情链接 | `friend_link`, `link_name`, `link_url` |
| bbs_misc.php | 其他杂项 | `no_data`, `loading`, `confirm_action` |

---

## 3. 核心 API

### 3.1 lang() 函数

```php
/**
 * 获取翻译文本
 * @param string $key    语言 key
 * @param array  $arr    可选,替换变量
 * @return string        翻译后的文本
 */
function lang($key, $arr = array()) {
    global $_SERVER;
    $text = isset($_SERVER['lang'][$key]) ? $_SERVER['lang'][$key] : $key;
    if (!empty($arr)) {
        $search = $replace = array();
        foreach ($arr as $k => $v) {
            $search[] = '{' . $k . '}';
            $replace[] = $v;
        }
        $text = str_replace($search, $replace, $text);
    }
    return $text;
}
```

### 3.2 使用示例

**基本用法**
```php
echo lang('login');              // 输出:登录 / Login / 登入
```

**变量替换**
```php
// 语言文件中的定义
'welcome' => '欢迎 {username},您有 {count} 条新消息',

// 调用
echo lang('welcome', array('username' => '张三', 'count' => 5));
// 输出:欢迎 张三,您有 5 条新消息
```

**HTML 内容翻译**
```php
// 语言文件中
'terms' => '我已阅读并同意<a href="/terms">服务条款</a>',

// 调用
echo lang('terms');
// 输出:我已阅读并同意<a href="/terms">服务条款</a>
```

### 3.3 key 命名规范

```php
// 推荐:下划线分隔,小写
'user_register_success' => '注册成功'
'thread_create_success' => '发帖成功'
'confirm_delete' => '确定要删除吗?'

// 不推荐:驼峰命名、混合大小写
'userRegisterSuccess'   // ❌
'THREAD_CREATE_SUCCESS' // ❌
```

---

## 4. 插件开发

### 4.1 插件语言文件位置

插件在 `hook/` 目录下放置语言文件:

```
plugin/my_plugin/
├── hook/
│   ├── lang_zh_cn_bbs.php    ← 中文翻译
│   ├── lang_en_us_bbs.php    ← 英文翻译
│   ├── lang_zh_tw_bbs.php    ← 繁体翻译
│   ├── lang_ru_ru_bbs.php    ← 俄语翻译
│   └── lang_th_th_bbs.php    ← 泰语翻译
└── ...
```

### 4.2 插件语言文件格式

```php
<?php
// hook/lang_zh_cn_bbs.php
return array(
    // 建议使用插件名前缀避免冲突
    'my_plugin_title' => '我的插件',
    'my_plugin_enable' => '启用插件',
    'my_plugin_disable' => '禁用插件',
    'my_plugin_config' => '插件设置',
    'my_plugin_settings_saved' => '设置已保存',
);
```

### 4.3 在模板中使用插件语言

```php
<?php echo lang('my_plugin_title'); ?>
```

### 4.4 后台管理语言

如果插件有后台设置页面,需要创建后台语言文件:

```
plugin/my_plugin/
├── hook/
│   ├── lang_zh_cn_bbs.php
│   ├── lang_en_us_bbs.php
│   └── ...
└── hook/admin/
    ├── lang_zh_cn_bbs_admin.php    ← 后台中文
    └── lang_en_us_bbs_admin.php    ← 后台英文
```

```php
<?php
// hook/admin/lang_zh_cn_bbs_admin.php
return array(
    'my_plugin_admin_title' => '我的插件管理',
    'my_plugin_admin_settings' => '插件设置',
);
```

后台语言会在加载 `bbs_admin.php` 时通过 hook 注入。

### 4.5 插件语言 key 前缀建议

为避免与核心或其他插件冲突,建议:

```php
// 推荐:插件名_功能
'haya_favorite_add' => '收藏'
'haya_favorite_remove' => '取消收藏'

// 推荐:插件名_模块_功能
'sitemap_admin_generate' => '生成站点地图'
'sitemap_admin_settings' => '地图设置'
```

---

## 5. 模板开发

### 5.1 正确使用 lang()

**HTML 模板中**
```html
<h1><?php echo lang('user_center'); ?></h1>
<button class="btn btn-primary"><?php echo lang('submit'); ?></button>
```

**JavaScript 中(通过 data 属性)**
```html
<button class="btn btn-danger" onclick="confirmDelete('<?php echo lang('confirm_delete'); ?>')">
    <?php echo lang('delete'); ?>
</button>
```

**JavaScript 中(通过 DOM 注入)**
```javascript
// 在 JS 文件中
var deleteConfirmText = document.getElementById('delete-btn').dataset.confirm;
```

```html
<button id="delete-btn" class="btn btn-danger" data-confirm="<?php echo lang('confirm_delete'); ?>">
    <?php echo lang('delete'); ?>
</button>
```

### 5.2 避免硬编码中文

**❌ 错误示例**
```html
<span>帖子</span>
<button>删除</button>
<a href="/user">个人中心</a>
```

**✅ 正确示例**
```html
<span><?php echo lang('thread'); ?></span>
<button><?php echo lang('delete'); ?></button>
<a href="<?php echo url('user'); ?>"><?php echo lang('user_center'); ?></a>
```

### 5.3 动态内容的翻译

对于运行时生成的文本,需要在语言包中添加对应的 key:

```php
// 语言文件
'upload_success' => '上传成功'
'upload_failed' => '上传失败,错误码:{error_code}'

// PHP 代码
if ($uploadSuccess) {
    message(0, lang('upload_success'));
} else {
    message(-1, lang('upload_failed', array('error_code' => $errorCode)));
}
```

---

## 6. 语言切换机制

### 6.1 前台语言切换流程

```
用户选择语言 → 设置 Cookie → 刷新页面 → 检测 Cookie → 加载对应语言包
```

**语言检测优先级**
1. Cookie 中的用户偏好(最高)
2. 浏览器 `Accept-Language` 头
3. 站点默认语言(conf.php 中的 `lang` 配置)

**相关代码位置**
- 入口:`index.inc.php` 中的 `detect_user_lang()`
- 切换路由:`route/lang.php`
- 前台导航:`view/htm/header_nav.inc.htm` 中的语言下拉菜单

### 6.2 Cookie 设置

```php
// route/lang.php
$lang_code = param(1) . '-' . param(2);  // 如 'en-us'
setcookie('lang', $lang_code, time() + 86400 * 365, '/');
```

### 6.3 URL 路由切换

前台语言切换通过 URL 路由实现:

```
?lang-zh-cn.htm  → 切换到简体中文
?lang-en-us.htm  → 切换到 English
```

**注意**:URL 中使用 `-` 分隔语言代码,如 `en-us`,不要使用 `en_us`。

### 6.4 后台语言处理

后台使用独立的语言包 `bbs_admin.php`,不受前台语言切换影响。

后台管理员可以在后台导航栏单独切换后台显示语言,设置同样保存在 Cookie 中。

---

## 7. 后台多语言

### 7.1 后台语言文件

```php
// lang/zh-cn/bbs_admin.php
return array(
    // 后台特有的翻译
    'admin_dashboard' => '仪表盘',
    'admin_settings' => '站点设置',
    'admin_user_manage' => '用户管理',
    // ...
);
```

### 7.2 后台模板中使用

```php
<?php echo lang('admin_dashboard'); ?>
```

### 7.3 后台语言与前台语言的关系

| 方面 | 后台语言 | 前台语言 |
|------|----------|----------|
| 文件 | `bbs_admin.php` | `bbs.php` 及子模块 |
| 语言 key 前缀 | `admin_` | 无特定前缀 |
| 切换影响 | 仅后台 | 仅前台 |
| 插件 hook | `hook/lang_*_bbs_admin.php` | `hook/lang_*_bbs.php` |

---

## 8. 安装程序多语言

### 8.1 安装语言文件

```php
// lang/zh-cn/bbs_install.php
return array(
    'install_welcome' => '欢迎使用 Xiuno BBS 安装向导',
    'install_database' => '数据库配置',
    'install_admin_account' => '创始人账号',
    'install_complete' => '安装完成',
    // ...
);
```

### 8.2 安装程序加载语言

```php
// install/index.php
$_lang = include APP_PATH . "lang/{$conf['lang']}/bbs_install.php";
```

### 8.3 安装时语言选择

安装程序通常需要先让用户选择语言,然后再加载对应语言包。

---

## 9. 同步检测工具

### 9.1 工具位置

```
tools/lang_sync_check.php
```

### 9.2 功能

- 检测各语言包的 key 数量是否一致
- 列出缺失的 key
- 生成缺失 key 的模板供翻译

### 9.3 使用方法

```bash
php tools/lang_sync_check.php
```

### 9.4 输出示例

```
=== 语言包同步检测 ===
基准语言:zh-cn (410 keys)

en-us: 367 keys, 缺失 43 个 key
  - missing_key_1
  - missing_key_2
  ...

zh-tw: 310 keys, 缺失 100 个 key
  - missing_key_1
  ...
```

---

## 10. API 多语言支持

### 10.1 RESTful API 多语言策略

API 返回的数据通常不需要翻译,但错误消息需要支持多语言。

### 10.2 错误消息多语言

**方式一:API 返回错误码,客户端翻译**

```php
// API 返回
return json(['code' => 1001, 'message' => lang('user_not_login')]);
```

客户端根据错误码和当前语言显示对应的消息。

**方式二:API 接受语言参数,返回翻译后的消息**

```php
// API 入口
$apiLang = param('lang', $conf['lang']);

// 加载对应语言包
$_lang = include APP_PATH . "lang/{$apiLang}/bbs.php";
```

### 10.3 API 语言包

如果 API 需要独立的语言处理,可以创建 `bbs_api.php`:

```php
// lang/zh-cn/bbs_api.php
return array(
    'api_success' => '操作成功',
    'api_failed' => '操作失败',
    'api_invalid_params' => '参数错误',
    'api unauthorized' => '未授权',
    // ...
);
```

---

## 11. 翻译协作流程

### 11.1 添加新语言 key 的流程

1. **在 zh-cn/bbs.php 及子模块中添加新 key**
   ```php
   // bbs_thread.php
   'thread_pin' => '置顶',
   ```

2. **在所有其他语言包中添加对应翻译**
   ```php
   // en-us/bbs_thread.php
   'thread_pin' => 'Pin Topic',
   ```

3. **运行同步检测工具确认无遗漏**

### 11.2 翻译质量检查

- [ ] 语法正确,无拼写错误
- [ ] 语气一致(正式/口语)
- [ ] 占位符 `{placeholder}` 保持一致
- [ ] HTML 标签正确闭合
- [ ] 复数形式处理(如有)

### 11.3 翻译注意事项

**变量占位符**
```php
// ❌ 错误:占位符顺序不一致
'zh' => '{name} 赞了 {user} 的帖子'
'en' => '{user} liked {name}\'s post'

// ✅ 正确:占位符顺序一致
'zh' => '{name} 赞了 {user} 的帖子'
'en' => '{user} liked {name}\'s post'
```

**HTML 内容**
```php
// ❌ 错误:HTML 标签不匹配
'zh' => '<strong>重要</strong> 请阅读'
'en' => '<strong>Important</strong> Please read'

// HTML 属性可能需要翻译
'search_placeholder' => '搜索...'
'en' => 'Search...'
```

**复数形式**
```php
// PHP 原生不支持复数,需要手动处理
$msg = $count == 1 
    ? lang('x_posts_single', ['count' => $count])
    : lang('x_posts_plural', ['count' => $count]);
```

### 11.4 维护建议

1. **定期同步**:每次添加新 key 后,同步到所有语言包
2. **使用工具**:`tools/lang_sync_check.php` 检测遗漏
3. **翻译协作**:考虑使用 PO 文件 + Poedit 或 Crowdin/Transifex 平台
4. **测试验证**:切换到非中文语言,测试所有页面文本

---

## 附录

### A. 语言相关常量

| 常量 | 说明 |
|------|------|
| `$conf['lang']` | 当前站点默认语言 |
| `$_SERVER['lang']` | 当前会话语言数组 |
| `$_COOKIE['lang']` | 用户语言偏好 Cookie |

### B. 相关文件位置

| 文件 | 位置 | 说明 |
|------|------|------|
| 语言入口 | `lang/{locale}/bbs.php` | 前台语言主文件 |
| 后台语言 | `lang/{locale}/bbs_admin.php` | 后台语言 |
| 安装语言 | `lang/{locale}/bbs_install.php` | 安装程序语言 |
| 切换路由 | `route/lang.php` | 语言切换处理 |
| 前台入口 | `index.inc.php` | 语言检测逻辑 |
| 检测工具 | `tools/lang_sync_check.php` | 同步检测脚本 |

### C. key 命名参考表

| 模块 | key 前缀 | 示例 |
|------|----------|------|
| 用户 | `user_` | `user_register`, `user_login`, `user_profile` |
| 帖子 | `thread_` / `post_` | `thread_create`, `thread_delete`, `post_edit` |
| 版块 | `forum_` | `forum_create`, `forum_update` |
| 消息 | `message_` / `notify_` | `message_send`, `notify_new` |
| 设置 | `setting_` | `setting_save`, `setting_reset` |
| 错误 | `error_` | `error_not_found`, `error_permission` |
| 成功 | `success_` | `success_create`, `success_update` |

---

*文档版本:1.0*
*最后更新:2026-06-01*
0 0 1
复制成功

回复 (1)

过时的手搓老牌程序员 4天前
#2
点赞,希望可以尽快面市,能尽早体验