package net.torvald.terrarum.langpack import net.torvald.terrarum.App import net.torvald.terrarum.App.printdbg import net.torvald.terrarum.utils.JsonFetcher import java.io.File import java.util.* class LangObject(val key: String, val fromLang: Boolean) { fun get() = if (fromLang) Lang[key] else key } /** * Created by minjaesong on 2016-01-22. */ object Lang { /** * Get record by its STRING_ID * * HashMap<"$key_$language", Value> * * E.g. langpack["MENU_LANGUAGE_THIS_fiFI"] */ private val langpack = HashMap() private val FALLBACK_LANG_CODE = "en" private val HANGUL_SYL_START = 0xAC00 val languageList = HashSet() val POLYGLOT_VERSION = "100" private val PREFIX_POLYGLOT = "Polyglot-${POLYGLOT_VERSION}_" private val PREFIX_NAMESET = "nameset_" private val HANGUL_POST_INDEX_ALPH = intArrayOf(// 0: 는, 가, ... 1: 은, 이, ... 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) private val HANGUL_POST_RO_INDEX_ALPH = intArrayOf(// 0: 로 1: 으로 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) private val ENGLISH_WORD_NORMAL_PLURAL = arrayOf("photo", "demo") private val FRENCH_WORD_NORMAL_PLURAL = arrayOf("bal", "banal", "fatal", "final") init { // load base langs load(File("./assets/locales/")) } @JvmStatic operator fun invoke() { /* dummy method for manual initialisation */ } fun load(localesDir: File) { printdbg(this, "Loading languages from $localesDir") // get all of the languages installed localesDir.listFiles().filter { it.isDirectory }.forEach { languageList.add(it.name) } // temporary filter languageList.remove("jaJPysi") for (lang in languageList) { printdbg(this, "Loading langpack from $localesDir/$lang/") val langFileListFiles = File("$localesDir/$lang/").listFiles() langFileListFiles?.forEach { // not a polyglot if (!it.name.startsWith("Polyglot") && it.name.endsWith(".json")) { processRegularLangfile(it, lang) } else if (it.name.startsWith("Polyglot") && it.name.endsWith(".json")) { processPolyglotLangFile(it, lang) } // else, ignore } } } private fun processRegularLangfile(file: File, lang: String) { val json = JsonFetcher(file) /* * Terrarum langpack JSON structure is: * * (root object) * "<>" = "<>" */ //println(json.entrySet()) JsonFetcher.forEachSiblings(json) { key, value -> langpack.put("${key}_$lang", value.asString()) } } private fun processPolyglotLangFile(file: File, lang: String) { val json = JsonFetcher(file) /* * Polyglot JSON structure is: * * (root object) * "resources": object * "polyglot": object * (polyglot meta) * "data": array * [0]: object * n = "CONTEXT_CHARACTER_CLASS" * s = "Class" * [1]: object * n = "CONTEXT_CHARACTER_DELETE" * s = "Delecte Character" * (the array continues) * */ JsonFetcher.forEachSiblings(json.get("resources").get("data")) { _, entry -> langpack.put( "${entry.getString("n")}_$lang", entry.getString("s") ) } } private val bindOp = ">>=" fun getOrNull(key: String?, capitalise: Boolean = true) = if (key == null) null else get(key, capitalise) /** * Syntax example: * * - `BLOCK_AIR` – Prints out `Lang.get("BLOCK_AIR")` * - `BLOCK_AIR>>=BLOCK_WALL_NAME_TEMPLATE` – Prints out `Formatter().format(Lang.get("BLOCK_WALL_NAME_TEMPLATE"), Lang.get("BLOCK_AIR")).toString()` */ operator fun get(key: String, capitalise: Boolean = true): String { fun getstr(s: String) = getByLocale(s, App.GAME_LOCALE, capitalise) ?: getByLocale(s, FALLBACK_LANG_CODE, capitalise) ?: "$$s" val args = key.split(bindOp).filter { it.isNotBlank() }.map { it.trim() } if (args.isEmpty()) return "" val sb = StringBuilder() val formatter = Formatter(sb) sb.append(getstr(args[0])) args.subList(1, args.size).forEach { val oldstr = sb.toString() sb.clear() formatter.format(getstr(it), oldstr) } return sb.toString() } fun getAndUseTemplate(key: String, capitalise: Boolean = true, vararg arguments: Any?): String { var raw = get(key, capitalise) arguments.forEachIndexed { index, it0 -> val it = if (capitalise) it0.toString().capitalize() else it0.toString() raw = raw.replace("{${index}}", it) } return raw } /** * Does NOT parse the operators */ fun getByLocale(key: String, locale: String, capitalise: Boolean): String? { val ret = langpack["${key}_$locale"] ?: return null fun String.CAP() = if (capitalise) this.capitalize() else this return if (locale.startsWith("bg")) "${App.fontGame.charsetOverrideBulgarian}${ret.CAP()}${App.fontGame.charsetOverrideDefault}" else if (locale.startsWith("sr")) "${App.fontGame.charsetOverrideSerbian}${ret.CAP()}${App.fontGame.charsetOverrideDefault}" else ret.CAP() } private fun String.getEndTag() = this.split("_").last() fun pluraliseLang(key: String, count: Int): String { return if (count > 1) get(key + "_PLURAL") else get(key) } fun pluralise(word: String, count: Int): String { if (count < 2) return word when (App.GAME_LOCALE) { "fr" -> { if (Arrays.binarySearch(FRENCH_WORD_NORMAL_PLURAL, word) >= 0) { return word + "s" } if (word.endsWith("al") || word.endsWith("au") || word.endsWith("eu") || word.endsWith("eau")) { return word.substring(0, word.length - 2) + "ux" } else if (word.endsWith("ail")) { return word.substring(0, word.length - 3) + "ux" } else { return word + "s" } } "en" -> { if (Arrays.binarySearch(ENGLISH_WORD_NORMAL_PLURAL, word) >= 0) { return word + "s" } else if (word.endsWith("f")) { // f -> ves return word.substring(0, word.length - 2) + "ves" } else if (word.endsWith("o") || word.endsWith("z")) { // o -> oes return word + "es" } else { return word + "s" } } else -> { if (Arrays.binarySearch(ENGLISH_WORD_NORMAL_PLURAL, word) >= 0) { return word + "s" } else if (word.endsWith("f")) { return word.substring(0, word.length - 2) + "ves" } else if (word.endsWith("o") || word.endsWith("z")) { return word + "es" } else { return word + "s" } } } } fun postEunNeun(word: String): String { val lastChar = getLastChar(word) if (isHangul(lastChar)) { val index = lastChar.toInt() - HANGUL_SYL_START return if (index % 28 == 0) word + "는" else word + "은" } else if (lastChar in 'A'..'Z' || lastChar in 'a'..'z') { val index = (lastChar.toInt() - 0x41) % 0x20 return if (HANGUL_POST_INDEX_ALPH[index] == 0) word + "는" else word + "은" } else { return "은" } } fun postIiGa(word: String): String { val lastChar = getLastChar(word) if (isHangul(lastChar)) { val index = lastChar.toInt() - HANGUL_SYL_START return if (index % 28 == 0) word + "가" else word + "이" } else if (lastChar in 'A'..'Z' || lastChar in 'a'..'z') { val index = (lastChar.toInt() - 0x41) % 0x20 return if (HANGUL_POST_INDEX_ALPH[index] == 0) word + "가" else word + "이" } else { return "이" } } private fun isHangul(c: Char): Boolean { return c.toInt() in 0xAC00..0xD7A3 } private fun getLastChar(s: String): Char { return s[s.length - 1] } }