Start of I18N work

This commit is contained in:
Aikar
2017-06-26 23:03:18 -04:00
parent 3a4ebe2d45
commit f2e96ea244
15 changed files with 329 additions and 33 deletions
@@ -23,6 +23,7 @@
package co.aikar.commands;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@@ -45,8 +46,15 @@ public class BukkitCommandIssuer implements CommandIssuer {
}
@Override
public void sendMessage(String message) {
sender.sendMessage(ACFBukkitUtil.color(message));
public void sendMessage(MessageType type, String message) {
switch (type) {
case ERROR:
case SYNTAX:
sender.sendMessage(ChatColor.RED + ACFBukkitUtil.color(message));
break;
default:
sender.sendMessage(ChatColor.YELLOW + ACFBukkitUtil.color(message));
}
}
@Override
@@ -23,6 +23,7 @@
package co.aikar.commands;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.ProxiedPlayer;
@@ -46,8 +47,15 @@ public class BungeeCommandIssuer implements CommandIssuer{
}
@Override
public void sendMessage(String message) {
sender.sendMessage(new TextComponent(ACFBungeeUtil.color(message)));
public void sendMessage(MessageType type, String message) {
switch (type) {
case ERROR:
case SYNTAX:
sender.sendMessage(new TextComponent(ChatColor.RED + ACFBungeeUtil.color(message)));
break;
default:
sender.sendMessage(new TextComponent(ChatColor.YELLOW + ACFBungeeUtil.color(message)));
}
}
@Override
+1 -1
View File
@@ -17,6 +17,6 @@
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.guava:guava:15.0" level="project" />
<orderEntry type="library" name="Maven: org.jetbrains:annotations:13.0" level="project" />
<orderEntry type="library" name="Maven: org.jetbrains:annotations:15.0" level="project" />
</component>
</module>
+5
View File
@@ -46,6 +46,11 @@
<version>15.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>15.0</version>
</dependency>
</dependencies>
</project>
@@ -315,7 +315,7 @@ public abstract class BaseCommand {
public void execute(CommandIssuer issuer, String commandLabel, String[] args) {
if (!this.hasPermission(issuer)) {
issuer.sendMessage(permissionMessage);
issuer.sendMessage(MessageType.ERROR, permissionMessage);
return;
}
commandLabel = commandLabel.toLowerCase();
@@ -393,7 +393,7 @@ public abstract class BaseCommand {
List<String> sargs = Lists.newArrayList(args);
cmd.invoke(issuer, sargs);
} else {
issuer.sendMessage(cmd.scope.permissionMessage);
issuer.sendMessage(MessageType.ERROR, cmd.scope.permissionMessage);
}
}
@@ -489,7 +489,7 @@ public abstract class BaseCommand {
help(manager.getCommandIssuer(issuer), args);
}
public void help(CommandIssuer issuer, String[] args) {
issuer.sendMessage("&cUnknown Command, please type &f/help");
issuer.sendMessage(MessageType.ERROR, "&cUnknown Command, please type &f/help");
}
public void doHelp(Object issuer, String... args) {
doHelp(manager.getCommandIssuer(issuer), args);
@@ -499,7 +499,7 @@ public abstract class BaseCommand {
}
public void showSyntax(CommandIssuer issuer, RegisteredCommand<?> cmd) {
issuer.sendMessage("&cUsage: /" + cmd.command + " " + cmd.syntaxText);
issuer.sendMessage(MessageType.SYNTAX, "&cUsage: /" + cmd.command + " " + cmd.syntaxText);
}
public boolean hasPermission(Object issuer) {
@@ -38,7 +38,7 @@ import java.util.Map;
@SuppressWarnings("WeakerAccess")
public class CommandContexts <R extends CommandExecutionContext<?>> {
protected final Map<Class<?>, ContextResolver<?, R>> contextMap = Maps.newHashMap();
private final CommandManager manager;
protected final CommandManager manager;
CommandContexts(CommandManager manager) {
this.manager = manager;
@@ -41,7 +41,9 @@ public interface CommandIssuer {
* Send the Command Issuer a message
* @param message
*/
void sendMessage(String message);
default void sendMessage(String message) {
sendMessage(MessageType.ERROR, message);
}
/**
* Has permission node
@@ -49,4 +51,6 @@ public interface CommandIssuer {
* @return
*/
boolean hasPermission(String permission);
void sendMessage(MessageType type, String message);
}
@@ -27,6 +27,7 @@ import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@SuppressWarnings("WeakerAccess")
@@ -34,6 +35,7 @@ abstract class CommandManager {
protected Map<String, RootCommand> rootCommands = new HashMap<>();
protected CommandReplacements replacements = new CommandReplacements(this);
protected Locales locales = new Locales(this);
protected ExceptionHandler defaultExceptionHandler = null;
/**
@@ -48,6 +50,28 @@ abstract class CommandManager {
*/
public abstract CommandCompletions<?> getCommandCompletions();
/**
* Registers a command with ACF
*
* @param command The command to register
* @return boolean
*/
public abstract void registerCommand(BaseCommand command);
public abstract boolean hasRegisteredCommands();
public abstract boolean isCommandIssuer(Class<?> type);
public abstract CommandIssuer getCommandIssuer(Object issuer);
public abstract RootCommand createRootCommand(String cmd);
public abstract <R extends CommandExecutionContext> R createCommandContext(RegisteredCommand command, Parameter parameter, CommandIssuer sender, List<String> args, int i, Map<String, Object> passedArgs);
public abstract CommandCompletionContext createCompletionContext(RegisteredCommand command, CommandIssuer sender, String input, String config, String[] args);
public abstract void log(final LogLevel level, final String message);
public abstract void log(final LogLevel level, final String message, final Throwable throwable);
/**
* Lets you add custom string replacements that can be applied to annotation values,
* to reduce duplication/repetition of common values such as permission nodes and command prefixes.
@@ -62,37 +86,25 @@ abstract class CommandManager {
}
/**
* Registers a command with ACF
*
* @param command The command to register
* @return boolean
* Returns a Locales Manager to add and modify language tables for your commands.
* @return
*/
public abstract void registerCommand(BaseCommand command);
public abstract boolean hasRegisteredCommands();
public abstract boolean isCommandIssuer(Class<?> type);
Locales getLocales() {
return locales;
}
public boolean hasPermission(CommandIssuer issuer, String permission) {
return permission == null || permission.isEmpty() || issuer.hasPermission(permission);
}
public abstract CommandIssuer getCommandIssuer(Object issuer);
public abstract RootCommand createRootCommand(String cmd);
public synchronized RootCommand obtainRootCommand(String cmd) {
return rootCommands.computeIfAbsent(cmd.toLowerCase(), this::createRootCommand);
}
public abstract <R extends CommandExecutionContext> R createCommandContext(RegisteredCommand command, Parameter parameter, CommandIssuer sender, List<String> args, int i, Map<String, Object> passedArgs);
public abstract CommandCompletionContext createCompletionContext(RegisteredCommand command, CommandIssuer sender, String input, String config, String[] args);
public RegisteredCommand createRegisteredCommand(BaseCommand command, String cmdName, Method method, String prefSubCommand) {
return new RegisteredCommand(command, cmdName, method, prefSubCommand);
}
public abstract void log(final LogLevel level, final String message);
public abstract void log(final LogLevel level, final String message, final Throwable throwable);
/**
* Sets the default {@link ExceptionHandler} that is called when an exception occurs while executing a command, if the command doesn't have it's own exception handler registered.
@@ -121,4 +133,19 @@ abstract class CommandManager {
}
return result;
}
protected void sendMessage(Object issuerArg, MessageType type, MessageKey key, String... replacements) {
CommandIssuer issuer = issuerArg instanceof CommandIssuer ? (CommandIssuer) issuerArg : getCommandIssuer(issuerArg);
Locale locale = getIssuerLocale(issuer);
String message = getLocales().getMessage(locale, key);
if (replacements.length > 0) {
message = ACFUtil.replaceStrings(message, replacements);
}
// TODO: Colors?
issuer.sendMessage(type, message);
}
public Locale getIssuerLocale(CommandIssuer issuer) {
return getLocales().getDefaultLocale();
}
}
@@ -0,0 +1,52 @@
/*
* Copyright (c) 2016-2017 Daniel Ennis (Aikar) - MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package co.aikar.commands;
import com.google.common.collect.Maps;
import org.jetbrains.annotations.NotNull;
import java.util.Locale;
import java.util.Map;
class LanguageTable {
private final Locale locale;
private final Map<MessageKey, String> messages = Maps.newHashMap();
LanguageTable(Locale locale) {
this.locale = locale;
}
public String addMessage(MessageKey key, String message) {
return messages.put(key, message);
}
public String getMessage(MessageKey key) {
return messages.get(key);
}
public void addMessages(@NotNull Map<MessageKey, String> messages) {
this.messages.putAll(messages);
}
}
@@ -0,0 +1,97 @@
/*
* Copyright (c) 2016-2017 Daniel Ennis (Aikar) - MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package co.aikar.commands;
import com.google.common.collect.Maps;
import org.jetbrains.annotations.NotNull;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
/**
* This isn't public yet, still WIP - API will break
* @deprecated
*/
@SuppressWarnings("WeakerAccess")
@Deprecated
public class Locales {
private Locale defaultLocale = Locale.ENGLISH;
private final CommandManager manager;
private final Map<Locale, LanguageTable> tables = Maps.newHashMap();
Locales(CommandManager manager) {
this.manager = manager;
this.initializeSystemMessages();
}
private void initializeSystemMessages() {
LanguageTable table = getTable(Locale.ENGLISH);
//table.addMessage(MessageKey.FOO, "bar");
}
/**
* Changes the default locale to use if the specified language key is missing for the desired locale
* @param locale
* @return Previous default locale
*/
public Locale setDefaultLocale(Locale locale) {
Locale prev = this.defaultLocale;
this.defaultLocale = locale;
return prev;
}
public Locale getDefaultLocale() {
return defaultLocale;
}
public void addMessages(Locale locale, @NotNull Map<MessageKey, String> messages) {
getTable(locale).addMessages(messages);
}
public String addMessage(Locale locale, MessageKey key, String message) {
return getTable(locale).addMessage(key, message);
}
public String getMessage(Locale locale, MessageKey key) {
String message = getTable(locale).getMessage(key);
if (message == null && !Objects.equals(locale, defaultLocale)) {
message = getTable(defaultLocale).getMessage(key);
}
if (message == null && !Objects.equals(Locale.ENGLISH, defaultLocale) && !Objects.equals(Locale.ENGLISH, locale)) {
message = getTable(Locale.ENGLISH).getMessage(key);
}
if (message == null) {
manager.log(LogLevel.ERROR, "Missing Language Key: " + key);
message = "<MISSING_LANGUAGE_KEY:" + key + ">";
}
return message;
}
public LanguageTable getTable(Locale locale) {
return tables.computeIfAbsent(locale, (l) -> new LanguageTable(locale));
}
}
@@ -0,0 +1,56 @@
/*
* Copyright (c) 2016-2017 Daniel Ennis (Aikar) - MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package co.aikar.commands;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
public class MessageKey {
private static final AtomicInteger counter = new AtomicInteger();
private static final Map<String, MessageKey> keyMap = new ConcurrentHashMap<>();
private final int id = counter.getAndIncrement();
private final String key;
private MessageKey(String key) {
this.key = key;
}
public static MessageKey of(String key) {
return keyMap.computeIfAbsent(key.toLowerCase(), MessageKey::new);
}
public int hashCode() {
return id;
}
@Override
public boolean equals(Object o) {
return (this == o);
}
public String getKey() {
return key;
}
}
@@ -0,0 +1,28 @@
/*
* Copyright (c) 2016-2017 Daniel Ennis (Aikar) - MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package co.aikar.commands;
public enum MessageType {
INFO, SYNTAX, ERROR
}
@@ -154,7 +154,7 @@ public class RegisteredCommand <R extends CommandExecutionContext<? extends Comm
}
if (e instanceof InvalidCommandArgument) {
if (e.getMessage() != null && !e.getMessage().isEmpty()) {
sender.sendMessage("&cError: " + e.getMessage());
sender.sendMessage(MessageType.ERROR, "&cError: " + e.getMessage());
}
if (((InvalidCommandArgument) e).showSyntax) {
scope.showSyntax(sender, this);
@@ -162,7 +162,7 @@ public class RegisteredCommand <R extends CommandExecutionContext<? extends Comm
} else {
boolean handeled = this.scope.manager.handleUncaughtException(scope, this, sender, args, e);
if(!handeled){
sender.sendMessage("&cI'm sorry, but there was an error performing this command.");
sender.sendMessage(MessageType.ERROR, "&cI'm sorry, but there was an error performing this command.");
}
this.scope.manager.log(LogLevel.ERROR, "Exception in command: " + command + " " + ACFUtil.join(args), e);
}
@@ -24,6 +24,7 @@
package co.aikar.acfexample;
import co.aikar.commands.BukkitCommandManager;
import co.aikar.commands.MessageType;
import com.google.common.collect.Lists;
import org.bukkit.plugin.java.JavaPlugin;
@@ -47,7 +48,7 @@ public final class ACFExample extends JavaPlugin {
));
commandManager.registerCommand(new SomeCommand().setExceptionHandler((command, registeredCommand, sender, args, t) -> {
sender.sendMessage("An error occured. This problem has been logged. Sorry for the inconvienence.");
sender.sendMessage(MessageType.ERROR, "An error occured. This problem has been logged. Sorry for the inconvienence.");
return true; // mark as handeled, default message will not be send to sender
}));
commandManager.registerCommand(new SomeCommand_ExtraSubs());
@@ -25,6 +25,9 @@ package co.aikar.commands;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.text.Text;
import org.spongepowered.api.text.format.TextColor;
import org.spongepowered.api.text.format.TextColors;
import org.spongepowered.api.text.serializer.TextSerializers;
public class SpongeCommandIssuer implements CommandIssuer {
@@ -47,8 +50,15 @@ public class SpongeCommandIssuer implements CommandIssuer {
}
@Override
public void sendMessage(final String message) {
this.source.sendMessage(TextSerializers.FORMATTING_CODE.deserialize(message));
public void sendMessage(MessageType type, String message) {
switch (type) {
case ERROR:
case SYNTAX:
this.source.sendMessage(Text.of(TextColors.RED, TextSerializers.LEGACY_FORMATTING_CODE.stripCodes(message)));
break;
default:
this.source.sendMessage(Text.of(TextColors.YELLOW, TextSerializers.LEGACY_FORMATTING_CODE.stripCodes(message)));
}
}
@Override