From f44aa17e3ed08abe1ecb74a0b08b1b2d84174253 Mon Sep 17 00:00:00 2001 From: F0Xde Date: Sat, 17 Oct 2020 21:17:32 +0200 Subject: [PATCH] Localize messages using `ResourceBundle`s Messages can be localized by calling `L10n.getMessage`, which also formats named arguments. Furthermore the extension function `Player.getMessage` determines the locale to use by invoking `L10n.localeProvider`. --- .../localization/KSpigotLocalization.kt | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 src/main/kotlin/net/axay/kspigot/localization/KSpigotLocalization.kt diff --git a/src/main/kotlin/net/axay/kspigot/localization/KSpigotLocalization.kt b/src/main/kotlin/net/axay/kspigot/localization/KSpigotLocalization.kt new file mode 100644 index 00000000..520d5f0b --- /dev/null +++ b/src/main/kotlin/net/axay/kspigot/localization/KSpigotLocalization.kt @@ -0,0 +1,81 @@ +package net.axay.kspigot.localization + +import org.apache.commons.lang.text.StrSubstitutor +import org.bukkit.entity.Player +import java.io.InputStreamReader +import java.nio.charset.StandardCharsets +import java.util.* +import kotlin.collections.HashMap + +private const val PREFIX = "{" +private const val SUFFIX = "}" + +/** + * Handles localization of strings using java [ResourceBundle]s. + * + * Message property files should reside in `src/main/resources` named + * `messages_locale.properties`, where `locale` is formatted as returned by + * [Locale.toString]. + */ +object L10n { + /** + * This function determines which locale is used for a player, by default + * [Locale.US] is returned. + * + * It is invoked every time a message is localized, so some sort of caching + * is advisable in many cases. + */ + var localeProvider: (Player) -> Locale = { Locale.US } + + private val bundles: MutableMap = HashMap() + + /** + * Returns the localized string for [key] + * + * @throws MissingResourceException if no messages exist for the given + * locale or [key] was not found in the messages + */ + fun getMessage(locale: Locale, key: String): String = + getOrLoadBundle(locale)?.getString(key) ?: throw MissingResourceException( + "Messages for locale '$locale' not found", "ResourceBundle", key + ) + + /** + * Additionally formats the localized string with the named [args], which + * are represented as `{argumentName}` in the translations + */ + fun getMessage(locale: Locale, key: String, vararg args: Pair): String = + StrSubstitutor.replace( + getMessage(locale, key), + mapOf(*args), + PREFIX, SUFFIX + ) + + private fun getOrLoadBundle(locale: Locale): ResourceBundle? { + return bundles[locale] ?: InputStreamReader( + javaClass + .classLoader + .getResourceAsStream("messages_$locale.properties") ?: return null, + StandardCharsets.UTF_8 + ).use { + val bundle = PropertyResourceBundle(it) + bundles[locale] = bundle + bundle + } + } +} + +/** + * Returns the localized message using the locale provided by + * [L10n.localeProvider] + * + * @see L10n.getMessage + */ +fun Player.getMessage(key: String) = + L10n.getMessage(L10n.localeProvider(this), key) + +/** + * @see L10n.getMessage + */ +fun Player.getMessage(key: String, vararg args: Pair) = + L10n.getMessage(L10n.localeProvider(this), key, *args) \ No newline at end of file