From 79ef5aadd6740992b8df28a326f15b8f42ae059e Mon Sep 17 00:00:00 2001 From: tiansongyu <478880214@qq.com> Date: Mon, 5 Oct 2020 08:59:46 +0800 Subject: [PATCH] Add a multi-language option and a Chinese option --- .gitmodules | 2 +- Makefile | 2 +- icon.jpg | Bin 24557 -> 13682 bytes include/about_tab.hpp | 2 - include/fs.hpp | 227 +++++++++++++++++++++++++++++++ include/lang.hpp | 35 +++++ include/language_option_page.hpp | 18 +++ include/tools_tab.hpp | 3 + pack_release.sh | 0 res/lang/ch.json | 150 ++++++++++++++++++++ res/lang/en.json | 149 ++++++++++++++++++++ source/JC_page.cpp | 26 ++-- source/about_tab.cpp | 11 +- source/app_page.cpp | 20 +-- source/changelog_page.cpp | 41 +++--- source/cheats_page.cpp | 19 +-- source/choice_page.cpp | 3 +- source/confirm_page.cpp | 9 +- source/download_payload_page.cpp | 20 +-- source/exclude_page.cpp | 9 +- source/extract.cpp | 3 +- source/lang.cpp | 122 +++++++++++++++++ source/language_option_page.cpp | 44 ++++++ source/list_download_tab.cpp | 46 +++---- source/main.cpp | 52 +++++++ source/main_frame.cpp | 20 +-- source/payload_page.cpp | 17 +-- source/tools_tab.cpp | 31 +++-- source/utils.cpp | 19 ++- 29 files changed, 947 insertions(+), 153 deletions(-) mode change 100755 => 100644 icon.jpg create mode 100644 include/fs.hpp create mode 100644 include/lang.hpp create mode 100644 include/language_option_page.hpp mode change 100755 => 100644 pack_release.sh create mode 100644 res/lang/ch.json create mode 100644 res/lang/en.json create mode 100644 source/lang.cpp create mode 100644 source/language_option_page.cpp diff --git a/.gitmodules b/.gitmodules index 469d0ff..000826a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,4 +3,4 @@ url = https://github.com/HamletDuFromage/zipper [submodule "lib/borealis"] path = lib/borealis - url = https://github.com/natinusala/borealis + url = https://github.com/tiansongyu/borealis diff --git a/Makefile b/Makefile index a68d241..fc65044 100644 --- a/Makefile +++ b/Makefile @@ -47,7 +47,7 @@ APP_TITLE := All-in-One Switch Updater APP_AUTHOR := HamletDuFromage APP_VERSION := 1.1.3 -#ROMFS := $(BUILD)/romfs +ROMFS = res BOREALIS_PATH := lib/borealis #BOREALIS_RESOURCES := romfs:/borealis/ #APP_RESOURCES := romfs:/ diff --git a/icon.jpg b/icon.jpg old mode 100755 new mode 100644 index 8a62d5942fc317bd14504c98fbbcdb5fd152b2e3..a6f7f4a9a6b43b3dc52a5fc20aa602b30ef0ee02 GIT binary patch literal 13682 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 K0+S}Elm8cpLvbGf diff --git a/include/about_tab.hpp b/include/about_tab.hpp index 1ed38e8..6b9e1ff 100644 --- a/include/about_tab.hpp +++ b/include/about_tab.hpp @@ -2,8 +2,6 @@ #include -#include - class AboutTab : public brls::List { public: diff --git a/include/fs.hpp b/include/fs.hpp new file mode 100644 index 0000000..f6dd830 --- /dev/null +++ b/include/fs.hpp @@ -0,0 +1,227 @@ +#pragma once + +#include +#include +#include +#include + +namespace fs { + +struct Directory { + FsDir handle = {}; + + constexpr inline Directory() = default; + constexpr inline Directory(const FsDir &handle): handle(handle) { } + + inline ~Directory() { + this->close(); + } + + inline Result open(FsFileSystem *fs, const std::string &path) { + auto tmp = path; + tmp.reserve(FS_MAX_PATH); // Fails otherwise + return fsFsOpenDirectory(fs, tmp.c_str(), FsDirOpenMode_ReadDirs | FsDirOpenMode_ReadFiles, &this->handle); + } + + inline void close() { + fsDirClose(&this->handle); + } + + inline bool is_open() const { + // TODO: Better heuristic? + return this->handle.s.session; + } + + inline std::size_t count() { + std::int64_t count = 0; + fsDirGetEntryCount(&this->handle, &count); + return count; + } + + std::vector list() { + std::int64_t total = 0; + auto c = this->count(); + auto entries = std::vector(c); + fsDirRead(&this->handle, &total, c, entries.data()); + return entries; + } +}; + +struct File { + FsFile handle = {}; + + constexpr inline File() = default; + constexpr inline File(const FsFile &handle): handle(handle) { } + + inline ~File() { + this->close(); + } + + inline Result open(FsFileSystem *fs, const std::string &path, std::uint32_t mode = FsOpenMode_Read) { + auto tmp = path; + tmp.reserve(FS_MAX_PATH); + return fsFsOpenFile(fs, tmp.c_str(), mode, &this->handle); + } + + inline void close() { + fsFileClose(&this->handle); + } + + inline bool is_open() const { + return this->handle.s.session; + } + + inline std::size_t size() { + std::int64_t tmp = 0; + fsFileGetSize(&this->handle, &tmp); + return tmp; + } + + inline void size(std::size_t size) { + fsFileSetSize(&this->handle, static_cast(size)); + } + + inline std::size_t read(void *buf, std::size_t size, std::size_t offset = 0) { + std::uint64_t tmp = 0; + auto rc = fsFileRead(&this->handle, static_cast(offset), buf, static_cast(size), FsReadOption_None, &tmp); + if (R_FAILED(rc)) + printf("Read failed with %#x\n", rc); + return tmp; + } + + inline void write(const void *buf, std::size_t size, std::size_t offset = 0) { + fsFileWrite(&this->handle, static_cast(offset), buf, size, FsWriteOption_None); + } + + inline void flush() { + fsFileFlush(&this->handle); + } +}; + +struct Filesystem { + FsFileSystem handle = {}; + + constexpr inline Filesystem() = default; + constexpr inline Filesystem(const FsFileSystem &handle): handle(handle) { } + + inline ~Filesystem() { + this->close(); + } + + inline Result open(FsBisPartitionId id) { + return fsOpenBisFileSystem(&this->handle, id, ""); + } + + inline Result open_sdmc() { + return fsOpenSdCardFileSystem(&this->handle); + } + + inline void close() { + flush(); + fsFsClose(&this->handle); + } + + inline bool is_open() const { + return this->handle.s.session; + } + + inline Result flush() { + return fsFsCommit(&this->handle); + } + + inline std::size_t total_space() { + std::int64_t tmp = 0; + fsFsGetTotalSpace(&this->handle, "/", &tmp); + return tmp; + } + + inline std::size_t free_space() { + std::int64_t tmp = 0; + fsFsGetFreeSpace(&this->handle, "/", &tmp); + return tmp; + } + + inline Result open_directory(Directory &d, const std::string &path) { + return d.open(&this->handle, path); + } + + inline Result open_file(File &f, const std::string &path, std::uint32_t mode = FsOpenMode_Read) { + return f.open(&this->handle, path, mode); + } + + inline Result create_directory(const std::string &path) { + return fsFsCreateDirectory(&this->handle, path.c_str()); + } + + inline Result create_file(const std::string &path, std::size_t size = 0) { + return fsFsCreateFile(&this->handle, path.c_str(), static_cast(size), 0); + } + + inline Result copy_file(const std::string &source, const std::string &destination) { + File source_f, dest_f; + if (auto rc = this->open_file(source_f, source) | this->open_file(dest_f, destination, FsOpenMode_Write); R_FAILED(rc)) + return rc; + + constexpr std::size_t buf_size = 0x100000; // 1 MiB + auto buf = std::vector(buf_size); + + std::size_t size = source_f.size(), offset = 0; + while (size) { + auto read = source_f.read(static_cast(buf.data()), buf_size, offset); + dest_f.write(buf.data(), read, offset); + offset += read; + size -= read; + } + + source_f.close(); + dest_f.close(); + + return 0; + } + + inline FsDirEntryType get_path_type(const std::string &path) { + FsDirEntryType type; + fsFsGetEntryType(&this->handle, path.c_str(), &type); + return type; + } + + inline bool is_directory(const std::string &path) { + return get_path_type(path) == FsDirEntryType_Dir; + } + + inline bool is_file(const std::string &path) { + return get_path_type(path) == FsDirEntryType_File; + } + + inline FsTimeStampRaw get_timestamp(const std::string &path) { + FsTimeStampRaw ts = {}; + fsFsGetFileTimeStampRaw(&this->handle, path.c_str(), &ts); + return ts; + } + + inline std::uint64_t get_timestamp_created(const std::string &path) { + return this->get_timestamp(path).created; + } + + inline std::uint64_t get_timestamp_modified(const std::string &path) { + return this->get_timestamp(path).modified; + } + + inline Result move_directory(const std::string &old_path, const std::string &new_path) { + return fsFsRenameDirectory(&this->handle, old_path.c_str(), new_path.c_str()); + } + + inline Result move_file(const std::string &old_path, const std::string &new_path) { + return fsFsRenameFile(&this->handle, old_path.c_str(), new_path.c_str()); + } + + inline Result delete_directory(const std::string &path) { + return fsFsDeleteDirectoryRecursively(&this->handle, path.c_str()); + } + + inline Result delete_file(const std::string &path) { + return fsFsDeleteFile(&this->handle, path.c_str()); + } +}; + +} // namespace fs diff --git a/include/lang.hpp b/include/lang.hpp new file mode 100644 index 0000000..76701e3 --- /dev/null +++ b/include/lang.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include +namespace lang { + +enum class Language { + English, + Chinese, + /* French, + Dutch, + Italian, + German, + Spanish,*/ + Default, +}; + +const nlohmann::json &get_json(); + +Language get_current_language(); +Result set_language(Language lang); +Result initialize_to_system_language(); + +std::string get_string(std::string key, const nlohmann::json &json = get_json()); + +namespace literals { + +inline std::string operator ""_lang(const char *key, size_t size) { + return get_string(std::string(key, size)); +} + +} // namespace literals + +}; // namespace lang diff --git a/include/language_option_page.hpp b/include/language_option_page.hpp new file mode 100644 index 0000000..f600eb2 --- /dev/null +++ b/include/language_option_page.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include +#include "app_page.hpp" +#include "exclude_page.hpp" + +class LanguageOptionPage : public brls::AppletFrame +{ + private: + brls::List* list; + brls::ListItem* English; + brls::ListItem* Chinese; + + brls::StagedAppletFrame* stagedFrame; + + public: + LanguageOptionPage(); +}; \ No newline at end of file diff --git a/include/tools_tab.hpp b/include/tools_tab.hpp index e9764e2..5a918aa 100644 --- a/include/tools_tab.hpp +++ b/include/tools_tab.hpp @@ -7,6 +7,7 @@ #include "payload_page.hpp" #include "download_payload_page.hpp" #include "changelog_page.hpp" +#include "language_option_page.hpp" #include "JC_page.hpp" #include "extract.hpp" #include "utils.hpp" @@ -20,6 +21,8 @@ class ToolsTab : public brls::List brls::ListItem* rebootPayload; brls::ListItem* downloadPayload; brls::ListItem* changelog; + brls::ListItem* language; + brls::StagedAppletFrame* stagedFrame; public: diff --git a/pack_release.sh b/pack_release.sh old mode 100755 new mode 100644 diff --git a/res/lang/ch.json b/res/lang/ch.json new file mode 100644 index 0000000..8ad0ad0 --- /dev/null +++ b/res/lang/ch.json @@ -0,0 +1,150 @@ +{ + "about_tab.cpp":"", + "About_Title": "多工具合一任天堂Switch更新器", + "copyright": "多工具合一任天堂Switch更新器遵循 GPL-3.0 协议\n\u00A9 2020 HamletDuFromage", + "Disclaimers": "\uE016 除了从主Gbatemp线程镜像的金手指外,本软件作者不拥有任何金手指。所有的信用归于各自的所有者\n\uE016 链接每三个小时刷新一次. 如果链接在3小时后仍然有问题,请到Github给作者 issue.\n", + + "app_page.cpp":"", + "app_title": "安装金手指", + "app_label": "以下游戏在您上次使用该应用程序时已经收到作弊代码更新。请注意,尽管已经更新下载的游戏,作弊可能不匹配其当前更新", + "text_download": "下载中:\n最新的金手指\n\n来自:\n", + "text_download_list": "下载最新的金手指", + "text_title": "获取金手指", + "Downloading": "下载中...", + "Extracting": "解压中...", + "All_done": "完成!", + "changelog_page.cpp":"", + "Changelog": "更新日志", + "v1_0_1": "v1.0.1", + "v1_0_1_text": "\uE016 增加了询问ini文件的对话框.\n\uE016 修复在没有链接到网络时更新软件链接的问题.\n\uE016 小更新.", + "v1_0_2": "v1.0.2", + "v1_0_2_text": "\uE016 修复ini文件处理不当安装签名时,现在提示用户是否需要替换hetake_ipl.ini文件", + "v1_0_3": "v1.0.3", + "v1_0_3_text": "\uE016 修复进度条在解压时有时被卡住的问题", + "v1_1_0": "v1.1.0", + "v1_1_0_text": "\uE016 增加了一个选项下载有效载荷'/bootloader/注入文件'.\n\uE016 清理了一些东西,使.ini覆盖更干净。\n", + "v1_1_1": "v1.1.1", + "v1_1_1_text": "\uE016 增加了下载/解压前的安全检查\n\uE016 增加了复制注入文件到'/atmosphere/reboot_payload.bin的可能性'\n\uE016 在'工具'中添加了更新日志\n", + "v1_1_2": "v1.1.2", + "v1_1_2_text": "\uE016 Added GUI to disable cheat updates for specific titles.", + "v1_1_3": "v1.1.3", + "v1_1_3_text": "\uE016 现在显示最新安装的金手指版本.\n\uE016 如果有新的更新可用,现在在应用程序标题中警告", + "Ok_button": "确定", + + "cheats_page.cpp":"", + "cheat_menu": "金手指菜单", + "cheat_view": "查看已安装金手指", + "cheat_exclude": "下载的金手指中,不存在可用金手指的游戏", + "cheat_delete_all_ex": "删除所有现有的金手指", + "cheat_delete_all_cheat": "删除所有金手指", + "cheat_Deleting": "删除中", + "cheat_All_done": "完成!", + + "choice_page.cpp":"", + "choice_yes":"是", + "choice_no":"否", + + "chnfirm_page.cpp":"", + "Back": "返回", + "Continue": "继续", + + "download_payload_page.cpp":"", + "Download_payloads": "下载注入程序", + "select": "选择要下载的注入文件'", + "Download": "下载中:\n", + "from": "\n\n从:\n", + "getting_paylaod": "获取注入文件", + "down": "下载中...", + "download_all_done": "完成!", + "description": "如果点击后界面没有反应,请确认switch是否连接网络\n如果检查后问题依然存在,请到github上给我提交issue", + "back": "返回", + + "exclude_page.cpp":"", + "exclude_titles": "不包括的游戏", + "you_can": "你可以用这个菜单关闭金手指更新", + "save": "保存并返回", + + "JC_page.cpp":"", + "joy_con": "Joy-Con 颜色更换器", + "jc_you_can_1": "你可以改变游戏机里的手柄颜色,确保手柄已经插入switch.\n颜色风格文件存储在 ' ", + "jc_you_can_goto": "'. 去这里 'http://bit.ly/JC-color' ", + "jc_you_can_2": "去制作你自己的颜色主题文件", + "jc_backup": "备份当前主题文件", + "jc_color": "手柄颜色修改器", + "jc_backing": "备份当前的颜色配置文件,确定手柄已经插入switch如果进程卡住,请拔插手柄反复试一下.", + "jc_all_done": "完成!", + "jc_con_color": "手柄颜色转换器", + "jc_change": "改变颜色,确保手柄已经被插入.如果进程卡住,请拔插手柄反复试一下 ", + "jc_all_": "完成,你可能需要拔插手柄来启动刚才的颜色更新", + + + "Language_option_page.cpp":"", + "Language_Option":"语言设置", + "reset_machine":"语言已经改变,请重启程序更换新语言", + + + "list_donwload.cpp":"", + "Getting": "获取 ", + "firmware_text": "\uE016 固件从 'https://darthsternie.net/switch-firmwares/ 下载'. 下载之后,文件会被解压到 '/firmware'. 你可以使用 Daybreak 或者 ChoiDuJour (大白兔)安装他们.\n\uE016 当前FW版本: ", + "currentCeatsver": "\uE016 这个金手指的下载和更新地址是 'gbatemp.net'. 你的机器中没有的游戏,这些金手指不会解压到你的SD卡中 你可以在'工具->金手指菜单' 关闭金手指更新.\n\uE016 当前金手指版本: ", + "operation_1": "数字签名", + "list_sigpatches": "\uE016 数字签名允许你运行没被任天堂收录的nsp文件. 确保为您的设置选择了正确的签名 (pure Atmosphere (大气层)or Hekate+Atmosphere).", + "operation_2": "固件", + "list_not": "没有找到", + "list_latest": "最新版本", + "list_app": "应用", + "list_cfw": "自制系统", + "list_main": "\uE016 主要 Switch 破解系统. 如果你使用 Atmosphere with Hekate, 下载 Atmosphere, 然后 Hekate.", + "list_latest_ver": "最新版本 (版本 ", + "list_cheats": "金手指", + "list_down": "下载中:\n", + "list_from": "\n\n从:\n", + "list_downing": "下载中...", + "list_extracting": "解压中...", + "list_All": "完成!", + "list_could_done": "如果点击功能后,界面没有反应,确保switch已经连接互联网\n如果问题依然存在\n请到github(https://github.com/HamletDuFromage/AIO-switch-updater)上为作者提交issue.", + + "main_frame.cpp":"", + "main_app": " - 最版本可用,请更新最新版本", + "main_v": " 版本号", + "main_about": "介绍", + "main_update_cfw": "更新自制系统版本", + "main_update_si": "更新数字签名", + "main_firmwares": "下载破解固件", + "main_cheats": "下载金手指", + "main_tools": "工具", + + "payload_page.cpp":"", + "payload_reboot": "重启菜单", + "payload_select": "选择一个注入文件重启.", + "payload_set": "设置 reboot_payload.bin", + "payload_success": "成功复制 '", + "payload_to": "' to '", + "payload_ok": "确定", + "payload_shut": "停止", + "payload_reboot_2": "重启", + + "tools_tab.cpp":"", + "tool_cheats": "金手指菜单", + "tool_change": "改变手柄颜色", + "tool_download": "下载注入文件存储到 ", + "tool_inject": "启用注入文件", + "tool_update": "更新软件版本 (v", + "tool_DownLoad": "下载中:\nAIO-switch-updater\n\n从:\n", + "tool_updating": "更新软件", + "tool_downloading":"下载中...", + "tool_extracting": "解压中....", + "tool_all_done": " 成功完成!", + "tool_changelog": "更新日志", + + "utils.cpp":"", + "utils_because": "由于 FW archive的文件大小, 仅支持在 Applet Mode 模式下更新固件. (百度查询进入applet Mode模式方法)请让程序在Applet Mode下启动", + "utils_ok": "确定", + "utils_do": "你想要覆盖已经存在的", + "utils_no": "否", + "utils_yes": "是", + "utils_the": "下载的文件没有数字签名认证. 可能是由于链接出问题了.如果3个小时还有类似的问题, 请到github上给作者提出issue.", + "utils_the_downloaded": "下载的文件没有数字签名认证. 可能是由于链接出问题了.如果3个小时还有类似的问题, 请到github上给作者提出issue", + "ultils_overwrite": "你想要覆盖已经存在的.ini 设置文件吗?", + "ultis_file": "下载的文件CFW出现问题. 可能是由于链接出问题了.如果3个小时还有类似的问题, 请到github上给作者提出issue." +} diff --git a/res/lang/en.json b/res/lang/en.json new file mode 100644 index 0000000..257c959 --- /dev/null +++ b/res/lang/en.json @@ -0,0 +1,149 @@ +{ + "about_tab.cpp":"", + "About_Title": "All-in-One Nintendo Switch Updater", + "copyright": "AIO-switch-updater is licensed under GPL-3.0\n\u00A9 2020 HamletDuFromage", + "Disclaimers": "\uE016 Aside from cheat codes that are mirrored from the main Gbatemp thread, HamletDuFromage isn't hosting anything. All credits go to respective owners\n\uE016 Links are refreshed every three hours. If a link remains broken after 3 hours have passed, please open a Github issue.\n", + + "app_page.cpp":"", + "app_title": "Installed cheats", + "app_label": "The following titles have recieved cheat code updates the last time you used the app. Please note that despite having been downloaded for a game, cheats may not match its current update.", + "text_download": "Downloading:\nLatest cheat codes\n\nFrom:\n", + "text_download_list": "Download latest cheat codes", + "text_title": "Getting cheat codes", + "Downloading": "Downloading...", + "Extracting": "Extracting...", + "All_done": "All done!", + + "changelog_page.cpp":"", + "Changelog":"Changelog", + "v1_0_1": "v1.0.1", + "v1_0_1_text": "\uE016 Added dialogue box asking about ini files.\n\uE016 Fixed update app link when not connected to the internet.\n\uE016 Minor fixes here and there.", + "v1_0_2": "v1.0.2", + "v1_0_2_text": "\uE016 Fixed .ini files being handled poorly when installing sigpatches. Now prompts the user if they want to replace hetake_ipl.ini.", + "v1_0_3": "v1.0.3", + "v1_0_3_text": "\uE016 Fixed progress bar sometimes being stuck when extracting.", + "v1_1_0": "v1.1.0", + "v1_1_0_text": "\uE016 Added an option to download payloads to '/bootloader/payloads'.\n\uE016 Cleaned up some stuff, made .ini overwriting cleaner.\n", + "v1_1_1": "v1.1.1", + "v1_1_1_text": "\uE016 Added some safety checks before downloading/extracting.\n\uE016 Added the possibility to copy a payload to '/atmosphere/reboot_payload.bin'\n\uE016 Added changelog in 'Tools'\n", + "v1_1_2": "v1.1.2", + "v1_1_2_text": "\uE016 Added GUI to disable cheat updates for specific titles.", + "v1_1_3": "v1.1.3", + "v1_1_3_text": "\uE016 Now displays the latest installed cheat version.\n\uE016 Now warns in the app title if a new update is available.", + "Ok_button": "Ok", + + "cheats_page.cpp":"", + "cheat_menu": "Cheats menu", + "cheat_view": "View installed cheats", + "cheat_exclude": "Exclude games from recieving cheat updates", + "cheat_delete_all_ex": "Delete all existing cheat codes", + "cheat_delete_all_cheat": "Delete all cheats", + "cheat_Deleting": "Deleting", + "cheat_All_done": "All done!", + + "choice_page.cpp":"", + "choice_yes":"yes", + "choice_no":"no", + + "chnfirm_page.cpp":"", + "Back": "Back", + "Continue": "Continue", + + "download_payload_page.cpp":"", + "Download_payloads": "Download payloads", + "select": "Select a payload to download to '", + "Download": "Downloading:\n", + "from": "\n\nFrom:\n", + "getting_paylaod": "getting payload files", + "down": "Downloading...", + "download_all_done": "All done!", + "description": "Could not find a download link, make sure the Switch has access to the internet.\nIf this problem persists, please open an issue on Github", + "back": "Back", + + "Language_option_page.cpp":"", + "Language_Option":"Language Option", + "reset_machine":"language has changed .please reboot the app to use the new language", + + "exclude_page.cpp":"", + "exclude_titles": "Exclude titles", + "you_can": "You can turn off cheat updates with this menu", + "save": "Save choice and return", + + "JC_page.cpp":"", + "joy_con": "Joy-Con color swapper", + "jc_you_can_1": "You can change the internal color of your Joy-Cons. Make sure they're docked.\nColor profiles are stored in '", + "jc_you_can_goto": "'. Go to 'http://bit.ly/JC-color' ", + "jc_you_can_2": "to generate your own custom profiles.", + "jc_backup": "Backup current color profile", + "jc_color": "Joy-Con color swapper", + "jc_backing": "Backing up the current color profile. Make sure the Joy-Con are docked. If the process hangs, try docking/undocking the JCs.", + "jc_all_done": "All done!", + "jc_con_color": "Joy-Con color swapper", + "jc_change": "Changing color. Make sure the Joy-Con are docked. If the process hangs, try docking/undocking the JCs.", + "jc_all_": "All done! You may need to dock/undock your Joy-Cons for the change to take effect", + + "list_donwload.cpp":"", + "Getting": "Getting ", + "firmware_text": "\uE016 Firmware dumps from 'https://darthsternie.net/switch-firmwares/'. Once downloaded, it will be extracted in '/firmware'. You can then install the update through Daybreak or ChoiDuJour.\n\uE016 Current FW: ", + "currentCeatsver": "\uE016 This will download a daily updated archive of cheat codes from 'gbatemp.net'. Cheat codes for games you don't have installed won't be extracted to your SD card. You can turn off cheat updated in 'Tools->Cheat menu'.\n\uE016 Current cheats version: ", + "operation_1": "sigpatches", + "list_sigpatches": "\uE016 Sigpatches allow your Switch to install and run unofficial NSP file. Make sure you pick the correct sigpatches for your setup (pure Atmosphere or Hekate+Atmosphere).", + "operation_2": "firmware", + "list_not": "not found", + "list_latest": "Latest version", + "list_app": "app", + "list_cfw": "CFW", + "list_main": "\uE016 Main Switch CFWs. If you want to use Atmosphere with Hekate, download Atmosphere, then Hekate.", + "list_latest_ver": "Latest (ver ", + "list_cheats": "cheats", + "list_down": "Downloading:\n", + "list_from": "\n\nFrom:\n", + "list_downing": "Downloading...", + "list_extracting": "Extracting...", + "list_All": "All done!", + "list_could_done": "Could not find a download link, make sure the Switch has access to the internet.\nIf this problem persists, please open an issue on Github.", + + "main_frame.cpp":"", + "main_app": " - New app update available", + "main_v": " v", + "main_about": "About", + "main_update_cfw": "Update CFW", + "main_update_si": "Update sigpatches", + "main_firmwares": "Download firmwares", + "main_cheats": "Download cheats", + "main_tools": "Tools", + + "payload_page.cpp":"", + "payload_reboot": "Reboot menu", + "payload_select": " Select a payload to reboot to.", + "payload_set": "Set as reboot_payload.bin", + "payload_success": "Successfully copied '", + "payload_to": "' to '", + "payload_ok": "Ok", + "payload_shut": "Shut Down", + "payload_reboot_2": "Reboot", + + "tools_tab.cpp":"", + "tool_cheats": "Cheats menu", + "tool_change": "Change the Joy-Cons color", + "tool_download": "Dowload payloads to ", + "tool_inject": "Inject payload", + "tool_update": "Update the app (v", + "tool_DownLoad": "Downloading:\nAIO-switch-updater\n\nFrom:\n", + "tool_updating": "Updating app", + "tool_downloading":"Downloading...", + "tool_extracting": "Extracting....", + "tool_all_done": " All done!", + "tool_changelog": "Changelog", + + "utils.cpp":"", + "utils_because": "Because of the size of the FW archive, downloading firmwares in Applet Mode is not supported. Please launch the app with full RAM access.", + "utils_ok": "Ok", + "utils_do": " Do you want to overwrite existing ", + "utils_no": "No", + "utils_yes": "Yes", + "utils_the": "The downloaded file is not a sigpatches archive. This is most likely due to a broken link. If the problem persists after more than 3 hours, please open an issue on Github.", + "utils_the_downloaded": "The downloaded file is not a firmware archive. This is most likely due to a broken link. If the problem persists after more than 3 hours, please open an issue on Github.", + "ultils_overwrite": "Do you want to overwrite existing .ini config files?", + "ultis_file": "The downloaded file is not a CFW archive. This is most likely due to a broken link. If the problem persists after more than 3 hours, please open an issue on Github." +} diff --git a/source/JC_page.cpp b/source/JC_page.cpp index 2d25fa8..7b34ee4 100644 --- a/source/JC_page.cpp +++ b/source/JC_page.cpp @@ -1,26 +1,25 @@ #include "JC_page.hpp" - +#include "lang.hpp" +using namespace lang::literals; JCPage::JCPage() : AppletFrame(true, true) { - this->setTitle("Joy-Con color swapper"); + this->setTitle("joy_con"_lang); list = new brls::List(); - std::string labelText = "You can change the internal color of your Joy-Cons. Make sure they're docked.\n"\ - "Color profiles are stored in '" + std::string(COLOR_PROFILES_PATH) + "'. Go to 'http://bit.ly/JC-color' "\ - "to generate your own custom profiles."; + std::string labelText = "jc_you_can_1"_lang + std::string(COLOR_PROFILES_PATH) + "jc_you_can_goto"\ + "jc_you_can_2"_lang; label = new brls::Label(brls::LabelStyle::DESCRIPTION, labelText, true); list->addView(label); - backup = new brls::ListItem("Backup current color profile"); + backup = new brls::ListItem("jc_backup"_lang); backup->getClickEvent()->subscribe([&](brls::View* view) { brls::StagedAppletFrame* stagedFrame = new brls::StagedAppletFrame(); - stagedFrame->setTitle("Joy-Con color swapper"); + stagedFrame->setTitle("jc_color"_lang); stagedFrame->addStage( - new WorkerPage(stagedFrame, "Backing up the current color profile. Make sure the Joy-Con are docked. "\ - "If the process hangs, try docking/undocking the JCs.", + new WorkerPage(stagedFrame, "jc_backing"_lang, [](){backupJCColor(COLOR_PROFILES_PATH);}) ); stagedFrame->addStage( - new ConfirmPage(stagedFrame, "All done!", true) + new ConfirmPage(stagedFrame, "jc_all_done"_lang, true) ); brls::Application::pushView(stagedFrame); }); @@ -38,14 +37,13 @@ JCPage::JCPage() : AppletFrame(true, true) items[i] = new brls::ListItem(names[i]); items[i]->getClickEvent()->subscribe([&, value](brls::View* view) { brls::StagedAppletFrame* stagedFrame = new brls::StagedAppletFrame(); - stagedFrame->setTitle("Joy-Con color swapper"); + stagedFrame->setTitle("jc_concolor"_lang); stagedFrame->addStage( - new WorkerPage(stagedFrame, "Changing color. Make sure the Joy-Con are docked. "\ - "If the process hangs, try docking/undocking the JCs.", + new WorkerPage(stagedFrame, "jc_changing"_lang, [value](){changeJCColor(value);}) ); stagedFrame->addStage( - new ConfirmPage(stagedFrame, "All done! You may need to dock/undock your Joy-Cons for the change to take effect", true) + new ConfirmPage(stagedFrame, "jc_all_"_lang, true) ); brls::Application::pushView(stagedFrame); }); diff --git a/source/about_tab.cpp b/source/about_tab.cpp index 106a3d7..e52611e 100644 --- a/source/about_tab.cpp +++ b/source/about_tab.cpp @@ -1,11 +1,13 @@ #include "about_tab.hpp" +#include "lang.hpp" +using namespace lang::literals; AboutTab::AboutTab() { // Subtitle brls::Label *subTitle = new brls::Label( brls::LabelStyle::REGULAR, - "All-in-One Nintendo Switch Updater", + "About_Title"_lang , true ); subTitle->setHorizontalAlign(NVG_ALIGN_CENTER); @@ -14,8 +16,7 @@ AboutTab::AboutTab() // Copyright brls::Label *copyright = new brls::Label( brls::LabelStyle::DESCRIPTION, - "AIO-switch-updater is licensed under GPL-3.0\n" \ - "\u00A9 2020 HamletDuFromage", + "copyright"_lang, true ); copyright->setHorizontalAlign(NVG_ALIGN_CENTER); @@ -25,9 +26,7 @@ AboutTab::AboutTab() this->addView(new brls::Header("Disclaimers")); brls::Label *links = new brls::Label( brls::LabelStyle::SMALL, - "\uE016 Aside from cheat codes that are mirrored from the main Gbatemp thread, "\ - "HamletDuFromage isn't hosting anything. All credits go to respective owners\n"\ - "\uE016 Links are refreshed every three hours. If a link remains broken after 3 hours have passed, please open a Github issue.\n", + "Disclaimers"_lang, true ); this->addView(links); diff --git a/source/app_page.cpp b/source/app_page.cpp index f1c354b..a386127 100644 --- a/source/app_page.cpp +++ b/source/app_page.cpp @@ -1,14 +1,14 @@ #include "app_page.hpp" //TODO: Serialize it in extract.cpp - +#include "lang.hpp" +using namespace lang::literals; AppPage::AppPage() : AppletFrame(true, true) { - this->setTitle("Installed cheats"); + this->setTitle("app_title"_lang); list = new brls::List(); label = new brls::Label( brls::LabelStyle::DESCRIPTION, - "The following titles have recieved cheat code updates the last time you used the app. Please note that despite having been "\ - "downloaded for a game, cheats may not match its current update.", + "app_label"_lang, true ); list->addView(label); @@ -60,7 +60,7 @@ AppPage::AppPage() : AppletFrame(true, true) i++; } } - std::string text("Downloading:\nLatest cheat codes\n\nFrom:\n"); + std::string text("text_download"); std::string url = ""; switch(getCFW()){ case ams: @@ -74,22 +74,22 @@ AppPage::AppPage() : AppletFrame(true, true) break; } text += url; - download = new brls::ListItem("Download latest cheat codes"); + download = new brls::ListItem("text_download_list"_lang); archiveType type = cheats; download->getClickEvent()->subscribe([&, url, text, type](brls::View* view) { brls::StagedAppletFrame* stagedFrame = new brls::StagedAppletFrame(); - stagedFrame->setTitle("Getting cheat codes"); + stagedFrame->setTitle("text_title"_lang); stagedFrame->addStage( new ConfirmPage(stagedFrame, text) ); stagedFrame->addStage( - new WorkerPage(stagedFrame, "Downloading...", [url, type](){downloadArchive(url, type);}) + new WorkerPage(stagedFrame, "Downloading"_lang, [url, type](){downloadArchive(url, type);}) ); stagedFrame->addStage( - new WorkerPage(stagedFrame, "Extracting...", [type](){extractArchive(type);}) + new WorkerPage(stagedFrame, "Extracting"_lang, [type](){extractArchive(type);}) ); stagedFrame->addStage( - new ConfirmPage(stagedFrame, "All done!", true) + new ConfirmPage(stagedFrame, "All_done"_lang, true) ); brls::Application::pushView(stagedFrame); }); diff --git a/source/changelog_page.cpp b/source/changelog_page.cpp index e9d1a9a..9bfd3e3 100644 --- a/source/changelog_page.cpp +++ b/source/changelog_page.cpp @@ -1,39 +1,34 @@ #include "changelog_page.hpp" - +#include "lang.hpp" +using namespace lang::literals; ChangelogPage::ChangelogPage() : AppletFrame(true, true) { - this->setTitle("Changelog"); + this->setTitle("Changelog"_lang); list = new brls::List(); std::vector verTitles; std::string change; std::vector changes; - verTitles.push_back("v1.0.1"); - changes.push_back("\uE016 Added dialogue box asking about ini files.\n"\ - "\uE016 Fixed update app link when not connected to the internet.\n"\ - "\uE016 Minor fixes here and there."); + verTitles.push_back("v1_0_1"_lang); + changes.push_back("v1_0_1_text"_lang); - verTitles.push_back("v1.0.2"); - changes.push_back("\uE016 Fixed .ini files being handled poorly when installing sigpatches. Now prompts the user if they want to replace hetake_ipl.ini."); + verTitles.push_back("v1_0_2"_lang); + changes.push_back("v1_0_2_text"_lang); - verTitles.push_back("v1.0.3"); - changes.push_back("\uE016 Fixed progress bar sometimes being stuck when extracting."); + verTitles.push_back("v1_0_3"_lang); + changes.push_back("v1_0_3_text"_lang); - verTitles.push_back("v1.1.0"); - changes.push_back("\uE016 Added an option to download payloads to '/bootloader/payloads'.\n"\ - "\uE016 Cleaned up some stuff, made .ini overwriting cleaner.\n"); + verTitles.push_back("v1_1_0"_lang); + changes.push_back("v1_1_0_text"_lang); - verTitles.push_back("v1.1.1"); - changes.push_back("\uE016 Added some safety checks before downloading/extracting.\n"\ - "\uE016 Added the possibility to copy a payload to '/atmosphere/reboot_payload.bin'\n"\ - "\uE016 Added changelog in 'Tools'\n"); + verTitles.push_back("v1_1_1"_lang); + changes.push_back("v1_1_1_text"_lang); - verTitles.push_back("v1.1.2"); - changes.push_back("\uE016 Added GUI to disable cheat updates for specific titles."); + verTitles.push_back("v1_1_2"_lang); + changes.push_back("v1_1_2_text"_lang); - verTitles.push_back("v1.1.3"); - changes.push_back("\uE016 Now displays the latest installed cheat version.\n"\ - "\uE016 Now warns in the app title if a new update is available."); + verTitles.push_back("v1_1_3"_lang); + changes.push_back("v1_1_3_text"_lang); int nbVersions = verTitles.size(); items.reserve(nbVersions); @@ -45,7 +40,7 @@ ChangelogPage::ChangelogPage() : AppletFrame(true, true) brls::GenericEvent::Callback callback = [dialog](brls::View* view) { dialog->close(); }; - dialog->addButton("Ok", callback); + dialog->addButton("Ok_button"_lang, callback); dialog->setCancelable(true); dialog->open(); }); diff --git a/source/cheats_page.cpp b/source/cheats_page.cpp index c905324..0512b2a 100644 --- a/source/cheats_page.cpp +++ b/source/cheats_page.cpp @@ -1,32 +1,33 @@ #include "cheats_page.hpp" - +#include "lang.hpp" +using namespace lang::literals; CheatsPage::CheatsPage() : AppletFrame(true, true) { - this->setTitle("Cheats menu"); + this->setTitle("cheat_menu"_lang); list = new brls::List(); - view = new brls::ListItem("View installed cheats"); + view = new brls::ListItem("cheat_view"_lang); view->getClickEvent()->subscribe([&](brls::View* view){ brls::Application::pushView(new AppPage()); }); list->addView(view); - exclude = new brls::ListItem("Exclude games from recieving cheat updates"); + exclude = new brls::ListItem("cheat_exclude"_lang); exclude->getClickEvent()->subscribe([&](brls::View* view){ brls::Application::pushView(new ExcludePage()); }); list->addView(exclude); - - deleteCheats = new brls::ListItem("Delete all existing cheat codes"); + + deleteCheats = new brls::ListItem("cheat_delete_all_ex"_lang); deleteCheats->getClickEvent()->subscribe([&](brls::View* view){ stagedFrame = new brls::StagedAppletFrame(); - stagedFrame->setTitle("Delete all cheats"); + stagedFrame->setTitle("cheat_delete_all_cheat"_lang); stagedFrame->addStage( - new WorkerPage(stagedFrame, "Deleting", [](){removeCheats(getCFW());}) + new WorkerPage(stagedFrame, "cheat_Deleting"_lang , [](){removeCheats(getCFW());}) ); stagedFrame->addStage( - new ConfirmPage(stagedFrame, "All done!", true) + new ConfirmPage(stagedFrame, "cheat_All_done"_lang, true) ); brls::Application::pushView(stagedFrame); }); diff --git a/source/choice_page.cpp b/source/choice_page.cpp index b16335f..479b793 100644 --- a/source/choice_page.cpp +++ b/source/choice_page.cpp @@ -1,5 +1,6 @@ #include "choice_page.hpp" - +#include "lang.hpp" +using namespace lang::literals; ChoicePage::ChoicePage(brls::StagedAppletFrame* frame, std::string text) { this->yes = (new brls::Button(brls::ButtonStyle::BORDERLESS))->setLabel("yes"); diff --git a/source/confirm_page.cpp b/source/confirm_page.cpp index accefc1..5df84a4 100644 --- a/source/confirm_page.cpp +++ b/source/confirm_page.cpp @@ -1,8 +1,9 @@ #include "confirm_page.hpp" - +#include "lang.hpp" +using namespace lang::literals; ConfirmPage::ConfirmPage(brls::StagedAppletFrame* frame, std::string text, bool done): done(done) { - this->button = (new brls::Button(brls::ButtonStyle::BORDERLESS))->setLabel(done ? "Back": "Continue"); + this->button = (new brls::Button(brls::ButtonStyle::BORDERLESS))->setLabel(done ? "Back"_lang: "Continue"_lang); this->button->setParent(this); this->button->getClickEvent()->subscribe([frame, this](View* view) { if (!frame->isLastStage()) frame->nextStage(); @@ -15,7 +16,7 @@ ConfirmPage::ConfirmPage(brls::StagedAppletFrame* frame, std::string text, bool this->label->setHorizontalAlign(NVG_ALIGN_CENTER); this->label->setParent(this); - this->registerAction("Back", brls::Key::B, [this] { + this->registerAction("Back"_lang, brls::Key::B, [this] { brls::Application::popView(); return true; }); @@ -26,7 +27,7 @@ void ConfirmPage::draw(NVGcontext* vg, int x, int y, unsigned width, unsigned he if(!this->done){ auto end = std::chrono::high_resolution_clock::now(); auto missing = std::max(1l - std::chrono::duration_cast(end - start).count(), 0l); - auto text = std::string("Continue"); + auto text = std::string("Continue"_lang); if (missing > 0) { this->button->setLabel(text + " (" + std::to_string(missing) + ")"); this->button->setState(brls::ButtonState::DISABLED); diff --git a/source/download_payload_page.cpp b/source/download_payload_page.cpp index 43e8443..2056bd7 100644 --- a/source/download_payload_page.cpp +++ b/source/download_payload_page.cpp @@ -1,12 +1,13 @@ #include "download_payload_page.hpp" - +#include "lang.hpp" +using namespace lang::literals; DownloadPayloadPage::DownloadPayloadPage() : AppletFrame(true, true) { - this->setTitle("Download payloads"); + this->setTitle("Download_payloads"_lang); list = new brls::List(); label = new brls::Label( brls::LabelStyle::DESCRIPTION, - "Select a payload to download to '" + std::string(BOOTLOADER_PL_PATH) + "'.", + "select"_lang + std::string(BOOTLOADER_PL_PATH) + "'."_lang, true ); list->addView(label); @@ -17,20 +18,20 @@ DownloadPayloadPage::DownloadPayloadPage() : AppletFrame(true, true) for (int i = 0; i(links)[i]; std::string path = std::string(BOOTLOADER_PL_PATH) + std::get<0>(links)[i]; - std::string text("Downloading:\n" + std::get<0>(links)[i] + "\n\nFrom:\n" + url); + std::string text("Download"_lang + std::get<0>(links)[i] + "from"_lang+ url); items[i] = new brls::ListItem(std::get<0>(links)[i]); items[i]->getClickEvent()->subscribe([&, text, url, path](brls::View* view) { createTree(BOOTLOADER_PL_PATH); brls::StagedAppletFrame* stagedFrame = new brls::StagedAppletFrame(); - stagedFrame->setTitle("Getting payload"); + stagedFrame->setTitle("getting_paylaod"_lang); stagedFrame->addStage( new ConfirmPage(stagedFrame, text) ); stagedFrame->addStage( - new WorkerPage(stagedFrame, "Downloading...", [url, path](){downloadFile(url.c_str(), path.c_str(), OFF);}) + new WorkerPage(stagedFrame, "down"_lang, [url, path](){downloadFile(url.c_str(), path.c_str(), OFF);}) ); stagedFrame->addStage( - new ConfirmPage(stagedFrame, "All done!", true) + new ConfirmPage(stagedFrame, "download_all_done"_lang, true) ); brls::Application::pushView(stagedFrame); }); @@ -40,13 +41,12 @@ DownloadPayloadPage::DownloadPayloadPage() : AppletFrame(true, true) else{ notFound = new brls::Label( brls::LabelStyle::DESCRIPTION, - "Could not find a download link, make sure the Switch has access to the internet.\n"\ - "If this problem persists, please open an issue on Github", + "description"_lang, true ); notFound->setHorizontalAlign(NVG_ALIGN_CENTER); list->addView(notFound); - brls::ListItem* back = new brls::ListItem("Back"); + brls::ListItem* back = new brls::ListItem("back"_lang); back->getClickEvent()->subscribe([&](brls::View* view) { brls::Application::popView(); }); diff --git a/source/exclude_page.cpp b/source/exclude_page.cpp index bc46187..f54a627 100644 --- a/source/exclude_page.cpp +++ b/source/exclude_page.cpp @@ -1,12 +1,13 @@ #include "exclude_page.hpp" - +#include "lang.hpp" +using namespace lang::literals; ExcludePage::ExcludePage() : AppletFrame(true, true) { - this->setTitle("Exclude titles"); + this->setTitle("exclude_titles"_lang); list = new brls::List(); label = new brls::Label( brls::LabelStyle::DESCRIPTION, - "You can turn off cheat updates with this menu", + "you_can"_lang, true ); list->addView(label); @@ -65,7 +66,7 @@ ExcludePage::ExcludePage() : AppletFrame(true, true) i++; } - list->registerAction("Save choice and return", brls::Key::B, [this] { + list->registerAction("save"_lang, brls::Key::B, [this] { std::set exclude{}; for(int i = 0; i < (int) std::get<1>(items).size(); i++){ if(!std::get<0>(items)[i]->getToggleState()){ diff --git a/source/extract.cpp b/source/extract.cpp index c5be214..21e3766 100644 --- a/source/extract.cpp +++ b/source/extract.cpp @@ -1,7 +1,8 @@ #include "extract.hpp" #include "utils.hpp" #include "download.hpp" - +#include "lang.hpp" +using namespace lang::literals; void extract(const char * filename, const char* workingPath, int overwriteInis){ ProgressEvent::instance().reset(); ProgressEvent::instance().setStep(1); diff --git a/source/lang.cpp b/source/lang.cpp new file mode 100644 index 0000000..78d2373 --- /dev/null +++ b/source/lang.cpp @@ -0,0 +1,122 @@ +#include +#include + +#include +#include + +using json = nlohmann::json; + +namespace lang { + +namespace { + +static json lang_json = nullptr; +static Language current_language = Language::Default; + +} // namespace + +const json &get_json() { + return lang_json; +} + +Language get_current_language() { + return current_language; +} + +Result set_language(Language lang) { + const char *path; + current_language = lang; + switch (lang) { + case Language::Chinese: + path = "romfs:/lang/ch.json"; + break; + //if you need add a new language + //use it ! + /*case Language::French: + path = "romfs:/lang/fr.json"; + break; + case Language::Dutch: + path = "romfs:/lang/nl.json"; + break; + case Language::Italian: + path = "romfs:/lang/it.json"; + break; + case Language::German: + path = "romfs:/lang/de.json"; + break; + case Language::Spanish: + path = "romfs:/lang/es.json"; + break;*/ + case Language::English: + case Language::Default: + default: + path = "romfs:/lang/en.json"; + break; + } + + auto *fp = fopen(path, "r"); + if (!fp) + return 1; + + fseek(fp, 0, SEEK_END); + std::size_t size = ftell(fp); + fseek(fp, 0, SEEK_SET); + + std::string contents(size, 0); + + if (auto read = fread(contents.data(), 1, size, fp); read != size) + return read; + fclose(fp); + + lang_json = json::parse(contents); + + return 0; +} + +Result initialize_to_system_language() { + if (auto rc = setInitialize(); R_FAILED(rc)) { + setExit(); + return rc; + } + + u64 l; + if (auto rc = setGetSystemLanguage(&l); R_FAILED(rc)) { + setExit(); + return rc; + } + + SetLanguage sl; + if (auto rc = setMakeLanguage(l, &sl); R_FAILED(rc)) { + setExit(); + return rc; + } + + switch (sl) { + case SetLanguage_ENGB: + case SetLanguage_ENUS: + return set_language(Language::English); + case SetLanguage_ZHCN: + case SetLanguage_ZHHANS: + return set_language(Language::Chinese); + //if you need add a new language + //use it ! + /*case SetLanguage_FR: + return set_language(Language::French); + case SetLanguage_NL: + return set_language(Language::Dutch); + case SetLanguage_IT: + return set_language(Language::Italian); + case SetLanguage_DE: + return set_language(Language::German); + case SetLanguage_ES: + return set_language(Language::Spanish);*/ + default: + return set_language(Language::Default); + } +} + +std::string get_string(std::string key, const json &json) { + return json.value(key, key); +} + +} // namespace lang diff --git a/source/language_option_page.cpp b/source/language_option_page.cpp new file mode 100644 index 0000000..665441c --- /dev/null +++ b/source/language_option_page.cpp @@ -0,0 +1,44 @@ +#include "language_option_page.hpp" +#include "lang.hpp" +#include +using namespace lang::literals; + +//this page is for language page + +LanguageOptionPage::LanguageOptionPage() : AppletFrame(true, true) +{ + this->setTitle("Language_Option"_lang); + list = new brls::List(); + + English = new brls::ListItem("English"); + English->getClickEvent()->subscribe([&](brls::View* view){ + + nlohmann::json json_file; + json_file["language"]=(int)lang::Language::English; + std::ofstream o("/switch/AIO-switch-updater/config.ini"); + o<addStage( + new ConfirmPage(stagedFrame, "reset_machine"_lang,false) + ); + brls::Application::pushView(stagedFrame); + + }); + list->addView(English); + Chinese = new brls::ListItem("中文"); + Chinese->getClickEvent()->subscribe([&](brls::View* view){ + nlohmann::json json_file; + json_file["language"]=(int)lang::Language::Chinese; + std::ofstream o("/switch/AIO-switch-updater/config.ini"); + o<addStage( + new ConfirmPage(stagedFrame, "reset_machine"_lang,false) + ); + brls::Application::pushView(stagedFrame); + }); + list->addView(Chinese); + this->setContentView(list); +} \ No newline at end of file diff --git a/source/list_download_tab.cpp b/source/list_download_tab.cpp index 99a6a99..8daee31 100644 --- a/source/list_download_tab.cpp +++ b/source/list_download_tab.cpp @@ -1,55 +1,50 @@ #include "list_download_tab.hpp" - +#include "lang.hpp" +using namespace lang::literals; ListDownloadTab::ListDownloadTab(archiveType type) : brls::List() { std::tuple, std::vector> links; - std::string operation = "Getting "; - std::string firmwareText("\uE016 Firmware dumps from 'https://darthsternie.net/switch-firmwares/'. "\ - "Once downloaded, it will be extracted in '/firmware'. You can then install the update through Daybreak or ChoiDuJour.\n"\ - "\uE016 Current FW: " + std::string operation = "Getting"_lang; + std::string firmwareText("firmware_text"_lang ); std::string currentCheatsVer = - "\uE016 This will download a daily updated archive of cheat codes from 'gbatemp.net'. "\ - "Cheat codes for games you don't have installed won't be extracted to your SD card. "\ - "You can turn off cheat updated in 'Tools->Cheat menu'.\n"\ - "\uE016 Current cheats version: "; + "currentCeatsver"_lang; this->description = new brls::Label(brls::LabelStyle::DESCRIPTION, "", true); switch(type){ case sigpatches: links = fetchLinks(SIGPATCHES_URL); - operation += "sigpatches"; + operation += "operation_1"_lang; this->description->setText( - "\uE016 Sigpatches allow your Switch to install and run unofficial NSP file. " \ - "Make sure you pick the correct sigpatches for your setup (pure Atmosphere or Hekate+Atmosphere)." + "list_sigpatches"_lang ); break; case fw: links = fetchLinks(FIRMWARE_URL); - operation += "firmware"; + operation += "operation_2"_lang; SetSysFirmwareVersion ver; if (R_SUCCEEDED(setsysGetFirmwareVersion(&ver))) firmwareText += ver.display_version; - else firmwareText += "not found"; + else firmwareText += "list_not"_lang; this->description->setText(firmwareText); break; case app: - std::get<0>(links).push_back("Latest version"); + std::get<0>(links).push_back("list_latest"_lang); std::get<1>(links).push_back(APP_URL); - operation += "app"; + operation += "list_app"_lang; break; case cfw: links = fetchLinks(CFW_URL); - operation += "CFW"; + operation += "list_cfw"_lang; this->description->setText( - "\uE016 Main Switch CFWs. If you want to use Atmosphere with Hekate, download Atmosphere, then Hekate." + "list_main"_lang ); break; case cheats: std::string cheatsVer = fetchTitle(CHEATS_RELEASE_URL); if(cheatsVer != "-1"){ - std::get<0>(links).push_back("Latest (ver " + cheatsVer + ")"); + std::get<0>(links).push_back("list_latest_ver"_lang + cheatsVer + ")"); switch(getCFW()){ case sxos: std::get<1>(links).push_back(CHEATS_URL_TITLES); @@ -62,7 +57,7 @@ ListDownloadTab::ListDownloadTab(archiveType type) : break; } } - operation += "cheats"; + operation += "list_cheats"_lang; currentCheatsVer += readVersion(CHEATS_VERSION); this->description->setText(currentCheatsVer); break; @@ -76,7 +71,7 @@ ListDownloadTab::ListDownloadTab(archiveType type) : linkItems.reserve(nbLinks); for (int i = 0; i(links)[i]; - std::string text("Downloading:\n" + std::get<0>(links)[i] + "\n\nFrom:\n" + url); + std::string text("list_down"_lang + std::get<0>(links)[i] + "list_from"_lang + url); linkItems[i] = new brls::ListItem(std::get<0>(links)[i]); linkItems[i]->getClickEvent()->subscribe([&, text, url, type, operation](brls::View* view) { brls::StagedAppletFrame* stagedFrame = new brls::StagedAppletFrame(); @@ -85,13 +80,13 @@ ListDownloadTab::ListDownloadTab(archiveType type) : new ConfirmPage(stagedFrame, text) ); stagedFrame->addStage( - new WorkerPage(stagedFrame, "Downloading...", [url, type](){downloadArchive(url, type);}) + new WorkerPage(stagedFrame, "list_downing"_lang, [url, type](){downloadArchive(url, type);}) ); stagedFrame->addStage( - new WorkerPage(stagedFrame, "Extracting...", [type](){extractArchive(type);}) + new WorkerPage(stagedFrame, "list_extracting"_lang, [type](){extractArchive(type);}) ); stagedFrame->addStage( - new ConfirmPage(stagedFrame, "All done!", true) + new ConfirmPage(stagedFrame, "list_All"_lang, true) ); brls::Application::pushView(stagedFrame); }); @@ -101,8 +96,7 @@ ListDownloadTab::ListDownloadTab(archiveType type) : else{ notFound = new brls::Label( brls::LabelStyle::DESCRIPTION, - "Could not find a download link, make sure the Switch has access to the internet.\n"\ - "If this problem persists, please open an issue on Github.", + "list_could_done", true ); notFound->setHorizontalAlign(NVG_ALIGN_CENTER); diff --git a/source/main.cpp b/source/main.cpp index 5b75f44..f9ae83b 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -8,6 +8,7 @@ #include "main_frame.hpp" #include "constants.hpp" #include "utils.hpp" +#include "lang.hpp" #include #include @@ -15,7 +16,10 @@ #include #include #include +#include +#include +#include int main(int argc, char* argv[]) { // Init the app @@ -31,6 +35,10 @@ int main(int argc, char* argv[]) #endif // Initialize services with a PC shim + setsysInitialize(); + plInitialize(PlServiceType_User); + romfsInit(); + nsInitialize(); socketInitializeDefault(); nxlinkStdio(); @@ -41,6 +49,47 @@ int main(int argc, char* argv[]) brls::Logger::setLogLevel(brls::LogLevel::DEBUG); brls::Logger::debug("Start"); + + //start code by tiansongyu please clean the code if you want + //checkout the /switch/AIO-switch-updater/config.ini + //save the language mode + //or you can use this + /* + if (auto rc = lang::initialize_to_system_language(); R_FAILED(rc)) + brls::Logger::debug("Failed to init language: %#x, will fall back to key names\n", rc); + */ + //this code will automatically choose the machine language that the user use + //there are two ways to set the language . choose that you like way :D + const char* switch_dir= "/switch/"; + if(opendir(switch_dir)==NULL) + { + mkdir(switch_dir,0755); + } + const char* aio_config_file = "/switch/AIO-switch-updater/"; + if(opendir(aio_config_file)==NULL) + { + mkdir(aio_config_file,0755); + } + if(access("/switch/AIO-switch-updater/config.ini",F_OK) ==0) + { + std::ifstream i("/switch/AIO-switch-updater/config.ini"); + nlohmann::json lang_json; + i>>lang_json; + int tmp_number =lang_json["language"]; + lang::set_language((lang::Language)(tmp_number)); + } + else + { + //init system language automatically + if (auto rc = lang::initialize_to_system_language(); R_FAILED(rc)) + brls::Logger::debug("Failed to init language: %#x, will fall back to key names\n", rc); + int language_number = (int)lang::get_current_language(); + nlohmann::json json_file; + json_file["language"]=language_number; + std::ofstream o("/switch/AIO-switch-updater/config.ini"); + o<addTab("About", new AboutTab()); + setTitle(std::string(APP_TITLE) + "main_v"_lang + std::string(APP_VERSION)); + this->addTab("main_about"_lang, new AboutTab()); this->addSeparator(); - this->addTab("Update CFW", new ListDownloadTab(cfw)); - this->addTab("Update sigpatches", new ListDownloadTab(sigpatches)); - this->addTab("Download firmwares", new ListDownloadTab(fw)); - this->addTab("Download cheats", new ListDownloadTab(cheats)); + this->addTab("main_update_cfw"_lang, new ListDownloadTab(cfw)); + this->addTab("main_update_si"_lang, new ListDownloadTab(sigpatches)); + this->addTab("main_firmwares"_lang, new ListDownloadTab(fw)); + this->addTab("main_cheats"_lang, new ListDownloadTab(cheats)); this->addSeparator(); - - this->addTab("Tools", new ToolsTab(tag)); + this->addTab("main_tools"_lang, new ToolsTab(tag)); } diff --git a/source/payload_page.cpp b/source/payload_page.cpp index 2415b32..2d01fa3 100644 --- a/source/payload_page.cpp +++ b/source/payload_page.cpp @@ -1,13 +1,14 @@ #include "payload_page.hpp" - +#include "lang.hpp" +using namespace lang::literals; PayloadPage::PayloadPage() : AppletFrame(true, true) { CFW cfw = getCFW(); - this->setTitle("Reboot menu"); + this->setTitle("payload_reboot"_lang); list = new brls::List(); label = new brls::Label( brls::LabelStyle::DESCRIPTION, - "Select a payload to reboot to.", + "payload_select"_lang, true ); list->addView(label); @@ -22,13 +23,13 @@ PayloadPage::PayloadPage() : AppletFrame(true, true) brls::Application::popView(); }); if(cfw == ams){ - items[i]->registerAction("Set as reboot_payload.bin", brls::Key::X, [this, payload] { + items[i]->registerAction("payload_set"_lang, brls::Key::X, [this, payload] { if(R_SUCCEEDED(CopyFile(payload.c_str(), REBOOT_PAYLOAD_PATH))){ - brls::Dialog* dialog = new brls::Dialog("Successfully copied '" + payload + "' to '" + std::string(REBOOT_PAYLOAD_PATH) + "'."); + brls::Dialog* dialog = new brls::Dialog("payload_success"_lang + payload + "payload_to"_lang + std::string(REBOOT_PAYLOAD_PATH) + "'."); brls::GenericEvent::Callback callback = [dialog](brls::View* view) { dialog->close(); }; - dialog->addButton("Ok", callback); + dialog->addButton("payload_ok"_lang, callback); dialog->setCancelable(true); dialog->open(); } @@ -40,14 +41,14 @@ PayloadPage::PayloadPage() : AppletFrame(true, true) } list->addView(new brls::ListItemGroupSpacing(true)); - shutDown = new brls::ListItem("Shut Down"); + shutDown = new brls::ListItem("payload_shut"_lang); shutDown->getClickEvent()->subscribe([](brls::View* view) { shut_down(false); brls::Application::popView(); }); list->addView(shutDown); - reboot = new brls::ListItem("Reboot"); + reboot = new brls::ListItem("payload_reboot_2"_lang); reboot->getClickEvent()->subscribe([](brls::View* view) { shut_down(true); brls::Application::popView(); diff --git a/source/tools_tab.cpp b/source/tools_tab.cpp index b6260e0..ad62bad 100644 --- a/source/tools_tab.cpp +++ b/source/tools_tab.cpp @@ -1,57 +1,64 @@ #include "tools_tab.hpp" - +#include "lang.hpp" +using namespace lang::literals; ToolsTab::ToolsTab(std::string tag) : brls::List() { - cheats = new brls::ListItem("Cheats menu"); + cheats = new brls::ListItem("tool_cheats"_lang); cheats->getClickEvent()->subscribe([&](brls::View* view){ brls::Application::pushView(new CheatsPage()); }); this->addView(cheats); - JCcolor = new brls::ListItem("Change the Joy-Cons color"); + JCcolor = new brls::ListItem("tool_change"_lang); JCcolor->getClickEvent()->subscribe([&](brls::View* view){ brls::Application::pushView(new JCPage()); }); this->addView(JCcolor); - downloadPayload = new brls::ListItem("Dowload payloads to " + std::string(BOOTLOADER_PL_PATH)); + downloadPayload = new brls::ListItem("tool_download"_lang + std::string(BOOTLOADER_PL_PATH)); downloadPayload->getClickEvent()->subscribe([&](brls::View* view){ brls::Application::pushView(new DownloadPayloadPage()); }); this->addView(downloadPayload); - rebootPayload = new brls::ListItem("Inject payload"); + rebootPayload = new brls::ListItem("tool_inject"_lang); rebootPayload->getClickEvent()->subscribe([&](brls::View* view){ brls::Application::pushView(new PayloadPage()); }); this->addView(rebootPayload); if(!tag.empty() && tag != APP_VERSION){ - updateApp = new brls::ListItem("Update the app (v" + tag +")"); - std::string text("Downloading:\nAIO-switch-updater\n\nFrom:\n" + std::string(APP_URL)); + updateApp = new brls::ListItem("tool_update"_lang + tag +")"); + std::string text("tool_DownLoad"_lang + std::string(APP_URL)); updateApp->getClickEvent()->subscribe([&, text](brls::View* view) { brls::StagedAppletFrame* stagedFrame = new brls::StagedAppletFrame(); - stagedFrame->setTitle("Updating app"); + stagedFrame->setTitle("tool_updating"_lang); stagedFrame->addStage( new ConfirmPage(stagedFrame, text) ); stagedFrame->addStage( - new WorkerPage(stagedFrame, "Downloading...", [](){downloadArchive(APP_URL, app);}) + new WorkerPage(stagedFrame, "tool_downloading"_lang, [](){downloadArchive(APP_URL, app);}) ); stagedFrame->addStage( - new WorkerPage(stagedFrame, "Extracting....", [](){extractArchive(app);}) + new WorkerPage(stagedFrame, "tool_extracting"_lang, [](){extractArchive(app);}) ); stagedFrame->addStage( - new ConfirmPage(stagedFrame, "All done!", true) + new ConfirmPage(stagedFrame, "tool_all_done"_lang, true) ); brls::Application::pushView(stagedFrame); }); this->addView(updateApp); } - changelog = new brls::ListItem("Changelog"); + changelog = new brls::ListItem("tool_changelog"_lang); changelog->getClickEvent()->subscribe([&](brls::View* view){ brls::Application::pushView(new ChangelogPage()); }); this->addView(changelog); + + language = new brls::ListItem("Language_Option"_lang); + language->getClickEvent()->subscribe([&](brls::View* view){ + brls::Application::pushView(new LanguageOptionPage()); + }); + this->addView(language); } \ No newline at end of file diff --git a/source/utils.cpp b/source/utils.cpp index 2a685b9..9d50a72 100644 --- a/source/utils.cpp +++ b/source/utils.cpp @@ -1,5 +1,6 @@ #include "utils.hpp" - +#include "lang.hpp" +using namespace lang::literals; bool isServiceRunning(const char *serviceName) { Handle handle; SmServiceName service_name = smEncodeName(serviceName); @@ -68,8 +69,7 @@ void downloadArchive(std::string url, archiveType type){ downloadFile(url.c_str(), FIRMWARE_FILENAME, OFF); } else{ - showDialogBox("Because of the size of the FW archive, downloading firmwares in Applet Mode is not supported. "\ - "Please launch the app with full RAM access.", "Ok"); + showDialogBox("utils_because"_lang, "utils_ok"_lang); brls::Application::pushView(new MainFrame()); } break; @@ -134,7 +134,7 @@ void extractArchive(archiveType type){ std::string backup(HEKATE_IPL_PATH); backup += ".old"; if(std::filesystem::exists(HEKATE_IPL_PATH)){ - overwriteInis = showDialogBox("Do you want to overwrite existing " + std::string(HEKATE_IPL_PATH) +"?", "No", "Yes"); + overwriteInis = showDialogBox("utils_do"_lang + std::string(HEKATE_IPL_PATH) +"?", "utils_no"_lang, "utils_yes"_lang); if(overwriteInis == 0){ extract(SIGPATCHES_FILENAME, ROOT_PATH, HEKATE_IPL_PATH); } @@ -147,8 +147,7 @@ void extractArchive(archiveType type){ } } else{ - showDialogBox("The downloaded file is not a sigpatches archive. This is most likely due to a broken link. If the problem persists after more than 3 hours, "\ - "please open an issue on Github.", "Ok"); + showDialogBox("utils_the"_lang, "utils_ok"_lang); brls::Application::pushView(new MainFrame()); } break; @@ -159,8 +158,7 @@ void extractArchive(archiveType type){ break; case fw: if(std::filesystem::file_size(FIRMWARE_FILENAME) < 200000){ - showDialogBox("The downloaded file is not a firmware archive. This is most likely due to a broken link. If the problem persists after more than 3 hours, "\ - "please open an issue on Github.", "Ok"); + showDialogBox("utils_the_downloaded"_lang, "utils_ok"_lang); brls::Application::pushView(new MainFrame()); } else{ @@ -174,12 +172,11 @@ void extractArchive(archiveType type){ break; case cfw: if(isArchive(CFW_FILENAME)){ - overwriteInis = showDialogBox("Do you want to overwrite existing .ini config files?", "No", "Yes"); + overwriteInis = showDialogBox("ultils_overwrite"_lang, "utils_no"_lang, "utils_yes"_lang); extract(CFW_FILENAME, ROOT_PATH, overwriteInis); } else{ - showDialogBox("The downloaded file is not a CFW archive. This is most likely due to a broken link. If the problem persists after more than 3 hours, "\ - "please open an issue on Github.", "Ok"); + showDialogBox("ultis_file"_lang, "utils_ok"_lang); brls::Application::pushView(new MainFrame()); } break;