Skip to content

Commit

Permalink
feat(core): Added distributor 3 localization
Browse files Browse the repository at this point in the history
  • Loading branch information
phinner committed Mar 21, 2024
1 parent af69ea8 commit cf158fc
Show file tree
Hide file tree
Showing 12 changed files with 499 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,12 @@ public ArcCommandManager(

this.registerDefaultExceptionHandlers();

/* TODO Set Distributor caption registry
this.captionRegistry().registerProvider((caption, sender) -> {
final var source = DistributorProvider.get().getGlobalLocalizationSource();
final var locale =
this.getBackwardsCommandSenderMapper().apply(sender).getLocale();
final var format = source.localize(caption.getKey(), locale);
return format != null ? format.toPattern() : "???" + caption.getKey() + "???";
final var source = DistributorProvider.get().getLocalizationSource();
final var locale = this.senderMapper().reverse(sender).getLocale();
final var format = source.localize(caption.key(), locale);
return format != null ? format.toPattern() : "???" + caption.key() + "???";
});
*/

this.captionFormatter((key, recipient, caption, variables) -> {
final var arguments = variables.toArray();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package com.xpdustry.distributor.core;

import com.xpdustry.distributor.core.command.CommandFacade;
import com.xpdustry.distributor.core.localization.MultiLocalizationSource;
import com.xpdustry.distributor.core.permission.PermissionManager;
import com.xpdustry.distributor.core.service.ServiceManager;

Expand All @@ -29,4 +30,6 @@ public interface Distributor {
CommandFacade.Factory getCommandFacadeFactory();

PermissionManager getPermissionManager();

MultiLocalizationSource getLocalizationSource();
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package com.xpdustry.distributor.core;

import com.xpdustry.distributor.core.command.CommandFacade;
import com.xpdustry.distributor.core.localization.MultiLocalizationSource;
import com.xpdustry.distributor.core.permission.PermissionManager;
import com.xpdustry.distributor.core.plugin.AbstractMindustryPlugin;
import com.xpdustry.distributor.core.service.ServiceManager;
Expand All @@ -31,6 +32,7 @@ public final class DistributorCorePlugin extends AbstractMindustryPlugin impleme
private final ServiceManager services = ServiceManager.simple();
private CommandFacade.@Nullable Factory factory = null;
private @Nullable PermissionManager permissions = null;
private final MultiLocalizationSource source = MultiLocalizationSource.create();

@Override
public ServiceManager getServiceManager() {
Expand All @@ -47,6 +49,11 @@ public PermissionManager getPermissionManager() {
return Objects.requireNonNull(permissions, notInitialized("permission"));
}

@Override
public MultiLocalizationSource getLocalizationSource() {
return this.source;
}

@Override
public void onInit() {
DistributorProvider.set(this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
package com.xpdustry.distributor.core.command;

import java.util.Locale;
import mindustry.gen.Player;

public interface CommandSender {
Expand All @@ -39,4 +40,6 @@ static CommandSender server() {
boolean isServer();

Player getPlayer();

Locale getLocale();
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
package com.xpdustry.distributor.core.command;

import java.util.Locale;
import mindustry.gen.Player;

record PlayerCommandSender(Player player) implements CommandSender {
Expand Down Expand Up @@ -46,4 +47,9 @@ public boolean isServer() {
public Player getPlayer() {
return this.player;
}

@Override
public Locale getLocale() {
return Locale.forLanguageTag(this.player.locale().replace('_', '-'));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package com.xpdustry.distributor.core.command;

import arc.util.Log;
import java.util.Locale;
import mindustry.gen.Player;

final class ServerCommandSender implements CommandSender {
Expand Down Expand Up @@ -55,4 +56,9 @@ public boolean isServer() {
public Player getPlayer() {
throw new UnsupportedOperationException();
}

@Override
public Locale getLocale() {
return Locale.getDefault();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Distributor, a feature-rich framework for Mindustry plugins.
*
* Copyright (C) 2024 Xpdustry
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.xpdustry.distributor.core.localization;

import java.text.MessageFormat;
import java.util.Locale;
import org.jspecify.annotations.Nullable;

/**
* A helper class for adding localization support to your plugin.
*/
public interface LocalizationSource {

/**
* Returns a {@code LocalizationSource} for the router language {@code :^)}.
*/
static LocalizationSource router() {
return RouterLocalizationSource.INSTANCE;
}

/**
* Returns the localized string for the given key or {@code null} if absent.
*
* <pre> {@code
* // Send a localized message to every player
* final LocalizationSource source = ...;
* Groups.player.each(player -> {
* final var locale = Locale.forLanguageTag(player.locale().replace('_', '-'));
* final var message = source.localize("example.key", locale);
* player.sendMessage(message == null ? "???example.key???" : message);
* }
* } </pre>
*
* @param key the key of the string to localize
* @return the localized string contained in a {@link MessageFormat}, or {@code null} if no string was found.
*/
@Nullable MessageFormat localize(final String key, final Locale locale);

/**
* Shorthand method to directly format a localized string, with a failover to a default value {@code ???key???}.
*
* <pre> {@code
* // Send a localized message to every player
* final LocalizationSource source = ...;
* Groups.player.each(player -> {
* final var locale = Locale.forLanguageTag(player.locale().replace('_', '-'));
* player.sendMessage(source.format("example.key", locale));
* }
* } </pre>
*
* @param key the key of the string to localize
* @param locale the locale to use
* @param args the arguments to pass to the {@link MessageFormat#format(Object)}
* @return the formatted string, or {@code ???key???} if no string was found.
*/
default String format(final String key, final Locale locale, final Object... args) {
final var format = this.localize(key, locale);
return format == null ? "???" + key + "???" : format.format(args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/*
* Distributor, a feature-rich framework for Mindustry plugins.
*
* Copyright (C) 2024 Xpdustry
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.xpdustry.distributor.core.localization;

import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.Map;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.function.Function;

/**
* A mutable localization source that can register localized strings.
*/
public interface LocalizationSourceRegistry extends LocalizationSource {

/**
* Creates a new {@code LocalizationSourceRegistry} instance.
*
* @param defaultLocale the default locale of the localization source
* @return a new {@code LocalizationSourceRegistry} instance
*/
static LocalizationSourceRegistry create(final Locale defaultLocale) {
return new LocalizationSourceRegistryImpl(defaultLocale);
}

/**
* Registers a map of localized strings.
*
* <pre> {@code
* final var strings = new HashMap<String, MessageFormat>();
* strings.put("example.hello", new MessageFormat("Hello {0}!", Locale.ENGLISH));
* strings.put("example.goodbye", new MessageFormat("Goodbye {0}!", Locale.ENGLISH));
* registry.registerAll(Locale.ENGLISH, strings);
* } </pre>
*
* @param locale the locale to register the strings to
* @param formats the map of localized strings
* @throws IllegalArgumentException if a key is already registered
*/
default void registerAll(final Locale locale, final Map<String, MessageFormat> formats) {
this.registerAll(locale, formats.keySet(), formats::get);
}

/**
* Registers a resource bundle of localized strings.
*
* @param locale the locale to register the strings to
* @param bundle the resource bundle to use
* @throws IllegalArgumentException if a key is already registered
*/
default void registerAll(final Locale locale, final ResourceBundle bundle) {
this.registerAll(locale, bundle.keySet(), key -> new MessageFormat(bundle.getString(key), locale));
}

/**
* Registers a resource bundle of localized strings via the classpath.
*
* <pre> {@code
* final Plugin plugin = ...;
* registry.registerAll(Locale.ENGLISH, "bundle", plugin.getClass().getClassLoader());
* registry.registerAll(Locale.FRENCH, "bundle", plugin.getClass().getClassLoader());
* } </pre>
*
* @param locale the locale to register the strings to
* @param baseName the base name of the resource bundle
* @param loader the class loader to use
* @throws IllegalArgumentException if a key is already registered
*/
default void registerAll(final Locale locale, final String baseName, final ClassLoader loader) {
this.registerAll(locale, ResourceBundle.getBundle(baseName, locale, loader));
}

/**
* Registers a resource bundle of localized strings via a file system.
*
* <pre> {@code
* final var english = Paths.get("bundle_en.properties");
* registry.registerAll(Locale.ENGLISH, path);
* final var english = Paths.get("bundle_fr.properties");
* registry.registerAll(Locale.FRENCH, path);
* } </pre>
*
* @param locale the locale to register the strings to
* @param path the path to the bundle file
* @throws IllegalArgumentException if a key is already registered
*/
default void registerAll(final Locale locale, final Path path) throws IOException {
try (final BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
this.registerAll(locale, new PropertyResourceBundle(reader));
}
}

/**
* Registers a set of localized strings by using a mapping function to obtain each string.
*
* @param locale the locale to register the strings to
* @param keys the set of keys to register
* @param function the mapping function
* @throws IllegalArgumentException if the key is already registered
*/
default void registerAll(
final Locale locale, final Set<String> keys, final Function<String, MessageFormat> function) {
for (final var key : keys) {
this.register(key, locale, function.apply(key));
}
}

/**
* Registers a localized string.
*
* @param key the key of the string
* @param locale the locale to register the string to
* @param format the localized string
* @throws IllegalArgumentException if the key is already registered
*/
void register(final String key, final Locale locale, final MessageFormat format);

/**
* Checks if a key is already registered, for any locale.
*
* @param key the key to check
* @return {@code true} if the key is already registered, {@code false} otherwise
*/
boolean registered(final String key);

/**
* Checks if a key is already registered for a specific locale.
*
* @param key the key to check
* @param locale the locale to check
* @return {@code true} if the key is already registered for the specific locale, {@code false} otherwise
*/
boolean registered(final String key, final Locale locale);

/**
* Unregisters a localized string.
*
* @param key the key of the string
*/
void unregister(final String key);

/**
* Returns the default locale of this source.
*/
Locale getDefaultLocale();
}
Loading

0 comments on commit cf158fc

Please sign in to comment.