Skip to content

Commit 8198985

Browse files
committed
feat: localize help message per client locale
1 parent 8c04fa7 commit 8198985

7 files changed

Lines changed: 192 additions & 66 deletions

File tree

authme-core/src/main/java/fr/xephi/authme/command/help/HelpMessagesService.java

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,27 @@ public class HelpMessagesService {
3535
* @return the localized description
3636
*/
3737
public CommandDescription buildLocalizedDescription(CommandDescription command) {
38+
return buildLocalizedDescription(command, null);
39+
}
40+
41+
/**
42+
* Creates a copy of the supplied command description with localized messages where present,
43+
* using the specified language when available.
44+
*
45+
* @param command the command to build a localized version of
46+
* @param language the AuthMe language code (e.g. {@code "fr"}), or {@code null} for server default
47+
* @return the localized description
48+
*/
49+
public CommandDescription buildLocalizedDescription(CommandDescription command, String language) {
3850
final String path = COMMAND_PREFIX + getCommandSubPath(command);
3951
if (!helpMessagesFileHandler.hasSection(path)) {
4052
// Messages file does not have a section for this command - return the provided command
4153
return command;
4254
}
4355

4456
CommandDescription.CommandBuilder builder = CommandDescription.builder()
45-
.description(getText(path + DESCRIPTION_SUFFIX, command::getDescription))
46-
.detailedDescription(getText(path + DETAILED_DESCRIPTION_SUFFIX, command::getDetailedDescription))
57+
.description(getText(path + DESCRIPTION_SUFFIX, command::getDescription, language))
58+
.detailedDescription(getText(path + DETAILED_DESCRIPTION_SUFFIX, command::getDetailedDescription, language))
4759
.executableCommand(command.getExecutableCommand())
4860
.parent(command.getParent())
4961
.labels(command.getLabels())
@@ -52,8 +64,8 @@ public CommandDescription buildLocalizedDescription(CommandDescription command)
5264
int i = 1;
5365
for (CommandArgumentDescription argument : command.getArguments()) {
5466
String argPath = path + ".arg" + i;
55-
String label = getText(argPath + ".label", argument::getName);
56-
String description = getText(argPath + ".description", argument::getDescription);
67+
String label = getText(argPath + ".label", argument::getName, language);
68+
String description = getText(argPath + ".description", argument::getDescription, language);
5769
builder.withArgument(label, description, argument.isOptional());
5870
++i;
5971
}
@@ -64,32 +76,48 @@ public CommandDescription buildLocalizedDescription(CommandDescription command)
6476
}
6577

6678
public String getDescription(CommandDescription command) {
67-
return getText(COMMAND_PREFIX + getCommandSubPath(command) + DESCRIPTION_SUFFIX, command::getDescription);
79+
return getDescription(command, null);
80+
}
81+
82+
public String getDescription(CommandDescription command, String language) {
83+
return getText(COMMAND_PREFIX + getCommandSubPath(command) + DESCRIPTION_SUFFIX,
84+
command::getDescription, language);
6885
}
6986

7087
public String getMessage(HelpMessage message) {
7188
return helpMessagesFileHandler.getMessage(message.getKey());
7289
}
7390

91+
public String getMessage(HelpMessage message, String language) {
92+
return helpMessagesFileHandler.getMessage(message.getKey(), language);
93+
}
94+
7495
public String getMessage(HelpSection section) {
7596
return helpMessagesFileHandler.getMessage(section.getKey());
7697
}
7798

99+
public String getMessage(HelpSection section, String language) {
100+
return helpMessagesFileHandler.getMessage(section.getKey(), language);
101+
}
102+
78103
public String getMessage(DefaultPermission defaultPermission) {
79104
// e.g. {default_permissions_path}.opOnly for DefaultPermission.OP_ONLY
80105
String path = DEFAULT_PERMISSIONS_PATH + getDefaultPermissionsSubPath(defaultPermission);
81106
return helpMessagesFileHandler.getMessage(path);
82107
}
83108

109+
public String getMessage(DefaultPermission defaultPermission, String language) {
110+
String path = DEFAULT_PERMISSIONS_PATH + getDefaultPermissionsSubPath(defaultPermission);
111+
return helpMessagesFileHandler.getMessage(path, language);
112+
}
113+
84114
public static String getDefaultPermissionsSubPath(DefaultPermission defaultPermission) {
85115
return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, defaultPermission.name());
86116
}
87117

88-
private String getText(String path, Supplier<String> defaultTextGetter) {
89-
String message = helpMessagesFileHandler.getMessageIfExists(path);
90-
return message == null
91-
? defaultTextGetter.get()
92-
: message;
118+
private String getText(String path, Supplier<String> defaultTextGetter, String language) {
119+
String message = helpMessagesFileHandler.getMessageIfExists(path, language);
120+
return message == null ? defaultTextGetter.get() : message;
93121
}
94122

95123

authme-core/src/main/java/fr/xephi/authme/command/help/HelpProvider.java

Lines changed: 38 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@
77
import fr.xephi.authme.command.CommandUtils;
88
import fr.xephi.authme.command.FoundCommandResult;
99
import fr.xephi.authme.initialization.Reloadable;
10+
import fr.xephi.authme.message.PlayerLocaleResolver;
1011
import fr.xephi.authme.permission.DefaultPermission;
1112
import fr.xephi.authme.permission.PermissionNode;
1213
import fr.xephi.authme.permission.PermissionsManager;
14+
import fr.xephi.authme.settings.Settings;
1315
import org.bukkit.ChatColor;
1416
import org.bukkit.command.CommandSender;
1517

@@ -49,13 +51,15 @@ public class HelpProvider implements Reloadable {
4951

5052
private final PermissionsManager permissionsManager;
5153
private final HelpMessagesService helpMessagesService;
54+
private final Settings settings;
5255
/** int with bit flags set corresponding to the above constants for enabled sections. */
5356
private Integer enabledSections;
5457

5558
@Inject
56-
HelpProvider(PermissionsManager permissionsManager, HelpMessagesService helpMessagesService) {
59+
HelpProvider(PermissionsManager permissionsManager, HelpMessagesService helpMessagesService, Settings settings) {
5760
this.permissionsManager = permissionsManager;
5861
this.helpMessagesService = helpMessagesService;
62+
this.settings = settings;
5963
}
6064

6165
/**
@@ -77,37 +81,38 @@ private List<String> buildHelpOutput(CommandSender sender, FoundCommandResult re
7781
// Return directly if no options are enabled so we don't include the help header
7882
return lines;
7983
}
80-
String header = helpMessagesService.getMessage(HelpMessage.HEADER);
84+
String language = PlayerLocaleResolver.resolveLanguage(settings, sender);
85+
String header = helpMessagesService.getMessage(HelpMessage.HEADER, language);
8186
if (!header.isEmpty()) {
8287
lines.add(ChatColor.GOLD + header);
8388
}
8489

85-
CommandDescription command = helpMessagesService.buildLocalizedDescription(result.getCommandDescription());
90+
CommandDescription command = helpMessagesService.buildLocalizedDescription(result.getCommandDescription(), language);
8691
List<String> correctLabels = ImmutableList.copyOf(filterCorrectLabels(command, result.getLabels()));
8792

8893
if (hasFlag(SHOW_COMMAND, options)) {
89-
lines.add(ChatColor.GOLD + helpMessagesService.getMessage(HelpSection.COMMAND) + ": "
94+
lines.add(ChatColor.GOLD + helpMessagesService.getMessage(HelpSection.COMMAND, language) + ": "
9095
+ CommandUtils.buildSyntax(command, correctLabels));
9196
}
9297
if (hasFlag(SHOW_DESCRIPTION, options)) {
93-
lines.add(ChatColor.GOLD + helpMessagesService.getMessage(SHORT_DESCRIPTION) + ": "
98+
lines.add(ChatColor.GOLD + helpMessagesService.getMessage(SHORT_DESCRIPTION, language) + ": "
9499
+ ChatColor.WHITE + command.getDescription());
95100
}
96101
if (hasFlag(SHOW_LONG_DESCRIPTION, options)) {
97-
lines.add(ChatColor.GOLD + helpMessagesService.getMessage(DETAILED_DESCRIPTION) + ":");
102+
lines.add(ChatColor.GOLD + helpMessagesService.getMessage(DETAILED_DESCRIPTION, language) + ":");
98103
lines.add(ChatColor.WHITE + " " + command.getDetailedDescription());
99104
}
100105
if (hasFlag(SHOW_ARGUMENTS, options)) {
101-
addArgumentsInfo(command, lines);
106+
addArgumentsInfo(command, lines, language);
102107
}
103108
if (hasFlag(SHOW_PERMISSIONS, options) && sender != null) {
104-
addPermissionsInfo(command, sender, lines);
109+
addPermissionsInfo(command, sender, lines, language);
105110
}
106111
if (hasFlag(SHOW_ALTERNATIVES, options)) {
107-
addAlternativesInfo(command, correctLabels, lines);
112+
addAlternativesInfo(command, correctLabels, lines, language);
108113
}
109114
if (hasFlag(SHOW_CHILDREN, options)) {
110-
addChildrenInfo(command, correctLabels, lines);
115+
addChildrenInfo(command, correctLabels, lines, language);
111116
}
112117

113118
return lines;
@@ -165,14 +170,14 @@ private int flagFor(HelpSection section, int flag) {
165170
* @param command the command to generate arguments info for
166171
* @param lines the output collection to add the info to
167172
*/
168-
private void addArgumentsInfo(CommandDescription command, List<String> lines) {
173+
private void addArgumentsInfo(CommandDescription command, List<String> lines, String language) {
169174
if (command.getArguments().isEmpty()) {
170175
return;
171176
}
172177

173-
lines.add(ChatColor.GOLD + helpMessagesService.getMessage(HelpSection.ARGUMENTS) + ":");
178+
lines.add(ChatColor.GOLD + helpMessagesService.getMessage(HelpSection.ARGUMENTS, language) + ":");
174179
StringBuilder argString = new StringBuilder();
175-
String optionalText = " (" + helpMessagesService.getMessage(HelpMessage.OPTIONAL) + ")";
180+
String optionalText = " (" + helpMessagesService.getMessage(HelpMessage.OPTIONAL, language) + ")";
176181
for (CommandArgumentDescription argument : command.getArguments()) {
177182
argString.setLength(0);
178183
argString.append(" ").append(ChatColor.YELLOW).append(ChatColor.ITALIC).append(argument.getName())
@@ -192,12 +197,13 @@ private void addArgumentsInfo(CommandDescription command, List<String> lines) {
192197
* @param correctLabels labels used to access the command (sanitized)
193198
* @param lines the output collection to add the info to
194199
*/
195-
private void addAlternativesInfo(CommandDescription command, List<String> correctLabels, List<String> lines) {
200+
private void addAlternativesInfo(CommandDescription command, List<String> correctLabels, List<String> lines,
201+
String language) {
196202
if (command.getLabels().size() <= 1) {
197203
return;
198204
}
199205

200-
lines.add(ChatColor.GOLD + helpMessagesService.getMessage(HelpSection.ALTERNATIVES) + ":");
206+
lines.add(ChatColor.GOLD + helpMessagesService.getMessage(HelpSection.ALTERNATIVES, language) + ":");
201207

202208
// Label with which the command was called -> don't show it as an alternative
203209
final String usedLabel;
@@ -227,45 +233,46 @@ private void addAlternativesInfo(CommandDescription command, List<String> correc
227233
* @param sender the command sender, used to evaluate permissions
228234
* @param lines the output collection to add the info to
229235
*/
230-
private void addPermissionsInfo(CommandDescription command, CommandSender sender, List<String> lines) {
236+
private void addPermissionsInfo(CommandDescription command, CommandSender sender, List<String> lines,
237+
String language) {
231238
PermissionNode permission = command.getPermission();
232239
if (permission == null) {
233240
return;
234241
}
235-
lines.add(ChatColor.GOLD + helpMessagesService.getMessage(HelpSection.PERMISSIONS) + ":");
242+
lines.add(ChatColor.GOLD + helpMessagesService.getMessage(HelpSection.PERMISSIONS, language) + ":");
236243

237244
boolean hasPermission = permissionsManager.hasPermission(sender, permission);
238245
lines.add(String.format(" " + ChatColor.YELLOW + ChatColor.ITALIC + "%s" + ChatColor.GRAY + " (%s)",
239-
permission.getNode(), getLocalPermissionText(hasPermission)));
246+
permission.getNode(), getLocalPermissionText(hasPermission, language)));
240247

241248
// Addendum to the line to specify whether the sender has permission or not when default is OP_ONLY
242249
final DefaultPermission defaultPermission = permission.getDefaultPermission();
243250
String addendum = "";
244251
if (DefaultPermission.OP_ONLY.equals(defaultPermission)) {
245-
addendum = " (" + getLocalPermissionText(defaultPermission.evaluate(sender)) + ")";
252+
addendum = " (" + getLocalPermissionText(defaultPermission.evaluate(sender), language) + ")";
246253
}
247-
lines.add(ChatColor.GOLD + helpMessagesService.getMessage(HelpMessage.DEFAULT) + ": "
248-
+ ChatColor.GRAY + ChatColor.ITALIC + helpMessagesService.getMessage(defaultPermission) + addendum);
254+
lines.add(ChatColor.GOLD + helpMessagesService.getMessage(HelpMessage.DEFAULT, language) + ": "
255+
+ ChatColor.GRAY + ChatColor.ITALIC + helpMessagesService.getMessage(defaultPermission, language) + addendum);
249256

250257
// Evaluate if the sender has permission to the command
251258
ChatColor permissionColor;
252259
String permissionText;
253260
if (permissionsManager.hasPermission(sender, command.getPermission())) {
254261
permissionColor = ChatColor.GREEN;
255-
permissionText = getLocalPermissionText(true);
262+
permissionText = getLocalPermissionText(true, language);
256263
} else {
257264
permissionColor = ChatColor.DARK_RED;
258-
permissionText = getLocalPermissionText(false);
265+
permissionText = getLocalPermissionText(false, language);
259266
}
260267
lines.add(String.format(ChatColor.GOLD + " %s: %s" + ChatColor.ITALIC + "%s",
261-
helpMessagesService.getMessage(HelpMessage.RESULT), permissionColor, permissionText));
268+
helpMessagesService.getMessage(HelpMessage.RESULT, language), permissionColor, permissionText));
262269
}
263270

264-
private String getLocalPermissionText(boolean hasPermission) {
271+
private String getLocalPermissionText(boolean hasPermission, String language) {
265272
if (hasPermission) {
266-
return helpMessagesService.getMessage(HelpMessage.HAS_PERMISSION);
273+
return helpMessagesService.getMessage(HelpMessage.HAS_PERMISSION, language);
267274
}
268-
return helpMessagesService.getMessage(HelpMessage.NO_PERMISSION);
275+
return helpMessagesService.getMessage(HelpMessage.NO_PERMISSION, language);
269276
}
270277

271278
/**
@@ -275,16 +282,17 @@ private String getLocalPermissionText(boolean hasPermission) {
275282
* @param correctLabels the labels used to access the given command (sanitized)
276283
* @param lines the output collection to add the info to
277284
*/
278-
private void addChildrenInfo(CommandDescription command, List<String> correctLabels, List<String> lines) {
285+
private void addChildrenInfo(CommandDescription command, List<String> correctLabels, List<String> lines,
286+
String language) {
279287
if (command.getChildren().isEmpty()) {
280288
return;
281289
}
282290

283-
lines.add(ChatColor.GOLD + helpMessagesService.getMessage(HelpSection.CHILDREN) + ":");
291+
lines.add(ChatColor.GOLD + helpMessagesService.getMessage(HelpSection.CHILDREN, language) + ":");
284292
String parentCommandPath = String.join(" ", correctLabels);
285293
for (CommandDescription child : command.getChildren()) {
286294
lines.add(" /" + parentCommandPath + " " + child.getLabels().get(0)
287-
+ ChatColor.GRAY + ChatColor.ITALIC + ": " + helpMessagesService.getDescription(child));
295+
+ ChatColor.GRAY + ChatColor.ITALIC + ": " + helpMessagesService.getDescription(child, language));
288296
}
289297
}
290298

authme-core/src/main/java/fr/xephi/authme/message/AbstractMessageFileHandler.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,26 @@ public String getMessageIfExists(String key) {
9999
return configuration.getString(key);
100100
}
101101

102+
/**
103+
* Returns the message for the given key in the requested language only if it exists,
104+
* falling back to the server-configured language. Returns {@code null} if not found in either.
105+
*
106+
* @param key the key to retrieve the message for
107+
* @param language the AuthMe language code (e.g. {@code "fr"}), or {@code null} to use the server default
108+
* @return the message, or {@code null} if not available
109+
*/
110+
public String getMessageIfExists(String key, String language) {
111+
if (language == null || language.equals(getLanguage())) {
112+
return getMessageIfExists(key);
113+
}
114+
FileConfiguration config = languageCache.computeIfAbsent(language, this::loadLanguageConfiguration);
115+
if (config == UNAVAILABLE) {
116+
return getMessageIfExists(key);
117+
}
118+
String message = config.getString(key);
119+
return message != null ? message : getMessageIfExists(key);
120+
}
121+
102122
/**
103123
* Returns the message for the given key in the requested language, falling back to the
104124
* server-configured language if the requested language is unavailable or missing the key.

authme-core/src/main/java/fr/xephi/authme/message/Messages.java

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import fr.xephi.authme.mail.EmailService;
77
import fr.xephi.authme.service.BukkitService;
88
import fr.xephi.authme.settings.Settings;
9-
import fr.xephi.authme.settings.properties.PluginSettings;
109
import fr.xephi.authme.util.expiring.Duration;
1110
import org.bukkit.ChatColor;
1211
import org.bukkit.command.CommandSender;
@@ -135,21 +134,9 @@ public String formatDuration(Duration duration) {
135134
* @return The message from the file
136135
*/
137136
private String retrieveMessage(MessageKey key, CommandSender sender) {
138-
String message;
139-
String displayName;
140-
if (sender instanceof Player player) {
141-
displayName = player.getDisplayName();
142-
if (settings != null && settings.getProperty(PluginSettings.PER_PLAYER_LOCALE)) {
143-
String language = PlayerLocaleResolver.toLanguageCode(player.getLocale());
144-
message = messagesFileHandler.getMessage(key.getKey(), language);
145-
} else {
146-
message = messagesFileHandler.getMessage(key.getKey());
147-
}
148-
} else {
149-
message = messagesFileHandler.getMessage(key.getKey());
150-
displayName = sender.getName();
151-
}
152-
137+
String language = PlayerLocaleResolver.resolveLanguage(settings, sender);
138+
String message = messagesFileHandler.getMessage(key.getKey(), language);
139+
String displayName = sender instanceof Player p ? p.getDisplayName() : sender.getName();
153140
return ChatColor.translateAlternateColorCodes('&', message)
154141
.replace(NEWLINE_TAG, "\n")
155142
.replace(USERNAME_TAG, sender.getName())

authme-core/src/main/java/fr/xephi/authme/message/PlayerLocaleResolver.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
package fr.xephi.authme.message;
22

3+
import fr.xephi.authme.settings.Settings;
4+
import fr.xephi.authme.settings.properties.PluginSettings;
5+
import org.bukkit.command.CommandSender;
6+
import org.bukkit.entity.Player;
7+
38
import java.util.Map;
49

510
/**
@@ -18,6 +23,23 @@ public final class PlayerLocaleResolver {
1823
private PlayerLocaleResolver() {
1924
}
2025

26+
/**
27+
* Returns the AuthMe language code to use for the given sender, or {@code null} to use the server default.
28+
* <p>
29+
* Returns non-null only when the sender is a {@link Player} and per-player locale is enabled in settings.
30+
*
31+
* @param settings the plugin settings (may be {@code null}, treated as per-player locale disabled)
32+
* @param sender the command sender
33+
* @return the player's language code, or {@code null} to fall back to the server-configured language
34+
*/
35+
public static String resolveLanguage(Settings settings, CommandSender sender) {
36+
if (settings != null && sender instanceof Player player
37+
&& settings.getProperty(PluginSettings.PER_PLAYER_LOCALE)) {
38+
return toLanguageCode(player.getLocale());
39+
}
40+
return null;
41+
}
42+
2143
/**
2244
* Converts a Minecraft client locale string to an AuthMe language code.
2345
* <p>

0 commit comments

Comments
 (0)