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}