diff --git a/annotations/annotation-processor/build.gradle.kts b/annotations/annotation-processor/build.gradle.kts index 164c539088..0c988ac0a1 100644 --- a/annotations/annotation-processor/build.gradle.kts +++ b/annotations/annotation-processor/build.gradle.kts @@ -10,6 +10,7 @@ metadata { dependencies { implementation(libs.kotlin.stdlib) + implementation(libs.kotlin.reflect) implementation(libs.koin.core) implementation(libs.ksp) diff --git a/annotations/annotation-processor/src/main/kotlin/dev/kordex/core/annotations/converters/builders/ConverterBuilderClassBuilder.kt b/annotations/annotation-processor/src/main/kotlin/dev/kordex/core/annotations/converters/builders/ConverterBuilderClassBuilder.kt index 8bbf2044d4..280316ae85 100644 --- a/annotations/annotation-processor/src/main/kotlin/dev/kordex/core/annotations/converters/builders/ConverterBuilderClassBuilder.kt +++ b/annotations/annotation-processor/src/main/kotlin/dev/kordex/core/annotations/converters/builders/ConverterBuilderClassBuilder.kt @@ -189,7 +189,7 @@ public class ConverterBuilderClassBuilder : KoinComponent { builder.append(" {\n") if (ConverterType.CHOICE in types) { - builder.append(" override var choices: MutableMap = mutableMapOf()\n\n") + builder.append(" override var choices: MutableMap = mutableMapOf()\n\n") } builder.append(" /** @inject: builderFields **/\n") diff --git a/annotations/annotation-processor/src/main/kotlin/dev/kordex/core/annotations/converters/builders/ConverterBuilderFunctionBuilder.kt b/annotations/annotation-processor/src/main/kotlin/dev/kordex/core/annotations/converters/builders/ConverterBuilderFunctionBuilder.kt index 437026730b..bcf20274dc 100644 --- a/annotations/annotation-processor/src/main/kotlin/dev/kordex/core/annotations/converters/builders/ConverterBuilderFunctionBuilder.kt +++ b/annotations/annotation-processor/src/main/kotlin/dev/kordex/core/annotations/converters/builders/ConverterBuilderFunctionBuilder.kt @@ -93,6 +93,8 @@ public class ConverterBuilderFunctionBuilder : KoinComponent { builder.append(".() -> Unit\n") + // TODO: Arbitrary function arguments + builder.append("): $converterType<$argumentType>") if (whereSuffix != null) { diff --git a/annotations/annotations/src/main/kotlin/dev/kordex/core/annotations/tooling/Translatable.kt b/annotations/annotations/src/main/kotlin/dev/kordex/core/annotations/tooling/Translatable.kt deleted file mode 100644 index 8932935ca9..0000000000 --- a/annotations/annotations/src/main/kotlin/dev/kordex/core/annotations/tooling/Translatable.kt +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyrighted (Kord Extensions, 2024). Licensed under the EUPL-1.2 - * with the specific provision (EUPL articles 14 & 15) that the - * applicable law is the (Republic of) Irish law and the Jurisdiction - * Dublin. - * Any redistribution must include the specific provision above. - */ - -package dev.kordex.core.annotations.tooling - -/** - * Tooling annotation representing something that relates to the translation system. - * - * @param type How the annotated element relates to the translation system. - */ -@MustBeDocumented -@Target(AnnotationTarget.PROPERTY, AnnotationTarget.VALUE_PARAMETER) -public annotation class Translatable( - val type: TranslatableType, -) diff --git a/annotations/annotations/src/main/kotlin/dev/kordex/core/annotations/tooling/TranslatableType.kt b/annotations/annotations/src/main/kotlin/dev/kordex/core/annotations/tooling/TranslatableType.kt deleted file mode 100644 index d5b76ea4ca..0000000000 --- a/annotations/annotations/src/main/kotlin/dev/kordex/core/annotations/tooling/TranslatableType.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyrighted (Kord Extensions, 2024). Licensed under the EUPL-1.2 - * with the specific provision (EUPL articles 14 & 15) that the - * applicable law is the (Republic of) Irish law and the Jurisdiction - * Dublin. - * Any redistribution must include the specific provision above. - */ - -package dev.kordex.core.annotations.tooling - -/** - * Enum explaining how an annotated type relates to the translation system. - */ -public enum class TranslatableType { - /** The annotated element specifies a bundle name. **/ - BUNDLE, - - /** The annotated element specifies a locale. **/ - LOCALE, - - /** The annotated element specifies an optionally translated string. **/ - STRING, -} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3a8d914cf2..c0d6a105b2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -51,6 +51,7 @@ koin-test = { module = "io.insert-koin:koin-test", version.ref = "koin" } kord = { module = "dev.kord:kord-core-voice", version.ref = "kord" } kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8" } +kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect" } ksp = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" } ktor-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" } diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/Exceptions.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/Exceptions.kt index baa9a77177..607e8f17aa 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/Exceptions.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/Exceptions.kt @@ -14,7 +14,6 @@ import dev.kordex.core.commands.chat.ChatCommand import dev.kordex.core.commands.converters.builders.ConverterBuilder import dev.kordex.core.events.EventHandler import dev.kordex.core.extensions.Extension -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.parser.StringParser import java.util.* @@ -148,7 +147,6 @@ public open class ArgumentParsingException( public override val reason: String, public override val translationKey: Key?, public val locale: Locale, - public val bundle: Bundle?, public val argument: Argument<*>?, public val arguments: Arguments, public val parser: StringParser?, @@ -158,7 +156,7 @@ public open class ArgumentParsingException( public constructor(other: ArgumentParsingException) : this( other.reason, - other.translationKey, other.locale, other.bundle, + other.translationKey, other.locale, other.argument, other.arguments, other.parser ) diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/annotations/NotTranslated.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/annotations/NotTranslated.kt new file mode 100644 index 0000000000..c3e44be007 --- /dev/null +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/annotations/NotTranslated.kt @@ -0,0 +1,26 @@ +/* + * Copyrighted (Kord Extensions, 2024). Licensed under the EUPL-1.2 + * with the specific provision (EUPL articles 14 & 15) that the + * applicable law is the (Republic of) Irish law and the Jurisdiction + * Dublin. + * Any redistribution must include the specific provision above. + */ + +package dev.kordex.core.annotations + +@RequiresOptIn( + level = RequiresOptIn.Level.WARNING, + message = "A String provided here will not be translated. " + + "If you intended to translate it, please switch to using a Key object." +) +@Target( + AnnotationTarget.CLASS, + AnnotationTarget.TYPEALIAS, + AnnotationTarget.FUNCTION, + AnnotationTarget.PROPERTY, + AnnotationTarget.FIELD, + AnnotationTarget.CONSTRUCTOR, + AnnotationTarget.PROPERTY_SETTER, + AnnotationTarget.PROPERTY_SETTER +) +public annotation class NotTranslated diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/builders/AboutBuilder.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/builders/AboutBuilder.kt index 723e489a31..67559ceeeb 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/builders/AboutBuilder.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/builders/AboutBuilder.kt @@ -12,7 +12,6 @@ import dev.kordex.core.annotations.BotBuilderDSL import dev.kordex.core.builders.about.Copyright import dev.kordex.core.builders.about.CopyrightType import dev.kordex.core.builders.about.Section -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.koin.KordExKoinComponent /** @@ -25,7 +24,6 @@ public class AboutBuilder : KordExKoinComponent { public var ephemeral: Boolean = true - public val translationBundle: Bundle? = null public val sections: MutableMap = mutableMapOf() init { @@ -94,9 +92,6 @@ public class AboutBuilder : KordExKoinComponent { builder(section) - section.translationBundle = section.translationBundle - ?: translationBundle - if (name == "extensions.about.copyright.commandName" || name == "copyright") { error("You may not replace the copyright section.") } diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/builders/ExtensibleBotBuilder.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/builders/ExtensibleBotBuilder.kt index 0810b64e3a..0abfc879dc 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/builders/ExtensibleBotBuilder.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/builders/ExtensibleBotBuilder.kt @@ -137,7 +137,7 @@ public open class ExtensibleBotBuilder { * If all the above values are missing, this setting defaults to "standard". * * For more information on what data KordEx collects, how to get at it, and how it's stored, please see here: - * TODO: LINK HERE + * https://docs.kordex.dev/data-collection.html */ @OptIn(InternalAPI::class) public var dataCollectionMode: DataCollection = DATA_COLLECTION diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/builders/I18nBuilder.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/builders/I18nBuilder.kt index 27d27d02dc..d7eea1a5a6 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/builders/I18nBuilder.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/builders/I18nBuilder.kt @@ -12,8 +12,6 @@ import dev.kord.common.asJavaLocale import dev.kord.common.kLocale import dev.kord.core.entity.interaction.Interaction import dev.kordex.core.annotations.BotBuilderDSL -import dev.kordex.core.annotations.tooling.Translatable -import dev.kordex.core.annotations.tooling.TranslatableType import dev.kordex.core.i18n.ResourceBundleTranslations import dev.kordex.core.i18n.SupportedLocales import dev.kordex.core.i18n.TranslationsProvider @@ -23,8 +21,7 @@ import dev.kord.common.Locale as KLocale /** Builder used to configure i18n options. **/ @BotBuilderDSL public class I18nBuilder { - /** Locale that should be used by default. **/ - @Translatable(TranslatableType.LOCALE) + public var defaultLocale: Locale = SupportedLocales.ENGLISH /** @@ -59,7 +56,6 @@ public class I18nBuilder { */ @JvmName("applicationCommandLocale_v1") public fun applicationCommandLocale( - @Translatable(TranslatableType.LOCALE) vararg locales: KLocale, ) { applicationCommandLocales.addAll(locales.toList()) @@ -72,7 +68,6 @@ public class I18nBuilder { */ @JvmName("applicationCommandLocale_v2") public fun applicationCommandLocale( - @Translatable(TranslatableType.LOCALE) vararg locales: Locale, ) { applicationCommandLocales.addAll(locales.map { it.kLocale }) @@ -85,7 +80,6 @@ public class I18nBuilder { */ @JvmName("applicationCommandLocale_c1") public fun applicationCommandLocale( - @Translatable(TranslatableType.LOCALE) locales: Collection, ) { applicationCommandLocales.addAll(locales) @@ -98,7 +92,6 @@ public class I18nBuilder { */ @JvmName("applicationCommandLocale_c2") public fun applicationCommandLocale( - @Translatable(TranslatableType.LOCALE) locales: Collection, ) { applicationCommandLocales.addAll(locales.map { it.kLocale }) diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/builders/about/Section.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/builders/about/Section.kt index 0e3ec822c2..b28fbdfd98 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/builders/about/Section.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/builders/about/Section.kt @@ -11,7 +11,6 @@ package dev.kordex.core.builders.about import dev.kord.rest.builder.message.MessageBuilder import dev.kordex.core.i18n.TranslationsProvider import dev.kordex.core.i18n.toKey -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.core.koin.KordExKoinComponent import org.koin.core.component.inject @@ -27,7 +26,6 @@ public class Section(public val name: Key, public val description: Key) : KordEx public val translations: TranslationsProvider by inject() public var ephemeral: Boolean? = null - public var translationBundle: Bundle? = null public lateinit var builder: SectionBuilder @@ -36,7 +34,7 @@ public class Section(public val name: Key, public val description: Key) : KordEx } public fun translate(key: Key, locale: Locale, replacements: Array = arrayOf()): String = - key.withBundle(translationBundle) + key .withLocale(locale) .translateArray(replacements) diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/checks/ChannelChecks.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/checks/ChannelChecks.kt index 49315f7947..c143b651f3 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/checks/ChannelChecks.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/checks/ChannelChecks.kt @@ -6,6 +6,8 @@ * Any redistribution must include the specific provision above. */ +@file:OptIn(NotTranslated::class) + package dev.kordex.core.checks import dev.kord.common.entity.Snowflake @@ -16,6 +18,7 @@ import dev.kord.core.behavior.channel.asChannelOfOrNull import dev.kord.core.entity.channel.Category import dev.kord.core.entity.channel.GuildChannel import dev.kord.core.event.Event +import dev.kordex.core.annotations.NotTranslated import dev.kordex.core.checks.types.CheckContext import dev.kordex.core.i18n.generated.CoreTranslations import dev.kordex.core.utils.isAbove diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/checks/ChannelTypeChecks.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/checks/ChannelTypeChecks.kt index b4331affae..f2a9ba2c1a 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/checks/ChannelTypeChecks.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/checks/ChannelTypeChecks.kt @@ -6,10 +6,13 @@ * Any redistribution must include the specific provision above. */ +@file:OptIn(NotTranslated::class) + package dev.kordex.core.checks import dev.kord.common.entity.ChannelType import dev.kord.core.event.Event +import dev.kordex.core.annotations.NotTranslated import dev.kordex.core.checks.types.CheckContext import dev.kordex.core.i18n.generated.CoreTranslations import dev.kordex.core.utils.translate diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/checks/GuildChecks.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/checks/GuildChecks.kt index cec2b1e462..80c7912376 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/checks/GuildChecks.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/checks/GuildChecks.kt @@ -6,11 +6,14 @@ * Any redistribution must include the specific provision above. */ +@file:OptIn(NotTranslated::class) + package dev.kordex.core.checks import dev.kord.common.entity.Snowflake import dev.kord.core.behavior.GuildBehavior import dev.kord.core.event.Event +import dev.kordex.core.annotations.NotTranslated import dev.kordex.core.checks.types.CheckContext import dev.kordex.core.i18n.generated.CoreTranslations import io.github.oshai.kotlinlogging.KotlinLogging diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/checks/MemberChecks.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/checks/MemberChecks.kt index 92308b1af2..3755729e10 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/checks/MemberChecks.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/checks/MemberChecks.kt @@ -6,12 +6,15 @@ * Any redistribution must include the specific provision above. */ +@file:OptIn(NotTranslated::class) + package dev.kordex.core.checks import dev.kord.common.entity.Permission import dev.kord.common.entity.Permissions import dev.kord.core.entity.channel.GuildChannel import dev.kord.core.event.Event +import dev.kordex.core.annotations.NotTranslated import dev.kordex.core.checks.types.CheckContext import dev.kordex.core.i18n.generated.CoreTranslations import dev.kordex.core.utils.hasPermission diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/checks/NSFWChecks.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/checks/NSFWChecks.kt index a00fc40272..c62c5023e1 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/checks/NSFWChecks.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/checks/NSFWChecks.kt @@ -7,12 +7,14 @@ */ @file:Suppress("StringLiteralDuplication") +@file:OptIn(NotTranslated::class) package dev.kordex.core.checks import dev.kord.common.entity.ChannelType import dev.kord.common.entity.NsfwLevel import dev.kord.core.event.Event +import dev.kordex.core.annotations.NotTranslated import dev.kordex.core.checks.types.CheckContext import dev.kordex.core.i18n.generated.CoreTranslations import dev.kordex.core.utils.compareTo diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/checks/RoleChecks.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/checks/RoleChecks.kt index 512fc1bc18..d9f331a1fc 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/checks/RoleChecks.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/checks/RoleChecks.kt @@ -7,12 +7,14 @@ */ @file:Suppress("StringLiteralDuplication") +@file:OptIn(NotTranslated::class) package dev.kordex.core.checks import dev.kord.common.entity.Snowflake import dev.kord.core.behavior.RoleBehavior import dev.kord.core.event.Event +import dev.kordex.core.annotations.NotTranslated import dev.kordex.core.checks.types.CheckContext import dev.kordex.core.i18n.generated.CoreTranslations import dev.kordex.core.utils.getTopRole diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/checks/TopChannelChecks.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/checks/TopChannelChecks.kt index 3dc5578418..3cc59126da 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/checks/TopChannelChecks.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/checks/TopChannelChecks.kt @@ -6,12 +6,15 @@ * Any redistribution must include the specific provision above. */ +@file:OptIn(NotTranslated::class) + package dev.kordex.core.checks import dev.kord.common.entity.Snowflake import dev.kord.core.behavior.channel.ChannelBehavior import dev.kord.core.entity.channel.thread.ThreadChannel import dev.kord.core.event.Event +import dev.kordex.core.annotations.NotTranslated import dev.kordex.core.checks.types.CheckContext import dev.kordex.core.i18n.generated.CoreTranslations import io.github.oshai.kotlinlogging.KotlinLogging diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/checks/_Events.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/checks/_Events.kt index 33f75ae0f5..cc8d2126ba 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/checks/_Events.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/checks/_Events.kt @@ -201,7 +201,7 @@ public suspend fun guildFor(event: Event): GuildBehavior? { is VoiceStateUpdateEvent -> event.state.getGuildOrNull() is WebhookUpdateEvent -> event.guild - // TODO: Kord doesn't have the guild yet? + // TODO: Kord doesn't have the guild yet! // is GuildAuditLogEntryCreateEvent -> event.auditLogEntry.userId else -> null diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/checks/types/CheckContext.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/checks/types/CheckContext.kt index 28474f9757..b6d4b0d0af 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/checks/types/CheckContext.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/checks/types/CheckContext.kt @@ -10,11 +10,9 @@ package dev.kordex.core.checks.types import dev.kord.core.event.Event import dev.kordex.core.DiscordRelayedException -import dev.kordex.core.annotations.tooling.Translatable -import dev.kordex.core.annotations.tooling.TranslatableType +import dev.kordex.core.annotations.NotTranslated import dev.kordex.core.i18n.TranslationsProvider import dev.kordex.core.i18n.generated.CoreTranslations -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.core.koin.KordExKoinComponent import org.koin.core.component.inject @@ -29,8 +27,6 @@ import java.util.* */ public open class CheckContext( public val event: T, - - @Translatable(TranslatableType.LOCALE) public val locale: Locale, ) : KordExKoinComponent { /** Translations provider. **/ @@ -44,13 +40,8 @@ public open class CheckContext( * **Note:** This *must* be a translation key. A bare string may not work, as the error response function uses * the replacement functionality of the translations system. */ - @Translatable(TranslatableType.STRING) public var errorResponseKey: Key = CoreTranslations.Checks.responseTemplate - /** Translation bundle used by [translate] by default and the error response translation, if not the default. **/ - @Translatable(TranslatableType.BUNDLE) - public var defaultBundle: Bundle? = null - /** Human-readable message for the user, if any. **/ public var message: String? = null @@ -63,16 +54,17 @@ public open class CheckContext( } /** Mark this check as having failed, optionally providing a message for the user. **/ - public fun fail(message: String? = null) { + @NotTranslated + public fun fail(message: String) { this.message = message this.passed = false } /** Mark this check as having failed, optionally providing a message for the user. **/ - public fun fail(message: Key) { - this.message = message.withBundle(defaultBundle) - .withLocale(locale) - .translate() + public fun fail(message: Key? = null) { + this.message = message + ?.withLocale(locale) + ?.translate() this.passed = false } @@ -82,7 +74,8 @@ public open class CheckContext( * * Returns `true` if the check was marked as having failed, `false` otherwise. */ - public fun failIf(value: Boolean, message: String? = null): Boolean { + @NotTranslated + public fun failIf(value: Boolean, message: String): Boolean { if (value) { fail(message) @@ -97,7 +90,7 @@ public open class CheckContext( * * Returns `true` if the check was marked as having failed, `false` otherwise. */ - public fun failIf(value: Boolean, message: Key): Boolean { + public fun failIf(value: Boolean, message: Key? = null): Boolean { if (value) { fail(message) @@ -112,7 +105,8 @@ public open class CheckContext( * * Returns `true` if the check was marked as having failed, `false` otherwise. */ - public suspend fun failIf(message: String? = null, callback: suspend () -> Boolean): Boolean = + @NotTranslated + public suspend fun failIf(message: String, callback: suspend () -> Boolean): Boolean = failIf(callback(), message) /** @@ -120,7 +114,7 @@ public open class CheckContext( * * Returns `true` if the check was marked as having failed, `false` otherwise. */ - public suspend fun failIf(message: Key, callback: suspend () -> Boolean): Boolean = + public suspend fun failIf(message: Key? = null, callback: suspend () -> Boolean): Boolean = failIf(callback(), message) /** @@ -128,7 +122,8 @@ public open class CheckContext( * * Returns `true` if the check was marked as having failed, `false` otherwise. */ - public fun failIfNot(value: Boolean, message: String? = null): Boolean = + @NotTranslated + public fun failIfNot(value: Boolean, message: String): Boolean = failIf(!value, message) /** @@ -136,7 +131,7 @@ public open class CheckContext( * * Returns `true` if the check was marked as having failed, `false` otherwise. */ - public fun failIfNot(value: Boolean, message: Key): Boolean = + public fun failIfNot(value: Boolean, message: Key? = null): Boolean = failIf(!value, message) /** @@ -144,7 +139,8 @@ public open class CheckContext( * * Returns `true` if the check was marked as having failed, `false` otherwise. */ - public suspend fun failIfNot(message: String? = null, callback: suspend () -> Boolean): Boolean = + @NotTranslated + public suspend fun failIfNot(message: String, callback: suspend () -> Boolean): Boolean = failIfNot(callback(), message) /** @@ -152,7 +148,7 @@ public open class CheckContext( * * Returns `true` if the check was marked as having failed, `false` otherwise. */ - public suspend fun failIfNot(message: Key, callback: suspend () -> Boolean): Boolean = + public suspend fun failIfNot(message: Key? = null, callback: suspend () -> Boolean): Boolean = failIfNot(callback(), message) /** @@ -214,50 +210,21 @@ public open class CheckContext( /** Quick access to translate strings using this check context's [locale]. **/ public fun translate( - @Translatable(TranslatableType.STRING) key: Key, - @Translatable(TranslatableType.BUNDLE) - bundle: Bundle? = defaultBundle, - replacements: Array = arrayOf(), ): String = - key.withBundle(bundle) + key .withLocale(locale) .translateArray(replacements) /** Quick access to translate strings using this check context's [locale]. **/ public fun translate( - @Translatable(TranslatableType.STRING) - key: Key, - - replacements: Array = arrayOf(), - ): String = - key.withBundle(defaultBundle) - .withLocale(locale) - .translateArray(replacements) - - /** Quick access to translate strings using this check context's [locale]. **/ - public fun translate( - @Translatable(TranslatableType.STRING) - key: Key, - - replacements: Map, - ): String = - key.withBundle(defaultBundle) - .withLocale(locale) - .translateNamed(replacements) - - /** Quick access to translate strings using this check context's [locale]. **/ - public fun translate( - @Translatable(TranslatableType.STRING) key: Key, - @Translatable(TranslatableType.BUNDLE) - bundle: Bundle?, replacements: Map, ): String = - key.withBundle(bundle) + key .withLocale(locale) .translateNamed(replacements) @@ -276,7 +243,7 @@ public open class CheckContext( /** Get the translated check failure message, if the check has failed and a message was set. **/ public fun getTranslatedMessage(): String? = if (passed.not() && message != null) { - errorResponseKey.withBundle(defaultBundle) + errorResponseKey .withLocale(locale) .translate(message) } else { diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/Argument.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/Argument.kt index 0fe8b11887..b46462fbd2 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/Argument.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/Argument.kt @@ -11,6 +11,7 @@ package dev.kordex.core.commands import dev.kordex.core.commands.converters.Converter import dev.kordex.core.i18n.TranslationsProvider import dev.kordex.core.i18n.types.Key +import dev.kordex.core.utils.getKoin /** * Data class representing a single argument. @@ -29,6 +30,5 @@ public data class Argument( } } -internal fun Argument<*>.getDefaultTranslatedDisplayName(provider: TranslationsProvider, command: Command): String = - displayName.withBundle(command.resolvedBundle ?: converter.bundle) - .translateLocale(provider.defaultLocale) +internal fun Argument<*>.getDefaultTranslatedDisplayName(): String = + displayName.translateLocale(getKoin().get().defaultLocale) diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/ChoiceOptionWrapper.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/ChoiceOptionWrapper.kt new file mode 100644 index 0000000000..3bcdd9d16c --- /dev/null +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/ChoiceOptionWrapper.kt @@ -0,0 +1,107 @@ +/* + * Copyrighted (Kord Extensions, 2024). Licensed under the EUPL-1.2 + * with the specific provision (EUPL articles 14 & 15) that the + * applicable law is the (Republic of) Irish law and the Jurisdiction + * Dublin. + * Any redistribution must include the specific provision above. + */ + +@file:Suppress("StringLiteralDuplication") + +package dev.kordex.core.commands + +import dev.kord.rest.builder.interaction.BaseChoiceBuilder +import dev.kord.rest.builder.interaction.IntegerOptionBuilder +import dev.kord.rest.builder.interaction.NumberOptionBuilder +import dev.kord.rest.builder.interaction.StringChoiceBuilder +import dev.kordex.core.annotations.InternalAPI +import dev.kordex.core.i18n.types.Key +import kotlin.reflect.KClass + +public sealed class ChoiceOptionWrapper, T> ( + displayName: Key, + description: Key, + + body: suspend B.() -> Unit, + type: KClass, +) : OptionWrapper(displayName, description, body, type) { + public val choices: MutableList = mutableListOf() + + public fun choice(name: Key, value: T) { + choices.add(ChoiceWrapper(name, value)) + } + + public inner class ChoiceWrapper(public val name: Key, public val value: T) + + public class Integer @InternalAPI constructor( + displayName: Key, + description: Key, + + body: suspend IntegerOptionBuilder.() -> Unit, + ) : ChoiceOptionWrapper(displayName, description, body, IntegerOptionBuilder::class) + + public class Number @InternalAPI constructor( + displayName: Key, + description: Key, + + body: suspend NumberOptionBuilder.() -> Unit, + ) : ChoiceOptionWrapper(displayName, description, body, NumberOptionBuilder::class) + + public class String @InternalAPI constructor( + displayName: Key, + description: Key, + + body: suspend StringChoiceBuilder.() -> Unit, + ) : ChoiceOptionWrapper(displayName, description, body, StringChoiceBuilder::class) +} + +@OptIn(InternalAPI::class) +public suspend fun wrapIntegerOption( + displayName: Key, + description: Key, + + body: suspend IntegerOptionBuilder.() -> Unit, +): ChoiceOptionWrapper.Integer { + val wrapper = ChoiceOptionWrapper.Integer(displayName, description, body) + val kord = wrapper.toKord() as BaseChoiceBuilder<*, *> + + if (kord.choices?.isNotEmpty() == true) { + error("Add choices to the wrapper, not directly to the builder.") + } + + return wrapper +} + +@OptIn(InternalAPI::class) +public suspend fun wrapNumberOption( + displayName: Key, + description: Key, + + body: suspend NumberOptionBuilder.() -> Unit, +): ChoiceOptionWrapper.Number { + val wrapper = ChoiceOptionWrapper.Number(displayName, description, body) + val kord = wrapper.toKord() as BaseChoiceBuilder<*, *> + + if (kord.choices?.isNotEmpty() == true) { + error("Add choices to the wrapper, not directly to the builder.") + } + + return wrapper +} + +@OptIn(InternalAPI::class) +public suspend fun wrapStringOption( + displayName: Key, + description: Key, + + body: suspend StringChoiceBuilder.() -> Unit, +): ChoiceOptionWrapper.String { + val wrapper = ChoiceOptionWrapper.String(displayName, description, body) + val kord = wrapper.toKord() as BaseChoiceBuilder<*, *> + + if (kord.choices?.isNotEmpty() == true) { + error("Add choices to the wrapper, not directly to the builder.") + } + + return wrapper +} diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/Command.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/Command.kt index 0c329a9694..2a35d102b8 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/Command.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/Command.kt @@ -22,7 +22,6 @@ import dev.kordex.core.commands.events.CommandEvent import dev.kordex.core.extensions.Extension import dev.kordex.core.i18n.TranslationsProvider import dev.kordex.core.i18n.generated.CoreTranslations -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.core.koin.KordExKoinComponent import dev.kordex.core.sentry.SentryAdapter @@ -52,16 +51,6 @@ public abstract class Command(public val extension: Extension) : Lockable, KordE /** Set this to `true` to lock command execution with a Mutex. **/ public override var locking: Boolean = false - /** Translation bundle to use, if not the one provided by the extension. **/ - public var bundle: Bundle? = null - - /** - * @suppress Bundle getter that exists because the extension bundle may have changed by the time the command is - * registered. - */ - public val resolvedBundle: Bundle? - get() = bundle ?: extension.bundle - override var mutex: Mutex? = null /** Translations provider, for retrieving translations. **/ diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/CommandContext.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/CommandContext.kt index 1c6f5025f5..817b987679 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/CommandContext.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/CommandContext.kt @@ -19,7 +19,6 @@ import dev.kordex.core.checks.guildFor import dev.kordex.core.checks.interactionFor import dev.kordex.core.checks.userFor import dev.kordex.core.i18n.TranslationsProvider -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.core.koin.KordExKoinComponent import dev.kordex.core.sentry.SentryContext @@ -53,9 +52,6 @@ public abstract class CommandContext( public override var resolvedLocale: Locale? = null - override val bundle: Bundle? - get() = command.resolvedBundle - /** Called before processing, used to populate any extra variables from event data. **/ public abstract suspend fun populate() @@ -98,24 +94,22 @@ public abstract class CommandContext( public override suspend fun translate( key: Key, - bundle: Bundle?, replacements: Array, ): String { val locale = getLocale() - return key.withBundle(bundle) + return key .withLocale(locale) .translateArray(replacements) } public override suspend fun translate( key: Key, - bundle: Bundle?, replacements: Map, ): String { val locale = getLocale() - return key.withBundle(bundle) + return key .withLocale(locale) .translateNamed(replacements) } diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/OptionWrapper.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/OptionWrapper.kt index 0bffa06fc1..e5618e7cf7 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/OptionWrapper.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/OptionWrapper.kt @@ -8,12 +8,21 @@ package dev.kordex.core.commands +import dev.kord.rest.builder.interaction.AttachmentBuilder +import dev.kord.rest.builder.interaction.BooleanBuilder +import dev.kord.rest.builder.interaction.ChannelBuilder +import dev.kord.rest.builder.interaction.IntegerOptionBuilder +import dev.kord.rest.builder.interaction.MentionableBuilder +import dev.kord.rest.builder.interaction.NumberOptionBuilder import dev.kord.rest.builder.interaction.OptionsBuilder +import dev.kord.rest.builder.interaction.RoleBuilder +import dev.kord.rest.builder.interaction.StringChoiceBuilder +import dev.kord.rest.builder.interaction.UserBuilder import dev.kordex.core.annotations.InternalAPI import dev.kordex.core.i18n.types.Key import kotlin.reflect.KClass -public class OptionWrapper @InternalAPI constructor( +public open class OptionWrapper @InternalAPI constructor( public var displayName: Key, public var description: Key, @@ -32,6 +41,55 @@ public class OptionWrapper @InternalAPI constructor( return builder } + + public suspend fun toKord(): OptionsBuilder = when (type) { + AttachmentBuilder::class -> { + this as OptionWrapper + apply(AttachmentBuilder(displayName.key, description.key)) + } + + BooleanBuilder::class -> { + this as OptionWrapper + apply(BooleanBuilder(displayName.key, description.key)) + } + + ChannelBuilder::class -> { + this as OptionWrapper + apply(ChannelBuilder(displayName.key, description.key)) + } + + IntegerOptionBuilder::class -> { + this as OptionWrapper + apply(IntegerOptionBuilder(displayName.key, description.key)) + } + + MentionableBuilder::class -> { + this as OptionWrapper + apply(MentionableBuilder(displayName.key, description.key)) + } + + NumberOptionBuilder::class -> { + this as OptionWrapper + apply(NumberOptionBuilder(displayName.key, description.key)) + } + + RoleBuilder::class -> { + this as OptionWrapper + apply(RoleBuilder(displayName.key, description.key)) + } + + StringChoiceBuilder::class -> { + this as OptionWrapper + apply(StringChoiceBuilder(displayName.key, description.key)) + } + + UserBuilder::class -> { + this as OptionWrapper + apply(UserBuilder(displayName.key, description.key)) + } + + else -> error("Unknown option builder type: ${type.qualifiedName} ($type)") + } } @OptIn(InternalAPI::class) diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/ApplicationCommand.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/ApplicationCommand.kt index a2b8870607..ba2ccd2383 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/ApplicationCommand.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/ApplicationCommand.kt @@ -121,9 +121,7 @@ public abstract class ApplicationCommand( key: Key, lowerCase: Boolean = false, ): Localized { - val bundledKey = key.withBundle(resolvedBundle) - - var default = bundledKey + var default = key .withLocale(translationsProvider.defaultLocale) .translate() @@ -133,7 +131,7 @@ public abstract class ApplicationCommand( val translations = bot.settings.i18nBuilder.applicationCommandLocales .associateWith { locale -> - val result = bundledKey + val result = key .withLocale(locale.asJavaLocale()) .translate() diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/ApplicationCommandRegistry.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/ApplicationCommandRegistry.kt index f1cd23e30e..48860aeb5b 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/ApplicationCommandRegistry.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/ApplicationCommandRegistry.kt @@ -37,6 +37,7 @@ import dev.kord.rest.builder.interaction.* import dev.kord.rest.request.KtorRequestException import dev.kordex.core.ExtensibleBot import dev.kordex.core.commands.Argument +import dev.kordex.core.commands.ChoiceOptionWrapper import dev.kordex.core.commands.OptionWrapper import dev.kordex.core.commands.application.message.MessageCommand import dev.kordex.core.commands.application.slash.SlashCommand @@ -45,7 +46,6 @@ import dev.kordex.core.commands.application.user.UserCommand import dev.kordex.core.commands.converters.SlashCommandConverter import dev.kordex.core.commands.getDefaultTranslatedDisplayName import dev.kordex.core.i18n.TranslationsProvider -import dev.kordex.core.i18n.toKey import dev.kordex.core.koin.KordExKoinComponent import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging @@ -549,61 +549,12 @@ public abstract class ApplicationCommandRegistry : KordExKoinComponent { // endregion - private suspend fun OptionWrapper<*>.toKord() = when (type) { - AttachmentBuilder::class -> { - this as OptionWrapper - apply(AttachmentBuilder(displayName.key, description.key)) - } - - BooleanBuilder::class -> { - this as OptionWrapper - apply(BooleanBuilder(displayName.key, description.key)) - } - - ChannelBuilder::class -> { - this as OptionWrapper - apply(ChannelBuilder(displayName.key, description.key)) - } - - IntegerOptionBuilder::class -> { - this as OptionWrapper - apply(IntegerOptionBuilder(displayName.key, description.key)) - } - - MentionableBuilder::class -> { - this as OptionWrapper - apply(MentionableBuilder(displayName.key, description.key)) - } - - NumberOptionBuilder::class -> { - this as OptionWrapper - apply(NumberOptionBuilder(displayName.key, description.key)) - } - - RoleBuilder::class -> { - this as OptionWrapper - apply(RoleBuilder(displayName.key, description.key)) - } - - StringChoiceBuilder::class -> { - this as OptionWrapper - apply(StringChoiceBuilder(displayName.key, description.key)) - } - - UserBuilder::class -> { - this as OptionWrapper - apply(UserBuilder(displayName.key, description.key)) - } - - else -> error("Unknown option builder type: ${type.qualifiedName} ($type)") - } - private fun OptionsBuilder.translate( option: OptionWrapper<*>, command: ApplicationCommand<*>, argObj: Argument<*>, ) { - val defaultName = argObj.getDefaultTranslatedDisplayName(command.translationsProvider, command) + val defaultName = argObj.getDefaultTranslatedDisplayName() if (defaultName != defaultName.lowercase(command.translationsProvider.defaultLocale)) { throw InvalidNameException( @@ -631,22 +582,28 @@ public abstract class ApplicationCommandRegistry : KordExKoinComponent { this.nameLocalizations = nameLocalizations if (this is BaseChoiceBuilder<*, *> && !choices.isNullOrEmpty()) { - translate(command) + translate(command, option as ChoiceOptionWrapper<*, *>) } } - @Suppress("DEPRECATION_ERROR") - private fun BaseChoiceBuilder<*, C>.translate(command: ApplicationCommand<*>) { - choices = choices!!.map { - // TODO: Choices? It'd be a lot of work to fix this probably! + private fun BaseChoiceBuilder<*, C>.translate( + command: ApplicationCommand<*>, + option: ChoiceOptionWrapper<*, *>, + ) { + choices = option.choices.map { val (name, nameLocalizations) = command.localize( - it.name.toKey() + it.name ) - when (val c = it as Choice) { - is Choice.NumberChoice -> Choice.NumberChoice(name, Optional(nameLocalizations), c.value) - is Choice.StringChoice -> Choice.StringChoice(name, Optional(nameLocalizations), c.value) - is Choice.IntegerChoice -> Choice.IntegerChoice(name, Optional(nameLocalizations), c.value) + when (option) { + is ChoiceOptionWrapper.Number -> + Choice.NumberChoice(name, Optional(nameLocalizations), it.value as Double) + + is ChoiceOptionWrapper.String -> + Choice.StringChoice(name, Optional(nameLocalizations), it.value as String) + + is ChoiceOptionWrapper.Integer -> + Choice.IntegerChoice(name, Optional(nameLocalizations), it.value as Long) } as C }.toMutableList() } diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/DefaultApplicationCommandRegistry.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/DefaultApplicationCommandRegistry.kt index 69eac7b353..82e830a613 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/DefaultApplicationCommandRegistry.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/DefaultApplicationCommandRegistry.kt @@ -386,7 +386,7 @@ public open class DefaultApplicationCommandRegistry : ApplicationCommandRegistry val arguments = command.arguments!!() val arg = arguments.args.firstOrNull { - it.getDefaultTranslatedDisplayName(translationsProvider, command) == option.first + it.getDefaultTranslatedDisplayName() == option.first } arg ?: return logger.warn { @@ -407,7 +407,7 @@ public open class DefaultApplicationCommandRegistry : ApplicationCommandRegistry break } - val argName = priorArg.getDefaultTranslatedDisplayName(translationsProvider, command) + val argName = priorArg.getDefaultTranslatedDisplayName() val currentOption = event.interaction.command.options[argName] if (currentOption == null) { diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/StorageAwareApplicationCommandRegistry.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/StorageAwareApplicationCommandRegistry.kt index c902c28517..4e27b98feb 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/StorageAwareApplicationCommandRegistry.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/StorageAwareApplicationCommandRegistry.kt @@ -120,10 +120,7 @@ public open class StorageAwareApplicationCommandRegistry( val arguments = command.arguments!!() val arg = arguments.args.firstOrNull { - it.getDefaultTranslatedDisplayName( - translationsProvider, - command - ) == option.first + it.getDefaultTranslatedDisplayName() == option.first } arg ?: return logger.warn { @@ -144,7 +141,7 @@ public open class StorageAwareApplicationCommandRegistry( break } - val argName = priorArg.getDefaultTranslatedDisplayName(translationsProvider, command) + val argName = priorArg.getDefaultTranslatedDisplayName() val currentOption = event.interaction.command.options[argName] if (currentOption == null) { diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/message/EphemeralMessageCommand.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/message/EphemeralMessageCommand.kt index 205cdd2eec..f719c54f1f 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/message/EphemeralMessageCommand.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/message/EphemeralMessageCommand.kt @@ -91,10 +91,10 @@ public class EphemeralMessageCommand( val locale = event.getLocale() event.interaction.modal( - modalObj.translateTitle(locale, resolvedBundle), + modalObj.translateTitle(locale), modalObj.id ) { - modalObj.applyToBuilder(this, event.getLocale(), resolvedBundle) + modalObj.applyToBuilder(this, event.getLocale()) } modalObj.awaitCompletion { diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/message/PublicMessageCommand.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/message/PublicMessageCommand.kt index 9873d86948..fbd7d71738 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/message/PublicMessageCommand.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/message/PublicMessageCommand.kt @@ -92,10 +92,10 @@ public class PublicMessageCommand( val locale = event.getLocale() event.interaction.modal( - modalObj.translateTitle(locale, resolvedBundle), + modalObj.translateTitle(locale), modalObj.id ) { - modalObj.applyToBuilder(this, event.getLocale(), resolvedBundle) + modalObj.applyToBuilder(this, event.getLocale()) } modalObj.awaitCompletion { diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/slash/EphemeralSlashCommand.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/slash/EphemeralSlashCommand.kt index 956ddfea13..836318c134 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/slash/EphemeralSlashCommand.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/slash/EphemeralSlashCommand.kt @@ -104,10 +104,10 @@ public class EphemeralSlashCommand( val locale = event.getLocale() event.interaction.modal( - modalObj.translateTitle(locale, resolvedBundle), + modalObj.translateTitle(locale), modalObj.id ) { - modalObj.applyToBuilder(this, event.getLocale(), resolvedBundle) + modalObj.applyToBuilder(this, event.getLocale()) } modalObj.awaitCompletion { diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/slash/PublicSlashCommand.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/slash/PublicSlashCommand.kt index b9faebf618..de8ad7aa22 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/slash/PublicSlashCommand.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/slash/PublicSlashCommand.kt @@ -99,10 +99,10 @@ public class PublicSlashCommand( val locale = event.getLocale() event.interaction.modal( - modalObj.translateTitle(locale, resolvedBundle), + modalObj.translateTitle(locale), modalObj.id ) { - modalObj.applyToBuilder(this, event.getLocale(), resolvedBundle) + modalObj.applyToBuilder(this, event.getLocale()) } modalObj.awaitCompletion { diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/slash/SlashCommandParser.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/slash/SlashCommandParser.kt index 3765f097be..976112e641 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/slash/SlashCommandParser.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/slash/SlashCommandParser.kt @@ -19,6 +19,8 @@ import dev.kordex.core.DiscordRelayedException import dev.kordex.core.commands.Arguments import dev.kordex.core.commands.converters.* import dev.kordex.core.commands.getDefaultTranslatedDisplayName +import dev.kordex.core.i18n.generated.CoreTranslations +import dev.kordex.core.utils.withContext import io.github.oshai.kotlinlogging.KotlinLogging private val logger = KotlinLogging.logger {} @@ -54,7 +56,7 @@ public open class SlashCommandParser { } else { it.value } - } as Map> + } var currentValue: OptionValue<*>? @@ -66,7 +68,7 @@ public open class SlashCommandParser { logger.trace { "Current argument: ${currentArg.displayName}" } currentValue = - values[currentArg.getDefaultTranslatedDisplayName(context.translationsProvider, context.command)] + values[currentArg.getDefaultTranslatedDisplayName()] logger.trace { "Current value: $currentValue" } @@ -84,24 +86,20 @@ public open class SlashCommandParser { if (converter.required && !parsed) { throw ArgumentParsingException( - context.translate( - "argumentParser.error.invalidValue", - - replacements = arrayOf( - context.translate( - currentArg.displayName, - bundleName = context.command.resolvedBundle ?: converter.bundle - ), + CoreTranslations.ArgumentParser.Error.invalidValue + .withContext(context) + .translate( + currentArg.displayName + .withContext(context) + .translate(), converter.getErrorString(context), currentValue - ) - ), + ), - "argumentParser.error.invalidValue", + CoreTranslations.ArgumentParser.Error.invalidValue, context.getLocale(), - context.command.resolvedBundle ?: converter.bundle, currentArg, argumentsObj, @@ -124,7 +122,6 @@ public open class SlashCommandParser { null, context.getLocale(), - context.command.resolvedBundle ?: converter.bundle, currentArg, argumentsObj, @@ -148,23 +145,20 @@ public open class SlashCommandParser { if (converter.required && !parsed) { throw ArgumentParsingException( - context.translate( - "argumentParser.error.invalidValue", - replacements = arrayOf( - context.translate( - currentArg.displayName, - bundleName = context.command.resolvedBundle ?: converter.bundle - ), + CoreTranslations.ArgumentParser.Error.invalidValue + .withContext(context) + .translate( + currentArg.displayName + .withContext(context) + .translate(), converter.getErrorString(context), currentValue - ) - ), + ), - "argumentParser.error.invalidValue", + CoreTranslations.ArgumentParser.Error.invalidValue, context.getLocale(), - context.command.resolvedBundle ?: converter.bundle, currentArg, argumentsObj, @@ -187,7 +181,6 @@ public open class SlashCommandParser { null, context.getLocale(), - context.command.resolvedBundle ?: converter.bundle, currentArg, argumentsObj, @@ -224,7 +217,6 @@ public open class SlashCommandParser { null, context.getLocale(), - context.command.resolvedBundle ?: converter.bundle, currentArg, argumentsObj, @@ -261,7 +253,6 @@ public open class SlashCommandParser { null, context.getLocale(), - context.command.resolvedBundle ?: converter.bundle, currentArg, argumentsObj, @@ -298,7 +289,6 @@ public open class SlashCommandParser { null, context.getLocale(), - context.command.resolvedBundle ?: converter.bundle, currentArg, argumentsObj, @@ -335,7 +325,6 @@ public open class SlashCommandParser { null, context.getLocale(), - context.command.resolvedBundle ?: converter.bundle, currentArg, argumentsObj, diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/slash/SlashGroup.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/slash/SlashGroup.kt index c5e6caafea..de1ea28a08 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/slash/SlashGroup.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/slash/SlashGroup.kt @@ -68,7 +68,6 @@ public class SlashGroup( if (!descriptionTranslationCache.containsKey(locale)) { descriptionTranslationCache[locale] = description - .withBundle(this.parent.resolvedBundle) .withLocale(locale) .translate() .lowercase(locale) diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/slash/converters/ChoiceConverter.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/slash/converters/ChoiceConverter.kt index 787e1fd685..3a31756de4 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/slash/converters/ChoiceConverter.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/slash/converters/ChoiceConverter.kt @@ -9,6 +9,7 @@ package dev.kordex.core.commands.application.slash.converters import dev.kordex.core.commands.converters.SingleConverter +import dev.kordex.core.i18n.types.Key private const val CHOICE_LIMIT = 25 // Discord doesn't allow more choices than this @@ -19,7 +20,7 @@ private const val CHOICE_LIMIT = 25 // Discord doesn't allow more choices than */ public abstract class ChoiceConverter( - public open val choices: Map, + public open val choices: Map, ) : SingleConverter() { init { if (choices.size > CHOICE_LIMIT) { diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/slash/converters/ChoiceEnum.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/slash/converters/ChoiceEnum.kt index 0da5fa1f77..fddf7b65c6 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/slash/converters/ChoiceEnum.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/slash/converters/ChoiceEnum.kt @@ -9,9 +9,10 @@ package dev.kordex.core.commands.application.slash.converters import dev.kordex.core.commands.application.slash.converters.impl.EnumChoiceConverter +import dev.kordex.core.i18n.types.Key /** Interface representing an enum used in the [EnumChoiceConverter]. **/ public interface ChoiceEnum { /** Human-readable name to show on Discord. **/ - public val readableName: String + public val readableName: Key } diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/slash/converters/impl/EnumChoiceConverter.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/slash/converters/impl/EnumChoiceConverter.kt index 521c78af48..42702891f5 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/slash/converters/impl/EnumChoiceConverter.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/slash/converters/impl/EnumChoiceConverter.kt @@ -20,13 +20,13 @@ import dev.kordex.core.commands.OptionWrapper import dev.kordex.core.commands.application.slash.converters.ChoiceConverter import dev.kordex.core.commands.application.slash.converters.ChoiceEnum import dev.kordex.core.commands.converters.Validator -import dev.kordex.core.commands.wrapOption +import dev.kordex.core.commands.wrapStringOption import dev.kordex.core.i18n.generated.CoreTranslations -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.core.utils.getIgnoringCase import dev.kordex.parser.StringParser import io.github.oshai.kotlinlogging.KotlinLogging +import java.util.Locale /** * Choice converter for enum arguments. Supports mapping up to 25 choices to an enum type. @@ -40,19 +40,17 @@ import io.github.oshai.kotlinlogging.KotlinLogging imports = [ "dev.kordex.core.commands.converters.impl.getEnum", "dev.kordex.core.commands.application.slash.converters.ChoiceEnum", - "dev.kordex.core.i18n.types.Bundle", - "dev.kordex.core.i18n.types.Key", + "java.util.Locale", ], builderGeneric = "E", builderConstructorArguments = [ - "public var getter: suspend (String) -> E?", - "!! argMap: Map", + "public var getter: suspend (String, Locale) -> E?", + "!! argMap: Map", ], builderFields = [ "public lateinit var typeName: Key", - "public var bundle: Bundle? = null", ], builderInitStatements = [ @@ -63,7 +61,7 @@ import io.github.oshai.kotlinlogging.KotlinLogging functionGeneric = "E", functionBuilderArguments = [ - "getter = { getEnum(it) }", + "getter = ::getEnum", "argMap = enumValues().associateBy { it.readableName }", ], @@ -71,10 +69,9 @@ import io.github.oshai.kotlinlogging.KotlinLogging ) public class EnumChoiceConverter( typeName: Key, - private val getter: suspend (String) -> E?, - choices: Map, + private val getter: suspend (String, Locale) -> E?, + choices: Map, override var validator: Validator = null, - override val bundle: Bundle? = null, ) : ChoiceConverter(choices) where E : Enum, E : ChoiceEnum { override val signatureType: Key = typeName @@ -92,7 +89,7 @@ public class EnumChoiceConverter( } try { - val result = getter.invoke(arg) + val result = getter.invoke(arg, context.getLocale()) ?: throw DiscordRelayedException( CoreTranslations.Converters.Choice.invalidChoice .withLocale(context.getLocale()) @@ -119,18 +116,21 @@ public class EnumChoiceConverter( return true } - override suspend fun toSlashOption(arg: Argument<*>): OptionWrapper = - wrapOption(arg.displayName, arg.description) { + override suspend fun toSlashOption(arg: Argument<*>): OptionWrapper { + val option = wrapStringOption(arg.displayName, arg.description) { required = true - - this@EnumChoiceConverter.choices.forEach { choice(it.key, it.value.name) } } + this.choices.forEach { option.choice(it.key, it.value.name) } + + return option + } + override suspend fun parseOption(context: CommandContext, option: OptionValue<*>): Boolean { val stringOption = option as? StringOptionValue ?: return false try { - parsed = getter.invoke(stringOption.value) ?: return false + parsed = getter.invoke(stringOption.value, context.getLocale()) ?: return false } catch (_: IllegalArgumentException) { return false } @@ -140,10 +140,11 @@ public class EnumChoiceConverter( } /** - * The default choice enum value getter - matches choice enums via a case-insensitive string comparison with the names. + * The default choice enum value getter — matches choice enums via a case-insensitive string comparison with the names. */ -public inline fun getEnum(arg: String): E? where E : Enum, E : ChoiceEnum = +public inline fun getEnum(arg: String, locale: Locale): E? where E : Enum, E : ChoiceEnum = enumValues().firstOrNull { - it.readableName.equals(arg, true) || + it.readableName.translateLocale(locale).equals(arg, true) || + it.readableName.key.equals(arg, true) || it.name.equals(arg, true) } diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/slash/converters/impl/NumberChoiceConverter.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/slash/converters/impl/NumberChoiceConverter.kt index 6e5e70bd86..3118db4ea5 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/slash/converters/impl/NumberChoiceConverter.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/slash/converters/impl/NumberChoiceConverter.kt @@ -19,7 +19,7 @@ import dev.kordex.core.commands.CommandContext import dev.kordex.core.commands.OptionWrapper import dev.kordex.core.commands.application.slash.converters.ChoiceConverter import dev.kordex.core.commands.converters.Validator -import dev.kordex.core.commands.wrapOption +import dev.kordex.core.commands.wrapIntegerOption import dev.kordex.core.i18n.generated.CoreTranslations import dev.kordex.core.i18n.types.Key import dev.kordex.core.utils.getIgnoringCase @@ -40,7 +40,7 @@ private const val DEFAULT_RADIX = 10 ) public class NumberChoiceConverter( private val radix: Int = DEFAULT_RADIX, - choices: Map, + choices: Map, override var validator: Validator = null, ) : ChoiceConverter(choices) { override val signatureType: Key = CoreTranslations.Converters.Number.signatureType @@ -87,13 +87,16 @@ public class NumberChoiceConverter( return true } - override suspend fun toSlashOption(arg: Argument<*>): OptionWrapper = - wrapOption(arg.displayName, arg.description) { + override suspend fun toSlashOption(arg: Argument<*>): OptionWrapper { + val option = wrapIntegerOption(arg.displayName, arg.description) { required = true - - this@NumberChoiceConverter.choices.forEach { choice(it.key, it.value) } } + this.choices.forEach { option.choice(it.key, it.value) } + + return option + } + override suspend fun parseOption(context: CommandContext, option: OptionValue<*>): Boolean { val optionValue = (option as? IntegerOptionValue)?.value ?: return false this.parsed = optionValue diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/slash/converters/impl/StringChoiceConverter.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/slash/converters/impl/StringChoiceConverter.kt index 60094c5856..8257d93b8d 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/slash/converters/impl/StringChoiceConverter.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/slash/converters/impl/StringChoiceConverter.kt @@ -19,7 +19,7 @@ import dev.kordex.core.commands.CommandContext import dev.kordex.core.commands.OptionWrapper import dev.kordex.core.commands.application.slash.converters.ChoiceConverter import dev.kordex.core.commands.converters.Validator -import dev.kordex.core.commands.wrapOption +import dev.kordex.core.commands.wrapStringOption import dev.kordex.core.i18n.generated.CoreTranslations import dev.kordex.core.i18n.types.Key import dev.kordex.core.utils.getIgnoringCase @@ -34,7 +34,7 @@ import dev.kordex.parser.StringParser types = [ConverterType.CHOICE, ConverterType.DEFAULTING, ConverterType.OPTIONAL, ConverterType.SINGLE] ) public class StringChoiceConverter( - choices: Map, + choices: Map, override var validator: Validator = null, ) : ChoiceConverter(choices) { override val signatureType: Key = CoreTranslations.Converters.String.signatureType @@ -65,13 +65,16 @@ public class StringChoiceConverter( return true } - override suspend fun toSlashOption(arg: Argument<*>): OptionWrapper = - wrapOption(arg.displayName, arg.description) { + override suspend fun toSlashOption(arg: Argument<*>): OptionWrapper { + val option = wrapStringOption(arg.displayName, arg.description) { required = true - - this@StringChoiceConverter.choices.forEach { choice(it.key, it.value) } } + this.choices.forEach { option.choice(it.key, it.value) } + + return option + } + override suspend fun parseOption(context: CommandContext, option: OptionValue<*>): Boolean { val optionValue = (option as? StringOptionValue)?.value ?: return false this.parsed = optionValue diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/user/EphemeralUserCommand.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/user/EphemeralUserCommand.kt index f72dd9256a..bb6bab2c49 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/user/EphemeralUserCommand.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/user/EphemeralUserCommand.kt @@ -91,10 +91,10 @@ public class EphemeralUserCommand( val locale = event.getLocale() event.interaction.modal( - modalObj.translateTitle(locale, resolvedBundle), + modalObj.translateTitle(locale), modalObj.id ) { - modalObj.applyToBuilder(this, event.getLocale(), resolvedBundle) + modalObj.applyToBuilder(this, event.getLocale()) } modalObj.awaitCompletion { diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/user/PublicUserCommand.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/user/PublicUserCommand.kt index d9e60eb289..92635f93f7 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/user/PublicUserCommand.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/application/user/PublicUserCommand.kt @@ -92,10 +92,10 @@ public class PublicUserCommand( val locale = event.getLocale() event.interaction.modal( - modalObj.translateTitle(locale, resolvedBundle), + modalObj.translateTitle(locale), modalObj.id ) { - modalObj.applyToBuilder(this, event.getLocale(), resolvedBundle) + modalObj.applyToBuilder(this, event.getLocale()) } modalObj.awaitCompletion { diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/chat/ChatCommand.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/chat/ChatCommand.kt index 01c6f53801..096c582d4e 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/chat/ChatCommand.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/chat/ChatCommand.kt @@ -94,23 +94,8 @@ public open class ChatCommand( */ public open var localeFallback: Boolean = false - /** - * Alternative names that can be used to invoke your command. - * - * There's no limit on the number of aliases a command may have, but in the event of an alias matching - * the [name] of a registered command, the command with the [name] takes priority. - */ - @Deprecated( - "Manual translation API access is unsupported. Set [aliasKey] to a [Key] object instead.", - level = DeprecationLevel.WARNING, - ) - public open var aliases: Array = arrayOf() - /** * Translation key referencing a comma-separated list of command aliases. - * - * If this is set, the [aliases] list is ignored. This is also slightly more efficient during the first - * translation pass, as only one key will ever need to be translated. */ public open var aliasKey: Key? = null @@ -144,7 +129,6 @@ public open class ChatCommand( if (!signatureCache.containsKey(locale)) { if (signature != null) { signatureCache[locale] = signature!! - .withBundle(resolvedBundle) .withLocale(locale) .translate() } else { @@ -158,7 +142,7 @@ public open class ChatCommand( /** Return this command's name translated for the given locale, cached as required. **/ public open fun getTranslatedName(locale: Locale): String { if (!nameTranslationCache.containsKey(locale)) { - nameTranslationCache[locale] = name.withBundle(resolvedBundle) + nameTranslationCache[locale] = name .withLocale(locale) .translate() } @@ -169,8 +153,8 @@ public open class ChatCommand( /** Return this command's aliases translated for the given locale, cached as required. **/ public open fun getTranslatedAliases(locale: Locale): Set { if (!aliasTranslationCache.containsKey(locale)) { - val translations = if (aliasKey != null) { - aliasKey!!.withBundle(resolvedBundle) + if (aliasKey != null) { + val translations = aliasKey!! .withLocale(locale) .translate() .lowercase() @@ -178,18 +162,11 @@ public open class ChatCommand( .map { it.trim() } .filter { it != EMPTY_VALUE_STRING } .toSortedSet() + + aliasTranslationCache[locale] = translations } else { - // TODO: Remove this when deprecations happen - this.aliases.map { - translationsProvider.translate( - key = it, - bundleName = resolvedBundle?.name, - locale = locale - ).lowercase() - }.toSortedSet() + aliasTranslationCache[locale] = setOf() } - - aliasTranslationCache[locale] = translations } return aliasTranslationCache[locale]!! diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/chat/ChatCommandContext.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/chat/ChatCommandContext.kt index a69e3e56ec..7f619fae0c 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/chat/ChatCommandContext.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/chat/ChatCommandContext.kt @@ -135,7 +135,7 @@ public open class ChatCommandContext( replacements: Array = arrayOf(), useReply: Boolean = true, ): Message = respond( - key.withBundle(command.resolvedBundle) + key .withLocale(getLocale()) .translateArray(replacements), @@ -150,7 +150,7 @@ public open class ChatCommandContext( replacements: Map, useReply: Boolean = true, ): Message = respond( - key.withBundle(command.resolvedBundle) + key .withLocale(getLocale()) .translateNamed(replacements), diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/chat/ChatCommandParser.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/chat/ChatCommandParser.kt index 67907acd35..99ffffb46e 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/chat/ChatCommandParser.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/chat/ChatCommandParser.kt @@ -24,7 +24,6 @@ import dev.kordex.core.commands.converters.* import dev.kordex.core.commands.converters.types.MultiNamedInputConverter import dev.kordex.core.commands.converters.types.SingleNamedInputConverter import dev.kordex.core.commands.getDefaultTranslatedDisplayName -import dev.kordex.core.i18n.KORDEX_BUNDLE import dev.kordex.core.i18n.TranslationsProvider import dev.kordex.core.i18n.generated.CoreTranslations import dev.kordex.core.koin.KordExKoinComponent @@ -74,7 +73,6 @@ public open class ChatCommandParser : KordExKoinComponent { translationKey = CoreTranslations.ArgumentParser.Error.requiresOneValue, locale = context.getLocale(), - bundle = KORDEX_BUNDLE, argument = argument, arguments = arguments, @@ -107,7 +105,6 @@ public open class ChatCommandParser : KordExKoinComponent { CoreTranslations.ArgumentParser.Error.errorInArgument, context.getLocale(), - KORDEX_BUNDLE, argument, arguments, @@ -170,7 +167,6 @@ public open class ChatCommandParser : KordExKoinComponent { CoreTranslations.ArgumentParser.Error.errorInArgument, context.getLocale(), - KORDEX_BUNDLE, argument, arguments, @@ -200,7 +196,6 @@ public open class ChatCommandParser : KordExKoinComponent { CoreTranslations.ArgumentParser.Error.errorInArgument, context.getLocale(), - KORDEX_BUNDLE, argument, arguments, @@ -228,7 +223,6 @@ public open class ChatCommandParser : KordExKoinComponent { .withLocale(context.getLocale()) .translate( argument.displayName - .withBundle(context.command.resolvedBundle ?: c.bundle, false) .withLocale(context.getLocale()) .translate(), @@ -238,7 +232,6 @@ public open class ChatCommandParser : KordExKoinComponent { CoreTranslations.ArgumentParser.Error.invalidValue, context.getLocale(), - argument.displayName.bundle ?: context.command.resolvedBundle ?: c.bundle, argument, arguments, @@ -261,14 +254,12 @@ public open class ChatCommandParser : KordExKoinComponent { .withLocale(context.getLocale()) .translate( argument.displayName - .withBundle(context.command.resolvedBundle ?: c.bundle, false) .withLocale(context.getLocale()) .translate(), numArgs, numParsed, c.signatureType - .withBundle(c.bundle) .withLocale(context.getLocale()) .translate() ), @@ -276,7 +267,6 @@ public open class ChatCommandParser : KordExKoinComponent { CoreTranslations.ArgumentParser.Error.notAllValid, context.getLocale(), - argument.displayName.bundle ?: context.command.resolvedBundle ?: c.bundle, argument, arguments, @@ -441,7 +431,6 @@ public open class ChatCommandParser : KordExKoinComponent { CoreTranslations.ArgumentParser.Error.errorInArgument, context.getLocale(), - KORDEX_BUNDLE, argument, arguments, @@ -509,7 +498,7 @@ public open class ChatCommandParser : KordExKoinComponent { val kwValue = keywordArgs[ currentArg - .getDefaultTranslatedDisplayName(context.translationsProvider, context.command) + .getDefaultTranslatedDisplayName() .lowercase(context.getLocale()) ] @@ -546,7 +535,6 @@ public open class ChatCommandParser : KordExKoinComponent { CoreTranslations.ArgumentParser.Error.noFilledArguments, context.getLocale(), - KORDEX_BUNDLE, null, argumentsObj, @@ -561,7 +549,6 @@ public open class ChatCommandParser : KordExKoinComponent { CoreTranslations.ArgumentParser.Error.someFilledArguments, context.getLocale(), - context.command.resolvedBundle, null, argumentsObj, @@ -602,7 +589,6 @@ public open class ChatCommandParser : KordExKoinComponent { append( it.converter.signatureType - .withBundle(it.converter.bundle) .withLocale(locale) .translate() ) diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/chat/ChatCommandRegistry.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/chat/ChatCommandRegistry.kt index 893a4d22ed..f68675c5e1 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/chat/ChatCommandRegistry.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/chat/ChatCommandRegistry.kt @@ -15,6 +15,7 @@ import dev.kordex.core.ExtensibleBot import dev.kordex.core.builders.ExtensibleBotBuilder import dev.kordex.core.commands.Arguments import dev.kordex.core.extensions.Extension +import dev.kordex.core.i18n.SupportedLocales import dev.kordex.core.koin.KordExKoinComponent import dev.kordex.core.utils.getLocale import dev.kordex.parser.StringParser @@ -62,30 +63,38 @@ public open class ChatCommandRegistry : KordExKoinComponent { @Throws(CommandRegistrationException::class) public open fun add(command: ChatCommand) { val existingCommand = commands.any { it.name == command.name } - val existingAlias: String? = commands.flatMap { - it.aliases.toList() - }.firstOrNull { command.aliases.contains(it) } if (existingCommand) { throw CommandRegistrationException( command.name, - "Chat command with this name already registered in '${command.extension.name}' extension." + "Chat command registered using duplicate name: ${command.name}" ) } - if (existingAlias != null) { + if (commands.contains(command)) { throw CommandRegistrationException( command.name, - "Chat command with alias '$existingAlias' already registered in '${command.extension.name}' " + - "extension." + "Chat command registered twice: ${command.name}" ) } - if (commands.contains(command)) { - throw CommandRegistrationException( - command.name, - "Chat command already registered in '${command.extension.name}' extension." - ) + val commandAliases = SupportedLocales.ALL_LOCALES_SET.flatMap { + command.getTranslatedAliases(it) + } + + val existingAliases = commands.flatMap { c -> + SupportedLocales.ALL_LOCALES_SET.flatMap { + c.getTranslatedAliases(it) + } + } + + val matchingAliases = commandAliases.intersect(existingAliases) + + if (matchingAliases.isNotEmpty()) { + logger.warn { + "Chat command named using ${command.name} provides aliases used by other " + + "commands: ${matchingAliases.joinToString()}" + } } commands.add(command) diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/chat/ChatSubCommand.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/chat/ChatSubCommand.kt index 0eaea314c4..48e98e11f7 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/chat/ChatSubCommand.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/chat/ChatSubCommand.kt @@ -45,7 +45,6 @@ public open class ChatSubCommand( override fun getTranslatedName(locale: Locale): String { if (!nameTranslationCache.containsKey(locale)) { nameTranslationCache[locale] = name - .withBundle(resolvedBundle) .withLocale(locale) .translate() .lowercase(locale) diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/Converter.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/Converter.kt index 7efecdaece..bfe427abd2 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/Converter.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/Converter.kt @@ -16,7 +16,6 @@ import dev.kordex.core.commands.Arguments import dev.kordex.core.commands.CommandContext import dev.kordex.core.commands.converters.builders.ConverterBuilder import dev.kordex.core.commands.converters.builders.ValidationContext -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.core.koin.KordExKoinComponent import dev.kordex.core.utils.withContext @@ -75,13 +74,6 @@ public abstract class Converter { /** List of possible choices, if any. **/ - public var choices: MutableStringKeyedMap + public var choices: MutableMap /** Add a choice to the list of possible choices. **/ - public fun choice(key: String, value: T) { + public fun choice(key: Key, value: T) { choices[key] = value } /** Add a choice to the list of possible choices. **/ - public fun choices(all: Map) { + public fun choices(all: Map) { choices = all.toMutableMap() } } diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/builders/ValidationContext.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/builders/ValidationContext.kt index a6089e5fee..be04cbb51b 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/builders/ValidationContext.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/builders/ValidationContext.kt @@ -9,13 +9,11 @@ package dev.kordex.core.commands.converters.builders import dev.kordex.core.DiscordRelayedException +import dev.kordex.core.annotations.NotTranslated import dev.kordex.core.commands.CommandContext import dev.kordex.core.i18n.generated.CoreTranslations -import dev.kordex.core.i18n.generated.CoreTranslations.bundle -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.core.koin.KordExKoinComponent -import java.util.* /** * Class representing the context for an argument validator. This allows the storage of validation steps and a message @@ -36,9 +34,6 @@ public class ValidationContext(public val value: T, public val context: C */ public var errorResponseKey: Key = CoreTranslations.Checks.responseTemplate - /** Translation bundle used by [translate] by default and the error response translation, if not the default. **/ - public var defaultBundle: Bundle? = context.command.resolvedBundle - /** Human-readable message for the user, if any. **/ public var message: String? = null @@ -51,17 +46,28 @@ public class ValidationContext(public val value: T, public val context: C } /** Mark this validator as having failed, optionally providing a message for the user. **/ - public fun fail(message: String? = null) { + @NotTranslated + public fun fail(message: String) { this.message = message this.passed = false } + /** Mark this validator as having failed, optionally providing a message for the user. **/ + public suspend fun fail(message: Key? = null) { + this.message = message + ?.withLocale(context.getLocale()) + ?.translate() + + this.passed = false + } + /** * If [value] is `true`, mark this validator as having failed, optionally providing a message for the user. * * Returns `true` if the validator was marked as having failed, `false` otherwise. */ - public fun failIf(value: Boolean, message: String? = null): Boolean { + @NotTranslated + public fun failIf(value: Boolean, message: String): Boolean { if (value) { fail(message) @@ -71,12 +77,36 @@ public class ValidationContext(public val value: T, public val context: C return false } + /** + * If [value] is `true`, mark this validator as having failed, optionally providing a message for the user. + * + * Returns `true` if the validator was marked as having failed, `false` otherwise. + */ + public suspend fun failIf(value: Boolean, message: Key? = null): Boolean { + if (value) { + fail(message) + + return true + } + + return false + } + + /** + * If [callback] returns `true`, mark this validator as having failed, optionally providing a message for the user. + * + * Returns `true` if the validator was marked as having failed, `false` otherwise. + */ + @NotTranslated + public suspend fun failIf(message: String, callback: suspend () -> Boolean): Boolean = + failIf(callback(), message) + /** * If [callback] returns `true`, mark this validator as having failed, optionally providing a message for the user. * * Returns `true` if the validator was marked as having failed, `false` otherwise. */ - public suspend fun failIf(message: String? = null, callback: suspend () -> Boolean): Boolean = + public suspend fun failIf(message: Key? = null, callback: suspend () -> Boolean): Boolean = failIf(callback(), message) /** @@ -84,7 +114,16 @@ public class ValidationContext(public val value: T, public val context: C * * Returns `true` if the validator was marked as having failed, `false` otherwise. */ - public fun failIfNot(value: Boolean, message: String? = null): Boolean = + @NotTranslated + public fun failIfNot(value: Boolean, message: String): Boolean = + failIf(!value, message) + + /** + * If [value] is `false`, mark this validator as having failed, optionally providing a message for the user. + * + * Returns `true` if the validator was marked as having failed, `false` otherwise. + */ + public suspend fun failIfNot(value: Boolean, message: Key? = null): Boolean = failIf(!value, message) /** @@ -92,7 +131,16 @@ public class ValidationContext(public val value: T, public val context: C * * Returns `true` if the validator was marked as having failed, `false` otherwise. */ - public suspend fun failIfNot(message: String? = null, callback: suspend () -> Boolean): Boolean = + @NotTranslated + public suspend fun failIfNot(message: String, callback: suspend () -> Boolean): Boolean = + failIfNot(callback(), message) + + /** + * If [callback] returns `false`, mark this validator as having failed, optionally providing a message for the user. + * + * Returns `true` if the validator was marked as having failed, `false` otherwise. + */ + public suspend fun failIfNot(message: Key? = null, callback: suspend () -> Boolean): Boolean = failIfNot(callback(), message) /** @@ -152,15 +200,6 @@ public class ValidationContext(public val value: T, public val context: C return null } - /** Quick access to translate strings using this validator context's locale. **/ - public fun translate( - key: Key, - bundle: Bundle? = defaultBundle, - replacements: Array = arrayOf(), - ): String = - key.withBundle(bundle) - .translateArray(replacements) - /** Quick access to translate strings using this validator context's locale. **/ public fun translate( key: Key, @@ -175,15 +214,6 @@ public class ValidationContext(public val value: T, public val context: C ): String = key.translateNamed(replacements) - /** Quick access to translate strings using this validator context's locale. **/ - public fun translate( - key: Key, - bundle: Bundle?, - replacements: Map, - ): String = - key.withBundle(bundle) - .translateNamed(replacements) - /** * If this validator has failed, throw a [DiscordRelayedException] with the translated message, if any. */ @@ -203,8 +233,8 @@ public class ValidationContext(public val value: T, public val context: C /** Get the translated validator failure message, if the validator has failed and a message was set. **/ public suspend fun getTranslatedMessage(): String? = if (passed.not() && message != null) { - errorResponseKey.withLocale(context.getLocale()) - .withBundle(defaultBundle) + errorResponseKey + .withLocale(context.getLocale()) .translate(message) } else { null diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/AttachmentConverter.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/AttachmentConverter.kt index bb8ff695e9..26055bdf0b 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/AttachmentConverter.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/AttachmentConverter.kt @@ -21,9 +21,7 @@ import dev.kordex.core.commands.OptionWrapper import dev.kordex.core.commands.converters.SingleConverter import dev.kordex.core.commands.converters.Validator import dev.kordex.core.commands.wrapOption -import dev.kordex.core.i18n.KORDEX_BUNDLE import dev.kordex.core.i18n.generated.CoreTranslations -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.parser.StringParser @@ -41,7 +39,6 @@ public class AttachmentConverter( override var validator: Validator = null, ) : SingleConverter() { override val signatureType: Key = CoreTranslations.Converters.Attachment.signatureType - override val bundle: Bundle = KORDEX_BUNDLE override suspend fun parse(parser: StringParser?, context: CommandContext, named: String?): Boolean = throw DiscordRelayedException( diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/BooleanConverter.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/BooleanConverter.kt index db7fc5b62e..8e472de950 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/BooleanConverter.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/BooleanConverter.kt @@ -19,9 +19,7 @@ import dev.kordex.core.commands.OptionWrapper import dev.kordex.core.commands.converters.SingleConverter import dev.kordex.core.commands.converters.Validator import dev.kordex.core.commands.wrapOption -import dev.kordex.core.i18n.KORDEX_BUNDLE import dev.kordex.core.i18n.generated.CoreTranslations -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.core.utils.parseBoolean import dev.kordex.parser.StringParser @@ -41,7 +39,6 @@ public class BooleanConverter( ) : SingleConverter() { public override val signatureType: Key = CoreTranslations.Converters.Boolean.signatureType public override val errorType: Key = CoreTranslations.Converters.Boolean.errorType - override val bundle: Bundle = KORDEX_BUNDLE override suspend fun parse(parser: StringParser?, context: CommandContext, named: String?): Boolean { val arg: String = named ?: parser?.parseNext()?.data ?: return false diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/ChannelConverter.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/ChannelConverter.kt index 5580409dd9..106e0f99b3 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/ChannelConverter.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/ChannelConverter.kt @@ -29,9 +29,7 @@ import dev.kordex.core.commands.OptionWrapper import dev.kordex.core.commands.converters.SingleConverter import dev.kordex.core.commands.converters.Validator import dev.kordex.core.commands.wrapOption -import dev.kordex.core.i18n.KORDEX_BUNDLE import dev.kordex.core.i18n.generated.CoreTranslations -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.core.utils.translate import dev.kordex.parser.StringParser @@ -80,7 +78,6 @@ public class ChannelConverter( override var validator: Validator = null, ) : SingleConverter() { override val signatureType: Key = CoreTranslations.Converters.Channel.signatureType - override val bundle: Bundle = KORDEX_BUNDLE override suspend fun parse(parser: StringParser?, context: CommandContext, named: String?): Boolean { val arg: String = named ?: parser?.parseNext()?.data ?: return false diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/ColorConverter.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/ColorConverter.kt index 3b1342b10b..a637c09743 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/ColorConverter.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/ColorConverter.kt @@ -23,9 +23,7 @@ import dev.kordex.core.commands.OptionWrapper import dev.kordex.core.commands.converters.SingleConverter import dev.kordex.core.commands.converters.Validator import dev.kordex.core.commands.wrapOption -import dev.kordex.core.i18n.KORDEX_BUNDLE import dev.kordex.core.i18n.generated.CoreTranslations -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.core.parsers.ColorParser import dev.kordex.parser.StringParser @@ -44,7 +42,6 @@ public class ColorConverter( override var validator: Validator = null, ) : SingleConverter() { override val signatureType: Key = CoreTranslations.Converters.Color.signatureType - override val bundle: Bundle = KORDEX_BUNDLE override suspend fun parse(parser: StringParser?, context: CommandContext, named: String?): Boolean { val arg: String = named ?: parser?.parseNext()?.data ?: return false diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/DecimalConverter.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/DecimalConverter.kt index 11015dff8c..a05388cf89 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/DecimalConverter.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/DecimalConverter.kt @@ -20,9 +20,7 @@ import dev.kordex.core.commands.OptionWrapper import dev.kordex.core.commands.converters.SingleConverter import dev.kordex.core.commands.converters.Validator import dev.kordex.core.commands.wrapOption -import dev.kordex.core.i18n.KORDEX_BUNDLE import dev.kordex.core.i18n.generated.CoreTranslations -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.parser.StringParser @@ -52,7 +50,6 @@ public class DecimalConverter( override var validator: Validator = null, ) : SingleConverter() { override val signatureType: Key = CoreTranslations.Converters.Decimal.signatureType - override val bundle: Bundle = KORDEX_BUNDLE override suspend fun parse(parser: StringParser?, context: CommandContext, named: String?): Boolean { val arg: String = named ?: parser?.parseNext()?.data ?: return false diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/DurationCoalescingConverter.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/DurationCoalescingConverter.kt index 8379283c7e..7a24b8e7fa 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/DurationCoalescingConverter.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/DurationCoalescingConverter.kt @@ -21,9 +21,7 @@ import dev.kordex.core.commands.converters.CoalescingConverter import dev.kordex.core.commands.converters.Validator import dev.kordex.core.commands.wrapOption import dev.kordex.core.i18n.EMPTY_VALUE_STRING -import dev.kordex.core.i18n.KORDEX_BUNDLE import dev.kordex.core.i18n.generated.CoreTranslations -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.core.parsers.DurationParser import dev.kordex.core.parsers.DurationParserException @@ -58,7 +56,6 @@ public class DurationCoalescingConverter( ) : CoalescingConverter(shouldThrow) { // TODO: The signature type is not an error! override val signatureType: Key = CoreTranslations.Converters.Duration.Error.signatureType - override val bundle: Bundle = KORDEX_BUNDLE private val logger = KotlinLogging.logger {} diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/DurationConverter.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/DurationConverter.kt index a2c152ca14..3179541c4e 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/DurationConverter.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/DurationConverter.kt @@ -20,9 +20,7 @@ import dev.kordex.core.commands.OptionWrapper import dev.kordex.core.commands.converters.SingleConverter import dev.kordex.core.commands.converters.Validator import dev.kordex.core.commands.wrapOption -import dev.kordex.core.i18n.KORDEX_BUNDLE import dev.kordex.core.i18n.generated.CoreTranslations -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.core.parsers.DurationParser import dev.kordex.core.parsers.DurationParserException @@ -55,7 +53,6 @@ public class DurationConverter( ) : SingleConverter() { // TODO: The signature type is not an error! override val signatureType: Key = CoreTranslations.Converters.Duration.Error.signatureType - override val bundle: Bundle = KORDEX_BUNDLE override suspend fun parse(parser: StringParser?, context: CommandContext, named: String?): Boolean { val arg: String = named ?: parser?.parseNext()?.data ?: return false diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/EmailConverter.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/EmailConverter.kt index 60d147f9ae..9b92252594 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/EmailConverter.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/EmailConverter.kt @@ -20,9 +20,7 @@ import dev.kordex.core.commands.OptionWrapper import dev.kordex.core.commands.converters.SingleConverter import dev.kordex.core.commands.converters.Validator import dev.kordex.core.commands.wrapOption -import dev.kordex.core.i18n.KORDEX_BUNDLE import dev.kordex.core.i18n.generated.CoreTranslations -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.parser.StringParser import org.apache.commons.validator.routines.EmailValidator @@ -39,7 +37,6 @@ public class EmailConverter( override var validator: Validator = null, ) : SingleConverter() { override val signatureType: Key = CoreTranslations.Converters.Email.signatureType - override val bundle: Bundle = KORDEX_BUNDLE override suspend fun parse(parser: StringParser?, context: CommandContext, named: String?): Boolean { val arg: String = named ?: parser?.parseNext()?.data ?: return false diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/EmojiConverter.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/EmojiConverter.kt index 1d89a974b5..d61490d3e9 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/EmojiConverter.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/EmojiConverter.kt @@ -23,9 +23,7 @@ import dev.kordex.core.commands.OptionWrapper import dev.kordex.core.commands.converters.SingleConverter import dev.kordex.core.commands.converters.Validator import dev.kordex.core.commands.wrapOption -import dev.kordex.core.i18n.KORDEX_BUNDLE import dev.kordex.core.i18n.generated.CoreTranslations -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.parser.StringParser import kotlinx.coroutines.flow.firstOrNull @@ -57,7 +55,6 @@ public class EmojiConverter( override var validator: Validator = null, ) : SingleConverter() { override val signatureType: Key = CoreTranslations.Converters.Emoji.signatureType - override val bundle: Bundle = KORDEX_BUNDLE override suspend fun parse(parser: StringParser?, context: CommandContext, named: String?): Boolean { val arg: String = named ?: parser?.parseNext()?.data ?: return false diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/EnumConverter.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/EnumConverter.kt index 73dedd94e8..1c72e8ed74 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/EnumConverter.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/EnumConverter.kt @@ -16,13 +16,13 @@ import dev.kordex.core.annotations.converters.ConverterType import dev.kordex.core.commands.Argument import dev.kordex.core.commands.CommandContext import dev.kordex.core.commands.OptionWrapper +import dev.kordex.core.commands.application.slash.converters.ChoiceEnum import dev.kordex.core.commands.converters.SingleConverter import dev.kordex.core.commands.converters.Validator import dev.kordex.core.commands.wrapOption -import dev.kordex.core.i18n.KORDEX_BUNDLE -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.parser.StringParser +import java.util.Locale /** * Argument converter for arbitrary enum arguments. @@ -40,27 +40,31 @@ import dev.kordex.parser.StringParser types = [ConverterType.SINGLE, ConverterType.DEFAULTING, ConverterType.OPTIONAL, ConverterType.LIST], imports = [ "dev.kordex.core.commands.converters.impl.getEnum", + "dev.kordex.core.commands.application.slash.converters.ChoiceEnum", + "java.util.Locale", ], - builderGeneric = "E: Enum", + builderGeneric = "E", builderConstructorArguments = [ - "public var getter: suspend (String) -> E?" + "public var getter: suspend (String, Locale) -> E?", ], builderFields = [ "public lateinit var typeName: Key", - "public var bundle: Bundle? = null" ], - functionGeneric = "E: Enum", + builderSuffixedWhere = "E : Enum, E : ChoiceEnum", + + functionGeneric = "E", functionBuilderArguments = [ - "getter = { getEnum(it) }", - ] + "getter = ::getEnum", + ], + + functionSuffixedWhere = "E : Enum, E : ChoiceEnum", ) public class EnumConverter>( typeName: Key, - private val getter: suspend (String) -> E?, - override val bundle: Bundle? = KORDEX_BUNDLE, + private val getter: suspend (String, Locale) -> E?, override var validator: Validator = null, ) : SingleConverter() { override val signatureType: Key = typeName @@ -69,7 +73,7 @@ public class EnumConverter>( val arg: String = named ?: parser?.parseNext()?.data ?: return false try { - parsed = getter.invoke(arg) ?: return false + parsed = getter.invoke(arg, context.getLocale()) ?: return false } catch (_: IllegalArgumentException) { return false } @@ -86,7 +90,7 @@ public class EnumConverter>( val optionValue = (option as? StringOptionValue)?.value ?: return false try { - parsed = getter.invoke(optionValue) ?: return false + parsed = getter.invoke(optionValue, context.getLocale()) ?: return false } catch (_: IllegalArgumentException) { return false } @@ -96,9 +100,11 @@ public class EnumConverter>( } /** - * The default enum value getter - matches enums based on a case-insensitive string comparison with the name. + * The default choice enum value getter — matches choice enums via a case-insensitive string comparison with the names. */ -public inline fun > getEnum(arg: String): E? = +public inline fun getEnum(arg: String, locale: Locale): E? where E : Enum, E : ChoiceEnum = enumValues().firstOrNull { - it.name.equals(arg, true) + it.readableName.translateLocale(locale).equals(arg, true) || + it.readableName.key.equals(arg, true) || + it.name.equals(arg, true) } diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/GuildConverter.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/GuildConverter.kt index 0ea0c04259..a6e15fb1b0 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/GuildConverter.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/GuildConverter.kt @@ -22,9 +22,7 @@ import dev.kordex.core.commands.OptionWrapper import dev.kordex.core.commands.converters.SingleConverter import dev.kordex.core.commands.converters.Validator import dev.kordex.core.commands.wrapOption -import dev.kordex.core.i18n.KORDEX_BUNDLE import dev.kordex.core.i18n.generated.CoreTranslations -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.parser.StringParser import kotlinx.coroutines.flow.firstOrNull @@ -49,7 +47,6 @@ public class GuildConverter( override var validator: Validator = null, ) : SingleConverter() { override val signatureType: Key = CoreTranslations.Converters.Guild.signatureType - override val bundle: Bundle = KORDEX_BUNDLE override suspend fun parse(parser: StringParser?, context: CommandContext, named: String?): Boolean { val arg: String = named ?: parser?.parseNext()?.data ?: return false diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/IntConverter.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/IntConverter.kt index 9e925ed13e..13323e076d 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/IntConverter.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/IntConverter.kt @@ -20,9 +20,7 @@ import dev.kordex.core.commands.OptionWrapper import dev.kordex.core.commands.converters.SingleConverter import dev.kordex.core.commands.converters.Validator import dev.kordex.core.commands.wrapOption -import dev.kordex.core.i18n.KORDEX_BUNDLE import dev.kordex.core.i18n.generated.CoreTranslations -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.parser.StringParser @@ -54,7 +52,6 @@ public class IntConverter( override var validator: Validator = null, ) : SingleConverter() { override val signatureType: Key = CoreTranslations.Converters.Number.signatureType - override val bundle: Bundle = KORDEX_BUNDLE override suspend fun parse(parser: StringParser?, context: CommandContext, named: String?): Boolean { val arg: String = named ?: parser?.parseNext()?.data ?: return false diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/LongConverter.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/LongConverter.kt index 791e0a7339..3aaa353491 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/LongConverter.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/LongConverter.kt @@ -20,9 +20,7 @@ import dev.kordex.core.commands.OptionWrapper import dev.kordex.core.commands.converters.SingleConverter import dev.kordex.core.commands.converters.Validator import dev.kordex.core.commands.wrapOption -import dev.kordex.core.i18n.KORDEX_BUNDLE import dev.kordex.core.i18n.generated.CoreTranslations -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.parser.StringParser @@ -54,7 +52,6 @@ public class LongConverter( override var validator: Validator = null, ) : SingleConverter() { override val signatureType: Key = CoreTranslations.Converters.Number.signatureType - override val bundle: Bundle = KORDEX_BUNDLE override suspend fun parse(parser: StringParser?, context: CommandContext, named: String?): Boolean { val arg: String = named ?: parser?.parseNext()?.data ?: return false diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/MemberConverter.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/MemberConverter.kt index c4ee4c24e5..208cdc0d75 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/MemberConverter.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/MemberConverter.kt @@ -27,10 +27,7 @@ import dev.kordex.core.commands.chat.ChatCommandContext import dev.kordex.core.commands.converters.SingleConverter import dev.kordex.core.commands.converters.Validator import dev.kordex.core.commands.wrapOption -import dev.kordex.core.i18n.KORDEX_BUNDLE import dev.kordex.core.i18n.generated.CoreTranslations -import dev.kordex.core.i18n.generated.CoreTranslations.Extensions.Sentry.Arguments.id -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.core.utils.users import dev.kordex.parser.StringParser @@ -68,7 +65,6 @@ public class MemberConverter( override var validator: Validator = null, ) : SingleConverter() { override val signatureType: Key = CoreTranslations.Converters.Member.signatureType - override val bundle: Bundle = KORDEX_BUNDLE override suspend fun parse(parser: StringParser?, context: CommandContext, named: String?): Boolean { val guild = context.getGuild() diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/MessageConverter.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/MessageConverter.kt index a36be3d8cf..2a43e7d6ef 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/MessageConverter.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/MessageConverter.kt @@ -29,9 +29,7 @@ import dev.kordex.core.commands.chat.ChatCommandContext import dev.kordex.core.commands.converters.SingleConverter import dev.kordex.core.commands.converters.Validator import dev.kordex.core.commands.wrapOption -import dev.kordex.core.i18n.KORDEX_BUNDLE import dev.kordex.core.i18n.generated.CoreTranslations -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.parser.StringParser import io.github.oshai.kotlinlogging.KotlinLogging @@ -69,7 +67,6 @@ public class MessageConverter( override var validator: Validator = null, ) : SingleConverter() { override val signatureType: Key = CoreTranslations.Converters.Message.signatureType - override val bundle: Bundle = KORDEX_BUNDLE override suspend fun parse(parser: StringParser?, context: CommandContext, named: String?): Boolean { if (useReply && context is ChatCommandContext<*>) { diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/RegexCoalescingConverter.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/RegexCoalescingConverter.kt index bdebeafbec..a6bb2b9124 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/RegexCoalescingConverter.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/RegexCoalescingConverter.kt @@ -19,9 +19,7 @@ import dev.kordex.core.commands.OptionWrapper import dev.kordex.core.commands.converters.CoalescingConverter import dev.kordex.core.commands.converters.Validator import dev.kordex.core.commands.wrapOption -import dev.kordex.core.i18n.KORDEX_BUNDLE import dev.kordex.core.i18n.generated.CoreTranslations -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.parser.StringParser @@ -51,7 +49,6 @@ public class RegexCoalescingConverter( ) : CoalescingConverter(shouldThrow) { override val signatureType: Key = CoreTranslations.Converters.Regex.SignatureType.plural override val showTypeInSignature: Boolean = false - override val bundle: Bundle = KORDEX_BUNDLE override suspend fun parse(parser: StringParser?, context: CommandContext, named: List?): Int { val args: String = named?.joinToString(" ") ?: parser?.consumeRemaining() ?: return 0 diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/RegexConverter.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/RegexConverter.kt index f22786273d..c282ec632f 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/RegexConverter.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/RegexConverter.kt @@ -19,9 +19,7 @@ import dev.kordex.core.commands.OptionWrapper import dev.kordex.core.commands.converters.SingleConverter import dev.kordex.core.commands.converters.Validator import dev.kordex.core.commands.wrapOption -import dev.kordex.core.i18n.KORDEX_BUNDLE import dev.kordex.core.i18n.generated.CoreTranslations -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.parser.StringParser @@ -47,7 +45,6 @@ public class RegexConverter( override var validator: Validator = null, ) : SingleConverter() { override val signatureType: Key = CoreTranslations.Converters.Regex.SignatureType.singular - override val bundle: Bundle = KORDEX_BUNDLE override suspend fun parse(parser: StringParser?, context: CommandContext, named: String?): Boolean { val arg: String = named ?: parser?.parseNext()?.data ?: return false diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/RoleConverter.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/RoleConverter.kt index f937310527..81b6551877 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/RoleConverter.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/RoleConverter.kt @@ -24,9 +24,7 @@ import dev.kordex.core.commands.OptionWrapper import dev.kordex.core.commands.converters.SingleConverter import dev.kordex.core.commands.converters.Validator import dev.kordex.core.commands.wrapOption -import dev.kordex.core.i18n.KORDEX_BUNDLE import dev.kordex.core.i18n.generated.CoreTranslations -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.parser.StringParser import kotlinx.coroutines.flow.firstOrNull @@ -54,7 +52,6 @@ public class RoleConverter( override var validator: Validator = null, ) : SingleConverter() { override val signatureType: Key = CoreTranslations.Converters.Role.signatureType - override val bundle: Bundle = KORDEX_BUNDLE override suspend fun parse(parser: StringParser?, context: CommandContext, named: String?): Boolean { val arg: String = named ?: parser?.parseNext()?.data ?: return false diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/SnowflakeConverter.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/SnowflakeConverter.kt index ac953718e0..ea101267ef 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/SnowflakeConverter.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/SnowflakeConverter.kt @@ -21,9 +21,7 @@ import dev.kordex.core.commands.OptionWrapper import dev.kordex.core.commands.converters.SingleConverter import dev.kordex.core.commands.converters.Validator import dev.kordex.core.commands.wrapOption -import dev.kordex.core.i18n.KORDEX_BUNDLE import dev.kordex.core.i18n.generated.CoreTranslations -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.parser.StringParser @@ -44,7 +42,6 @@ public class SnowflakeConverter( override var validator: Validator = null, ) : SingleConverter() { override val signatureType: Key = CoreTranslations.Converters.Snowflake.signatureType - override val bundle: Bundle = KORDEX_BUNDLE override suspend fun parse(parser: StringParser?, context: CommandContext, named: String?): Boolean { val arg: String = named ?: parser?.parseNext()?.data ?: return false diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/StringCoalescingConverter.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/StringCoalescingConverter.kt index 9cb7557d25..04fb37da3f 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/StringCoalescingConverter.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/StringCoalescingConverter.kt @@ -19,9 +19,7 @@ import dev.kordex.core.commands.OptionWrapper import dev.kordex.core.commands.converters.CoalescingConverter import dev.kordex.core.commands.converters.Validator import dev.kordex.core.commands.wrapOption -import dev.kordex.core.i18n.KORDEX_BUNDLE import dev.kordex.core.i18n.generated.CoreTranslations -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.parser.StringParser @@ -43,7 +41,6 @@ public class StringCoalescingConverter( ) : CoalescingConverter(shouldThrow) { override val signatureType: Key = CoreTranslations.Converters.String.signatureType override val showTypeInSignature: Boolean = false - override val bundle: Bundle = KORDEX_BUNDLE override suspend fun parse(parser: StringParser?, context: CommandContext, named: List?): Int { this.parsed = named?.joinToString(" ") ?: parser?.consumeRemaining() ?: return 0 diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/StringConverter.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/StringConverter.kt index baf019bfc7..a21172749c 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/StringConverter.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/StringConverter.kt @@ -20,9 +20,7 @@ import dev.kordex.core.commands.OptionWrapper import dev.kordex.core.commands.converters.SingleConverter import dev.kordex.core.commands.converters.Validator import dev.kordex.core.commands.wrapOption -import dev.kordex.core.i18n.KORDEX_BUNDLE import dev.kordex.core.i18n.generated.CoreTranslations -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.parser.StringParser @@ -51,7 +49,6 @@ public class StringConverter( ) : SingleConverter() { override val signatureType: Key = CoreTranslations.Converters.String.signatureType override val showTypeInSignature: Boolean = false - override val bundle: Bundle = KORDEX_BUNDLE override suspend fun parse(parser: StringParser?, context: CommandContext, named: String?): Boolean { val arg: String = named ?: parser?.parseNext()?.data ?: return false diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/SupportedLocaleConverter.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/SupportedLocaleConverter.kt index 9175d2950f..bf440f5774 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/SupportedLocaleConverter.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/SupportedLocaleConverter.kt @@ -22,10 +22,8 @@ import dev.kordex.core.commands.OptionWrapper import dev.kordex.core.commands.converters.SingleConverter import dev.kordex.core.commands.converters.Validator import dev.kordex.core.commands.wrapOption -import dev.kordex.core.i18n.KORDEX_BUNDLE import dev.kordex.core.i18n.SupportedLocales import dev.kordex.core.i18n.generated.CoreTranslations -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.parser.StringParser import java.util.* @@ -48,7 +46,6 @@ public class SupportedLocaleConverter( override var validator: Validator = null, ) : SingleConverter() { override val signatureType: Key = CoreTranslations.Converters.SupportedLocale.signatureType - override val bundle: Bundle = KORDEX_BUNDLE override suspend fun parse(parser: StringParser?, context: CommandContext, named: String?): Boolean { val arg: String = named ?: parser?.parseNext()?.data ?: return false diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/TagConverter.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/TagConverter.kt index 16e5ec1220..ad75a6f756 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/TagConverter.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/TagConverter.kt @@ -26,11 +26,8 @@ import dev.kordex.core.commands.OptionWrapper import dev.kordex.core.commands.converters.SingleConverter import dev.kordex.core.commands.converters.Validator import dev.kordex.core.commands.wrapOption -import dev.kordex.core.i18n.DEFAULT_KORDEX_BUNDLE -import dev.kordex.core.i18n.KORDEX_BUNDLE import dev.kordex.core.i18n.TranslationsProvider import dev.kordex.core.i18n.generated.CoreTranslations -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.core.koin.KordExKoinComponent import dev.kordex.core.utils.getLocale @@ -73,7 +70,6 @@ public class TagConverter( override var validator: Validator = null, ) : SingleConverter() { public override val signatureType: Key = CoreTranslations.Converters.Tag.signatureType - override val bundle: Bundle = KORDEX_BUNDLE override suspend fun parse(parser: StringParser?, context: CommandContext, named: String?): Boolean { val input: String = named ?: parser?.parseNext()?.data ?: return false @@ -165,16 +161,13 @@ public class TagConverter( if (channel == null) { throw DiscordRelayedException( - translationsProvider.translate( - key = if (getter == null) { - "converters.tag.error.wrongChannelType" - } else { - "converters.tag.error.wrongChannelTypeWithGetter" - }, - - bundleName = DEFAULT_KORDEX_BUNDLE, - locale = event.getLocale(), - ) + if (getter == null) { + CoreTranslations.Converters.Tag.Error.wrongChannelType + } else { + CoreTranslations.Converters.Tag.Error.wrongChannelTypeWithGetter + } + .withLocale(event.getLocale()) + .translate() ) } diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/TimestampConverter.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/TimestampConverter.kt index 8626fa7bba..a550fbde4e 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/TimestampConverter.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/TimestampConverter.kt @@ -20,9 +20,7 @@ import dev.kordex.core.commands.OptionWrapper import dev.kordex.core.commands.converters.SingleConverter import dev.kordex.core.commands.converters.Validator import dev.kordex.core.commands.wrapOption -import dev.kordex.core.i18n.KORDEX_BUNDLE import dev.kordex.core.i18n.generated.CoreTranslations -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.core.time.TimestampType import dev.kordex.core.time.toDiscord @@ -44,7 +42,6 @@ public class TimestampConverter( override var validator: Validator = null, ) : SingleConverter() { override val signatureType: Key = CoreTranslations.Converters.Timestamp.signatureType - override val bundle: Bundle = KORDEX_BUNDLE override suspend fun parse(parser: StringParser?, context: CommandContext, named: String?): Boolean { val arg: String = named ?: parser?.parseNext()?.data ?: return false diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/UserConverter.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/UserConverter.kt index 896668909f..969b60d2b9 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/UserConverter.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl/UserConverter.kt @@ -24,9 +24,7 @@ import dev.kordex.core.commands.chat.ChatCommandContext import dev.kordex.core.commands.converters.SingleConverter import dev.kordex.core.commands.converters.Validator import dev.kordex.core.commands.wrapOption -import dev.kordex.core.i18n.KORDEX_BUNDLE import dev.kordex.core.i18n.generated.CoreTranslations -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.core.utils.users import dev.kordex.parser.StringParser @@ -55,7 +53,6 @@ public class UserConverter( override var validator: Validator = null, ) : SingleConverter() { override val signatureType: Key = CoreTranslations.Converters.User.signatureType - override val bundle: Bundle = KORDEX_BUNDLE override suspend fun parse(parser: StringParser?, context: CommandContext, named: String?): Boolean { if (useReply && context is ChatCommandContext<*>) { diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/components/Component.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/components/Component.kt index 3321be0323..f68a8a5172 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/components/Component.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/components/Component.kt @@ -14,7 +14,6 @@ import dev.kordex.core.ExtensibleBot import dev.kordex.core.builders.ExtensibleBotBuilder import dev.kordex.core.commands.application.ApplicationCommandRegistry import dev.kordex.core.i18n.TranslationsProvider -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.koin.KordExKoinComponent import dev.kordex.core.sentry.SentryAdapter import org.koin.core.component.inject @@ -42,9 +41,6 @@ public abstract class Component : KordExKoinComponent { /** Sentry adapter, for easy access to Sentry functions. **/ public val sentry: SentryAdapter by inject() - /** Translation bundle, to retrieve translations from. **/ - public open var bundle: Bundle? = null - /** Validation function, called to ensure the component is valid, throws exceptions if not. **/ public abstract fun validate() diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/components/ComponentContext.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/components/ComponentContext.kt index 5d7c90d8be..7ce6c5654a 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/components/ComponentContext.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/components/ComponentContext.kt @@ -24,7 +24,6 @@ import dev.kordex.core.checks.channelFor import dev.kordex.core.checks.guildFor import dev.kordex.core.checks.userFor import dev.kordex.core.i18n.TranslationsProvider -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.core.koin.KordExKoinComponent import dev.kordex.core.sentry.SentryContext @@ -51,9 +50,6 @@ public abstract class ComponentContext( /** Translations provider, for retrieving translations. **/ public val translationsProvider: TranslationsProvider by inject() - override val bundle: Bundle? - get() = component.bundle - /** Configured bot settings object. **/ public val settings: ExtensibleBotBuilder by inject() @@ -140,28 +136,26 @@ public abstract class ComponentContext( } /** - * Given a translation key and bundle name, return the translation for the locale provided by the bot's configured + * Given a translation key, return the translation for the locale provided by the bot's configured * locale resolvers. */ public override suspend fun translate( key: Key, - bundle: Bundle?, replacements: Array, ): String = - key.withBundle(bundle) + key .withLocale(getLocale()) .translateArray(replacements) /** - * Given a translation key and bundle name, return the translation for the locale provided by the bot's configured + * Given a translation key, return the translation for the locale provided by the bot's configured * locale resolvers. */ public override suspend fun translate( key: Key, - bundleName: Bundle?, replacements: Map, ): String = - key.withBundle(bundle) + key .withLocale(getLocale()) .translateNamed(replacements) diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/components/buttons/EphemeralInteractionButton.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/components/buttons/EphemeralInteractionButton.kt index c65257fd39..f0e8c598d2 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/components/buttons/EphemeralInteractionButton.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/components/buttons/EphemeralInteractionButton.kt @@ -80,10 +80,10 @@ public open class EphemeralInteractionButton( val locale = event.getLocale() event.interaction.modal( - modalObj.translateTitle(locale, bundle), + modalObj.translateTitle(locale), modalObj.id ) { - modalObj.applyToBuilder(this, event.getLocale(), bundle) + modalObj.applyToBuilder(this, event.getLocale()) } modalObj.awaitCompletion { diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/components/buttons/PublicInteractionButton.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/components/buttons/PublicInteractionButton.kt index f28221167b..884b9c9561 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/components/buttons/PublicInteractionButton.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/components/buttons/PublicInteractionButton.kt @@ -81,10 +81,10 @@ public open class PublicInteractionButton( val locale = event.getLocale() event.interaction.modal( - modalObj.translateTitle(locale, bundle), + modalObj.translateTitle(locale), modalObj.id ) { - modalObj.applyToBuilder(this, event.getLocale(), bundle) + modalObj.applyToBuilder(this, event.getLocale()) } modalObj.awaitCompletion { diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/components/forms/ModalForm.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/components/forms/ModalForm.kt index 5c02a20218..c1ef881250 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/components/forms/ModalForm.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/components/forms/ModalForm.kt @@ -29,7 +29,6 @@ import dev.kordex.core.components.forms.widgets.Widget import dev.kordex.core.events.EventContext import dev.kordex.core.events.ModalInteractionCompleteEvent import dev.kordex.core.i18n.TranslationsProvider -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.core.koin.KordExKoinComponent import dev.kordex.core.utils.waitFor @@ -47,9 +46,6 @@ public abstract class ModalForm : Form(), KordExKoinComponent { /** The modal's title, shown on Discord. **/ public abstract var title: Key - /** Translation bundle to use for this modal's title and widgets. **/ - public open var bundle: Bundle? = null - /** @suppress Internal reference. **/ protected val bot: ExtensibleBot by inject() @@ -118,7 +114,7 @@ public abstract class ModalForm : Form(), KordExKoinComponent { } /** Given a ModalBuilder, apply this modal's widgets for display on Discord. **/ - public suspend fun applyToBuilder(builder: ModalBuilder, locale: Locale, resolvedBundle: Bundle?) { + public suspend fun applyToBuilder(builder: ModalBuilder, locale: Locale) { val appliedWidgets = mutableSetOf>() grid.forEach { row -> @@ -129,7 +125,7 @@ public abstract class ModalForm : Form(), KordExKoinComponent { builder.actionRow { filteredRow.forEach { widget -> if (widget !in appliedWidgets) { - widget.apply(this, locale, bundle ?: resolvedBundle) + widget.apply(this, locale) appliedWidgets.add(widget) } } @@ -145,10 +141,9 @@ public abstract class ModalForm : Form(), KordExKoinComponent { return callback(completionEvent?.interaction) } - /** Return a translated modal title using the given locale, and the given bundle if the modal doesn't have one. **/ - public fun translateTitle(locale: Locale, otherBundle: Bundle?): String = + /** Return a translated modal title using the given locale. **/ + public fun translateTitle(locale: Locale): String = title - .withBundle(bundle ?: otherBundle) .withLocale(locale) .translate() @@ -162,14 +157,13 @@ public abstract class ModalForm : Form(), KordExKoinComponent { */ public suspend fun sendAndAwait( locale: Locale, - bundle: Bundle?, interaction: ModalParentInteractionBehavior, callback: suspend (ModalSubmitInteraction?) -> T, ): T { componentRegistry.register(this) - interaction.modal(translateTitle(locale, bundle), id) { - applyToBuilder(this, locale, bundle) + interaction.modal(translateTitle(locale), id) { + applyToBuilder(this, locale) } return awaitCompletion(callback) @@ -188,7 +182,7 @@ public abstract class ModalForm : Form(), KordExKoinComponent { val interaction = context.event.interaction as? ModalParentInteractionBehavior ?: error("Interaction ${context.event.interaction} does not support responding with a modal.") - return sendAndAwait(context.getLocale(), context.bundle, interaction, callback) + return sendAndAwait(context.getLocale(), interaction, callback) } /** @@ -204,7 +198,7 @@ public abstract class ModalForm : Form(), KordExKoinComponent { val interaction = context.genericEvent.interaction as? ModalParentInteractionBehavior ?: error("Interaction ${context.genericEvent.interaction} does not support responding with a modal.") - return sendAndAwait(context.getLocale(), context.bundle, interaction, callback) + return sendAndAwait(context.getLocale(), interaction, callback) } /** @@ -220,7 +214,7 @@ public abstract class ModalForm : Form(), KordExKoinComponent { val interaction = context.event.interaction as? ModalParentInteractionBehavior ?: error("Interaction ${context.event.interaction} does not support responding with a modal.") - return sendAndAwait(context.getLocale(), context.bundle, interaction, callback) + return sendAndAwait(context.getLocale(), interaction, callback) } /** diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/components/forms/widgets/TextInputWidget.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/components/forms/widgets/TextInputWidget.kt index 1798dfb289..1412a1316a 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/components/forms/widgets/TextInputWidget.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/components/forms/widgets/TextInputWidget.kt @@ -11,7 +11,6 @@ package dev.kordex.core.components.forms.widgets import dev.kord.common.entity.TextInputStyle import dev.kord.rest.builder.component.ActionRowBuilder import dev.kordex.core.i18n.TranslationsProvider -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.core.koin.KordExKoinComponent import io.github.oshai.kotlinlogging.KotlinLogging @@ -88,17 +87,17 @@ public abstract class TextInputWidget> : Widget( } } - override suspend fun apply(builder: ActionRowBuilder, locale: Locale, bundle: Bundle?) { - val translatedLabel = label.withBundle(bundle) + override suspend fun apply(builder: ActionRowBuilder, locale: Locale) { + val translatedLabel = label .withLocale(locale) .translate() - val translatedPlaceholder = placeholder?.withBundle(bundle) + val translatedPlaceholder = placeholder ?.withLocale(locale) ?.translate() val translatedInitialValue = if (translateInitialValue) { - initialValue?.withBundle(bundle) + initialValue ?.withLocale(locale) ?.translate() } else { diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/components/forms/widgets/Widget.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/components/forms/widgets/Widget.kt index 4b3c3a7ffc..e4f5b95ebb 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/components/forms/widgets/Widget.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/components/forms/widgets/Widget.kt @@ -9,7 +9,6 @@ package dev.kordex.core.components.forms.widgets import dev.kord.rest.builder.component.ActionRowBuilder -import dev.kordex.core.i18n.types.Bundle import java.util.* /** Abstract type representing a grid-based widget. **/ @@ -30,7 +29,7 @@ public abstract class Widget { "${this::class.simpleName}@${hashCode()} ($width x $height)" /** Function called to apply this widget to a Discord action row. **/ - public abstract suspend fun apply(builder: ActionRowBuilder, locale: Locale, bundle: Bundle?) + public abstract suspend fun apply(builder: ActionRowBuilder, locale: Locale) /** Function called to ensure that this widget was set up correctly. **/ public abstract fun validate() diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/components/menus/EphemeralSelectMenu.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/components/menus/EphemeralSelectMenu.kt index 786cfcd390..2a7cf318a5 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/components/menus/EphemeralSelectMenu.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/components/menus/EphemeralSelectMenu.kt @@ -78,10 +78,10 @@ public abstract class EphemeralSelectMenu( val locale = event.getLocale() event.interaction.modal( - modalObj.translateTitle(locale, bundle), + modalObj.translateTitle(locale), modalObj.id ) { - modalObj.applyToBuilder(this, event.getLocale(), bundle) + modalObj.applyToBuilder(this, event.getLocale()) } modalObj.awaitCompletion { diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/components/menus/PublicSelectMenu.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/components/menus/PublicSelectMenu.kt index de1c12732b..76861d47a0 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/components/menus/PublicSelectMenu.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/components/menus/PublicSelectMenu.kt @@ -79,10 +79,10 @@ public abstract class PublicSelectMenu( val locale = event.getLocale() event.interaction.modal( - modalObj.translateTitle(locale, bundle), + modalObj.translateTitle(locale), modalObj.id ) { - modalObj.applyToBuilder(this, event.getLocale(), bundle) + modalObj.applyToBuilder(this, event.getLocale()) } modalObj.awaitCompletion { diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/events/EventContext.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/events/EventContext.kt index ad1f868334..059b5e7085 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/events/EventContext.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/events/EventContext.kt @@ -14,7 +14,6 @@ import dev.kordex.core.checks.guildFor import dev.kordex.core.checks.interactionFor import dev.kordex.core.checks.userFor import dev.kordex.core.i18n.TranslationsProvider -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.core.koin.KordExKoinComponent import dev.kordex.core.sentry.SentryContext @@ -44,37 +43,32 @@ public open class EventContext( override var resolvedLocale: Locale? = null - override val bundle: Bundle? - get() = eventHandler.extension.bundle - /** - * Given a translation key and optional bundle name, return the translation for the locale provided by the bot's + * Given a translation key, return the translation for the locale provided by the bot's * configured locale resolvers. */ public override suspend fun translate( key: Key, - bundleName: Bundle?, replacements: Array, ): String { val locale: Locale = getLocale() - return key.withBundle(bundleName) + return key .withLocale(locale) .translateArray(replacements) } /** - * Given a translation key and optional bundle name, return the translation for the locale provided by the bot's + * Given a translation key, return the translation for the locale provided by the bot's * configured locale resolvers. */ public override suspend fun translate( key: Key, - bundleName: Bundle?, replacements: Map, ): String { val locale: Locale = getLocale() - return key.withBundle(bundleName) + return key .withLocale(locale) .translateNamed(replacements) } diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/events/EventHandler.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/events/EventHandler.kt index 0f3083a3eb..25dd169e97 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/events/EventHandler.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/events/EventHandler.kt @@ -18,8 +18,6 @@ import dev.kordex.core.checks.types.CheckContextWithCache import dev.kordex.core.checks.types.CheckWithCache import dev.kordex.core.extensions.Extension import dev.kordex.core.i18n.TranslationsProvider -import dev.kordex.core.i18n.generated.CoreTranslations.bundle -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.core.koin.KordExKoinComponent import dev.kordex.core.sentry.BreadcrumbType @@ -244,48 +242,26 @@ public open class EventHandler( } /** - * Given a translation key and bundle name, return the translation for the locale provided by the bot's configured + * Given a translation key, return the translation for the locale provided by the bot's configured * locale resolvers. */ public suspend fun Event.translate( key: Key, - bundle: Bundle?, replacements: Array = arrayOf(), ): String = - key.withBundle(bundle) + key .withLocale(getLocale()) .translateArray(replacements) /** - * Given a translation key and bundle name, return the translation for the locale provided by the bot's configured + * Given a translation key, return the translation for the locale provided by the bot's configured * locale resolvers. */ public suspend fun Event.translate( key: Key, - bundle: Bundle?, replacements: Map, ): String = - key.withBundle(bundle) + key .withLocale(getLocale()) .translateNamed(replacements) - - /** - * Given a translation key and possible replacements, return the translation for the given locale in the - * extension's configured bundle, for the locale provided by the bot's configured locale resolvers. - */ - public suspend fun Event.translate(key: Key, replacements: Array = arrayOf()): String = translate( - key, - extension.bundle, - replacements - ) - - /** - * Given a translation key and possible replacements, return the translation for the given locale in the - * extension's configured bundle, for the locale provided by the bot's configured locale resolvers. - */ - public suspend fun Event.translate(key: Key, replacements: Map): String = translate( - key, - extension.bundle, - replacements - ) } diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/extensions/Extension.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/extensions/Extension.kt index 9143529d0c..76d2f79706 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/extensions/Extension.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/extensions/Extension.kt @@ -26,7 +26,6 @@ import dev.kordex.core.commands.chat.ChatCommand import dev.kordex.core.commands.chat.ChatCommandRegistry import dev.kordex.core.events.EventHandler import dev.kordex.core.events.ExtensionStateEvent -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.koin.KordExKoinComponent import io.github.oshai.kotlinlogging.KotlinLogging import org.koin.core.component.inject @@ -144,9 +143,6 @@ public abstract class Extension : KordExKoinComponent { */ public val userCommandChecks: MutableList = mutableListOf() - /** String representing the bundle to get translations from for command names/descriptions. **/ - public open val bundle: Bundle? = null - /** Set of intents required by this extension's event handlers and commands. **/ public open val intents: MutableSet = mutableSetOf() diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/extensions/impl/AboutExtension.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/extensions/impl/AboutExtension.kt index 3aa7d1380d..21eb632fa1 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/extensions/impl/AboutExtension.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/extensions/impl/AboutExtension.kt @@ -53,7 +53,6 @@ public class AboutExtension : Extension() { this.chatCommand { this.name = section.name description = section.description - bundle = section.translationBundle action { message.reply { @@ -87,7 +86,6 @@ public class AboutExtension : Extension() { ephemeralSubCommand { name = section.name description = section.description - bundle = section.translationBundle action { respond { @@ -99,7 +97,6 @@ public class AboutExtension : Extension() { publicSubCommand { name = section.name description = section.description - bundle = section.translationBundle action { respond { @@ -133,7 +130,6 @@ public class AboutExtension : Extension() { ephemeralSubCommand { name = section.name description = section.description - bundle = section.translationBundle action { respond { @@ -145,7 +141,6 @@ public class AboutExtension : Extension() { publicSubCommand { name = section.name description = section.description - bundle = section.translationBundle action { respond { diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/extensions/impl/HelpExtension.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/extensions/impl/HelpExtension.kt index 3d438d41d9..0d7bda7804 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/extensions/impl/HelpExtension.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/extensions/impl/HelpExtension.kt @@ -280,13 +280,11 @@ public class HelpExtension : HelpProvider, Extension() { if (longDescription) { append( command.description - .withBundle(command.extension.bundle) .translateLocale(locale) ) } else { append( command.description - .withBundle(command.extension.bundle) .translateLocale(locale) .trim() .takeWhile { it != '\n' } @@ -378,7 +376,6 @@ public class HelpExtension : HelpProvider, Extension() { append( it.converter.signatureType - .withBundle(it.converter.bundle) .translateLocale(locale) ) @@ -388,7 +385,6 @@ public class HelpExtension : HelpProvider, Extension() { append("`: ") append( it.description - .withBundle(command.extension.bundle) .translateLocale(locale) ) } diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/i18n/TranslationsProvider.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/i18n/TranslationsProvider.kt index 04fc7f38e5..66de7cd8e9 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/i18n/TranslationsProvider.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/i18n/TranslationsProvider.kt @@ -28,10 +28,10 @@ public abstract class TranslationsProvider( */ public open val defaultLocale: Locale by lazy { defaultLocaleBuilder() } - /** Check whether a translation key exists in the given bundle and locale. **/ + /** Check whether a translation key exists. **/ public abstract fun hasKey(key: Key): Boolean - /** Get a translation by key from the given locale and bundle name (`kordex.strings` by default). **/ + /** Get a translation by key. **/ public abstract fun get(key: Key): String /** Get a formatted translation using the provided arguments. **/ diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/i18n/_Constants.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/i18n/_Constants.kt index 923dbb1713..72613ddb9a 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/i18n/_Constants.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/i18n/_Constants.kt @@ -24,6 +24,3 @@ public val KORDEX_BUNDLE: Bundle = Bundle("$KORDEX_KEY.$DEFAULT_BUNDLE_SUFFIX") /** String used to denote an empty translation value - `∅∅∅` (`\u2205\u2205\u2205`). **/ public const val EMPTY_VALUE_STRING: String = "∅∅∅" - -/** String representing the default KordEx bundle. **/ -public const val DEFAULT_KORDEX_BUNDLE: String = "$KORDEX_KEY.$DEFAULT_BUNDLE_SUFFIX" diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/i18n/types/Key.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/i18n/types/Key.kt index 73b8afbe61..15056f013c 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/i18n/types/Key.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/i18n/types/Key.kt @@ -102,9 +102,10 @@ public data class Key( public fun translateNamedLocale(locale: Locale, vararg replacements: Pair): String = translateNamedLocale(locale, replacements.toMap()) + // Key "translation.key" (Bundle "name" / Locale "en_GB") override fun toString(): String = buildString { - append("Key $key") + append("Key \"$key\"") if (bundle != null || locale != null) { append("(") @@ -118,7 +119,7 @@ public data class Key( } if (locale != null) { - append("${locale.toLanguageTag()}") + append("Locale \"${locale.toLanguageTag()}\"") } append(")") diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/koin/KordExContext.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/koin/KordExContext.kt index b5852193d4..7b6918bdf7 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/koin/KordExContext.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/koin/KordExContext.kt @@ -6,6 +6,8 @@ * Any redistribution must include the specific provision above. */ +@file:Suppress("TYPEALIAS_EXPANSION_DEPRECATION") + package dev.kordex.core.koin import org.koin.core.Koin @@ -49,7 +51,7 @@ public object KordExContext : KoinContext { * * @param koinApplication The application to registers. * - * @throws KoinAppAlreadyStartedException The [KoinApplication] has already been instantiated. + * @throws ApplicationAlreadyStartedException The [KoinApplication] has already been instantiated. */ private fun register(koinApplication: KoinApplication) { if (koin != null) { @@ -71,7 +73,7 @@ public object KordExContext : KoinContext { * * @param koinApplication The application to start with. * - * @throws KoinAppAlreadyStartedException The [KoinApplication] has already been instantiated. + * @throws ApplicationAlreadyStartedException The [KoinApplication] has already been instantiated. */ override fun startKoin(koinApplication: KoinApplication): KoinApplication = synchronized(this) { register(koinApplication) @@ -85,7 +87,7 @@ public object KordExContext : KoinContext { * * @param appDeclaration The application declaration to start with. * - * @throws KoinAppAlreadyStartedException The [KoinApplication] has already been instantiated. + * @throws ApplicationAlreadyStartedException The [KoinApplication] has already been instantiated. */ override fun startKoin(appDeclaration: KoinAppDeclaration): KoinApplication = synchronized(this) { val koinApplication = KoinApplication.init() diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/pagination/BaseButtonPaginator.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/pagination/BaseButtonPaginator.kt index f10b45553e..7c6e2dbb73 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/pagination/BaseButtonPaginator.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/pagination/BaseButtonPaginator.kt @@ -19,7 +19,6 @@ import dev.kordex.core.components.publicButton import dev.kordex.core.components.types.emoji import dev.kordex.core.i18n.EMPTY_KEY import dev.kordex.core.i18n.generated.CoreTranslations -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.core.pagination.builders.PageTransitionCallback import dev.kordex.core.pagination.pages.Pages @@ -42,9 +41,8 @@ public abstract class BaseButtonPaginator( keepEmbed: Boolean = true, switchEmoji: ReactionEmoji = if (pages.groups.size == 2) EXPAND_EMOJI else SWITCH_EMOJI, mutator: PageTransitionCallback? = null, - bundle: Bundle? = null, locale: Locale? = null, -) : BasePaginator(pages, chunkedPages, owner, timeoutSeconds, keepEmbed, switchEmoji, mutator, bundle, locale) { +) : BasePaginator(pages, chunkedPages, owner, timeoutSeconds, keepEmbed, switchEmoji, mutator, locale) { /** [ComponentContainer] instance managing the buttons for this paginator. **/ public open var components: ComponentContainer = ComponentContainer() diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/pagination/BasePaginator.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/pagination/BasePaginator.kt index b1f1346f27..b1f62aae0f 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/pagination/BasePaginator.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/pagination/BasePaginator.kt @@ -17,7 +17,6 @@ import dev.kord.rest.builder.message.embed import dev.kordex.core.DISCORD_BLURPLE import dev.kordex.core.ExtensibleBot import dev.kordex.core.i18n.TranslationsProvider -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.core.koin.KordExKoinComponent import dev.kordex.core.pagination.builders.PageTransitionCallback @@ -64,7 +63,6 @@ public val EXPAND_EMOJI: ReactionEmoji.Unicode = ReactionEmoji.Unicode("\u2139\u * @param keepEmbed Set this to `false` to remove the paginator's message when it's destroyed * @param switchEmoji The `ReactionEmoji` to use for group switching * @param locale A Locale object for this pagination context, which defaults to the bot's default locale - * @param bundle Translation bundle to use for this paginator */ public abstract class BasePaginator( public open val pages: Pages, @@ -74,7 +72,6 @@ public abstract class BasePaginator( public open val keepEmbed: Boolean = true, public open val switchEmoji: ReactionEmoji = if (pages.groups.size == 2) EXPAND_EMOJI else SWITCH_EMOJI, public open val mutator: PageTransitionCallback? = null, - public open val bundle: Bundle? = null, locale: Locale? = null, ) : KordExKoinComponent { @@ -166,7 +163,7 @@ public abstract class BasePaginator( logger.debug { "Building footer page" } - Page(bundle) { + Page { color = DISCORD_BLURPLE }.build( localeObj, @@ -246,10 +243,9 @@ public abstract class BasePaginator( } } - /** Quick access to translations, using the paginator's locale and bundle. **/ + /** Quick access to translations, using the paginator's locale. **/ public fun translate(key: Key, replacements: Array = arrayOf()): String = key - .withBundle(bundle) .withLocale(localeObj) .translateArray(replacements) } diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/pagination/EphemeralResponsePaginator.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/pagination/EphemeralResponsePaginator.kt index e459bcf249..ce59047e9d 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/pagination/EphemeralResponsePaginator.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/pagination/EphemeralResponsePaginator.kt @@ -12,7 +12,6 @@ import dev.kord.core.behavior.UserBehavior import dev.kord.core.behavior.interaction.response.EphemeralMessageInteractionResponseBehavior import dev.kord.core.behavior.interaction.response.edit import dev.kord.core.entity.ReactionEmoji -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.pagination.builders.PageTransitionCallback import dev.kordex.core.pagination.builders.PaginatorBuilder import dev.kordex.core.pagination.pages.Pages @@ -30,11 +29,10 @@ public class EphemeralResponsePaginator( timeoutSeconds: Long? = null, switchEmoji: ReactionEmoji = if (pages.groups.size == 2) EXPAND_EMOJI else SWITCH_EMOJI, mutator: PageTransitionCallback? = null, - bundle: Bundle? = null, locale: Locale? = null, public val interaction: EphemeralMessageInteractionResponseBehavior, -) : BaseButtonPaginator(pages, chunkedPages, owner, timeoutSeconds, true, switchEmoji, mutator, bundle, locale) { +) : BaseButtonPaginator(pages, chunkedPages, owner, timeoutSeconds, true, switchEmoji, mutator, locale) { /** Whether this paginator has been set up for the first time. **/ public var isSetup: Boolean = false @@ -84,7 +82,6 @@ public fun EphemeralResponsePaginator( owner = builder.owner, timeoutSeconds = builder.timeoutSeconds, mutator = builder.mutator, - bundle = builder.bundle, locale = builder.locale, interaction = interaction, diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/pagination/MessageButtonPaginator.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/pagination/MessageButtonPaginator.kt index 77c0c66dad..37d2bca51d 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/pagination/MessageButtonPaginator.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/pagination/MessageButtonPaginator.kt @@ -15,7 +15,6 @@ import dev.kord.core.behavior.edit import dev.kord.core.entity.Message import dev.kord.core.entity.ReactionEmoji import dev.kord.rest.builder.message.allowedMentions -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.pagination.builders.PageTransitionCallback import dev.kordex.core.pagination.builders.PaginatorBuilder import dev.kordex.core.pagination.pages.Pages @@ -36,13 +35,12 @@ public class MessageButtonPaginator( keepEmbed: Boolean = true, switchEmoji: ReactionEmoji = if (pages.groups.size == 2) EXPAND_EMOJI else SWITCH_EMOJI, mutator: PageTransitionCallback? = null, - bundle: Bundle? = null, locale: Locale? = null, public val pingInReply: Boolean = true, public val targetChannel: MessageChannelBehavior? = null, public val targetMessage: Message? = null, -) : BaseButtonPaginator(pages, chunkedPages, owner, timeoutSeconds, keepEmbed, switchEmoji, mutator, bundle, locale) { +) : BaseButtonPaginator(pages, chunkedPages, owner, timeoutSeconds, keepEmbed, switchEmoji, mutator, locale) { init { if (targetChannel == null && targetMessage == null) { throw IllegalArgumentException("Must provide either a target channel or target message") @@ -120,7 +118,6 @@ public fun MessageButtonPaginator( timeoutSeconds = builder.timeoutSeconds, keepEmbed = builder.keepEmbed, mutator = builder.mutator, - bundle = builder.bundle, locale = builder.locale, pingInReply = pingInReply, diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/pagination/PublicFollowUpPaginator.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/pagination/PublicFollowUpPaginator.kt index 3801bb5fd5..806eb2dd37 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/pagination/PublicFollowUpPaginator.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/pagination/PublicFollowUpPaginator.kt @@ -14,7 +14,6 @@ import dev.kord.core.behavior.interaction.response.FollowupPermittingInteraction import dev.kord.core.behavior.interaction.response.createPublicFollowup import dev.kord.core.entity.ReactionEmoji import dev.kord.core.entity.interaction.followup.PublicFollowupMessage -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.pagination.builders.PageTransitionCallback import dev.kordex.core.pagination.builders.PaginatorBuilder import dev.kordex.core.pagination.pages.Pages @@ -34,11 +33,10 @@ public class PublicFollowUpPaginator( keepEmbed: Boolean = true, switchEmoji: ReactionEmoji = if (pages.groups.size == 2) EXPAND_EMOJI else SWITCH_EMOJI, mutator: PageTransitionCallback? = null, - bundle: Bundle? = null, locale: Locale? = null, public val interaction: FollowupPermittingInteractionResponseBehavior, -) : BaseButtonPaginator(pages, chunkedPages, owner, timeoutSeconds, keepEmbed, switchEmoji, mutator, bundle, locale) { +) : BaseButtonPaginator(pages, chunkedPages, owner, timeoutSeconds, keepEmbed, switchEmoji, mutator, locale) { /** Follow-up interaction to use for this paginator's embeds. Will be created by [send]. **/ public var embedInteraction: PublicFollowupMessage? = null @@ -99,7 +97,6 @@ public fun PublicFollowUpPaginator( timeoutSeconds = builder.timeoutSeconds, keepEmbed = builder.keepEmbed, mutator = builder.mutator, - bundle = builder.bundle, locale = builder.locale, interaction = interaction, diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/pagination/PublicResponsePaginator.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/pagination/PublicResponsePaginator.kt index 200d9b210e..5d5b64ae23 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/pagination/PublicResponsePaginator.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/pagination/PublicResponsePaginator.kt @@ -12,7 +12,6 @@ import dev.kord.core.behavior.UserBehavior import dev.kord.core.behavior.interaction.response.PublicMessageInteractionResponseBehavior import dev.kord.core.behavior.interaction.response.edit import dev.kord.core.entity.ReactionEmoji -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.pagination.builders.PageTransitionCallback import dev.kordex.core.pagination.builders.PaginatorBuilder import dev.kordex.core.pagination.pages.Pages @@ -31,11 +30,10 @@ public class PublicResponsePaginator( keepEmbed: Boolean = true, switchEmoji: ReactionEmoji = if (pages.groups.size == 2) EXPAND_EMOJI else SWITCH_EMOJI, mutator: PageTransitionCallback? = null, - bundle: Bundle? = null, locale: Locale? = null, public val interaction: PublicMessageInteractionResponseBehavior, -) : BaseButtonPaginator(pages, chunkedPages, owner, timeoutSeconds, keepEmbed, switchEmoji, mutator, bundle, locale) { +) : BaseButtonPaginator(pages, chunkedPages, owner, timeoutSeconds, keepEmbed, switchEmoji, mutator, locale) { /** Whether this paginator has been set up for the first time. **/ public var isSetup: Boolean = false @@ -86,7 +84,6 @@ public fun PublicResponsePaginator( timeoutSeconds = builder.timeoutSeconds, keepEmbed = builder.keepEmbed, mutator = builder.mutator, - bundle = builder.bundle, locale = builder.locale, interaction = interaction, diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/pagination/builders/PaginatorBuilder.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/pagination/builders/PaginatorBuilder.kt index 2c22d41fc9..0e373341a0 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/pagination/builders/PaginatorBuilder.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/pagination/builders/PaginatorBuilder.kt @@ -12,7 +12,6 @@ import dev.kord.core.behavior.UserBehavior import dev.kord.core.entity.ReactionEmoji import dev.kord.rest.builder.message.EmbedBuilder import dev.kordex.core.i18n.EMPTY_KEY -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.core.pagination.pages.Page import dev.kordex.core.pagination.pages.Pages @@ -46,9 +45,6 @@ public class PaginatorBuilder( /** Alternative switch button emoji, if needed. **/ public var switchEmoji: ReactionEmoji? = null - /** Translations bundle to use for page groups, if any. **/ - public var bundle: Bundle? = null - /** Object containing paginator mutation functions. **/ public var mutator: PageTransitionCallback? = null @@ -60,18 +56,16 @@ public class PaginatorBuilder( /** Add a page to [pages], using the default group. **/ public fun page( - bundle: Bundle? = null, builder: suspend EmbedBuilder.() -> Unit, ): Unit = - page(Page(builder = builder, bundle = bundle)) + page(Page(builder = builder)) /** Add a page to [pages], using the given group. **/ public fun page( group: Key, - bundle: Bundle? = null, builder: suspend EmbedBuilder.() -> Unit, ): Unit = - page(group, Page(builder = builder, bundle = bundle)) + page(group, Page(builder = builder)) /** * Mutate the paginator and pages, as pages are generated and sent. diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/pagination/pages/Page.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/pagination/pages/Page.kt index a6b34977f8..cfb321c1c9 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/pagination/pages/Page.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/pagination/pages/Page.kt @@ -12,7 +12,6 @@ import dev.kord.rest.builder.message.EmbedBuilder import dev.kordex.core.ExtensibleBot import dev.kordex.core.i18n.TranslationsProvider import dev.kordex.core.i18n.generated.CoreTranslations -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.core.koin.KordExKoinComponent import dev.kordex.core.pagination.builders.PageMutator @@ -24,13 +23,11 @@ import kotlin.math.ceil import kotlin.math.roundToInt /** - * Representation of a single paginator page. You can extend this to customize it if you wish! + * Representation of a single paginator page. You can extend this to customise it if you wish! * - * @param bundle Optional: Translations bundle for group names * @param builder Embed builder callable for building the page's embed */ public open class Page( - public open val bundle: Bundle? = null, public open val builder: suspend EmbedBuilder.() -> Unit, ) : KordExKoinComponent { /** Current instance of the bot. **/ @@ -101,7 +98,6 @@ public open class Page( ) } else { val groupName = group - .withBundle(bundle) .withLocale(locale) .translate() .capitalizeWords(locale) diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/sentry/SentryIdConverter.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/sentry/SentryIdConverter.kt index 605628a579..86c573fcc6 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/sentry/SentryIdConverter.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/sentry/SentryIdConverter.kt @@ -20,9 +20,7 @@ import dev.kordex.core.commands.OptionWrapper import dev.kordex.core.commands.converters.SingleConverter import dev.kordex.core.commands.converters.Validator import dev.kordex.core.commands.wrapOption -import dev.kordex.core.i18n.KORDEX_BUNDLE import dev.kordex.core.i18n.generated.CoreTranslations -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.parser.StringParser import io.sentry.protocol.SentryId @@ -41,7 +39,6 @@ public class SentryIdConverter( override var validator: Validator = null, ) : SingleConverter() { override val signatureType: Key = CoreTranslations.Extensions.Sentry.Converter.SentryId.signatureType - override val bundle: Bundle = KORDEX_BUNDLE override suspend fun parse(parser: StringParser?, context: CommandContext, named: String?): Boolean { val arg: String = named ?: parser?.parseNext()?.data ?: return false diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/types/TranslatableContext.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/types/TranslatableContext.kt index 391d8af7e7..c48a8480d0 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/types/TranslatableContext.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/types/TranslatableContext.kt @@ -9,7 +9,6 @@ package dev.kordex.core.types import dev.kordex.core.i18n.TranslationsProvider -import dev.kordex.core.i18n.types.Bundle import dev.kordex.core.i18n.types.Key import dev.kordex.core.koin.KordExContext import java.util.* @@ -24,9 +23,6 @@ public interface TranslatableContext { /** Cached locale variable, stored and retrieved by [getLocale]. **/ public var resolvedLocale: Locale? - /** Default bundle to use for the [translate] functions. **/ - public val bundle: Bundle? - /** Retrieve the bot's translation provider from Koin. **/ public fun getTranslationProvider(): TranslationsProvider = KordExContext.get().get() @@ -34,44 +30,20 @@ public interface TranslatableContext { public suspend fun getLocale(): Locale /** - * Given a translation key and bundle name, return the translation for the locale provided by the bot's configured + * Given a translation key, return the translation for the locale provided by the bot's configured * locale resolvers. */ public suspend fun translate( key: Key, - bundleName: Bundle?, replacements: Array = arrayOf(), ): String /** - * Given a translation key and bundle name, return the translation for the locale provided by the bot's configured + * Given a translation key, return the translation for the locale provided by the bot's configured * locale resolvers. */ public suspend fun translate( key: Key, - bundleName: Bundle?, replacements: Map, ): String - - /** - * Given a translation key, return the translation for the locale provided by the bot's configured locale - * resolvers, using the bundle provided for this context. - */ - public suspend fun translate( - key: Key, - replacements: Array = arrayOf(), - ): String = translate( - key, bundle, replacements - ) - - /** - * Given a translation key, return the translation for the locale provided by the bot's configured locale - * resolvers, using the bundle provided for this context. - */ - public suspend fun translate( - key: Key, - replacements: Map, - ): String = translate( - key, bundle, replacements - ) } diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/utils/_Maps.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/utils/_Maps.kt index a4331c14b5..49ed211000 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/utils/_Maps.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/utils/_Maps.kt @@ -10,6 +10,7 @@ package dev.kordex.core.utils import dev.kord.common.annotation.KordPreview import dev.kord.core.event.Event +import dev.kordex.core.i18n.types.Key import java.util.* /** Type alias representing a string keyed map. **/ @@ -97,3 +98,15 @@ public fun Map.getIgnoringCase(key: String, locale: Locale? lowerCase[key.lowercase()] } } + +/** For Key-keyed maps, attempt to retrieve a value using a case-insensitive key. **/ +@JvmName("getIgnoringCaseKeyed") +public fun Map.getIgnoringCase(key: String, locale: Locale? = null): V? { + val lowerCase = entries.associate { it.key.withLocale(locale).translate().lowercase() to it.value } + + return if (locale != null) { + lowerCase[key.lowercase(locale)] + } else { + lowerCase[key.lowercase()] + } +} diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/utils/_i18n.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/utils/_i18n.kt index e3b3e3bd26..9ec4aa3473 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/utils/_i18n.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/utils/_i18n.kt @@ -12,4 +12,4 @@ import dev.kordex.core.i18n.types.Key import dev.kordex.core.types.TranslatableContext public suspend fun Key.withContext(context: TranslatableContext) = - withBoth(context.bundle, context.getLocale(), overwriteBundle = false) + withLocale(context.getLocale()) diff --git a/modules/dev/dev-java-time/src/main/kotlin/dev/kordex/modules/dev/java/time/J8DurationCoalescingConverter.kt b/modules/dev/dev-java-time/src/main/kotlin/dev/kordex/modules/dev/java/time/J8DurationCoalescingConverter.kt index d5520b28c9..a3c0790854 100644 --- a/modules/dev/dev-java-time/src/main/kotlin/dev/kordex/modules/dev/java/time/J8DurationCoalescingConverter.kt +++ b/modules/dev/dev-java-time/src/main/kotlin/dev/kordex/modules/dev/java/time/J8DurationCoalescingConverter.kt @@ -10,19 +10,22 @@ package dev.kordex.modules.dev.java.time import dev.kord.core.entity.interaction.OptionValue import dev.kord.core.entity.interaction.StringOptionValue -import dev.kord.rest.builder.interaction.OptionsBuilder import dev.kord.rest.builder.interaction.StringChoiceBuilder import dev.kordex.core.DiscordRelayedException import dev.kordex.core.annotations.converters.Converter import dev.kordex.core.annotations.converters.ConverterType import dev.kordex.core.commands.Argument import dev.kordex.core.commands.CommandContext +import dev.kordex.core.commands.OptionWrapper import dev.kordex.core.commands.converters.CoalescingConverter import dev.kordex.core.commands.converters.Validator -import dev.kordex.core.i18n.DEFAULT_KORDEX_BUNDLE +import dev.kordex.core.commands.wrapOption import dev.kordex.core.i18n.EMPTY_VALUE_STRING +import dev.kordex.core.i18n.generated.CoreTranslations +import dev.kordex.core.i18n.types.Key import dev.kordex.core.parsers.DurationParserException import dev.kordex.core.parsers.InvalidTimeUnitException +import dev.kordex.core.utils.withContext import dev.kordex.parser.StringParser import dev.kordex.parser.tokens.PositionalArgumentToken import io.github.oshai.kotlinlogging.KLogger @@ -58,15 +61,16 @@ public class J8DurationCoalescingConverter( shouldThrow: Boolean = false, override var validator: Validator = null, ) : CoalescingConverter(shouldThrow) { - override val signatureType: String = "converters.duration.error.signatureType" - override val bundle: String = DEFAULT_KORDEX_BUNDLE + override val signatureType: Key = CoreTranslations.Converters.Duration.Error.signatureType private val logger: KLogger = KotlinLogging.logger {} override suspend fun parse(parser: StringParser?, context: CommandContext, named: List?): Int { val durations: MutableList = mutableListOf() - val ignoredWords: List = context.translate("utils.durations.ignoredWords") + val ignoredWords: List = CoreTranslations.Utils.Durations.ignoredWords + .withContext(context) + .translate() .split(",") .toMutableList() .apply { remove(EMPTY_VALUE_STRING) } @@ -147,7 +151,11 @@ public class J8DurationCoalescingConverter( normalized.normalize(LocalDateTime.now()) if (!normalized.isPositive()) { - throw DiscordRelayedException(context.translate("converters.duration.error.positiveOnly")) + throw DiscordRelayedException( + CoreTranslations.Converters.Duration.Error.positiveOnly + .withContext(context) + .translate() + ) } } @@ -168,10 +176,23 @@ public class J8DurationCoalescingConverter( ): Unit = if (shouldThrow || override) { when (e) { is InvalidTimeUnitException -> { - val message: String = context.translate( - "converters.duration.error.invalidUnit", - replacements = arrayOf(e.unit) - ) + if (longHelp) "\n\n" + context.translate("converters.duration.help") else "" + val message: String = buildString { + append( + CoreTranslations.Converters.Duration.Error.invalidUnit + .withLocale(context.getLocale()) + .translate(e.unit) + ) + + if (longHelp) { + append("\n\n") + + append( + CoreTranslations.Converters.Duration.help + .withLocale(context.getLocale()) + .translate() + ) + } + } throw DiscordRelayedException(message) } @@ -184,8 +205,10 @@ public class J8DurationCoalescingConverter( logger.debug(e) { "Error thrown during duration parsing" } } - override suspend fun toSlashOption(arg: Argument<*>): OptionsBuilder = - StringChoiceBuilder(arg.displayName, arg.description).apply { required = true } + override suspend fun toSlashOption(arg: Argument<*>): OptionWrapper = + wrapOption(arg.displayName, arg.description) { + required = true + } override suspend fun parseOption(context: CommandContext, option: OptionValue<*>): Boolean { val arg: String = (option as? StringOptionValue)?.value ?: return false @@ -199,16 +222,33 @@ public class J8DurationCoalescingConverter( normalized.normalize(LocalDateTime.now()) if (!normalized.isPositive()) { - throw DiscordRelayedException(context.translate("converters.duration.error.positiveOnly")) + throw DiscordRelayedException( + CoreTranslations.Converters.Duration.Error.positiveOnly + .withContext(context) + .translate() + ) } } parsed = result } catch (e: InvalidTimeUnitException) { - val message: String = context.translate( - "converters.duration.error.invalidUnit", - replacements = arrayOf(e.unit) - ) + if (longHelp) "\n\n" + context.translate("converters.duration.help") else "" + val message: String = buildString { + append( + CoreTranslations.Converters.Duration.Error.invalidUnit + .withLocale(context.getLocale()) + .translate(e.unit) + ) + + if (longHelp) { + append("\n\n") + + append( + CoreTranslations.Converters.Duration.help + .withLocale(context.getLocale()) + .translate() + ) + } + } throw DiscordRelayedException(message) } catch (e: DurationParserException) { diff --git a/modules/dev/dev-java-time/src/main/kotlin/dev/kordex/modules/dev/java/time/J8DurationConverter.kt b/modules/dev/dev-java-time/src/main/kotlin/dev/kordex/modules/dev/java/time/J8DurationConverter.kt index 1abb1f4ea2..fce0928e38 100644 --- a/modules/dev/dev-java-time/src/main/kotlin/dev/kordex/modules/dev/java/time/J8DurationConverter.kt +++ b/modules/dev/dev-java-time/src/main/kotlin/dev/kordex/modules/dev/java/time/J8DurationConverter.kt @@ -10,18 +10,21 @@ package dev.kordex.modules.dev.java.time import dev.kord.core.entity.interaction.OptionValue import dev.kord.core.entity.interaction.StringOptionValue -import dev.kord.rest.builder.interaction.OptionsBuilder import dev.kord.rest.builder.interaction.StringChoiceBuilder import dev.kordex.core.DiscordRelayedException import dev.kordex.core.annotations.converters.Converter import dev.kordex.core.annotations.converters.ConverterType import dev.kordex.core.commands.Argument import dev.kordex.core.commands.CommandContext +import dev.kordex.core.commands.OptionWrapper import dev.kordex.core.commands.converters.SingleConverter import dev.kordex.core.commands.converters.Validator -import dev.kordex.core.i18n.DEFAULT_KORDEX_BUNDLE +import dev.kordex.core.commands.wrapOption +import dev.kordex.core.i18n.generated.CoreTranslations +import dev.kordex.core.i18n.types.Key import dev.kordex.core.parsers.DurationParserException import dev.kordex.core.parsers.InvalidTimeUnitException +import dev.kordex.core.utils.withContext import dev.kordex.parser.StringParser import java.time.Duration import java.time.LocalDateTime @@ -50,8 +53,7 @@ public class J8DurationConverter( public val positiveOnly: Boolean = true, override var validator: Validator = null, ) : SingleConverter() { - override val signatureType: String = "converters.duration.error.signatureType" - override val bundle: String = DEFAULT_KORDEX_BUNDLE + override val signatureType: Key = CoreTranslations.Converters.Duration.Error.signatureType override suspend fun parse(parser: StringParser?, context: CommandContext, named: String?): Boolean { val arg: String = named ?: parser?.parseNext()?.data ?: return false @@ -65,16 +67,33 @@ public class J8DurationConverter( normalized.normalize(LocalDateTime.now()) if (!normalized.isPositive()) { - throw DiscordRelayedException(context.translate("converters.duration.error.positiveOnly")) + throw DiscordRelayedException( + CoreTranslations.Converters.Duration.Error.positiveOnly + .withContext(context) + .translate() + ) } } parsed = result } catch (e: InvalidTimeUnitException) { - val message: String = context.translate( - "converters.duration.error.invalidUnit", - replacements = arrayOf(e.unit) - ) + if (longHelp) "\n\n" + context.translate("converters.duration.help") else "" + val message: String = buildString { + append( + CoreTranslations.Converters.Duration.Error.invalidUnit + .withLocale(context.getLocale()) + .translate(e.unit) + ) + + if (longHelp) { + append("\n\n") + + append( + CoreTranslations.Converters.Duration.help + .withLocale(context.getLocale()) + .translate() + ) + } + } throw DiscordRelayedException(message) } catch (e: DurationParserException) { @@ -84,8 +103,10 @@ public class J8DurationConverter( return true } - override suspend fun toSlashOption(arg: Argument<*>): OptionsBuilder = - StringChoiceBuilder(arg.displayName, arg.description).apply { required = true } + override suspend fun toSlashOption(arg: Argument<*>): OptionWrapper = + wrapOption(arg.displayName, arg.description) { + required = true + } override suspend fun parseOption(context: CommandContext, option: OptionValue<*>): Boolean { val arg: String = (option as? StringOptionValue)?.value ?: return false @@ -99,16 +120,33 @@ public class J8DurationConverter( normalized.normalize(LocalDateTime.now()) if (!normalized.isPositive()) { - throw DiscordRelayedException(context.translate("converters.duration.error.positiveOnly")) + throw DiscordRelayedException( + CoreTranslations.Converters.Duration.Error.positiveOnly + .withContext(context) + .translate() + ) } } parsed = result } catch (e: InvalidTimeUnitException) { - val message: String = context.translate( - "converters.duration.error.invalidUnit", - replacements = arrayOf(e.unit) - ) + if (longHelp) "\n\n" + context.translate("converters.duration.help") else "" + val message: String = buildString { + append( + CoreTranslations.Converters.Duration.Error.invalidUnit + .withLocale(context.getLocale()) + .translate(e.unit) + ) + + if (longHelp) { + append("\n\n") + + append( + CoreTranslations.Converters.Duration.help + .withLocale(context.getLocale()) + .translate() + ) + } + } throw DiscordRelayedException(message) } catch (e: DurationParserException) { diff --git a/modules/dev/dev-java-time/src/main/kotlin/dev/kordex/modules/dev/java/time/J8DurationParser.kt b/modules/dev/dev-java-time/src/main/kotlin/dev/kordex/modules/dev/java/time/J8DurationParser.kt index cf27012ce3..fb3ecd3a11 100644 --- a/modules/dev/dev-java-time/src/main/kotlin/dev/kordex/modules/dev/java/time/J8DurationParser.kt +++ b/modules/dev/dev-java-time/src/main/kotlin/dev/kordex/modules/dev/java/time/J8DurationParser.kt @@ -8,20 +8,17 @@ package dev.kordex.modules.dev.java.time -import dev.kordex.core.i18n.TranslationsProvider +import dev.kordex.core.i18n.generated.CoreTranslations import dev.kordex.core.koin.KordExKoinComponent import dev.kordex.core.parsers.DurationParserException import dev.kordex.core.parsers.InvalidTimeUnitException import dev.kordex.core.utils.splitOn -import org.koin.core.component.inject import java.util.* /** * Object in charge of parsing strings into [ChronoContainer]s, using translated locale-aware units. */ public object J8DurationParser : KordExKoinComponent { - private val translations: TranslationsProvider by inject() - /** Check whether the given character is a valid duration unit character. **/ public fun charValid(char: Char, locale: Locale): Boolean = char.isDigit() || @@ -58,7 +55,9 @@ public object J8DurationParser : KordExKoinComponent { } if (values.size != units.size) { - throw DurationParserException(translations.translate("converters.duration.error.badUnitPairs", locale)) + throw DurationParserException( + CoreTranslations.Converters.Duration.Error.badUnitPairs.translateLocale(locale) + ) } while (units.isNotEmpty()) { diff --git a/modules/dev/dev-java-time/src/main/kotlin/dev/kordex/modules/dev/java/time/J8TimeUnitCache.kt b/modules/dev/dev-java-time/src/main/kotlin/dev/kordex/modules/dev/java/time/J8TimeUnitCache.kt index 76c6ae84e3..3feb194470 100644 --- a/modules/dev/dev-java-time/src/main/kotlin/dev/kordex/modules/dev/java/time/J8TimeUnitCache.kt +++ b/modules/dev/dev-java-time/src/main/kotlin/dev/kordex/modules/dev/java/time/J8TimeUnitCache.kt @@ -8,38 +8,38 @@ package dev.kordex.modules.dev.java.time -import dev.kordex.core.i18n.TranslationsProvider +import dev.kordex.core.i18n.generated.CoreTranslations +import dev.kordex.core.i18n.types.Key import dev.kordex.core.koin.KordExKoinComponent -import org.koin.core.component.inject import java.time.temporal.ChronoUnit import java.util.* -private typealias UnitMap = LinkedHashMap +private typealias UnitMap = LinkedHashMap +private typealias StringUnitMap = LinkedHashMap private val keyMap: UnitMap = linkedMapOf( - "utils.units.second" to ChronoUnit.SECONDS, - "utils.units.minute" to ChronoUnit.MINUTES, - "utils.units.hour" to ChronoUnit.HOURS, - "utils.units.day" to ChronoUnit.DAYS, - "utils.units.week" to ChronoUnit.WEEKS, - "utils.units.month" to ChronoUnit.MONTHS, - "utils.units.year" to ChronoUnit.YEARS, + CoreTranslations.Utils.Units.second to ChronoUnit.SECONDS, + CoreTranslations.Utils.Units.minute to ChronoUnit.MINUTES, + CoreTranslations.Utils.Units.hour to ChronoUnit.HOURS, + CoreTranslations.Utils.Units.day to ChronoUnit.DAYS, + CoreTranslations.Utils.Units.week to ChronoUnit.WEEKS, + CoreTranslations.Utils.Units.month to ChronoUnit.MONTHS, + CoreTranslations.Utils.Units.year to ChronoUnit.YEARS, ) /** * Simple object that caches translated time units per locale. */ public object J8TimeUnitCache : KordExKoinComponent { - private val translations: TranslationsProvider by inject() - private val valueCache: MutableMap = mutableMapOf() + private val valueCache: MutableMap = mutableMapOf() /** Return a mapping of all translated unit names to ChronoUnit objects, based on the given locale. **/ - public fun getUnits(locale: Locale): UnitMap { + public fun getUnits(locale: Locale): StringUnitMap { if (valueCache[locale] == null) { - val unitMap: UnitMap = linkedMapOf() + val unitMap: StringUnitMap = linkedMapOf() keyMap.forEach { key, value -> - val result = translations.translate(key, locale) + val result = key.translateLocale(locale) result.split(",").map { it.trim() }.forEach { unitMap[it] = value diff --git a/modules/dev/dev-time4j/src/main/kotlin/dev/kordex/modules/dev/time4j/T4JDurationCoalescingConverter.kt b/modules/dev/dev-time4j/src/main/kotlin/dev/kordex/modules/dev/time4j/T4JDurationCoalescingConverter.kt index 07f1248b7c..5941ed3d6e 100644 --- a/modules/dev/dev-time4j/src/main/kotlin/dev/kordex/modules/dev/time4j/T4JDurationCoalescingConverter.kt +++ b/modules/dev/dev-time4j/src/main/kotlin/dev/kordex/modules/dev/time4j/T4JDurationCoalescingConverter.kt @@ -10,19 +10,22 @@ package dev.kordex.modules.dev.time4j import dev.kord.core.entity.interaction.OptionValue import dev.kord.core.entity.interaction.StringOptionValue -import dev.kord.rest.builder.interaction.OptionsBuilder import dev.kord.rest.builder.interaction.StringChoiceBuilder import dev.kordex.core.DiscordRelayedException import dev.kordex.core.annotations.converters.Converter import dev.kordex.core.annotations.converters.ConverterType import dev.kordex.core.commands.Argument import dev.kordex.core.commands.CommandContext +import dev.kordex.core.commands.OptionWrapper import dev.kordex.core.commands.converters.CoalescingConverter import dev.kordex.core.commands.converters.Validator -import dev.kordex.core.i18n.DEFAULT_KORDEX_BUNDLE +import dev.kordex.core.commands.wrapOption import dev.kordex.core.i18n.EMPTY_VALUE_STRING +import dev.kordex.core.i18n.generated.CoreTranslations +import dev.kordex.core.i18n.types.Key import dev.kordex.core.parsers.DurationParserException import dev.kordex.core.parsers.InvalidTimeUnitException +import dev.kordex.core.utils.withContext import dev.kordex.parser.StringParser import dev.kordex.parser.tokens.PositionalArgumentToken import io.github.oshai.kotlinlogging.KLogger @@ -55,8 +58,7 @@ public class T4JDurationCoalescingConverter( shouldThrow: Boolean = false, override var validator: Validator> = null, ) : CoalescingConverter>(shouldThrow) { - override val signatureType: String = "converters.duration.error.signatureType" - override val bundle: String = DEFAULT_KORDEX_BUNDLE + override val signatureType: Key = CoreTranslations.Converters.Duration.Error.signatureType init { bot.settings.aboutBuilder.addCopyright() @@ -67,7 +69,9 @@ public class T4JDurationCoalescingConverter( override suspend fun parse(parser: StringParser?, context: CommandContext, named: List?): Int { val durations: MutableList = mutableListOf() - val ignoredWords: List = context.translate("utils.durations.ignoredWords") + val ignoredWords: List = CoreTranslations.Utils.Durations.ignoredWords + .withContext(context) + .translate() .split(",") .toMutableList() .apply { remove(EMPTY_VALUE_STRING) } @@ -157,10 +161,22 @@ public class T4JDurationCoalescingConverter( ): Unit = if (shouldThrow || override) { when (e) { is InvalidTimeUnitException -> { - val message: String = context.translate( - "converters.duration.error.invalidUnit", - replacements = arrayOf(e.unit) - ) + if (longHelp) "\n\n" + context.translate("converters.duration.help") else "" + val message: String = buildString { + append( + CoreTranslations.Converters.Duration.Error.invalidUnit + .withContext(context) + .translate(e.unit) + ) + + if (longHelp) { + append("\n\n") + append( + CoreTranslations.Converters.Duration.help + .withContext(context) + .translate() + ) + } + } throw DiscordRelayedException(message) } @@ -173,8 +189,10 @@ public class T4JDurationCoalescingConverter( logger.debug(e) { "Error thrown during duration parsing" } } - override suspend fun toSlashOption(arg: Argument<*>): OptionsBuilder = - StringChoiceBuilder(arg.displayName, arg.description).apply { required = true } + override suspend fun toSlashOption(arg: Argument<*>): OptionWrapper = + wrapOption(arg.displayName, arg.description) { + required = true + } override suspend fun parseOption(context: CommandContext, option: OptionValue<*>): Boolean { val arg: String = (option as? StringOptionValue)?.value ?: return false @@ -182,10 +200,22 @@ public class T4JDurationCoalescingConverter( try { this.parsed = T4JDurationParser.parse(arg, context.getLocale()) } catch (e: InvalidTimeUnitException) { - val message: String = context.translate( - "converters.duration.error.invalidUnit", - replacements = arrayOf(e.unit) - ) + if (longHelp) "\n\n" + context.translate("converters.duration.help") else "" + val message: String = buildString { + append( + CoreTranslations.Converters.Duration.Error.invalidUnit + .withContext(context) + .translate(e.unit) + ) + + if (longHelp) { + append("\n\n") + append( + CoreTranslations.Converters.Duration.help + .withContext(context) + .translate() + ) + } + } throw DiscordRelayedException(message) } catch (e: DurationParserException) { diff --git a/modules/dev/dev-time4j/src/main/kotlin/dev/kordex/modules/dev/time4j/T4JDurationConverter.kt b/modules/dev/dev-time4j/src/main/kotlin/dev/kordex/modules/dev/time4j/T4JDurationConverter.kt index 9fa6ad345d..be2170f616 100644 --- a/modules/dev/dev-time4j/src/main/kotlin/dev/kordex/modules/dev/time4j/T4JDurationConverter.kt +++ b/modules/dev/dev-time4j/src/main/kotlin/dev/kordex/modules/dev/time4j/T4JDurationConverter.kt @@ -10,18 +10,21 @@ package dev.kordex.modules.dev.time4j import dev.kord.core.entity.interaction.OptionValue import dev.kord.core.entity.interaction.StringOptionValue -import dev.kord.rest.builder.interaction.OptionsBuilder import dev.kord.rest.builder.interaction.StringChoiceBuilder import dev.kordex.core.DiscordRelayedException import dev.kordex.core.annotations.converters.Converter import dev.kordex.core.annotations.converters.ConverterType import dev.kordex.core.commands.Argument import dev.kordex.core.commands.CommandContext +import dev.kordex.core.commands.OptionWrapper import dev.kordex.core.commands.converters.SingleConverter import dev.kordex.core.commands.converters.Validator -import dev.kordex.core.i18n.DEFAULT_KORDEX_BUNDLE +import dev.kordex.core.commands.wrapOption +import dev.kordex.core.i18n.generated.CoreTranslations +import dev.kordex.core.i18n.types.Key import dev.kordex.core.parsers.DurationParserException import dev.kordex.core.parsers.InvalidTimeUnitException +import dev.kordex.core.utils.withContext import dev.kordex.parser.StringParser import net.time4j.Duration import net.time4j.IsoUnit @@ -50,8 +53,7 @@ public class T4JDurationConverter( public val longHelp: Boolean = true, override var validator: Validator> = null, ) : SingleConverter>() { - override val signatureType: String = "converters.duration.error.signatureType" - override val bundle: String = DEFAULT_KORDEX_BUNDLE + override val signatureType: Key = CoreTranslations.Converters.Duration.Error.signatureType init { bot.settings.aboutBuilder.addCopyright() @@ -63,10 +65,22 @@ public class T4JDurationConverter( try { this.parsed = T4JDurationParser.parse(arg, context.getLocale()) } catch (e: InvalidTimeUnitException) { - val message: String = context.translate( - "converters.duration.error.invalidUnit", - replacements = arrayOf(e.unit) - ) + if (longHelp) "\n\n" + context.translate("converters.duration.help") else "" + val message: String = buildString { + append( + CoreTranslations.Converters.Duration.Error.invalidUnit + .withContext(context) + .translate(e.unit) + ) + + if (longHelp) { + append("\n\n") + append( + CoreTranslations.Converters.Duration.help + .withContext(context) + .translate() + ) + } + } throw DiscordRelayedException(message) } catch (e: DurationParserException) { @@ -76,8 +90,10 @@ public class T4JDurationConverter( return true } - override suspend fun toSlashOption(arg: Argument<*>): OptionsBuilder = - StringChoiceBuilder(arg.displayName, arg.description).apply { required = true } + override suspend fun toSlashOption(arg: Argument<*>): OptionWrapper = + wrapOption(arg.displayName, arg.description) { + required = true + } override suspend fun parseOption(context: CommandContext, option: OptionValue<*>): Boolean { val arg: String = (option as? StringOptionValue)?.value ?: return false @@ -85,10 +101,22 @@ public class T4JDurationConverter( try { this.parsed = T4JDurationParser.parse(arg, context.getLocale()) } catch (e: InvalidTimeUnitException) { - val message: String = context.translate( - "converters.duration.error.invalidUnit", - replacements = arrayOf(e.unit) - ) + if (longHelp) "\n\n" + context.translate("converters.duration.help") else "" + val message: String = buildString { + append( + CoreTranslations.Converters.Duration.Error.invalidUnit + .withContext(context) + .translate(e.unit) + ) + + if (longHelp) { + append("\n\n") + append( + CoreTranslations.Converters.Duration.help + .withContext(context) + .translate() + ) + } + } throw DiscordRelayedException(message) } catch (e: DurationParserException) { diff --git a/modules/dev/dev-time4j/src/main/kotlin/dev/kordex/modules/dev/time4j/T4JDurationParser.kt b/modules/dev/dev-time4j/src/main/kotlin/dev/kordex/modules/dev/time4j/T4JDurationParser.kt index 14711fe676..39cc2e1fd1 100644 --- a/modules/dev/dev-time4j/src/main/kotlin/dev/kordex/modules/dev/time4j/T4JDurationParser.kt +++ b/modules/dev/dev-time4j/src/main/kotlin/dev/kordex/modules/dev/time4j/T4JDurationParser.kt @@ -9,7 +9,7 @@ package dev.kordex.modules.dev.time4j import dev.kordex.core.builders.ExtensibleBotBuilder -import dev.kordex.core.i18n.TranslationsProvider +import dev.kordex.core.i18n.generated.CoreTranslations import dev.kordex.core.koin.KordExKoinComponent import dev.kordex.core.parsers.DurationParserException import dev.kordex.core.parsers.InvalidTimeUnitException @@ -23,7 +23,6 @@ import java.util.* * Object in charge of parsing strings into [Duration]s, using translated locale-aware units. */ public object T4JDurationParser : KordExKoinComponent { - private val translations: TranslationsProvider by inject() private val settings: ExtensibleBotBuilder by inject() init { @@ -42,7 +41,7 @@ public object T4JDurationParser : KordExKoinComponent { public fun parse(input: String, locale: Locale): Duration { if ("-" in input) { throw DurationParserException( - translations.translate("converters.duration.error.negativeUnsupported", locale) + CoreTranslations.Converters.Duration.Error.negativeUnsupported.translateLocale(locale) ) } @@ -72,7 +71,9 @@ public object T4JDurationParser : KordExKoinComponent { } if (values.size != units.size) { - throw DurationParserException(translations.translate("converters.duration.error.badUnitPairs", locale)) + throw DurationParserException( + CoreTranslations.Converters.Duration.Error.badUnitPairs.translateLocale(locale) + ) } while (units.isNotEmpty()) { diff --git a/modules/dev/dev-time4j/src/main/kotlin/dev/kordex/modules/dev/time4j/T4JTimeUnitCache.kt b/modules/dev/dev-time4j/src/main/kotlin/dev/kordex/modules/dev/time4j/T4JTimeUnitCache.kt index feaa63d7da..11426413d1 100644 --- a/modules/dev/dev-time4j/src/main/kotlin/dev/kordex/modules/dev/time4j/T4JTimeUnitCache.kt +++ b/modules/dev/dev-time4j/src/main/kotlin/dev/kordex/modules/dev/time4j/T4JTimeUnitCache.kt @@ -9,7 +9,8 @@ package dev.kordex.modules.dev.time4j import dev.kordex.core.builders.ExtensibleBotBuilder -import dev.kordex.core.i18n.TranslationsProvider +import dev.kordex.core.i18n.generated.CoreTranslations +import dev.kordex.core.i18n.types.Key import dev.kordex.core.koin.KordExKoinComponent import net.time4j.CalendarUnit import net.time4j.ClockUnit @@ -17,24 +18,24 @@ import net.time4j.IsoUnit import org.koin.core.component.inject import java.util.* -private typealias UnitMap = LinkedHashMap +private typealias UnitMap = LinkedHashMap +private typealias StringUnitMap = LinkedHashMap private val keyMap: UnitMap = linkedMapOf( - "utils.units.second" to ClockUnit.SECONDS, - "utils.units.minute" to ClockUnit.MINUTES, - "utils.units.hour" to ClockUnit.HOURS, - "utils.units.day" to CalendarUnit.DAYS, - "utils.units.week" to CalendarUnit.WEEKS, - "utils.units.month" to CalendarUnit.MONTHS, - "utils.units.year" to CalendarUnit.YEARS, + CoreTranslations.Utils.Units.second to ClockUnit.SECONDS, + CoreTranslations.Utils.Units.minute to ClockUnit.MINUTES, + CoreTranslations.Utils.Units.hour to ClockUnit.HOURS, + CoreTranslations.Utils.Units.day to CalendarUnit.DAYS, + CoreTranslations.Utils.Units.week to CalendarUnit.WEEKS, + CoreTranslations.Utils.Units.month to CalendarUnit.MONTHS, + CoreTranslations.Utils.Units.year to CalendarUnit.YEARS, ) /** * Simple object that caches translated time units per locale. */ public object T4JTimeUnitCache : KordExKoinComponent { - private val translations: TranslationsProvider by inject() - private val valueCache: MutableMap = mutableMapOf() + private val valueCache: MutableMap = mutableMapOf() private val settings: ExtensibleBotBuilder by inject() init { @@ -42,12 +43,12 @@ public object T4JTimeUnitCache : KordExKoinComponent { } /** Return a mapping of all translated unit names to ChronoUnit objects, based on the given locale. **/ - public fun getUnits(locale: Locale): UnitMap { + public fun getUnits(locale: Locale): StringUnitMap { if (valueCache[locale] == null) { - val unitMap: UnitMap = linkedMapOf() + val unitMap: StringUnitMap = linkedMapOf() keyMap.forEach { (key, value) -> - val result = translations.translate(key, locale) + val result = key.translateLocale(locale) result.split(",").map { it.trim() }.forEach { unitMap[it] = value diff --git a/modules/dev/dev-unsafe/src/main/kotlin/dev/kordex/modules/dev/unsafe/commands/UnsafeCommandInteractionContext.kt b/modules/dev/dev-unsafe/src/main/kotlin/dev/kordex/modules/dev/unsafe/commands/UnsafeCommandInteractionContext.kt index ee6fc078fa..4d876218f0 100644 --- a/modules/dev/dev-unsafe/src/main/kotlin/dev/kordex/modules/dev/unsafe/commands/UnsafeCommandInteractionContext.kt +++ b/modules/dev/dev-unsafe/src/main/kotlin/dev/kordex/modules/dev/unsafe/commands/UnsafeCommandInteractionContext.kt @@ -23,6 +23,7 @@ import dev.kord.rest.builder.message.create.FollowupMessageCreateBuilder import dev.kord.rest.builder.message.create.InteractionResponseCreateBuilder import dev.kord.rest.builder.message.modify.InteractionResponseModifyBuilder import dev.kordex.core.annotations.AlwaysPublicResponse +import dev.kordex.core.i18n.types.Key import dev.kordex.core.pagination.BaseButtonPaginator import dev.kordex.core.pagination.EphemeralResponsePaginator import dev.kordex.core.pagination.PublicFollowUpPaginator @@ -108,7 +109,7 @@ public interface UnsafeCommandInteractionContext : @UnsafeAPI override suspend fun editingPaginator( - defaultGroup: String, + defaultGroup: Key, locale: Locale?, builder: suspend PaginatorBuilder.() -> Unit, ): BaseButtonPaginator { @@ -129,7 +130,7 @@ public interface UnsafeCommandInteractionContext : @AlwaysPublicResponse @UnsafeAPI override suspend fun respondingPaginator( - defaultGroup: String, + defaultGroup: Key, locale: Locale?, builder: suspend PaginatorBuilder.() -> Unit, ): BaseButtonPaginator { diff --git a/modules/dev/dev-unsafe/src/main/kotlin/dev/kordex/modules/dev/unsafe/commands/message/UnsafeMessageCommand.kt b/modules/dev/dev-unsafe/src/main/kotlin/dev/kordex/modules/dev/unsafe/commands/message/UnsafeMessageCommand.kt index 0999fcef70..1f67eb806c 100644 --- a/modules/dev/dev-unsafe/src/main/kotlin/dev/kordex/modules/dev/unsafe/commands/message/UnsafeMessageCommand.kt +++ b/modules/dev/dev-unsafe/src/main/kotlin/dev/kordex/modules/dev/unsafe/commands/message/UnsafeMessageCommand.kt @@ -100,10 +100,10 @@ public class UnsafeMessageCommand( val locale = event.getLocale() event.interaction.modal( - modalObj.translateTitle(locale, bundle), + modalObj.translateTitle(locale), modalObj.id ) { - modalObj.applyToBuilder(this, event.getLocale(), bundle) + modalObj.applyToBuilder(this, event.getLocale()) } modalObj.awaitCompletion { diff --git a/modules/dev/dev-unsafe/src/main/kotlin/dev/kordex/modules/dev/unsafe/commands/slash/UnsafeSlashCommand.kt b/modules/dev/dev-unsafe/src/main/kotlin/dev/kordex/modules/dev/unsafe/commands/slash/UnsafeSlashCommand.kt index 1faf30556c..831a064f45 100644 --- a/modules/dev/dev-unsafe/src/main/kotlin/dev/kordex/modules/dev/unsafe/commands/slash/UnsafeSlashCommand.kt +++ b/modules/dev/dev-unsafe/src/main/kotlin/dev/kordex/modules/dev/unsafe/commands/slash/UnsafeSlashCommand.kt @@ -109,10 +109,10 @@ public class UnsafeSlashCommand( val locale = event.getLocale() event.interaction.modal( - modalObj.translateTitle(locale, bundle), + modalObj.translateTitle(locale), modalObj.id ) { - modalObj.applyToBuilder(this, event.getLocale(), bundle) + modalObj.applyToBuilder(this, event.getLocale()) } modalObj.awaitCompletion { diff --git a/modules/dev/dev-unsafe/src/main/kotlin/dev/kordex/modules/dev/unsafe/commands/user/UnsafeUserCommand.kt b/modules/dev/dev-unsafe/src/main/kotlin/dev/kordex/modules/dev/unsafe/commands/user/UnsafeUserCommand.kt index 3bcc9f4cb2..825a56e6a0 100644 --- a/modules/dev/dev-unsafe/src/main/kotlin/dev/kordex/modules/dev/unsafe/commands/user/UnsafeUserCommand.kt +++ b/modules/dev/dev-unsafe/src/main/kotlin/dev/kordex/modules/dev/unsafe/commands/user/UnsafeUserCommand.kt @@ -101,10 +101,10 @@ public class UnsafeUserCommand( val locale = event.getLocale() event.interaction.modal( - modalObj.translateTitle(locale, bundle), + modalObj.translateTitle(locale), modalObj.id ) { - modalObj.applyToBuilder(this, event.getLocale(), bundle) + modalObj.applyToBuilder(this, event.getLocale()) } modalObj.awaitCompletion { diff --git a/modules/dev/dev-unsafe/src/main/kotlin/dev/kordex/modules/dev/unsafe/components/UnsafeComponentInteractionContext.kt b/modules/dev/dev-unsafe/src/main/kotlin/dev/kordex/modules/dev/unsafe/components/UnsafeComponentInteractionContext.kt index 8d09ca846c..11dad1e697 100644 --- a/modules/dev/dev-unsafe/src/main/kotlin/dev/kordex/modules/dev/unsafe/components/UnsafeComponentInteractionContext.kt +++ b/modules/dev/dev-unsafe/src/main/kotlin/dev/kordex/modules/dev/unsafe/components/UnsafeComponentInteractionContext.kt @@ -23,6 +23,7 @@ import dev.kord.rest.builder.message.create.FollowupMessageCreateBuilder import dev.kord.rest.builder.message.create.InteractionResponseCreateBuilder import dev.kord.rest.builder.message.modify.InteractionResponseModifyBuilder import dev.kordex.core.annotations.AlwaysPublicResponse +import dev.kordex.core.i18n.types.Key import dev.kordex.core.pagination.BaseButtonPaginator import dev.kordex.core.pagination.EphemeralResponsePaginator import dev.kordex.core.pagination.PublicFollowUpPaginator @@ -110,7 +111,7 @@ public interface UnsafeComponentInteractionContext Unit, ): BaseButtonPaginator { @@ -131,7 +132,7 @@ public interface UnsafeComponentInteractionContext Unit, ): BaseButtonPaginator { diff --git a/modules/dev/dev-unsafe/src/main/kotlin/dev/kordex/modules/dev/unsafe/components/buttons/UnsafeInteractionButton.kt b/modules/dev/dev-unsafe/src/main/kotlin/dev/kordex/modules/dev/unsafe/components/buttons/UnsafeInteractionButton.kt index 6b05ed4e77..2380936fcd 100644 --- a/modules/dev/dev-unsafe/src/main/kotlin/dev/kordex/modules/dev/unsafe/components/buttons/UnsafeInteractionButton.kt +++ b/modules/dev/dev-unsafe/src/main/kotlin/dev/kordex/modules/dev/unsafe/components/buttons/UnsafeInteractionButton.kt @@ -87,10 +87,10 @@ public open class UnsafeInteractionButton( val locale = event.getLocale() event.interaction.modal( - modalObj.translateTitle(locale, bundle), + modalObj.translateTitle(locale), modalObj.id ) { - modalObj.applyToBuilder(this, event.getLocale(), bundle) + modalObj.applyToBuilder(this, event.getLocale()) } modalObj.awaitCompletion { diff --git a/modules/dev/dev-unsafe/src/main/kotlin/dev/kordex/modules/dev/unsafe/components/menus/UnsafeSelectMenu.kt b/modules/dev/dev-unsafe/src/main/kotlin/dev/kordex/modules/dev/unsafe/components/menus/UnsafeSelectMenu.kt index c77e592234..c741ad560b 100644 --- a/modules/dev/dev-unsafe/src/main/kotlin/dev/kordex/modules/dev/unsafe/components/menus/UnsafeSelectMenu.kt +++ b/modules/dev/dev-unsafe/src/main/kotlin/dev/kordex/modules/dev/unsafe/components/menus/UnsafeSelectMenu.kt @@ -81,10 +81,10 @@ public abstract class UnsafeSelectMenu( val locale = event.getLocale() event.interaction.modal( - modalObj.translateTitle(locale, bundle), + modalObj.translateTitle(locale), modalObj.id ) { - modalObj.applyToBuilder(this, event.getLocale(), bundle) + modalObj.applyToBuilder(this, event.getLocale()) } modalObj.awaitCompletion { diff --git a/modules/dev/dev-unsafe/src/main/kotlin/dev/kordex/modules/dev/unsafe/converters/UnionConverter.kt b/modules/dev/dev-unsafe/src/main/kotlin/dev/kordex/modules/dev/unsafe/converters/UnionConverter.kt index b00b219f72..bc3be7cfc6 100644 --- a/modules/dev/dev-unsafe/src/main/kotlin/dev/kordex/modules/dev/unsafe/converters/UnionConverter.kt +++ b/modules/dev/dev-unsafe/src/main/kotlin/dev/kordex/modules/dev/unsafe/converters/UnionConverter.kt @@ -15,19 +15,21 @@ package dev.kordex.modules.dev.unsafe.converters import dev.kord.core.entity.interaction.OptionValue -import dev.kord.rest.builder.interaction.OptionsBuilder import dev.kord.rest.builder.interaction.StringChoiceBuilder import dev.kordex.core.DiscordRelayedException import dev.kordex.core.annotations.UnexpectedFunctionBehaviour import dev.kordex.core.commands.Argument import dev.kordex.core.commands.Arguments import dev.kordex.core.commands.CommandContext +import dev.kordex.core.commands.OptionWrapper import dev.kordex.core.commands.converters.* -import dev.kordex.core.i18n.DEFAULT_KORDEX_BUNDLE -import dev.kordex.core.i18n.TranslationsProvider +import dev.kordex.core.commands.wrapOption +import dev.kordex.core.i18n.generated.CoreTranslations +import dev.kordex.core.i18n.toKey +import dev.kordex.core.i18n.types.Key +import dev.kordex.core.utils.withContext import dev.kordex.modules.dev.unsafe.annotations.UnsafeAPI import dev.kordex.parser.StringParser -import org.koin.core.component.inject @UnsafeAPI private typealias GenericConverter = Converter<*, *, *, *> @@ -41,17 +43,14 @@ private typealias GenericConverter = Converter<*, *, *, *> public class UnionConverter( private val converters: Collection, - typeName: String? = null, + typeName: Key? = null, shouldThrow: Boolean = false, - override val bundle: String? = DEFAULT_KORDEX_BUNDLE, override var validator: Validator = null, ) : CoalescingConverter(shouldThrow) { - private val translations: TranslationsProvider by inject() - - override val signatureType: String = typeName ?: converters.joinToString(" | ") { - translations.translate(key = it.signatureType, bundleName = it.bundle) - } + override val signatureType: Key = typeName ?: converters.joinToString(" | ") { + signatureType.translate() + }.toKey() /** @suppress Internal validation function. **/ public fun validateUnion() { @@ -172,10 +171,9 @@ public class UnionConverter( } else -> throw DiscordRelayedException( - context.translate( - "converters.union.error.unknownConverterType", - replacements = arrayOf(converter) - ) + CoreTranslations.Converters.Union.Error.unknownConverterType + .withContext(context) + .translate(converter) ) } } @@ -183,8 +181,10 @@ public class UnionConverter( return 0 } - override suspend fun toSlashOption(arg: Argument<*>): OptionsBuilder = - StringChoiceBuilder(arg.displayName, arg.description).apply { required = true } + override suspend fun toSlashOption(arg: Argument<*>): OptionWrapper = + wrapOption(arg.displayName, arg.description) { + required = true + } override suspend fun parseOption(context: CommandContext, option: OptionValue<*>): Boolean { for (converter in converters) { @@ -230,10 +230,9 @@ public class UnionConverter( } is ListConverter<*> -> throw DiscordRelayedException( - context.translate( - "converters.union.error.unknownConverterType", - replacements = arrayOf(converter) - ) + CoreTranslations.Converters.Union.Error.unknownConverterType + .withContext(context) + .translate(converter) ) is CoalescingConverter<*> -> try { @@ -276,10 +275,9 @@ public class UnionConverter( } else -> throw DiscordRelayedException( - context.translate( - "converters.union.error.unknownConverterType", - replacements = arrayOf(converter) - ) + CoreTranslations.Converters.Union.Error.unknownConverterType + .withContext(context) + .translate(converter) ) } } @@ -298,15 +296,14 @@ public class UnionConverter( */ @UnsafeAPI public fun Arguments.union( - displayName: String, - description: String, - typeName: String? = null, + displayName: Key, + description: Key, + typeName: Key? = null, shouldThrow: Boolean = false, vararg converters: GenericConverter, - bundle: String? = null, validator: Validator = null, ): UnionConverter { - val converter: UnionConverter = UnionConverter(converters.toList(), typeName, shouldThrow, bundle, validator) + val converter: UnionConverter = UnionConverter(converters.toList(), typeName, shouldThrow, validator) converter.validateUnion() @@ -332,15 +329,14 @@ public fun Arguments.union( */ @UnsafeAPI public fun Arguments.optionalUnion( - displayName: String, - description: String, - typeName: String? = null, + displayName: Key, + description: Key, + typeName: Key? = null, shouldThrow: Boolean = false, vararg converters: GenericConverter, - bundle: String? = null, validator: Validator = null, ): OptionalCoalescingConverter { - val converter: UnionConverter = UnionConverter(converters.toList(), typeName, shouldThrow, bundle) + val converter: UnionConverter = UnionConverter(converters.toList(), typeName, shouldThrow) converter.validateUnion() diff --git a/modules/dev/dev-unsafe/src/main/kotlin/dev/kordex/modules/dev/unsafe/types/UnsafeInteractionContext.kt b/modules/dev/dev-unsafe/src/main/kotlin/dev/kordex/modules/dev/unsafe/types/UnsafeInteractionContext.kt index 79557fb629..f139beb6b2 100644 --- a/modules/dev/dev-unsafe/src/main/kotlin/dev/kordex/modules/dev/unsafe/types/UnsafeInteractionContext.kt +++ b/modules/dev/dev-unsafe/src/main/kotlin/dev/kordex/modules/dev/unsafe/types/UnsafeInteractionContext.kt @@ -21,6 +21,8 @@ import dev.kord.rest.builder.message.create.FollowupMessageCreateBuilder import dev.kord.rest.builder.message.create.InteractionResponseCreateBuilder import dev.kord.rest.builder.message.modify.InteractionResponseModifyBuilder import dev.kordex.core.annotations.AlwaysPublicResponse +import dev.kordex.core.i18n.EMPTY_KEY +import dev.kordex.core.i18n.types.Key import dev.kordex.core.pagination.BaseButtonPaginator import dev.kordex.core.pagination.builders.PaginatorBuilder import dev.kordex.modules.dev.unsafe.annotations.UnsafeAPI @@ -70,7 +72,7 @@ public interface UnsafeInteractionContext Unit, ): BaseButtonPaginator @@ -79,7 +81,7 @@ public interface UnsafeInteractionContext Unit, ): BaseButtonPaginator diff --git a/modules/functionality/func-mappings/src/main/kotlin/dev/kordex/modules/func/mappings/MappingsExtension.kt b/modules/functionality/func-mappings/src/main/kotlin/dev/kordex/modules/func/mappings/MappingsExtension.kt index 3b9e35f175..eaed0e64a3 100644 --- a/modules/functionality/func-mappings/src/main/kotlin/dev/kordex/modules/func/mappings/MappingsExtension.kt +++ b/modules/functionality/func-mappings/src/main/kotlin/dev/kordex/modules/func/mappings/MappingsExtension.kt @@ -30,6 +30,7 @@ import dev.kordex.core.components.forms.ModalForm import dev.kordex.core.extensions.Extension import dev.kordex.core.extensions.ephemeralSlashCommand import dev.kordex.core.extensions.publicSlashCommand +import dev.kordex.core.i18n.types.Key import dev.kordex.core.pagination.EXPAND_EMOJI import dev.kordex.core.pagination.PublicResponsePaginator import dev.kordex.core.pagination.pages.Page @@ -92,7 +93,6 @@ private const val PAGE_FOOTER_ICON = class MappingsExtension : Extension() { private val logger = KotlinLogging.logger { } override val name: String = MappingsPlugin.PLUGIN_ID - override val bundle: String = "kordex.func-mappings" private val guildConfig = StorageUnit( StorageType.Config, @@ -117,7 +117,7 @@ class MappingsExtension : Extension() { val yarnChannels = Channels.entries.joinToString(", ") { "`${it.readableName}`" } suspend fun slashCommand( - parentName: String, + parentName: Key, friendlyName: String, namespace: Namespace, arguments: () -> T, diff --git a/modules/functionality/func-phishing/build.gradle.kts b/modules/functionality/func-phishing/build.gradle.kts index 9847971428..a3b3520ce6 100644 --- a/modules/functionality/func-phishing/build.gradle.kts +++ b/modules/functionality/func-phishing/build.gradle.kts @@ -13,6 +13,13 @@ metadata { description = "KordEx extra module that provides anti-phishing functionality for bots" } +getTranslations( + "func-phishing", + "dev.kordex.modules.func.phishing.i18n", + "kordex.func-phishing", + "PhishingTranslations" +) + repositories { maven { name = "Sonatype Snapshots" diff --git a/modules/functionality/func-phishing/src/main/kotlin/dev/kordex/modules/func/phishing/DetectionAction.kt b/modules/functionality/func-phishing/src/main/kotlin/dev/kordex/modules/func/phishing/DetectionAction.kt index 319cf8e8c0..d7db33473d 100644 --- a/modules/functionality/func-phishing/src/main/kotlin/dev/kordex/modules/func/phishing/DetectionAction.kt +++ b/modules/functionality/func-phishing/src/main/kotlin/dev/kordex/modules/func/phishing/DetectionAction.kt @@ -8,6 +8,9 @@ package dev.kordex.modules.func.phishing +import dev.kordex.core.i18n.types.Key +import dev.kordex.modules.func.phishing.i18n.generated.PhishingTranslations + /** * Sealed class representing what should happen when a phishing link is detected. * @@ -15,16 +18,16 @@ package dev.kordex.modules.func.phishing * * @property message Message to return to the user. */ -sealed class DetectionAction(val message: String) { +sealed class DetectionAction(val message: Key) { /** Ban 'em and delete the message. **/ - object Ban : DetectionAction("you have been banned from the server") + object Ban : DetectionAction(PhishingTranslations.Actions.Ban.text) /** Delete the message. **/ - object Delete : DetectionAction("it has been deleted") + object Delete : DetectionAction(PhishingTranslations.Actions.Delete.text) /** Kick 'em and delete the message. **/ - object Kick : DetectionAction("you have been kicked from the server") + object Kick : DetectionAction(PhishingTranslations.Actions.Kick.text) /** Don't do anything, just log it in the logs channel. **/ - object LogOnly : DetectionAction("it has been logged for the server staff to review") + object LogOnly : DetectionAction(PhishingTranslations.Actions.LogOnly.text) } diff --git a/modules/functionality/func-phishing/src/main/kotlin/dev/kordex/modules/func/phishing/PhishingExtension.kt b/modules/functionality/func-phishing/src/main/kotlin/dev/kordex/modules/func/phishing/PhishingExtension.kt index 8fd908f13c..e0fba5200a 100644 --- a/modules/functionality/func-phishing/src/main/kotlin/dev/kordex/modules/func/phishing/PhishingExtension.kt +++ b/modules/functionality/func-phishing/src/main/kotlin/dev/kordex/modules/func/phishing/PhishingExtension.kt @@ -10,6 +10,7 @@ package dev.kordex.modules.func.phishing +import dev.kord.common.asJavaLocale import dev.kord.core.behavior.ban import dev.kord.core.behavior.channel.createMessage import dev.kord.core.entity.Message @@ -27,9 +28,12 @@ import dev.kordex.core.extensions.Extension import dev.kordex.core.extensions.ephemeralMessageCommand import dev.kordex.core.extensions.ephemeralSlashCommand import dev.kordex.core.extensions.event +import dev.kordex.core.i18n.generated.CoreTranslations.Extensions.Help.Paginator.Title.arguments import dev.kordex.core.utils.dm import dev.kordex.core.utils.getJumpUrl import dev.kordex.core.utils.kordExUserAgent +import dev.kordex.modules.func.phishing.i18n.generated.PhishingTranslations +import dev.kordex.modules.func.phishing.i18n.generated.PhishingTranslations.Actions.logMessage import io.github.oshai.kotlinlogging.KotlinLogging import io.ktor.client.* import io.ktor.client.plugins.* @@ -41,6 +45,7 @@ import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.lastOrNull import kotlinx.coroutines.launch import org.jsoup.Jsoup +import java.util.Locale /** The maximum number of redirects to attempt to follow for a URL. **/ const val MAX_REDIRECTS = 5 @@ -83,7 +88,7 @@ class PhishingExtension(private val settings: ExtPhishingBuilder) : Extension() } action { - handleMessage(event.message.asMessageOrNull()) + handleMessage(event.message.asMessageOrNull(), getLocale()) } } @@ -98,12 +103,12 @@ class PhishingExtension(private val settings: ExtPhishingBuilder) : Extension() } action { - handleMessage(event.message.asMessageOrNull()) + handleMessage(event.message.asMessageOrNull(), getLocale()) } } ephemeralMessageCommand { - name = "URL Safety Check" + name = PhishingTranslations.Commands.Message.name if (this@PhishingExtension.settings.requiredCommandPermission != null) { check { hasPermission(this@PhishingExtension.settings.requiredCommandPermission!!) } @@ -112,14 +117,28 @@ class PhishingExtension(private val settings: ExtPhishingBuilder) : Extension() action { for (message in targetMessages) { val matches = parseDomains(message.content) + val locale = getLocale() respond { content = if (matches.isNotEmpty()) { - "⚠️ [Message ${message.id.value}](${message.getJumpUrl()}) " + - "**contains ${matches.size} known unsafe link/s**." + PhishingTranslations.Response.Message.unsafe + .translateNamedLocale( + locale, + + "emoji" to locale.unsafeEmoji(), + "id" to message.id.value, + "url" to message.getJumpUrl(), + "matches" to matches.size + ) } else { - "✅ [Message ${message.id.value}](${message.getJumpUrl()}) " + - "**does not contain any known unsafe links**." + PhishingTranslations.Response.Message.safe + .translateNamedLocale( + locale, + + "emoji" to locale.safeEmoji(), + "id" to message.id.value, + "url" to message.getJumpUrl(), + ) } } } @@ -127,26 +146,40 @@ class PhishingExtension(private val settings: ExtPhishingBuilder) : Extension() } ephemeralSlashCommand(::DomainArgs) { - name = "url-safety-check" - description = "Check whether a given domain is a known unsafe domain." + name = PhishingTranslations.Commands.Slash.name + description = PhishingTranslations.Commands.Slash.description if (this@PhishingExtension.settings.requiredCommandPermission != null) { check { hasPermission(this@PhishingExtension.settings.requiredCommandPermission!!) } } action { + val locale = getLocale() + respond { content = if (domainCache.contains(arguments.domain.lowercase())) { - "⚠️ `${arguments.domain}` is a known unsafe domain." + PhishingTranslations.Response.Domain.unsafe + .translateNamedLocale( + locale, + + "emoji" to locale.unsafeEmoji(), + "domain" to arguments.domain, + ) } else { - "✅ `${arguments.domain}` is not a known unsafe domain." + PhishingTranslations.Response.Domain.safe + .translateNamedLocale( + locale, + + "emoji" to locale.unsafeEmoji(), + "domain" to arguments.domain, + ) } } } } } - private suspend fun handleMessage(message: Message?) { + private suspend fun handleMessage(message: Message?, locale: Locale) { if (message == null) { return } @@ -159,53 +192,62 @@ class PhishingExtension(private val settings: ExtPhishingBuilder) : Extension() if (settings.notifyUser) { message.kord.launch { message.author!!.dm { - content = "We've detected that the following message contains an unsafe domain. For this " + - "reason, **${settings.detectionAction.message}**." + content = PhishingTranslations.Notice.Message.unsafe + .translateNamedLocale( + locale, + + "action" to settings.detectionAction.message.translateLocale(locale) + ) embed { - title = "Unsafe domain detected" + title = PhishingTranslations.Embed.Logging.Unsafe.title.translateLocale(locale) description = message.content color = DISCORD_RED field { inline = true - name = "Channel" + name = PhishingTranslations.Fields.channel.translateLocale(locale) value = message.channel.mention } field { inline = true - name = "Message ID" + name = PhishingTranslations.Fields.messageId.translateLocale(locale) value = "`${message.id.value}`" } field { inline = true - name = "Server" - value = message.getGuildOrNull()?.name ?: "Unable to get guild" + name = PhishingTranslations.Fields.Server.name.translateLocale(locale) + value = message.getGuildOrNull()?.name + ?: PhishingTranslations.Fields.Server.unknown.translateLocale(locale) } } } } } + val translatedLogMessage = PhishingTranslations.Actions.logMessage + .withLocale(message.getGuildOrNull()?.preferredLocale?.asJavaLocale()) + .translate() + when (settings.detectionAction) { DetectionAction.Ban -> { message.getAuthorAsMemberOrNull()!!.ban { - reason = "Message linked to an unsafe domain" + reason = translatedLogMessage } - message.delete("Message linked to an unsafe domain") + message.delete(translatedLogMessage) } - DetectionAction.Delete -> message.delete("Message linked to an unsafe domain") + DetectionAction.Delete -> message.delete(translatedLogMessage) DetectionAction.Kick -> { - message.getAuthorAsMemberOrNull()!!.kick("Message linked to an unsafe domain") - message.delete("Message linked to an unsafe domain") + message.getAuthorAsMemberOrNull()!!.kick(translatedLogMessage) + message.delete(translatedLogMessage) } DetectionAction.LogOnly -> { @@ -213,11 +255,11 @@ class PhishingExtension(private val settings: ExtPhishingBuilder) : Extension() } } - logDeletion(message, matches) + logDeletion(message, locale, matches) } } - private suspend fun logDeletion(message: Message, matches: Set) { + private suspend fun logDeletion(message: Message, locale: Locale, matches: Set) { val guild = message.getGuild() val channel = message @@ -235,25 +277,28 @@ class PhishingExtension(private val settings: ExtPhishingBuilder) : Extension() return } - val matchList = "# Unsafe Domain Matches\n\n" + - "**Total:** ${matches.size}\n\n" + - matches.joinToString("\n") { "* `$it`" } + val matchList = PhishingTranslations.Response.Matches.header + .translateNamedLocale( + locale, + + "matches" to matches.size + ) + "\n\n" + matches.joinToString("\n") { "* `$it`" } channel.createMessage { addFile( - "matches.md", + PhishingTranslations.Response.Matches.filename.translateLocale(locale), ChannelProvider { matchList.byteInputStream().toByteReadChannel() } ) embed { - title = "Unsafe domain detected" + title = PhishingTranslations.Embed.Logging.Unsafe.title.translateLocale(locale) description = message.content color = DISCORD_RED field { inline = true - name = "Author" + name = PhishingTranslations.Fields.author.translateLocale(locale) value = "${message.author!!.mention} (" + "`${message.author!!.tag}` / " + "`${message.author!!.id.value}`" + @@ -263,21 +308,21 @@ class PhishingExtension(private val settings: ExtPhishingBuilder) : Extension() field { inline = true - name = "Channel" + name = PhishingTranslations.Fields.channel.translateLocale(locale) value = "${message.channel.mention} (`${message.channelId.value}`)" } field { inline = true - name = "Message" + name = PhishingTranslations.Fields.message.translateLocale(locale) value = "[`${message.id.value}`](${message.getJumpUrl()})" } field { inline = true - name = "Total Matches" + name = PhishingTranslations.Fields.totalMatches.translateLocale(locale) value = matches.size.toString() } } @@ -403,6 +448,12 @@ class PhishingExtension(private val settings: ExtPhishingBuilder) : Extension() websocket.stop() } + suspend fun Locale.safeEmoji() = + PhishingTranslations.Response.Emoji.safe.translateLocale(this) + + suspend fun Locale.unsafeEmoji() = + PhishingTranslations.Response.Emoji.unsafe.translateLocale(this) + private fun handleChange(change: DomainChange) { when (change.type) { DomainChangeType.Add -> domainCache.addAll(change.domains) @@ -414,11 +465,11 @@ class PhishingExtension(private val settings: ExtPhishingBuilder) : Extension() inner class DomainArgs : Arguments() { /** Targeted domain string. **/ val domain by string { - name = "domain" - description = "Domain to check" + name = PhishingTranslations.Args.Domain.name + description = PhishingTranslations.Args.Domain.description validate { - failIf("Please provide the domain name only, without the protocol or a path.") { "/" in value } + failIf(PhishingTranslations.Args.Domain.validationError) { "/" in value } } } } diff --git a/modules/functionality/func-welcome/build.gradle.kts b/modules/functionality/func-welcome/build.gradle.kts index 9ccad99388..5b1c985ec6 100644 --- a/modules/functionality/func-welcome/build.gradle.kts +++ b/modules/functionality/func-welcome/build.gradle.kts @@ -13,6 +13,13 @@ metadata { description = "KordEx extra module that provides welcome channel management driven by web-accessible YAML files" } +getTranslations( + "func-welcome", + "dev.kordex.modules.func.welcome.i18n", + "kordex.func-welcome", + "WelcomeTranslations" +) + repositories { maven { name = "Sonatype Snapshots" diff --git a/modules/functionality/func-welcome/src/main/kotlin/dev/kordex/modules/func/welcome/WelcomeExtension.kt b/modules/functionality/func-welcome/src/main/kotlin/dev/kordex/modules/func/welcome/WelcomeExtension.kt index 5b63210da7..436e5d3be5 100644 --- a/modules/functionality/func-welcome/src/main/kotlin/dev/kordex/modules/func/welcome/WelcomeExtension.kt +++ b/modules/functionality/func-welcome/src/main/kotlin/dev/kordex/modules/func/welcome/WelcomeExtension.kt @@ -31,6 +31,7 @@ import dev.kordex.core.extensions.ephemeralSlashCommand import dev.kordex.core.extensions.event import dev.kordex.modules.func.welcome.config.WelcomeChannelConfig import dev.kordex.modules.func.welcome.data.WelcomeChannelData +import dev.kordex.modules.func.welcome.i18n.generated.WelcomeTranslations import kotlinx.coroutines.flow.toList import org.koin.core.component.inject @@ -74,23 +75,24 @@ class WelcomeExtension : Extension() { } ephemeralSlashCommand { - name = "welcome-channels" - description = "Manage welcome channels" + name = WelcomeTranslations.Command.Base.name + description = WelcomeTranslations.Command.Base.description allowInDms = false config.getStaffCommandChecks().forEach(::check) ephemeralSlashCommand(::ChannelArgs) { - name = "blocks" - description = "Get a list of the configured blocks" + name = WelcomeTranslations.Command.Blocks.name + description = WelcomeTranslations.Command.Blocks.description action { val welcomeChannel = welcomeChannels[arguments.channel.id] if (welcomeChannel == null) { respond { - content = "No configuration for ${arguments.channel.mention} exists" + content = WelcomeTranslations.Responses.Config.missing + .translateLocale(getLocale(), arguments.channel.mention) } return@action @@ -101,10 +103,13 @@ class WelcomeExtension : Extension() { respond { content = buildString { if (blocks.isEmpty()) { - append("A configuration was found, but it doesn't contain any blocks.") + append( + WelcomeTranslations.Responses.Config.noBlocks + .translateLocale(getLocale()) + ) } else { blocks.forEach { - appendLine("**»** ${it.javaClass.simpleName}") + appendLine("* ${it.javaClass.simpleName}") } } } @@ -113,10 +118,11 @@ class WelcomeExtension : Extension() { } ephemeralSubCommand(::ChannelArgs) { - name = "delete" - description = "Delete a welcome channel configuration" + name = WelcomeTranslations.Command.Delete.name + description = WelcomeTranslations.Command.Delete.description action { + val locale = getLocale() val welcomeChannel = welcomeChannels[arguments.channel.id] if (welcomeChannel != null) { @@ -126,18 +132,19 @@ class WelcomeExtension : Extension() { val deletedUrl = data.removeChannel(arguments.channel.id) respond { - content = "Configuration removed - old URL was `$deletedUrl`" + content = WelcomeTranslations.Responses.Config.removed + .translateLocale(locale, deletedUrl) } welcomeChannel.log { embed { - title = "Welcome channel removed" + title = WelcomeTranslations.Embed.ChannelRemoved.title.translateLocale(locale) color = DISCORD_YELLOW - description = "Welcome channel configuration removed." + description = WelcomeTranslations.Embed.ChannelRemoved.description.translateLocale(locale) field { - name = "Channel" + name = WelcomeTranslations.Fields.channel.translateLocale(locale) value = "${welcomeChannel.channel.mention} (" + "`${welcomeChannel.channel.id}` / " + "`${welcomeChannel.channel.name}`" + @@ -145,7 +152,7 @@ class WelcomeExtension : Extension() { } field { - name = "Staff Member" + name = WelcomeTranslations.Fields.staffMember.translateLocale(locale) value = "${user.mention} (" + "`${user.id}` / " + "`${user.asUser().tag}`" + @@ -155,64 +162,63 @@ class WelcomeExtension : Extension() { } } else { respond { - content = "No configuration for ${arguments.channel.mention} exists" + content = WelcomeTranslations.Responses.Config.missing.translateLocale(locale) } } } } ephemeralSubCommand(WelcomeExtension::ChannelArgs) { - name = "get" - description = "Get the url for a welcome channel, if it's configured" + name = WelcomeTranslations.Command.Get.name + description = WelcomeTranslations.Command.Get.description action { + val locale = getLocale() val url = data.getUrlForChannel(arguments.channel.id) respond { content = if (url != null) { - "The configuration URL for ${arguments.channel.mention} is `$url`" + WelcomeTranslations.Responses.Config.get.translateLocale(locale, url) } else { - "No configuration for ${arguments.channel.mention} exists" + WelcomeTranslations.Responses.Config.missing.translateLocale(locale) } } } } ephemeralSubCommand(WelcomeExtension::ChannelRefreshArgs) { - name = "refresh" - description = "Manually repopulate the given welcome channel" + name = WelcomeTranslations.Command.Refresh.name + description = WelcomeTranslations.Command.Refresh.description action { + val locale = getLocale() val welcomeChannel = welcomeChannels[arguments.channel.id] if (welcomeChannel == null) { respond { - content = "No configuration for ${arguments.channel.mention} exists" + content = WelcomeTranslations.Responses.Config.missing.translateLocale(locale) } return@action } respond { - content = "Manually refreshing ${arguments.channel.mention} now..." + content = WelcomeTranslations.Responses.refreshingNow + .translateLocale(locale, arguments.channel.mention) } welcomeChannel.log { embed { - title = "Welcome channel refreshed" + title = WelcomeTranslations.Embed.ChannelRefreshed.title.translateLocale(locale) color = DISCORD_YELLOW - description = buildString { - append("Manually ") - - if (arguments.clear) { - append("**clearing** and ") - } - - append("refreshing welcome channel...") - } + description = if (arguments.clear) { + WelcomeTranslations.Embed.ChannelRefreshed.Description.clearing + } else { + WelcomeTranslations.Embed.ChannelRefreshed.Description.notClearing + }.translateLocale(locale) field { - name = "Channel" + name = WelcomeTranslations.Fields.channel.translateLocale(locale) value = "${welcomeChannel.channel.mention} (" + "`${welcomeChannel.channel.id}` / " + "`${welcomeChannel.channel.name}`" + @@ -220,7 +226,7 @@ class WelcomeExtension : Extension() { } field { - name = "Staff Member" + name = WelcomeTranslations.Fields.staffMember.translateLocale(locale) value = "${user.mention} (" + "`${user.id}` / " + "`${user.asUser().tag}`" + @@ -238,10 +244,11 @@ class WelcomeExtension : Extension() { } ephemeralSubCommand(WelcomeExtension::ChannelCreateArgs) { - name = "set" - description = "Set the URL for a welcome channel, and populate it" + name = WelcomeTranslations.Command.Set.name + description = WelcomeTranslations.Command.Set.description action { + val locale = getLocale() var welcomeChannel = welcomeChannels[arguments.channel.id] if (welcomeChannel != null) { @@ -255,33 +262,26 @@ class WelcomeExtension : Extension() { welcomeChannels[arguments.channel.id] = welcomeChannel respond { - content = buildString { - append("Set the configuration URL for ${arguments.channel.mention} to `${arguments.url}`, ") - - if (arguments.clear) { - append("clearing and ") - } - - append("refreshing...") - } + content = if (arguments.clear) { + WelcomeTranslations.Responses.Config.Set.clearing + } else { + WelcomeTranslations.Responses.Config.Set.notClearing + }.translateLocale(locale, arguments.channel.mention, arguments.url) } welcomeChannel.log { embed { - title = "Welcome channel created/edited" + title = WelcomeTranslations.Embed.ChannelUpdated.title.translateLocale(locale) color = DISCORD_YELLOW - description = buildString { - append("Welcome channel URL set: `${arguments.url}`") - - if (arguments.clear) { - appendLine() - appendLine("**Clearing channel...**") - } - } + description = if (arguments.clear) { + WelcomeTranslations.Embed.ChannelUpdated.Description.clearing + } else { + WelcomeTranslations.Embed.ChannelUpdated.Description.notClearing + }.translateLocale(locale, arguments.url) field { - name = "Channel" + name = WelcomeTranslations.Fields.channel.translateLocale(locale) value = "${welcomeChannel.channel.mention} (" + "`${welcomeChannel.channel.id}` / " + "`${welcomeChannel.channel.name}`" + @@ -289,7 +289,7 @@ class WelcomeExtension : Extension() { } field { - name = "Staff Member" + name = WelcomeTranslations.Fields.staffMember.translateLocale(locale) value = "${user.mention} (" + "`${user.id}` / " + "`${user.asUser().tag}`" + @@ -318,16 +318,24 @@ class WelcomeExtension : Extension() { internal class ChannelCreateArgs : Arguments() { val channel by channel { - name = "channel" - description = "Channel representing a welcome channel" + name = WelcomeTranslations.Args.Channel.name + description = WelcomeTranslations.Args.Channel.description + + validate { + failIf(WelcomeTranslations.Args.Channel.validationError) { + val guildChannel = value.asChannelOfOrNull() + + guildChannel == null || guildChannel.guildId != context.getGuild()?.id + } + } } val url by string { - name = "url" - description = "Public link to a YAML file used to configure a welcome channel" + name = WelcomeTranslations.Args.Url.name + description = WelcomeTranslations.Args.Url.description validate { - failIf("URLs must contain a protocol (eg `https://`)") { + failIf(WelcomeTranslations.Args.Url.validationError) { value.contains("://").not() || value.startsWith("://") } @@ -335,19 +343,19 @@ class WelcomeExtension : Extension() { } val clear by defaultingBoolean { - name = "clear" - description = "Whether to clear the channel before repopulating it" + name = WelcomeTranslations.Args.Clear.name + description = WelcomeTranslations.Args.Clear.description defaultValue = false } } internal class ChannelRefreshArgs : Arguments() { val channel by channel { - name = "channel" - description = "Channel representing a welcome channel" + name = WelcomeTranslations.Args.Channel.name + description = WelcomeTranslations.Args.Channel.description validate { - failIf("Given channel must be a message channel on the current server") { + failIf(WelcomeTranslations.Args.Channel.validationError) { val guildChannel = value.asChannelOfOrNull() guildChannel == null || guildChannel.guildId != context.getGuild()?.id @@ -356,19 +364,19 @@ class WelcomeExtension : Extension() { } val clear by defaultingBoolean { - name = "clear" - description = "Whether to clear the channel before repopulating it" + name = WelcomeTranslations.Args.Clear.name + description = WelcomeTranslations.Args.Clear.description defaultValue = false } } internal class ChannelArgs : Arguments() { val channel by channel { - name = "channel" - description = "Channel representing a welcome channel" + name = WelcomeTranslations.Args.Channel.name + description = WelcomeTranslations.Args.Channel.description validate { - failIf("Given channel must be a message channel on the current server") { + failIf(WelcomeTranslations.Args.Channel.validationError) { val guildChannel = value.asChannelOfOrNull() guildChannel == null || guildChannel.guildId != context.getGuild()?.id diff --git a/modules/integrations/pluralkit/src/main/kotlin/dev/kordex/modules/pluralkit/PKExtension.kt b/modules/integrations/pluralkit/src/main/kotlin/dev/kordex/modules/pluralkit/PKExtension.kt index 27d88fe456..dacdb505c8 100644 --- a/modules/integrations/pluralkit/src/main/kotlin/dev/kordex/modules/pluralkit/PKExtension.kt +++ b/modules/integrations/pluralkit/src/main/kotlin/dev/kordex/modules/pluralkit/PKExtension.kt @@ -35,6 +35,7 @@ import dev.kordex.core.commands.converters.impl.optionalUser import dev.kordex.core.extensions.Extension import dev.kordex.core.extensions.ephemeralSlashCommand import dev.kordex.core.extensions.event +import dev.kordex.core.i18n.EMPTY_VALUE_STRING import dev.kordex.core.storage.StorageType import dev.kordex.core.storage.StorageUnit import dev.kordex.core.utils.MutableStringKeyedMap @@ -45,6 +46,7 @@ import dev.kordex.modules.pluralkit.api.PluralKit import dev.kordex.modules.pluralkit.config.PKConfigBuilder import dev.kordex.modules.pluralkit.events.proxied import dev.kordex.modules.pluralkit.events.unproxied +import dev.kordex.modules.pluralkit.i18n.generated.PluralKitTranslations import dev.kordex.modules.pluralkit.storage.PKGuildConfig import dev.kordex.modules.pluralkit.utils.LRUHashMap import io.github.oshai.kotlinlogging.KotlinLogging @@ -53,6 +55,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlinx.datetime.Clock +import kotlin.text.split import kotlin.time.Duration.Companion.seconds const val NEGATIVE_EMOTE = "❌" @@ -61,7 +64,6 @@ const val POSITIVE_EMOTE = "✅" @Suppress("StringLiteralDuplication") class PKExtension(val config: PKConfigBuilder) : Extension() { override val name: String = "ext-pluralkit" - override val bundle: String = "kordex.pluralkit" private val logger = KotlinLogging.logger( "dev.kordex.modules.pluralkit.PKExtension" @@ -273,14 +275,14 @@ class PKExtension(val config: PKConfigBuilder) : Extension() { } ephemeralSlashCommand { - name = "command.pluralkit.name" - description = "command.pluralkit.description" + name = PluralKitTranslations.Command.Pluralkit.name + description = PluralKitTranslations.Command.Pluralkit.description check { anyGuild() } ephemeralSubCommand(::ApiUrlArgs) { - name = "command.pluralkit.api-url.name" - description = "command.pluralkit.api-url.description" + name = PluralKitTranslations.Command.Pluralkit.ApiUrl.name + description = PluralKitTranslations.Command.Pluralkit.ApiUrl.description check { hasPermission(Permission.ManageGuild) } @@ -291,35 +293,30 @@ class PKExtension(val config: PKConfigBuilder) : Extension() { if (arguments.url == null) { respond { - content = translate( - "command.pluralkit.api-url.response.current", - arrayOf(config.apiUrl) - ) + content = PluralKitTranslations.Command.Pluralkit.ApiUrl.Response.current + .translateLocale(getLocale(), config.apiUrl) } return@action } + val translatedResetWords = PluralKitTranslations.Arguments.reset.translateLocale(getLocale()) + val resetWords = arrayOf( "reset", - - translate("arguments.reset"), - - translationsProvider.translate( - key = "arguments.reset", - bundleName = this@ephemeralSubCommand.bundle - ) - ) + ) + if (translatedResetWords != EMPTY_VALUE_STRING) { + translatedResetWords.split(",") + } else { + listOf() + } if (arguments.url in resetWords) { config.apiUrl = PKGuildConfig().apiUrl configUnit.save(config) respond { - content = translate( - "command.pluralkit.api-url.response.reset", - arrayOf(config.apiUrl) - ) + content = PluralKitTranslations.Command.Pluralkit.ApiUrl.Response.reset + .translateLocale(getLocale(), config.apiUrl) } return@action @@ -329,17 +326,15 @@ class PKExtension(val config: PKConfigBuilder) : Extension() { configUnit.save(config) respond { - content = translate( - "command.pluralkit.api-url.response.updated", - arrayOf(config.apiUrl) - ) + content = PluralKitTranslations.Command.Pluralkit.ApiUrl.Response.updated + .translateLocale(getLocale(), config.apiUrl) } } } ephemeralSubCommand(::BotArgs) { - name = "command.pluralkit.bot.name" - description = "command.pluralkit.bot.description" + name = PluralKitTranslations.Command.Pluralkit.Bot.name + description = PluralKitTranslations.Command.Pluralkit.Bot.description check { hasPermission(Permission.ManageGuild) } @@ -350,10 +345,8 @@ class PKExtension(val config: PKConfigBuilder) : Extension() { if (arguments.bot == null) { respond { - content = translate( - "command.pluralkit.bot.response.current", - arrayOf("<@${config.botId}>") - ) + content = PluralKitTranslations.Command.Pluralkit.Bot.Response.current + .translateLocale(getLocale(), "<@${config.botId}>") } return@action @@ -363,42 +356,40 @@ class PKExtension(val config: PKConfigBuilder) : Extension() { configUnit.save(config) respond { - content = translate( - "command.pluralkit.bot.response.updated", - arrayOf("<@${config.botId}>") - ) + content = PluralKitTranslations.Command.Pluralkit.Bot.Response.updated + .translateLocale(getLocale(), "<@${config.botId}>") } } } ephemeralSubCommand { - name = "command.pluralkit.status.name" - description = "command.pluralkit.status.description" + name = PluralKitTranslations.Command.Pluralkit.Status.name + description = PluralKitTranslations.Command.Pluralkit.Status.description action { val config = guild!!.asGuild().config() respond { embed { - title = translate("command.pluralkit.status.response.title") + title = PluralKitTranslations.Command.Pluralkit.Status.Response.title + .translateLocale(getLocale()) - description = translate( - "command.pluralkit.status.response.description", + description = PluralKitTranslations.Command.Pluralkit.Status.Response.description + .translateLocale( + getLocale(), - arrayOf( config.apiUrl, "<@${config.botId}>", config.enabled.emote(), ) - ) } } } } ephemeralSubCommand(::ToggleSupportArgs) { - name = "command.pluralkit.toggle-support.name" - description = "command.pluralkit.toggle-support.description" + name = PluralKitTranslations.Command.Pluralkit.ToggleSupport.name + description = PluralKitTranslations.Command.Pluralkit.ToggleSupport.description check { hasPermission(Permission.ManageGuild) } @@ -409,10 +400,8 @@ class PKExtension(val config: PKConfigBuilder) : Extension() { if (arguments.toggle == null) { respond { - content = translate( - "command.pluralkit.toggle-support.response.current", - arrayOf(config.enabled.emote()) - ) + content = PluralKitTranslations.Command.Pluralkit.ToggleSupport.Response.current + .translateLocale(getLocale(), config.enabled.emote()) } return@action @@ -422,10 +411,8 @@ class PKExtension(val config: PKConfigBuilder) : Extension() { configUnit.save(config) respond { - content = translate( - "command.pluralkit.toggle-support.response.updated", - arrayOf(config.enabled.emote()) - ) + content = PluralKitTranslations.Command.Pluralkit.ToggleSupport.Response.updated + .translateLocale(getLocale(), config.enabled.emote()) } } } @@ -465,22 +452,22 @@ class PKExtension(val config: PKConfigBuilder) : Extension() { inner class ApiUrlArgs : Arguments() { val url by optionalString { - name = "argument.api-url.name" - description = "argument.api-url.description" + name = PluralKitTranslations.Argument.ApiUrl.name + description = PluralKitTranslations.Argument.ApiUrl.description } } inner class BotArgs : Arguments() { val bot by optionalUser { - name = "argument.bot.name" - description = "argument.bot.description" + name = PluralKitTranslations.Argument.Bot.name + description = PluralKitTranslations.Argument.Bot.description } } inner class ToggleSupportArgs : Arguments() { val toggle by optionalBoolean { - name = "argument.toggle.name" - description = "argument.toggle.description" + name = PluralKitTranslations.Argument.Toggle.name + description = PluralKitTranslations.Argument.Toggle.description } } }