Skip to content

Commit ec0c148

Browse files
authored
Merge pull request #286 from BentoBoxWorld/feat/188-material-translations
Add optional manual material/entity name translations in locale files
2 parents 6b01d70 + 6878631 commit ec0c148

6 files changed

Lines changed: 194 additions & 87 deletions

File tree

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package world.bentobox.limits;
2+
3+
import java.util.Locale;
4+
5+
import org.bukkit.NamespacedKey;
6+
import org.bukkit.entity.EntityType;
7+
8+
import world.bentobox.bentobox.api.user.User;
9+
import world.bentobox.bentobox.util.Util;
10+
11+
/**
12+
* Resolves the display name of a limited material or entity for a user.
13+
*
14+
* <p>Locale files may provide manual translations under
15+
* {@code island.limits.materials.<material>} and {@code island.limits.entities.<entity>}
16+
* (lowercase Bukkit names). Any key without a translation falls back to the automatic
17+
* prettified English name, so translations are entirely optional.
18+
*/
19+
public final class DisplayNames {
20+
21+
private DisplayNames() {
22+
}
23+
24+
/**
25+
* @param user the user whose locale is used
26+
* @param key the material key, e.g. {@code minecraft:hopper}
27+
* @return translated material name, or the prettified key if no translation exists
28+
*/
29+
public static String material(User user, NamespacedKey key) {
30+
String translated = user.getTranslationOrNothing("island.limits.materials." + key.getKey());
31+
return translated == null || translated.isEmpty() ? Util.prettifyText(key.getKey()) : translated;
32+
}
33+
34+
/**
35+
* @param user the user whose locale is used
36+
* @param type the entity type
37+
* @return translated entity name, or the prettified type name if no translation exists
38+
*/
39+
public static String entity(User user, EntityType type) {
40+
String translated = user
41+
.getTranslationOrNothing("island.limits.entities." + type.toString().toLowerCase(Locale.ROOT));
42+
return translated == null || translated.isEmpty() ? Util.prettifyText(type.toString()) : translated;
43+
}
44+
}

src/main/java/world/bentobox/limits/commands/player/LimitTab.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder;
2727
import world.bentobox.bentobox.api.user.User;
2828
import world.bentobox.bentobox.util.Util;
29+
import world.bentobox.limits.DisplayNames;
2930
import world.bentobox.limits.EntityGroup;
3031
import world.bentobox.limits.Limits;
3132
import world.bentobox.limits.objects.IslandBlockCount;
@@ -112,7 +113,7 @@ private void addEntityGroupLimits(IslandBlockCount ibc) {
112113
PanelItemBuilder pib = new PanelItemBuilder();
113114
pib.name(user.getTranslation("island.limits.panel.entity-group-name-syntax", TextVariables.NAME,
114115
g.getName()));
115-
String description = "(" + prettyNames(g) + ")\n";
116+
String description = "(" + prettyNames(user, g) + ")\n";
116117
pib.icon(g.getIcon());
117118
int count = ibc == null ? 0 : sumGroupCount(ibc, g);
118119
String color = count >= limit ? user.getTranslation(MAX_COLOR_KEY)
@@ -144,7 +145,7 @@ private void addEntityLimits(IslandBlockCount ibc) {
144145
map.forEach((k, v) -> {
145146
PanelItemBuilder pib = new PanelItemBuilder();
146147
pib.name(user.getTranslation("island.limits.panel.entity-name-syntax", TextVariables.NAME,
147-
Util.prettifyText(k.toString())));
148+
DisplayNames.entity(user, k)));
148149
Material m;
149150
try {
150151
if (E2M.containsKey(k)) {
@@ -172,7 +173,7 @@ private void addMaterialIcons(IslandBlockCount ibc, Map<NamespacedKey, Integer>
172173
for (Entry<NamespacedKey, Integer> en : matLimits.entrySet()) {
173174
PanelItemBuilder pib = new PanelItemBuilder();
174175
pib.name(user.getTranslation("island.limits.panel.block-name-syntax", TextVariables.NAME,
175-
Util.prettifyText(en.getKey().getKey())));
176+
DisplayNames.material(user, en.getKey())));
176177
Material mat = Registry.MATERIAL.get(B2M.getOrDefault(en.getKey(), en.getKey()));
177178
pib.icon(Objects.requireNonNullElse(mat, Material.PAPER));
178179

@@ -222,11 +223,11 @@ public String getPermission() {
222223
return "";
223224
}
224225

225-
private String prettyNames(EntityGroup v) {
226+
private String prettyNames(User user, EntityGroup v) {
226227
StringBuilder sb = new StringBuilder();
227228
List<EntityType> l = new ArrayList<>(v.getTypes());
228229
for (int i = 0; i < l.size(); i++) {
229-
sb.append(Util.prettifyText(l.get(i).toString()));
230+
sb.append(DisplayNames.entity(user, l.get(i)));
230231
if (i + 1 < l.size()) sb.append(", ");
231232
if ((i + 1) % 5 == 0) sb.append("\n");
232233
}

src/main/java/world/bentobox/limits/listeners/BlockLimitsListener.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import world.bentobox.bentobox.database.Database;
5353
import world.bentobox.bentobox.database.objects.Island;
5454
import world.bentobox.bentobox.util.Util;
55+
import world.bentobox.limits.DisplayNames;
5556
import world.bentobox.limits.Limits;
5657
import world.bentobox.limits.Settings;
5758
import world.bentobox.limits.objects.IslandBlockCount;
@@ -310,7 +311,7 @@ private void notify(Cancellable e, User user, int limit, Material m) {
310311
if (limit > -1) {
311312
if (addon.getSettings().isShowLimitMessages()) {
312313
user.notify("block-limits.hit-limit",
313-
"[material]", Util.prettifyText(m.toString()),
314+
"[material]", DisplayNames.material(user, m.getKey()),
314315
TextVariables.NUMBER, String.valueOf(limit));
315316
}
316317
e.setCancelled(true);

src/main/java/world/bentobox/limits/listeners/EntityLimitListener.java

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import world.bentobox.bentobox.database.objects.Island;
2929
import world.bentobox.bentobox.util.Util;
3030
import world.bentobox.limits.EntityGroup;
31+
import world.bentobox.limits.DisplayNames;
3132
import world.bentobox.limits.Limits;
3233
import world.bentobox.limits.Settings;
3334
import world.bentobox.limits.objects.IslandBlockCount;
@@ -151,15 +152,16 @@ public void onBlock(HangingPlaceEvent hangingPlaceEvent) {
151152
if (!addon.getSettings().isShowLimitMessages()) {
152153
return;
153154
}
155+
User u = User.getInstance(player);
154156
if (res.getTypelimit() != null) {
155-
User.getInstance(player).notify("block-limits.hit-limit", "[material]",
156-
Util.prettifyText(hangingPlaceEvent.getEntity().getType().toString()),
157+
u.notify("block-limits.hit-limit", "[material]",
158+
DisplayNames.entity(u, hangingPlaceEvent.getEntity().getType()),
157159
TextVariables.NUMBER, String.valueOf(res.getTypelimit().getValue()));
158160
} else {
159-
User.getInstance(player).notify("block-limits.hit-limit", "[material]",
161+
u.notify("block-limits.hit-limit", "[material]",
160162
res.getGrouplimit().getKey().getName() + " ("
161163
+ res.getGrouplimit().getKey().getTypes().stream()
162-
.map(x -> Util.prettifyText(x.toString()))
164+
.map(x -> DisplayNames.entity(u, x))
163165
.collect(Collectors.joining(", "))
164166
+ ")",
165167
TextVariables.NUMBER, String.valueOf(res.getGrouplimit().getValue()));
@@ -235,15 +237,16 @@ private void notifyEntityLimit(Player player, EntityType type, AtLimitResult res
235237
if (!addon.getSettings().isShowLimitMessages()) {
236238
return;
237239
}
240+
User u = User.getInstance(player);
238241
if (res.getTypelimit() != null) {
239-
User.getInstance(player).notify(ENTITY_LIMIT_HIT, ENTITY_PLACEHOLDER,
240-
Util.prettifyText(type.toString()), TextVariables.NUMBER,
242+
u.notify(ENTITY_LIMIT_HIT, ENTITY_PLACEHOLDER,
243+
DisplayNames.entity(u, type), TextVariables.NUMBER,
241244
String.valueOf(res.getTypelimit().getValue()));
242245
} else {
243-
User.getInstance(player).notify(ENTITY_LIMIT_HIT, ENTITY_PLACEHOLDER,
246+
u.notify(ENTITY_LIMIT_HIT, ENTITY_PLACEHOLDER,
244247
res.getGrouplimit().getKey().getName() + " ("
245248
+ res.getGrouplimit().getKey().getTypes().stream()
246-
.map(x -> Util.prettifyText(x.toString()))
249+
.map(x -> DisplayNames.entity(u, x))
247250
.collect(Collectors.joining(", "))
248251
+ ")",
249252
TextVariables.NUMBER, String.valueOf(res.getGrouplimit().getValue()));
@@ -568,15 +571,16 @@ private void tellPlayers(Location location, Entity entity, SpawnReason spawnReas
568571
for (Entity ent : w.getNearbyEntities(location, 5, 5, 5)) {
569572
if (ent instanceof Player p) {
570573
p.updateInventory();
574+
User u = User.getInstance(p);
571575
if (res.getTypelimit() != null) {
572-
User.getInstance(p).notify(ENTITY_LIMIT_HIT, ENTITY_PLACEHOLDER,
573-
Util.prettifyText(entity.getType().toString()),
576+
u.notify(ENTITY_LIMIT_HIT, ENTITY_PLACEHOLDER,
577+
DisplayNames.entity(u, entity.getType()),
574578
TextVariables.NUMBER, String.valueOf(res.getTypelimit().getValue()));
575579
} else {
576-
User.getInstance(p).notify(ENTITY_LIMIT_HIT, ENTITY_PLACEHOLDER,
580+
u.notify(ENTITY_LIMIT_HIT, ENTITY_PLACEHOLDER,
577581
res.getGrouplimit().getKey().getName() + " ("
578582
+ res.getGrouplimit().getKey().getTypes().stream()
579-
.map(x -> Util.prettifyText(x.toString()))
583+
.map(x -> DisplayNames.entity(u, x))
580584
.collect(Collectors.joining(", "))
581585
+ ")",
582586
TextVariables.NUMBER, String.valueOf(res.getGrouplimit().getValue()));
Lines changed: 76 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,76 @@
1-
###########################################################################################
2-
# This is a YML file. Be careful when editing. Check your edits in a YAML checker like #
3-
# the one at http://yaml-online-parser.appspot.com #
4-
###########################################################################################
5-
6-
block-limits:
7-
hit-limit: "<red>[material] limited to [number]!"
8-
entity-limits:
9-
hit-limit: "<red>[entity] spawning limited to [number]!"
10-
limits:
11-
panel-title: "Island limits"
12-
13-
admin:
14-
limits:
15-
main:
16-
parameters: "<player>"
17-
description: "show the island limits for player"
18-
calc:
19-
parameters: "<player>"
20-
description: "recalculate the island limits for player"
21-
finished: "<green> Island recalc finished successfully!"
22-
offset:
23-
unknown: "<red> Unknown material or entity [name]."
24-
description: "allows to manage limits offsets for materials and entities"
25-
set:
26-
parameters: "<player> <material|entity> <number>"
27-
description: "sets new offset for material or entity limit"
28-
success: "<green> Limit offset for [name] is set to [number]."
29-
same: "<red> Limit offset for [name] is already [number]."
30-
add:
31-
parameters: "<player> <material|entity> <number>"
32-
description: "adds offset for material or entity limit"
33-
success: "<green> Limit offset for [name] is increased till [number]."
34-
remove:
35-
parameters: "<player> <material|entity> <number>"
36-
description: "reduces offset for material or entity limit"
37-
success: "<green> Limit offset for [name] is reduced till [number]."
38-
reset:
39-
parameters: "<player> <material|entity>"
40-
description: "removes offset for material or entity"
41-
success: "<green> Limit offset for [name] is set to 0."
42-
view:
43-
parameters: "<player> <material|entity>"
44-
description: "displays offset for material or entity"
45-
message: "<green> [name] offset is set to [number]."
46-
island:
47-
limits:
48-
description: "show your island limits"
49-
max-color: "<red>"
50-
regular-color: "<green>"
51-
block-limit-syntax: "[number]/[limit]"
52-
no-limits: "<red> No limits set in this world"
53-
panel:
54-
title-syntax: '[title] [env]'
55-
entity-group-name-syntax: '[name]'
56-
entity-name-syntax: '[name]'
57-
block-name-syntax: '[name]'
58-
env-overworld: "Overworld"
59-
env-nether: "Nether"
60-
env-end: "End"
61-
errors:
62-
no-owner: "<red> That island has no owner"
63-
not-on-island: "<red> This location does not have limits set."
64-
recount:
65-
description: "recounts limits for your island"
66-
now-recounting: "<aqua> Now recounting. This could take a while, please wait..."
67-
in-progress: "<red> Island recound is in progress. Please wait..."
68-
time-out: "<red> Time out when recounting. Is the island really big?"
69-
1+
###########################################################################################
2+
# This is a YML file. Be careful when editing. Check your edits in a YAML checker like #
3+
# the one at http://yaml-online-parser.appspot.com #
4+
###########################################################################################
5+
6+
block-limits:
7+
hit-limit: "<red>[material] limited to [number]!"
8+
entity-limits:
9+
hit-limit: "<red>[entity] spawning limited to [number]!"
10+
limits:
11+
panel-title: "Island limits"
12+
13+
admin:
14+
limits:
15+
main:
16+
parameters: "<player>"
17+
description: "show the island limits for player"
18+
calc:
19+
parameters: "<player>"
20+
description: "recalculate the island limits for player"
21+
finished: "<green> Island recalc finished successfully!"
22+
offset:
23+
unknown: "<red> Unknown material or entity [name]."
24+
description: "allows to manage limits offsets for materials and entities"
25+
set:
26+
parameters: "<player> <material|entity> <number>"
27+
description: "sets new offset for material or entity limit"
28+
success: "<green> Limit offset for [name] is set to [number]."
29+
same: "<red> Limit offset for [name] is already [number]."
30+
add:
31+
parameters: "<player> <material|entity> <number>"
32+
description: "adds offset for material or entity limit"
33+
success: "<green> Limit offset for [name] is increased till [number]."
34+
remove:
35+
parameters: "<player> <material|entity> <number>"
36+
description: "reduces offset for material or entity limit"
37+
success: "<green> Limit offset for [name] is reduced till [number]."
38+
reset:
39+
parameters: "<player> <material|entity>"
40+
description: "removes offset for material or entity"
41+
success: "<green> Limit offset for [name] is set to 0."
42+
view:
43+
parameters: "<player> <material|entity>"
44+
description: "displays offset for material or entity"
45+
message: "<green> [name] offset is set to [number]."
46+
island:
47+
limits:
48+
description: "show your island limits"
49+
# Optional manual name translations, used in the limits GUI and hit-limit messages.
50+
# Keys are lowercase Bukkit Material / EntityType names. Anything not listed falls
51+
# back to the automatic English name, so these sections can be left empty.
52+
#materials:
53+
# hopper: "Hopper"
54+
#entities:
55+
# enderman: "Enderman"
56+
max-color: "<red>"
57+
regular-color: "<green>"
58+
block-limit-syntax: "[number]/[limit]"
59+
no-limits: "<red> No limits set in this world"
60+
panel:
61+
title-syntax: '[title] [env]'
62+
entity-group-name-syntax: '[name]'
63+
entity-name-syntax: '[name]'
64+
block-name-syntax: '[name]'
65+
env-overworld: "Overworld"
66+
env-nether: "Nether"
67+
env-end: "End"
68+
errors:
69+
no-owner: "<red> That island has no owner"
70+
not-on-island: "<red> This location does not have limits set."
71+
recount:
72+
description: "recounts limits for your island"
73+
now-recounting: "<aqua> Now recounting. This could take a while, please wait..."
74+
in-progress: "<red> Island recound is in progress. Please wait..."
75+
time-out: "<red> Time out when recounting. Is the island really big?"
76+
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package world.bentobox.limits;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.mockito.ArgumentMatchers.anyString;
5+
import static org.mockito.Mockito.when;
6+
7+
import org.bukkit.Material;
8+
import org.bukkit.entity.EntityType;
9+
import org.junit.jupiter.api.Test;
10+
import org.junit.jupiter.api.extension.ExtendWith;
11+
import org.mockito.Mock;
12+
import org.mockito.junit.jupiter.MockitoExtension;
13+
import org.mockito.junit.jupiter.MockitoSettings;
14+
import org.mockito.quality.Strictness;
15+
16+
import world.bentobox.bentobox.api.user.User;
17+
18+
@ExtendWith(MockitoExtension.class)
19+
@MockitoSettings(strictness = Strictness.LENIENT)
20+
class DisplayNamesTest {
21+
22+
@Mock
23+
private User user;
24+
25+
@Test
26+
void testMaterialTranslated() {
27+
when(user.getTranslationOrNothing(anyString())).thenReturn("");
28+
when(user.getTranslationOrNothing("island.limits.materials.hopper")).thenReturn("Trichter");
29+
assertEquals("Trichter", DisplayNames.material(user, Material.HOPPER.getKey()));
30+
}
31+
32+
@Test
33+
void testMaterialFallsBackToPrettyName() {
34+
when(user.getTranslationOrNothing(anyString())).thenReturn("");
35+
assertEquals("Hopper", DisplayNames.material(user, Material.HOPPER.getKey()));
36+
}
37+
38+
@Test
39+
void testEntityTranslated() {
40+
when(user.getTranslationOrNothing(anyString())).thenReturn("");
41+
when(user.getTranslationOrNothing("island.limits.entities.enderman")).thenReturn("Enderman (DE)");
42+
assertEquals("Enderman (DE)", DisplayNames.entity(user, EntityType.ENDERMAN));
43+
}
44+
45+
@Test
46+
void testEntityFallsBackToPrettyName() {
47+
when(user.getTranslationOrNothing(anyString())).thenReturn("");
48+
assertEquals("Enderman", DisplayNames.entity(user, EntityType.ENDERMAN));
49+
}
50+
}

0 commit comments

Comments
 (0)