mirror of
https://github.com/yairm210/Unciv.git
synced 2025-01-10 23:37:31 +07:00
TranslationFileWriter support for CivilopediaText (#4631)
* TranslationFileWriter support for CivilopediaText * Merge branch 'master' into TranslationWriter-Civtext - patch
This commit is contained in:
parent
78dddac962
commit
6c32d10924
@ -2,16 +2,17 @@ package com.unciv.models.ruleset
|
||||
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.models.stats.INamed
|
||||
import com.unciv.ui.civilopedia.CivilopediaText
|
||||
import com.unciv.ui.civilopedia.FormattedLine
|
||||
import com.unciv.ui.civilopedia.ICivilopediaText
|
||||
import java.util.ArrayList
|
||||
|
||||
class Belief: INamed, CivilopediaText() {
|
||||
class Belief : INamed, ICivilopediaText {
|
||||
override var name: String = ""
|
||||
var type: BeliefType = BeliefType.None
|
||||
var uniques = ArrayList<String>()
|
||||
val uniqueObjects: List<Unique> by lazy { uniques.map { Unique(it) } }
|
||||
|
||||
override var civilopediaText = listOf<FormattedLine>()
|
||||
|
||||
override fun makeLink() = "Belief/$name"
|
||||
override fun replacesCivilopediaDescription() = true
|
||||
|
@ -9,7 +9,7 @@ import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.Unique
|
||||
import com.unciv.models.stats.INamed
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.civilopedia.CivilopediaText
|
||||
import com.unciv.ui.civilopedia.ICivilopediaText
|
||||
import com.unciv.ui.civilopedia.FormattedLine
|
||||
import com.unciv.ui.utils.Fonts
|
||||
import kotlin.math.pow
|
||||
@ -18,7 +18,7 @@ import kotlin.math.pow
|
||||
|
||||
/** This is the basic info of the units, as specified in Units.json,
|
||||
in contrast to MapUnit, which is a specific unit of a certain type that appears on the map */
|
||||
class BaseUnit : INamed, IConstruction, CivilopediaText() {
|
||||
class BaseUnit : INamed, IConstruction, ICivilopediaText {
|
||||
|
||||
override lateinit var name: String
|
||||
var cost: Int = 0
|
||||
@ -41,6 +41,8 @@ class BaseUnit : INamed, IConstruction, CivilopediaText() {
|
||||
var uniqueTo: String? = null
|
||||
var attackSound: String? = null
|
||||
|
||||
override var civilopediaText = listOf<FormattedLine>()
|
||||
|
||||
fun getShortDescription(): String {
|
||||
val infoList = mutableListOf<String>()
|
||||
if (strength != 0) infoList += "$strength${Fonts.strength}"
|
||||
|
@ -16,6 +16,7 @@ import com.unciv.models.ruleset.unit.UnitType
|
||||
import com.unciv.models.stats.Stat
|
||||
import com.unciv.models.stats.Stats
|
||||
import java.lang.reflect.Field
|
||||
import java.lang.reflect.Modifier
|
||||
|
||||
object TranslationFileWriter {
|
||||
|
||||
@ -181,6 +182,52 @@ object TranslationFileWriter {
|
||||
}
|
||||
|
||||
private fun generateStringsFromJSONs(jsonsFolder: FileHandle): LinkedHashMap<String, MutableSet<String>> {
|
||||
// build maps identifying parameters as certain types of filters - unitFilter etc
|
||||
val ruleset = RulesetCache.getBaseRuleset()
|
||||
val tileFilterMap = ruleset.terrains.keys.toMutableSet().apply { addAll(sequenceOf(
|
||||
"Friendly Land",
|
||||
"Foreign Land",
|
||||
"Fresh water",
|
||||
"non-fresh water",
|
||||
"Open Terrain",
|
||||
"Rough Terrain",
|
||||
"Natural Wonder"
|
||||
)) }
|
||||
val tileImprovementMap = ruleset.tileImprovements.keys.toMutableSet().apply { add("Great Improvement") }
|
||||
val buildingMap = ruleset.buildings.keys.toMutableSet().apply { addAll(sequenceOf(
|
||||
"Wonders",
|
||||
"Wonder",
|
||||
"Buildings",
|
||||
"Building"
|
||||
)) }
|
||||
val unitTypeMap = UnitType.values().map { it.name }.toMutableSet().apply { addAll(sequenceOf(
|
||||
"Military",
|
||||
"Civilian",
|
||||
"non-air",
|
||||
"relevant",
|
||||
"Nuclear Weapon",
|
||||
"Submarine",
|
||||
// These are up for debate
|
||||
"Air",
|
||||
"land units",
|
||||
"water units",
|
||||
"air units",
|
||||
"military units",
|
||||
"submarine units"
|
||||
// Note: this can't handle combinations of parameters (e.g. [{Military} {Water}])
|
||||
)) }
|
||||
val cityFilterMap = setOf(
|
||||
"in this city",
|
||||
"in all cities",
|
||||
"in all coastal cities",
|
||||
"in capital",
|
||||
"in all non-occupied cities",
|
||||
"in all cities with a world wonder",
|
||||
"in all cities connected to capital",
|
||||
"in all cities with a garrison"
|
||||
)
|
||||
|
||||
val startMillis = System.currentTimeMillis()
|
||||
|
||||
// Using LinkedHashMap (instead of HashMap) is important to maintain the order of sections in the translation file
|
||||
val generatedStrings = LinkedHashMap<String, MutableSet<String>>()
|
||||
@ -215,55 +262,16 @@ object TranslationFileWriter {
|
||||
var parameterName = when {
|
||||
parameter.toFloatOrNull() != null -> "amount"
|
||||
Stat.values().any { it.name == parameter } -> "stat"
|
||||
RulesetCache.getBaseRuleset().terrains.containsKey(parameter)
|
||||
|| parameter == "Friendly Land"
|
||||
|| parameter == "Foreign Land"
|
||||
|| parameter == "Fresh water"
|
||||
|| parameter == "non-fresh water"
|
||||
|| parameter == "Open Terrain"
|
||||
|| parameter == "Rough Terrain"
|
||||
|| parameter == "Natural Wonder"
|
||||
-> "tileFilter"
|
||||
RulesetCache.getBaseRuleset().units.containsKey(parameter) -> "unit"
|
||||
RulesetCache.getBaseRuleset().tileImprovements.containsKey(parameter)
|
||||
|| parameter == "Great Improvement"
|
||||
-> "tileImprovement"
|
||||
RulesetCache.getBaseRuleset().tileResources.containsKey(parameter) -> "resource"
|
||||
RulesetCache.getBaseRuleset().technologies.containsKey(parameter) -> "tech"
|
||||
RulesetCache.getBaseRuleset().unitPromotions.containsKey(parameter) -> "promotion"
|
||||
RulesetCache.getBaseRuleset().buildings.containsKey(parameter)
|
||||
|| parameter == "Wonders"
|
||||
|| parameter == "Wonder"
|
||||
|| parameter == "Buildings"
|
||||
|| parameter == "Building"
|
||||
-> "building"
|
||||
|
||||
UnitType.values().any { it.name == parameter }
|
||||
|| parameter == "Military"
|
||||
|| parameter == "Civilian"
|
||||
|| parameter == "non-air"
|
||||
|| parameter == "relevant"
|
||||
|| parameter == "Nuclear Weapon"
|
||||
|| parameter == "Submarine"
|
||||
// These are up for debate
|
||||
|| parameter == "Air"
|
||||
|| parameter == "land units"
|
||||
|| parameter == "water units"
|
||||
|| parameter == "air units"
|
||||
|| parameter == "military units"
|
||||
|| parameter == "submarine units"
|
||||
// Note: this can't handle combinations of parameters (e.g. [{Military} {Water}])
|
||||
-> "unitType"
|
||||
parameter in tileFilterMap -> "tileFilter"
|
||||
ruleset.units.containsKey(parameter) -> "unit"
|
||||
parameter in tileImprovementMap -> "tileImprovement"
|
||||
ruleset.tileResources.containsKey(parameter) -> "resource"
|
||||
ruleset.technologies.containsKey(parameter) -> "tech"
|
||||
ruleset.unitPromotions.containsKey(parameter) -> "promotion"
|
||||
parameter in buildingMap -> "building"
|
||||
parameter in unitTypeMap -> "unitType"
|
||||
Stats.isStats(parameter) -> "stats"
|
||||
parameter == "in this city"
|
||||
|| parameter == "in all cities"
|
||||
|| parameter == "in all coastal cities"
|
||||
|| parameter == "in capital"
|
||||
|| parameter == "in all non-occupied cities"
|
||||
|| parameter == "in all cities with a world wonder"
|
||||
|| parameter == "in all cities connected to capital"
|
||||
|| parameter == "in all cities with a garrison"
|
||||
-> "cityFilter"
|
||||
parameter in cityFilterMap -> "cityFilter"
|
||||
else -> "param"
|
||||
}
|
||||
if (parameterName in existingParameterNames) {
|
||||
@ -285,23 +293,34 @@ object TranslationFileWriter {
|
||||
submitString(element)
|
||||
return
|
||||
}
|
||||
val allFields = (element.javaClass.declaredFields + element.javaClass.fields
|
||||
+ element.javaClass.superclass.declaredFields) // This is so the main PolicyBranch, which inherits from Policy, will recognize its Uniques and have them translated
|
||||
.filter {
|
||||
val allFields = (
|
||||
element.javaClass.declaredFields
|
||||
+ element.javaClass.fields
|
||||
// Include superclass so the main PolicyBranch, which inherits from Policy,
|
||||
// will recognize its Uniques and have them translated
|
||||
+ element.javaClass.superclass.declaredFields
|
||||
).filter {
|
||||
it.type == String::class.java ||
|
||||
it.type == java.util.ArrayList::class.java ||
|
||||
it.type == java.util.HashSet::class.java
|
||||
it.type == java.util.ArrayList::class.java ||
|
||||
it.type == java.util.List::class.java || // CivilopediaText is not an ArrayList
|
||||
it.type == java.util.HashSet::class.java ||
|
||||
it.type.isEnum // allow scanning Enum names
|
||||
}
|
||||
for (field in allFields) {
|
||||
field.isAccessible = true
|
||||
val fieldValue = field.get(element)
|
||||
if (isFieldTranslatable(field, fieldValue)) { // skip fields which must not be translated
|
||||
// this field can contain sub-objects, let's serialize them as well
|
||||
if (fieldValue is java.util.AbstractCollection<*>) {
|
||||
for (item in fieldValue)
|
||||
if (item is String) submitString(item) else serializeElement(item!!)
|
||||
} else
|
||||
submitString(fieldValue)
|
||||
@Suppress("RemoveRedundantQualifierName") // to clarify List does _not_ inherit from anything in java.util
|
||||
when (fieldValue) {
|
||||
is java.util.AbstractCollection<*> ->
|
||||
for (item in fieldValue)
|
||||
if (item is String) submitString(item) else serializeElement(item!!)
|
||||
is kotlin.collections.List<*> ->
|
||||
for (item in fieldValue)
|
||||
if (item is String) submitString(item) else serializeElement(item!!)
|
||||
else -> submitString(fieldValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -313,6 +332,8 @@ object TranslationFileWriter {
|
||||
resultStrings!!.add("$specialNewLineCode ${uniqueIndexOfNewLine++}")
|
||||
}
|
||||
}
|
||||
println("Translation writer took ${System.currentTimeMillis()-startMillis}ms for ${jsonsFolder.name()}")
|
||||
|
||||
return generatedStrings
|
||||
}
|
||||
|
||||
@ -323,16 +344,21 @@ object TranslationFileWriter {
|
||||
"providesFreeBuilding", "replaces", "requiredBuilding", "requiredBuildingInAllCities",
|
||||
"requiredNearbyImprovedResources", "requiredResource", "requiredTech", "requires",
|
||||
"resourceTerrainAllow", "revealedBy", "startBias", "techRequired",
|
||||
"terrainsCanBeBuiltOn", "terrainsCanBeFoundOn", "turnsInto", "uniqueTo", "upgradesTo"
|
||||
"terrainsCanBeBuiltOn", "terrainsCanBeFoundOn", "turnsInto", "uniqueTo", "upgradesTo",
|
||||
"link", "icon", "extraImage", "color" // FormattedLine
|
||||
)
|
||||
private val translatableEnumsSet = setOf("BeliefType")
|
||||
|
||||
private fun isFieldTranslatable(field: Field, fieldValue: Any?): Boolean {
|
||||
// Exclude fields by name that contain references to items defined elsewhere
|
||||
// - the definition should cause the inclusion in our translatables list, not the reference.
|
||||
// This prevents duplication within the base game (e.g. Mines were duplicated by being output
|
||||
// by both TerrainResources and TerrainImprovements) and duplication of base game items into
|
||||
// or are otherwise Strings but not user-displayed.
|
||||
// The Modifier.STATIC exclusion removes fields from e.g. companion objects.
|
||||
// Fields of enum types need that type explicitly allowed in translatableEnumsSet
|
||||
// (Using an annotation failed, even with @Retention RUNTIME they were invisible to reflection)
|
||||
return fieldValue != null &&
|
||||
fieldValue != "" &&
|
||||
(field.modifiers and Modifier.STATIC) == 0 &&
|
||||
(!field.type.isEnum || field.type.simpleName in translatableEnumsSet) &&
|
||||
field.name !in untranslatableFieldSet
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ package com.unciv.ui.worldscreen.mainmenu
|
||||
|
||||
import com.badlogic.gdx.Application
|
||||
import com.badlogic.gdx.Gdx
|
||||
import com.badlogic.gdx.Input
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.*
|
||||
import com.unciv.MainMenuScreen
|
||||
@ -13,6 +14,7 @@ import com.unciv.models.translations.TranslationFileWriter
|
||||
import com.unciv.models.translations.Translations
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.utils.*
|
||||
import com.unciv.ui.utils.UncivTooltip.Companion.addTooltip
|
||||
import com.unciv.ui.worldscreen.WorldScreen
|
||||
import java.util.*
|
||||
import kotlin.concurrent.thread
|
||||
@ -245,7 +247,7 @@ class OptionsPopup(val previousScreen:CameraStageBaseScreen) : Popup(previousScr
|
||||
private fun addTranslationGeneration() {
|
||||
if (Gdx.app.type == Application.ApplicationType.Desktop) {
|
||||
val generateTranslationsButton = "Generate translation files".toTextButton()
|
||||
generateTranslationsButton.onClick {
|
||||
val generateAction = {
|
||||
val translations = Translations()
|
||||
translations.readAllLanguagesTranslation()
|
||||
TranslationFileWriter.writeNewTranslationFiles(translations)
|
||||
@ -253,6 +255,9 @@ class OptionsPopup(val previousScreen:CameraStageBaseScreen) : Popup(previousScr
|
||||
generateTranslationsButton.setText("Translation files are generated successfully.".tr())
|
||||
generateTranslationsButton.disable()
|
||||
}
|
||||
generateTranslationsButton.onClick(generateAction)
|
||||
keyPressDispatcher[Input.Keys.F12] = generateAction
|
||||
generateTranslationsButton.addTooltip("F12",18f)
|
||||
optionsTable.add(generateTranslationsButton).colspan(2).row()
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user