-
Notifications
You must be signed in to change notification settings - Fork 0
LanguageManager and MessageKey System
The LanguageManager
and MessageKey
system provides a robust and flexible way to handle localization in your Minecraft plugin. This system allows you to define structured message keys, load localized messages from YAML files, and retrieve those messages based on either the player's language settings or the server's default locale for system-wide messages.
The system is designed to simplify the process of adding translations for different languages, ensuring that both in-game messages for players and system-level messages (such as those in the console) are consistently localized.
The MessageKey
interface allows you to define message keys in a structured, hierarchical way. By creating nested classes, you can mirror the structure of your YAML files and organize message keys logically.
This hierarchy makes it easier to manage large numbers of messages, and it ensures that your keys are easy to locate both in your code and in the language files.
Each message key is an object that corresponds to a specific path in the YAML file. If a message is missing from the YAML file, the key's name will be used as a fallback.
Here’s an example of how to define message keys using the MessageKey
interface. This example creates a structure for GUI-related messages:
open class Main : MessageKey {
open class Gui : Main() {
open class Title : Gui() {
object MAIN_BOARD : Title()
object SETTINGS : Title()
}
object BUTTONS : Gui() {
object CLICK : BUTTONS()
object HOVER : BUTTONS()
}
}
}
-
Main
serves as the top-level class for all message keys related to the main plugin. - Inside
Gui
, there are further nested classes for different parts of the GUI.-
Title
contains keys for titles likeMAIN_BOARD
andSETTINGS
. -
BUTTONS
contains keys for button-related actions likeCLICK
andHOVER
.
-
In the corresponding YAML file, you would define these keys like this:
gui:
title:
main_board: "Main Board"
settings: "Settings Menu"
buttons:
click: "Click me!"
hover: "Hover over the button"
This structure allows you to easily manage and organize your messages in a logical, hierarchical way.
The MessageKey
interface defines two important methods for handling localized messages: c()
and t(Player)
. These methods ensure that there is always a fallback if a localized message is missing or undefined.
The c()
method returns the default message or the key name itself when no localized message is found. This acts as a fallback mechanism, ensuring that if the YAML file does not contain a specific translation for a message, the name of the MessageKey
class (or object) is returned instead.
fun c(): TextComponent {
return Component.text(this.javaClass.simpleName)
}
- Use case: This method is particularly useful when localization is incomplete or when a message is not defined in the language files. The key name will serve as a placeholder to indicate which message is missing.
-
Example: If a
MessageKey
object likeMain.Gui.Title.MAIN_BOARD
is missing from the YAML file, the fallback message will display"MainBoard"
(or similar, based on class naming conventions).
The t(Player)
method retrieves a localized message for a given player, based on the language settings of the player's Minecraft client. It first looks for the corresponding message in the YAML file. If the message is not found, it falls back to the key name using the c()
method.
fun t(player: Player): TextComponent {
return LanguageManager.getMessage(player, this)
}
-
Use case: This method is used when you want to send a message to a specific player in their preferred language. It ensures that the correct localized string is retrieved from the
LanguageManager
, providing a player-specific experience. -
Example: If the player has their language set to Japanese and the corresponding translation exists in the YAML file,
t(player)
will return the Japanese translation ofMain.Gui.Title.MAIN_BOARD
. If not, the fallback will be the key name ("MainBoard"
).
The LanguageManager
class is responsible for loading messages from YAML files and mapping them to MessageKey
objects. It also provides methods for retrieving messages based on a player's language settings or the server’s default locale for system-wide messages.
When the plugin loads, LanguageManager
scans the plugin’s specified package for any MessageKey
implementations. These keys are automatically mapped to their corresponding messages in the YAML files.
The processYamlAndMapMessageKeys()
method is responsible for reading the YAML data and matching it to the appropriate MessageKey
. It ensures that the structure in the YAML file mirrors the class hierarchy defined in your code.
You can retrieve localized messages for individual players using the getMessage()
method. This method uses the player's language settings to return the appropriate message from the loaded YAML file.
val message: TextComponent = LanguageManager.getMessage(player, Main.Gui.Title.MAIN_BOARD)
This will return the message defined in the YAML for the Main.Gui.Title.MAIN_BOARD
key, in the language that the player has set in their client.
getSysMessage()
is used to retrieve localized messages that are not player-specific, such as those intended for the server console or system logs. It uses the server’s default locale (as defined by Locale.getDefault()
) to select the appropriate language for the message.
This method is useful when you want to send messages to the console or perform logging in the system's language, ensuring that even system-level interactions can be localized properly.
val systemMessage: String = LanguageManager.getSysMessage(Main.Gui.Title.MAIN_BOARD)
In this case, the message defined in the YAML for Main.Gui.Title.MAIN_BOARD
will be retrieved using the system's locale. If the message is missing or not found for the system’s language, the key itself will be used as the default message.
The findMissingKeys()
method scans the YAML files for any missing message keys. If a key is defined in your code but missing in the YAML file, a warning is logged, and the key’s name is used as a fallback message.
This method ensures that all expected keys are present in the language files, preventing missing translations during runtime.
val missingKeys = LanguageManager.findMissingKeys("en")
if (missingKeys.isNotEmpty()) {
missingKeys.forEach { key ->
logger.warn("Missing key: $key in language file")
}
}
This helps you maintain complete and consistent localization coverage across all languages.
Hint: Output to the log is automatic, meaning there’s no need to manually output missing keys to the log.
The YAML files should follow the same hierarchical structure as the MessageKey
classes. Each key in the file corresponds to a MessageKey
object in the code.
gui:
title:
main_board: "Main Board"
settings: "Settings"
buttons:
click: "Click!"
hover: "Hover!"
In this file:
-
gui.title.main_board
corresponds toMain.Gui.Title.MAIN_BOARD
. -
gui.buttons.click
corresponds toMain.Gui.BUTTONS.CLICK
.
-
Case Sensitivity: All keys in the YAML file should be written in lowercase. The
LanguageManager
automatically converts class names to lowercase for key matching, so mismatched case may result in keys not being found. -
Fallback: If a message is not found for a given key, the
MessageKey
name will be used as the fallback message.