# 多语言输出 (/zh/docs/guide/guides/i18n)

六种盘面语言、哪些字段会被翻译、换盘面语言对结果的影响，以及标识与译名的双向换算。



*适合：开发者*

## 支持的盘面语言 [#支持的盘面语言]

「盘面语言」指排盘结果里那些人读的文本用哪种语言写，与你用哪种**编程语言**调用无关。

| 取值      | 语言       | Rust 枚举          |
| ------- | -------- | ---------------- |
| `zh-CN` | 简体中文（默认） | `Language::ZhCN` |
| `zh-TW` | 繁体中文     | `Language::ZhTW` |
| `en-US` | 英文       | `Language::EnUS` |
| `ja-JP` | 日文       | `Language::JaJP` |
| `ko-KR` | 韩文       | `Language::KoKR` |
| `vi-VN` | 越南文      | `Language::ViVN` |

```python
chart = astro.by_solar("2000-8-16", 2, "female", language="en-US")
```

```go
chart, _ := iztro.BySolar("2000-8-16", 2, iztro.GenderFemale, true, iztro.LanguageJaJP, nil)
```

```rust
by_solar("2000-8-16", 2, Gender::Female, true, Language::KoKR, Config::default())?;
```

## 哪些内容会翻译 [#哪些内容会翻译]

会翻译的是所有面向人阅读的文本：

* 星耀名、宫位名、四化名、亮度名
* 天干、地支、五行局
* 时辰名与时间段、星座、生肖、性别
* 农历日期的中文表示、干支纪日展示串
* 运限层级名（大限 / 流年 / …）

**不翻译**的是所有标识字段与数值字段：星耀的 `key`、宫位的 `nameKey`、
宫位索引、大限区间、虚岁等。见 [key 契约](/zh/docs/guide/guides/keys)。

<Callout type="warn" title="非中文译名的三个坑">
  1. **英文与韩文没有亮度译名**，输出的是记号：`[+3]`（庙）、`[+2]`（旺）、
     `[+1]`（得）、`[0]`（利）、`[-1]`（平）、`[-2]`（不）、`[-3]`（陷）。
     繁体、日文、越南文有真译名。
  2. **英文四化输出 `A`/`B`/`C`/`D`**，依次是禄、权、科、忌。
  3. **非中文译名沿用 iztro 的词表，不保证是该语言命理界的通行译法**，
     个别条目并非真词 —— 例如英文的 `considery`、`disastery`。
     译名只做展示，判断一律用标识字段。
</Callout>

## 换盘面语言不改变排盘结果 [#换盘面语言不改变排盘结果]

盘面语言只影响翻译层。同一个生日在六种盘面语言下：

* 十二宫的位置与宫名顺序完全相同
* 每个宫里的星耀完全相同
* 四化、亮度、大限小限、运限干支完全相同

变的只是这些东西被写成什么字。所以下面两张盘除文本外逐字段相等：

```python
zh = astro.by_solar("2000-8-16", 2, "female", language="zh-CN")
en = astro.by_solar("2000-8-16", 2, "female", language="en-US")

assert zh.palace(PalaceName.SOUL).index == en.palace(PalaceName.SOUL).index
assert zh.soul_key == en.soul_key
```

六种盘面语言的一致性由变体金标测试覆盖，零容忍差异。

## 标识与译名的换算 [#标识与译名的换算]

手上只有标识（或只有某种语言的译名）时，用双向查找函数换算，不必重新排盘：

<Tabs items="['Rust', 'Python', 'Go']">
  <Tab value="Rust">
    ```rust
    translate_key("ziweiMaj", Language::EnUS);   // Some("emperor")
    key_of("emperor");                           // Some("ziweiMaj")
    key_of("자미");                               // Some("ziweiMaj")
    ```

    类别已知时用强类型版本更直接，也免去 `Option`：

    ```rust
    use x_iztro::{translate_palace, translate_star};

    translate_star(StarKey::ZiweiMaj, Language::ViVN);   // Tử Vi
    translate_palace(Palace::Soul, Language::KoKR);      // 명궁
    ```
  </Tab>

  <Tab value="Python">
    ```python
    i18n.translate("ziweiMaj", "en-US")   # emperor
    i18n.key_of("emperor")                # ziweiMaj
    i18n.key_of("자미")                    # ziweiMaj
    ```
  </Tab>

  <Tab value="Go">
    ```go
    name, _ := iztro.Translate(iztro.StarZiweiMaj, iztro.LanguageEnUS)   // emperor
    key, _ := iztro.KeyOf("emperor")                          // ziweiMaj
    key, _ = iztro.KeyOf("자미")                               // ziweiMaj
    ```

    Go 侧两个函数都返回 `(string, error)`：标识未知或反查不到时返回空串与
    `*iztro.Error`（分类 `invalid_argument`）。
  </Tab>
</Tabs>

覆盖十二类共 260 个标识：星耀、宫位（含身宫、来因宫）、天干、地支、亮度、四化、
五行局、性别、生肖、时辰、星座、运限层级。完整清单与逐条说明见
[Rust](/zh/docs/rust/i18n)、[Python](/zh/docs/python/i18n)、[Go](/zh/docs/go/i18n) 三页。

<Callout type="warn" title="反查查不到时返回空，不是原样返回">
  `key_of("不存在的名字")` 返回 `None` / 空串，而不是把入参吐回来。

  另有同形译名的问题：不同标识在某些语言下译名相同（`horse`、`dragon`、`유시` 等）。
  反查按固定的扫描顺序取第一个命中，与 iztro 的 `kot` 逐例一致。
  要指定类别就用 `key_of_in`（Rust）/ `key_of(text, key_filter)`（Python）/
  `KeyOfIn`（Go），传标识名的共同后缀消歧：
  `"Maj"` 只看十四主星、`"Min"` 只看辅星、`"Palace"` 只看宫位、`"Hour"` 只看时辰。
</Callout>

## 三种编程语言的取值形态不同 [#三种编程语言的取值形态不同]

|        | 排盘结果里存什么                                   | 换盘面语言的代价            |
| ------ | ------------------------------------------ | ------------------- |
| Rust   | 枚举（`StarKey`、`Palace`…），只有少数展示字段是 `String` | 调翻译函数，同一张盘可同时输出多种语言 |
| Python | 译名与标识两组字段都已经是字符串                           | 重新排盘                |
| Go     | 同上                                         | 重新排盘                |

排盘本身是毫秒级，多排几次不是问题。判断逻辑请始终用标识字段，
这样换盘面语言不需要改任何代码。

## 没有全局语言开关 [#没有全局语言开关]

x-iztro 不设「当前语言」这样的全局状态：排盘时语言随参数传入，
翻译函数每次调用都显式指定目标语言。

<Callout type="info" title="为什么">
  全局语言开关会让同一段代码在不同调用顺序下产出不同结果，并发环境尤其危险。
  显式传参使每次调用的结果只由入参决定。
</Callout>

## 新增一种语言要改哪些地方 [#新增一种语言要改哪些地方]

词表不是一个可以外挂的资源文件，是编译进库的静态表。加一种语言要动四处：

<Steps>
  <Step>
    `src/data/types.rs`

     的 

    `Language`

     枚举加一个变体，并在 

    `as_code`

     / 

    `from_code`

     里补上语言代码
  </Step>

  <Step>
    `src/i18n/`

     下新增一个词表文件，实现与既有文件相同的一组函数（星名、宫名、干支名、亮度、四化……）
  </Step>

  <Step>
    `src/i18n/mod.rs`

     里每个翻译函数的 

    `match`

     各加一条分派 —— 这里是逐个函数的，不是一处
  </Step>

  <Step>
    `src/i18n/lookup.rs`

     的 

    `lang_index`

     与反查扫描顺序表加一项；反查顺序会影响同形译名落到哪个标识，须与金标对照
  </Step>
</Steps>

绑定层不需要改：语言代码是字符串传入的，加了枚举变体三侧自动可用。
