下面是为提供的 JavaScript 代码编写的 **Markdown 格式技术文档**，包含类的功能说明、构造函数参数、方法详解以及使用示例。

---

# `bricks.I18n` 国际化支持类

`bricks.I18n` 是一个轻量级的国际化（i18n）工具类，用于在前端应用中实现多语言文本翻译和动态语言切换。它支持从远程 URL 加载语言包，并缓存已加载的语言资源以提高性能。

该类依赖于全局对象 `window.bricks`，并使用 `bricks.HttpJson` 进行异步请求，同时假设存在辅助函数 `objget` 和 `obj_fmtstr` 用于对象操作和字符串格式化。

---

## 基本结构

```javascript
var bricks = window.bricks || {};
bricks.I18n = class {
    // 实现见下文
};
```

> ⚠️ 注意：此代码片段中存在一处变量名错误（`opts` 未定义），已在文档中指出并修正。

---

## 构造函数

### `new bricks.I18n(options)`

初始化 I18n 实例，配置语言资源路径与默认行为。

#### 参数

| 参数 | 类型 | 必填 | 描述 |
|------|------|------|------|
| `url` | `string` | 否 | 获取语言包的 API 地址（如 `/api/i18n`）。若不提供，则仅支持手动设置字典。 |
| `method` | `string` | 否 | 请求语言包时使用的 HTTP 方法，默认为 `'GET'`。 |
| `default_lang` | `string` | 否 | 默认语言代码（如 `'en'`, `'zh-CN'`），默认值为 `'en'`。 |

#### 示例

```js
const i18n = new bricks.I18n({
    url: '/api/translations',
    default_lang: 'zh-CN'
});
```

> ❌ **注意原始代码 Bug**：
> 原始代码中使用了未定义的 `opts` 变量：
> ```js
> this.url = opts.url;
> ```
> 应改为传入的构造函数参数 `url`, `method`, `default_lang`。**正确实现应如下：**

```js
constructor(url, default_lang, method) {
    this.url = url;
    this.default_lang = default_lang || 'en';
    this.method = method || 'GET';
    this.lang_msgs = {}; // 缓存所有语言的消息字典
    this.msgs = {};      // 当前激活语言的翻译映射
}
```

或者接受一个选项对象：

```js
constructor(options) {
    this.url = options.url;
    this.default_lang = options.default_lang || 'en';
    this.method = options.method || 'GET';
    this.lang_msgs = {};
    this.msgs = {};
}
```

> ✅ 文档后续基于 **修正后的构造函数** 进行描述。

---

## 方法

### `_ (txt, obj) → string`

获取指定键的翻译文本，并可选地进行变量替换。

#### 参数

| 参数 | 类型 | 描述 |
|------|------|------|
| `txt` | `string` | 要翻译的键名（key）。 |
| `obj` | `Object` 或 `undefined` | 用于填充模板字符串的变量对象。 |

#### 返回值

- 如果当前语言中有对应的翻译，返回翻译后的内容；
- 若提供了 `obj`，则调用 `obj_fmtstr(obj, translatedStr)` 对结果进行格式化；
- 否则返回原始 `txt`。

#### 示例

```js
i18n.setup_dict({
    "hello": "Hello, {name}!",
    "welcome": "Welcome aboard."
}, 'en');

i18n._("hello", {name: "Alice"}); 
// 输出: "Hello, Alice!"

i18n._("welcome");
// 输出: "Welcome aboard."
```

---

### `is_loaded(lang) → boolean`

检查某个语言包是否已被加载到内存中。

#### 参数

| 参数 | 类型 | 描述 |
|------|------|------|
| `lang` | `string` | 语言代码（如 `'zh-CN'`）。 |

#### 返回值

- `true`：该语言的消息字典已加载；
- `false`：尚未加载。

#### 示例

```js
if (!i18n.is_loaded('fr')) {
    await i18n.change_lang('fr');
}
```

---

### `setup_dict(dic, lang)`

将给定的语言字典注册到实例中，并立即激活该语言。

> 此方法通常由 `change_lang` 内部调用，也可用于预加载或静态注入语言包。

#### 参数

| 参数 | 类型 | 描述 |
|------|------|------|
| `dic` | `Object` | 键值对形式的语言翻译表，例如 `{ "ok": "确定" }`。 |
| `lang` | `string` | 该字典对应的语言代码。 |

#### 行为

- 将 `dic` 存储至 `this.lang_msgs[lang]` 中；
- 设置 `this.msgs = dic`，即当前活跃语言为 `lang`。

#### 示例

```js
i18n.setup_dict({
    "title": "应用标题",
    "save": "保存"
}, 'zh-CN');
```

---

### `async change_lang(lang) → Promise<void>`

切换当前语言环境。如果目标语言未加载，会自动通过网络请求获取。

#### 参数

| 参数 | 类型 | 描述 |
|------|------|------|
| `lang` | `string` | 目标语言代码（如 `'es'`, `'ja'`）。 |

#### 流程

1. 检查是否已加载该语言：
   - 是 → 直接激活；
   - 否 → 发起 HTTP 请求获取语言包。
2. 使用 `bricks.HttpJson` 发送请求，携带参数 `{ lang }`。
3. 成功响应后，调用 `setup_dict` 注册并激活新语言。

#### 异常处理

- 若未设置 `this.url`，则跳过请求，无法加载新语言。
- 错误需由 `HttpJson` 层自行抛出或捕获。

#### 示例

```js
await i18n.change_lang('zh-CN');
document.title = i18n._('app_title'); // 使用新语言更新界面
```

---

## 依赖说明

本类依赖以下外部模块或函数：

| 名称 | 说明 |
|------|------|
| `bricks.HttpJson` | 用于发送 JSON 格式的 HTTP 请求，需具备 `httpcall(url, config)` 方法。 |
| `objget(obj, path)` | 工具函数，安全访问嵌套对象属性（类似 lodash.get）。 |
| `obj_fmtstr(obj, str)` | 字符串模板替换函数，支持 `{key}` 语法，用 `obj.key` 替换。 |

> 示例 `obj_fmtstr` 实现：
> ```js
> function obj_fmtstr(obj, str) {
>     return str.replace(/\{([^}]+)\}/g, (m, key) => obj[key] || m);
> }
> ```

---

## 使用场景示例

### 初始化与语言切换

```js
// 初始化 i18n 实例
const i18n = new bricks.I18n({
    url: '/api/translations',
    default_lang: 'en'
});

// 切换语言
async function setLanguage(lang) {
    await i18n.change_lang(lang);
    updateUI(); // 重新渲染页面文本
}

function updateUI() {
    document.getElementById('greeting').textContent = i18n._('welcome', { user: 'Tom' });
}
```

### 预加载语言包（离线模式）

```js
// 手动注入英文包
i18n.setup_dict({
    "welcome": "Welcome!",
    "goodbye": "Goodbye!"
}, 'en');

// 不发起请求，直接切换
i18n.change_lang('en').then(updateUI);
```

---

## 注意事项

1. ✅ **修复建议**：原始代码中的 `opts` 应为构造函数参数对象，请确保传参方式一致。
2. 🔐 安全性：确保服务端返回的语言包内容经过清洗，防止 XSS。
3. 🌐 性能优化：所有语言包会被缓存，避免重复请求。
4. ⏱ 异步加载：首次切换未加载语言时需要等待网络响应，请添加加载状态提示。

---

## 版本历史（示例）

| 版本 | 修改内容 |
|------|----------|
| 0.1 | 初始版本，支持基本翻译与远程加载 |

---

## 许可证

MIT License

--- 

✅ *文档结束*