ludic.i18n: a key with holes inside another key's hole survives - an argument's own 31s and 27s are escaped with 27 and the decoder splits on the unescaped ones (trf(k"a", trf(k"b", x))); keys_test holds it
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
f0cf85943f
commit
bdda22cb2e
5 changed files with 77 additions and 30 deletions
|
|
@ -14,4 +14,5 @@ import "translate.ludic"
|
||||||
import "keys.ludic"
|
import "keys.ludic"
|
||||||
import "keyed.ludic"
|
import "keyed.ludic"
|
||||||
import "keyed_render.ludic"
|
import "keyed_render.ludic"
|
||||||
|
import "keyed_args.ludic"
|
||||||
import "plural.ludic"
|
import "plural.ludic"
|
||||||
|
|
|
||||||
37
packages/ludic.i18n/keyed_args.ludic
Normal file
37
packages/ludic.i18n/keyed_args.ludic
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
# keyed_args.ludic - a key's arguments as the decoder reads them: unescaped, looked up while still English, a count
|
||||||
|
|
||||||
|
# an argument as it was handed in: each 27 before a 27 or a 31 taken off
|
||||||
|
@alloc_ok("a key made into text (a cache miss): an argument that held a key with holes")
|
||||||
|
function kr_unescape(s: string) -> string {
|
||||||
|
var esc = false
|
||||||
|
for i in 0 .. len(s) { if s[i] == I18N_ESC { esc = true } }
|
||||||
|
if not esc { return s }
|
||||||
|
let buf = buffer(len(s))
|
||||||
|
var n = 0
|
||||||
|
var i = 0
|
||||||
|
while i < len(s) {
|
||||||
|
if s[i] == I18N_ESC and i + 1 < len(s) { i += 1 }
|
||||||
|
buf[n] = s[i]
|
||||||
|
n += 1
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
return text_of(buf, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
# an argument with letters in it (not a number, a time, a sign): while the game still hands English
|
||||||
|
# into a key's holes (until its code and data are keys, phase 26.9), it gets the old English lookup
|
||||||
|
function kr_words(s: string) -> bool {
|
||||||
|
for i in 0 .. len(s) { if (s[i] >= 65 and s[i] <= 90) or (s[i] >= 97 and s[i] <= 122) { return true } }
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
# a count written as decimal digits (a leading - allowed); anything else is 0
|
||||||
|
function kr_int(s: string) -> int {
|
||||||
|
var v = 0
|
||||||
|
var neg = false
|
||||||
|
for i in 0 .. len(s) {
|
||||||
|
if i == 0 and s[i] == 45 { neg = true } else if s[i] >= 48 and s[i] <= 57 { v = v * 10 + (s[i] - 48) }
|
||||||
|
}
|
||||||
|
if neg { return -v }
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
@ -28,8 +28,14 @@ function kr_make(i18n_st: mut I18nState, s: string) -> string {
|
||||||
var start = 1
|
var start = 1
|
||||||
var i = 1
|
var i = 1
|
||||||
while i <= len(s) {
|
while i <= len(s) {
|
||||||
if i == len(s) or s[i] == I18N_SEP {
|
if i < len(s) and s[i] == I18N_ESC {
|
||||||
push(args, s[start..i])
|
i += 2 # an escaped byte belongs to its argument
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if i >= len(s) or s[i] == I18N_SEP {
|
||||||
|
var e = i
|
||||||
|
if e > len(s) { e = len(s) }
|
||||||
|
push(args, kr_unescape(s[start..e]))
|
||||||
start = i + 1
|
start = i + 1
|
||||||
}
|
}
|
||||||
i += 1
|
i += 1
|
||||||
|
|
@ -61,21 +67,3 @@ function kr_fill(i18n_st: mut I18nState, t: string, args: []string) -> string {
|
||||||
}
|
}
|
||||||
return r + t[seg..len(t)]
|
return r + t[seg..len(t)]
|
||||||
}
|
}
|
||||||
|
|
||||||
# an argument with letters in it (not a number, a time, a sign): while the game still hands English
|
|
||||||
# into a key's holes (until its code and data are keys, phase 26.9), it gets the old English lookup
|
|
||||||
function kr_words(s: string) -> bool {
|
|
||||||
for i in 0 .. len(s) { if (s[i] >= 65 and s[i] <= 90) or (s[i] >= 97 and s[i] <= 122) { return true } }
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
# a count written as decimal digits (a leading - allowed); anything else is 0
|
|
||||||
function kr_int(s: string) -> int {
|
|
||||||
var v = 0
|
|
||||||
var neg = false
|
|
||||||
for i in 0 .. len(s) {
|
|
||||||
if i == 0 and s[i] == 45 { neg = true } else if s[i] >= 48 and s[i] <= 57 { v = v * 10 + (s[i] - 48) }
|
|
||||||
}
|
|
||||||
if neg { return -v }
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
export const I18N_KEY: int = 1 # k"pause.resume" is "\\x01pause.resume"
|
export const I18N_KEY: int = 1 # k"pause.resume" is "\\x01pause.resume"
|
||||||
export const I18N_PLURAL: int = 2 # kn"catch.count": a count chooses the form
|
export const I18N_PLURAL: int = 2 # kn"catch.count": a count chooses the form
|
||||||
export const I18N_SEP: int = 31 # between the key and each argument
|
export const I18N_SEP: int = 31 # between the key and each argument
|
||||||
|
export const I18N_ESC: int = 27 # inside an argument, before a 27 or a 31 of its own (a key with its own holes)
|
||||||
|
|
||||||
# is this a key (or a key with its arguments), rather than English
|
# is this a key (or a key with its arguments), rather than English
|
||||||
export function i18n_is_key(s: string) -> bool { return len(s) > 0 and (s[0] == I18N_KEY or s[0] == I18N_PLURAL) }
|
export function i18n_is_key(s: string) -> bool { return len(s) > 0 and (s[0] == I18N_KEY or s[0] == I18N_PLURAL) }
|
||||||
|
|
@ -43,23 +44,41 @@ function kx_count(a: string, b: string, c: string, d: string) -> int {
|
||||||
# the marker, the name, then each of the first `n` arguments after a byte 31
|
# the marker, the name, then each of the first `n` arguments after a byte 31
|
||||||
function kx_join(mark: int, name: string, a: string, b: string, c: string, d: string, n: int) -> string {
|
function kx_join(mark: int, name: string, a: string, b: string, c: string, d: string, n: int) -> string {
|
||||||
var size = 1 + len(name)
|
var size = 1 + len(name)
|
||||||
if n > 0 { size += 1 + len(a) }
|
if n > 0 { size += 1 + kx_len(a) }
|
||||||
if n > 1 { size += 1 + len(b) }
|
if n > 1 { size += 1 + kx_len(b) }
|
||||||
if n > 2 { size += 1 + len(c) }
|
if n > 2 { size += 1 + kx_len(c) }
|
||||||
if n > 3 { size += 1 + len(d) }
|
if n > 3 { size += 1 + kx_len(d) }
|
||||||
let buf = buffer(size)
|
let buf = buffer(size)
|
||||||
buf[0] = mark
|
buf[0] = mark
|
||||||
var at = kx_put(buf, 1, name)
|
var at = kx_put(buf, 1, name)
|
||||||
if n > 0 { at = kx_put(buf, kx_sep(buf, at), a) }
|
if n > 0 { at = kx_arg(buf, kx_sep(buf, at), a) }
|
||||||
if n > 1 { at = kx_put(buf, kx_sep(buf, at), b) }
|
if n > 1 { at = kx_arg(buf, kx_sep(buf, at), b) }
|
||||||
if n > 2 { at = kx_put(buf, kx_sep(buf, at), c) }
|
if n > 2 { at = kx_arg(buf, kx_sep(buf, at), c) }
|
||||||
if n > 3 { at = kx_put(buf, kx_sep(buf, at), d) }
|
if n > 3 { at = kx_arg(buf, kx_sep(buf, at), d) }
|
||||||
return text_of(buf, size)
|
return text_of(buf, size)
|
||||||
}
|
}
|
||||||
function kx_sep(buf: []byte, at: int) -> int {
|
function kx_sep(buf: []byte, at: int) -> int {
|
||||||
buf[at] = I18N_SEP
|
buf[at] = I18N_SEP
|
||||||
return at + 1
|
return at + 1
|
||||||
}
|
}
|
||||||
|
# an argument's bytes, a 27 before each 27 or 31 of its own, so a key with holes can be one
|
||||||
|
function kx_len(s: string) -> int {
|
||||||
|
var n = len(s)
|
||||||
|
for i in 0 .. len(s) { if s[i] == I18N_SEP or s[i] == I18N_ESC { n += 1 } }
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
function kx_arg(buf: []byte, at: int, s: string) -> int {
|
||||||
|
var j = at
|
||||||
|
for i in 0 .. len(s) {
|
||||||
|
if s[i] == I18N_SEP or s[i] == I18N_ESC {
|
||||||
|
buf[j] = I18N_ESC
|
||||||
|
j += 1
|
||||||
|
}
|
||||||
|
buf[j] = s[i]
|
||||||
|
j += 1
|
||||||
|
}
|
||||||
|
return j
|
||||||
|
}
|
||||||
function kx_put(buf: []byte, at: int, s: string) -> int {
|
function kx_put(buf: []byte, at: int, s: string) -> int {
|
||||||
for i in 0 .. len(s) { buf[at + i] = s[i] }
|
for i in 0 .. len(s) { buf[at + i] = s[i] }
|
||||||
return at + len(s)
|
return at + len(s)
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,8 @@ program I18nKeysTest {
|
||||||
expect(L(i18n_st, trf(k"hud.day", "12", "", "", "")) == "12. Gun")
|
expect(L(i18n_st, trf(k"hud.day", "12", "", "", "")) == "12. Gun")
|
||||||
expect(L(i18n_st, saw) == "5:30 saatinde bir geyik gordun.")
|
expect(L(i18n_st, saw) == "5:30 saatinde bir geyik gordun.")
|
||||||
expect(L(i18n_st, trf(k"hud.day", "", "", "", "")) == ". Gun") # a missing argument is empty
|
expect(L(i18n_st, trf(k"hud.day", "", "", "", "")) == ". Gun") # a missing argument is empty
|
||||||
|
let nested = trf(k"catch.saw", trf(k"hud.day", "3", "", "", ""), "5:30", "", "") # a key with holes inside a hole
|
||||||
|
expect(L(i18n_st, nested) == "5:30 saatinde 3. Gun gordun.")
|
||||||
expect(key_text(saw) == "catch.saw" and key_text("Settings") == "Settings")
|
expect(key_text(saw) == "catch.saw" and key_text("Settings") == "Settings")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue