mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-13 12:04:06 +09:00
277 lines
8.9 KiB
Kotlin
277 lines
8.9 KiB
Kotlin
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<String, String>()
|
||
private val FALLBACK_LANG_CODE = "en"
|
||
|
||
private val HANGUL_SYL_START = 0xAC00
|
||
|
||
val languageList = HashSet<String>()
|
||
|
||
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)
|
||
* "<<STRING ID>>" = "<<LOCALISED TEXT>>"
|
||
*/
|
||
//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]
|
||
}
|
||
}
|