Skip to content

Commit

Permalink
Initial support of DynamicLocaleReader
Browse files Browse the repository at this point in the history
  • Loading branch information
bivashy committed Nov 23, 2023
1 parent 16b296a commit 6328cd2
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 47 deletions.
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

0 comments on commit 6328cd2

Please sign in to comment.