diff --git a/packages/ludic.i18n/index.ludic b/packages/ludic.i18n/index.ludic index 65ffb6ca..732d5f5b 100644 --- a/packages/ludic.i18n/index.ludic +++ b/packages/ludic.i18n/index.ludic @@ -3,6 +3,7 @@ module ludic_i18n uses numbers float import "ports.ludic" import "state.ludic" +import "tables.ludic" import "bytes.ludic" import "po.ludic" import "langs.ludic" diff --git a/packages/ludic.i18n/langs.ludic b/packages/ludic.i18n/langs.ludic index af1a735a..673c4f82 100644 --- a/packages/ludic.i18n/langs.ludic +++ b/packages/ludic.i18n/langs.ludic @@ -6,6 +6,7 @@ export function i18n_init(i18n_st: mut I18nState) -> void { 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 { @@ -37,6 +38,7 @@ function lang_add(i18n_st: mut I18nState, code: string, name: string, path: stri 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 } } diff --git a/packages/ludic.i18n/ports.ludic b/packages/ludic.i18n/ports.ludic index 31bcb362..93e099fb 100644 --- a/packages/ludic.i18n/ports.ludic +++ b/packages/ludic.i18n/ports.ludic @@ -44,6 +44,7 @@ export state I18nState { plural: Dict = null # a plural msgid (the singular) -> index into forms / 3 forms: []string = null # three forms per entry, "" where the file has fewer plural_rule: int = 0 # PL_* + tabs: []I18nTables = null # by language: its lookups once read, kept for the next switch } # where the shipped languages are (a directory with languages.txt and .po), and the atlas a diff --git a/packages/ludic.i18n/tables.ludic b/packages/ludic.i18n/tables.ludic new file mode 100644 index 00000000..fadd8403 --- /dev/null +++ b/packages/ludic.i18n/tables.ludic @@ -0,0 +1,88 @@ +# tables.ludic - a language's lookups, kept once read: switching back to it is a copy of references, +# not the .po parsed again (every change of language once dropped the old tables for good) +property I18nTables { + exact: Dict = null + out: []string = null + lits: []string = null + pat_out: []string = null + pat_l0: words = null + pat_nl: words = null + pat_sc: words = null + pat_ph: words = null + pat_n: int = 0 + pfx: Dict = null + sfx: Dict = null + gen_head: int = 0 + nx_p: words = null + nx_s: words = null + nx_g: words = null + cache: Dict = null + cache_out: []string = null + caps: []string = null + plural: Dict = null + forms: []string = null + plural_rule: int = 0 +} + +@alloc_ok("once per language: its lookups, kept") +function tables_keep(i18n_st: I18nState) -> I18nTables { + let t = new I18nTables + t.exact = i18n_st.exact + t.out = i18n_st.out + t.lits = i18n_st.lits + t.pat_out = i18n_st.pat_out + t.pat_l0 = i18n_st.pat_l0 + t.pat_nl = i18n_st.pat_nl + t.pat_sc = i18n_st.pat_sc + t.pat_ph = i18n_st.pat_ph + t.pat_n = i18n_st.pat_n + t.pfx = i18n_st.pfx + t.sfx = i18n_st.sfx + t.gen_head = i18n_st.gen_head + t.nx_p = i18n_st.nx_p + t.nx_s = i18n_st.nx_s + t.nx_g = i18n_st.nx_g + t.cache = i18n_st.cache + t.cache_out = i18n_st.cache_out + t.caps = i18n_st.caps + t.plural = i18n_st.plural + t.forms = i18n_st.forms + t.plural_rule = i18n_st.plural_rule + return t +} + +function tables_put(i18n_st: mut I18nState, t: I18nTables) -> void { + i18n_st.exact = t.exact + i18n_st.out = t.out + i18n_st.lits = t.lits + i18n_st.pat_out = t.pat_out + i18n_st.pat_l0 = t.pat_l0 + i18n_st.pat_nl = t.pat_nl + i18n_st.pat_sc = t.pat_sc + i18n_st.pat_ph = t.pat_ph + i18n_st.pat_n = t.pat_n + i18n_st.pfx = t.pfx + i18n_st.sfx = t.sfx + i18n_st.gen_head = t.gen_head + i18n_st.nx_p = t.nx_p + i18n_st.nx_s = t.nx_s + i18n_st.nx_g = t.nx_g + i18n_st.cache = t.cache + i18n_st.cache_out = t.cache_out + i18n_st.caps = t.caps + i18n_st.plural = t.plural + i18n_st.forms = t.forms + i18n_st.plural_rule = t.plural_rule +} + +function tables_have(i18n_st: I18nState, k: int) -> bool { + if i18n_st.tabs == null or k >= len(i18n_st.tabs) { return false } + return i18n_st.tabs[k] != null +} + +@alloc_ok("once per language: the slot its lookups are kept in") +function tables_file(i18n_st: mut I18nState, k: int) -> void { + if i18n_st.tabs == null { i18n_st.tabs = new []I18nTables } + while len(i18n_st.tabs) <= k { push(i18n_st.tabs, null) } + i18n_st.tabs[k] = tables_keep(i18n_st) +} diff --git a/packages/ludic.i18n/tests/i18n_test.ludic b/packages/ludic.i18n/tests/i18n_test.ludic index 77cc9307..fd206573 100644 --- a/packages/ludic.i18n/tests/i18n_test.ludic +++ b/packages/ludic.i18n/tests/i18n_test.ludic @@ -114,6 +114,19 @@ program I18nTest { expect(i18n_font_now(i18n_st) == "font_default") } + test "a language switched back to is its kept tables, not its file read again; a new probe reads it" (i18n_st: mut I18nState, i18n_test_st: mut I18nTestState) { + fresh(i18n_st, i18n_test_st) + i18n_use(i18n_st, "tt") + let first = L(i18n_st, "Settings") + Fs.write_text(dir() + "/tt.po", header("Tongue", "") + po_line("Settings", "Changed")) + i18n_use(i18n_st, "en") + i18n_use(i18n_st, "tt") + expect(L(i18n_st, "Settings") == first) + i18n_init(i18n_st) + i18n_use(i18n_st, "tt") + expect(L(i18n_st, "Settings") == "Changed") + } + test "a mod in the player's folder is found, replaces a shipped code, and goes when removed" (i18n_st: mut I18nState, i18n_test_st: mut I18nTestState) { fresh(i18n_st, i18n_test_st) Fs.write_text(mods() + "/zz.po", header("Mod Tongue", "") + po_line("Settings", "Mettings")) diff --git a/packages/ludic.i18n/use.ludic b/packages/ludic.i18n/use.ludic index 096ec7d5..08fdce80 100644 --- a/packages/ludic.i18n/use.ludic +++ b/packages/ludic.i18n/use.ludic @@ -7,10 +7,16 @@ export function i18n_use(i18n_st: mut I18nState, code: string) -> void { let forced = I18nWorld.forced() if len(forced) > 0 { k = i18n_index(i18n_st, forced) } var font = "" - if k > 0 { + if k > 0 and tables_have(i18n_st, k) { + tables_put(i18n_st, i18n_st.tabs[k]) + i18n_st.on = true + i18n_st.cur = i18n_st.codes[k] + font = i18n_st.fonts[k] + } else if k > 0 { let text = Fs.read_text(i18n_st.paths[k]) if text != null { i18n_load_text(i18n_st, text) + tables_file(i18n_st, k) i18n_st.on = true i18n_st.cur = i18n_st.codes[k] font = i18n_st.fonts[k]