package org.meshbay.client.shell /** * A sentence from the interface's own catalogues, for the few things the * application draws itself (main.js `nativeText`). The page chooses the * language and nothing else: a confirmation it worded would be one it could * answer for itself. * * The catalogues are `export default { 'key': '…', … }` with single-quoted * strings; a plural entry is an object, of which `other` is taken. */ class NativeText(private val readCatalogue: (String) -> String?) { fun get(key: String, locale: String, params: Map = emptyMap()): String { var text = pick(readCatalogue(locale), key) ?: pick(readCatalogue("en"), key) ?: key // split/join, as i18n.js: a folder name may contain `$&`. for ((k, v) in params) text = text.split("{$k}").joinToString(v) return text } companion object { fun pick(source: String?, key: String): String? { source ?: return null val k = Regex.escape(key) Regex("""(?m)^\s*'$k'\s*:\s*'((?:[^'\\]|\\.)*)'""").find(source)?.let { return unescape(it.groupValues[1]) } Regex("""(?ms)^\s*'$k'\s*:\s*\{.*?\bother\s*:\s*'((?:[^'\\]|\\.)*)'""").find(source)?.let { return unescape(it.groupValues[1]) } return null } fun unescape(s: String): String { val out = StringBuilder() var i = 0 while (i < s.length) { val c = s[i] if (c == '\\' && i + 1 < s.length) { val n = s[i + 1] when (n) { 'n' -> out.append('\n'); 't' -> out.append('\t') 'u' -> if (i + 5 < s.length) { out.append(s.substring(i + 2, i + 6).toInt(16).toChar()); i += 4 } else -> out.append(n) } i += 2 } else { out.append(c); i++ } } return out.toString() } } }