diff --git a/packages/ludic.i18n/index.ludic b/packages/ludic.i18n/index.ludic index 6466e3bc..b4ab8e80 100644 --- a/packages/ludic.i18n/index.ludic +++ b/packages/ludic.i18n/index.ludic @@ -15,4 +15,5 @@ import "keys.ludic" import "keyed.ludic" import "keyed_render.ludic" import "keyed_args.ludic" +import "mixed.ludic" import "plural.ludic" diff --git a/packages/ludic.i18n/keyed_render.ludic b/packages/ludic.i18n/keyed_render.ludic index aeb8e7b9..cda9f5ab 100644 --- a/packages/ludic.i18n/keyed_render.ludic +++ b/packages/ludic.i18n/keyed_render.ludic @@ -57,7 +57,7 @@ function kr_fill(i18n_st: mut I18nState, t: string, args: []string) -> string { if h >= 1 { var v = "" if h < len(args) { v = args[h] } - if i18n_is_key(v) { v = keyed(i18n_st, v) } else if i18n_st.on and kr_words(v) { v = L(i18n_st, v) } + if i18n_is_key(v) or kx_mixed(v) { v = L(i18n_st, v) } else if i18n_st.on and kr_words(v) { v = L(i18n_st, v) } r = r + t[seg..i] + v i = hole_end(t, i) seg = i diff --git a/packages/ludic.i18n/keys.ludic b/packages/ludic.i18n/keys.ludic index e1191d82..5d9a9b38 100644 --- a/packages/ludic.i18n/keys.ludic +++ b/packages/ludic.i18n/keys.ludic @@ -16,7 +16,6 @@ export function i18n_key(name: string) -> string { return kx_join(I18N_KEY, name export function i18n_key_plural(name: string) -> string { return kx_join(I18N_PLURAL, name, "", "", "", "", 0) } # the key's name without its marker ("pause.resume"), or the string as it is -@alloc_ok("a key's name: a slice of text already made, kept by whoever holds the key") export function key_text(k: string) -> string { if not i18n_is_key(k) { return k } var e = 1 @@ -25,7 +24,6 @@ export function key_text(k: string) -> string { } # the text of a key: drawn translated where it is drawn (its marked text, a free cast) -@alloc_ok("a cast: the key's own marked text, nothing is made") export function tr(k: Key) -> string { return string(k) } # a key's text with its holes, {1} .. {4}, filled by strings already made (a number, a name, another key) @alloc_ok("a line with its holes, made when something happens and kept by what shows it") diff --git a/packages/ludic.i18n/mixed.ludic b/packages/ludic.i18n/mixed.ludic new file mode 100644 index 00000000..a1576e37 --- /dev/null +++ b/packages/ludic.i18n/mixed.ludic @@ -0,0 +1,59 @@ +# mixed.ludic - a key's text glued into an English line ("A {1}, {2} cm." given a name that is a key, or a +# name + "\n" + its blurb): each marked name made in the language in use, then the line as English. Interim, +# until every such line is a key of its own (26.9); a mark with arguments cannot be told from the line after it + +# a key's name: a-z, 0-9, _ and . (every key the compiler derives or a game writes), a full stop at its +# end left to the sentence +function kx_name_byte(c: int) -> bool { return (c >= 97 and c <= 122) or (c >= 48 and c <= 57) or c == 95 or c == 46 } + +function kx_name_end(s: string, i: int) -> int { + var e = i + while e < len(s) and kx_name_byte(s[e]) { e += 1 } + while e > i and s[e - 1] == 46 { e -= 1 } + return e +} + +# a string with a mark that is not the whole of it: a mark past its start, or words after a key's name +export function kx_mixed(s: string) -> bool { + let n = len(s) + if n == 0 { return false } + if i18n_is_key(s) { + let e = kx_name_end(s, 1) + return e < n and s[e] != I18N_SEP + } + for i in 1 .. n { if s[i] == I18N_KEY or s[i] == I18N_PLURAL { return true } } + return false +} + +# what L does with such a line: made once per distinct line in this language, kept with the keys +function kx_line(i18n_st: mut I18nState, s: string) -> string { + if i18n_st.kc == null { kc_clear(i18n_st) } + let ci = Dict.get_or(i18n_st.kc, s, -1) + if ci >= 0 { return i18n_st.kc_out[ci] } + var r = kx_unmix(i18n_st, s) + if i18n_st.on { r = whole(i18n_st, r) } + if Dict.size(i18n_st.kc) >= CACHE_MAX { kc_clear(i18n_st) } + @alloc_ok("a line with a key glued in, made into text: kept, up to CACHE_MAX") + Dict.set(i18n_st.kc, s, len(i18n_st.kc_out)) + @alloc_ok("a line with a key glued in: as above") + push(i18n_st.kc_out, r) + return r +} + +@alloc_ok("a line with a key glued in (a cache miss): once per distinct line") +function kx_unmix(i18n_st: mut I18nState, s: string) -> string { + var r = "" + var start = 0 + var i = 0 + while i < len(s) { + if s[i] == I18N_KEY or s[i] == I18N_PLURAL { + let e = kx_name_end(s, i + 1) + r = r + s[start..i] + keyed(i18n_st, s[i..e]) + start = e + i = e + } else { + i += 1 + } + } + return r + s[start..len(s)] +} diff --git a/packages/ludic.i18n/tests/keys_test.ludic b/packages/ludic.i18n/tests/keys_test.ludic index 851898eb..045692eb 100644 --- a/packages/ludic.i18n/tests/keys_test.ludic +++ b/packages/ludic.i18n/tests/keys_test.ludic @@ -85,4 +85,16 @@ program I18nKeysTest { expect(L(i18n_st, trf(k"catch.saw", "Day 3", "5:30", "", "")) == "5:30 saatinde 3. gun gordun.") # a pattern expect(L(i18n_st, trf(k"catch.saw", "Nowhere known", "7", "", "")) == "7 saatinde Nowhere known gordun.") # no line: as it is } + + test "a key's text glued into an English line is made where it sits, until the line is a key of its own" (i18n_st: mut I18nState) { + start (i18n_st) + let elk = tr(k"species.elk.a_name") + expect(L(i18n_st, "Saw " + elk + ", 30 cm.") == "Saw an elk, 30 cm." and L(i18n_st, "Saw " + elk + ".") == "Saw an elk.") + expect(L(i18n_st, elk + ": 30 cm") == "an elk: 30 cm" and L(i18n_st, elk + "\n" + tr(k"pause.quit")) == "an elk\nQuit") + expect(L(i18n_st, "Not owned. " + tr(k"pause.quit")) == "Not owned. Quit" and L(i18n_st, elk + "Quit") == "an elkQuit") + expect(L(i18n_st, trf(k"catch.saw", "Seen: " + elk, "5:30", "", "")) == "You saw Seen: an elk at 5:30.") + i18n_load_text(i18n_st, tt()) + expect(L(i18n_st, "Day " + tr(k"pause.resume")) == "Devam. gun") # the name made, then the line's own pattern + expect(L(i18n_st, trf(k"catch.saw", elk, "5:30", "", "")) == "5:30 saatinde bir geyik gordun.") # a key whole is untouched + } } diff --git a/packages/ludic.i18n/translate.ludic b/packages/ludic.i18n/translate.ludic index e5a2d70c..fe946450 100644 --- a/packages/ludic.i18n/translate.ludic +++ b/packages/ludic.i18n/translate.ludic @@ -1,6 +1,7 @@ # translate.ludic - what the game draws, in the language in use: a key (keyed_render.ludic); else, the old # English form, exact, else a pattern, else padding taken off, else a paragraph a sentence at a time export function L(i18n_st: mut I18nState, s: string) -> string { + if kx_mixed(s) { return kx_line(i18n_st, s) } if i18n_is_key(s) { return keyed(i18n_st, s) } if not i18n_st.on { return s } let ci = Dict.get_or(i18n_st.cache, s, -1)