All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.freya022.botcommands.api.localization.context.LocalizationContext.kt Maven / Gradle / Ivy

Go to download

A Kotlin-first (and Java) framework that makes creating Discord bots a piece of cake, using the JDA library.

There is a newer version: 3.0.0-alpha.18
Show newest version
package io.github.freya022.botcommands.api.localization.context

import io.github.freya022.botcommands.api.commands.text.BaseCommandEvent
import io.github.freya022.botcommands.api.core.BContext
import io.github.freya022.botcommands.api.core.service.getService
import io.github.freya022.botcommands.api.localization.Localization
import io.github.freya022.botcommands.api.localization.LocalizationService
import io.github.freya022.botcommands.api.localization.annotations.LocalizationBundle
import io.github.freya022.botcommands.api.localization.context.LocalizationContext.Companion.create
import io.github.freya022.botcommands.internal.localization.LocalizationContextImpl
import net.dv8tion.jda.api.entities.Guild
import net.dv8tion.jda.api.entities.Message
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel
import net.dv8tion.jda.api.interactions.DiscordLocale
import net.dv8tion.jda.api.interactions.Interaction
import net.dv8tion.jda.api.interactions.InteractionHook
import net.dv8tion.jda.api.interactions.callbacks.IMessageEditCallback
import net.dv8tion.jda.api.interactions.callbacks.IReplyCallback
import net.dv8tion.jda.api.requests.restaction.MessageCreateAction
import net.dv8tion.jda.api.requests.restaction.MessageEditAction
import net.dv8tion.jda.api.requests.restaction.WebhookMessageCreateAction
import net.dv8tion.jda.api.requests.restaction.WebhookMessageEditAction
import net.dv8tion.jda.api.requests.restaction.interactions.MessageEditCallbackAction
import net.dv8tion.jda.api.requests.restaction.interactions.ReplyCallbackAction
import javax.annotation.CheckReturnValue

typealias PairEntry = Pair

/**
 * Interface helping in localizing content, supports preset localization bundles,
 * localization prefixes, and context-aware localization.
 *
 * While this interface cannot be injected, sub-interfaces can.
 *
 * @see TextLocalizationContext
 * @see AppLocalizationContext
 * @see create
 */
interface LocalizationContext {
    /**
     * The locale used when no locale is specified, the best locale is picked in this order:
     * - The [user locale][Interaction.getUserLocale]
     * - The [guild locale][Guild.getLocale]
     * - The default locale ([DiscordLocale.ENGLISH_US])
     */
    val effectiveLocale: DiscordLocale

    /**
     * Returns the localization bundle of the current context.
     *
     * The localization bundle can either come from [LocalizationBundle.value] or [withBundle].
     *
     * @return The localization bundle for this context
     *
     * @see withBundle
     */
    val localizationBundle: String

    /**
     * Returns the localization prefix of the current context.
     *
     * The localization prefix can either come from [LocalizationBundle.prefix] or [withPrefix].
     *
     * @return The localization prefix for this context, or `null` if none has been set
     *
     * @see withPrefix
     */
    val localizationPrefix: String?

    /**
     * Returns a new [TextLocalizationContext] with the specified guild locale.
     *
     * @param guildLocale The guild locale to use, or `null` to remove it
     */
    @CheckReturnValue
    fun withGuildLocale(guildLocale: DiscordLocale?): TextLocalizationContext

    /**
     * Returns a new [AppLocalizationContext] with the specified user locale.
     *
     * @param userLocale The user locale to use, or `null` to remove it
     */
    @CheckReturnValue
    fun withUserLocale(userLocale: DiscordLocale?): AppLocalizationContext

    /**
     * Returns a new localization context with the specified localization bundle.
     *
     * @param localizationBundle The localization bundle to use
     */
    @CheckReturnValue
    fun withBundle(localizationBundle: String): LocalizationContext

    /**
     * Returns a new localization context with the specified localization prefix.
     *
     * @param localizationPrefix The localization prefix to use, or `null` to remove it
     */
    @CheckReturnValue
    fun withPrefix(localizationPrefix: String?): LocalizationContext

    /**
     * Returns a new localization context with the specified localization bundle,
     * and resets the localization prefix.
     *
     * @param localizationBundle The localization bundle to use
     */
    @CheckReturnValue
    fun switchBundle(localizationBundle: String): LocalizationContext

    /**
     * Localizes the provided path, with the provided locale.
     *
     * @param locale           The [DiscordLocale] to use when fetching the localization bundle
     * @param localizationPath The path of the localization template, prefixed with [localizationPrefix][LocalizationContext.localizationPrefix]
     * @param entries          The entries to fill the template with
     */
    fun localize(locale: DiscordLocale, localizationPath: String, vararg entries: Localization.Entry): String

    /**
     * Localizes the provided path, with the provided locale, or returns `null` if the path does not exist.
     *
     * @param locale           The [DiscordLocale] to use when fetching the localization bundle
     * @param localizationPath The path of the localization template, prefixed with [localizationPrefix][LocalizationContext.localizationPrefix]
     * @param entries          The entries to fill the template with
     */
    fun localizeOrNull(locale: DiscordLocale, localizationPath: String, vararg entries: Localization.Entry): String?

    /**
     * Localizes the provided path, with the [best locale][effectiveLocale] available.
     *
     * @param localizationPath The path of the localization template, prefixed with [localizationPrefix][LocalizationContext.localizationPrefix]
     * @param entries          The entries to fill the template with
     */
    fun localize(localizationPath: String, vararg entries: Localization.Entry): String =
        localize(effectiveLocale, localizationPath, *entries)

    /**
     * Localizes the provided path, with the [best locale][effectiveLocale] available,
     * or returns `null` if the path does not exist.
     *
     * @param localizationPath The path of the localization template, prefixed with [localizationPrefix][LocalizationContext.localizationPrefix]
     * @param entries          The entries to fill the template with
     */
    fun localizeOrNull(localizationPath: String, vararg entries: Localization.Entry): String? =
        localizeOrNull(effectiveLocale, localizationPath, *entries)

    companion object {
        @JvmStatic
        @JvmOverloads
        fun create(
            context: BContext,
            localizationBundle: String,
            localizationPrefix: String? = null,
            guildLocale: DiscordLocale? = null,
            userLocale: DiscordLocale? = null
        ): AppLocalizationContext {
            return LocalizationContextImpl(
                context.getService(),
                localizationBundle,
                localizationPrefix,
                guildLocale,
                userLocale
            )
        }
    }
}

internal fun Array.mapToEntries() = Array(this.size) {
    Localization.Entry(this[it].first, this[it].second)
}

/**
 * Localizes the provided path, with the provided locale.
 *
 * @param locale           The [DiscordLocale] to use when fetching the localization bundle
 * @param localizationPath The path of the localization template, prefixed with [localizationPrefix][LocalizationContext.localizationPrefix]
 * @param entries          The entries to fill the template with
 */
fun LocalizationContext.localize(locale: DiscordLocale, localizationPath: String, vararg entries: PairEntry): String =
    localize(locale, localizationPath, *entries.mapToEntries())

/**
 * Localizes the provided path, with the provided locale, or returns `null` if the path does not exist.
 *
 * @param locale           The [DiscordLocale] to use when fetching the localization bundle
 * @param localizationPath The path of the localization template, prefixed with [localizationPrefix][LocalizationContext.localizationPrefix]
 * @param entries          The entries to fill the template with
 */
fun LocalizationContext.localizeOrNull(locale: DiscordLocale, localizationPath: String, vararg entries: PairEntry): String? =
    localizeOrNull(locale, localizationPath, *entries.mapToEntries())

/**
 * Localizes the provided path, with the [best locale][LocalizationContext.effectiveLocale] available.
 *
 * @param localizationPath The path of the localization template, prefixed with [localizationPrefix][LocalizationContext.localizationPrefix]
 * @param entries          The entries to fill the template with
 */
fun LocalizationContext.localize(localizationPath: String, vararg entries: PairEntry): String =
    localize(effectiveLocale, localizationPath, *entries.mapToEntries())

/**
 * Localizes the provided path, with the [best locale][LocalizationContext.effectiveLocale] available,
 * or returns `null` if the path does not exist.
 *
 * @param localizationPath The path of the localization template, prefixed with [localizationPrefix][LocalizationContext.localizationPrefix]
 * @param entries          The entries to fill the template with
 */
fun LocalizationContext.localizeOrNull(localizationPath: String, vararg entries: PairEntry): String? =
    localizeOrNull(effectiveLocale, localizationPath, *entries.mapToEntries())

//region Localized responses
/**
 * Sends a localized message to this channel.
 *
 * @param localizationPath The path of the localization template, prefixed with [localizationPrefix][LocalizationContext.localizationPrefix]
 * @param entries          The entries to fill the template with
 * 
 * @see MessageChannel.sendMessage
 */
fun MessageChannel.sendLocalized(context: LocalizationContext, localizationPath: String, vararg entries: PairEntry): MessageCreateAction =
    sendMessage(context.localize(localizationPath, *entries))

/**
 * Sends a localized message to the event's channel.
 *
 * @param localizationPath The path of the localization template, prefixed with [localizationPrefix][LocalizationContext.localizationPrefix]
 * @param entries          The entries to fill the template with
 *
 * @see MessageChannel.sendMessage
 */
fun BaseCommandEvent.respondLocalized(context: LocalizationContext, localizationPath: String, vararg entries: PairEntry): MessageCreateAction =
    respond(context.localize(localizationPath, *entries))

/**
 * Replies a localized message to the user's command.
 *
 * @param localizationPath The path of the localization template, prefixed with [localizationPrefix][LocalizationContext.localizationPrefix]
 * @param entries          The entries to fill the template with
 * 
 * @see MessageChannel.sendMessage
 */
fun BaseCommandEvent.replyLocalized(context: LocalizationContext, localizationPath: String, vararg entries: PairEntry): MessageCreateAction =
    reply(context.localize(localizationPath, *entries))

/**
 * Replies a localized message to this interaction and acknowledges it.
 *
 * @param localizationPath The path of the localization template, prefixed with [localizationPrefix][LocalizationContext.localizationPrefix]
 * @param entries          The entries to fill the template with
 *
 * @see IReplyCallback.reply
 */
fun IReplyCallback.replyLocalized(context: LocalizationContext, localizationPath: String, vararg entries: PairEntry): ReplyCallbackAction =
    reply(context.localize(localizationPath, *entries))

/**
 * Replies a localized ephemeral message to this interaction and acknowledges it.
 *
 * @param localizationPath The path of the localization template, prefixed with [localizationPrefix][LocalizationContext.localizationPrefix]
 * @param entries          The entries to fill the template with
 *
 * @see IReplyCallback.reply
 */
fun IReplyCallback.replyLocalizedEphemeral(context: LocalizationContext, localizationPath: String, vararg entries: PairEntry): ReplyCallbackAction =
    reply(context.localize(localizationPath, *entries)).setEphemeral(true)

/**
 * Sends a localized followup message to this interaction hook.
 *
 * If the interaction was originally [deferred][IReplyCallback.deferReply],
 * then the ephemeral-ness of this message depends on what was passed there.
 *
 * @param localizationPath The path of the localization template, prefixed with [localizationPrefix][LocalizationContext.localizationPrefix]
 * @param entries          The entries to fill the template with
 *
 * @see InteractionHook.sendMessage
 */
fun InteractionHook.sendLocalized(context: LocalizationContext, localizationPath: String, vararg entries: PairEntry): WebhookMessageCreateAction =
    sendMessage(context.localize(localizationPath, *entries))

/**
 * Sends a localized ephemeral followup message to this interaction hook.
 *
 * If the interaction was originally [deferred][IReplyCallback.deferReply],
 * then the ephemeral-ness of this message depends on what was passed there.
 *
 * @param localizationPath The path of the localization template, prefixed with [localizationPrefix][LocalizationContext.localizationPrefix]
 * @param entries          The entries to fill the template with
 *
 * @see InteractionHook.sendMessage
 */
fun InteractionHook.sendLocalizedEphemeral(context: LocalizationContext, localizationPath: String, vararg entries: PairEntry): WebhookMessageCreateAction =
    sendMessage(context.localize(localizationPath, *entries)).setEphemeral(true)
//endregion

//region Localized edits
/**
 * Edits the text content of the original message with a localized message.
 *
 * @param localizationPath The path of the localization template, prefixed with [localizationPrefix][LocalizationContext.localizationPrefix]
 * @param entries          The entries to fill the template with
 *
 * @see IMessageEditCallback.editMessage
 */
fun IMessageEditCallback.editLocalized(context: LocalizationContext, localizationPath: String, vararg entries: PairEntry): MessageEditCallbackAction =
    editMessage(context.localize(localizationPath, *entries))

/**
 * Replaces the entire original message with a localized message.
 *
 * @param localizationPath The path of the localization template, prefixed with [localizationPrefix][LocalizationContext.localizationPrefix]
 * @param entries          The entries to fill the template with
 *
 * @see IMessageEditCallback.editMessage
 */
fun IMessageEditCallback.replaceLocalized(context: LocalizationContext, localizationPath: String, vararg entries: PairEntry): MessageEditCallbackAction =
    editMessage(context.localize(localizationPath, *entries)).setReplace(true)

/**
 * Edits the text content of the original message with a localized message.
 *
 * @param localizationPath The path of the localization template, prefixed with [localizationPrefix][LocalizationContext.localizationPrefix]
 * @param entries          The entries to fill the template with
 *
 * @see InteractionHook.editOriginal
 */
fun InteractionHook.editLocalized(context: LocalizationContext, localizationPath: String, vararg entries: PairEntry): WebhookMessageEditAction =
    editOriginal(context.localize(localizationPath, *entries))

/**
 * Replaces the entire original message with a localized message.
 *
 * @param localizationPath The path of the localization template, prefixed with [localizationPrefix][LocalizationContext.localizationPrefix]
 * @param entries          The entries to fill the template with
 *
 * @see InteractionHook.editOriginal
 */
fun InteractionHook.replaceLocalized(context: LocalizationContext, localizationPath: String, vararg entries: PairEntry): WebhookMessageEditAction =
    editOriginal(context.localize(localizationPath, *entries)).setReplace(true)

/**
 * Edits the text content of the message with a localized message.
 *
 * @param localizationPath The path of the localization template, prefixed with [localizationPrefix][LocalizationContext.localizationPrefix]
 * @param entries          The entries to fill the template with
 *
 * @see Message.editMessage
 */
fun Message.editLocalized(context: LocalizationContext, localizationPath: String, vararg entries: PairEntry): MessageEditAction =
    editMessage(context.localize(localizationPath, *entries))

/**
 * Replaces the entire message with a localized message.
 *
 * @param localizationPath The path of the localization template, prefixed with [localizationPrefix][LocalizationContext.localizationPrefix]
 * @param entries          The entries to fill the template with
 *
 * @see Message.editMessage
 */
fun Message.replaceLocalized(context: LocalizationContext, localizationPath: String, vararg entries: PairEntry): MessageEditAction =
    editMessage(context.localize(localizationPath, *entries)).setReplace(true)
//endregion




© 2015 - 2024 Weber Informatics LLC | Privacy Policy