LiteLoaderBDS-1.16.40/ScriptEngine/API/InternationalAPI.cpp
2022-09-21 19:47:03 +08:00

235 lines
7.8 KiB
C++

#define NEW_DEFINES
#include "InternationalAPI.h"
#include <I18nAPI.h>
#include <third-party/FMT/args.h>
void FormatHelper(std::vector<Local<Value>>& args, const std::vector<char*>& names, bool enableObject,
fmt::dynamic_format_arg_store<fmt::format_context>& s,
std::vector<char*>& delList) {
for (auto i = 0; i < args.size(); ++i) {
auto& arg = args[i];
char* name = nullptr;
if (i < names.size()) {
name = names[i];
}
switch (arg.getKind()) {
case ValueKind::kBoolean: {
if (!name) {
s.push_back(arg.asBoolean().value());
} else {
s.push_back(fmt::arg(name, arg.asBoolean().value()));
}
break;
}
case ValueKind::kNumber: {
if (CheckIsFloat(arg)) {
if (!name) {
s.push_back(arg.asNumber().toDouble());
} else {
s.push_back(fmt::arg(name, arg.asNumber().toDouble()));
}
} else {
if (!name) {
s.push_back(arg.asNumber().toInt64());
} else {
s.push_back(fmt::arg(name, arg.asNumber().toInt64()));
}
}
break;
}
case ValueKind::kString: {
if (!name) {
s.push_back(arg.toStr());
} else {
s.push_back(fmt::arg(name, arg.toStr()));
}
break;
}
case ValueKind::kArray: {
if (enableObject) {
auto arr = arg.asArray();
std::vector<Local<Value>> vals;
for (auto j = 0ULL; j < arr.size(); ++j) {
vals.push_back(arr.get(j));
}
FormatHelper(vals, {}, false, s, delList);
break;
}
// Follow
}
case ValueKind::kObject: {
if (enableObject) {
auto obj = arg.asObject();
auto keys = obj.getKeys();
std::vector<Local<Value>> vals;
std::vector<char*> nextNames;
for (auto j = 0ULL; j < keys.size(); ++j) {
auto key = keys[j].toString();
#if __cplusplus <= 201703L
char* cName = new char[key.size() + 1];
std::memset(cName, 0, key.size() + 1);
#else
char* cName = new char[key.size() + 1](0);
#endif
std::copy(key.begin(), key.end(), cName);
delList.push_back(cName);
nextNames.push_back(cName);
vals.push_back(obj.get(keys[j]));
}
FormatHelper(vals, nextNames, false, s, delList);
break;
}
// Follow
}
default: {
auto str = ValueToString(arg);
if (!name) {
s.push_back(str);
} else {
s.push_back(fmt::arg(name, str));
}
break;
}
}
}
}
Local<Value> TrFormat(const Arguments& args, size_t offset, std::string key,
const std::string& localeName = "") {
try {
size_t argsLength = args.size() - offset;
auto i18n = ENGINE_OWN_DATA()->i18n;
if (i18n) {
key = i18n->get(key, localeName);
}
// realFormatStr = FixCurlyBracket(realFormatStr);
if (0ULL == argsLength) {
// Avoid fmt if only one argument
return String::newString(key);
} else {
fmt::dynamic_format_arg_store<fmt::format_context> s;
std::vector<char*> delList;
std::vector<Local<Value>> vals;
for (auto i = offset; i < args.size(); ++i) {
vals.push_back(args[i]);
}
FormatHelper(vals, {}, true, s, delList);
auto result = String::newString(fmt::vformat(key, s));
for (auto& ptr : delList) {
delete[] ptr;
}
return result;
}
} catch (const fmt::format_error&) {
return String::newString(key);
}
}
ClassDefine<void> I18nClassBuilder =
defineClass<void>("i18n")
.function("tr", &I18nClass::tr)
.function("trl", &I18nClass::trl)
.function("get", &I18nClass::get)
.function("load", &I18nClass::load)
.build();
Local<Value> I18nClass::tr(const Arguments& args) {
CHECK_ARGS_LEAST_COUNT(1); // At least 1
CHECK_ARG_TYPE(0, kString);
try {
return TrFormat(args, 1ULL, args[0].toStr());
}
CATCH_AND_THROW;
}
Local<Value> I18nClass::trl(const Arguments& args) {
CHECK_ARGS_LEAST_COUNT(2);
CHECK_ARG_TYPE(0, kString);
CHECK_ARG_TYPE(1, kString);
try {
return TrFormat(args, 2ULL, args[1].toStr(), args[0].toStr());
}
CATCH_AND_THROW;
}
Local<Value> I18nClass::get(const Arguments& args) {
CHECK_ARGS_LEAST_COUNT(1);
CHECK_ARG_TYPE(0, kString);
if (args.size() == 2) {
CHECK_ARG_TYPE(1, kString);
} else {
CHECK_ARGS_COUNT(1);
}
try {
auto key = args[0].toStr();
std::string localeName{};
if (args.size() == 2) {
localeName = args[1].toStr();
}
auto i18n = ENGINE_OWN_DATA()->i18n;
if (!i18n) {
throw Exception("I18n data has not been loaded yet!");
}
return String::newString(i18n->get(key, localeName));
}
CATCH_AND_THROW;
}
Local<Value> I18nClass::load(const Arguments& args) {
CHECK_ARGS_LEAST_COUNT(1);
CHECK_ARG_TYPE(0, kString);
if (args.size() > 1) {
CHECK_ARG_TYPE(1, kString);
}
if (args.size() > 2) {
CHECK_ARG_TYPE(2, kObject);
}
try {
auto path = args[0].toStr();
I18nBase::LangData defaultLangData;
std::string defaultLocaleName;
if (args.size() > 1) {
defaultLocaleName = args[1].toStr();
}
if (args.size() > 2) {
auto rawLangData = args[2].asObject();
auto rawLangDataKeys = rawLangData.getKeys();
for (auto i = 0ULL; i < rawLangDataKeys.size(); ++i) {
auto localeName = rawLangDataKeys[i].toString();
auto val = rawLangData.get(rawLangDataKeys[i]);
if (val.getKind() != ValueKind::kObject) {
throw Exception("Value in LangData must be object");
}
auto obj = val.asObject();
I18nBase::SubLangData data;
auto objKeys = obj.getKeys();
for (auto j = 0ULL; j < objKeys.size(); ++j) {
auto str = obj.get(objKeys[j]);
if (str.getKind() != ValueKind::kString) {
throw Exception("Value in SubLangData must be string");
}
data.emplace(objKeys[j].toString(), str.toStr());
}
defaultLangData.emplace(localeName, data);
}
}
I18nBase* res = nullptr;
if (EndsWith(path, "/") || EndsWith(path, "\\") || std::filesystem::is_directory(path)) { // Directory
res = new MultiFileI18N(path, defaultLocaleName, defaultLangData);
} else {
res = new SingleFileI18N(path, defaultLocaleName, defaultLangData);
}
ENGINE_OWN_DATA()->i18n = res;
if (res) {
return Boolean::newBoolean(true);
}
return Boolean::newBoolean(false);
}
CATCH_AND_THROW;
}