the issue isn't with the pr itself, however it did expose the lack of caching with the translation strings, it will continue to search for strings, even if we already found them. to solve this, i used a map to cache said strings
148 lines
4.3 KiB
C++
148 lines
4.3 KiB
C++
#include "i18n.hpp"
|
|
#include "fs.hpp"
|
|
#include "log.hpp"
|
|
#include <yyjson.h>
|
|
#include <vector>
|
|
#include <unordered_map>
|
|
|
|
namespace sphaira::i18n {
|
|
namespace {
|
|
|
|
std::vector<u8> g_i18n_data;
|
|
yyjson_doc* json;
|
|
yyjson_val* root;
|
|
std::unordered_map<std::string, std::string> g_tr_cache;
|
|
|
|
std::string get_internal(const char* str, size_t len) {
|
|
const std::string kkey = {str, len};
|
|
|
|
if (auto it = g_tr_cache.find(kkey); it != g_tr_cache.end()) {
|
|
return it->second;
|
|
}
|
|
|
|
// add default entry
|
|
g_tr_cache.emplace(kkey, kkey);
|
|
|
|
if (!json || !root) {
|
|
log_write("no json or root\n");
|
|
return kkey;
|
|
}
|
|
|
|
auto key = yyjson_obj_getn(root, str, len);
|
|
if (!key) {
|
|
log_write("\tfailed to find key: [%s]\n", kkey.c_str());
|
|
return kkey;
|
|
}
|
|
|
|
auto val = yyjson_get_str(key);
|
|
auto val_len = yyjson_get_len(key);
|
|
if (!val || !val_len) {
|
|
log_write("\tfailed to get value: [%s]\n", kkey.c_str());
|
|
return kkey;
|
|
}
|
|
|
|
// update entry in cache
|
|
const std::string ret = {val, val_len};
|
|
g_tr_cache.insert_or_assign(kkey, ret);
|
|
return ret;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
bool init(long index) {
|
|
g_tr_cache.clear();
|
|
R_TRY_RESULT(romfsInit(), false);
|
|
ON_SCOPE_EXIT( romfsExit() );
|
|
|
|
u64 languageCode;
|
|
SetLanguage setLanguage = SetLanguage_ENGB;
|
|
std::string lang_name = "en";
|
|
|
|
switch (index) {
|
|
case 0: // auto
|
|
if (R_SUCCEEDED(setGetSystemLanguage(&languageCode))) {
|
|
setMakeLanguage(languageCode, &setLanguage);
|
|
}
|
|
break;
|
|
|
|
case 1: setLanguage = SetLanguage_ENGB; break; // "English"
|
|
case 2: setLanguage = SetLanguage_JA; break; // "Japanese"
|
|
case 3: setLanguage = SetLanguage_FR; break; // "French"
|
|
case 4: setLanguage = SetLanguage_DE; break; // "German"
|
|
case 5: setLanguage = SetLanguage_IT; break; // "Italian"
|
|
case 6: setLanguage = SetLanguage_ES; break; // "Spanish"
|
|
case 7: setLanguage = SetLanguage_ZHCN; break; // "Chinese"
|
|
case 8: setLanguage = SetLanguage_KO; break; // "Korean"
|
|
case 9: setLanguage = SetLanguage_NL; break; // "Dutch"
|
|
case 10: setLanguage = SetLanguage_PT; break; // "Portuguese"
|
|
case 11: setLanguage = SetLanguage_RU; break; // "Russian"
|
|
case 12: lang_name = "se"; break; // "Swedish"
|
|
}
|
|
|
|
switch (setLanguage) {
|
|
case SetLanguage_JA: lang_name = "ja"; break;
|
|
case SetLanguage_FR: lang_name = "fr"; break;
|
|
case SetLanguage_DE: lang_name = "de"; break;
|
|
case SetLanguage_IT: lang_name = "it"; break;
|
|
case SetLanguage_ES: lang_name = "es"; break;
|
|
case SetLanguage_ZHCN: lang_name = "zh"; break;
|
|
case SetLanguage_KO: lang_name = "ko"; break;
|
|
case SetLanguage_NL: lang_name = "nl"; break;
|
|
case SetLanguage_PT: lang_name = "pt"; break;
|
|
case SetLanguage_RU: lang_name = "ru"; break;
|
|
case SetLanguage_ZHTW: lang_name = "zh"; break;
|
|
}
|
|
|
|
const fs::FsPath sdmc_path = "/config/sphaira/i18n/" + lang_name + ".json";
|
|
const fs::FsPath romfs_path = "romfs:/i18n/" + lang_name + ".json";
|
|
fs::FsPath path = sdmc_path;
|
|
|
|
// try and load override translation first
|
|
Result rc = fs::FsNativeSd().read_entire_file(path, g_i18n_data);
|
|
if (R_FAILED(rc)) {
|
|
path = romfs_path;
|
|
rc = fs::FsStdio().read_entire_file(path, g_i18n_data);
|
|
}
|
|
|
|
if (R_SUCCEEDED(rc)) {
|
|
json = yyjson_read((const char*)g_i18n_data.data(), g_i18n_data.size(), YYJSON_READ_ALLOW_TRAILING_COMMAS|YYJSON_READ_ALLOW_COMMENTS|YYJSON_READ_ALLOW_INVALID_UNICODE);
|
|
if (json) {
|
|
root = yyjson_doc_get_root(json);
|
|
if (root) {
|
|
log_write("opened json: %s\n", path.s);
|
|
return true;
|
|
} else {
|
|
log_write("failed to find root\n");
|
|
}
|
|
} else {
|
|
log_write("failed open json\n");
|
|
}
|
|
} else {
|
|
log_write("failed to read file\n");
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void exit() {
|
|
if (json) {
|
|
yyjson_doc_free(json);
|
|
json = nullptr;
|
|
}
|
|
g_i18n_data.clear();
|
|
}
|
|
|
|
std::string get(const char* str) {
|
|
return get_internal(str, std::strlen(str));
|
|
}
|
|
|
|
} // namespace sphaira::i18n
|
|
|
|
namespace literals {
|
|
|
|
std::string operator"" _i18n(const char* str, size_t len) {
|
|
return sphaira::i18n::get_internal(str, len);
|
|
}
|
|
|
|
} // namespace literals
|