User: moulberry
Date: 03 May 24 09:26
Revision: 336924bed50a7b31ab01929da3477c5742cf9b02
Summary:
Translation: option to force json and configurable default i18n call
TeamCity URL: https://ci.mcdev.io/viewModification.html?tab=vcsModificationFiles&modId=9361&personal=false
Index: src/main/kotlin/TranslationSettings.kt
===================================================================
--- src/main/kotlin/TranslationSettings.kt (revision 336924bed50a7b31ab01929da3477c5742cf9b02)
+++ src/main/kotlin/TranslationSettings.kt (revision 336924bed50a7b31ab01929da3477c5742cf9b02)
@@ -0,0 +1,71 @@
+/*
+ * Minecraft Development for IntelliJ
+ *
+ * https://mcdev.io/
+ *
+ * Copyright (C) 2024 minecraft-dev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, version 3.0 only.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.demonwav.mcdev
+
+import com.intellij.openapi.components.PersistentStateComponent
+import com.intellij.openapi.components.State
+import com.intellij.openapi.components.Storage
+import com.intellij.openapi.components.service
+import com.intellij.openapi.project.Project
+
+@State(name = "TranslationSettings", storages = [Storage("minecraft_dev.xml")])
+class TranslationSettings : PersistentStateComponent {
+
+ data class State(
+ var isForceJsonTranslationFile: Boolean = false,
+ var isUseCustomConvertToTranslationTemplate: Boolean = false,
+ var convertToTranslationTemplate: String = "net.minecraft.client.resources.I18n.format(\"\$key\")",
+ )
+
+ private var state = State()
+
+ override fun getState(): State {
+ return state
+ }
+
+ override fun loadState(state: State) {
+ this.state = state
+ }
+
+ // State mappings
+ var isForceJsonTranslationFile: Boolean
+ get() = state.isForceJsonTranslationFile
+ set(forceJsonTranslationFile) {
+ state.isForceJsonTranslationFile = forceJsonTranslationFile
+ }
+
+ var isUseCustomConvertToTranslationTemplate: Boolean
+ get() = state.isUseCustomConvertToTranslationTemplate
+ set(useCustomConvertToTranslationTemplate) {
+ state.isUseCustomConvertToTranslationTemplate = useCustomConvertToTranslationTemplate
+ }
+
+ var convertToTranslationTemplate: String
+ get() = state.convertToTranslationTemplate
+ set(convertToTranslationTemplate) {
+ state.convertToTranslationTemplate = convertToTranslationTemplate
+ }
+
+ companion object {
+ @JvmStatic
+ fun getInstance(project: Project): TranslationSettings = project.service()
+ }
+}
Index: src/main/kotlin/platform/mcp/mappings/HardcodedYarnToMojmap.kt
===================================================================
--- src/main/kotlin/platform/mcp/mappings/HardcodedYarnToMojmap.kt (revision caca374c623d9f3315f3c9f9fc3e40597c40b28f)
+++ src/main/kotlin/platform/mcp/mappings/HardcodedYarnToMojmap.kt (revision 336924bed50a7b31ab01929da3477c5742cf9b02)
@@ -43,6 +43,15 @@
owner = "net.minecraft.network.chat.Component",
name = "translatableEscape",
descriptor = "(Ljava/lang/String;[Ljava/lang/Object;)Lnet/minecraft/network/chat/MutableComponent;"
+ ),
+ MemberReference(
+ owner = "net.minecraft.client.resource.language.I18n",
+ name = "translate",
+ descriptor = "(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;"
+ ) mapTo MemberReference(
+ owner = "net.minecraft.client.resources.language.I18n",
+ name = "get",
+ descriptor = "(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;"
)
),
hashMapOf(),
Index: src/main/kotlin/platform/mcp/mappings/Mappings.kt
===================================================================
--- src/main/kotlin/platform/mcp/mappings/Mappings.kt (revision caca374c623d9f3315f3c9f9fc3e40597c40b28f)
+++ src/main/kotlin/platform/mcp/mappings/Mappings.kt (revision 336924bed50a7b31ab01929da3477c5742cf9b02)
@@ -119,6 +119,13 @@
return getMappedMethod(MemberReference(mojangMethod, mojangDescriptor, mojangClass))
}
+fun Module.getMappedMethodCall(mojangClass: String, mojangMethod: String, mojangDescriptor: String, p: String): String {
+ val mappedMethodRef = namedToMojang?.tryGetMappedMethod(
+ MemberReference(mojangMethod, mojangDescriptor, mojangClass)
+ ) ?: return "$mojangClass.$mojangMethod($p)"
+ return "${mappedMethodRef.owner}.${mappedMethodRef.name}($p)"
+}
+
fun Module.getMojangMethod(mappedMethod: MemberReference): String {
return namedToMojang?.getIntermediaryMethod(mappedMethod)?.name ?: return mappedMethod.name
}
Index: src/main/kotlin/translations/TranslationFiles.kt
===================================================================
--- src/main/kotlin/translations/TranslationFiles.kt (revision caca374c623d9f3315f3c9f9fc3e40597c40b28f)
+++ src/main/kotlin/translations/TranslationFiles.kt (revision 336924bed50a7b31ab01929da3477c5742cf9b02)
@@ -20,6 +20,7 @@
package com.demonwav.mcdev.translations
+import com.demonwav.mcdev.TranslationSettings
import com.demonwav.mcdev.translations.index.TranslationIndex
import com.demonwav.mcdev.translations.index.TranslationInverseIndex
import com.demonwav.mcdev.translations.lang.LangFile
@@ -110,12 +111,43 @@
element.delete()
}
+ fun findTranslationKeyForText(context: PsiElement, text: String): String? {
+ val module = context.findModule()
+ ?: throw IllegalArgumentException("Cannot add translation for element outside of module")
+ var jsonVersion = true
+ if (!TranslationSettings.getInstance(context.project).isForceJsonTranslationFile) {
+ val version =
+ context.mcVersion ?: throw IllegalArgumentException("Cannot determine MC version for element $context")
+ jsonVersion = version > MC_1_12_2
+ }
+
+ if (!jsonVersion) {
+ // This feature only supports JSON translation files
+ return null
+ }
+
+ val files = FileTypeIndex.getFiles(
+ JsonFileType.INSTANCE,
+ GlobalSearchScope.moduleScope(module),
+ ).filter { getLocale(it) == TranslationConstants.DEFAULT_LOCALE }
+
+ for (file in files) {
+ val psiFile = PsiManager.getInstance(context.project).findFile(file) ?: continue
+ psiFile.findKeyForTextAsJson(text)?.let { return it }
+ }
+
+ return null
+ }
+
fun add(context: PsiElement, key: String, text: String) {
val module = context.findModule()
?: throw IllegalArgumentException("Cannot add translation for element outside of module")
+ var jsonVersion = true
+ if (!TranslationSettings.getInstance(context.project).isForceJsonTranslationFile) {
- val version =
- context.mcVersion ?: throw IllegalArgumentException("Cannot determine MC version for element $context")
+ val version =
+ context.mcVersion ?: throw IllegalArgumentException("Cannot determine MC version for element $context")
- val jsonVersion = version > MC_1_12_2
+ jsonVersion = version > MC_1_12_2
+ }
fun write(files: Iterable) {
for (file in files) {
@@ -223,6 +255,13 @@
doc.insertString(rootObject.lastChild.prevSibling.textOffset, content)
}
+ private fun PsiFile.findKeyForTextAsJson(text: String): String? {
+ val rootObject = this.firstChild as? JsonObject ?: return null
+ return rootObject.propertyList.firstOrNull {
+ (it.value as? JsonStringLiteral)?.value == text
+ }?.name
+ }
+
private fun generateJsonFile(
leadingComma: Boolean,
indent: CharSequence,
@@ -292,9 +331,12 @@
fun buildSortingTemplateFromDefault(context: PsiElement, domain: String? = null): Template? {
val module = context.findModule()
?: throw IllegalArgumentException("Cannot add translation for element outside of module")
+ var jsonVersion = true
+ if (!TranslationSettings.getInstance(context.project).isForceJsonTranslationFile) {
- val version =
- context.mcVersion ?: throw IllegalArgumentException("Cannot determine MC version for element $context")
+ val version =
+ context.mcVersion ?: throw IllegalArgumentException("Cannot determine MC version for element $context")
- val jsonVersion = version > MC_1_12_2
+ jsonVersion = version > MC_1_12_2
+ }
val defaultTranslationFile = FileBasedIndex.getInstance()
.getContainingFiles(
Index: src/main/kotlin/translations/intentions/ConvertToTranslationIntention.kt
===================================================================
--- src/main/kotlin/translations/intentions/ConvertToTranslationIntention.kt (revision caca374c623d9f3315f3c9f9fc3e40597c40b28f)
+++ src/main/kotlin/translations/intentions/ConvertToTranslationIntention.kt (revision 336924bed50a7b31ab01929da3477c5742cf9b02)
@@ -20,7 +20,10 @@
package com.demonwav.mcdev.translations.intentions
+import com.demonwav.mcdev.TranslationSettings
+import com.demonwav.mcdev.platform.mcp.mappings.getMappedMethodCall
import com.demonwav.mcdev.translations.TranslationFiles
+import com.demonwav.mcdev.util.findModule
import com.demonwav.mcdev.util.runWriteAction
import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction
import com.intellij.lang.java.JavaLanguage
@@ -42,6 +45,9 @@
override fun invoke(project: Project, editor: Editor, element: PsiElement) {
if (element.parent is PsiLiteral) {
val value = (element.parent as PsiLiteral).value as? String ?: return
+
+ val existingKey = TranslationFiles.findTranslationKeyForText(element, value)
+
val result = Messages.showInputDialogWithCheckBox(
"Enter translation key:",
"Convert String Literal to Translation",
@@ -49,7 +55,7 @@
true,
true,
Messages.getQuestionIcon(),
- null,
+ existingKey,
object : InputValidatorEx {
override fun getErrorText(inputString: String): String? {
if (inputString.isEmpty()) {
@@ -73,12 +79,24 @@
val key = result.first ?: return
val replaceLiteral = result.second
try {
+ if (existingKey != key) {
- TranslationFiles.add(element, key, value)
+ TranslationFiles.add(element, key, value)
+ }
if (replaceLiteral) {
+ val translationSettings = TranslationSettings.getInstance(project)
val psi = PsiDocumentManager.getInstance(project).getPsiFile(editor.document) ?: return
psi.runWriteAction {
val expression = JavaPsiFacade.getElementFactory(project).createExpressionFromText(
- "net.minecraft.client.resources.I18n.format(\"$key\")",
+ if (translationSettings.isUseCustomConvertToTranslationTemplate) {
+ translationSettings.convertToTranslationTemplate.replace("\$key", key)
+ } else {
+ element.findModule()?.getMappedMethodCall(
+ "net.minecraft.client.resource.language.I18n",
+ "translate",
+ "(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;",
+ "\"$key\""
+ ) ?: "net.minecraft.client.resource.I18n.get(\"$key\")"
+ },
element.context,
)
if (psi.language === JavaLanguage.INSTANCE) {
Index: src/main/kotlin/translations/sorting/TranslationTemplateConfigurable.kt
===================================================================
--- src/main/kotlin/translations/sorting/TranslationTemplateConfigurable.kt (revision caca374c623d9f3315f3c9f9fc3e40597c40b28f)
+++ src/main/kotlin/translations/sorting/TranslationTemplateConfigurable.kt (revision 336924bed50a7b31ab01929da3477c5742cf9b02)
@@ -20,6 +20,7 @@
package com.demonwav.mcdev.translations.sorting
+import com.demonwav.mcdev.TranslationSettings
import com.demonwav.mcdev.asset.MCDevBundle
import com.demonwav.mcdev.translations.lang.colors.LangSyntaxHighlighter
import com.intellij.codeInsight.template.impl.TemplateEditorUtil
@@ -32,7 +33,13 @@
import com.intellij.openapi.options.Configurable
import com.intellij.openapi.project.Project
import com.intellij.ui.dsl.builder.Align
+import com.intellij.ui.dsl.builder.COLUMNS_LARGE
+import com.intellij.ui.dsl.builder.bindSelected
+import com.intellij.ui.dsl.builder.bindText
+import com.intellij.ui.dsl.builder.columns
import com.intellij.ui.dsl.builder.panel
+import com.intellij.ui.dsl.builder.selected
+import com.intellij.ui.layout.ComponentPredicate
import com.intellij.util.ui.JBUI
import java.awt.BorderLayout
import javax.swing.DefaultComboBoxModel
@@ -46,7 +53,7 @@
private var templateEditor: Editor? = null
private val editorPanel = JPanel(BorderLayout()).apply {
- preferredSize = JBUI.size(250, 450)
+ preferredSize = JBUI.size(250, 350)
minimumSize = preferredSize
}
@@ -62,8 +69,27 @@
row {
cell(editorPanel).align(Align.FILL)
}
+
+ val translationSettings = TranslationSettings.getInstance(project)
+ row {
+ checkBox(MCDevBundle("minecraft.settings.translation.force_json_translation_file"))
+ .bindSelected(translationSettings::isForceJsonTranslationFile)
- }
+ }
+ lateinit var allowConvertToTranslationTemplate: ComponentPredicate
+ row {
+ val checkBox = checkBox(MCDevBundle("minecraft.settings.translation.use_custom_convert_template"))
+ .bindSelected(translationSettings::isUseCustomConvertToTranslationTemplate)
+ allowConvertToTranslationTemplate = checkBox.selected
+ }
+
+ row {
+ textField().bindText(translationSettings::convertToTranslationTemplate)
+ .enabledIf(allowConvertToTranslationTemplate)
+ .columns(COLUMNS_LARGE)
+ }
+ }
+
@Nls
override fun getDisplayName() = MCDevBundle("minecraft.settings.lang_template.display_name")
@@ -107,7 +133,7 @@
}
override fun isModified(): Boolean {
- return templateEditor?.document?.text != getActiveTemplateText() != false
+ return templateEditor?.document?.text != getActiveTemplateText() != false || panel.isModified()
}
override fun apply() {
@@ -120,9 +146,12 @@
} else if (project != null) {
TemplateManager.writeProjectTemplate(project, editor.document.text)
}
+
+ panel.apply()
}
override fun reset() {
init()
+ panel.reset()
}
}
Index: src/main/resources/META-INF/plugin.xml
===================================================================
--- src/main/resources/META-INF/plugin.xml (revision caca374c623d9f3315f3c9f9fc3e40597c40b28f)
+++ src/main/resources/META-INF/plugin.xml (revision 336924bed50a7b31ab01929da3477c5742cf9b02)
@@ -285,6 +285,7 @@
+
Index: src/main/resources/messages/MinecraftDevelopment.properties
===================================================================
--- src/main/resources/messages/MinecraftDevelopment.properties (revision caca374c623d9f3315f3c9f9fc3e40597c40b28f)
+++ src/main/resources/messages/MinecraftDevelopment.properties (revision 336924bed50a7b31ab01929da3477c5742cf9b02)
@@ -213,3 +213,6 @@
minecraft.settings.lang_template.comment=You may edit the template used for translation key sorting here.\
Each line may be empty, a comment (with #) or a glob pattern for matching translation keys (like "item.*").\
Note: Empty lines are respected and will be put into the sorting result.
+minecraft.settings.translation=Translation
+minecraft.settings.translation.force_json_translation_file=Force JSON translation file (1.13+)
+minecraft.settings.translation.use_custom_convert_template=Use custom template for convert literal to translation