Guides

Multilingual output

Six chart languages, which fields get translated, what switching language does to the result, and two-way conversion between keys and names.

For: developers

Supported chart languages

"Chart language" means which language the human-readable text in the output is written in. It has nothing to do with which programming language you call from.

ValueLanguageRust enum
zh-CNSimplified Chinese (default)Language::ZhCN
zh-TWTraditional ChineseLanguage::ZhTW
en-USEnglishLanguage::EnUS
ja-JPJapaneseLanguage::JaJP
ko-KRKoreanLanguage::KoKR
vi-VNVietnameseLanguage::ViVN
chart = astro.by_solar("2000-8-16", 2, "female", language="en-US")
chart, _ := iztro.BySolar("2000-8-16", 2, iztro.GenderFemale, true, iztro.LanguageJaJP, nil)
by_solar("2000-8-16", 2, Gender::Female, true, Language::KoKR, Config::default())?;

What gets translated

Everything meant for a person to read:

  • Star names, palace names, mutagen names, brightness names
  • Heavenly stems, earthly branches, the Five Elements class
  • Hour names and their clock ranges, zodiac sign, zodiac animal, gender
  • The Chinese rendering of the lunar date, and the stem-branch display string
  • Horoscope scope names (decadal / yearly / …)

Not translated: every key field and every numeric field — a star's key, a palace's nameKey, palace indexes, decadal ranges, nominal ages (虚岁). See The key contract.

Three traps in the non-Chinese vocabularies

  1. English and Korean have no brightness translations, so the output is a mark: [+3] (miaowang), [+2] (wangxiang), [+1] (dedi), [0] (liyi), [-1] (pinghe), [-2] (budedi), [-3] (luoxian). Traditional Chinese, Japanese and Vietnamese have real translations.
  2. English mutagens print as A/B/C/D, in the order Lu, Quan, Ke, Ji.
  3. The non-Chinese vocabularies come from iztro's word list and are not guaranteed to be the rendering conventional among practitioners in that language. Some entries are simply wrong — Korean renders the Original palace as 라인, a transliteration of the English word "line". A few English entries are not words at all (considery, disastery). Translations are for display; predicate on the key fields.

Switching chart language does not change the chart

The chart language affects only the translation layer. Across all six languages, for one birthday:

  • The positions of the twelve palaces and the order of the palace names are identical.
  • The stars in each palace are identical.
  • Mutagens, brightness, decadals, age fortune and horoscope stems and branches are identical.

All that changes is which characters those things are written in. So the two charts below are equal field for field apart from the text:

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

Consistency across the six chart languages is covered by the variant golden tests, with zero tolerated deviation.

Converting between keys and names

When you hold only a key (or only a name in some language), use the two-way lookup functions rather than charting again:

translate_key("ziweiMaj", Language::EnUS);   // Some("emperor")
key_of("emperor");                           // Some("ziweiMaj")
key_of("자미");                               // Some("ziweiMaj")

When the category is already known, the strongly typed versions are more direct and drop the Option:

use x_iztro::{translate_palace, translate_star};

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

Coverage is 260 keys across twelve categories: stars, palaces (including the Body and Original palaces), heavenly stems, earthly branches, brightness, mutagens, the Five Elements class, gender, zodiac animal, hour, zodiac sign and horoscope scope. The complete list with per-entry notes is on the i18n page for Rust, Python and Go.

A failed reverse lookup returns empty, not the input

key_of("no such name") returns None / an empty string; it does not echo the argument back.

There is also the matter of homographs: different keys translate to the same name in some languages (horse, dragon, 유시 and others). Reverse lookup takes the first hit in a fixed scan order, matching iztro's kot case for case. To pin down a category use key_of_in (Rust) / key_of(text, key_filter) (Python) / KeyOfIn (Go), passing the shared suffix of the key names to disambiguate: "Maj" searches only the fourteen major stars, "Min" only the minor stars, "Palace" only palaces, "Hour" only hours.

The three programming languages hold values differently

What the chart holdsCost of switching chart language
RustEnums (StarKey, Palace, …), with only a few display fields as StringCall a translation function; one chart can emit several languages at once
PythonBoth the translated and the key fields are already stringsChart again
GoAs aboveChart again

Charting itself is a matter of milliseconds, so charting more than once is not a problem. Keep predicates on the key fields and switching chart language requires no code changes at all.

There is no global language switch

x-iztro keeps no "current language" global state: the language is passed as a parameter when charting, and translation functions name their target language explicitly on every call.

Why

A global language switch makes the same code produce different results depending on call order, which is especially dangerous under concurrency. Explicit parameters make each call's result a function of its arguments alone.

What adding a language would touch

The vocabularies are not a resource file you can drop in; they are static tables compiled into the library. Adding a language touches four places:

Add a variant to the Language enum in src/data/types.rs, and add its language code to as_code / from_code
Add a vocabulary file under src/i18n/, implementing the same set of functions as the existing files (star names, palace names, stem and branch names, brightness, mutagens, …)
Add a dispatch arm to the match in every translation function in src/i18n/mod.rs — this is per function, not one place
Add an entry to lang_index and to the reverse-lookup scan order table in src/i18n/lookup.rs; the scan order decides which key a homographic name resolves to, and has to be checked against the golden data

The binding layer needs no changes: language codes are passed as strings, so a new enum variant is immediately available in all three programming languages.

On this page