mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-22 10:54:19 -04:00
Translate numbers to selected language (#11898)
* implement number translation * fix previous error * maybe this will fix things? * try to make the regex more concise and fix the issues * update implementation * Transient cache * implement Number.tr() * Update core/src/com/unciv/models/metadata/GameSettings.kt Co-authored-by: Yair Morgenstern <yairm210@hotmail.com> * fix formatting * move getNumberFormatFromLanguage() to Translations.kt and cache NumberFormat * remove null cases and resolve suggestions * why was I doing this? I forgot. * move getLocaleFromLanguage() to Translations.kt * add tests * Update TranslationTests.kt fix issues * also remove also * fix all given cases * fix missing language param --------- Co-authored-by: Yair Morgenstern <yairm210@hotmail.com>
This commit is contained in:
parent
ebba2eaeea
commit
461a7ac99d
@ -7,6 +7,8 @@ import com.unciv.Constants
|
|||||||
import com.unciv.UncivGame
|
import com.unciv.UncivGame
|
||||||
import com.unciv.logic.multiplayer.FriendList
|
import com.unciv.logic.multiplayer.FriendList
|
||||||
import com.unciv.models.UncivSound
|
import com.unciv.models.UncivSound
|
||||||
|
import com.unciv.models.translations.Translations.Companion.getLocaleFromLanguage
|
||||||
|
import com.unciv.models.translations.Translations.Companion.getNumberFormatFromLanguage
|
||||||
import com.unciv.ui.components.fonts.FontFamilyData
|
import com.unciv.ui.components.fonts.FontFamilyData
|
||||||
import com.unciv.ui.components.fonts.Fonts
|
import com.unciv.ui.components.fonts.Fonts
|
||||||
import com.unciv.ui.components.input.KeyboardBindings
|
import com.unciv.ui.components.input.KeyboardBindings
|
||||||
@ -15,6 +17,7 @@ import com.unciv.ui.screens.worldscreen.NotificationsScroll
|
|||||||
import com.unciv.utils.Display
|
import com.unciv.utils.Display
|
||||||
import com.unciv.utils.ScreenOrientation
|
import com.unciv.utils.ScreenOrientation
|
||||||
import java.text.Collator
|
import java.text.Collator
|
||||||
|
import java.text.NumberFormat
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
@ -169,14 +172,7 @@ class GameSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun updateLocaleFromLanguage() {
|
fun updateLocaleFromLanguage() {
|
||||||
val bannedCharacters = listOf(' ', '_', '-', '(', ')') // Things not to have in enum names
|
locale = getLocaleFromLanguage(language)
|
||||||
val languageName = language.filterNot { it in bannedCharacters }
|
|
||||||
locale = try {
|
|
||||||
val code = LocaleCode.valueOf(languageName)
|
|
||||||
Locale(code.language, code.country)
|
|
||||||
} catch (_: Exception) {
|
|
||||||
Locale.getDefault()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getFontSize(): Int {
|
fun getFontSize(): Int {
|
||||||
@ -193,6 +189,10 @@ class GameSettings {
|
|||||||
return Collator.getInstance(getCurrentLocale())
|
return Collator.getInstance(getCurrentLocale())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getCurrentNumberFormat(): NumberFormat {
|
||||||
|
return getNumberFormatFromLanguage(language)
|
||||||
|
}
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
//region <Nested classes>
|
//region <Nested classes>
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package com.unciv.models.translations
|
|||||||
import com.badlogic.gdx.Gdx
|
import com.badlogic.gdx.Gdx
|
||||||
import com.unciv.Constants
|
import com.unciv.Constants
|
||||||
import com.unciv.UncivGame
|
import com.unciv.UncivGame
|
||||||
|
import com.unciv.models.metadata.GameSettings.LocaleCode
|
||||||
import com.unciv.models.ruleset.RulesetCache
|
import com.unciv.models.ruleset.RulesetCache
|
||||||
import com.unciv.models.ruleset.unique.Unique
|
import com.unciv.models.ruleset.unique.Unique
|
||||||
import com.unciv.models.stats.Stat
|
import com.unciv.models.stats.Stat
|
||||||
@ -13,6 +14,7 @@ import com.unciv.utils.Log
|
|||||||
import com.unciv.utils.debug
|
import com.unciv.utils.debug
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import org.jetbrains.annotations.VisibleForTesting
|
import org.jetbrains.annotations.VisibleForTesting
|
||||||
|
import java.text.NumberFormat
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This collection holds all translations for the game.
|
* This collection holds all translations for the game.
|
||||||
@ -231,6 +233,26 @@ class Translations : LinkedHashMap<String, TranslationEntry>() {
|
|||||||
const val conditionalUniqueOrderString = "ConditionalsPlacement"
|
const val conditionalUniqueOrderString = "ConditionalsPlacement"
|
||||||
const val shouldCapitalizeString = "StartWithCapitalLetter"
|
const val shouldCapitalizeString = "StartWithCapitalLetter"
|
||||||
const val effectBeforeCause = "EffectBeforeCause"
|
const val effectBeforeCause = "EffectBeforeCause"
|
||||||
|
|
||||||
|
// NumberFormat cache, key: language, value: NumberFormat
|
||||||
|
private val languageToNumberFormat = mutableMapOf<String, NumberFormat>()
|
||||||
|
|
||||||
|
fun getLocaleFromLanguage(language: String): Locale {
|
||||||
|
val bannedCharacters =
|
||||||
|
listOf(' ', '_', '-', '(', ')') // Things not to have in enum names
|
||||||
|
val languageName = language.filterNot { it in bannedCharacters }
|
||||||
|
return try {
|
||||||
|
val code = LocaleCode.valueOf(languageName)
|
||||||
|
Locale(code.language, code.country)
|
||||||
|
} catch (_: Exception) {
|
||||||
|
Locale.getDefault()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getNumberFormatFromLanguage(language: String): NumberFormat =
|
||||||
|
languageToNumberFormat.getOrPut(language) {
|
||||||
|
NumberFormat.getInstance(getLocaleFromLanguage(language))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,6 +274,9 @@ val curlyBraceRegex = Regex("""\{([^}]*)\}""")
|
|||||||
@Suppress("RegExpRedundantEscape") // Some Android versions need ]}) escaped
|
@Suppress("RegExpRedundantEscape") // Some Android versions need ]}) escaped
|
||||||
val pointyBraceRegex = Regex("""\<([^>]*)\>""")
|
val pointyBraceRegex = Regex("""\<([^>]*)\>""")
|
||||||
|
|
||||||
|
// Used to match continous digits 0, 12, 1232 etc
|
||||||
|
@Suppress("RegExpRedundantEscape") // Some Android versions need ]}) escaped
|
||||||
|
val digitsRegex = Regex("""\d+""")
|
||||||
|
|
||||||
object TranslationActiveModsCache {
|
object TranslationActiveModsCache {
|
||||||
private var cachedHash = Int.MIN_VALUE
|
private var cachedHash = Int.MIN_VALUE
|
||||||
@ -443,7 +468,9 @@ private fun String.translatePlaceholders(language: String, hideIcons: Boolean):
|
|||||||
private fun String.translateIndividualWord(language: String, hideIcons: Boolean): String {
|
private fun String.translateIndividualWord(language: String, hideIcons: Boolean): String {
|
||||||
if (Stats.isStats(this)) return Stats.parse(this).toString()
|
if (Stats.isStats(this)) return Stats.parse(this).toString()
|
||||||
|
|
||||||
val translation = UncivGame.Current.translations.getText(this, language, TranslationActiveModsCache.activeMods)
|
val translation = UncivGame.Current.translations.getText(
|
||||||
|
this, language, TranslationActiveModsCache.activeMods
|
||||||
|
).replace(digitsRegex) { it.value.toLong().tr(language) }
|
||||||
|
|
||||||
val stat = Stat.safeValueOf(this)
|
val stat = Stat.safeValueOf(this)
|
||||||
if (stat != null) return stat.character + translation
|
if (stat != null) return stat.character + translation
|
||||||
@ -528,3 +555,13 @@ fun String.removeConditionals(): String {
|
|||||||
.replace(" ", " ")
|
.replace(" ", " ")
|
||||||
.trim()
|
.trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// formats number according to current language
|
||||||
|
fun Number.tr(): String {
|
||||||
|
return UncivGame.Current.settings.getCurrentNumberFormat().format(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
// formats number according to given language
|
||||||
|
fun Number.tr(language: String): String {
|
||||||
|
return Translations.getNumberFormatFromLanguage(language).format(this)
|
||||||
|
}
|
||||||
|
@ -5,6 +5,7 @@ import com.badlogic.gdx.Gdx
|
|||||||
import com.unciv.Constants
|
import com.unciv.Constants
|
||||||
import com.unciv.UncivGame
|
import com.unciv.UncivGame
|
||||||
import com.unciv.models.metadata.GameSettings
|
import com.unciv.models.metadata.GameSettings
|
||||||
|
import com.unciv.models.metadata.GameSettings.LocaleCode
|
||||||
import com.unciv.models.ruleset.Ruleset
|
import com.unciv.models.ruleset.Ruleset
|
||||||
import com.unciv.models.ruleset.RulesetCache
|
import com.unciv.models.ruleset.RulesetCache
|
||||||
import com.unciv.models.stats.Stats
|
import com.unciv.models.stats.Stats
|
||||||
@ -392,6 +393,52 @@ class TranslationTests {
|
|||||||
additionalTest?.invoke(translated)
|
additionalTest?.invoke(translated)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testNumberTr() {
|
||||||
|
UncivGame.Current = UncivGame()
|
||||||
|
UncivGame.Current.settings = GameSettings()
|
||||||
|
|
||||||
|
val testCases = arrayOf(1, -1, 0.123, -0.123)
|
||||||
|
|
||||||
|
val expectedEnglishOutputs = arrayOf("1", "-1", "0.123", "-0.123")
|
||||||
|
Assert.assertArrayEquals(
|
||||||
|
"Number.tr()", expectedEnglishOutputs, testCases.map { it.tr() }.toTypedArray()
|
||||||
|
)
|
||||||
|
Assert.assertArrayEquals(
|
||||||
|
"Number.tr(${LocaleCode.English.name})",
|
||||||
|
expectedEnglishOutputs,
|
||||||
|
testCases.map { it.tr(LocaleCode.English.name) }.toTypedArray()
|
||||||
|
)
|
||||||
|
|
||||||
|
val expectedBanglaOutputs = arrayOf("১", "-১", "০.১২৩", "-০.১২৩")
|
||||||
|
Assert.assertArrayEquals(
|
||||||
|
"Number.tr(${LocaleCode.Bangla.name})",
|
||||||
|
expectedBanglaOutputs,
|
||||||
|
testCases.map { it.tr(LocaleCode.Bangla.name) }.toTypedArray()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testStringsWithNumbers() {
|
||||||
|
UncivGame.Current = UncivGame()
|
||||||
|
UncivGame.Current.settings = GameSettings()
|
||||||
|
|
||||||
|
val tests = arrayOf("1", "+1", "-1", "1.0", "+1.0", "-1.0", "0%", "1/2", "(3/4)")
|
||||||
|
|
||||||
|
UncivGame.Current.settings.language = LocaleCode.English.name
|
||||||
|
Assert.assertArrayEquals(
|
||||||
|
"English", tests, // assume unchanged
|
||||||
|
tests.map { it.tr() }.toTypedArray()
|
||||||
|
)
|
||||||
|
|
||||||
|
UncivGame.Current.settings.language = LocaleCode.Bangla.name
|
||||||
|
Assert.assertArrayEquals(
|
||||||
|
"Bangla",
|
||||||
|
arrayOf("১", "+১", "-১", "১.০", "+১.০", "-১.০", "০%", "১/২", "(৩/৪)"),
|
||||||
|
tests.map { it.tr() }.toTypedArray()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// @Test
|
// @Test
|
||||||
// fun allConditionalsAreContainedInConditionalOrderTranslation() {
|
// fun allConditionalsAreContainedInConditionalOrderTranslation() {
|
||||||
// val orderedConditionals = Translations.englishConditionalOrderingString
|
// val orderedConditionals = Translations.englishConditionalOrderingString
|
||||||
|
Loading…
x
Reference in New Issue
Block a user