Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
1d0fb1e
feat: translations
ArikSquad Feb 10, 2026
3649f72
feat: translations
ArikSquad Feb 10, 2026
57ba5bb
Merge branch 'master' into feat/translations
Swofty-Developments Feb 26, 2026
236e4f5
feat(i18n): add en_US translation files for all GUI subsystems
Swofty-Developments Feb 26, 2026
fac4824
refactor(i18n): move GUI files to gui/ subfolder and convert to MiniM…
Swofty-Developments Feb 26, 2026
8345aee
feat(i18n): enhance I18n with dialogue support and placeholder helpers
Swofty-Developments Feb 26, 2026
1cfbb65
refactor(i18n): split gui_misc.properties into per-GUI translation files
Swofty-Developments Feb 26, 2026
a9a161b
feat(i18n): add NPC dialogue and command translation files
Swofty-Developments Feb 26, 2026
2dff66c
feat(i18n): add startup validation for missing translation keys
Swofty-Developments Feb 26, 2026
1a31cc5
refactor(i18n): migrate 8 hub NPCs to use DialogueSet.ofTranslation()
Swofty-Developments Feb 26, 2026
d25f1dc
refactor(i18n): migrate 6 commands to use I18n translation system
Swofty-Developments Feb 26, 2026
88c63d5
refactor(i18n): migrate 92 GUI files to use I18n translation system
Swofty-Developments Feb 26, 2026
df1bc89
fix(items): add missing material fields to 491 item YAML definitions
Swofty-Developments Feb 26, 2026
9f7a4a5
Revert "fix(items): add missing material fields to 491 item YAML defi…
Swofty-Developments Feb 26, 2026
abc7184
feat(i18n): convert scoreboard strings to translation system
Swofty-Developments Feb 26, 2026
6c43dfe
feat(i18n): convert tablist module strings to translation system
Swofty-Developments Feb 26, 2026
dfdb8cb
feat(i18n): convert SkyblockItem lore strings to translation system
Swofty-Developments Feb 26, 2026
bfabd07
Merge remote-tracking branch 'origin/master' into feat/translations
ArikSquad Feb 26, 2026
642b8ec
fix(i18n): correct translation formatting and unescape bug
Swofty-Developments Feb 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 80 additions & 1 deletion commons/src/main/java/net/swofty/commons/StringUtility.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import net.minestom.server.instance.block.Block;
import net.minestom.server.item.Material;
import net.swofty.commons.skyblock.statistics.ItemStatistic;

import java.math.RoundingMode;
import java.text.DecimalFormat;
Expand Down Expand Up @@ -152,6 +151,86 @@ public static String profileAge(long tbf) {
return formatTimeWentBy(System.currentTimeMillis() - tbf);
}

/**
* Unescapes a string that contains standard Java escape sequences.
* This is untested and may not work as expected.
*
* @param input the string to unescape
* @return the unescaped string
*/
public static String unescapeJava(String input) {
if (input == null) {
return null;
}
StringBuilder sb = new StringBuilder(input.length());
for (int i = 0; i < input.length(); i++) {
char c = input.charAt(i);
if (c == '\\' && i < input.length() - 1) {
char next = input.charAt(i + 1);
switch (next) {
case 'b':
sb.append('\b');
i++;
break;
case 't':
sb.append('\t');
i++;
break;
case 'n':
sb.append('\n');
i++;
break;
case 'f':
sb.append('\f');
i++;
break;
case 'r':
sb.append('\r');
i++;
break;
case '\"':
sb.append('\"');
i++;
break;
case '\'':
sb.append('\'');
i++;
break;
case '\\':
sb.append('\\');
i++;
break;
case 'u':
// Unicode escape sequence:
if (i + 5 < input.length()) {
String hex = input.substring(i + 2, i + 6);
try {
int code = Integer.parseInt(hex, 16);
sb.append((char) code);
i += 5;
} catch (NumberFormatException e) {
// Not a valid Unicode escape, so fall back
sb.append('\\').append('u');
i++;
}
} else {
sb.append('\\').append('u');
i++;
}
break;
default:
// Unrecognized escape, skip the backslash
sb.append(next);
i++;
break;
}
} else {
sb.append(c);
}
}
return sb.toString();
}

public static String getAsRomanNumeral(int num) {
if (num == 0) return "";
int[] values = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
Expand Down
10 changes: 10 additions & 0 deletions configuration/i18n/en_US/bedwars.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
bedwars.kill.died.default="<player> <gray>died."
bedwars.kill.died_by.default="<player> <gray>was killed by <killer><gray>."
bedwars.kill.projectile.default="<player> <gray>was shot by <killer><gray>."
bedwars.kill.assist.default="<player> <gray>was killed by <victim><gray>'s <entity>."
bedwars.kill.void.default="<player> <gray>fell in to the void."
bedwars.kill.void_by.default="<player> <gray>was knocked into the void by <killer><gray>."
bedwars.kill.bed.default="<gray>was destroyed by"
bedwars.kill.final_kill="<aqua><bold>FINAL KILL!</aqua></bold>"
bedwars.bed_destroyed="<white><bold>BED DESTRUCTION ></bold> <gray><team> Bed <gray><message> <gray>."
bedwars.bed_destroyed_self="<white><bold>BED DESTRUCTION ></bold> <gray>Your bed <gray><message> <gray>."
204 changes: 204 additions & 0 deletions configuration/i18n/en_US/commands.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
# ============================================================
# Common / Shared
# ============================================================
commands.common.separator = <blue><strikethrough>-----------------------------------------------------
commands.common.player_not_found = <red>Can't find a player by the name of '{player}'
commands.common.player_not_found_short = <red>Could not find player: {player}
commands.common.unknown_command_use_help = <red>Unknown command. Use /{command} for help.
commands.common.service_offline_friend = <red>Could not connect to the friend service! Please try again later.
commands.common.service_offline_party = <red>Could not connect to the party service! Please try again later.
commands.common.service_offline_party_alt = <red>Couldn't find a party service! Please try again later.
commands.common.service_offline_punishment = <red>Could not {action} this player at this time. The punishment service may be offline.
commands.common.empty_line = <white>

# ============================================================
# Friend Command
# ============================================================
commands.friend.help_header = <gold>Friend Commands
commands.friend.help_add = <yellow>/f add <player> <dark_gray>- <gray>Add a player as a friend
commands.friend.help_accept = <yellow>/f accept <player> <dark_gray>- <gray>Accept a friend request
commands.friend.help_deny = <yellow>/f deny <player> <dark_gray>- <gray>Deny a friend request
commands.friend.help_remove = <yellow>/f remove <player> <dark_gray>- <gray>Remove a friend
commands.friend.help_removeall = <yellow>/f removeall <dark_gray>- <gray>Remove all friends (keeps best friends)
commands.friend.help_best = <yellow>/f best <player> <dark_gray>- <gray>Toggle best friend status
commands.friend.help_nickname = <yellow>/f nickname <player> <name> <dark_gray>- <gray>Set a nickname for a friend
commands.friend.help_list = <yellow>/f list [page/best] <dark_gray>- <gray>List your friends
commands.friend.help_requests = <yellow>/f requests [page] <dark_gray>- <gray>View pending friend requests
commands.friend.help_toggle = <yellow>/f toggle <dark_gray>- <gray>Toggle accepting friend requests
commands.friend.help_notifications = <yellow>/f notifications <dark_gray>- <gray>Toggle join/leave notifications
commands.friend.error_not_found = <red>Couldn't find a player with that name!
commands.friend.error_cannot_add_self = <red>You cannot add yourself as a friend!

# ============================================================
# Party Command
# ============================================================
commands.party.help_header = <gold>Party Commands
commands.party.help_accept = <yellow>/p accept <dark_gray>- <gray><italic>Accept a party invite from a player
commands.party.help_invite = <yellow>/p invite <player> <dark_gray>- <gray><italic>Invite another player to your party
commands.party.help_list = <yellow>/p list <dark_gray>- <gray><italic>Lists the players in your current party
commands.party.help_leave = <yellow>/p leave <dark_gray>- <gray><italic>Leaves your current party
commands.party.help_warp = <yellow>/p warp <dark_gray>- <gray><italic>Warps the members of a party to your current server
commands.party.help_disband = <yellow>/p disband <dark_gray>- <gray><italic>Disbands the party
commands.party.help_transfer = <yellow>/p transfer <player> <dark_gray>- <gray><italic>Transfers the party to another player
commands.party.help_kick = <yellow>/p kick <player> <dark_gray>- <gray><italic>Remove a player from your party
commands.party.help_promote = <yellow>/p promote <player> <dark_gray>- <gray><italic>Promote a player to moderator
commands.party.help_demote = <yellow>/p demote <player> <dark_gray>- <gray><italic>Demote a player from moderator
commands.party.help_chat = <yellow>/p chat <dark_gray>- <gray><italic>Sends a chat message to the entire party
commands.party.help_hijack = <yellow>/p hijack <player> <dark_gray>- <gray><italic>Hijacks a party (Admin only)
commands.party.not_in_party = <red>You are not in a party!
commands.party.list_header = <gold>Party Members ({count})
commands.party.list_leader = <yellow>Party Leader: {leader}
commands.party.list_moderators = <yellow>Party Moderators: {moderators}
commands.party.list_members = <yellow>Party Members: {members}
commands.party.error_not_found = <red>Couldn't find a player with that name!
commands.party.error_not_online = <red>That player is not online!
commands.party.error_invite_self = <red>You cannot invite yourself!
commands.party.error_already_in_party = <red>That player is already in your party!
commands.party.error_must_leave_party = <red>You must leave your current party before accepting an invite!
commands.party.error_need_staff = <red>You need STAFF to do this command

# ============================================================
# Message Command
# ============================================================
commands.message.player_not_found = <red>Can't find a player by the name of '{player}'
commands.message.player_not_online = <red>The player you tried to message, {player}, is not online.
commands.message.outgoing = <light_purple>To {target}<gray>: {message}
commands.message.incoming = <light_purple>From {sender}<gray>: {message}

# ============================================================
# Mute Command
# ============================================================
commands.mute.invalid_reason = <red>Invalid mute reason. Use tab-completion to see valid options.
commands.mute.success = <green>Successfully muted player <yellow>{player}<green>. <dark_gray>Punishment ID: <gray>{id}
commands.mute.already_muted = <red>This player already has an active mute. Punishment ID: <gray>{id}
commands.mute.failed = <red>Failed to mute player: {error}
commands.mute.service_offline = <red>Could not mute this player at this time. The punishment service may be offline.

# ============================================================
# Ban Command
# ============================================================
commands.ban.invalid_reason = <red>Invalid ban reason. Use tab-completion to see valid options.
commands.ban.success = <green>Successfully banned player <yellow>{player}<green>. <dark_gray>Punishment ID: <gray>{id}
commands.ban.already_banned = <red>This player is already banned. Use the tag -O to overwrite. Punishment ID: <gray>{id}
commands.ban.failed = <red>Failed to ban player: {error}
commands.ban.service_offline = <red>Could not ban this player at this time. The punishment service may be offline.

# ============================================================
# UnMute Command
# ============================================================
commands.unmute.success = <green>Successfully unmuted player: {player}
commands.unmute.service_offline = <red>Could not unmute this player at this time. The punishment service may be offline.

# ============================================================
# UnBan Command
# ============================================================
commands.unban.success = <green>Successfully unbanned player: {player}
commands.unban.service_offline = <red>Could not unban this player at this time. The punishment service may be offline.

# ============================================================
# Rank Command
# ============================================================
commands.rank.player_not_found = <red>Couldn't find a player by the name of <yellow>{player}<red>.
commands.rank.success = <green>Successfully set <yellow>{player}<green>'s rank to <yellow>{rank}<green>.

# ============================================================
# Chat Command
# ============================================================
commands.chat.unknown_type = <red>Unknown chat type.
commands.chat.staff_view_toggled = <green>Staff chat viewing is now {state}
commands.chat.staff_view_enabled = <green>enabled
commands.chat.staff_view_disabled = <red>disabled
commands.chat.switched_channel = <green>You are now in the <gold>{channel} <green>channel

# ============================================================
# Dragon Command
# ============================================================
commands.dragon.help_header = <gold>Dragon Commands
commands.dragon.help_spawn = <yellow>/dragon spawn [speed] <dark_gray>- <gray>Spawn a dragon at your location
commands.dragon.help_follow = <yellow>/dragon follow [speed] <dark_gray>- <gray>Make the dragon follow you
commands.dragon.help_idle = <yellow>/dragon idle [distance] <dark_gray>- <gray>Make the dragon idle around your position
commands.dragon.help_stop = <yellow>/dragon stop <dark_gray>- <gray>Stop the dragon
commands.dragon.help_remove = <yellow>/dragon remove <dark_gray>- <gray>Remove your dragon
commands.dragon.already_spawned = <red>You already have a dragon spawned! Use /dragon remove first.
commands.dragon.spawned = <green>Dragon spawned!
commands.dragon.no_dragon = <red>You don't have a dragon!
commands.dragon.no_dragon_spawn_first = <red>You don't have a dragon! Use /dragon spawn first.
commands.dragon.following = <green>Dragon is now following you at speed {speed}!
commands.dragon.idling = <green>Dragon is now idling around your position with distance {distance}!
commands.dragon.stopped = <green>Dragon stopped.
commands.dragon.removed = <green>Dragon removed!

# ============================================================
# Gamemode Command
# ============================================================
commands.gamemode.success = <green>Set your gamemode to <yellow>{gamemode}<green>.

# ============================================================
# FlySpeed Command
# ============================================================
commands.flyspeed.success = <green>Set your {type} speed to <yellow>{speed}<green>.

# ============================================================
# WipeMe Command
# ============================================================
commands.wipeme.kick_message = <red>You have been wiped

# ============================================================
# Teleport Command
# ============================================================
commands.teleport.player_not_found = <red>Couldn't find a player by the name of <yellow>{player}<red>.

# ============================================================
# AdminMe Command
# ============================================================
commands.adminme.running_checks = <dark_gray>Running checks...
commands.adminme.not_allowed = <red>You are not allowed to use this command.
commands.adminme.success = <green>Successfully set rank to {rank}<green>.

# ============================================================
# StaffChat Command
# ============================================================
commands.staffchat.usage = <red>Usage: /sc <message>

# ============================================================
# Hub Command
# ============================================================
commands.hub.must_complete_missions = <red>You must complete your starting missions!

# ============================================================
# Warp Command
# ============================================================
commands.warp.warped_to = <gray>You have been warped to {destination}<gray>!
commands.warp.not_unlocked = <red>You have not unlocked this warp.
commands.warp.not_found = <red>Could not find a warp with the name '{warp}'.

# ============================================================
# Coop Command
# ============================================================
commands.coop.no_outgoing_invite = <red>You don't have an outgoing co-op invite!
commands.coop.create_hint = <yellow>Use <aqua>/coop <player 1> <player 2>... <yellow>to create one!
commands.coop.add_hint = <yellow>Use <green>/coopadd <player> <yellow>to add a player to your current co-op!
commands.coop.validating = <gray>Validating invite...
commands.coop.cannot_invite_self = <red>You can't invite yourself to a co-op!
commands.coop.players_not_online = <aqua>[Co-op] <red>One or more of the players you specified are not online!
commands.coop.players_already_have_coop = <aqua>[Co-op] <red>One or more of the players you specified already have a co-op or an invite pending!
commands.coop.cannot_invite_same_twice = <aqua>[Co-op] <red>You can't invite the same player twice!
commands.coop.too_many_players = <aqua>[Co-op] <red>You can't invite more than 4 players!
commands.coop.invite_sent = <aqua>[Co-op] <yellow>You invited {player} to a SkyBlock co-op!
commands.coop.invite_received_separator = <aqua>----------------------------------------
commands.coop.invite_received = {player} <yellow>invited you to a SkyBlock co-op!
commands.coop.invite_click_to_view = <gold>Click here <yellow>to view!
commands.coop.invite_click_hover = <yellow>Click here to view the invite
commands.coop.status_hint = <yellow>Use <aqua>/coop <yellow>or <green><bold>CLICK THIS <yellow>for status!
commands.coop.already_in_coop = <red>You are already in a co-op!
commands.coop.leave_hint = <yellow>Run <green>/coopleave <yellow>to leave your current co-op.
commands.coop.outgoing_invite_exists = <yellow>You already have an outgoing co-op invite! <green><bold>CLICK TO VIEW!
commands.coop.incoming_invite_exists = <red>You already have an incoming co-op invite! <green><bold>CLICK TO VIEW!

# ============================================================
# CoopLeave Command
# ============================================================
commands.coopleave.not_in_coop = <aqua>[Co-op] <red>You are not on a coop profile!
commands.coopleave.cannot_leave_last = <aqua>[Co-op] <red>You cannot leave your last profile!
commands.coopleave.make_another_profile = <aqua>[Co-op] <yellow>Make another profile before deleting this one.
commands.coopleave.kick_message = <red>You must reconnect for this change to take effect
31 changes: 31 additions & 0 deletions configuration/i18n/en_US/gui/gui_abiphone.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# =============================================================================
# Abiphone GUI Translations
# =============================================================================

# --- GUIAbiphone / AbiphoneView ---
gui_abiphone.sort_button = <green>Sort <aqua>TO-DO
gui_abiphone.sort_button.lore = \n<gray> First Added\n<gray> Alphabetical\n<gray> Last Called\n<gray> Most Called\n<gray> Do Not Disturb First\n\n<aqua>Right-click to go backwards!\n<yellow>Click to switch!

gui_abiphone.contacts_directory = <green>Contacts Directory <aqua>TO-DO
gui_abiphone.contacts_directory.lore = <gray>Browse through all NPCs in SkyBlock\n<gray>which both own an Abiphone AND are\n<gray>willing to add you as a contact.\n\n<gray>Your contacts: <green>{contact_count}<aqua>/{total_contacts}\n\n<yellow>Click to view contacts!

gui_abiphone.contact_manage_hint = <dark_gray>Right-click to manage!
gui_abiphone.contact_call_hint = <yellow>Left-click to call!

gui_abiphone.ring_1 = <yellow>\u2706 RING...
gui_abiphone.ring_2 = <yellow>\u2706 RING... RING...
gui_abiphone.ring_3 = <yellow>\u2706 RING... RING... RING...

# --- GUIConfirmAbiphone ---
gui_abiphone.confirm.title = Confirm
gui_abiphone.confirm.confirm_button = <green>Confirm
gui_abiphone.confirm.confirm_button.lore = <yellow>Click to remove {npc_name}\n<yellow>from your\ncontacts!
gui_abiphone.confirm.cancel_button = <red>Cancel

# --- GUIContactManagement / GUIContactManagementView ---
gui_abiphone.management.title = Contact Management

gui_abiphone.management.remove_contact = <red>Remove Contact
gui_abiphone.management.remove_contact.lore = <gray>In case you're no longer friends, or\n<gray>whatever other reason.\n \n<yellow>Click to remove!

gui_abiphone.management.removed_message = <red>\u2706 Removed {npc_name} <red>from your contacts!
Loading