Files
Terrarum/src/net/torvald/terrarum/langpack/Lang.kt
2023-08-14 03:53:25 +09:00

277 lines
8.9 KiB
Kotlin
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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]
}
}