code wiki / _hdl_build / nx_cms_i18n.nx
nx_cms_i18n.nx source
↩ module page · 63 lines · 3141 B
1// nx_cms_i18n.nx -- CMS MULTILINGUAL content resolution (sovereign, offline, RFC-4647 "Lookup" correct).
2// The WPML/Polylang class, made Nishi-native: a content bundle stores locale-tagged fields in the
3// nx_cms_store record format (`@title.fr-CA`, `@title.fr`, `@title.en`, bare `@title`), and resolution
4// follows the RFC 4647 Lookup algorithm -- try the exact tag, then progressively drop the rightmost
5// subtag (fr-CA -> fr; zh-Hant-TW -> zh-Hant -> zh), then a configured default locale, then the bare
6// field. Sovereign + offline (no WPML cloud/license server, no per-string API call = the exceed angle),
7// deterministic, and it NEVER serves a wrong-locale value silently (a true miss returns -1). Composes
8// nx_cms_store (cst_get). license_tier: ORIGINAL
9import "nx_cms_store.nx"
10import "nx_syscalls.nx"
11
12// resolution tiers (also the out_tier value)
13const NX_I18N_EXACT: i64 = 0 // matched the requested tag verbatim
14const NX_I18N_TRUNC: i64 = 1 // matched a truncated tag (region/script subtag dropped)
15const NX_I18N_DEFAULT: i64 = 2 // fell back to the configured default locale
16const NX_I18N_BARE: i64 = 3 // fell back to the un-tagged bare field
17const NX_I18N_NONE: i64 = 0 - 1 // no value at any tier (honest miss)
18
19// build "field.locale" (NUL-terminated) into out; returns length
20func i18n_key(out: *u8, field: *u8, locale: *u8) -> i64 {
21 var o: i64 = 0
22 var i: i64 = 0
23 while field[i] != (0 as u8) { out[o] = field[i]; o = o + 1; i = i + 1 }
24 out[o] = 46 as u8; o = o + 1 // '.'
25 i = 0
26 while locale[i] != (0 as u8) { out[o] = locale[i]; o = o + 1; i = i + 1 }
27 out[o] = 0 as u8
28 return o
29}
30
31// resolve field for locale with RFC 4647 Lookup fallback over store buf[0..n). Writes the value into
32// out (cap), sets *out_tier to the tier that matched, returns the value length or NX_I18N_NONE.
33func i18n_resolve(store: *u8, n: i64, field: *u8, locale: *u8, deflocale: *u8, out: *u8, cap: i64, out_tier: *i64) -> i64 {
34 let cand: *u8 = sys_mmap(256)
35 let keyb: *u8 = sys_mmap(512)
36 var ll: i64 = 0
37 while locale[ll] != (0 as u8) { cand[ll] = locale[ll]; ll = ll + 1 }
38 cand[ll] = 0 as u8
39 var first: i64 = 1
40 while ll > 0 {
41 i18n_key(keyb, field, cand)
42 let r: i64 = cst_get(store, n, keyb, out, cap)
43 if r >= 0 {
44 if first == 1 { out_tier[0] = NX_I18N_EXACT } else { out_tier[0] = NX_I18N_TRUNC }
45 return r
46 }
47 first = 0
48 // truncate cand at its rightmost '-' (drop one subtag); stop if none
49 var cut: i64 = 0 - 1
50 var i: i64 = 0
51 while i < ll { if (cand[i] as i64) == 45 { cut = i } i = i + 1 }
52 if cut < 0 { ll = 0 } else { ll = cut; cand[ll] = 0 as u8 }
53 }
54 // configured default locale
55 i18n_key(keyb, field, deflocale)
56 let rd: i64 = cst_get(store, n, keyb, out, cap)
57 if rd >= 0 { out_tier[0] = NX_I18N_DEFAULT; return rd }
58 // bare, un-tagged field
59 let rb: i64 = cst_get(store, n, field, out, cap)
60 if rb >= 0 { out_tier[0] = NX_I18N_BARE; return rb }
61 out_tier[0] = NX_I18N_NONE
62 return NX_I18N_NONE
63}