Convert to the superior language system. (pt. 1)

This commit is contained in:
KingRainbow44 2022-05-05 23:57:55 -04:00
parent 775f4cb7c1
commit 023c5baffe
No known key found for this signature in database
GPG Key ID: FC2CB64B00D257BE
6 changed files with 104 additions and 646 deletions

View File

@ -28,7 +28,7 @@ plugins {
// Eclipse Support // Eclipse Support
id 'eclipse' id 'eclipse'
// Intelij Support // IntelliJ Support
id 'idea' id 'idea'
// Maven // Maven

View File

@ -4,9 +4,7 @@ import java.io.File;
import java.io.FileReader; import java.io.FileReader;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOError; import java.io.IOError;
import java.net.InetSocketAddress;
import java.util.Calendar; import java.util.Calendar;
import java.util.Locale;
import emu.grasscutter.command.CommandMap; import emu.grasscutter.command.CommandMap;
import emu.grasscutter.plugin.PluginManager; import emu.grasscutter.plugin.PluginManager;
@ -28,24 +26,25 @@ import com.google.gson.GsonBuilder;
import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.Logger;
import emu.grasscutter.data.ResourceLoader; import emu.grasscutter.data.ResourceLoader;
import emu.grasscutter.database.DatabaseManager; import emu.grasscutter.database.DatabaseManager;
import emu.grasscutter.languages.CNLanguage; import emu.grasscutter.utils.Language;
import emu.grasscutter.languages.Language;
import emu.grasscutter.server.dispatch.DispatchServer; import emu.grasscutter.server.dispatch.DispatchServer;
import emu.grasscutter.server.game.GameServer; import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.tools.Tools; import emu.grasscutter.tools.Tools;
import emu.grasscutter.utils.Crypto; import emu.grasscutter.utils.Crypto;
import static emu.grasscutter.utils.Language.translate;
public final class Grasscutter { public final class Grasscutter {
private static final Logger log = (Logger) LoggerFactory.getLogger(Grasscutter.class); private static final Logger log = (Logger) LoggerFactory.getLogger(Grasscutter.class);
private static Config config;
private static LineReader consoleLineReader = null; private static LineReader consoleLineReader = null;
private static Config config;
private static Language language; private static Language language;
private static CNLanguage cn_language;
private static final Gson gson = new GsonBuilder().setPrettyPrinting().create(); private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
private static final File configFile = new File("./config.json"); private static final File configFile = new File("./config.json");
private static int day; // Current day of week private static int day; // Current day of week.
private static DispatchServer dispatchServer; private static DispatchServer dispatchServer;
private static GameServer gameServer; private static GameServer gameServer;
@ -60,7 +59,7 @@ public final class Grasscutter {
// Load server configuration. // Load server configuration.
Grasscutter.loadConfig(); Grasscutter.loadConfig();
// Load Language // Load translation files.
Grasscutter.loadLanguage(); Grasscutter.loadLanguage();
// Check server structure. // Check server structure.
@ -68,21 +67,18 @@ public final class Grasscutter {
} }
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
Crypto.loadKeys(); Crypto.loadKeys(); // Load keys from buffers.
// Parse arguments.
for (String arg : args) { for (String arg : args) {
switch (arg.toLowerCase()) { switch (arg.toLowerCase()) {
case "-handbook" -> { case "-handbook" -> Tools.createGmHandbook();
Tools.createGmHandbook(); return; case "-gachamap" -> Tools.createGachaMapping();
}
case "-gachamap" -> {
Tools.createGachaMapping(); return;
}
} }
} }
// Initialize server. // Initialize server.
Grasscutter.getLogger().info(language.Starting_Grasscutter); Grasscutter.getLogger().info(translate("messages.status.starting"));
// Load all resources. // Load all resources.
Grasscutter.updateDayOfWeek(); Grasscutter.updateDayOfWeek();
@ -97,7 +93,7 @@ public final class Grasscutter {
// Create server instances. // Create server instances.
dispatchServer = new DispatchServer(); dispatchServer = new DispatchServer();
gameServer = new GameServer(new InetSocketAddress(getConfig().getGameServerOptions().Ip, getConfig().getGameServerOptions().Port)); gameServer = new GameServer();
// Create a server hook instance with both servers. // Create a server hook instance with both servers.
new ServerHook(gameServer, dispatchServer); new ServerHook(gameServer, dispatchServer);
@ -110,9 +106,9 @@ public final class Grasscutter {
} else if (getConfig().RunMode == ServerRunMode.GAME_ONLY) { } else if (getConfig().RunMode == ServerRunMode.GAME_ONLY) {
gameServer.start(); gameServer.start();
} else { } else {
getLogger().error(language.Invalid_server_run_mode + " " + getConfig().RunMode); getLogger().error(translate("messages.status.run_mode_error", getConfig().RunMode));
getLogger().error(language.Server_run_mode); getLogger().error(translate("messages.status.run_mode_help"));
getLogger().error(language.Shutting_down); getLogger().error(translate("messages.status.shutdown"));
System.exit(1); System.exit(1);
} }
@ -145,41 +141,8 @@ public final class Grasscutter {
} }
public static void loadLanguage() { public static void loadLanguage() {
try (FileReader file = new FileReader(String.format("%s%s.json", getConfig().LANGUAGE_FOLDER, Grasscutter.config.LocaleLanguage))) { var locale = config.LocaleLanguage;
language = gson.fromJson(file, Language.class); language = Language.getLanguage(locale.toLanguageTag());
} catch (Exception e) {
Grasscutter.language = new Language();
Grasscutter.cn_language = new CNLanguage();
Grasscutter.config.LocaleLanguage = Locale.getDefault();
saveConfig();
try {
File folder = new File("./languages");
if (!folder.exists() && !folder.isDirectory()) {
//noinspection ResultOfMethodCallIgnored
folder.mkdirs();
}
} catch (Exception ee) {
Grasscutter.getLogger().error("Unable to create language folder.");
}
try (FileWriter file = new FileWriter("./languages/" + Locale.US + ".json")) {
file.write(gson.toJson(language));
} catch (Exception ee) {
Grasscutter.getLogger().error("Unable to create language file.");
}
try (FileWriter file = new FileWriter("./languages/" + Locale.SIMPLIFIED_CHINESE + ".json")) {
file.write(gson.toJson(cn_language));
} catch (Exception ee) {
Grasscutter.getLogger().error("无法创建简体中文语言文件。");
}
// try again
try (FileReader file = new FileReader(String.format("%s%s.json", getConfig().LANGUAGE_FOLDER, Grasscutter.config.LocaleLanguage))) {
language = gson.fromJson(file, Language.class);
} catch (Exception ee) {
Grasscutter.getLogger().error("Unable to load " + Grasscutter.config.LocaleLanguage + ".json");
}
}
} }
public static void saveConfig() { public static void saveConfig() {
@ -193,11 +156,11 @@ public final class Grasscutter {
public static void startConsole() { public static void startConsole() {
// Console should not start in dispatch only mode. // Console should not start in dispatch only mode.
if (getConfig().RunMode == ServerRunMode.DISPATCH_ONLY) { if (getConfig().RunMode == ServerRunMode.DISPATCH_ONLY) {
getLogger().info(language.Dispatch_mode_not_support_command); getLogger().info(translate("messages.dispatch.no_commands_error"));
return; return;
} }
getLogger().info(language.Start_done); getLogger().info(translate("messages.status.done"));
String input = null; String input = null;
boolean isLastInterrupted = false; boolean isLastInterrupted = false;
while (true) { while (true) {
@ -223,7 +186,7 @@ public final class Grasscutter {
try { try {
CommandMap.getInstance().invoke(null, null, input); CommandMap.getInstance().invoke(null, null, input);
} catch (Exception e) { } catch (Exception e) {
Grasscutter.getLogger().error(language.Command_error, e); Grasscutter.getLogger().error(translate("messages.game.command_error"), e);
} }
} }
} }

View File

@ -1,285 +0,0 @@
package emu.grasscutter.languages;
public final class CNLanguage {
public String An_error_occurred_during_game_update = "游戏更新时发生了错误.";
public String Starting_Grasscutter = "正在开启Grasscutter...";
public String Invalid_server_run_mode = "无效的服务器运行模式. ";
public String Server_run_mode = "服务器运行模式必须为以下几种之一: 'HYBRID'(混合模式), 'DISPATCH_ONLY'(仅dispatch模式), 或 'GAME_ONLY'(仅游戏模式). 无法启动Grasscutter...";
public String Shutting_down = "正在停止....";
public String Start_done = "加载完成!需要指令帮助请输入 \"help\"";
public String Dispatch_mode_not_support_command = "仅dispatch模式无法使用指令。";
public String Command_error = "命令错误:";
public String Error = "出现错误.";
public String Grasscutter_is_free = "Grasscutter是免费软件如果你是花钱买到的你大概被骗了。主页: https://github.com/Grasscutters/Grasscutter";
public String Game_start_port = "游戏服务器已在端口 {port} 上开启。";
public String Client_connect = "来自 {address} 的客户端已连接。";
public String Client_disconnect = "来自 {address} 的客户端已断开。";
public String Client_request = "[Dispatch] 客户端 {ip} 请求: {method} {url}";
public String Not_load_keystore = "[Dispatch] 无法加载证书,正在尝试默认密码...";
public String Use_default_keystore = "[Dispatch] 成功使用默认密码加载证书. 请考虑将config.json中的KeystorePassword项改为123456.";
public String Load_keystore_error = "[Dispatch] 加载证书时出现错误!";
public String Not_find_ssl_cert = "[Dispatch] 未找到SSL证书正在回滚至HTTP模式。";
public String Welcome = "欢迎使用Grasscutter";
public String Potential_unhandled_request = "[Dispatch] 潜在的未处理请求: {method} {url}";
public String Client_login_token = "[Dispatch] 客户端 {ip} 正在尝试使用token登录...";
public String Client_token_login_failed = "[Dispatch] 客户端 {ip} 使用token登录失败。";
public String Client_login_in_token = "[Dispatch] 客户端 {ip} 使用token以 {uid} 的身份登录。";
public String Game_account_cache_error = "游戏账户缓存出现错误。";
public String Wrong_session_key = "会话密钥错误。";
public String Client_exchange_combo_token = "[Dispatch] 客户端 {ip} 成功交换token。";
public String Client_failed_exchange_combo_token = "[Dispatch] 客户端 {ip} 交换token失败。";
public String Dispatch_start_server_port = "[Dispatch] Dispatch服务器已在端口 {port} 上开启。";
public String Client_failed_login_account_create = "[Dispatch] 客户端 {ip} 登录失败: 已创建UID为 {uid} 的账户。";
public String Client_failed_login_account_create_failed = "[Dispatch] 客户端 {ip} 登录失败: 创建账户失败。";
public String Client_failed_login_account_no_found = "[Dispatch] 客户端 {ip} 登录失败: 未找到帐户。";
public String Client_login = "[Dispatch] 客户端 {ip} 以 {uid} 的身份登录。";
public String Username_not_found = "未找到此用户名.";
public String Username_not_found_create_failed = "未找到此用户名, 创建失败。.";
// Command
public String No_command_specified = "未指定命令.";
public String Unknown_command = "未知命令: ";
public String You_not_permission_run_command = "你没有权限运行此命令.";
public String This_command_can_only_run_from_console = "此命令只能在控制台运行.";
public String Run_this_command_in_game = "请在游戏内运行此命令.";
public String Invalid_playerId = "无效的玩家ID.";
public String Player_not_found = "未找到此玩家.";
public String Player_is_offline = "此玩家已离线.";
public String Invalid_item_id = "无效的物品ID.";
public String Invalid_item_or_player_id = "无效的玩家或物品ID.";
public String Enabled = "启用";
public String Disabled = "禁用";
public String No_command_found = "未找到命令.";
public String Help = "帮助";
public String Player_not_found_or_offline = "此玩家不存在或已离线.";
public String Invalid_arguments = "无效的参数.";
public String Success = "成功";
public String Invalid_entity_id = "无效的实体ID.";
// Help
public String Help_usage = " 用法: ";
public String Help_aliases = " 别名: ";
public String Help_available_command = " 可用命令:";
// Account
public String Modify_user_account = "修改用户帐户";
public String Invalid_UID = "无效的UID.";
public String Account_exists = "账户已存在.";
public String Account_create_UID = "UID为 {uid} 的账户已创建.";
public String Account_delete = "已删除账户.";
public String Account_not_find = "账户不存在.";
public String Account_command_usage = "用法: account <create(创建)|delete(删除)> <用户名> [uid]";
// Broadcast
public String Broadcast_command_usage = "用法: broadcast <消息>";
public String Broadcast_message_sent = "消息已发送.";
// ChangeScene
public String Change_screen_usage = "用法: changescene <场景id>";
public String Change_screen_you_in_that_screen = "你已经在此场景中了";
public String Change_screen = "切换到场景 ";
public String Change_screen_not_exist = "此场景不存在。";
// Clear
public String Clear_weapons = "已清除 {name} 的武器.";
public String Clear_artifacts = "已清除 {name} 的圣遗物 .";
public String Clear_materials = "已清除 {name} 的材料.";
public String Clear_furniture = "已清除 {name} 的摆设.";
public String Clear_displays = "已清除 {name} 的displays.";
public String Clear_virtuals = "已清除 {name} 的virtuals.";
public String Clear_everything = "已清除 {name} 的所有物品.";
// Coop
public String Coop_usage = "用法: coop <玩家ID> <房主的玩家ID>";
// Drop
public String Drop_usage = "用法: drop <物品ID|物品名> [数量]";
public String Drop_dropped_of = "已在地上丢弃 {amount} 个 {item}.";
// EnterDungeon
public String EnterDungeon_usage = "用法: enterdungeon <副本 id>";
public String EnterDungeon_changed_to_dungeon = "已进入副本 ";
public String EnterDungeon_dungeon_not_found = "副本不存在";
public String EnterDungeon_you_in_that_dungeon = "你已经在此副本中了。";
// GiveAll
public String GiveAll_usage = "用法: giveall [玩家] [数量]";
public String GiveAll_item = "正在给予所有物品...";
public String GiveAll_done = "完成。";
public String GiveAll_invalid_amount_or_playerId = "无效的数量或玩家ID";
// GiveArtifact
public String GiveArtifact_usage = "用法: giveart|gart [玩家] <圣遗物Id> <主词条Id> [<副词条Id>[,<被强化次数>]]... [等级]";
public String GiveArtifact_invalid_artifact_id = "无效的圣遗物Id.";
public String GiveArtifact_given = "已将 {itemId} 给予 {target}.";
// GiveChar
public String GiveChar_usage = "用法: givechar <p玩家> <角色Id|角色名> [等级]";
public String GiveChar_given = "将等级为 {level} 的 {avatarId} 给予 {target}.";
public String GiveChar_invalid_avatar_id = "无效的角色ID";
public String GiveChar_invalid_avatar_level = "无效的角色等级.";
public String GiveChar_invalid_avatar_or_player_id = "无效的角色ID或玩家ID.";
// Give
public String Give_usage = "用法: give [玩家名] <物品ID|物品名> [数量] [等级] ";
public String Give_refinement_only_applicable_weapons = "精炼只对武器有效。";
public String Give_refinement_must_between_1_and_5 = "精炼等级必须在1和5之间。";
public String Give_given = "已将 {amount} 个 {item} 给与 {target}.";
public String Give_given_with_level_and_refinement = "已将 {amount} 个等级为 {lvl}, 精炼 {refinement} 的 {item} 给予 {target}.";
public String Give_given_level = "已将 {amount} 个等级为 {lvl} 的 {item} 给与 {target}.";
// GodMode
public String Godmode_status = "设置 {name} 的无敌模式为 {status} ";
// Heal
public String Heal_message = "所有角色已被治疗。";
// Kick
public String Kick_player_kick_player = "玩家 [{sendUid}:{sendName}] 已踢出 [{kickUid}:{kickName}]";
public String Kick_server_player = "正在踢出玩家 [{kickUid}:{kickName}]";
// Kill
public String Kill_usage = "用法: killall [玩家UID] [场景ID]";
public String Kill_scene_not_found_in_player_world = "未在玩家世界中找到此场景";
public String Kill_kill_monsters_in_scene = "已杀死场景 {id} 中的 {size} 只怪物。 ";
// KillCharacter
public String KillCharacter_usage = "用法: /killcharacter [玩家Id]";
public String KillCharacter_kill_current_character = "已干掉 {name} 当前的场上角色.";
// List
public String List_message = "现有 {size} 名玩家在线:";
// Permission
public String Permission_usage = "用法: permission <add|remove> <用户名> <权限名>";
public String Permission_add = "权限已添加。";
public String Permission_have_permission = "此玩家已拥有此权限!";
public String Permission_remove = "权限已移除。";
public String Permission_not_have_permission = "此玩家未拥有此权限!";
// Position
public String Position_message = "坐标: {x},{y},{z}\n场景: {id}";
// Reload
public String Reload_reload_start = "正在重新加载配置.";
public String Reload_reload_done = "完成.";
// ResetConst
public String ResetConst_reset_all = "重置你所有角色的命座。";
public String ResetConst_reset_all_done = "{name} 的命座已被重置。请重新登录。";
// ResetShopLimit
public String ResetShopLimit_usage = "用法: /resetshop <玩家id>";
// SendMail
public String SendMail_usage = "用法: give [player] <itemId|itemName> [amount]";
public String SendMail_user_not_exist = "不存在id为 '{id}' 的用户。";
public String SendMail_start_composition = "开始编辑邮件的组成部分.\n请使用 `/sendmail <标题>` 以继续.\n你可以在任何时候使用`/sendmail stop` 来停止编辑.";
public String SendMail_templates = "很快就会有邮件模板了.......";
public String SendMail_invalid_arguments = "无效的参数.\n用法 `/sendmail <用户Id|all|help> [模板Id]``";
public String SendMail_send_cancel = "已取消发送邮件。";
public String SendMail_send_done = "已向 {name} 发送邮件!";
public String SendMail_send_all_done = "已向所有玩家发送邮件!";
public String SendMail_not_composition_end = "邮件组成部分编辑尚未结束.\n请使用 `/sendmail {args}` 或 `/sendmail stop` 来停止编辑";
public String SendMail_Please_use = "请使用 `/sendmail {args}`";
public String SendMail_set_title = "邮件标题已设为 '{title}'.\n使用 '/sendmail <邮件正文>' 以继续.";
public String SendMail_set_contents = "邮件的正文如下:\n '{contents}'\n使用 '/sendmail <发送者署名>' 以继续.";
public String SendMail_set_message_sender = "邮件的发送者已设为 '{send}'.\n使用 '/sendmail <物品Id|物品名|finish(结束编辑并发送)> [数量] [等级]";
public String SendMail_send = "已将 {amount} 个 {item} (等级 {lvl}) 作为邮件附件.\n你可以继续添加附件也可以使用 `/sendmail finish` 来停止编辑并发送邮件.";
public String SendMail_invalid_arguments_please_use = "无效的参数 \n 请使用 `/sendmail {args}`";
public String SendMail_title = "<标题>";
public String SendMail_message = "<正文>";
public String SendMail_sender = "<发送者>";
public String SendMail_arguments = "<物品Id|物品名|finish(结束编辑并发送)> [数量] [等级]";
public String SendMail_error = "错误:无效的编写阶段 {stage}. 需要stacktrace请看服务器命令行.";
// SendMessage
public String SendMessage_usage = "用法: sendmessage <玩家名> <消息>";
public String SenaMessage_message_sent = "已发送.";
// SetFetterLevel
public String SetFetterLevel_usage = "用法: setfetterlevel <等级>";
public String SetFetterLevel_fetter_level_must_between_0_and_10 = "设置的好感等级必须位于 0 和 10 之间。";
public String SetFetterLevel_fetter_set_level = "好感等级已设置为 {level}";
public String SetFetterLevel_invalid_fetter_level = "无效的好感等级。";
// SetStats
public String SetStats_usage = "用法: setstats|stats <stat> <value>";
public String SetStats_setstats_help_message = "用法: /setstats|stats <hp(生命值) | mhp(最大生命值) | def(防御力) | atk(攻击) | em(元素精通) | crate(暴击率) | cdmg(暴击伤害)> <数值> 基本属性(整数)";
public String SetStats_stats_help_message = "用法: /stats <er(元素充能) | epyro(火伤) | ecryo(冰伤) | ehydro(水伤) | eanemo(风伤) | egeo(岩伤) | edend(草伤) | eelec(雷伤) | ephys(物伤)> <数值> 元素属性(百分比)";
public String SetStats_set_max_hp = "最大生命值已设为 {int}.";
public String SetStats_set_max_hp_error = "无效的生命数值.";
public String SetStats_set_hp = "生命设置为 {int}.";
public String SetStats_set_hp_error = "无效的生命数值.";
public String SetStats_set_def = "防御力设置为 {int}.";
public String SetStats_set_def_error = "无效的防御力数值.";
public String SetStats_set_atk = "攻击力设置为 {int}.";
public String SetStats_set_atk_error = "无效的攻击力数值.";
public String SetStats_set_em = "元素精通设置为 {int}.";
public String SetStats_set_em_error = "无效的元素精通数值.";
public String SetStats_set_er = "元素充能设置为 {int}%.";
public String SetStats_set_er_error = "无效的元素充能数值.";
public String SetStats_set_cr = "暴击率设置为 {int}%.";
public String SetStats_set_cr_error = "无效的暴击率数值.";
public String SetStats_set_cd = "暴击伤害设置为 {int}%.";
public String SetStats_set_cd_error = "无效的暴击伤害数值.";
public String SetStats_set_pdb = "火伤设置为 {int}%.";
public String SetStats_set_pdb_error = "无效的火伤数值.";
public String SetStats_set_cdb = "冰伤设置为 {int}%.";
public String SetStats_set_cdb_error = "无效的冰伤数值.";
public String SetStats_set_hdb = "水伤设置为 {int}%.";
public String SetStats_set_hdb_error = "无效的水伤数值.";
public String SetStats_set_adb = "风伤设置为 {int}%.";
public String SetStats_set_adb_error = "无效的风伤数值.";
public String SetStats_set_gdb = "岩伤设置为 {int}%.";
public String SetStats_set_gdb_error = "无效的岩伤数值.";
public String SetStats_set_edb = "雷伤设置为 {int}%.";
public String SetStats_set_edb_error = "无效的雷伤数值.";
public String SetStats_set_physdb = "物伤设置为 {int}%.";
public String SetStats_set_physdb_error = "无效的物伤数值.";
public String SetStats_set_ddb = "草伤设置为 {int}%.";
public String SetStats_set_ddb_error = "无效的草伤数值.";
// SetWorldLevel
public String SetWorldLevel_usage = "用法: setworldlevel <level>";
public String SetWorldLevel_world_level_must_between_0_and_8 = "世界等级必须在0-8之间。";
public String SetWorldLevel_set_world_level = "世界等级已设置为 {level}.";
public String SetWorldLevel_invalid_world_level = "无效的世界等级.";
// Spawn
public String Spawn_usage = "用法: spawn <实体ID|实体名> [数量] [等级(仅限怪物)]";
public String Spawn_message = "已生成 {amount} 个 {id}.";
// Stop
public String Stop_message = "正在关闭服务器...";
// Talent
public String Talent_usage_1 = "设置技能等级: /talent set <技能ID> <数值>";
public String Talent_usage_2 = "另一种方式: /talent <n 或 e 或 q> <数值>";
public String Talent_usage_3 = "获取技能ID: /talent getid";
public String Talent_lower_16 = "技能等级应低于16。";
public String Talent_set_atk = "设置普通攻击等级为 {level}.";
public String Talent_set_e = "设置元素战技(e技能)等级为 {level}.";
public String Talent_set_q = "设置元素爆发(q技能)等级为 {level}.";
public String Talent_invalid_skill_id = "无效的技能ID。";
public String Talent_set_this = "技能等级已设为 {level}.";
public String Talent_invalid_talent_level = "无效的技能等级。";
public String Talent_normal_attack_id = "普通攻击技能ID {id}.";
public String Talent_e_skill_id = "元素战技(e技能)ID {id}.";
public String Talent_q_skill_id = "元素爆发(q技能)ID {id}.";
// TeleportAll
public String TeleportAll_message = "此命令仅在多人游戏下可用。";
// Teleport
public String Teleport_usage_server = "用法: /tp @<玩家ID> <x> <y> <z> [场景ID]";
public String Teleport_usage = "用法: /tp @<玩家ID不指定则为你自己> <x> <y> <z> [场景ID]";
public String Teleport_specify_player_id = "你必须指定一个玩家。";
public String Teleport_invalid_position = "无效的位置。";
public String Teleport_message = "已将 {name} 传送到场景 {id} ,坐标 {x},{y},{z}";
// Weather
public String Weather_usage = "用法: weather <天气ID> [气候ID]";
public String Weather_message = "已修改天气为 {weatherId} 气候为 {climateId}";
public String Weather_invalid_id = "无效的ID。";
}

View File

@ -1,300 +0,0 @@
package emu.grasscutter.languages;
public final class Language {
public String An_error_occurred_during_game_update = "An error occurred during game update.";
public String Starting_Grasscutter = "Starting Grasscutter...";
public String Invalid_server_run_mode = "Invalid server run mode.";
public String Server_run_mode = "Server run mode must be 'HYBRID', 'DISPATCH_ONLY', or 'GAME_ONLY'. Unable to start Grasscutter...";
public String Shutting_down = "Shutting down...";
public String Start_done = "Done! For help, type \"help\"";
public String Dispatch_mode_not_support_command = "Commands are not supported in dispatch only mode.";
public String Command_error = "Command error:";
public String Error = "An error occurred.";
public String Grasscutter_is_free = "Grasscutter is FREE software. If you have paid for this, you may have been scammed. Homepage: https://github.com/Grasscutters/Grasscutter";
public String Game_start_port = "Game Server started on port {port}";
public String Client_connect = "Client connected from {address}";
public String Client_disconnect = "Client disconnected from {address}";
public String Client_request = "[Dispatch] Client {ip} {method} request: {url}";
public String Not_load_keystore = "[Dispatch] Unable to load keystore. Trying default keystore password...";
public String Use_default_keystore = "[Dispatch] The default keystore password was loaded successfully. Please consider setting the password to 123456 in config.json.";
public String Load_keystore_error = "[Dispatch] Error while loading keystore!";
public String Not_find_ssl_cert = "[Dispatch] No SSL cert found! Falling back to HTTP server.";
public String Welcome = "Welcome to Grasscutter";
public String Potential_unhandled_request = "[Dispatch] Potential unhandled {method} request: {url}";
public String Client_try_login = "[Dispatch] Client {ip} is trying to log in";
public String Client_login_token = "[Dispatch] Client {ip} is trying to log in via token";
public String Client_token_login_failed = "[Dispatch] Client {ip} failed to log in via token";
public String Client_login_in_token = "[Dispatch] Client {ip} logged in via token as {uid}";
public String Game_account_cache_error = "Game account cache information error";
public String Wrong_session_key = "Wrong session key.";
public String Client_exchange_combo_token = "[Dispatch] Client {ip} succeed to exchange combo token";
public String Client_failed_exchange_combo_token = "[Dispatch] Client {ip} failed to exchange combo token";
public String Dispatch_start_server_port = "[Dispatch] Dispatch server started on port {port}";
public String Client_failed_login_account_create = "[Dispatch] Client {ip} failed to log in: Account {uid} created";
public String Client_failed_login_account_create_failed = "[Dispatch] Client {ip} failed to log in: Account create failed";
public String Client_failed_login_account_no_found = "[Dispatch] Client {ip} failed to log in: Account no found";
public String Client_login = "[Dispatch] Client {ip} logged in as {uid}";
public String Username_not_found = "Username not found.";
public String Username_not_found_create_failed = "Username not found, create failed.";
public String Create_resources_folder = "Creating resources folder...";
public String Place_copy = "Place a copy of 'BinOutput' and 'ExcelBinOutput' in the resources folder.";
// Command
public String No_command_specified = "No command specified.";
public String Unknown_command = "Unknown command: ";
public String You_not_permission_run_command = "You do not have permission to run this command.";
public String This_command_can_only_run_from_console = "This command can only be run from the console.";
public String Run_this_command_in_game = "Run this command in-game.";
public String Invalid_amount = "Invalid amount.";
public String Invalid_arguments = "Invalid arguments.";
public String Invalid_artifact_id = "Invalid artifact ID.";
public String Invalid_avatar_id = "Invalid avatar id.";
public String Invalid_avatar_level = "Invalid avatar level.";
public String Invalid_entity_id = "Invalid entity id.";
public String Invalid_item_id = "Invalid item id.";
public String Invalid_item_level = "Invalid item level.";
public String Invalid_item_refinement = "Invalid item refinement level.";
public String Invalid_playerId = "Invalid playerId.";
public String Invalid_UID = "Invalid UID.";
public String Player_not_found = "Player not found.";
public String Player_is_offline = "Player is offline.";
public String Enabled = "enabled";
public String Disabled = "disabled";
public String No_command_found = "No command found.";
public String Help = "Help";
public String Player_not_found_or_offline = "Player not found or offline.";
public String Success = "Success";
public String Target_cleared = "Target cleared.";
public String Target_set = "Subsequent commands will target @{uid} by default.";
public String Target_needed = "This command requires a target UID. Add a <@UID> argument or set a persistent target with /target @UID.";
// Help
public String Help_usage = " Usage: ";
public String Help_aliases = " Aliases: ";
public String Help_available_command = "Available commands:";
// Account
public String Modify_user_account = "Modify user accounts";
public String Account_exists = "Account already exists.";
public String Account_create_UID = "Account created with UID {uid}.";
public String Account_delete = "Account deleted.";
public String Account_not_find = "Account not found.";
public String Account_command_usage = "Usage: account <create|delete> <username> [uid]";
// Broadcast
public String Broadcast_command_usage = "Usage: broadcast <message>";
public String Broadcast_message_sent = "Message sent.";
// ChangeScene
public String Change_screen_usage = "Usage: changescene <scene id>";
public String Change_screen_you_in_that_screen = "You are already in that scene";
public String Change_screen = "Changed to scene ";
public String Change_screen_not_exist = "Scene does not exist";
// Clear
public String Clear_usage = "Usage: clear <all|wp|art|mat>";
public String Clear_weapons = "Cleared weapons for {name} .";
public String Clear_artifacts = "Cleared artifacts for {name} .";
public String Clear_materials = "Cleared materials for {name} .";
public String Clear_furniture = "Cleared furniture for {name} .";
public String Clear_displays = "Cleared displays for {name} .";
public String Clear_virtuals = "Cleared virtuals for {name} .";
public String Clear_everything = "Cleared everything for {name} .";
// Coop
public String Coop_usage = "Usage: coop <host UID>";
public String Coop_success = "Summoned {target} to {host}'s world.";
// Drop
public String Drop_usage = "Usage: drop <itemId|itemName> [amount]";
public String Drop_dropped_of = "Dropped {amount} of {item}.";
// EnterDungeon
public String EnterDungeon_usage = "Usage: enterdungeon <dungeon id>";
public String EnterDungeon_changed_to_dungeon = "Changed to dungeon ";
public String EnterDungeon_dungeon_not_found = "Dungeon does not exist";
public String EnterDungeon_you_in_that_dungeon = "You are already in that dungeon";
// GiveAll
public String GiveAll_usage = "Usage: giveall [amount]";
public String GiveAll_item = "Giving all items...";
public String GiveAll_done = "Giving all items done";
// GiveArtifact
public String GiveArtifact_usage = "Usage: giveart|gart [player] <artifactId> <mainPropId> [<appendPropId>[,<times>]]... [level]";
public String GiveArtifact_given = "Given {itemId} to {target}.";
// GiveChar
public String GiveChar_usage = "Usage: givechar <player> <itemId|itemName> [amount]";
public String GiveChar_given = "Given {avatarId} with level {level} to {target}.";
// Give
public String Give_usage = "Usage: give <player> <itemId|itemName> [amount] [level]";
public String Give_refinement_only_applicable_weapons = "Refinement is only applicable to weapons.";
public String Give_refinement_must_between_1_and_5 = "Refinement must be between 1 and 5.";
public String Give_given = "Given {amount} of {item} to {target}.";
public String Give_given_with_level_and_refinement = "Given {item} with level {lvl}, refinement {refinement} {amount} times to {target}";
public String Give_given_level = "Given {item} with level {lvl} {amount} times to {target}";
// GodMode
public String Godmode_usage = "Usage: godmode [on|off|toggle]";
public String Godmode_status = "Godmode is now {status} for {name}.";
// Heal
public String Heal_message = "All characters have been healed.";
// Kick
public String Kick_player_kick_player = "Player [{sendUid}:{sendName}] has kicked player [{kickUid}:{kickName}]";
public String Kick_server_player = "Kicking player [{kickUid}:{kickName}]";
// Kill
public String Kill_usage = "Usage: killall [playerUid] [sceneId]";
public String Kill_scene_not_found_in_player_world = "Scene not found in player world";
public String Kill_kill_monsters_in_scene = "Killing {size} monsters in scene {id}";
// KillCharacter
public String KillCharacter_usage = "Usage: /killcharacter [playerId]";
public String KillCharacter_kill_current_character = "Killed {name} current character.";
// List
public String List_message = "There are {size} player(s) online:";
// Permission
public String Permission_usage = "Usage: permission <add|remove> <permission>";
public String Permission_add = "Permission added.";
public String Permission_have_permission = "They already have this permission!";
public String Permission_remove = "Permission removed.";
public String Permission_not_have_permission = "They don't have this permission!";
// Position
public String Position_message = "Coord: {x}, {y}, {z}\nScene id: {id}";
// Reload
public String Reload_reload_start = "Reloading config.";
public String Reload_reload_done = "Reload complete.";
// ResetConst
public String ResetConst_reset_all = "Reset all avatars' constellations.";
public String ResetConst_reset_all_done = "Constellations for {name} have been reset. Please relog to see changes.";
// ResetShopLimit
public String ResetShopLimit_usage = "Usage: /resetshop <player id>";
// SendMail
public String SendMail_usage = "Usage: give [player] <itemId|itemName> [amount]";
public String SendMail_user_not_exist = "The user with an id of '{id}' does not exist";
public String SendMail_start_composition = "Starting composition of message.\nPlease use `/sendmail <title>` to continue.\nYou can use `/sendmail stop` at any time";
public String SendMail_templates = "Mail templates coming soon implemented...";
public String SendMail_invalid_arguments = "Invalid arguments.\nUsage `/sendmail <userId|all|help> [templateId]`";
public String SendMail_send_cancel = "Message sending cancelled";
public String SendMail_send_done = "Message sent to user {name}!";
public String SendMail_send_all_done = "Message sent to all users!";
public String SendMail_not_composition_end = "Message composition not at final stage.\nPlease use `/sendmail {args}` or `/sendmail stop` to cancel";
public String SendMail_please_use = "Please use `/sendmail {args}`";
public String SendMail_set_title = "Message title set as '{title}'.\nUse '/sendmail <content>' to continue.";
public String SendMail_set_contents = "Message contents set as '{contents}'.\nUse '/sendmail <sender>' to continue.";
public String SendMail_set_message_sender = "Message sender set as '{send}'.\nUse '/sendmail <itemId|itemName|finish> [amount] [level]' to continue.";
public String SendMail_send = "Attached {amount} of {item} (level {lvl}) to the message.\nContinue adding more items or use `/sendmail finish` to send the message.";
public String SendMail_invalid_arguments_please_use = "Invalid arguments \n Please use `/sendmail {args}`";
public String SendMail_title = "<title>";
public String SendMail_message = "<message>";
public String SendMail_sender = "<sender>";
public String SendMail_arguments = "<itemId|itemName|finish> [amount] [level]";
public String SendMail_error = "ERROR: invalid construction stage {stage}. Check console for stacktrace.";
// SendMessage
public String SendMessage_usage = "Usage: sendmessage <player> <message>";
public String SenaMessage_message_sent = "Message sent.";
// SetFetterLevel
public String SetFetterLevel_usage = "Usage: setfetterlevel <level>";
public String SetFetterLevel_fetter_level_must_between_0_and_10 = "Fetter level must be between 0 and 10.";
public String SetFetterLevel_fetter_set_level = "Fetter level set to {level}";
public String SetFetterLevel_invalid_fetter_level = "Invalid fetter level.";
// SetStats
public String SetStats_usage_console = "Usage: setstats|stats @<UID> <stat> <value>";
public String SetStats_usage_ingame = "Usage: setstats|stats [@UID] <stat> <value>";
public String SetStats_help_message = """
\n\tValues for <stat>: hp | maxhp | def | atk | em | er | crate | cdmg | cdr | heal | heali | shield | defi
\t(cont.) Elemental DMG Bonus: epyro | ecryo | ehydro | egeo | edendro | eelectro | ephys
\t(cont.) Elemental RES: respyro | rescryo | reshydro | resgeo | resdendro | reselectro | resphys
""";
public String SetStats_value_error = "Invalid stat value.";
public String SetStats_set_self = "{name} set to {value}.";
public String SetStats_set_for_uid = "{name} for {uid} set to {value}.";
public String Stats_FIGHT_PROP_MAX_HP = "Max HP";
public String Stats_FIGHT_PROP_CUR_HP = "Current HP";
public String Stats_FIGHT_PROP_CUR_ATTACK = "ATK";
public String Stats_FIGHT_PROP_BASE_ATTACK = "Base ATK";
public String Stats_FIGHT_PROP_DEFENSE = "DEF";
public String Stats_FIGHT_PROP_ELEMENT_MASTERY = "Elemental Mastery";
public String Stats_FIGHT_PROP_CHARGE_EFFICIENCY = "Energy Recharge";
public String Stats_FIGHT_PROP_CRITICAL = "Crit Rate";
public String Stats_FIGHT_PROP_CRITICAL_HURT = "Crit DMG";
public String Stats_FIGHT_PROP_ADD_HURT = "DMG Bonus";
public String Stats_FIGHT_PROP_WIND_ADD_HURT = "Anemo DMG Bonus";
public String Stats_FIGHT_PROP_ICE_ADD_HURT = "Cryo DMG Bonus";
public String Stats_FIGHT_PROP_GRASS_ADD_HURT = "Dendro DMG Bonus";
public String Stats_FIGHT_PROP_ELEC_ADD_HURT = "Electro DMG Bonus";
public String Stats_FIGHT_PROP_ROCK_ADD_HURT = "Geo DMG Bonus";
public String Stats_FIGHT_PROP_WATER_ADD_HURT = "Hydro DMG Bonus";
public String Stats_FIGHT_PROP_FIRE_ADD_HURT = "Pyro DMG Bonus";
public String Stats_FIGHT_PROP_PHYSICAL_ADD_HURT = "Physical DMG Bonus";
public String Stats_FIGHT_PROP_SUB_HURT = "DMG Reduction";
public String Stats_FIGHT_PROP_WIND_SUB_HURT = "Anemo RES";
public String Stats_FIGHT_PROP_ICE_SUB_HURT = "Cryo RES";
public String Stats_FIGHT_PROP_GRASS_SUB_HURT = "Dendro RES";
public String Stats_FIGHT_PROP_ELEC_SUB_HURT = "Electro RES";
public String Stats_FIGHT_PROP_ROCK_SUB_HURT = "Geo RES";
public String Stats_FIGHT_PROP_WATER_SUB_HURT = "Hydro RES";
public String Stats_FIGHT_PROP_FIRE_SUB_HURT = "Pyro RES";
public String Stats_FIGHT_PROP_PHYSICAL_SUB_HURT = "Physical RES";
public String Stats_FIGHT_PROP_SKILL_CD_MINUS_RATIO = "Cooldown Reduction";
public String Stats_FIGHT_PROP_HEAL_ADD = "Healing Bonus";
public String Stats_FIGHT_PROP_HEALED_ADD = "Incoming Healing Bonus";
public String Stats_FIGHT_PROP_SHIELD_COST_MINUS_RATIO = "Shield Strength";
public String Stats_FIGHT_PROP_DEFENCE_IGNORE_RATIO = "DEF Ignore";
// SetWorldLevel
public String SetWorldLevel_usage = "Usage: setworldlevel <level>";
public String SetWorldLevel_world_level_must_between_0_and_8 = "World level must be between 0-8";
public String SetWorldLevel_set_world_level = "World level set to {level}.";
public String SetWorldLevel_invalid_world_level = "Invalid world level.";
// Spawn
public String Spawn_usage = "Usage: spawn <entityId> [amount] [level(monster only)]";
public String Spawn_message = "Spawned {amount} of {id}.";
// Stop
public String Stop_message = "Server shutting down...";
// Talent
public String Talent_usage_1 = "To set talent level: /talent set <talentID> <value>";
public String Talent_usage_2 = "Another way to set talent level: /talent <n or e or q> <value>";
public String Talent_usage_3 = "To get talent ID: /talent getid";
public String Talent_lower_16 = "Invalid talent level. Level should be lower than 16";
public String Talent_set_id = "Set talent {id} to {level}.";
public String Talent_set_atk = "Set talent Normal ATK to {level}.";
public String Talent_set_e = "Set talent E to {level}.";
public String Talent_set_q = "Set talent Q to {level}.";
public String Talent_invalid_skill_id = "Invalid skill ID.";
public String Talent_set_this = "Set this talent to {level}.";
public String Talent_invalid_talent_level = "Invalid talent level.";
public String Talent_normal_attack_id = "Normal Attack ID {id}.";
public String Talent_e_skill_id = "E skill ID {id}.";
public String Talent_q_skill_id = "Q skill ID {id}.";
// TeleportAll
public String TeleportAll_message = "You only can use this command in MP mode.";
// Teleport
public String Teleport_usage = "Usage: /tp <x> <y> <z> [scene id]";
public String Teleport_invalid_position = "Invalid position.";
public String Teleport_message = "Teleported {name} to {x},{y},{z} in scene {id}";
// Weather
public String Weather_usage = "Usage: weather <weatherId> [climateId]";
public String Weather_message = "Changed weather to {weatherId} with climate {climateId}";
public String Weather_invalid_id = "Invalid ID.";
}

View File

@ -1,2 +0,0 @@
# Grasscutter Plugin API
**Warning!** As of now, this is a work in progress and isn't completely documented.

View File

@ -0,0 +1,82 @@
package emu.grasscutter.utils;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import emu.grasscutter.Grasscutter;
import javax.annotation.Nullable;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
public final class Language {
private final JsonObject languageData;
private final Map<String, String> cachedTranslations = new HashMap<>();
/**
* Creates a language instance from a code.
* @param langCode The language code.
* @return A language instance.
*/
public static Language getLanguage(String langCode) {
return new Language(langCode + ".json");
}
/**
* Returns the translated value from the key while substituting arguments.
* @param key The key of the translated value to return.
* @param args The arguments to substitute.
* @return A translated value with arguments substituted.
*/
public static String translate(String key, Object... args) {
return Grasscutter.getLanguage().get(key).formatted(args);
}
/**
* Reads a file and creates a language instance.
* @param fileName The name of the language file.
*/
private Language(String fileName) {
@Nullable JsonObject languageData = null;
try {
InputStream file = Grasscutter.class.getResourceAsStream("/lang/" + fileName);
languageData = Grasscutter.getGsonFactory().fromJson(Utils.readFromInputStream(file), JsonObject.class);
} catch (Exception exception) {
Grasscutter.getLogger().error("Failed to load language file: " + fileName, exception);
} this.languageData = languageData;
}
/**
* Returns the value (as a string) from a nested key.
* @param key The key to look for.
* @return The value (as a string) from a nested key.
*/
public String get(String key) {
if(this.cachedTranslations.containsKey(key)) {
return this.cachedTranslations.get(key);
}
String[] keys = key.split("\\.");
JsonObject object = this.languageData;
int index = 0;
String result = "";
while (true) {
if(index == keys.length) break;
String currentKey = keys[index++];
if(object.has(currentKey)) {
JsonElement element = object.get(currentKey);
if(element.isJsonObject())
object = element.getAsJsonObject();
else {
result = element.getAsString(); break;
}
} else break;
}
this.cachedTranslations.put(key, result); return result;
}
}