add lang debug command and fix found translation issue

This commit is contained in:
alexcrea 2026-08-17 13:54:41 +02:00
parent f373cae100
commit fbd3bf2eb2
Signed by: alexcrea
GPG key ID: E59DF23EE2A2266C
13 changed files with 240 additions and 40 deletions

View file

@ -83,7 +83,7 @@ public class GroupConfigSubSettingGui extends MappedToListSubSettingGui implemen
ItemMeta selectItemMeta = selectItem.getItemMeta(); ItemMeta selectItemMeta = selectItem.getItemMeta();
assert selectItemMeta != null; assert selectItemMeta != null;
ComponentUtil.INSTANCE.setMessageName(selectItemMeta, materialSelectionName); ComponentUtil.INSTANCE.setMessageName(selectItemMeta, materialSelectionName, name);
selectItem.setItemMeta(selectItemMeta); selectItem.setItemMeta(selectItemMeta);
this.materialSelection = new GuiItem(selectItem, (event) -> { this.materialSelection = new GuiItem(selectItem, (event) -> {
@ -100,7 +100,7 @@ public class GroupConfigSubSettingGui extends MappedToListSubSettingGui implemen
ItemMeta selectGroupMeta = selectGroup.getItemMeta(); ItemMeta selectGroupMeta = selectGroup.getItemMeta();
assert selectGroupMeta != null; assert selectGroupMeta != null;
ComponentUtil.INSTANCE.setMessageName(selectGroupMeta, selectGroupName); ComponentUtil.INSTANCE.setMessageName(selectGroupMeta, selectGroupName, name);
selectGroup.setItemMeta(selectGroupMeta); selectGroup.setItemMeta(selectGroupMeta);
this.groupSelection = new GuiItem(selectGroup, (event) -> { this.groupSelection = new GuiItem(selectGroup, (event) -> {

View file

@ -9,9 +9,14 @@ import net.md_5.bungee.api.chat.hover.content.Text
import org.bukkit.command.Command import org.bukkit.command.Command
import org.bukkit.command.CommandSender import org.bukkit.command.CommandSender
import org.bukkit.entity.Player import org.bukkit.entity.Player
import xyz.alexcrea.cuanvil.lang.Lang
import xyz.alexcrea.cuanvil.lang.Message import xyz.alexcrea.cuanvil.lang.Message
import xyz.alexcrea.cuanvil.lang.MsgCommand import xyz.alexcrea.cuanvil.lang.MsgCommand
import xyz.alexcrea.cuanvil.lang.MsgError
import xyz.alexcrea.cuanvil.lang.MsgUI
import xyz.alexcrea.cuanvil.lang.MsgWarning
import xyz.alexcrea.cuanvil.util.ComponentUtil.serializePlain import xyz.alexcrea.cuanvil.util.ComponentUtil.serializePlain
import java.util.Locale
class DebugToggleExecutor: CASubCommand { class DebugToggleExecutor: CASubCommand {
@ -51,6 +56,10 @@ class DebugToggleExecutor : CASubCommand {
MsgCommand.DEBUG_LOG_CLEARED.send(sender) MsgCommand.DEBUG_LOG_CLEARED.send(sender)
} }
"lang" -> {
executeLanguageDebug(sender, args)
}
else -> { else -> {
MsgCommand.SHARED_UNKNOWN_SUB_COMMAND.send(sender) MsgCommand.SHARED_UNKNOWN_SUB_COMMAND.send(sender)
return false return false
@ -106,14 +115,142 @@ class DebugToggleExecutor : CASubCommand {
} }
} }
private fun executeLanguageDebug(sender: CommandSender, args: Array<out String>) {
if(args.size > 1 && "details".contentEquals(args[1], ignoreCase = true))
detailedLangDebug(sender)
else
simpleLangDebug(sender)
}
private fun simpleLangDebug(sender: CommandSender) {
var validCount = 0
// load key from all provider class
MsgCommand.DEBUG_DATA_HEADER
MsgUI.SHARED_CONFIG_NO_EDIT_PERM
MsgError.LOAD_LISTENERS
MsgWarning.ANVIL_GENERIC_EXCEPTION
val registeredKeys = Message.getValues()
for(message in registeredKeys)
if(Lang.has(message.key)) validCount++
val valid = (100.0 * validCount) / registeredKeys.size
sender.sendMessage("Translated (${Lang.currentLang()}): ${"%.1f".format(Locale.ROOT, valid)}% ($validCount/${registeredKeys.size})")
}
private fun detailedLangDebug(sender: CommandSender) {
simpleLangDebug(sender)
val stb = StringBuilder("Report of potential issue for language ${Lang.currentLang()}:\n")
var hadAny = false
val keySet = mutableSetOf<String>()
val registeredKeys = Message.getValues()
for(message in registeredKeys) {
val key = message.key
if(!Lang.has(key)) {
stb.append("Missing key inside translation file: $key\n")
hadAny = true
} else if(hashParamIssue(message, stb))
hadAny = true
if(keySet.contains(key)) {
stb.append("Duplicate registered key: $key\n")
hadAny = true
} else keySet.add(key)
}
for(key in Lang.getKeys()) {
if(!keySet.contains(key)) {
stb.append("Found unregistered key: $key\n")
hadAny = true
}
}
if(hadAny) {
val message = TextComponent(MsgCommand.DEBUG_LANG_COPY.legacy())
message.clickEvent = ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, stb.toString())
message.hoverEvent = HoverEvent(
HoverEvent.Action.SHOW_TEXT,
Text(MsgCommand.SHARED_HOVER_COPY.legacy())
)
sender.spigot().sendMessage(message);
} else {
sender.sendMessage("No additional issue found")
}
}
private fun hashParamIssue(message: Message, stb: StringBuilder): Boolean {
val section = Lang.getSection(message.key)
val texts = if(section == null)
listOf(Lang.getTranslated(message.key))
else
section.getValues(false).map { it.value.toString() }
val textParams = ArrayList<String>()
for(text in texts) {
var index = 0
while(true) {
index = text.indexOf('%', index)
//TODO add \% to "ignore" % as param inside param finder
if(index > 0 && text[index-1] == '\\') {
index++
continue
}
if(index++ < 0) break
var end = text.indexOf(' ', index)
if(end == -1) end = text.length
val param = text.substring(index, end)
if(!textParams.contains(param)) textParams.add(param)
}
}
var hadIssue = false
// Check all parameter are valid
val usedParam = mutableSetOf<String>()
for(textParam in textParams) {
var found = false
for(param in message.params) {
if(param.isEmpty()) continue
if(textParam.startsWith(param)) {
found = true
usedParam.add(param)
break
}
}
if(!found) {
hadIssue = true
stb.append("Did not found param %$textParam in register list for ${message.key}\n")
}
}
for(param in message.params) {
if(usedParam.contains(param)) continue
if("unused".contentEquals(param)) continue
hadIssue = true
stb.append("Param %$param is not used for key ${message.key}\n")
}
return hadIssue
}
override fun tabCompleter(sender: CommandSender, args: Array<out String>, list: MutableList<String>) { override fun tabCompleter(sender: CommandSender, args: Array<out String>, list: MutableList<String>) {
if(!allowed(sender)) return if(!allowed(sender)) return
list.addAll( list.addAll(
when(args.size) { when(args.size) {
1 -> listOf("toggle", "get", "get-and-clear", "clear") 1 -> listOf("toggle", "get", "get-and-clear", "clear", "lang")
2 -> when(args[0].lowercase()) { 2 -> when(args[0].lowercase()) {
"toggle" -> listOf("default", "verbose") "toggle" -> listOf("default", "verbose")
"lang" -> listOf("details")
else -> listOf() else -> listOf()
} }

View file

@ -3,7 +3,6 @@ package xyz.alexcrea.cuanvil.dependency
import io.delilaheve.CustomAnvil import io.delilaheve.CustomAnvil
import net.kyori.adventure.text.Component import net.kyori.adventure.text.Component
import org.bukkit.Bukkit import org.bukkit.Bukkit
import org.bukkit.ChatColor
import org.bukkit.command.CommandSender import org.bukkit.command.CommandSender
import org.bukkit.entity.HumanEntity import org.bukkit.entity.HumanEntity
import org.bukkit.entity.Player import org.bukkit.entity.Player
@ -32,7 +31,6 @@ import xyz.alexcrea.cuanvil.lang.MsgWarning
import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener.Companion.ANVIL_OUTPUT_SLOT import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener.Companion.ANVIL_OUTPUT_SLOT
import xyz.alexcrea.cuanvil.util.MetricsUtil.trackError import xyz.alexcrea.cuanvil.util.MetricsUtil.trackError
import java.lang.IllegalStateException import java.lang.IllegalStateException
import java.util.logging.Level
@Suppress("UnstableApiUsage") @Suppress("UnstableApiUsage")
object DependencyManager { object DependencyManager {
@ -166,7 +164,7 @@ object DependencyManager {
trackError(e) trackError(e)
// Finally, warn the player // Finally, warn the player
MsgWarning.DEPENDENCY_GENERIC_EXCEPTION.send(target) MsgWarning.ANVIL_GENERIC_EXCEPTION.send(target)
} }
private fun logExceptionAndClear(view: AnvilView, e: Exception) { private fun logExceptionAndClear(view: AnvilView, e: Exception) {

View file

@ -3,6 +3,7 @@ package xyz.alexcrea.cuanvil.lang
import io.delilaheve.CustomAnvil import io.delilaheve.CustomAnvil
import org.bukkit.configuration.ConfigurationSection import org.bukkit.configuration.ConfigurationSection
import xyz.alexcrea.cuanvil.config.ConfigHolder import xyz.alexcrea.cuanvil.config.ConfigHolder
import java.util.stream.Stream
object Lang { object Lang {
@ -51,6 +52,18 @@ object Lang {
return default.getSection(key) return default.getSection(key)
} }
fun currentLang(): String {
return lang.name
}
fun has(key: String): Boolean {
return lang.has(key)
}
fun getKeys(): Collection<String> {
return lang.getFilteredKeys()
}
/* /*
* Config Options & get * Config Options & get
*/ */

View file

@ -55,6 +55,45 @@ class Language(private val id: String, private val default: Boolean = false) {
return conf.getConfigurationSection(key) return conf.getConfigurationSection(key)
} }
fun has(key: String): Boolean {
if(conf.isString(key)) return true
// we want at all child key as valid numbers and valid key if claimed to be multi line
val section = getSection(key) ?: return false
for(key in section.getKeys(false)) {
if(key.toUIntOrNull() == null) return false
if(!section.isString(key)) return false
}
return true
}
fun getFilteredKeys(): Collection<String> {
val result = ArrayList<String>()
// First pass we ignore key from root
for(root in conf.getKeys(false)) {
val section = conf.getConfigurationSection(root) ?: continue
exploreDeeper(section, root, result)
}
return result
}
private fun exploreDeeper(section: ConfigurationSection, root: String, result: ArrayList<String>) {
for(key in section.getKeys(false)) {
val newRoot = "$root.$key"
if(has(key)) {
result.add(newRoot)
continue
}
val newSection = section.getConfigurationSection(key) ?: continue
exploreDeeper(newSection, newRoot, result)
}
}
val name: String val name: String
get() = conf.getString("name", id)!! get() = conf.getString("name", id)!!
} }

View file

@ -10,10 +10,23 @@ import xyz.alexcrea.cuanvil.util.ComponentUtil.send
import xyz.alexcrea.cuanvil.util.ComponentUtil.serializeLegacy import xyz.alexcrea.cuanvil.util.ComponentUtil.serializeLegacy
import xyz.alexcrea.cuanvil.util.ComponentUtil.serializePlain import xyz.alexcrea.cuanvil.util.ComponentUtil.serializePlain
import xyz.alexcrea.cuanvil.util.MiniMessageUtil import xyz.alexcrea.cuanvil.util.MiniMessageUtil
import java.util.Collections
import java.util.logging.Level import java.util.logging.Level
import kotlin.math.min import kotlin.math.min
open class Message(val key: String, vararg val params: String) { open class Message(val key: String, vararg val params: String, register: Boolean = true) {
companion object {
private val values = ArrayList<Message>()
fun getValues(): Collection<Message> {
return Collections.unmodifiableCollection(values)
}
}
init {
if(register) values.add(this)
}
protected fun replaceParameters(stb: StringBuilder, vararg values: Any) { protected fun replaceParameters(stb: StringBuilder, vararg values: Any) {
// replace all placeholder thingy %key -> value // replace all placeholder thingy %key -> value

View file

@ -1,4 +0,0 @@
package xyz.alexcrea.cuanvil.lang
object Msg {
}

View file

@ -20,6 +20,7 @@ object MsgCommand {
val DEBUG_LOG_CLEARED = Message("debug.log-cleared") val DEBUG_LOG_CLEARED = Message("debug.log-cleared")
val DEBUG_TOGGLED = Message("debug.toggled", "type") val DEBUG_TOGGLED = Message("debug.toggled", "type")
val DEBUG_COPY = Message("debug.copy") val DEBUG_COPY = Message("debug.copy")
val DEBUG_LANG_COPY = Message("debug.copy-lang")
val DEBUG_DATA_HEADER = Message("debug.data.header") val DEBUG_DATA_HEADER = Message("debug.data.header")
val DEBUG_DATA_LINE_COUNT = Message("debug.data.line-count", "count") val DEBUG_DATA_LINE_COUNT = Message("debug.data.line-count", "count")
@ -45,8 +46,8 @@ object MsgCommand {
// Enchant // Enchant
val ENCHANT_DESCRIPTION = Message("enchant.description") val ENCHANT_DESCRIPTION = Message("enchant.description")
val ENCHANT_REMOVE = Message("enchant.remove.", "name") val ENCHANT_REMOVE = Message("enchant.removed", "name")
val ENCHANT_SET = Message("enchant.set.", "name", "level") val ENCHANT_SET = Message("enchant.set", "name", "level")
val ENCHANT_MISSING_PARAMETER_WARNING = Message("enchant.warning.missing_parameter") val ENCHANT_MISSING_PARAMETER_WARNING = Message("enchant.warning.missing_parameter")
val ENCHANT_NOT_FOUND_WARNING = Message("enchant.warning.not_found", "path") val ENCHANT_NOT_FOUND_WARNING = Message("enchant.warning.not_found", "path")

View file

@ -16,7 +16,7 @@ object MsgError {
val LOAD_NON_DEFAULT_CONFIG = ErrorMessage("load.non-default-config") val LOAD_NON_DEFAULT_CONFIG = ErrorMessage("load.non-default-config")
val RELOAD_FAIL = ErrorMessage("reload.resource.fail", "path") val RELOAD_FAIL = ErrorMessage("reload.resource.fail", "path")
val RELOAD_HARD_FAIL = ErrorMessage("reload.resource.hardfail") val RELOAD_HARD_FAIL = ErrorMessage("reload.resource.hard-fail")
/* /*
* ---- * ----

View file

@ -16,44 +16,44 @@ object MsgUI {
val ELEMENT_LIST_CANCELLED_NEW = Message("element-list.cancelled-new", "type") val ELEMENT_LIST_CANCELLED_NEW = Message("element-list.cancelled-new", "type")
val ELEMENT_LIST_DUPLICATED_NEW = Message("element-list.duplicated-new", "type") val ELEMENT_LIST_DUPLICATED_NEW = Message("element-list.duplicated-new", "type")
val UNIT_REPAIR_TITLE = Message("unit-repair.title") val UNIT_REPAIR_TITLE = Message("unit-repair.title", "unused", "page", "max_page")
val UNIT_REPAIR_ELEMENT_TITLE = Message("unit-repair.element_title") val UNIT_REPAIR_ELEMENT_TITLE = Message("unit-repair.element.title", "type", "page", "max_page")
val UNIT_REPAIR_NEW_TITLE = Message("unit-repair.new.title") val UNIT_REPAIR_NEW_TITLE = Message("unit-repair.new.title")
val UNIT_REPAIR_NEW_DESCRIPTION = Message("unit-repair.new.description") val UNIT_REPAIR_NEW_DESCRIPTION = Message("unit-repair.new.description")
val UNIT_REPAIR_NEW_ELEMENT_TITLE = Message("unit-repair.element.new.title", "name") val UNIT_REPAIR_NEW_ELEMENT_TITLE = Message("unit-repair.element.new.title", "unused")
val UNIT_REPAIR_NEW_ELEMENT_DESCRIPTION = Message("unit-repair.element.new.description", "name") val UNIT_REPAIR_NEW_ELEMENT_DESCRIPTION = Message("unit-repair.element.new.description", "name")
val UNIT_REPAIR_NEW_ELEMENT_CANNOT_REPAIR = Message("unit-repair.element.new.cannot-damage") val UNIT_REPAIR_NEW_ELEMENT_CANNOT_REPAIR = Message("unit-repair.element.new.cannot-damage")
val UNIT_REPAIR_NEW_ELEMENT_SAME_TYPE = Message("unit-repair.element.new.same-type") val UNIT_REPAIR_NEW_ELEMENT_SAME_TYPE = Message("unit-repair.element.new.same-type")
val CUSTOM_RECIPE_TITLE = Message("custom-recipe.title") val CUSTOM_RECIPE_TITLE = Message("custom-recipe.title", "unused", "page", "max_page")
val CUSTOM_RECIPE_ELEMENT_DELETE_TITLE = Message("custom-recipe.element.delete.title", "type") val CUSTOM_RECIPE_ELEMENT_DELETE_TITLE = Message("custom-recipe.element.delete.title", "type")
val CUSTOM_RECIPE_ELEMENT_DELETE_DESCRIPTION = Message("custom-recipe.element.delete.description", "type") val CUSTOM_RECIPE_ELEMENT_DELETE_DESCRIPTION = Message("custom-recipe.element.delete.description", "unused")
val ENCHANTMENT_LEVEL_COST_TITLE = Message("enchant-level-cost.title") val ENCHANTMENT_LEVEL_COST_TITLE = Message("enchant-level-cost.title", "unused", "page", "max_page")
val ENCHANTMENT_LEVEL_LIMIT_TITLE = Message("enchant-level-limit.title") val ENCHANTMENT_LEVEL_LIMIT_TITLE = Message("enchant-level-limit.title", "unused", "page", "max_page")
val ENCHANTMENT_MERGE_LIMIT_TITLE = Message("enchant-merge-limit.title") val ENCHANTMENT_MERGE_LIMIT_TITLE = Message("enchant-merge-limit.title", "unused", "page", "max_page")
val ENCHANTMENT_CONFLICT_TITLE = Message("enchant-conflict.title") val ENCHANTMENT_CONFLICT_TITLE = Message("enchant-conflict.title", "unused", "page", "max_page")
val ENCHANTMENT_CONFLICT_ELEMENT_ENCHANTMENTS = Message("enchant-conflict.element.selected-enchantments", "group") val ENCHANTMENT_CONFLICT_ELEMENT_ENCHANTMENTS = Message("enchant-conflict.element.selected-enchantments", "group")
val ENCHANTMENT_CONFLICT_ELEMENT_SUB_GROUPS = Message("enchant-conflict.element.selected-sub-groups", "group") val ENCHANTMENT_CONFLICT_ELEMENT_SUB_GROUPS = Message("enchant-conflict.element.selected-sub-groups", "group")
val ENCHANTMENT_CONFLICT_ELEMENT_DELETE_TITLE = Message("enchant-conflict.element.delete.title", "type") val ENCHANTMENT_CONFLICT_ELEMENT_DELETE_TITLE = Message("enchant-conflict.element.delete.title", "type")
val ENCHANTMENT_CONFLICT_ELEMENT_DELETE_DESCRIPTION = Message("enchant-conflict.element.delete.description", "type") val ENCHANTMENT_CONFLICT_ELEMENT_DELETE_DESCRIPTION = Message("enchant-conflict.element.delete.description", "unused")
val MATERIAL_GROUP_TITLE = Message("material-group.title") val MATERIAL_GROUP_TITLE = Message("material-group.title", "unused", "page", "max_page")
val MATERIAL_GROUP_ELEMENT_SELECTED_MATERIALS = Message("material-group.element.selected-materials", "group") val MATERIAL_GROUP_ELEMENT_SELECTED_MATERIALS = Message("material-group.element.selected-materials", "group")
val MATERIAL_GROUP_ELEMENT_SELECTED_SUB_GROUPS = Message("material-group.element.selected-sub-groups", "group") val MATERIAL_GROUP_ELEMENT_SELECTED_SUB_GROUPS = Message("material-group.element.selected-sub-groups", "group")
val MATERIAL_GROUP_ELEMENT_DELETE_TITLE = Message("material-group.element.delete.title", "type") val MATERIAL_GROUP_ELEMENT_DELETE_TITLE = Message("material-group.element.delete.title", "type")
val MATERIAL_GROUP_ELEMENT_DELETE_DESCRIPTION = Message("material-group.element.delete.description", "type") val MATERIAL_GROUP_ELEMENT_DELETE_DESCRIPTION = Message("material-group.element.delete.description", "unused")
val MATERIAL_GROUP_ELEMENT_DELETE_BUTTON_NAME = Message("material-group.element.delete.button.name") val MATERIAL_GROUP_ELEMENT_DELETE_BUTTON_NAME = Message("material-group.element.delete.button.name")
val MATERIAL_GROUP_ELEMENT_DELETE_BUTTON_LORE = Message("material-group.element.delete.button.lore") val MATERIAL_GROUP_ELEMENT_DELETE_BUTTON_LORE = Message("material-group.element.delete.button.lore")
val MATERIAL_SELECT_CONFIRM_TITLE = Message("material-select.confirm.title","name") val MATERIAL_SELECT_CONFIRM_TITLE = Message("material-select.new.confirm.title","name")
val MATERIAL_SELECT_CONFIRM_DESCRIPTION = Message("material-select.confirm.description","name") val MATERIAL_SELECT_CONFIRM_DESCRIPTION = Message("material-select.new.confirm.description","name")
} }

View file

@ -15,6 +15,6 @@ object MsgWarning {
val LOAD_LEGACY_SPIGOT = Message("load.legacy.spigot") val LOAD_LEGACY_SPIGOT = Message("load.legacy.spigot")
val LOAD_LEGACY_SPIGOT_OLD = Message("load.legacy.spigot-old") val LOAD_LEGACY_SPIGOT_OLD = Message("load.legacy.spigot-old")
val DEPENDENCY_GENERIC_EXCEPTION = Message("config-ui.shared.no-permission") val ANVIL_GENERIC_EXCEPTION = Message("anvil.generic")
} }

View file

@ -41,8 +41,8 @@ object ComponentUtil {
meta.lore = this.map {obj -> obj.serializeLegacy()} meta.lore = this.map {obj -> obj.serializeLegacy()}
} }
fun ItemMeta.setMessageName(message: Message) { fun ItemMeta.setMessageName(message: Message, vararg params: Any) {
this.setComponentDisplayName(message.formattedConcatenated()) this.setComponentDisplayName(message.formattedConcatenated(*params))
} }
} }

View file

@ -14,6 +14,8 @@ warning:
1: "If replace too expensive is not working this is likely because of spigot" 1: "If replace too expensive is not working this is likely because of spigot"
2: "As native nms is not supported for spigot starting 26.1" 2: "As native nms is not supported for spigot starting 26.1"
update.available: "An update may be available: %version" update.available: "An update may be available: %version"
anvil:
generic: "<white>[<yellow>CustomAnvil<white>] <red>Error while handling the anvil."
error: error:
load: load:
@ -53,6 +55,7 @@ command:
log-cleared: "Log Cleared" log-cleared: "Log Cleared"
toggled: "Debug toggled to %type" toggled: "Debug toggled to %type"
copy: "<green>Click to copy log data" copy: "<green>Click to copy log data"
copy-lang: "<green>Click to copy detailed lang issues"
data: data:
header: "Debug Log data:" header: "Debug Log data:"
line-count: "Found %count lines" line-count: "Found %count lines"
@ -122,14 +125,14 @@ config-ui:
duplicated-new: "<red>Please enter a %type name that do not already exist..." duplicated-new: "<red>Please enter a %type name that do not already exist..."
unit-repair: unit-repair:
tile: "Unit Repair Config" title: "Unit Repair Config <reset>(%page/%max_page)"
new: new:
title: "Select unit repair item." title: "Select unit repair item."
description: description:
1: "<gray>Click here with an item to set the item" 1: "<gray>Click here with an item to set the item"
2: "<gray>You like to be an unit repair item" 2: "<gray>You like to be an unit repair item"
element: element:
title: "<yellow>%type <red>Unit repair" title: "<yellow>%type <red>Unit repair <reset>(%page/%max_page)"
new: new:
title: "Select item to be repaired." title: "Select item to be repaired."
description: description:
@ -139,23 +142,23 @@ config-ui:
same-type: "<red>Item can't repair something of the same type." same-type: "<red>Item can't repair something of the same type."
custom-recipe: custom-recipe:
title: "Custom Recipe Config" title: "Custom Recipe Config <reset>(%page/%max_page)"
element: element:
delete: delete:
title: "<red>Delete <yellow>%type<red>?" title: "<red>Delete <yellow>%type<red>?"
description: "<gray>Confirm that you want to delete this recipe." description: "<gray>Confirm that you want to delete this recipe."
enchant-level-cost: enchant-level-cost:
title: "<dark_gray>Enchantment Level Limit" title: "<dark_gray>Enchantment Level Limit <reset>(%page/%max_page)"
enchant-level-limit: enchant-level-limit:
title: "<dark_gray>Enchantment Level Limit" title: "<dark_gray>Enchantment Level Limit <reset>(%page/%max_page)"
enchant-merge-limit: enchant-merge-limit:
title: "<dark_gray>Enchantment Maximum Merge Level" title: "<dark_gray>Enchantment Maximum Merge Level <reset>(%page/%max_page)"
enchant-conflict: enchant-conflict:
title: "Conflict Config" title: "Conflict Config <reset>(%page/%max_page)"
element: element:
selected-enchantments: "<yellow>%group<dark_purple>" # likely need page and max page selected-enchantments: "<yellow>%group<dark_purple>" # likely need page and max page
selected-sub-groups: "<yellow>%group <red>Groups" selected-sub-groups: "<yellow>%group <red>Groups"
@ -164,7 +167,7 @@ config-ui:
description: "<gray>Confirm that you want to delete this conflict." description: "<gray>Confirm that you want to delete this conflict."
material-group: material-group:
title: "Group Config" title: "Group Config <reset>(%page/%max_page)"
element: element:
selected-materials: "<yellow>%group <red>Materials" selected-materials: "<yellow>%group <red>Materials"
selected-sub-groups: "<yellow>%group <red>Groups" selected-sub-groups: "<yellow>%group <red>Groups"