diff --git a/src/main/java/world/bentobox/limits/DisplayNames.java b/src/main/java/world/bentobox/limits/DisplayNames.java new file mode 100644 index 0000000..e7d698b --- /dev/null +++ b/src/main/java/world/bentobox/limits/DisplayNames.java @@ -0,0 +1,44 @@ +package world.bentobox.limits; + +import java.util.Locale; + +import org.bukkit.NamespacedKey; +import org.bukkit.entity.EntityType; + +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.util.Util; + +/** + * Resolves the display name of a limited material or entity for a user. + * + *

Locale files may provide manual translations under + * {@code island.limits.materials.} and {@code island.limits.entities.} + * (lowercase Bukkit names). Any key without a translation falls back to the automatic + * prettified English name, so translations are entirely optional. + */ +public final class DisplayNames { + + private DisplayNames() { + } + + /** + * @param user the user whose locale is used + * @param key the material key, e.g. {@code minecraft:hopper} + * @return translated material name, or the prettified key if no translation exists + */ + public static String material(User user, NamespacedKey key) { + String translated = user.getTranslationOrNothing("island.limits.materials." + key.getKey()); + return translated == null || translated.isEmpty() ? Util.prettifyText(key.getKey()) : translated; + } + + /** + * @param user the user whose locale is used + * @param type the entity type + * @return translated entity name, or the prettified type name if no translation exists + */ + public static String entity(User user, EntityType type) { + String translated = user + .getTranslationOrNothing("island.limits.entities." + type.toString().toLowerCase(Locale.ROOT)); + return translated == null || translated.isEmpty() ? Util.prettifyText(type.toString()) : translated; + } +} diff --git a/src/main/java/world/bentobox/limits/commands/player/LimitTab.java b/src/main/java/world/bentobox/limits/commands/player/LimitTab.java index e5acaa8..94d29eb 100644 --- a/src/main/java/world/bentobox/limits/commands/player/LimitTab.java +++ b/src/main/java/world/bentobox/limits/commands/player/LimitTab.java @@ -26,6 +26,7 @@ import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.util.Util; +import world.bentobox.limits.DisplayNames; import world.bentobox.limits.EntityGroup; import world.bentobox.limits.Limits; import world.bentobox.limits.objects.IslandBlockCount; @@ -112,7 +113,7 @@ private void addEntityGroupLimits(IslandBlockCount ibc) { PanelItemBuilder pib = new PanelItemBuilder(); pib.name(user.getTranslation("island.limits.panel.entity-group-name-syntax", TextVariables.NAME, g.getName())); - String description = "(" + prettyNames(g) + ")\n"; + String description = "(" + prettyNames(user, g) + ")\n"; pib.icon(g.getIcon()); int count = ibc == null ? 0 : sumGroupCount(ibc, g); String color = count >= limit ? user.getTranslation(MAX_COLOR_KEY) @@ -144,7 +145,7 @@ private void addEntityLimits(IslandBlockCount ibc) { map.forEach((k, v) -> { PanelItemBuilder pib = new PanelItemBuilder(); pib.name(user.getTranslation("island.limits.panel.entity-name-syntax", TextVariables.NAME, - Util.prettifyText(k.toString()))); + DisplayNames.entity(user, k))); Material m; try { if (E2M.containsKey(k)) { @@ -172,7 +173,7 @@ private void addMaterialIcons(IslandBlockCount ibc, Map for (Entry en : matLimits.entrySet()) { PanelItemBuilder pib = new PanelItemBuilder(); pib.name(user.getTranslation("island.limits.panel.block-name-syntax", TextVariables.NAME, - Util.prettifyText(en.getKey().getKey()))); + DisplayNames.material(user, en.getKey()))); Material mat = Registry.MATERIAL.get(B2M.getOrDefault(en.getKey(), en.getKey())); pib.icon(Objects.requireNonNullElse(mat, Material.PAPER)); @@ -222,11 +223,11 @@ public String getPermission() { return ""; } - private String prettyNames(EntityGroup v) { + private String prettyNames(User user, EntityGroup v) { StringBuilder sb = new StringBuilder(); List l = new ArrayList<>(v.getTypes()); for (int i = 0; i < l.size(); i++) { - sb.append(Util.prettifyText(l.get(i).toString())); + sb.append(DisplayNames.entity(user, l.get(i))); if (i + 1 < l.size()) sb.append(", "); if ((i + 1) % 5 == 0) sb.append("\n"); } diff --git a/src/main/java/world/bentobox/limits/listeners/BlockLimitsListener.java b/src/main/java/world/bentobox/limits/listeners/BlockLimitsListener.java index 41c2ebb..4206bb4 100644 --- a/src/main/java/world/bentobox/limits/listeners/BlockLimitsListener.java +++ b/src/main/java/world/bentobox/limits/listeners/BlockLimitsListener.java @@ -52,6 +52,7 @@ import world.bentobox.bentobox.database.Database; import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.util.Util; +import world.bentobox.limits.DisplayNames; import world.bentobox.limits.Limits; import world.bentobox.limits.Settings; import world.bentobox.limits.objects.IslandBlockCount; @@ -307,7 +308,7 @@ private void notify(Cancellable e, User user, int limit, Material m) { if (limit > -1) { if (addon.getSettings().isShowLimitMessages()) { user.notify("block-limits.hit-limit", - "[material]", Util.prettifyText(m.toString()), + "[material]", DisplayNames.material(user, m.getKey()), TextVariables.NUMBER, String.valueOf(limit)); } e.setCancelled(true); diff --git a/src/main/java/world/bentobox/limits/listeners/EntityLimitListener.java b/src/main/java/world/bentobox/limits/listeners/EntityLimitListener.java index e6f91f9..7226811 100644 --- a/src/main/java/world/bentobox/limits/listeners/EntityLimitListener.java +++ b/src/main/java/world/bentobox/limits/listeners/EntityLimitListener.java @@ -28,6 +28,7 @@ import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.util.Util; import world.bentobox.limits.EntityGroup; +import world.bentobox.limits.DisplayNames; import world.bentobox.limits.Limits; import world.bentobox.limits.Settings; import world.bentobox.limits.objects.IslandBlockCount; @@ -151,15 +152,16 @@ public void onBlock(HangingPlaceEvent hangingPlaceEvent) { if (!addon.getSettings().isShowLimitMessages()) { return; } + User u = User.getInstance(player); if (res.getTypelimit() != null) { - User.getInstance(player).notify("block-limits.hit-limit", "[material]", - Util.prettifyText(hangingPlaceEvent.getEntity().getType().toString()), + u.notify("block-limits.hit-limit", "[material]", + DisplayNames.entity(u, hangingPlaceEvent.getEntity().getType()), TextVariables.NUMBER, String.valueOf(res.getTypelimit().getValue())); } else { - User.getInstance(player).notify("block-limits.hit-limit", "[material]", + u.notify("block-limits.hit-limit", "[material]", res.getGrouplimit().getKey().getName() + " (" + res.getGrouplimit().getKey().getTypes().stream() - .map(x -> Util.prettifyText(x.toString())) + .map(x -> DisplayNames.entity(u, x)) .collect(Collectors.joining(", ")) + ")", TextVariables.NUMBER, String.valueOf(res.getGrouplimit().getValue())); @@ -235,15 +237,16 @@ private void notifyEntityLimit(Player player, EntityType type, AtLimitResult res if (!addon.getSettings().isShowLimitMessages()) { return; } + User u = User.getInstance(player); if (res.getTypelimit() != null) { - User.getInstance(player).notify(ENTITY_LIMIT_HIT, ENTITY_PLACEHOLDER, - Util.prettifyText(type.toString()), TextVariables.NUMBER, + u.notify(ENTITY_LIMIT_HIT, ENTITY_PLACEHOLDER, + DisplayNames.entity(u, type), TextVariables.NUMBER, String.valueOf(res.getTypelimit().getValue())); } else { - User.getInstance(player).notify(ENTITY_LIMIT_HIT, ENTITY_PLACEHOLDER, + u.notify(ENTITY_LIMIT_HIT, ENTITY_PLACEHOLDER, res.getGrouplimit().getKey().getName() + " (" + res.getGrouplimit().getKey().getTypes().stream() - .map(x -> Util.prettifyText(x.toString())) + .map(x -> DisplayNames.entity(u, x)) .collect(Collectors.joining(", ")) + ")", TextVariables.NUMBER, String.valueOf(res.getGrouplimit().getValue())); @@ -568,15 +571,16 @@ private void tellPlayers(Location location, Entity entity, SpawnReason spawnReas for (Entity ent : w.getNearbyEntities(location, 5, 5, 5)) { if (ent instanceof Player p) { p.updateInventory(); + User u = User.getInstance(p); if (res.getTypelimit() != null) { - User.getInstance(p).notify(ENTITY_LIMIT_HIT, ENTITY_PLACEHOLDER, - Util.prettifyText(entity.getType().toString()), + u.notify(ENTITY_LIMIT_HIT, ENTITY_PLACEHOLDER, + DisplayNames.entity(u, entity.getType()), TextVariables.NUMBER, String.valueOf(res.getTypelimit().getValue())); } else { - User.getInstance(p).notify(ENTITY_LIMIT_HIT, ENTITY_PLACEHOLDER, + u.notify(ENTITY_LIMIT_HIT, ENTITY_PLACEHOLDER, res.getGrouplimit().getKey().getName() + " (" + res.getGrouplimit().getKey().getTypes().stream() - .map(x -> Util.prettifyText(x.toString())) + .map(x -> DisplayNames.entity(u, x)) .collect(Collectors.joining(", ")) + ")", TextVariables.NUMBER, String.valueOf(res.getGrouplimit().getValue())); diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml index 477425b..4fea8dc 100755 --- a/src/main/resources/locales/en-US.yml +++ b/src/main/resources/locales/en-US.yml @@ -1,69 +1,76 @@ -########################################################################################### -# This is a YML file. Be careful when editing. Check your edits in a YAML checker like # -# the one at http://yaml-online-parser.appspot.com # -########################################################################################### - -block-limits: - hit-limit: "[material] limited to [number]!" -entity-limits: - hit-limit: "[entity] spawning limited to [number]!" -limits: - panel-title: "Island limits" - -admin: - limits: - main: - parameters: "" - description: "show the island limits for player" - calc: - parameters: "" - description: "recalculate the island limits for player" - finished: " Island recalc finished successfully!" - offset: - unknown: " Unknown material or entity [name]." - description: "allows to manage limits offsets for materials and entities" - set: - parameters: " " - description: "sets new offset for material or entity limit" - success: " Limit offset for [name] is set to [number]." - same: " Limit offset for [name] is already [number]." - add: - parameters: " " - description: "adds offset for material or entity limit" - success: " Limit offset for [name] is increased till [number]." - remove: - parameters: " " - description: "reduces offset for material or entity limit" - success: " Limit offset for [name] is reduced till [number]." - reset: - parameters: " " - description: "removes offset for material or entity" - success: " Limit offset for [name] is set to 0." - view: - parameters: " " - description: "displays offset for material or entity" - message: " [name] offset is set to [number]." -island: - limits: - description: "show your island limits" - max-color: "" - regular-color: "" - block-limit-syntax: "[number]/[limit]" - no-limits: " No limits set in this world" - panel: - title-syntax: '[title] [env]' - entity-group-name-syntax: '[name]' - entity-name-syntax: '[name]' - block-name-syntax: '[name]' - env-overworld: "Overworld" - env-nether: "Nether" - env-end: "End" - errors: - no-owner: " That island has no owner" - not-on-island: " This location does not have limits set." - recount: - description: "recounts limits for your island" - now-recounting: " Now recounting. This could take a while, please wait..." - in-progress: " Island recound is in progress. Please wait..." - time-out: " Time out when recounting. Is the island really big?" - +########################################################################################### +# This is a YML file. Be careful when editing. Check your edits in a YAML checker like # +# the one at http://yaml-online-parser.appspot.com # +########################################################################################### + +block-limits: + hit-limit: "[material] limited to [number]!" +entity-limits: + hit-limit: "[entity] spawning limited to [number]!" +limits: + panel-title: "Island limits" + +admin: + limits: + main: + parameters: "" + description: "show the island limits for player" + calc: + parameters: "" + description: "recalculate the island limits for player" + finished: " Island recalc finished successfully!" + offset: + unknown: " Unknown material or entity [name]." + description: "allows to manage limits offsets for materials and entities" + set: + parameters: " " + description: "sets new offset for material or entity limit" + success: " Limit offset for [name] is set to [number]." + same: " Limit offset for [name] is already [number]." + add: + parameters: " " + description: "adds offset for material or entity limit" + success: " Limit offset for [name] is increased till [number]." + remove: + parameters: " " + description: "reduces offset for material or entity limit" + success: " Limit offset for [name] is reduced till [number]." + reset: + parameters: " " + description: "removes offset for material or entity" + success: " Limit offset for [name] is set to 0." + view: + parameters: " " + description: "displays offset for material or entity" + message: " [name] offset is set to [number]." +island: + limits: + description: "show your island limits" + # Optional manual name translations, used in the limits GUI and hit-limit messages. + # Keys are lowercase Bukkit Material / EntityType names. Anything not listed falls + # back to the automatic English name, so these sections can be left empty. + #materials: + # hopper: "Hopper" + #entities: + # enderman: "Enderman" + max-color: "" + regular-color: "" + block-limit-syntax: "[number]/[limit]" + no-limits: " No limits set in this world" + panel: + title-syntax: '[title] [env]' + entity-group-name-syntax: '[name]' + entity-name-syntax: '[name]' + block-name-syntax: '[name]' + env-overworld: "Overworld" + env-nether: "Nether" + env-end: "End" + errors: + no-owner: " That island has no owner" + not-on-island: " This location does not have limits set." + recount: + description: "recounts limits for your island" + now-recounting: " Now recounting. This could take a while, please wait..." + in-progress: " Island recound is in progress. Please wait..." + time-out: " Time out when recounting. Is the island really big?" + diff --git a/src/test/java/world/bentobox/limits/DisplayNamesTest.java b/src/test/java/world/bentobox/limits/DisplayNamesTest.java new file mode 100644 index 0000000..15e3188 --- /dev/null +++ b/src/test/java/world/bentobox/limits/DisplayNamesTest.java @@ -0,0 +1,50 @@ +package world.bentobox.limits; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +import org.bukkit.Material; +import org.bukkit.entity.EntityType; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import world.bentobox.bentobox.api.user.User; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +class DisplayNamesTest { + + @Mock + private User user; + + @Test + void testMaterialTranslated() { + when(user.getTranslationOrNothing(anyString())).thenReturn(""); + when(user.getTranslationOrNothing("island.limits.materials.hopper")).thenReturn("Trichter"); + assertEquals("Trichter", DisplayNames.material(user, Material.HOPPER.getKey())); + } + + @Test + void testMaterialFallsBackToPrettyName() { + when(user.getTranslationOrNothing(anyString())).thenReturn(""); + assertEquals("Hopper", DisplayNames.material(user, Material.HOPPER.getKey())); + } + + @Test + void testEntityTranslated() { + when(user.getTranslationOrNothing(anyString())).thenReturn(""); + when(user.getTranslationOrNothing("island.limits.entities.enderman")).thenReturn("Enderman (DE)"); + assertEquals("Enderman (DE)", DisplayNames.entity(user, EntityType.ENDERMAN)); + } + + @Test + void testEntityFallsBackToPrettyName() { + when(user.getTranslationOrNothing(anyString())).thenReturn(""); + assertEquals("Enderman", DisplayNames.entity(user, EntityType.ENDERMAN)); + } +}