Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[Feature] Custom message types in Locale/Translator #78

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import revxrsal.commands.CommandHandler;
import revxrsal.commands.locales.Translator;

import java.text.MessageFormat;
import java.util.Locale;
import java.util.Optional;
import java.util.UUID;
Expand Down Expand Up @@ -117,8 +116,7 @@ default Translator getTranslator() {
* @param args The arguments to format with
*/
default void replyLocalized(@NotNull String key, Object... args) {
String message = MessageFormat.format(getTranslator().get(key, getLocale()), args);
reply(message);
getTranslator().reply(this, key, getLocale(), args);
}

/**
Expand All @@ -128,8 +126,7 @@ default void replyLocalized(@NotNull String key, Object... args) {
* @param args The arguments to format with
*/
default void errorLocalized(@NotNull String key, Object... args) {
String message = MessageFormat.format(getTranslator().get(key, getLocale()), args);
error(message);
getTranslator().error(this, key, getLocale(), args);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package revxrsal.commands.locales;

import revxrsal.commands.command.CommandActor;

import java.util.Locale;

/**
* A locale reader is a source in which custom typed messages for a specific locale
* are fetched. This can be a file, a resource bundle, or even a remote
* source.
*/
public interface DynamicLocaleReader<T> extends LocaleMessageTransmitter<T> {

/**
* Returns whether this reader contains a mapping for the
* given key.
*
* @param key Key to check for
* @return {@code true} if this reader has a mapping for the key
*/
boolean containsKey(String key);

/**
* Returns the mapping value for this key. It is recommended that
* this only return values if {@link #containsKey(String)} is true,
* otherwise throwing an exception to avoid confusion.
*
* @param key Key to fetch for
* @return The string value
*/

T get(String key);

/**
* Returns the locale of by this reader
*
* @return The reader's locale
*/
Locale getLocale();

default void reply(CommandActor actor, String key, Object... args) {
reply(actor, format(get(key), args));
}

default void error(CommandActor actor, String key, Object... args) {
error(actor, format(get(key), args));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package revxrsal.commands.locales;

import revxrsal.commands.command.CommandActor;

public interface LocaleMessageTransmitter<T> {
void reply(CommandActor actor, T message);

default void error(CommandActor actor, T message) {
reply(actor, message);
}

T format(T message, Object... args);
}
39 changes: 13 additions & 26 deletions common/src/main/java/revxrsal/commands/locales/LocaleReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,42 +24,29 @@
package revxrsal.commands.locales;

import org.jetbrains.annotations.NotNull;
import revxrsal.commands.command.CommandActor;

import java.util.Locale;
import java.text.MessageFormat;
import java.util.ResourceBundle;

/**
* A locale reader is a source in which messages for a specific locale
* A locale reader is a source in which plain messages for a specific locale
* are fetched. This can be a file, a resource bundle, or even a remote
* source.
*/
public interface LocaleReader {
public interface LocaleReader extends DynamicLocaleReader<String> {

/**
* Returns whether this reader contains a mapping for the
* given key.
*
* @param key Key to check for
* @return {@code true} if this reader has a mapping for the key
*/
boolean containsKey(String key);
@Override default void reply(CommandActor actor, String message) {
actor.reply(message);
}

/**
* Returns the mapping value for this key. It is recommended that
* this only return values if {@link #containsKey(String)} is true,
* otherwise throwing an exception to avoid confusion.
*
* @param key Key to fetch for
* @return The string value
*/
String get(String key);
@Override default void error(CommandActor actor, String message) {
actor.error(message);
}

/**
* Returns the locale of by this reader
*
* @return The reader's locale
*/
Locale getLocale();
@Override default String format(String message, Object... args) {
return MessageFormat.format(message, args);
}

/**
* Wraps a {@link ResourceBundle} in a {@link LocaleReader}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,15 @@
package revxrsal.commands.locales;

import org.jetbrains.annotations.NotNull;
import revxrsal.commands.command.CommandActor;

import java.util.*;

import static revxrsal.commands.util.Preconditions.notNull;

final class SimpleTranslator implements Translator {

private static final LinkedList<LocaleReader> EMPTY_LIST = new LinkedList<>();

private final Map<Locale, LinkedList<LocaleReader>> registeredBundles = new HashMap<>();
private final Map<Locale, LinkedList<DynamicLocaleReader<?>>> registeredBundles = new HashMap<>();
private volatile Locale locale = Locale.ENGLISH;

SimpleTranslator() {
Expand All @@ -50,26 +49,47 @@ final class SimpleTranslator implements Translator {
addResourceBundle("lamp-jda");
}

@Override public void reply(@NotNull CommandActor actor, @NotNull String key, @NotNull Locale locale, Object... args) {
notNull(actor, "actor");
notNull(key, "key");
notNull(locale, "locale");
notNull(args, "args");
DynamicLocaleReader<?> localeReader = findDynamicLocaleReader(key, locale)
.orElseGet(() -> findDynamicLocaleReader(key, this.locale).orElse(null));
if (localeReader == null) {
actor.reply(key);
return;
}
localeReader.reply(actor, key, args);
}

@Override public void error(@NotNull CommandActor actor, @NotNull String key, @NotNull Locale locale, Object... args) {
notNull(actor, "actor");
notNull(key, "key");
notNull(locale, "locale");
notNull(args, "args");
DynamicLocaleReader<?> localeReader = findDynamicLocaleReader(key, locale)
.orElseGet(() -> findDynamicLocaleReader(key, this.locale).orElse(null));
if (localeReader == null) {
actor.error(key);
return;
}
localeReader.error(actor, key, args);
}

@Override public @NotNull String get(@NotNull String key) {
return get(key, locale);
}

@Override public @NotNull String get(@NotNull String key, @NotNull Locale locale) {
notNull(key, "key");
notNull(locale, "locale");
for (LocaleReader registeredBundle : registeredBundles.getOrDefault(locale, EMPTY_LIST)) {
if (registeredBundle.containsKey(key))
return registeredBundle.get(key);
}
for (LocaleReader registeredBundle : registeredBundles.getOrDefault(this.locale, EMPTY_LIST)) {
if (registeredBundle.containsKey(key))
return registeredBundle.get(key);
}
return key;
return findLocaleMessage(key, locale)
.orElseGet(() -> findLocaleMessage(key, this.locale).orElse(key));
}

@Override public void add(@NotNull LocaleReader reader) {
LinkedList<LocaleReader> list = registeredBundles.computeIfAbsent(reader.getLocale(), v -> new LinkedList<>());
@Override public void add(@NotNull DynamicLocaleReader<?> reader) {
LinkedList<DynamicLocaleReader<?>> list = registeredBundles.computeIfAbsent(reader.getLocale(), v -> new LinkedList<>());
list.push(reader);
}

Expand Down Expand Up @@ -113,6 +133,26 @@ public void add(@NotNull ResourceBundle resourceBundle) {
add(LocaleReader.wrap(resourceBundle));
}

private Optional<String> findLocaleMessage(@NotNull String key, @NotNull Locale locale) {
return findLocaleReader(key, locale).map(reader -> reader.get(key));
}

private Optional<LocaleReader> findLocaleReader(@NotNull String key, @NotNull Locale locale) {
if (!registeredBundles.containsKey(locale))
return Optional.empty();
return registeredBundles.get(locale).stream()
.filter(reader -> reader.containsKey(key))
.filter(reader -> reader instanceof LocaleReader)
.map(reader -> (LocaleReader) reader)
.findFirst();
}

private Optional<DynamicLocaleReader<?>> findDynamicLocaleReader(@NotNull String key, @NotNull Locale locale) {
if (!registeredBundles.containsKey(locale))
return Optional.empty();
return registeredBundles.get(locale).stream().filter(reader -> reader.containsKey(key)).findFirst();
}

private static boolean classExists(String name) {
try {
Class.forName(name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
package revxrsal.commands.locales;

import org.jetbrains.annotations.NotNull;
import revxrsal.commands.command.CommandActor;

import java.util.Locale;
import java.util.ResourceBundle;
Expand All @@ -43,6 +44,10 @@ public interface Translator {
return new SimpleTranslator();
}

void reply(@NotNull CommandActor actor, @NotNull String key, @NotNull Locale locale, @NotNull Object... args);

void error(@NotNull CommandActor actor, @NotNull String key, @NotNull Locale locale, @NotNull Object... args);

/**
* Returns the message that corresponds to the given key, using
* the current {@link #getLocale()}. If no such message is found,
Expand All @@ -65,11 +70,11 @@ public interface Translator {
@NotNull String get(@NotNull String key, @NotNull Locale locale);

/**
* Adds the given locale reader to this translator.
* Adds the given dynamic locale reader to this translator.
*
* @param reader The locale reader to add
*/
void add(@NotNull LocaleReader reader);
void add(@NotNull DynamicLocaleReader<?> reader);

/**
* Registers the given resource bundle
Expand Down