From d77887fd2c4b6456225aa420131220c7750082d1 Mon Sep 17 00:00:00 2001 From: davioooh Date: Wed, 18 Apr 2018 17:14:32 +0200 Subject: [PATCH] Add basic internationalization support --- .../abilitybots/api/bot/AbilityBot.java | 50 ++++++++++++------- .../abilitybots/api/objects/EndUser.java | 21 ++++++-- .../abilitybots/api/util/AbilityUtils.java | 26 ++++++++++ .../resources/default_messages.properties | 22 ++++++++ .../abilitybots/api/bot/AbilityBotTest.java | 1 + 5 files changed, 98 insertions(+), 22 deletions(-) create mode 100644 telegrambots-abilities/src/main/resources/default_messages.properties diff --git a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/bot/AbilityBot.java b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/bot/AbilityBot.java index c2024f46..fd4d4aae 100644 --- a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/bot/AbilityBot.java +++ b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/bot/AbilityBot.java @@ -103,6 +103,7 @@ public abstract class AbilityBot extends TelegramLongPollingBot { protected static final String COMMANDS = "commands"; // Messages + // TODO replace hardcoded messages... protected static final String RECOVERY_MESSAGE = "I am ready to receive the backup file. Please reply to this message with the backup file attached."; protected static final String RECOVER_SUCCESS = "I have successfully recovered."; @@ -306,7 +307,7 @@ public abstract class AbilityBot extends TelegramLongPollingBot { }) .sorted() .reduce((a, b) -> format("%s%n%s", a, b)) - .orElse("No public commands found."); + .orElse(getLocalizedMessage("ability.commands.notFound", ctx.user().locale())); silent.send(commands, ctx.chatId()); }) @@ -371,11 +372,13 @@ public abstract class AbilityBot extends TelegramLongPollingBot { if (db.recover(backupData)) { silent.send(RECOVER_SUCCESS, chatId); } else { - silent.send("Oops, something went wrong during recovery.", chatId); + silent.send(getLocalizedMessage("ability.recover.fail", + AbilityUtils.getUser(update).getLanguageCode()), chatId); } } catch (Exception e) { BotLogger.error("Could not recover DB from backup", TAG, e); - silent.send("I have failed to recover.", chatId); + silent.send(getLocalizedMessage("ability.recover.error", + AbilityUtils.getUser(update).getLanguageCode()), chatId); } }, MESSAGE, DOCUMENT, REPLY, isReplyTo(RECOVERY_MESSAGE)) .build(); @@ -411,10 +414,10 @@ public abstract class AbilityBot extends TelegramLongPollingBot { Set blacklist = blacklist(); if (blacklist.contains(userId)) - silent.sendMd(format("%s is already *banned*.", escape(bannedUser)), ctx.chatId()); + silent.sendMd(getLocalizedMessage("ability.ban.alreadyBanned", ctx.user().locale(), escape(bannedUser)), ctx.chatId()); else { blacklist.add(userId); - silent.sendMd(format("%s is now *banned*.", escape(bannedUser)), ctx.chatId()); + silent.sendMd(getLocalizedMessage("ability.ban.banned", ctx.user().locale(), escape(bannedUser)), ctx.chatId()); } }) .post(commitTo(db)) @@ -439,9 +442,9 @@ public abstract class AbilityBot extends TelegramLongPollingBot { Set blacklist = blacklist(); if (!blacklist.remove(userId)) - silent.sendMd(format("@%s is *not* on the *blacklist*.", escape(username)), ctx.chatId()); + silent.sendMd(getLocalizedMessage("ability.unban.notBanned", ctx.user().locale(), escape(username)), ctx.chatId()); else { - silent.sendMd(format("@%s, your ban has been *lifted*.", escape(username)), ctx.chatId()); + silent.sendMd(getLocalizedMessage("ability.unban.lifted", ctx.user().locale(), escape(username)), ctx.chatId()); } }) .post(commitTo(db)) @@ -463,10 +466,10 @@ public abstract class AbilityBot extends TelegramLongPollingBot { Set admins = admins(); if (admins.contains(userId)) - silent.sendMd(format("@%s is already an *admin*.", escape(username)), ctx.chatId()); + silent.sendMd(getLocalizedMessage("ability.promote.alreadyPromoted", ctx.user().locale(), escape(username)), ctx.chatId()); else { admins.add(userId); - silent.sendMd(format("@%s has been *promoted*.", escape(username)), ctx.chatId()); + silent.sendMd(getLocalizedMessage("ability.promote.promoted", ctx.user().locale(), escape(username)), ctx.chatId()); } }).post(commitTo(db)) .build(); @@ -487,9 +490,9 @@ public abstract class AbilityBot extends TelegramLongPollingBot { Set admins = admins(); if (admins.remove(userId)) { - silent.sendMd(format("@%s has been *demoted*.", escape(username)), ctx.chatId()); + silent.sendMd(getLocalizedMessage("ability.demote.demoted", ctx.user().locale(), escape(username)), ctx.chatId()); } else { - silent.sendMd(format("@%s is *not* an *admin*.", escape(username)), ctx.chatId()); + silent.sendMd(getLocalizedMessage("ability.demote.alreadyDemoted", ctx.user().locale(), escape(username)), ctx.chatId()); } }) .post(commitTo(db)) @@ -514,10 +517,10 @@ public abstract class AbilityBot extends TelegramLongPollingBot { long chatId = ctx.chatId(); if (admins.contains(id)) - silent.send("You're already my master.", chatId); + silent.send(getLocalizedMessage("ability.claim.alreadyClaimed", ctx.user().locale()), chatId); else { admins.add(id); - silent.send("You're now my master.", chatId); + silent.send(getLocalizedMessage("ability.claim.claimed", ctx.user().locale()), chatId); } } else { // This is not a joke @@ -615,7 +618,12 @@ public abstract class AbilityBot extends TelegramLongPollingBot { boolean isOk = abilityTokens == 0 || (tokens.length > 0 && tokens.length == abilityTokens); if (!isOk) - silent.send(format("Sorry, this feature requires %d additional %s.", abilityTokens, abilityTokens == 1 ? "input" : "inputs"), getChatId(trio.a())); + silent.send( + getLocalizedMessage( + "checkInput.fail", + AbilityUtils.getUser(trio.a()).getLanguageCode(), + abilityTokens, abilityTokens == 1 ? "input" : "inputs"), + getChatId(trio.a())); return isOk; } @@ -627,7 +635,12 @@ public abstract class AbilityBot extends TelegramLongPollingBot { boolean isOk = abilityLocality == ALL || locality == abilityLocality; if (!isOk) - silent.send(format("Sorry, %s-only feature.", abilityLocality.toString().toLowerCase()), getChatId(trio.a())); + silent.send( + getLocalizedMessage( + "checkLocality.fail", + AbilityUtils.getUser(trio.a()).getLanguageCode(), + abilityLocality.toString().toLowerCase()), + getChatId(trio.a())); return isOk; } @@ -642,8 +655,11 @@ public abstract class AbilityBot extends TelegramLongPollingBot { boolean isOk = privacy.compareTo(trio.b().privacy()) >= 0; if (!isOk) - silent.send("Sorry, you don't have the required access level to do that.", getChatId(trio.a())); - + silent.send( + getLocalizedMessage( + "checkPrivacy.fail", + AbilityUtils.getUser(trio.a()).getLanguageCode()), + getChatId(trio.a())); return isOk; } diff --git a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/objects/EndUser.java b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/objects/EndUser.java index 7cc08145..0c123434 100644 --- a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/objects/EndUser.java +++ b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/objects/EndUser.java @@ -1,11 +1,14 @@ package org.telegram.abilitybots.api.objects; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.base.MoreObjects; +import com.google.common.base.Strings; import org.telegram.telegrambots.api.objects.User; import java.io.Serializable; +import java.util.Locale; import java.util.Objects; import java.util.StringJoiner; @@ -27,12 +30,15 @@ public final class EndUser implements Serializable { private final String lastName; @JsonProperty("username") private final String username; + @JsonIgnore + private Locale locale; - private EndUser(Integer id, String firstName, String lastName, String username) { + private EndUser(Integer id, String firstName, String lastName, String username, Locale locale) { this.id = id; this.firstName = firstName; this.lastName = lastName; this.username = username; + this.locale = locale != null? locale : Locale.ENGLISH; } @JsonCreator @@ -40,7 +46,7 @@ public final class EndUser implements Serializable { @JsonProperty("firstName") String firstName, @JsonProperty("lastName") String lastName, @JsonProperty("username") String username) { - return new EndUser(id, firstName, lastName, username); + return new EndUser(id, firstName, lastName, username, null); } /** @@ -50,7 +56,8 @@ public final class EndUser implements Serializable { * @return an augmented end-user */ public static EndUser fromUser(User user) { - return new EndUser(user.getId(), user.getFirstName(), user.getLastName(), user.getUserName()); + Locale locale = Strings.isNullOrEmpty(user.getLanguageCode()) ? null : Locale.forLanguageTag(user.getLanguageCode()); + return new EndUser(user.getId(), user.getFirstName(), user.getLastName(), user.getUserName(), locale); } public int id() { @@ -69,6 +76,8 @@ public final class EndUser implements Serializable { return username; } + public Locale locale() { return locale; } + /** * The full name is identified as the concatenation of the first and last name, separated by a space. * This method can return an empty name if both first and last name are empty. @@ -118,12 +127,13 @@ public final class EndUser implements Serializable { return Objects.equals(id, endUser.id) && Objects.equals(firstName, endUser.firstName) && Objects.equals(lastName, endUser.lastName) && - Objects.equals(username, endUser.username); + Objects.equals(username, endUser.username) && + Objects.equals(locale, endUser.locale); } @Override public int hashCode() { - return Objects.hash(id, firstName, lastName, username); + return Objects.hash(id, firstName, lastName, username, locale); } @Override @@ -133,6 +143,7 @@ public final class EndUser implements Serializable { .add("firstName", firstName) .add("lastName", lastName) .add("username", username) + .add("locale", locale.toString()) .toString(); } } diff --git a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/util/AbilityUtils.java b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/util/AbilityUtils.java index 6e313df4..21043fb3 100644 --- a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/util/AbilityUtils.java +++ b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/util/AbilityUtils.java @@ -1,10 +1,15 @@ package org.telegram.abilitybots.api.util; +import com.google.common.base.Strings; import org.telegram.abilitybots.api.db.DBContext; import org.telegram.abilitybots.api.objects.MessageContext; import org.telegram.telegrambots.api.objects.Update; import org.telegram.telegrambots.api.objects.User; +import java.text.MessageFormat; +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; import java.util.function.Consumer; import java.util.function.Predicate; @@ -172,4 +177,25 @@ public final class AbilityUtils { public static Predicate isReplyTo(String msg) { return update -> update.getMessage().getReplyToMessage().getText().equals(msg); } + + public static String getLocalizedMessage(String messageCode, Locale locale, Object...arguments) { + ResourceBundle bundle; + if(locale == null){ + bundle = ResourceBundle.getBundle("default_messages"); + }else { + try { + bundle = ResourceBundle.getBundle("messages", locale); + } catch (MissingResourceException e) { + bundle = ResourceBundle.getBundle("default_messages"); + } + } + String message = bundle.getString(messageCode); + return MessageFormat.format(message, arguments); + } + + public static String getLocalizedMessage(String messageCode, String languageCode, Object...arguments){ + Locale locale = Strings.isNullOrEmpty(languageCode) ? null : Locale.forLanguageTag(languageCode); + return getLocalizedMessage(messageCode, locale, arguments); + } + } diff --git a/telegrambots-abilities/src/main/resources/default_messages.properties b/telegrambots-abilities/src/main/resources/default_messages.properties new file mode 100644 index 00000000..18dc77df --- /dev/null +++ b/telegrambots-abilities/src/main/resources/default_messages.properties @@ -0,0 +1,22 @@ +ability.commands.notFound=No public commands found. +ability.recover.fail=Oops, something went wrong during recovery. +ability.recover.error=I have failed to recover. + +ability.ban.alreadyBanned={0} is already *banned*. +ability.ban.banned={0} is now *banned*. + +ability.unban.notBanned=@{0} is *not* on the *blacklist*. +ability.unban.lifted=@{0}, your ban has been *lifted*. + +ability.promote.alreadyPromoted=@{0} is already an *admin*. +ability.promote.promoted=@{0} has been *promoted*. + +ability.demote.alreadyDemoted=@{0} is *not* an *admin*. +ability.demote.demoted=@{0} has been *demoted*. + +ability.claim.alreadyClaimed=You''re already my master. +ability.claim.claimed=You''re now my master. + +checkInput.fail=Sorry, this feature requires {0,number,integer} additional {1}. +checkLocality.fail=Sorry, {0}-only feature. +checkPrivacy.fail=Sorry, you don''t have the required access level to do that. diff --git a/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/bot/AbilityBotTest.java b/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/bot/AbilityBotTest.java index 29b26e01..8727e86f 100644 --- a/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/bot/AbilityBotTest.java +++ b/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/bot/AbilityBotTest.java @@ -556,6 +556,7 @@ public class AbilityBotTest { when(message.hasText()).thenReturn(true); MessageContext context = mock(MessageContext.class); when(context.chatId()).thenReturn(GROUP_ID); + when(context.user()).thenReturn(MUSER); bot.reportCommands().action().accept(context);