fix(titles): improve title matching with accent normalization and removal aliases

This commit is contained in:
Marcos Paulo
2026-08-20 16:12:50 -03:00
parent 60989cd72f
commit aa56f385f8
2 changed files with 29 additions and 6 deletions
@@ -1530,8 +1530,10 @@ final class CanalhandiaCommand implements CommandExecutor, TabCompleter {
} }
return true; return true;
} }
String arg = String.join(" ", args); String arg = String.join(" ", args).trim();
if (arg.equalsIgnoreCase("limpar") || arg.equalsIgnoreCase("nenhum")) { if (arg.equalsIgnoreCase("limpar") || arg.equalsIgnoreCase("nenhum")
|| arg.equalsIgnoreCase("remover") || arg.equalsIgnoreCase("remove")
|| arg.equalsIgnoreCase("clear")) {
plugin.titles().clear(player.getUniqueId()); plugin.titles().clear(player.getUniqueId());
Msg.ok(player, Lang.tr("canalhandia.cmd.titulo.removido")); Msg.ok(player, Lang.tr("canalhandia.cmd.titulo.removido"));
return true; return true;
@@ -1546,20 +1548,37 @@ final class CanalhandiaCommand implements CommandExecutor, TabCompleter {
return true; return true;
} }
/** Matches typed text to an earned achievement by key or (case-insensitive) title. */ /** Matches typed text to an earned achievement by key, title, or accent-normalized text. */
static Achievement matchEarned(String text, List<Achievement> earned) { static Achievement matchEarned(String text, List<Achievement> earned) {
Achievement byKey = Achievement.byKey(text); if (text == null || text.isBlank() || earned == null || earned.isEmpty()) {
return null;
}
String clean = text.trim();
Achievement byKey = Achievement.byKey(clean);
if (byKey != null && earned.contains(byKey)) { if (byKey != null && earned.contains(byKey)) {
return byKey; return byKey;
} }
for (Achievement achievement : earned) { for (Achievement achievement : earned) {
if (achievement.title().equalsIgnoreCase(text.trim())) { if (achievement.title().equalsIgnoreCase(clean)) {
return achievement;
}
}
String normalizedClean = normalizeText(clean);
for (Achievement achievement : earned) {
if (normalizeText(achievement.title()).equalsIgnoreCase(normalizedClean)
|| normalizeText(achievement.key()).equalsIgnoreCase(normalizedClean)) {
return achievement; return achievement;
} }
} }
return null; return null;
} }
private static String normalizeText(String input) {
if (input == null) return "";
return java.text.Normalizer.normalize(input, java.text.Normalizer.Form.NFD)
.replaceAll("\\p{M}", "");
}
private static String titlesList(List<Achievement> earned) { private static String titlesList(List<Achievement> earned) {
List<String> names = new ArrayList<>(); List<String> names = new ArrayList<>();
for (Achievement achievement : earned) { for (Achievement achievement : earned) {
@@ -25,10 +25,14 @@ class TitlesTest {
void matchesEarnedTitleByKeyAndByName() { void matchesEarnedTitleByKeyAndByName() {
Achievement pedreiro = Achievement.byKey("pedreiro"); Achievement pedreiro = Achievement.byKey("pedreiro");
Achievement veterano = Achievement.byKey("veterano"); Achievement veterano = Achievement.byKey("veterano");
List<Achievement> earned = List.of(pedreiro, veterano); Achievement cacador = Achievement.byKey("cacador");
List<Achievement> earned = List.of(pedreiro, veterano, cacador);
assertSame(pedreiro, CanalhandiaCommand.matchEarned("pedreiro", earned)); assertSame(pedreiro, CanalhandiaCommand.matchEarned("pedreiro", earned));
assertSame(veterano, CanalhandiaCommand.matchEarned("Veterano", earned)); // display name, ci assertSame(veterano, CanalhandiaCommand.matchEarned("Veterano", earned)); // display name, ci
assertSame(pedreiro, CanalhandiaCommand.matchEarned("Pedreiro", earned)); assertSame(pedreiro, CanalhandiaCommand.matchEarned("Pedreiro", earned));
assertSame(cacador, CanalhandiaCommand.matchEarned("cacador", earned));
assertSame(cacador, CanalhandiaCommand.matchEarned("Caçador", earned));
assertSame(cacador, CanalhandiaCommand.matchEarned("Cacador", earned));
} }
@Test @Test