Skip to content

Commit

Permalink
TranslationFileWriter support for CivilopediaText (yairm210#4631)
Browse files Browse the repository at this point in the history
* TranslationFileWriter support for CivilopediaText

* Merge branch 'master' into TranslationWriter-Civtext - patch
  • Loading branch information
SomeTroglodyte authored Aug 1, 2021
1 parent 78dddac commit 6c32d10
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 67 deletions.
5 changes: 3 additions & 2 deletions core/src/com/unciv/models/ruleset/Belief.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 4 additions & 2 deletions core/src/com/unciv/models/ruleset/unit/BaseUnit.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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}"
Expand Down
150 changes: 88 additions & 62 deletions core/src/com/unciv/models/translations/TranslationFileWriter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -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>>()
Expand Down Expand Up @@ -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) {
Expand All @@ -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)
}
}
}
}
Expand All @@ -313,6 +332,8 @@ object TranslationFileWriter {
resultStrings!!.add("$specialNewLineCode ${uniqueIndexOfNewLine++}")
}
}
println("Translation writer took ${System.currentTimeMillis()-startMillis}ms for ${jsonsFolder.name()}")

return generatedStrings
}

Expand All @@ -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
}

Expand Down
7 changes: 6 additions & 1 deletion core/src/com/unciv/ui/worldscreen/mainmenu/OptionsPopup.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -245,14 +247,17 @@ 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)
// notify about completion
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()
}
}
Expand Down

0 comments on commit 6c32d10

Please sign in to comment.