Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for lists, property file registry and update libraries #13

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
34 changes: 13 additions & 21 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -15,7 +13,7 @@ plugins {
alias(libs.plugins.ktlint)
}

tasks.withType<AbstractPublishToMaven>() {
tasks.withType<AbstractPublishToMaven> {
onlyIf { false }
}

Expand All @@ -28,17 +26,21 @@ subprojects {
apply<IndraLicenseHeaderPlugin>()
apply<IndraPublishingPlugin>()
apply<KotlinPluginWrapper>()
apply<KtlintBasePlugin>()
apply<KtlintIdeaPlugin>()

repositories {
mavenCentral()
}
apply(plugin = "org.jlleitschuh.gradle.ktlint")

dependencies {
compileOnly(rootProject.project.libs.kotlin.stdlib)
}

kotlin {
jvmToolchain {
languageVersion.set(JavaLanguageVersion.of(17))
}

jvmToolchain(17)
explicitApi()
}

tasks {
indra {
mitLicense()
Expand All @@ -61,21 +63,11 @@ subprojects {
}
}

java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(17))
}
}

kotlin {
explicitApi()
}

ktlint {
version.set("0.45.1")
version.set("1.0.1")
}

withType<KotlinCompile>() {
withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = freeCompilerArgs + "-Xcontext-receivers"
}
Expand Down
69 changes: 55 additions & 14 deletions core/src/main/kotlin/dev/kezz/miniphrase/MiniPhrase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,37 +33,79 @@ 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. */
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 {
translationRegistry.reload()
}

/** This MiniPhrase instance. */
override val miniPhrase: MiniPhrase = this

/** Formats a string and applies styles and tags. */
public fun format(
text: String,
locale: Locale? = defaultLocale,
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 translateOrNull(
key: String,
locale: Locale? = null,
tags: (TagResolverBuilder.() -> Unit)? = null,
): Component? {
val targetLocale = locale ?: defaultLocale
val translationString = translationRegistry[key, targetLocale] ?: key
val resolver = TagResolverBuilder.configureAndBuild(this) {
if (includePhraseTag) withPhraseTag(locale)
if (tags != null) tags()
}
return miniMessage.deserialize(translationString, resolver)
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,
locale: Locale? = null,
tags: (TagResolverBuilder.() -> Unit)? = null,
): Component {
val targetLocale = locale ?: defaultLocale
val translationString = translationRegistry[key, targetLocale] ?: translationRegistry[key, defaultLocale] ?: key

return format(translationString, locale ?: defaultLocale, tags)
}

/** Translates a key with a given locale, or the default locale into multiple lines. */
public fun translateList(
key: String,
locale: Locale? = null,
tags: (TagResolverBuilder.() -> Unit)? = null,
): List<Component> {
val targetLocale = locale ?: defaultLocale
val lines =
translationRegistry.getList(key, targetLocale) ?: translationRegistry.getList(key, defaultLocale) ?: listOf(key)

return lines.map { format(it, locale ?: defaultLocale, tags) }
}

/** Builder class for MiniPhrase instances. */
Expand Down Expand Up @@ -104,7 +146,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)
}
}
13 changes: 4 additions & 9 deletions core/src/main/kotlin/dev/kezz/miniphrase/audience/ArrayExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,28 +26,23 @@ 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

/** Shorthand for [Audience.audience]. */
public fun Array<out Audience>.asAudience(): Audience =
Audience.audience(*this)
public fun Array<out Audience>.asAudience(): Audience = Audience.audience(*this)

/** @see [Audience.sendTranslated]. */
context(MiniPhraseContext)
public fun Array<out Audience>.sendTranslated(
key: String,
type: MessageType = MessageType.CHAT,
identity: Identity = Identity.nil(),
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.
asAudience().sendTranslated(key, type, 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, type, identity, locale, tags) }
forEach { audience -> audience.sendTranslated(key, locale, tags) }
}
}
42 changes: 31 additions & 11 deletions core/src/main/kotlin/dev/kezz/miniphrase/audience/AudienceExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ 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

Expand All @@ -48,14 +47,10 @@ 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. */
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() -> {
Expand All @@ -64,15 +59,40 @@ 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)
}
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)
sendMessage(identity, miniPhrase.translate(key, targetLocale, tags), type)
sendMessage(miniPhrase.translate(key, targetLocale, tags))
}
}
}

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) }
}
}
}
13 changes: 4 additions & 9 deletions core/src/main/kotlin/dev/kezz/miniphrase/audience/IterableExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,28 +26,23 @@ 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

/** Shorthand for [Audience.audience]. */
public fun Iterable<Audience>.asAudience(): Audience =
Audience.audience(this)
public fun Iterable<Audience>.asAudience(): Audience = Audience.audience(this)

/** @see [Audience.sendTranslated] */
context(MiniPhraseContext)
public fun Iterable<Audience>.sendTranslated(
key: String,
type: MessageType = MessageType.CHAT,
identity: Identity = Identity.nil(),
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.
asAudience().sendTranslated(key, type, 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, type, identity, locale, tags) }
forEach { audience -> audience.sendTranslated(key, locale, tags) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,17 @@ 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: Locale,
): String? = null

override fun getList(
key: String,
locale: Locale,
): List<String> = listOf()

override fun getLocales(): Set<Locale> = setOf()
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,20 @@ 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<Locale, Map<String, String>>
private val supplier: () -> Map<Locale, Map<String, String>>,
) : TranslationRegistry {
private var map: Map<Locale, Map<String, String>> = mapOf()

override suspend fun reload() {
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<Locale> = map.keys
}
Loading