mirror of
https://github.com/yairm210/Unciv.git
synced 2025-08-03 12:37:42 -04:00
Reworked Locale handling + deprecations + linting (#13142)
* Linting and deprecations * Trial and error for Locale factories * Reworked Locale handling * Clean up fastlane for Rusyn * Decide open question in favour of supported methods
This commit is contained in:
parent
8a1f14e683
commit
1c7a50e1c9
@ -143,7 +143,7 @@ open class FileChooser(
|
||||
add(fileScroll).colspan(2).fill().row()
|
||||
addSeparator(height = 1f)
|
||||
fileNameCell = add().colspan(2).growX()
|
||||
row()
|
||||
super.row()
|
||||
|
||||
addCloseButton(Constants.cancel) {
|
||||
reportResult(false)
|
||||
|
@ -59,7 +59,7 @@ class UncivFiles(
|
||||
}
|
||||
|
||||
fun getModsFolder() = getLocalFile("mods")
|
||||
fun getModFolder(modName: String) = getModsFolder().child(modName)
|
||||
fun getModFolder(modName: String): FileHandle = getModsFolder().child(modName)
|
||||
|
||||
/** The folder that holds data that the game changes while running - all the mods, maps, save files, etc */
|
||||
fun getDataFolder() = getLocalFile("")
|
||||
@ -167,7 +167,7 @@ class UncivFiles(
|
||||
/**
|
||||
* Only use this with a [FileHandle] obtained by one of the methods of this class!
|
||||
*/
|
||||
fun saveGame(game: GameInfo, file: FileHandle, saveCompletionCallback: (Exception?) -> Unit = { if (it != null) throw it }) {
|
||||
private fun saveGame(game: GameInfo, file: FileHandle, saveCompletionCallback: (Exception?) -> Unit = { if (it != null) throw it }) {
|
||||
try {
|
||||
debug("Saving GameInfo %s to %s", game.gameId, file.path())
|
||||
val string = gameInfoToString(game)
|
||||
@ -322,7 +322,7 @@ class UncivFiles(
|
||||
//endregion
|
||||
|
||||
//region Scenarios
|
||||
val scenarioFolder = "scenarios"
|
||||
private val scenarioFolder = "scenarios"
|
||||
fun getScenarioFiles() = sequence {
|
||||
for (mod in RulesetCache.values) {
|
||||
val modFolder = mod.folderLocation ?: continue
|
||||
|
@ -19,7 +19,7 @@ import java.io.FileFilter
|
||||
import java.io.InputStream
|
||||
import java.io.InputStreamReader
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
import java.net.URI
|
||||
import java.nio.ByteBuffer
|
||||
import java.util.zip.ZipException
|
||||
|
||||
@ -48,7 +48,7 @@ object Github {
|
||||
// Consider merging this with the Dropbox function
|
||||
/**
|
||||
* Helper opens an url and accesses its input stream, logging errors to the console
|
||||
* @param url String representing a [URL] to download.
|
||||
* @param url String representing a [URI] to download.
|
||||
* @param preDownloadAction Optional callback that will be executed between opening the connection and
|
||||
* accessing its data - passes the [connection][HttpURLConnection] and allows e.g. reading the response headers.
|
||||
* @return The [InputStream] if successful, `null` otherwise.
|
||||
@ -56,9 +56,8 @@ object Github {
|
||||
fun download(url: String, preDownloadAction: (HttpURLConnection) -> Unit = {}): InputStream? {
|
||||
try {
|
||||
// Problem type 1 - opening the URL connection
|
||||
@Suppress("DEPRECATION") // We still support Java < 20
|
||||
with(URL(url).openConnection() as HttpURLConnection)
|
||||
{
|
||||
// URL(string) is deprecated, URI.toUrl(string) API level 36, see [Android Doc](https://developer.android.com/reference/java/net/URI#toURL()):
|
||||
with(URI(url).toURL().openConnection() as HttpURLConnection) {
|
||||
preDownloadAction(this)
|
||||
// Problem type 2 - getting the information
|
||||
try {
|
||||
|
@ -9,8 +9,7 @@ import java.io.DataOutputStream
|
||||
import java.io.InputStream
|
||||
import java.io.InputStreamReader
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
import java.nio.charset.Charset
|
||||
import java.net.URI
|
||||
import java.util.Date
|
||||
import java.util.Timer
|
||||
import kotlin.concurrent.timer
|
||||
@ -25,7 +24,8 @@ object DropBox: FileStorage {
|
||||
if (remainingRateLimitSeconds > 0)
|
||||
throw FileStorageRateLimitReached(remainingRateLimitSeconds)
|
||||
|
||||
with(URL(url).openConnection() as HttpURLConnection) {
|
||||
// URL(string) is deprecated, URI.toUrl(string) API level 36:
|
||||
with(URI(url).toURL().openConnection() as HttpURLConnection) {
|
||||
requestMethod = "POST" // default is GET
|
||||
|
||||
@Suppress("SpellCheckingInspection")
|
||||
|
@ -7,8 +7,6 @@ import com.unciv.Constants
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.logic.multiplayer.FriendList
|
||||
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.Fonts
|
||||
import com.unciv.ui.components.input.KeyboardBindings
|
||||
@ -175,7 +173,7 @@ class GameSettings {
|
||||
}
|
||||
|
||||
fun updateLocaleFromLanguage() {
|
||||
locale = getLocaleFromLanguage(language)
|
||||
locale = LocaleCode.getLocale(language)
|
||||
}
|
||||
|
||||
fun getFontSize(): Int {
|
||||
@ -193,7 +191,7 @@ class GameSettings {
|
||||
}
|
||||
|
||||
fun getCurrentNumberFormat(): NumberFormat {
|
||||
return getNumberFormatFromLanguage(language)
|
||||
return LocaleCode.getNumberFormatFromLanguage(language)
|
||||
}
|
||||
|
||||
//endregion
|
||||
@ -262,67 +260,6 @@ class GameSettings {
|
||||
|
||||
enum class NationPickerListMode { Icons, List }
|
||||
|
||||
/** Map Unciv language key to Java locale, for the purpose of getting a Collator for sorting.
|
||||
* - Effect depends on the Java libraries and may not always conform to expectations.
|
||||
* If in doubt, debug and see what Locale instance you get and compare its properties with `Locale.getDefault()`.
|
||||
* (`Collator.getInstance(LocaleCode.*.run { Locale(language, country) }) to Collator.getInstance()`, drill to both `rules`, compare hashes - if equal and other properties equal, then Java doesn't know your Language))
|
||||
* @property name same as translation file name with ' ', '_', '-', '(', ')' removed
|
||||
* @property language ISO 639-1 code for the language
|
||||
* @property country ISO 3166 code for the nation this is predominantly spoken in
|
||||
* @property trueLanguage If set, used instead of language to trick Java into supplying a close-enough collator (a no-match would otherwise give us the default collator, not a collator for a partial match)
|
||||
*/
|
||||
enum class LocaleCode(val language: String, val country: String, val trueLanguage: String? = null) {
|
||||
Afrikaans("af", "ZA"),
|
||||
Arabic("ar", "IQ"),
|
||||
Bangla("bn", "BD"),
|
||||
Belarusian("be", "BY"),
|
||||
Bosnian("bs", "BA"),
|
||||
BrazilianPortuguese("pt", "BR"),
|
||||
Bulgarian("bg", "BG"),
|
||||
Catalan("ca", "ES"),
|
||||
Croatian("hr", "HR"),
|
||||
Czech("cs", "CZ"),
|
||||
Danish("da", "DK"),
|
||||
Dutch("nl", "NL"),
|
||||
English("en", "US"),
|
||||
Estonian("et", "EE"),
|
||||
Finnish("fi", "FI"),
|
||||
French("fr", "FR"),
|
||||
Galician("gl", "ES"),
|
||||
German("de", "DE"),
|
||||
Greek("el", "GR"),
|
||||
Hindi("hi", "IN"),
|
||||
Hungarian("hu", "HU"),
|
||||
Indonesian("in", "ID"),
|
||||
Italian("it", "IT"),
|
||||
Japanese("ja", "JP"),
|
||||
Korean("ko", "KR"),
|
||||
Latin("la", "IT"),
|
||||
Latvian("lv", "LV"),
|
||||
Lithuanian("lt", "LT"),
|
||||
Malay("ms", "MY"),
|
||||
Norwegian("no", "NO"),
|
||||
NorwegianNynorsk("nn", "NO"),
|
||||
PersianPinglishDIN("fa", "IR"), // These might just fall back to default
|
||||
PersianPinglishUN("fa", "IR"),
|
||||
Polish("pl", "PL"),
|
||||
Portuguese("pt", "PT"),
|
||||
Romanian("ro", "RO"),
|
||||
Russian("ru", "RU"),
|
||||
Rusyn("uk", "UA", "rus"), // No specific locale for rus exists, so use closest for collator
|
||||
Serbian("sr", "RS"),
|
||||
SimplifiedChinese("zh", "CN"),
|
||||
Slovak("sk", "SK"),
|
||||
Spanish("es", "ES"),
|
||||
Swedish("sv", "SE"),
|
||||
Thai("th", "TH"),
|
||||
TraditionalChinese("zh", "TW"),
|
||||
Turkish("tr", "TR"),
|
||||
Ukrainian("uk", "UA"),
|
||||
Vietnamese("vi", "VN"),
|
||||
Zulu("zu", "ZA")
|
||||
}
|
||||
|
||||
//endregion
|
||||
//region Multiplayer-specific
|
||||
|
||||
|
97
core/src/com/unciv/models/metadata/LocaleCode.kt
Normal file
97
core/src/com/unciv/models/metadata/LocaleCode.kt
Normal file
@ -0,0 +1,97 @@
|
||||
package com.unciv.models.metadata
|
||||
|
||||
import java.text.NumberFormat
|
||||
import java.util.Locale
|
||||
|
||||
/** Map Unciv language key to Java locale, for the purpose of getting a Collator for sorting.
|
||||
* - Effect depends on the Java libraries and may not always conform to expectations.
|
||||
* If in doubt, debug and see what Locale instance you get and compare its properties with `Locale.getDefault()`.
|
||||
* (`Collator.getInstance(LocaleCode.*.run { Locale(language, country) }) to Collator.getInstance()`, drill to both `rules`, compare hashes - if equal and other properties equal, then Java doesn't know your Language))
|
||||
* - For languages without an easy predefined Locale, collation or numeric formats can be forced using [Unicode Extensions for BCP 47](https://www.unicode.org/reports/tr35/#Locale_Extension_Key_and_Type_Data).
|
||||
*
|
||||
* @property name **Must** be the same as the translation file name with ' ', '_', '-', '(', ')' removed
|
||||
* @property languageTag IETF BCP 47 language tag - see [forLanguageTag][Locale.forLanguageTag] or [Android reference][https://developer.android.com/reference/java/util/Locale#forLanguageTag(java.lang.String)]
|
||||
* Usually the ISO 639-1 code for the language, a dash, and the ISO 3166 code for the nation this is predominantly spoken in
|
||||
* @property fastlaneFolder If set, it's used instead of the language part of [languageTag] as fastlane folder name
|
||||
*/
|
||||
enum class LocaleCode(val languageTag: String, private val fastlaneFolder: String? = null) {
|
||||
Afrikaans("af-ZA"),
|
||||
Arabic("ar-IQ"),
|
||||
Bangla("bn-BD"),
|
||||
Belarusian("be-BY"),
|
||||
Bosnian("bs-BA"),
|
||||
BrazilianPortuguese("pt-BR"),
|
||||
Bulgarian("bg-BG"),
|
||||
Catalan("ca-ES"),
|
||||
Croatian("hr-HR"),
|
||||
Czech("cs-CZ"),
|
||||
Danish("da-DK"),
|
||||
Dutch("nl-NL"),
|
||||
English("en-US"),
|
||||
Estonian("et-EE"),
|
||||
Finnish("fi-FI"),
|
||||
French("fr-FR"),
|
||||
Galician("gl-ES"),
|
||||
German("de-DE"),
|
||||
Greek("el-GR"),
|
||||
Hindi("hi-IN"),
|
||||
Hungarian("hu-HU"),
|
||||
Indonesian("in-ID"),
|
||||
Italian("it-IT"),
|
||||
Japanese("ja-JP"),
|
||||
Korean("ko-KR"),
|
||||
Latin("la-IT"),
|
||||
Latvian("lv-LV"),
|
||||
Lithuanian("lt-LT"),
|
||||
Malay("ms-MY"),
|
||||
Norwegian("no-NO"),
|
||||
NorwegianNynorsk("nn-NO"),
|
||||
PersianPinglishDIN("fa-IR"), // These might just fall back to default
|
||||
PersianPinglishUN("fa-IR"),
|
||||
Polish("pl-PL"),
|
||||
Portuguese("pt-PT"),
|
||||
Romanian("ro-RO"),
|
||||
Russian("ru-RU"),
|
||||
Rusyn("rue-SK-u-kr-cyrl-latn-digit", "rue"), // No specific locale exists, so use explicit cyrillic collation. Chose country with most speakers.
|
||||
Serbian("sr-RS"),
|
||||
SimplifiedChinese("zh-CN"),
|
||||
Slovak("sk-SK"),
|
||||
Spanish("es-ES"),
|
||||
Swedish("sv-SE"),
|
||||
Thai("th-TH"),
|
||||
TraditionalChinese("zh-TW"),
|
||||
Turkish("tr-TR"),
|
||||
Ukrainian("uk-UA"),
|
||||
Vietnamese("vi-VN"),
|
||||
Zulu("zu-ZA")
|
||||
;
|
||||
|
||||
fun locale(): Locale = Locale.forLanguageTag(languageTag)
|
||||
fun fastlaneFolder(): String = this.fastlaneFolder ?: locale().language
|
||||
|
||||
companion object {
|
||||
private val bannedCharacters = listOf(' ', '_', '-', '(', ')') // Things not to have in enum names
|
||||
|
||||
/** Find a LocaleCode for a [language] as stored in GameSettings */
|
||||
fun find(language: String): LocaleCode? {
|
||||
val languageName = language.filterNot { it in bannedCharacters }
|
||||
return LocaleCode.entries.firstOrNull { it.name == languageName }
|
||||
}
|
||||
|
||||
/** Get a Java Locale for a [language] as stored in GameSettings */
|
||||
fun getLocale(language: String): Locale =
|
||||
find(language)?.locale() ?: Locale.getDefault()
|
||||
|
||||
/** Get the fastlane folder name for a [language] as stored in GameSettings */
|
||||
fun fastlaneFolder(language: String) =
|
||||
find(language)?.fastlaneFolder() ?: "en"
|
||||
|
||||
// NumberFormat cache, key: language, value: NumberFormat
|
||||
private val languageToNumberFormat = mutableMapOf<String, NumberFormat>()
|
||||
|
||||
fun getNumberFormatFromLanguage(language: String): NumberFormat =
|
||||
languageToNumberFormat.getOrPut(language) {
|
||||
NumberFormat.getInstance(getLocale(language))
|
||||
}
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@ import com.unciv.json.json
|
||||
import com.unciv.logic.civilization.diplomacy.DiplomaticModifiers
|
||||
import com.unciv.models.SpyAction
|
||||
import com.unciv.models.metadata.BaseRuleset
|
||||
import com.unciv.models.metadata.GameSettings.LocaleCode
|
||||
import com.unciv.models.metadata.LocaleCode
|
||||
import com.unciv.models.ruleset.Belief
|
||||
import com.unciv.models.ruleset.Building
|
||||
import com.unciv.models.ruleset.Event
|
||||
@ -558,8 +558,7 @@ object TranslationFileWriter {
|
||||
!endWithNewline && translated.endsWith('\n') -> translated.removeSuffix("\n")
|
||||
else -> translated
|
||||
}
|
||||
val localeCode = LocaleCode.valueOf(language.replace("_",""))
|
||||
val path = fastlanePath + (localeCode.trueLanguage ?: localeCode.language)
|
||||
val path = fastlanePath + LocaleCode.fastlaneFolder(language)
|
||||
File(path).mkdirs()
|
||||
File(path + File.separator + fileName).writeText(fileContent)
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ package com.unciv.models.translations
|
||||
import com.badlogic.gdx.Gdx
|
||||
import com.unciv.Constants
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.models.metadata.GameSettings.LocaleCode
|
||||
import com.unciv.models.metadata.LocaleCode
|
||||
import com.unciv.models.ruleset.RulesetCache
|
||||
import com.unciv.models.ruleset.unique.Unique
|
||||
import com.unciv.models.stats.Stat
|
||||
@ -14,7 +14,6 @@ import com.unciv.utils.Log
|
||||
import com.unciv.utils.debug
|
||||
import java.util.Locale
|
||||
import org.jetbrains.annotations.VisibleForTesting
|
||||
import java.text.NumberFormat
|
||||
|
||||
/**
|
||||
* This collection holds all translations for the game.
|
||||
@ -209,11 +208,8 @@ class Translations : LinkedHashMap<String, TranslationEntry>() {
|
||||
return getText(englishConditionalOrderingString, language, null)
|
||||
}
|
||||
|
||||
fun placeConditionalsAfterUnique(language: String): Boolean {
|
||||
if (get(conditionalUniqueOrderString, language, null)?.get(language) == "before")
|
||||
return false
|
||||
return true
|
||||
}
|
||||
fun placeConditionalsAfterUnique(language: String) =
|
||||
get(conditionalUniqueOrderString, language, null)?.get(language) != "before"
|
||||
|
||||
/** Returns the equivalent of a space in the given language
|
||||
* Defaults to a space if no translation is provided
|
||||
@ -239,26 +235,6 @@ class Translations : LinkedHashMap<String, TranslationEntry>() {
|
||||
const val conditionalUniqueOrderString = "ConditionalsPlacement"
|
||||
const val shouldCapitalizeString = "StartWithCapitalLetter"
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -572,5 +548,5 @@ fun Number.tr(): String {
|
||||
|
||||
// formats number according to given language
|
||||
fun Number.tr(language: String): String {
|
||||
return Translations.getNumberFormatFromLanguage(language).format(this)
|
||||
return LocaleCode.getNumberFormatFromLanguage(language).format(this)
|
||||
}
|
||||
|
@ -1,14 +0,0 @@
|
||||
Удтвореня майпопуларної бавкы за створьованя цвілізацій – фрішноє, манінькоє, без рекламы, навхтема бесплатноє
|
||||
Стройте свою цівілізацію, изучайте технолоґії, росширяйте свої території, сказіт тай дотовчіт своїх ворогув!
|
||||
|
||||
Понуканя? Багы? Испис задач сьої бавкы увидьте туйкы https://github.com/yairm210/Unciv/issues, ушитка помуч дуже ся чистує!
|
||||
|
||||
Звіданкы? Коментарії? Просто лудно? Прикапчуйте ся ид нам у діскордови https://discord.gg/bjrB4Xw.
|
||||
|
||||
Хотіли бы-сьте помочи перекласти бавку вашым языком? Напишіт ми мейл на yairm210@hotmail.com.
|
||||
|
||||
Знаєте за Grok Java авадь Kotlin? Пойте сюды https://github.com/yairm210/Unciv.
|
||||
|
||||
Світ вас чекат! Ци вчините из свойої цівілізації імперію, яка утримат провбу часом?
|
||||
|
||||
Позволеня на доступ ид сіті мусай дати про ініціовані бавлячом стирьханя тай про мультіплеєрноє бавліня. Ушиткі другі позволеня самі ся додавут API, якый изхосновали-сьме використали про уповісткы за ходы у совмістнуй бавці. Позволеня на доступ ид интернетови мусай дати, обы пак побрати си моды тай музыку, а ще для коректного ладованя мултіплеєрных бавок. Ушиткі другі міньбы не ініціювут ся ігров Unciv.
|
@ -1 +0,0 @@
|
||||
4X-стратеґія за створьованя цівілізацій
|
@ -5,7 +5,7 @@ import com.badlogic.gdx.Gdx
|
||||
import com.unciv.Constants
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.models.metadata.GameSettings
|
||||
import com.unciv.models.metadata.GameSettings.LocaleCode
|
||||
import com.unciv.models.metadata.LocaleCode
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.RulesetCache
|
||||
import com.unciv.models.stats.Stats
|
||||
|
Loading…
x
Reference in New Issue
Block a user