Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New i18n #503

Merged
merged 10 commits into from
Jul 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion LiteLoader/Header/GlobalServiceAPI.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#pragma once
#include "Global.h"

//Types
namespace RakNet {
Expand Down
243 changes: 161 additions & 82 deletions LiteLoader/Header/I18nAPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@
//
// [Usage - Translation]
//
// Translation::load("plugins/xxx/lang/zh_CN.json");
// Translation::load("plugins/xxx/lang.json");
// ...
// tr("There are {0} days before {1} to come back", 3, "alex"); // return translated string [std::string]
// trc("There are {0} days before {1} to come back", 3, "alex"); // return translated string [const char*]
//
// ** In Translation File: plugins/xxx/lang/zh_CN.json
// ** In Translation File: plugins/xxx/lang.json
// {
// "There are {0} days before {1} to come back": "在{1}回来前还剩{0}天",
// "...": "...",
// "...": "..."
// "zh_CN": {
// "There are {0} days before {1} to come back": "在{1}回来前还剩{0}天",
// "...": "...",
// "...": "..."
// }
// }
//
//
Expand All @@ -33,7 +35,6 @@

#include "Global.h"
#include "LLAPI.h"
#include "LoggerAPI.h"
#include "Utils/FileHelper.h"
#include "Utils/PluginOwnData.h"
#include "third-party/Nlohmann/json.hpp"
Expand All @@ -44,7 +45,7 @@

/**
* @brief I18N API class.
*
*
*/
class I18N {

Expand All @@ -54,20 +55,19 @@ class I18N {
private:
HMODULE curModule;
std::string filePath;
std::string defaultLangCode = "en_US";
LangData langData;
LangData defaultLangData;

void load(const std::string& fileName);
void save();

public:

static const constexpr char* POD_KEY = "_ll_plugin_i18n"; ///< PluginOwnData key
std::string defaultLangCode = "en_US";

/**
* @brief Construct a I18N object.
*
*
* @param filePath The path to the i18n file(json)
* @param defaultLangCode The default language code(if no lang code is specified, it will use this)
* @param defaultLangData The default translation data
Expand All @@ -79,24 +79,30 @@ class I18N {
: filePath(filePath)
, defaultLangCode(defaultLangCode)
, defaultLangData(defaultLangData) {
if (hModule)
if (hModule)
curModule = hModule;
else
curModule = GetCurrentModule();
load(filePath);
PluginOwnData::setImpl<I18N>(curModule, POD_KEY, *this);
}
/// Copy constructor
I18N(const I18N& other) {
*this = other;
}

/**
* @brief Get the translation of the specified key.
*
* @param key The language key
* @param langCode The language code like en_US,zh_CN("" => this->defaultLangCode)
* @return The translation
*
* @param key The language key
* @param langCode The language code like en_US,zh_CN("" => this->defaultLangCode)
* @return std::string The translation
* @see I18N::defaultLangCode
*/
LIAPI std::string get(const std::string& key, const std::string& langCode = "");

static const constexpr char* POD_KEY = "ll_plugin_i18n"; ///< PluginOwnData key

};

#ifdef UNICODE
Expand All @@ -109,61 +115,93 @@ class I18N {

namespace Translation {

template <bool B, class T = void> using enable_if_t = typename std::enable_if<B, T>::type;
template <bool B, class T = void>
using enable_if_t = typename std::enable_if<B, T>::type;

///////////////// tr Impl /////////////////
template <typename S, typename... Args, Translation::enable_if_t<(fmt::v8::detail::is_string<S>::value), int> = 0>
inline std::string trImpl(HMODULE hPlugin, const S& formatStr, const Args&... args) {
auto& i18n = PluginOwnData::getImpl<I18N>(hPlugin, I18N::POD_KEY);

std::string realFormatStr = i18n.get(formatStr);
// realFormatStr = FixCurlyBracket(realFormatStr);
if constexpr (0 == sizeof...(args)) {
// Avoid fmt if only one argument
return realFormatStr;
} else {
return fmt::format(realFormatStr, args...);
///////////////// tr Impl /////////////////
template <typename S, typename... Args, Translation::enable_if_t<(fmt::v8::detail::is_string<S>::value), int> = 0>
inline std::string trImpl(HMODULE hPlugin, const S& formatStr, const Args&... args) {
std::string realFormatStr = formatStr;
if (PluginOwnData::hasImpl(hPlugin, I18N::POD_KEY)) {
auto& i18n = PluginOwnData::getImpl<I18N>(hPlugin, I18N::POD_KEY);
realFormatStr = i18n.get(formatStr);
}
// realFormatStr = FixCurlyBracket(realFormatStr);
if constexpr (0 == sizeof...(args)) {
// Avoid fmt if only one argument
return realFormatStr;
} else {
return fmt::format(realFormatStr, args...);
}
}
template <typename S, typename... Args, Translation::enable_if_t<(fmt::v8::detail::is_string<S>::value), int> = 0>
inline std::string trlImpl(HMODULE hPlugin, const std::string& langCode, const S& formatStr, const Args&... args) {
std::string realFormatStr = formatStr;
if (PluginOwnData::hasImpl(hPlugin, I18N::POD_KEY)) {
auto& i18n = PluginOwnData::getImpl<I18N>(hPlugin, I18N::POD_KEY);
realFormatStr = i18n.get(formatStr, langCode);
}
// realFormatStr = FixCurlyBracket(realFormatStr);
if constexpr (0 == sizeof...(args)) {
// Avoid fmt if only one argument
return realFormatStr;
} else {
return fmt::format(realFormatStr, args...);
}
}
}

///////////////// trc Impl /////////////////
template <typename S, typename... Args, Translation::enable_if_t<(fmt::v8::detail::is_string<S>::value), int> = 0>
[[deprecated("Please use trImpl(...).c_str()")]]
inline const char* trcImpl(HMODULE hPlugin, const S& formatStr, const Args&... args) {
std::string res = trImpl(hPlugin, formatStr, args...);
std::string name = std::string(I18N::POD_KEY) + "_translation_" + fmt::v8::detail::to_string_view<S>(formatStr).data();
auto& str = PluginOwnData::setImpl<std::string>(hPlugin, name, res);
return str.c_str();
}
///////////////// trc Impl /////////////////
template <typename S, typename... Args, Translation::enable_if_t<(fmt::v8::detail::is_string<S>::value), int> = 0>
[[deprecated("Use trImpl(...).c_str() instead")]] inline const char* trcImpl(HMODULE hPlugin, const S& formatStr,
const Args&... args) {
std::string res = trImpl(hPlugin, formatStr, args...);
std::string name =
std::string(I18N::POD_KEY) + "_translation_" + fmt::v8::detail::to_string_view<S>(formatStr).data();
auto& str = PluginOwnData::setImpl<std::string>(hPlugin, name, res);
return str.c_str();
}

[[deprecated]]
LIAPI bool loadImpl(HMODULE hPlugin, const std::string& filePath); // For compatibility
LIAPI bool loadImpl(HMODULE hPlugin, const std::string& filePath, const std::string& defaultLangCode,
const I18N::LangData& defaultLangData);
[[deprecated]] LIAPI bool loadImpl(HMODULE hPlugin, const std::string& filePath); // For compatibility
LIAPI I18N* loadImpl(HMODULE hPlugin, const std::string& filePath, const std::string& defaultLangCode,
const I18N::LangData& defaultLangData);

/**
* @brief Load i18n from a file.
*
* @param filePath The path to the i18n file(json)
* @param defaultLangCode The default language code(if no lang code is specified, it will use this)
* @param defaultLangData The default translation data
* @return True if load successfully, false otherwise.
*/
inline bool load(const std::string& filePath, const std::string& defaultLangCode = "en_US",
const I18N::LangData& defaultLangData = {}) {
return loadImpl(GetCurrentModule(), filePath, defaultLangCode, defaultLangData);
}
/**
* @brief Load i18n from a file.
*
* @param filePath The path to the i18n file(json)
* @param defaultLangCode The default language code(if no lang code is specified, it will use this)
* @param defaultLangData The default translation data
* @return I18N* The pointer to the I18N object in PluginOwnData, null if failed
*/
inline I18N* load(const std::string& filePath, const std::string& defaultLangCode = "en_US",
const I18N::LangData& defaultLangData = {}) {
return loadImpl(GetCurrentModule(), filePath, defaultLangCode, defaultLangData);
}

/**
* @brief Get the I18N object of a certain plugin.
*
* @param hPlugin The plugin handle(nullptr -> GetCurrentModule())
* @return std::optional<I18N> The I18N object
*/
inline std::optional<I18N> getI18N(HMODULE hPlugin = nullptr) {
auto handle = (hPlugin == nullptr ? GetCurrentModule() : hPlugin);
if (handle && PluginOwnData::hasImpl(handle, I18N::POD_KEY)) {
return PluginOwnData::getImpl<I18N>(handle, I18N::POD_KEY);
}
return std::optional<I18N>();
}

}; // namespace Translation

/**
* @brief Translate a str.
*
* @tparam S The string type
* @tparam Args ...
* @param formatStr The str to translate and format
* @param args The format arguments
* @return The translated str
*
* @tparam S The string type
* @tparam Args ...
* @param formatStr The str to translate and format
* @param args The format arguments
* @return std::string The translated str
* @see fmt::format
* @see https://fmt.dev/latest/index.html
* @par Example
Expand All @@ -179,10 +217,10 @@ inline std::string tr(const S& formatStr, const Args&... args) {
/**
* @brief Translate a str.
*
* @tparam Args ...
* @param formatStr The str to translate and format
* @param args The format arguments
* @return The translated str
* @tparam Args ...
* @param formatStr The str to translate and format
* @param args The format arguments
* @return std::string The translated str
* @see fmt::format
* @see https://fmt.dev/latest/index.html
* @par Example
Expand All @@ -198,11 +236,11 @@ inline std::string tr(const char* formatStr, const Args&... args) {
/**
* @brief Translate a str(c-style str).
*
* @tparam S The string type
* @tparam Args ...
* @param formatStr The str to translate and format
* @param args The format arguments
* @return The translated str(c-style str)
* @tparam S The string type
* @tparam Args ...
* @param formatStr The str to translate and format
* @param args The format arguments
* @return const char* The translated str(c-style str)
* @see fmt::format
* @see https://fmt.dev/latest/index.html
* @par Example
Expand All @@ -218,10 +256,10 @@ inline const char* trc(const S& formatStr, const Args&... args) {
/**
* @brief Translate a str(c-style str).
*
* @tparam Args ...
* @param formatStr The str to translate and format
* @param args The format arguments
* @return The translated str(c-style str)
* @tparam Args ...
* @param formatStr The str to translate and format
* @param args The format arguments
* @return const char* The translated str(c-style str)
* @see fmt::format
* @see https://fmt.dev/latest/index.html
* @par Example
Expand All @@ -234,15 +272,56 @@ inline const char* trc(const char* formatStr, const Args&... args) {
return trc(std::string(formatStr), args...);
}

/**
* @brief Translate a str to the specified language.
*
* @tparam S The string type
* @tparam Args ...
* @param langCode The language code like en_US
* @param formatStr The str to translate and format
* @param args The format arguments
* @return std::string The translated str
* @see fmt::format
* @see https://fmt.dev/latest/index.html
* @par Example
* @code
* trl("zh_CN", "There are {0} days before {1} to come back", 3, "alex");
* @endcode
*/
template <typename S, typename... Args>
inline std::string trl(const std::string& langCode, const S& formatStr, const Args&... args) {
return Translation::trlImpl(GetCurrentModule(), langCode, formatStr, args...);
}

/**
* @brief Translate a str to the specified language.
*
* @tparam Args ...
* @param langCode The language code like en_US
* @param formatStr The str to translate and format(c-style)
* @param args The format arguments
* @return std::string The translated str
* @see fmt::format
* @see https://fmt.dev/latest/index.html
* @par Example
* @code
* trl("zh_CN", "There are {0} days before {1} to come back", 3, "alex");
* @endcode
*/
template <typename... Args>
inline std::string trl(const std::string& langCode, const char* formatStr, const Args&... args) {
return trl(langCode, std::string(formatStr), args...);
}

// For text encoding
namespace TextEncoding {
LIAPI Encoding getLocalEncoding();
LIAPI Encoding detectEncoding(const std::string& text, bool* isReliable = nullptr);
LIAPI Encoding getLocalEncoding();
LIAPI Encoding detectEncoding(const std::string& text, bool* isReliable = nullptr);

LIAPI std::string fromUnicode(const std::wstring& text, Encoding to = Encoding::UTF8);
LIAPI std::wstring toUnicode(const std::string& text, Encoding from = Encoding::UTF8);
LIAPI std::string toUTF8(const std::string& text);
LIAPI std::string toUTF8(const std::string& text, Encoding from);
LIAPI std::string fromUnicode(const std::wstring& text, Encoding to = Encoding::UTF8);
LIAPI std::wstring toUnicode(const std::string& text, Encoding from = Encoding::UTF8);
LIAPI std::string toUTF8(const std::string& text);
LIAPI std::string toUTF8(const std::string& text, Encoding from);

LIAPI std::string convert(const std::string& text, Encoding from, Encoding to);
} // namespace TextEncoding
LIAPI std::string convert(const std::string& text, Encoding from, Encoding to);
} // namespace TextEncoding
9 changes: 9 additions & 0 deletions LiteLoader/Header/LoggerAPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include "Utils/FileHelper.h"
#include "Utils/PluginOwnData.h"
#include "Utils/StringHelper.h"
#include "I18nAPI.h"
#include <string>
#include <sstream>
#include <iostream>
Expand Down Expand Up @@ -119,6 +120,10 @@ class Logger {
template <typename S, typename... Args, enable_if_type<(fmt::v8::detail::is_string<S>::value), int> = 0>
void operator()(const S& formatStr, const Args&... args)
{
if (PluginOwnData::has(I18N::POD_KEY)) {
*this << tr(formatStr, args...) << endl;
return;
}
if constexpr (0 == sizeof...(args))
{
// Avoid fmt if only one argument
Expand All @@ -134,6 +139,10 @@ class Logger {
template <typename... Args>
void operator()(const char* formatStr, const Args&... args)
{
if (PluginOwnData::has(I18N::POD_KEY)) {
*this << tr(formatStr, args...) << endl;
return;
}
if constexpr (0 == sizeof...(args))
{
// Avoid fmt if only one argument
Expand Down
Loading