From 4ee9bc709eacc0a0246a0a3b84e297f9f5684af1 Mon Sep 17 00:00:00 2001 From: Hugo Vallejo Date: Wed, 13 Dec 2023 18:20:16 +0100 Subject: [PATCH 01/17] Create properties file translation registry. --- .../i18n/MapBasedTranslationRegistry.kt | 2 +- .../i18n/PropertiesFileTranslationRegistry.kt | 51 +++++++++++++++++++ .../kezz/miniphrase/tag/TagResolverBuilder.kt | 4 +- 3 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 core/src/main/kotlin/dev/kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/MapBasedTranslationRegistry.kt b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/MapBasedTranslationRegistry.kt index 9aec454..e597523 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/MapBasedTranslationRegistry.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/MapBasedTranslationRegistry.kt @@ -26,7 +26,7 @@ package dev.kezz.miniphrase.i18n import java.util.Locale /** A translation registry that is backed by a map populated by a supplier. */ -public class MapBasedTranslationRegistry( +public open class MapBasedTranslationRegistry( /** The supplier of content for the map, used in reloads. */ private val supplier: suspend () -> Map> ) : TranslationRegistry { diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt new file mode 100644 index 0000000..4e5e556 --- /dev/null +++ b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt @@ -0,0 +1,51 @@ +/* + * MIT License + * + * Copyright (c) 2022 Kezz + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.kezz.miniphrase.i18n + +import java.io.File +import java.io.FileInputStream +import java.util.* + +/** + * A translation registry that is populated by looking for property files in [path]. + * Format: en.properties + */ +public class PropertiesFileTranslationRegistry( + private val path: File, +) : MapBasedTranslationRegistry({ + buildMap { + Locale.getAvailableLocales().forEach { + val translationsFile = File(path, it.toLanguageTag() + ".properties") + + val inputStream = FileInputStream(translationsFile) + + if (translationsFile.exists()) { + val properties = Properties() + properties.load(inputStream) + + put(it, properties.stringPropertyNames().associateWith { key -> properties.getProperty(key) }) + } + } + } +}) diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/tag/TagResolverBuilder.kt b/core/src/main/kotlin/dev/kezz/miniphrase/tag/TagResolverBuilder.kt index a6d2a59..bae2bfc 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/tag/TagResolverBuilder.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/tag/TagResolverBuilder.kt @@ -23,14 +23,14 @@ */ package dev.kezz.miniphrase.tag -import dev.kezz.miniphrase.MiniPhraseContext import dev.kezz.miniphrase.MiniPhrase +import dev.kezz.miniphrase.MiniPhraseContext import net.kyori.adventure.text.Component import net.kyori.adventure.text.minimessage.Context import net.kyori.adventure.text.minimessage.tag.Tag import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver -import java.util.Locale +import java.util.* /** * A wrapper around [TagResolver.Builder] with useful defaults, utility methods and From f50c71793b77796bc80b4f02a26a231f525d083e Mon Sep 17 00:00:00 2001 From: Hugo Vallejo Date: Wed, 13 Dec 2023 18:21:33 +0100 Subject: [PATCH 02/17] Do things properly. --- .../miniphrase/i18n/PropertiesFileTranslationRegistry.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt index 4e5e556..bf96e23 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt @@ -38,11 +38,13 @@ public class PropertiesFileTranslationRegistry( Locale.getAvailableLocales().forEach { val translationsFile = File(path, it.toLanguageTag() + ".properties") - val inputStream = FileInputStream(translationsFile) - if (translationsFile.exists()) { + val inputStream = FileInputStream(translationsFile) + val properties = Properties() + properties.load(inputStream) + inputStream.close() put(it, properties.stringPropertyNames().associateWith { key -> properties.getProperty(key) }) } From 9dd884c8feb8d32d890460c15383f589d35ece49 Mon Sep 17 00:00:00 2001 From: Hugo Vallejo Date: Wed, 13 Dec 2023 18:54:44 +0100 Subject: [PATCH 03/17] Update some libraries and methods. --- build.gradle.kts | 18 ++++++++---------- .../dev/kezz/miniphrase/audience/ArrayExt.kt | 8 +++----- .../kezz/miniphrase/audience/AudienceExt.kt | 9 +++------ .../kezz/miniphrase/audience/IterableExt.kt | 8 +++----- gradle/libs.versions.toml | 4 ++-- 5 files changed, 19 insertions(+), 28 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 66a3eee..f9aaf53 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -39,6 +39,14 @@ subprojects { compileOnly(rootProject.project.libs.kotlin.stdlib) } + kotlin { + jvmToolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } + + jvmToolchain(17) + } + tasks { indra { mitLicense() @@ -61,16 +69,6 @@ subprojects { } } - java { - toolchain { - languageVersion.set(JavaLanguageVersion.of(17)) - } - } - - kotlin { - explicitApi() - } - ktlint { version.set("0.45.1") } diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/audience/ArrayExt.kt b/core/src/main/kotlin/dev/kezz/miniphrase/audience/ArrayExt.kt index d872ec9..4e8ed5f 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/audience/ArrayExt.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/audience/ArrayExt.kt @@ -26,9 +26,8 @@ package dev.kezz.miniphrase.audience import dev.kezz.miniphrase.MiniPhraseContext import dev.kezz.miniphrase.tag.TagResolverBuilder import net.kyori.adventure.audience.Audience -import net.kyori.adventure.audience.MessageType import net.kyori.adventure.identity.Identity -import java.util.Locale +import java.util.* /** Shorthand for [Audience.audience]. */ public fun Array.asAudience(): Audience = @@ -38,16 +37,15 @@ public fun Array.asAudience(): Audience = context(MiniPhraseContext) public fun Array.sendTranslated( key: String, - type: MessageType = MessageType.CHAT, identity: Identity = Identity.nil(), locale: Locale? = null, tags: (TagResolverBuilder.() -> Unit)? = null ) { if (locale != null) { // If we've got an override locale, we can save rendering by wrapping this in an audience. - asAudience().sendTranslated(key, type, identity, locale, tags) + asAudience().sendTranslated(key, identity, locale, tags) } else { // Otherwise, we need to pull it from each audience member, so just delegate. - forEach { audience -> audience.sendTranslated(key, type, identity, locale, tags) } + forEach { audience -> audience.sendTranslated(key, identity, locale, tags) } } } diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/audience/AudienceExt.kt b/core/src/main/kotlin/dev/kezz/miniphrase/audience/AudienceExt.kt index 8553261..90ba9d6 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/audience/AudienceExt.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/audience/AudienceExt.kt @@ -27,9 +27,8 @@ import dev.kezz.miniphrase.MiniPhraseContext import dev.kezz.miniphrase.tag.TagResolverBuilder import net.kyori.adventure.audience.Audience import net.kyori.adventure.audience.ForwardingAudience -import net.kyori.adventure.audience.MessageType import net.kyori.adventure.identity.Identity -import java.util.Locale +import java.util.* /** * Sends a message to this audience, with optional additional tags. @@ -48,8 +47,6 @@ context(MiniPhraseContext) public fun Audience.sendTranslated( /** The key of the message. */ key: String, - /** The type of the message. */ - type: MessageType = MessageType.CHAT, /** The identity of the message sender. */ identity: Identity = Identity.nil(), /** The locale to translate the message in, if not the default for the audience. */ @@ -65,14 +62,14 @@ public fun Audience.sendTranslated( locale == null && this is ForwardingAudience -> { // We only run through each child if the locale is null (i.e. we're pulling it from the audience itself). forEachAudience { child -> - child.sendTranslated(key, type, identity, locale, tags) + child.sendTranslated(key, identity, locale, tags) } } else -> { // Try and get the locale from the audience, otherwise default, then translate and send! val targetLocale = locale ?: get(Identity.LOCALE).orElseGet(miniPhrase::defaultLocale) - sendMessage(identity, miniPhrase.translate(key, targetLocale, tags), type) + sendMessage(miniPhrase.translate(key, targetLocale, tags)) } } } diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/audience/IterableExt.kt b/core/src/main/kotlin/dev/kezz/miniphrase/audience/IterableExt.kt index 3a838ca..43745e9 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/audience/IterableExt.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/audience/IterableExt.kt @@ -26,9 +26,8 @@ package dev.kezz.miniphrase.audience import dev.kezz.miniphrase.MiniPhraseContext import dev.kezz.miniphrase.tag.TagResolverBuilder import net.kyori.adventure.audience.Audience -import net.kyori.adventure.audience.MessageType import net.kyori.adventure.identity.Identity -import java.util.Locale +import java.util.* /** Shorthand for [Audience.audience]. */ public fun Iterable.asAudience(): Audience = @@ -38,16 +37,15 @@ public fun Iterable.asAudience(): Audience = context(MiniPhraseContext) public fun Iterable.sendTranslated( key: String, - type: MessageType = MessageType.CHAT, identity: Identity = Identity.nil(), locale: Locale? = null, tags: (TagResolverBuilder.() -> Unit)? = null ) { if (locale != null) { // If we've got an override locale, we can save rendering by wrapping this in an audience. - asAudience().sendTranslated(key, type, identity, locale, tags) + asAudience().sendTranslated(key, identity, locale, tags) } else { // Otherwise, we need to pull it from each audience member, so just delegate. - forEach { audience -> audience.sendTranslated(key, type, identity, locale, tags) } + forEach { audience -> audience.sendTranslated(key, identity, locale, tags) } } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index cca3783..86a0dab 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,9 +1,9 @@ [versions] indra = "2.1.1" -kotlin = "1.6.21" +kotlin = "1.9.21" [libraries] -adventure-minimessage = { group = "net.kyori", name = "adventure-text-minimessage", version = "4.10.1" } +adventure-minimessage = { group = "net.kyori", name = "adventure-text-minimessage", version = "4.14.0" } kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib", version.ref = "kotlin" } [plugins] From 01c3614dd50ca8670a69afa0a757f44478a9b8d1 Mon Sep 17 00:00:00 2001 From: Hugo Vallejo Date: Wed, 13 Dec 2023 19:43:24 +0100 Subject: [PATCH 04/17] Use locale's language string as a key, get property files form resources. --- .../kotlin/dev/kezz/miniphrase/MiniPhrase.kt | 6 +++- .../i18n/EmptyTranslationRegistry.kt | 6 ++-- .../i18n/MapBasedTranslationRegistry.kt | 10 +++--- .../i18n/PropertiesFileTranslationRegistry.kt | 32 +++++++++++++++++-- .../miniphrase/i18n/TranslationRegistry.kt | 4 +-- .../kezz/miniphrase/tag/TagResolverBuilder.kt | 2 +- 6 files changed, 43 insertions(+), 17 deletions(-) diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt b/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt index b444ebc..1315c4d 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt @@ -52,13 +52,17 @@ public class MiniPhrase private constructor( Builder().apply(builder).build() } + init { + translationRegistry.reload() + } + /** This MiniPhrase instance. */ override val miniPhrase: MiniPhrase = this /** Translates a key with a given locale, or the default locale. */ public fun translate(key: String, locale: Locale? = null, tags: (TagResolverBuilder.() -> Unit)? = null): Component { val targetLocale = locale ?: defaultLocale - val translationString = translationRegistry[key, targetLocale] ?: key + val translationString = translationRegistry[key, targetLocale.language] ?: key val resolver = TagResolverBuilder.configureAndBuild(this) { if (includePhraseTag) withPhraseTag(locale) if (tags != null) tags() diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/EmptyTranslationRegistry.kt b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/EmptyTranslationRegistry.kt index 95a75cb..333cb16 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/EmptyTranslationRegistry.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/EmptyTranslationRegistry.kt @@ -23,10 +23,8 @@ */ package dev.kezz.miniphrase.i18n -import java.util.Locale - /** An empty translation registry. */ public object EmptyTranslationRegistry : TranslationRegistry { - override suspend fun reload() { } - override fun get(key: String, locale: Locale): String? = null + override fun reload() { } + override fun get(key: String, locale: String): String? = null } diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/MapBasedTranslationRegistry.kt b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/MapBasedTranslationRegistry.kt index e597523..0a18301 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/MapBasedTranslationRegistry.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/MapBasedTranslationRegistry.kt @@ -23,19 +23,17 @@ */ package dev.kezz.miniphrase.i18n -import java.util.Locale - /** A translation registry that is backed by a map populated by a supplier. */ public open class MapBasedTranslationRegistry( /** The supplier of content for the map, used in reloads. */ - private val supplier: suspend () -> Map> + private val supplier: () -> Map> ) : TranslationRegistry { - private var map: Map> = mapOf() + private var map: Map> = mapOf() - override suspend fun reload() { + override fun reload() { map = supplier() } - override fun get(key: String, locale: Locale): String? = + override fun get(key: String, locale: String): String? = map[locale]?.get(key) } diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt index bf96e23..c794e63 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt @@ -25,6 +25,8 @@ package dev.kezz.miniphrase.i18n import java.io.File import java.io.FileInputStream +import java.nio.file.Files +import java.nio.file.StandardCopyOption import java.util.* /** @@ -35,9 +37,32 @@ public class PropertiesFileTranslationRegistry( private val path: File, ) : MapBasedTranslationRegistry({ buildMap { - Locale.getAvailableLocales().forEach { - val translationsFile = File(path, it.toLanguageTag() + ".properties") + if (path.exists() && !path.isDirectory) return@buildMap + if (!path.exists()) path.mkdirs() + + Locale.getAvailableLocales().map { it.language }.distinct().forEach { languageKey -> + val translationsFile = File(path, "$languageKey.properties") + + // If the file doesn't exist we check for the file in the resources and copy it + // if the file exists there. + if (!translationsFile.exists()) { + javaClass.getResourceAsStream("/$languageKey.properties")?.let { + try { + translationsFile.createNewFile() + + Files.copy( + it, + translationsFile.getAbsoluteFile().toPath(), + StandardCopyOption.REPLACE_EXISTING + ) + } catch (exception: Exception) { + exception.printStackTrace() + } + } + } + + // If there is a file we read its contents. if (translationsFile.exists()) { val inputStream = FileInputStream(translationsFile) @@ -46,7 +71,8 @@ public class PropertiesFileTranslationRegistry( properties.load(inputStream) inputStream.close() - put(it, properties.stringPropertyNames().associateWith { key -> properties.getProperty(key) }) + put(languageKey, + properties.stringPropertyNames().associateWith { key -> properties.getProperty(key) }) } } } diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/TranslationRegistry.kt b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/TranslationRegistry.kt index 10cd019..2b01a7b 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/TranslationRegistry.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/TranslationRegistry.kt @@ -29,8 +29,8 @@ import java.util.Locale public interface TranslationRegistry { /** Loads or reloads all translations. */ - public suspend fun reload() + public fun reload() /** Returns a translation for a given [key] in a [locale]. */ - public operator fun get(key: String, locale: Locale): String? + public operator fun get(key: String, locale: String): String? } diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/tag/TagResolverBuilder.kt b/core/src/main/kotlin/dev/kezz/miniphrase/tag/TagResolverBuilder.kt index bae2bfc..be6bbfb 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/tag/TagResolverBuilder.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/tag/TagResolverBuilder.kt @@ -120,7 +120,7 @@ public class TagResolverBuilder private constructor( } } ?: locale ?: miniPhrase.defaultLocale - val result = miniPhrase.translationRegistry[key, targetLocale] + val result = miniPhrase.translationRegistry[key, targetLocale.language] if (result == null) { Tag.inserting(Component.text(key)) From 6ac9e423e4136d1f9a724c66f70abcf011345ac7 Mon Sep 17 00:00:00 2001 From: Hugo Vallejo Date: Wed, 13 Dec 2023 22:17:36 +0100 Subject: [PATCH 05/17] More changing locale to string. --- core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt | 6 +++--- .../main/kotlin/dev/kezz/miniphrase/audience/ArrayExt.kt | 2 +- .../main/kotlin/dev/kezz/miniphrase/audience/AudienceExt.kt | 4 ++-- .../main/kotlin/dev/kezz/miniphrase/audience/IterableExt.kt | 2 +- .../kotlin/dev/kezz/miniphrase/tag/TagResolverBuilder.kt | 6 +++--- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt b/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt index 1315c4d..0dd051a 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt @@ -60,9 +60,9 @@ public class MiniPhrase private constructor( override val miniPhrase: MiniPhrase = this /** Translates a key with a given locale, or the default locale. */ - public fun translate(key: String, locale: Locale? = null, tags: (TagResolverBuilder.() -> Unit)? = null): Component { - val targetLocale = locale ?: defaultLocale - val translationString = translationRegistry[key, targetLocale.language] ?: key + public fun translate(key: String, locale: String? = null, tags: (TagResolverBuilder.() -> Unit)? = null): Component { + val targetLocale = locale ?: defaultLocale.language + val translationString = translationRegistry[key, targetLocale] ?: key val resolver = TagResolverBuilder.configureAndBuild(this) { if (includePhraseTag) withPhraseTag(locale) if (tags != null) tags() diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/audience/ArrayExt.kt b/core/src/main/kotlin/dev/kezz/miniphrase/audience/ArrayExt.kt index 4e8ed5f..354db40 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/audience/ArrayExt.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/audience/ArrayExt.kt @@ -38,7 +38,7 @@ context(MiniPhraseContext) public fun Array.sendTranslated( key: String, identity: Identity = Identity.nil(), - locale: Locale? = null, + locale: String? = null, tags: (TagResolverBuilder.() -> Unit)? = null ) { if (locale != null) { diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/audience/AudienceExt.kt b/core/src/main/kotlin/dev/kezz/miniphrase/audience/AudienceExt.kt index 90ba9d6..3e83257 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/audience/AudienceExt.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/audience/AudienceExt.kt @@ -50,7 +50,7 @@ public fun Audience.sendTranslated( /** The identity of the message sender. */ identity: Identity = Identity.nil(), /** The locale to translate the message in, if not the default for the audience. */ - locale: Locale? = null, + locale: String? = null, /* A builder of additional tags to use in the deserialization process. */ tags: (TagResolverBuilder.() -> Unit)? = null ) { @@ -68,7 +68,7 @@ public fun Audience.sendTranslated( else -> { // Try and get the locale from the audience, otherwise default, then translate and send! - val targetLocale = locale ?: get(Identity.LOCALE).orElseGet(miniPhrase::defaultLocale) + val targetLocale = locale ?: get(Identity.LOCALE).orElseGet(miniPhrase::defaultLocale).language sendMessage(miniPhrase.translate(key, targetLocale, tags)) } } diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/audience/IterableExt.kt b/core/src/main/kotlin/dev/kezz/miniphrase/audience/IterableExt.kt index 43745e9..dfcf618 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/audience/IterableExt.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/audience/IterableExt.kt @@ -38,7 +38,7 @@ context(MiniPhraseContext) public fun Iterable.sendTranslated( key: String, identity: Identity = Identity.nil(), - locale: Locale? = null, + locale: String? = null, tags: (TagResolverBuilder.() -> Unit)? = null ) { if (locale != null) { diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/tag/TagResolverBuilder.kt b/core/src/main/kotlin/dev/kezz/miniphrase/tag/TagResolverBuilder.kt index be6bbfb..c407a2f 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/tag/TagResolverBuilder.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/tag/TagResolverBuilder.kt @@ -111,16 +111,16 @@ public class TagResolverBuilder private constructor( } /** Adds the phrase tag resolver to this builder, optionally with an overridden default locale. */ - public fun withPhraseTag(locale: Locale? = null) { + public fun withPhraseTag(locale: String? = null) { tag(PHRASE_TAG_NAME) { arguments, ctx -> val key = arguments.popOr("No key provided.").lowerValue() val targetLocale = arguments.peek()?.let { arg -> runCatching { Locale.forLanguageTag(arg.lowerValue()) }.getOrElse { exception -> throw ctx.newException("Invalid language tag $arg.", exception, arguments) } - } ?: locale ?: miniPhrase.defaultLocale + }?.language ?: locale ?: miniPhrase.defaultLocale.language - val result = miniPhrase.translationRegistry[key, targetLocale.language] + val result = miniPhrase.translationRegistry[key, targetLocale] if (result == null) { Tag.inserting(Component.text(key)) From 461011f4dc4d2e1fab843e4d0c1e1a1fa17c048a Mon Sep 17 00:00:00 2001 From: Hugo Vallejo Date: Thu, 14 Dec 2023 12:02:59 +0100 Subject: [PATCH 06/17] Allow for list translations. --- .../kotlin/dev/kezz/miniphrase/MiniPhrase.kt | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt b/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt index 0dd051a..76521a0 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt @@ -59,15 +59,35 @@ public class MiniPhrase private constructor( /** This MiniPhrase instance. */ override val miniPhrase: MiniPhrase = this - /** Translates a key with a given locale, or the default locale. */ - public fun translate(key: String, locale: String? = null, tags: (TagResolverBuilder.() -> Unit)? = null): Component { - val targetLocale = locale ?: defaultLocale.language - val translationString = translationRegistry[key, targetLocale] ?: key + /** Formats a string and applies styles and tags. */ + private fun format(text: String, locale: String? = null, tags: (TagResolverBuilder.() -> Unit)? = null): Component { val resolver = TagResolverBuilder.configureAndBuild(this) { if (includePhraseTag) withPhraseTag(locale) if (tags != null) tags() } - return miniMessage.deserialize(translationString, resolver) + return miniMessage.deserialize(text, resolver) + } + + /** Translates a key with a given locale, or the default locale. */ + public fun translate(key: String, locale: String? = null, tags: (TagResolverBuilder.() -> Unit)? = null): Component { + val targetLocale = locale ?: defaultLocale.language + val translationString = translationRegistry[key, targetLocale] ?: key + + return format(translationString, locale, tags) + } + + /** Translates a key with a given locale, or the default locale into multiple lines. */ + public fun translateList( + key: String, + locale: String? = null, + tags: (TagResolverBuilder.() -> Unit)? = null + ): List { + val targetLocale = locale ?: defaultLocale.language + val translationString = translationRegistry[key, targetLocale] ?: key + + val lines = translationString.split("\n") + + return lines.map { format(it, locale, tags) } } /** Builder class for MiniPhrase instances. */ From c2299c8f95ec97b89268928a02ac0bf21d94f0bb Mon Sep 17 00:00:00 2001 From: Hugo Vallejo Date: Thu, 14 Dec 2023 12:22:33 +0100 Subject: [PATCH 07/17] Make format function public --- build.gradle.kts | 1 + core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt | 2 +- .../main/kotlin/dev/kezz/miniphrase/i18n/TranslationRegistry.kt | 2 -- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index f9aaf53..33bf65e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -45,6 +45,7 @@ subprojects { } jvmToolchain(17) + explicitApi() } tasks { diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt b/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt index 76521a0..9040200 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt @@ -60,7 +60,7 @@ public class MiniPhrase private constructor( override val miniPhrase: MiniPhrase = this /** Formats a string and applies styles and tags. */ - private fun format(text: String, locale: String? = null, tags: (TagResolverBuilder.() -> Unit)? = null): Component { + public fun format(text: String, locale: String? = null, tags: (TagResolverBuilder.() -> Unit)? = null): Component { val resolver = TagResolverBuilder.configureAndBuild(this) { if (includePhraseTag) withPhraseTag(locale) if (tags != null) tags() diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/TranslationRegistry.kt b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/TranslationRegistry.kt index 2b01a7b..8abb631 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/TranslationRegistry.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/TranslationRegistry.kt @@ -23,8 +23,6 @@ */ package dev.kezz.miniphrase.i18n -import java.util.Locale - /** A registry of translations. */ public interface TranslationRegistry { From b9093231e92b71d6f37c888cee5aa60178b61c15 Mon Sep 17 00:00:00 2001 From: Hugo Vallejo Date: Thu, 14 Dec 2023 12:48:33 +0100 Subject: [PATCH 08/17] Allow for more stuff --- core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt | 4 +--- .../dev/kezz/miniphrase/i18n/EmptyTranslationRegistry.kt | 4 ++++ .../dev/kezz/miniphrase/i18n/MapBasedTranslationRegistry.kt | 5 +++++ .../kotlin/dev/kezz/miniphrase/i18n/TranslationRegistry.kt | 6 ++++++ 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt b/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt index 9040200..e6f1792 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt @@ -83,9 +83,7 @@ public class MiniPhrase private constructor( tags: (TagResolverBuilder.() -> Unit)? = null ): List { val targetLocale = locale ?: defaultLocale.language - val translationString = translationRegistry[key, targetLocale] ?: key - - val lines = translationString.split("\n") + val lines = translationRegistry.getTranslationList(key, targetLocale) return lines.map { format(it, locale, tags) } } diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/EmptyTranslationRegistry.kt b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/EmptyTranslationRegistry.kt index 333cb16..3730405 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/EmptyTranslationRegistry.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/EmptyTranslationRegistry.kt @@ -25,6 +25,10 @@ package dev.kezz.miniphrase.i18n /** An empty translation registry. */ public object EmptyTranslationRegistry : TranslationRegistry { + override fun reload() { } override fun get(key: String, locale: String): String? = null + + override fun getTranslationList(key: String, locale: String): List = listOf() + override fun getLocales(): Set = setOf() } diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/MapBasedTranslationRegistry.kt b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/MapBasedTranslationRegistry.kt index 0a18301..824e97e 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/MapBasedTranslationRegistry.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/MapBasedTranslationRegistry.kt @@ -36,4 +36,9 @@ public open class MapBasedTranslationRegistry( override fun get(key: String, locale: String): String? = map[locale]?.get(key) + + override fun getTranslationList(key: String, locale: String): List = + (get(key, locale) ?: key).split("\n") + + override fun getLocales(): Set = map.keys } diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/TranslationRegistry.kt b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/TranslationRegistry.kt index 8abb631..bc7c56b 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/TranslationRegistry.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/TranslationRegistry.kt @@ -31,4 +31,10 @@ public interface TranslationRegistry { /** Returns a translation for a given [key] in a [locale]. */ public operator fun get(key: String, locale: String): String? + + /** Returns a translation list for a given [key] in a [locale]. */ + public fun getTranslationList(key: String, locale: String): List + + /** Returns the list of registered locales. */ + public fun getLocales(): Set } From ca013815c2f2eb951a835e5f3e1a7fa6d41fef8c Mon Sep 17 00:00:00 2001 From: Hugo Vallejo Date: Thu, 14 Dec 2023 17:03:38 +0100 Subject: [PATCH 09/17] Revert to using Locale instead of strings, make things nicer --- .../kotlin/dev/kezz/miniphrase/MiniPhrase.kt | 12 +++---- .../dev/kezz/miniphrase/audience/ArrayExt.kt | 8 ++--- .../kezz/miniphrase/audience/AudienceExt.kt | 11 +++--- .../kezz/miniphrase/audience/IterableExt.kt | 8 ++--- .../i18n/EmptyTranslationRegistry.kt | 10 +++--- .../i18n/MapBasedTranslationRegistry.kt | 15 ++++---- .../i18n/PropertiesFileTranslationRegistry.kt | 35 ++++++++----------- .../miniphrase/i18n/TranslationRegistry.kt | 13 ++++--- .../kezz/miniphrase/tag/TagResolverBuilder.kt | 4 +-- 9 files changed, 54 insertions(+), 62 deletions(-) diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt b/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt index e6f1792..917ed17 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt @@ -60,7 +60,7 @@ public class MiniPhrase private constructor( override val miniPhrase: MiniPhrase = this /** Formats a string and applies styles and tags. */ - public fun format(text: String, locale: String? = null, tags: (TagResolverBuilder.() -> Unit)? = null): Component { + public fun format(text: String, locale: Locale? = null, tags: (TagResolverBuilder.() -> Unit)? = null): Component { val resolver = TagResolverBuilder.configureAndBuild(this) { if (includePhraseTag) withPhraseTag(locale) if (tags != null) tags() @@ -69,8 +69,8 @@ public class MiniPhrase private constructor( } /** Translates a key with a given locale, or the default locale. */ - public fun translate(key: String, locale: String? = null, tags: (TagResolverBuilder.() -> Unit)? = null): Component { - val targetLocale = locale ?: defaultLocale.language + public fun translate(key: String, locale: Locale? = null, tags: (TagResolverBuilder.() -> Unit)? = null): Component { + val targetLocale = locale ?: defaultLocale val translationString = translationRegistry[key, targetLocale] ?: key return format(translationString, locale, tags) @@ -79,11 +79,11 @@ public class MiniPhrase private constructor( /** Translates a key with a given locale, or the default locale into multiple lines. */ public fun translateList( key: String, - locale: String? = null, + locale: Locale? = null, tags: (TagResolverBuilder.() -> Unit)? = null ): List { - val targetLocale = locale ?: defaultLocale.language - val lines = translationRegistry.getTranslationList(key, targetLocale) + val targetLocale = locale ?: defaultLocale + val lines = translationRegistry.getList(key, targetLocale) return lines.map { format(it, locale, tags) } } diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/audience/ArrayExt.kt b/core/src/main/kotlin/dev/kezz/miniphrase/audience/ArrayExt.kt index 354db40..c9ebc44 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/audience/ArrayExt.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/audience/ArrayExt.kt @@ -26,7 +26,6 @@ package dev.kezz.miniphrase.audience import dev.kezz.miniphrase.MiniPhraseContext import dev.kezz.miniphrase.tag.TagResolverBuilder import net.kyori.adventure.audience.Audience -import net.kyori.adventure.identity.Identity import java.util.* /** Shorthand for [Audience.audience]. */ @@ -37,15 +36,14 @@ public fun Array.asAudience(): Audience = context(MiniPhraseContext) public fun Array.sendTranslated( key: String, - identity: Identity = Identity.nil(), - locale: String? = null, + locale: Locale? = null, tags: (TagResolverBuilder.() -> Unit)? = null ) { if (locale != null) { // If we've got an override locale, we can save rendering by wrapping this in an audience. - asAudience().sendTranslated(key, identity, locale, tags) + asAudience().sendTranslated(key, locale, tags) } else { // Otherwise, we need to pull it from each audience member, so just delegate. - forEach { audience -> audience.sendTranslated(key, identity, locale, tags) } + forEach { audience -> audience.sendTranslated(key, locale, tags) } } } diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/audience/AudienceExt.kt b/core/src/main/kotlin/dev/kezz/miniphrase/audience/AudienceExt.kt index 3e83257..7d76fbf 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/audience/AudienceExt.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/audience/AudienceExt.kt @@ -27,6 +27,7 @@ import dev.kezz.miniphrase.MiniPhraseContext import dev.kezz.miniphrase.tag.TagResolverBuilder import net.kyori.adventure.audience.Audience import net.kyori.adventure.audience.ForwardingAudience +import net.kyori.adventure.chat.ChatType import net.kyori.adventure.identity.Identity import java.util.* @@ -47,10 +48,8 @@ context(MiniPhraseContext) public fun Audience.sendTranslated( /** The key of the message. */ key: String, - /** The identity of the message sender. */ - identity: Identity = Identity.nil(), /** The locale to translate the message in, if not the default for the audience. */ - locale: String? = null, + locale: Locale? = null, /* A builder of additional tags to use in the deserialization process. */ tags: (TagResolverBuilder.() -> Unit)? = null ) { @@ -61,14 +60,12 @@ public fun Audience.sendTranslated( locale == null && this is ForwardingAudience -> { // We only run through each child if the locale is null (i.e. we're pulling it from the audience itself). - forEachAudience { child -> - child.sendTranslated(key, identity, locale, tags) - } + forEachAudience { child -> child.sendTranslated(key, locale, tags) } } else -> { // Try and get the locale from the audience, otherwise default, then translate and send! - val targetLocale = locale ?: get(Identity.LOCALE).orElseGet(miniPhrase::defaultLocale).language + val targetLocale = locale ?: get(Identity.LOCALE).orElseGet(miniPhrase::defaultLocale) sendMessage(miniPhrase.translate(key, targetLocale, tags)) } } diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/audience/IterableExt.kt b/core/src/main/kotlin/dev/kezz/miniphrase/audience/IterableExt.kt index dfcf618..a58a475 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/audience/IterableExt.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/audience/IterableExt.kt @@ -26,7 +26,6 @@ package dev.kezz.miniphrase.audience import dev.kezz.miniphrase.MiniPhraseContext import dev.kezz.miniphrase.tag.TagResolverBuilder import net.kyori.adventure.audience.Audience -import net.kyori.adventure.identity.Identity import java.util.* /** Shorthand for [Audience.audience]. */ @@ -37,15 +36,14 @@ public fun Iterable.asAudience(): Audience = context(MiniPhraseContext) public fun Iterable.sendTranslated( key: String, - identity: Identity = Identity.nil(), - locale: String? = null, + locale: Locale? = null, tags: (TagResolverBuilder.() -> Unit)? = null ) { if (locale != null) { // If we've got an override locale, we can save rendering by wrapping this in an audience. - asAudience().sendTranslated(key, identity, locale, tags) + asAudience().sendTranslated(key, locale, tags) } else { // Otherwise, we need to pull it from each audience member, so just delegate. - forEach { audience -> audience.sendTranslated(key, identity, locale, tags) } + forEach { audience -> audience.sendTranslated(key, locale, tags) } } } diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/EmptyTranslationRegistry.kt b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/EmptyTranslationRegistry.kt index 3730405..54066df 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/EmptyTranslationRegistry.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/EmptyTranslationRegistry.kt @@ -23,12 +23,12 @@ */ package dev.kezz.miniphrase.i18n +import java.util.* + /** An empty translation registry. */ public object EmptyTranslationRegistry : TranslationRegistry { - override fun reload() { } - override fun get(key: String, locale: String): String? = null - - override fun getTranslationList(key: String, locale: String): List = listOf() - override fun getLocales(): Set = setOf() + override fun get(key: String, locale: Locale): String? = null + override fun getList(key: String, locale: Locale): List = listOf() + override fun getLocales(): Set = setOf() } diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/MapBasedTranslationRegistry.kt b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/MapBasedTranslationRegistry.kt index 824e97e..081b792 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/MapBasedTranslationRegistry.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/MapBasedTranslationRegistry.kt @@ -23,22 +23,21 @@ */ package dev.kezz.miniphrase.i18n +import java.util.* + /** A translation registry that is backed by a map populated by a supplier. */ public open class MapBasedTranslationRegistry( /** The supplier of content for the map, used in reloads. */ - private val supplier: () -> Map> + private val supplier: () -> Map> ) : TranslationRegistry { - private var map: Map> = mapOf() + + private var map: Map> = mapOf() override fun reload() { map = supplier() } - override fun get(key: String, locale: String): String? = - map[locale]?.get(key) - - override fun getTranslationList(key: String, locale: String): List = - (get(key, locale) ?: key).split("\n") + override fun get(key: String, locale: Locale): String? = map[locale]?.get(key) - override fun getLocales(): Set = map.keys + override fun getLocales(): Set = map.keys } diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt index c794e63..1cadd04 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt @@ -31,38 +31,34 @@ import java.util.* /** * A translation registry that is populated by looking for property files in [path]. - * Format: en.properties + * If [fetchFromResources], it will look for any language files in the resources directory. + * + * Format: en_us.properties */ public class PropertiesFileTranslationRegistry( private val path: File, + private val fetchFromResources: Boolean = true ) : MapBasedTranslationRegistry({ buildMap { - if (path.exists() && !path.isDirectory) return@buildMap + val exists = path.exists() - if (!path.exists()) path.mkdirs() + if (exists && !path.isDirectory) return@buildMap + if (!exists) path.mkdirs() - Locale.getAvailableLocales().map { it.language }.distinct().forEach { languageKey -> + Locale.getAvailableLocales().forEach { language -> + val languageKey = language.toLanguageTag() val translationsFile = File(path, "$languageKey.properties") // If the file doesn't exist we check for the file in the resources and copy it // if the file exists there. - if (!translationsFile.exists()) { - javaClass.getResourceAsStream("/$languageKey.properties")?.let { - try { - translationsFile.createNewFile() + if (!translationsFile.exists() && fetchFromResources) { + val resourceStream = javaClass.getResourceAsStream("/$languageKey.properties") ?: return@forEach - Files.copy( - it, - translationsFile.getAbsoluteFile().toPath(), - StandardCopyOption.REPLACE_EXISTING - ) - } catch (exception: Exception) { - exception.printStackTrace() - } - } + translationsFile.createNewFile() + Files.copy(resourceStream, translationsFile.getAbsoluteFile().toPath(), StandardCopyOption.REPLACE_EXISTING) } - // If there is a file we read its contents. + // If there is a file we read its contents and save it into the map. if (translationsFile.exists()) { val inputStream = FileInputStream(translationsFile) @@ -71,8 +67,7 @@ public class PropertiesFileTranslationRegistry( properties.load(inputStream) inputStream.close() - put(languageKey, - properties.stringPropertyNames().associateWith { key -> properties.getProperty(key) }) + put(language, properties.stringPropertyNames().associateWith { key -> properties.getProperty(key) }) } } } diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/TranslationRegistry.kt b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/TranslationRegistry.kt index bc7c56b..ced4edb 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/TranslationRegistry.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/TranslationRegistry.kt @@ -23,6 +23,8 @@ */ package dev.kezz.miniphrase.i18n +import java.util.* + /** A registry of translations. */ public interface TranslationRegistry { @@ -30,11 +32,14 @@ public interface TranslationRegistry { public fun reload() /** Returns a translation for a given [key] in a [locale]. */ - public operator fun get(key: String, locale: String): String? + public operator fun get(key: String, locale: Locale): String? - /** Returns a translation list for a given [key] in a [locale]. */ - public fun getTranslationList(key: String, locale: String): List + /** + * Returns a translation list for a given [key] in a [locale]. + * List members are split with "\n" inside a translation. + */ + public fun getList(key: String, locale: Locale): List = get(key, locale)?.split("\n") ?: listOf(key) /** Returns the list of registered locales. */ - public fun getLocales(): Set + public fun getLocales(): Set } diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/tag/TagResolverBuilder.kt b/core/src/main/kotlin/dev/kezz/miniphrase/tag/TagResolverBuilder.kt index c407a2f..bae2bfc 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/tag/TagResolverBuilder.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/tag/TagResolverBuilder.kt @@ -111,14 +111,14 @@ public class TagResolverBuilder private constructor( } /** Adds the phrase tag resolver to this builder, optionally with an overridden default locale. */ - public fun withPhraseTag(locale: String? = null) { + public fun withPhraseTag(locale: Locale? = null) { tag(PHRASE_TAG_NAME) { arguments, ctx -> val key = arguments.popOr("No key provided.").lowerValue() val targetLocale = arguments.peek()?.let { arg -> runCatching { Locale.forLanguageTag(arg.lowerValue()) }.getOrElse { exception -> throw ctx.newException("Invalid language tag $arg.", exception, arguments) } - }?.language ?: locale ?: miniPhrase.defaultLocale.language + } ?: locale ?: miniPhrase.defaultLocale val result = miniPhrase.translationRegistry[key, targetLocale] From e0c985f81b784d1f6eb786fcdd22592834977f3f Mon Sep 17 00:00:00 2001 From: Hugo Vallejo Date: Thu, 14 Dec 2023 17:34:58 +0100 Subject: [PATCH 10/17] Update libraries and stuff --- build.gradle.kts | 15 +--- .../kotlin/dev/kezz/miniphrase/MiniPhrase.kt | 32 ++++---- .../dev/kezz/miniphrase/audience/ArrayExt.kt | 7 +- .../kezz/miniphrase/audience/AudienceExt.kt | 7 +- .../kezz/miniphrase/audience/IterableExt.kt | 7 +- .../i18n/EmptyTranslationRegistry.kt | 15 +++- .../i18n/MapBasedTranslationRegistry.kt | 10 ++- .../i18n/PropertiesFileTranslationRegistry.kt | 51 ++++++------- .../miniphrase/i18n/TranslationRegistry.kt | 13 +++- .../kezz/miniphrase/tag/TagResolverBuilder.kt | 75 +++++++++++++------ gradle/libs.versions.toml | 4 +- gradle/wrapper/gradle-wrapper.properties | 2 +- settings.gradle.kts | 2 +- 13 files changed, 142 insertions(+), 98 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 33bf65e..4c8a76d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,8 +3,6 @@ import net.kyori.indra.IndraPlugin import net.kyori.indra.IndraPublishingPlugin import org.jetbrains.kotlin.gradle.plugin.KotlinPluginWrapper import org.jetbrains.kotlin.gradle.tasks.KotlinCompile -import org.jlleitschuh.gradle.ktlint.KtlintBasePlugin -import org.jlleitschuh.gradle.ktlint.KtlintIdeaPlugin @Suppress("DSL_SCOPE_VIOLATION") // https://youtrack.jetbrains.com/issue/KTIJ-19369 plugins { @@ -15,7 +13,7 @@ plugins { alias(libs.plugins.ktlint) } -tasks.withType() { +tasks.withType { onlyIf { false } } @@ -28,12 +26,7 @@ subprojects { apply() apply() apply() - apply() - apply() - - repositories { - mavenCentral() - } + apply(plugin = "org.jlleitschuh.gradle.ktlint") dependencies { compileOnly(rootProject.project.libs.kotlin.stdlib) @@ -71,10 +64,10 @@ subprojects { } ktlint { - version.set("0.45.1") + version.set("1.0.1") } - withType() { + withType { kotlinOptions { freeCompilerArgs = freeCompilerArgs + "-Xcontext-receivers" } diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt b/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt index 917ed17..ef1302e 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt @@ -39,17 +39,15 @@ public class MiniPhrase private constructor( /** The default locale for translations. */ public val defaultLocale: Locale, /** If the phrase tag should be included by default. */ - public val includePhraseTag: Boolean + public val includePhraseTag: Boolean, ) : MiniPhraseContext { - public companion object { /** Creates a simple MiniPhrase instance from a given translation registry. */ public fun fromTranslationRegistry(translationRegistry: TranslationRegistry): MiniPhrase = configureAndBuild { translationRegistry(translationRegistry) } /** Configures and builds a MiniPhrase instance using the provided [builder]. */ - public fun configureAndBuild(builder: Builder.() -> Unit): MiniPhrase = - Builder().apply(builder).build() + public fun configureAndBuild(builder: Builder.() -> Unit): MiniPhrase = Builder().apply(builder).build() } init { @@ -60,16 +58,25 @@ public class MiniPhrase private constructor( override val miniPhrase: MiniPhrase = this /** Formats a string and applies styles and tags. */ - public fun format(text: String, locale: Locale? = null, tags: (TagResolverBuilder.() -> Unit)? = null): Component { - val resolver = TagResolverBuilder.configureAndBuild(this) { - if (includePhraseTag) withPhraseTag(locale) - if (tags != null) tags() - } + public fun format( + text: String, + locale: Locale? = null, + tags: (TagResolverBuilder.() -> Unit)? = null, + ): Component { + val resolver = + TagResolverBuilder.configureAndBuild(this) { + if (includePhraseTag) withPhraseTag(locale) + if (tags != null) tags() + } return miniMessage.deserialize(text, resolver) } /** Translates a key with a given locale, or the default locale. */ - public fun translate(key: String, locale: Locale? = null, tags: (TagResolverBuilder.() -> Unit)? = null): Component { + public fun translate( + key: String, + locale: Locale? = null, + tags: (TagResolverBuilder.() -> Unit)? = null, + ): Component { val targetLocale = locale ?: defaultLocale val translationString = translationRegistry[key, targetLocale] ?: key @@ -80,7 +87,7 @@ public class MiniPhrase private constructor( public fun translateList( key: String, locale: Locale? = null, - tags: (TagResolverBuilder.() -> Unit)? = null + tags: (TagResolverBuilder.() -> Unit)? = null, ): List { val targetLocale = locale ?: defaultLocale val lines = translationRegistry.getList(key, targetLocale) @@ -126,7 +133,6 @@ public class MiniPhrase private constructor( } /** Creates a MiniPhrase instance from this builder. */ - public fun build(): MiniPhrase = - MiniPhrase(miniMessage, translationRegistry, defaultLocale, includePhraseTag) + public fun build(): MiniPhrase = MiniPhrase(miniMessage, translationRegistry, defaultLocale, includePhraseTag) } } diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/audience/ArrayExt.kt b/core/src/main/kotlin/dev/kezz/miniphrase/audience/ArrayExt.kt index c9ebc44..6e54104 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/audience/ArrayExt.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/audience/ArrayExt.kt @@ -26,18 +26,17 @@ package dev.kezz.miniphrase.audience import dev.kezz.miniphrase.MiniPhraseContext import dev.kezz.miniphrase.tag.TagResolverBuilder import net.kyori.adventure.audience.Audience -import java.util.* +import java.util.Locale /** Shorthand for [Audience.audience]. */ -public fun Array.asAudience(): Audience = - Audience.audience(*this) +public fun Array.asAudience(): Audience = Audience.audience(*this) /** @see [Audience.sendTranslated]. */ context(MiniPhraseContext) public fun Array.sendTranslated( key: String, locale: Locale? = null, - tags: (TagResolverBuilder.() -> Unit)? = null + tags: (TagResolverBuilder.() -> Unit)? = null, ) { if (locale != null) { // If we've got an override locale, we can save rendering by wrapping this in an audience. diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/audience/AudienceExt.kt b/core/src/main/kotlin/dev/kezz/miniphrase/audience/AudienceExt.kt index 7d76fbf..7b4c4b6 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/audience/AudienceExt.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/audience/AudienceExt.kt @@ -27,9 +27,8 @@ import dev.kezz.miniphrase.MiniPhraseContext import dev.kezz.miniphrase.tag.TagResolverBuilder import net.kyori.adventure.audience.Audience import net.kyori.adventure.audience.ForwardingAudience -import net.kyori.adventure.chat.ChatType import net.kyori.adventure.identity.Identity -import java.util.* +import java.util.Locale /** * Sends a message to this audience, with optional additional tags. @@ -50,8 +49,8 @@ public fun Audience.sendTranslated( key: String, /** The locale to translate the message in, if not the default for the audience. */ locale: Locale? = null, - /* A builder of additional tags to use in the deserialization process. */ - tags: (TagResolverBuilder.() -> Unit)? = null + // A builder of additional tags to use in the deserialization process. + tags: (TagResolverBuilder.() -> Unit)? = null, ) { when { this == Audience.empty() -> { diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/audience/IterableExt.kt b/core/src/main/kotlin/dev/kezz/miniphrase/audience/IterableExt.kt index a58a475..cede84c 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/audience/IterableExt.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/audience/IterableExt.kt @@ -26,18 +26,17 @@ package dev.kezz.miniphrase.audience import dev.kezz.miniphrase.MiniPhraseContext import dev.kezz.miniphrase.tag.TagResolverBuilder import net.kyori.adventure.audience.Audience -import java.util.* +import java.util.Locale /** Shorthand for [Audience.audience]. */ -public fun Iterable.asAudience(): Audience = - Audience.audience(this) +public fun Iterable.asAudience(): Audience = Audience.audience(this) /** @see [Audience.sendTranslated] */ context(MiniPhraseContext) public fun Iterable.sendTranslated( key: String, locale: Locale? = null, - tags: (TagResolverBuilder.() -> Unit)? = null + tags: (TagResolverBuilder.() -> Unit)? = null, ) { if (locale != null) { // If we've got an override locale, we can save rendering by wrapping this in an audience. diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/EmptyTranslationRegistry.kt b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/EmptyTranslationRegistry.kt index 54066df..9d2c4ea 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/EmptyTranslationRegistry.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/EmptyTranslationRegistry.kt @@ -23,12 +23,21 @@ */ package dev.kezz.miniphrase.i18n -import java.util.* +import java.util.Locale /** An empty translation registry. */ public object EmptyTranslationRegistry : TranslationRegistry { override fun reload() { } - override fun get(key: String, locale: Locale): String? = null - override fun getList(key: String, locale: Locale): List = listOf() + + override fun get( + key: String, + locale: Locale, + ): String? = null + + override fun getList( + key: String, + locale: Locale, + ): List = listOf() + override fun getLocales(): Set = setOf() } diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/MapBasedTranslationRegistry.kt b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/MapBasedTranslationRegistry.kt index 081b792..397a787 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/MapBasedTranslationRegistry.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/MapBasedTranslationRegistry.kt @@ -23,21 +23,23 @@ */ package dev.kezz.miniphrase.i18n -import java.util.* +import java.util.Locale /** A translation registry that is backed by a map populated by a supplier. */ public open class MapBasedTranslationRegistry( /** The supplier of content for the map, used in reloads. */ - private val supplier: () -> Map> + private val supplier: () -> Map>, ) : TranslationRegistry { - private var map: Map> = mapOf() override fun reload() { map = supplier() } - override fun get(key: String, locale: Locale): String? = map[locale]?.get(key) + override fun get( + key: String, + locale: Locale, + ): String? = map[locale]?.get(key) override fun getLocales(): Set = map.keys } diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt index 1cadd04..14ecca5 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt @@ -27,7 +27,8 @@ import java.io.File import java.io.FileInputStream import java.nio.file.Files import java.nio.file.StandardCopyOption -import java.util.* +import java.util.Locale +import java.util.Properties /** * A translation registry that is populated by looking for property files in [path]. @@ -37,38 +38,38 @@ import java.util.* */ public class PropertiesFileTranslationRegistry( private val path: File, - private val fetchFromResources: Boolean = true + private val fetchFromResources: Boolean = true, ) : MapBasedTranslationRegistry({ - buildMap { - val exists = path.exists() + buildMap { + val exists = path.exists() - if (exists && !path.isDirectory) return@buildMap - if (!exists) path.mkdirs() + if (exists && !path.isDirectory) return@buildMap + if (!exists) path.mkdirs() - Locale.getAvailableLocales().forEach { language -> - val languageKey = language.toLanguageTag() - val translationsFile = File(path, "$languageKey.properties") + Locale.getAvailableLocales().forEach { language -> + val languageKey = language.toLanguageTag() + val translationsFile = File(path, "$languageKey.properties") - // If the file doesn't exist we check for the file in the resources and copy it - // if the file exists there. - if (!translationsFile.exists() && fetchFromResources) { - val resourceStream = javaClass.getResourceAsStream("/$languageKey.properties") ?: return@forEach + // If the file doesn't exist we check for the file in the resources and copy it + // if the file exists there. + if (!translationsFile.exists() && fetchFromResources) { + val resourceStream = javaClass.getResourceAsStream("/$languageKey.properties") ?: return@forEach - translationsFile.createNewFile() - Files.copy(resourceStream, translationsFile.getAbsoluteFile().toPath(), StandardCopyOption.REPLACE_EXISTING) - } + translationsFile.createNewFile() + Files.copy(resourceStream, translationsFile.getAbsoluteFile().toPath(), StandardCopyOption.REPLACE_EXISTING) + } - // If there is a file we read its contents and save it into the map. - if (translationsFile.exists()) { - val inputStream = FileInputStream(translationsFile) + // If there is a file we read its contents and save it into the map. + if (translationsFile.exists()) { + val inputStream = FileInputStream(translationsFile) - val properties = Properties() + val properties = Properties() - properties.load(inputStream) - inputStream.close() + properties.load(inputStream) + inputStream.close() - put(language, properties.stringPropertyNames().associateWith { key -> properties.getProperty(key) }) + put(language, properties.stringPropertyNames().associateWith { key -> properties.getProperty(key) }) + } } } - } -}) + }) diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/TranslationRegistry.kt b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/TranslationRegistry.kt index ced4edb..9710b64 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/TranslationRegistry.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/TranslationRegistry.kt @@ -23,22 +23,27 @@ */ package dev.kezz.miniphrase.i18n -import java.util.* +import java.util.Locale /** A registry of translations. */ public interface TranslationRegistry { - /** Loads or reloads all translations. */ public fun reload() /** Returns a translation for a given [key] in a [locale]. */ - public operator fun get(key: String, locale: Locale): String? + public operator fun get( + key: String, + locale: Locale, + ): String? /** * Returns a translation list for a given [key] in a [locale]. * List members are split with "\n" inside a translation. */ - public fun getList(key: String, locale: Locale): List = get(key, locale)?.split("\n") ?: listOf(key) + public fun getList( + key: String, + locale: Locale, + ): List = get(key, locale)?.split("\n") ?: listOf(key) /** Returns the list of registered locales. */ public fun getLocales(): Set diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/tag/TagResolverBuilder.kt b/core/src/main/kotlin/dev/kezz/miniphrase/tag/TagResolverBuilder.kt index bae2bfc..0e48dce 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/tag/TagResolverBuilder.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/tag/TagResolverBuilder.kt @@ -30,78 +30,109 @@ import net.kyori.adventure.text.minimessage.Context import net.kyori.adventure.text.minimessage.tag.Tag import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver -import java.util.* +import java.util.Locale /** * A wrapper around [TagResolver.Builder] with useful defaults, utility methods and * helper functions for MiniPhrase-specific elements. */ public class TagResolverBuilder private constructor( - override val miniPhrase: MiniPhrase + override val miniPhrase: MiniPhrase, ) : MiniPhraseContext { - public companion object { private const val PHRASE_TAG_NAME: String = "phrase" /** Configures and builds a tag resolver using the provided builder and MiniPhrase instance. */ - public fun configureAndBuild(miniPhrase: MiniPhrase, builder: TagResolverBuilder.() -> Unit): TagResolver = - TagResolverBuilder(miniPhrase).apply(builder).build() + public fun configureAndBuild( + miniPhrase: MiniPhrase, + builder: TagResolverBuilder.() -> Unit, + ): TagResolver = TagResolverBuilder(miniPhrase).apply(builder).build() } private val builder: TagResolver.Builder = TagResolver.builder() /** Adds a parsed placeholder to this builder. */ - public fun parsed(key: String, value: Any?) { + public fun parsed( + key: String, + value: Any?, + ) { parsed(key, value.toString()) } /** Adds a parsed placeholder to this builder. */ - public fun parsed(key: String, value: String) { + public fun parsed( + key: String, + value: String, + ) { tag(key, Tag.preProcessParsed(value)) } /** Adds a parsed placeholder to this builder. The value is lazily computed. */ - public fun parsed(key: String, value: () -> String) { + public fun parsed( + key: String, + value: () -> String, + ) { tag(key) { _, _ -> Tag.preProcessParsed(value()) } } /** Adds an unparsed placeholder to this builder. */ - public fun unparsed(key: String, value: Any?) { + public fun unparsed( + key: String, + value: Any?, + ) { unparsed(key, value.toString()) } /** Adds an unparsed placeholder to this builder. */ - public fun unparsed(key: String, value: String) { + public fun unparsed( + key: String, + value: String, + ) { tag(key, Tag.inserting(Component.text(value))) } /** Adds an unparsed placeholder to this builder. The value is lazily computed. */ @JvmName("unparsedAny") - public fun unparsed(key: String, value: () -> Any?) { + public fun unparsed( + key: String, + value: () -> Any?, + ) { unparsed(key) { value().toString() } } /** Adds an unparsed placeholder to this builder. The value is lazily computed. */ - public fun unparsed(key: String, value: () -> String) { + public fun unparsed( + key: String, + value: () -> String, + ) { tag(key) { _, _ -> Tag.inserting(Component.text(value())) } } /** Adds a tag that inserts a component. */ - public fun inserting(key: String, value: Component) { + public fun inserting( + key: String, + value: Component, + ) { tag(key, Tag.inserting(value)) } /** Adds a tag to this builder. */ - public fun tag(key: String, tag: Tag) { + public fun tag( + key: String, + tag: Tag, + ) { builder.tag(key, tag) } /** Constructs a tag and adds it to this builder. */ - public fun tag(key: String, tag: (arguments: ArgumentQueue, ctx: Context) -> Tag) { + public fun tag( + key: String, + tag: (arguments: ArgumentQueue, ctx: Context) -> Tag, + ) { builder.tag(key, tag) } @@ -114,11 +145,12 @@ public class TagResolverBuilder private constructor( public fun withPhraseTag(locale: Locale? = null) { tag(PHRASE_TAG_NAME) { arguments, ctx -> val key = arguments.popOr("No key provided.").lowerValue() - val targetLocale = arguments.peek()?.let { arg -> - runCatching { Locale.forLanguageTag(arg.lowerValue()) }.getOrElse { exception -> - throw ctx.newException("Invalid language tag $arg.", exception, arguments) - } - } ?: locale ?: miniPhrase.defaultLocale + val targetLocale = + arguments.peek()?.let { arg -> + runCatching { Locale.forLanguageTag(arg.lowerValue()) }.getOrElse { exception -> + throw ctx.newException("Invalid language tag $arg.", exception, arguments) + } + } ?: locale ?: miniPhrase.defaultLocale val result = miniPhrase.translationRegistry[key, targetLocale] @@ -131,6 +163,5 @@ public class TagResolverBuilder private constructor( } /** Creates a tag resolver from this builder. */ - public fun build(): TagResolver = - builder.build() + public fun build(): TagResolver = builder.build() } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 86a0dab..f22916a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -indra = "2.1.1" +indra = "3.1.3" kotlin = "1.9.21" [libraries] @@ -11,4 +11,4 @@ indra = { id = "net.kyori.indra", version.ref = "indra" } indra-license = { id = "net.kyori.indra.license-header", version.ref = "indra" } indra-sonatype = { id = "net.kyori.indra.publishing.sonatype", version.ref = "indra" } kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } -ktlint = { id = "org.jlleitschuh.gradle.ktlint", version = "10.3.0" } +ktlint = { id = "org.jlleitschuh.gradle.ktlint", version = "12.0.3" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index aa991fc..a595206 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/settings.gradle.kts b/settings.gradle.kts index 68bee5b..0b13ecd 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -8,7 +8,7 @@ dependencyResolutionManagement { rootProject.name = "miniphrase-parent" sequenceOf( - "core" + "core", ).forEach { projectName -> include("miniphrase-$projectName") project(":miniphrase-$projectName").projectDir = file(projectName) From ad671de1eb6326e74eb613617e900cd1de6244bf Mon Sep 17 00:00:00 2001 From: Hugo Vallejo Date: Sun, 17 Dec 2023 18:24:25 +0100 Subject: [PATCH 11/17] Some small tweaks --- .../dev/kezz/miniphrase/i18n/MapBasedTranslationRegistry.kt | 2 +- .../miniphrase/i18n/PropertiesFileTranslationRegistry.kt | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/MapBasedTranslationRegistry.kt b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/MapBasedTranslationRegistry.kt index 397a787..c45ea59 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/MapBasedTranslationRegistry.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/MapBasedTranslationRegistry.kt @@ -39,7 +39,7 @@ public open class MapBasedTranslationRegistry( override fun get( key: String, locale: Locale, - ): String? = map[locale]?.get(key) + ): String? = map[locale]?.get(key) ?: map[Locale.US]?.get(key) override fun getLocales(): Set = map.keys } diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt index 14ecca5..7d5596c 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt @@ -38,6 +38,7 @@ import java.util.Properties */ public class PropertiesFileTranslationRegistry( private val path: File, + private val resourcesPath: String = "/", private val fetchFromResources: Boolean = true, ) : MapBasedTranslationRegistry({ buildMap { @@ -47,13 +48,13 @@ public class PropertiesFileTranslationRegistry( if (!exists) path.mkdirs() Locale.getAvailableLocales().forEach { language -> - val languageKey = language.toLanguageTag() + val languageKey = language.toLanguageTag().lowercase().replace("-", "_") val translationsFile = File(path, "$languageKey.properties") // If the file doesn't exist we check for the file in the resources and copy it // if the file exists there. if (!translationsFile.exists() && fetchFromResources) { - val resourceStream = javaClass.getResourceAsStream("/$languageKey.properties") ?: return@forEach + val resourceStream = javaClass.getResourceAsStream("$resourcesPath$languageKey.properties") ?: return@forEach translationsFile.createNewFile() Files.copy(resourceStream, translationsFile.getAbsoluteFile().toPath(), StandardCopyOption.REPLACE_EXISTING) From a12d6d466ef7529b351620105152a322b92c10aa Mon Sep 17 00:00:00 2001 From: Hugo Vallejo Date: Sun, 17 Dec 2023 18:29:04 +0100 Subject: [PATCH 12/17] Better default language handling --- .../src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt | 11 ++++++----- .../miniphrase/i18n/MapBasedTranslationRegistry.kt | 2 +- .../dev/kezz/miniphrase/i18n/TranslationRegistry.kt | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt b/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt index ef1302e..80001a9 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt @@ -60,7 +60,7 @@ public class MiniPhrase private constructor( /** Formats a string and applies styles and tags. */ public fun format( text: String, - locale: Locale? = null, + locale: Locale? = defaultLocale, tags: (TagResolverBuilder.() -> Unit)? = null, ): Component { val resolver = @@ -78,9 +78,9 @@ public class MiniPhrase private constructor( tags: (TagResolverBuilder.() -> Unit)? = null, ): Component { val targetLocale = locale ?: defaultLocale - val translationString = translationRegistry[key, targetLocale] ?: key + val translationString = translationRegistry[key, targetLocale] ?: translationRegistry[key, defaultLocale] ?: key - return format(translationString, locale, tags) + return format(translationString, locale ?: defaultLocale, tags) } /** Translates a key with a given locale, or the default locale into multiple lines. */ @@ -90,9 +90,10 @@ public class MiniPhrase private constructor( tags: (TagResolverBuilder.() -> Unit)? = null, ): List { val targetLocale = locale ?: defaultLocale - val lines = translationRegistry.getList(key, targetLocale) + val lines = + translationRegistry.getList(key, targetLocale) ?: translationRegistry.getList(key, defaultLocale) ?: listOf(key) - return lines.map { format(it, locale, tags) } + return lines.map { format(it, locale ?: defaultLocale, tags) } } /** Builder class for MiniPhrase instances. */ diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/MapBasedTranslationRegistry.kt b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/MapBasedTranslationRegistry.kt index c45ea59..397a787 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/MapBasedTranslationRegistry.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/MapBasedTranslationRegistry.kt @@ -39,7 +39,7 @@ public open class MapBasedTranslationRegistry( override fun get( key: String, locale: Locale, - ): String? = map[locale]?.get(key) ?: map[Locale.US]?.get(key) + ): String? = map[locale]?.get(key) override fun getLocales(): Set = map.keys } diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/TranslationRegistry.kt b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/TranslationRegistry.kt index 9710b64..a0f5438 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/TranslationRegistry.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/TranslationRegistry.kt @@ -43,7 +43,7 @@ public interface TranslationRegistry { public fun getList( key: String, locale: Locale, - ): List = get(key, locale)?.split("\n") ?: listOf(key) + ): List? = get(key, locale)?.split("\n") /** Returns the list of registered locales. */ public fun getLocales(): Set From 8ae243f3b7fb5e78b01c4d1906e86e039fcd4c5c Mon Sep 17 00:00:00 2001 From: Hugo Vallejo Date: Sun, 17 Dec 2023 18:39:59 +0100 Subject: [PATCH 13/17] Better resources handling --- .../kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt index 7d5596c..48ba6ab 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt @@ -38,7 +38,7 @@ import java.util.Properties */ public class PropertiesFileTranslationRegistry( private val path: File, - private val resourcesPath: String = "/", + private val resourcesPrefix: String = "", private val fetchFromResources: Boolean = true, ) : MapBasedTranslationRegistry({ buildMap { @@ -54,7 +54,7 @@ public class PropertiesFileTranslationRegistry( // If the file doesn't exist we check for the file in the resources and copy it // if the file exists there. if (!translationsFile.exists() && fetchFromResources) { - val resourceStream = javaClass.getResourceAsStream("$resourcesPath$languageKey.properties") ?: return@forEach + val resourceStream = javaClass.getResourceAsStream("$resourcesPrefix$languageKey.properties") ?: return@forEach translationsFile.createNewFile() Files.copy(resourceStream, translationsFile.getAbsoluteFile().toPath(), StandardCopyOption.REPLACE_EXISTING) From e90e0253a3ba92d6cbb8fc98b6e5761448d99e2b Mon Sep 17 00:00:00 2001 From: Hugo Vallejo Date: Sun, 17 Dec 2023 18:53:03 +0100 Subject: [PATCH 14/17] Fix resources --- .../kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt index 48ba6ab..2db92de 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt @@ -54,7 +54,7 @@ public class PropertiesFileTranslationRegistry( // If the file doesn't exist we check for the file in the resources and copy it // if the file exists there. if (!translationsFile.exists() && fetchFromResources) { - val resourceStream = javaClass.getResourceAsStream("$resourcesPrefix$languageKey.properties") ?: return@forEach + val resourceStream = javaClass.getResourceAsStream("/$resourcesPrefix$languageKey.properties") ?: return@forEach translationsFile.createNewFile() Files.copy(resourceStream, translationsFile.getAbsoluteFile().toPath(), StandardCopyOption.REPLACE_EXISTING) From af67fef6be760241d2f9d616e1171bf499cabfb8 Mon Sep 17 00:00:00 2001 From: Hugo Vallejo Date: Sun, 17 Mar 2024 17:16:41 +0100 Subject: [PATCH 15/17] Allow to change the minimessage instance of a MiniPhrase instance. --- core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt | 2 +- .../kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt b/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt index 80001a9..ebbd84f 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt @@ -33,7 +33,7 @@ import java.util.Locale /** The main entry-point for the MiniPhrase library. */ public class MiniPhrase private constructor( /** The MiniMessage instance. */ - public val miniMessage: MiniMessage, + public var miniMessage: MiniMessage, /** The translation registry. */ public val translationRegistry: TranslationRegistry, /** The default locale for translations. */ diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt index 2db92de..2cbd26c 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/i18n/PropertiesFileTranslationRegistry.kt @@ -25,6 +25,8 @@ package dev.kezz.miniphrase.i18n import java.io.File import java.io.FileInputStream +import java.io.InputStreamReader +import java.nio.charset.Charset import java.nio.file.Files import java.nio.file.StandardCopyOption import java.util.Locale @@ -66,7 +68,7 @@ public class PropertiesFileTranslationRegistry( val properties = Properties() - properties.load(inputStream) + properties.load(InputStreamReader(inputStream, Charset.forName("UTF-8"))) inputStream.close() put(language, properties.stringPropertyNames().associateWith { key -> properties.getProperty(key) }) From 16ea7a2362c7bb90f00de022baadc13e9f27c8e4 Mon Sep 17 00:00:00 2001 From: Hugo Vallejo Date: Sun, 26 May 2024 16:30:16 +0200 Subject: [PATCH 16/17] Add sendIfPresent --- .../kotlin/dev/kezz/miniphrase/MiniPhrase.kt | 12 +++++++++ .../kezz/miniphrase/audience/AudienceExt.kt | 27 +++++++++++++++++++ gradle/libs.versions.toml | 2 +- 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt b/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt index ebbd84f..afb460a 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt @@ -71,6 +71,18 @@ public class MiniPhrase private constructor( return miniMessage.deserialize(text, resolver) } + /** Translates a key with a given locale, or the default locale. */ + public fun translateOrNull( + key: String, + locale: Locale? = null, + tags: (TagResolverBuilder.() -> Unit)? = null, + ): Component? { + val targetLocale = locale ?: defaultLocale + val translationString = translationRegistry[key, targetLocale] ?: translationRegistry[key, defaultLocale] + + return translationString?.let { format(it, locale ?: defaultLocale, tags) } + } + /** Translates a key with a given locale, or the default locale. */ public fun translate( key: String, diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/audience/AudienceExt.kt b/core/src/main/kotlin/dev/kezz/miniphrase/audience/AudienceExt.kt index 7b4c4b6..9007e28 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/audience/AudienceExt.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/audience/AudienceExt.kt @@ -69,3 +69,30 @@ public fun Audience.sendTranslated( } } } + +context(MiniPhraseContext) +public fun Audience.sendTranslatedIfPresent( + /** The key of the message. */ + key: String, + /** The locale to translate the message in, if not the default for the audience. */ + locale: Locale? = null, + // A builder of additional tags to use in the deserialization process. + tags: (TagResolverBuilder.() -> Unit)? = null, +) { + when { + this == Audience.empty() -> { + // Do nothing if this audience is the empty audience. + } + + locale == null && this is ForwardingAudience -> { + // We only run through each child if the locale is null (i.e. we're pulling it from the audience itself). + forEachAudience { child -> child.sendTranslated(key, locale, tags) } + } + + else -> { + // Try and get the locale from the audience, otherwise default, then translate and send! + val targetLocale = locale ?: get(Identity.LOCALE).orElseGet(miniPhrase::defaultLocale) + miniPhrase.translateOrNull(key, targetLocale, tags)?.let { sendMessage(it) } + } + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f22916a..8f357ec 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ indra = "3.1.3" kotlin = "1.9.21" [libraries] -adventure-minimessage = { group = "net.kyori", name = "adventure-text-minimessage", version = "4.14.0" } +adventure-minimessage = { group = "net.kyori", name = "adventure-text-minimessage", version = "4.17.0" } kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib", version.ref = "kotlin" } [plugins] From 6f6f16120c86a4dcead9f275b0cf4b41b7d9a25a Mon Sep 17 00:00:00 2001 From: Hugo Vallejo Date: Tue, 19 Nov 2024 19:08:08 +0100 Subject: [PATCH 17/17] Use the default language in phrase tags --- build.gradle.kts | 2 +- .../main/kotlin/dev/kezz/miniphrase/tag/TagResolverBuilder.kt | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 4c8a76d..afb8cd9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -34,7 +34,7 @@ subprojects { kotlin { jvmToolchain { - languageVersion.set(JavaLanguageVersion.of(17)) + languageVersion.set(JavaLanguageVersion.of(21)) } jvmToolchain(17) diff --git a/core/src/main/kotlin/dev/kezz/miniphrase/tag/TagResolverBuilder.kt b/core/src/main/kotlin/dev/kezz/miniphrase/tag/TagResolverBuilder.kt index 0e48dce..c4a9fd6 100644 --- a/core/src/main/kotlin/dev/kezz/miniphrase/tag/TagResolverBuilder.kt +++ b/core/src/main/kotlin/dev/kezz/miniphrase/tag/TagResolverBuilder.kt @@ -152,7 +152,9 @@ public class TagResolverBuilder private constructor( } } ?: locale ?: miniPhrase.defaultLocale - val result = miniPhrase.translationRegistry[key, targetLocale] + val result = + miniPhrase.translationRegistry[key, targetLocale] + ?: miniPhrase.translationRegistry[key, miniPhrase.defaultLocale] if (result == null) { Tag.inserting(Component.text(key))