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));
+ }
+}