ludic/packages/ludic.i18n/langs.ludic
Orkuncakilkaya 1667d7f16e ludic.i18n: a language's lookups kept once read; switching back restores them
i18n_use re-read and re-parsed the .po on every change of language (ev_SettingsChanged, SetLang)
and dropped the old tables for good: the play of 22 minutes left lines_of 650 KB, fs_read_text
409 KB, PoEntry 113 KB and unquote 273 KB unreachable, and three of its @alloc_ok sites were
declared but unbounded. Each language's tables are now filed by index once built (tables.ludic,
I18nTables) and put back by reference; a new probe (i18n_init) or a different file for a code
(lang_add) reads it again.

Test: a language switched back to is its kept tables, not its file read again; a new probe reads
it (9 tests pass).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 12:21:53 +03:00

85 lines
3 KiB
Text

# langs.ludic - where languages come from, later ones replacing earlier ones with the same code:
# <lang_dir>/languages.txt (one code per line, # a comment) and every .po in the mod folder
@alloc_ok("once at start: the shipped and dropped-in languages found")
export function i18n_init(i18n_st: mut I18nState) -> void {
i18n_st.codes = new []string
i18n_st.names = new []string
i18n_st.paths = new []string
i18n_st.fonts = new []string
i18n_st.tabs = new []I18nTables
lang_add(i18n_st, "en", "English", "", "")
let idx = Fs.read_text(i18n_st.lang_dir + "/languages.txt")
if idx != null {
let ls = lines_of(idx)
for i in 0 .. len(ls) {
let ln = trim(ls[i])
if len(ln) == 0 or ln[0] == 35 { continue }
probe(i18n_st, ln, `{i18n_st.lang_dir}/{ln}.po`, i18n_st.lang_dir)
}
}
let md = i18n_mod_dir()
if len(md) == 0 { return }
Fs.mkdir(md)
let files = Fs.list(md)
if files == null { return }
for i in 0 .. len(files) {
let nm = files[i]
let n = len(nm)
if n > 3 and i18n_ends(nm, ".po") { probe(i18n_st, nm[0..n - 3], md + "/" + nm, md) }
}
}
# where a player's own .po files go; "" when the game has none
export function i18n_mod_dir() -> string { return I18nWorld.mod_dir() }
function lang_add(i18n_st: mut I18nState, code: string, name: string, path: string, font: string) -> void {
for i in 0 .. len(i18n_st.codes) {
if i18n_st.codes[i] == code {
i18n_st.names[i] = name
i18n_st.paths[i] = path
i18n_st.fonts[i] = font
if tables_have(i18n_st, i) { i18n_st.tabs[i] = null } # another file for it: read that one next time
return
}
}
push(i18n_st.codes, code)
push(i18n_st.names, name)
push(i18n_st.paths, path)
push(i18n_st.fonts, font)
}
function probe(i18n_st: mut I18nState, code: string, path: string, dir: string) -> void {
let text = Fs.read_text(path)
if text == null { return }
var name = i18n_header(text, "X-Language-Name")
if len(name) == 0 { name = code }
var font = i18n_header(text, "X-Font")
if len(font) > 0 { font = dir + "/" + font }
lang_add(i18n_st, code, name, path, font)
}
export function i18n_count(i18n_st: I18nState) -> int {
if i18n_st.codes == null { return 1 }
return len(i18n_st.codes)
}
export function i18n_index(i18n_st: I18nState, code: string) -> int {
if i18n_st.codes != null { for i in 0 .. len(i18n_st.codes) { if i18n_st.codes[i] == code { return i } } }
return 0
}
export function i18n_code_at(i18n_st: I18nState, i: int) -> string {
if i18n_st.codes == null or i < 0 or i >= len(i18n_st.codes) { return "en" }
return i18n_st.codes[i]
}
export function i18n_name_at(i18n_st: I18nState, i: int) -> string {
if i18n_st.names == null or i < 0 or i >= len(i18n_st.names) { return "English" }
return i18n_st.names[i]
}
# the font atlas a language names with X-Font, beside its .po; "" is the default
export function i18n_font_at(i18n_st: I18nState, i: int) -> string {
if i18n_st.fonts == null or i < 0 or i >= len(i18n_st.fonts) { return "" }
return i18n_st.fonts[i]
}