diff --git a/hikyuu_cpp/hikyuu_server/common/mo.cpp b/hikyuu_cpp/hikyuu_server/common/mo.cpp index e447f1686..d344c470e 100644 --- a/hikyuu_cpp/hikyuu_server/common/mo.cpp +++ b/hikyuu_cpp/hikyuu_server/common/mo.cpp @@ -1,7 +1,7 @@ /* * Copyright(C) 2021 hikyuu.org * - * Create on: 2021-05-01 + * Create on: 2021-05-02 * Author: fasiondog */ @@ -9,10 +9,11 @@ namespace hku { -moFileLib::moFileReader g_moFR; +std::unordered_map MOHelper::ms_dict; -void mo_init(const char *filename) { - g_moFR.ReadFile(filename); +void MOHelper::init() { + ms_dict["zh_cn"] = moFileLib::moFileReader(); + ms_dict["zh_cn"].ReadFile("i8n/zh_CN.mo"); } } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu_server/common/mo.h b/hikyuu_cpp/hikyuu_server/common/mo.h index fce7b709c..e267c0af6 100644 --- a/hikyuu_cpp/hikyuu_server/common/mo.h +++ b/hikyuu_cpp/hikyuu_server/common/mo.h @@ -7,18 +7,33 @@ #pragma once +#include +#include #include "moFileReader.hpp" -// 多国语言支持 -#define _(S) hku::g_moFR.Lookup(S) -#define _L(S) hku::moFR.Lookup(S) -#define _LC(ctx, str) hku::moFR.LookupWithContext(ctx, str) +#if defined(_MSC_VER) +// moFileReader.hpp 最后打开了4251告警,这里关闭 +#pragma warning(disable : 4251) +#endif /* _MSC_VER */ namespace hku { -extern moFileLib::moFileReader g_moFR; +class MOHelper { +public: + static void init(); -// 初始化读取mo文件 -void mo_init(const char *filename); + static std::string translate(const std::string &lang, const char *id) { + auto iter = ms_dict.find(lang); + return iter != ms_dict.end() ? ms_dict[lang].Lookup(id) : std::string(id); + } + + static std::string translate(const std::string &lang, const char *ctx, const char *id) { + auto iter = ms_dict.find(lang); + return iter != ms_dict.end() ? ms_dict[lang].LookupWithContext(ctx, id) : std::string(id); + } + +private: + static std::unordered_map ms_dict; +}; } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu_server/extract_pot.bat b/hikyuu_cpp/hikyuu_server/extract_pot.bat index 95dd65cb6..12222aa8b 100644 --- a/hikyuu_cpp/hikyuu_server/extract_pot.bat +++ b/hikyuu_cpp/hikyuu_server/extract_pot.bat @@ -1 +1,3 @@ -xgettext -k_ -k"_LC:1c,2" -k"_LC:1c,2" -s -c --package-name=hikyuu -f po/POTFILES.in -o po/hikyuu.pot \ No newline at end of file +; 实际不需要使用该命令,因为不能自动更新;实际直接使用 poedit 打开 i8n/zh_CN.po 进行更新 +; 此处保留,用于 poedit 设置关键字时参考 +xgettext -k_tr -k"_ctr:1c,2" -s -c --package-name=hikyuu -f i8n/POTFILES.in -o i8n/hikyuu.pot \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu_server/http/HttpHandle.cpp b/hikyuu_cpp/hikyuu_server/http/HttpHandle.cpp index d070a1da8..de7b8192e 100644 --- a/hikyuu_cpp/hikyuu_server/http/HttpHandle.cpp +++ b/hikyuu_cpp/hikyuu_server/http/HttpHandle.cpp @@ -5,6 +5,7 @@ * Author: fasiondog */ +#include #include "url.h" #include "HttpHandle.h" @@ -109,9 +110,9 @@ void HttpHandle::unknown_error(const std::string& errmsg) { } } -std::string HttpHandle::getReqHeader(const std::string& name) { +std::string HttpHandle::getReqHeader(const char* name) const { std::string result; - const char* head = nng_http_req_get_header(m_nng_req, name.c_str()); + const char* head = nng_http_req_get_header(m_nng_req, name); if (head) { result = std::string(head); } @@ -192,4 +193,16 @@ bool HttpHandle::getQueryParams(QueryParams& query_params) { return query_params.size() != 0; } +std::string HttpHandle::getLanguage() const { + std::string lang = getReqHeader("Accept-Language"); + auto pos = lang.find_first_of(','); + if (pos != std::string::npos) { + lang = lang.substr(0, pos); + } + if (!lang.empty()) { + to_lower(lang); + } + return lang; +} + } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu_server/http/HttpHandle.h b/hikyuu_cpp/hikyuu_server/http/HttpHandle.h index f2727160c..a07ecd741 100644 --- a/hikyuu_cpp/hikyuu_server/http/HttpHandle.h +++ b/hikyuu_cpp/hikyuu_server/http/HttpHandle.h @@ -12,9 +12,9 @@ #include #include - #include "HttpError.h" -#include "../common/log.h" +#include "common/mo.h" +#include "common/log.h" using json = nlohmann::json; // 不保持插入排序 using ordered_json = nlohmann::ordered_json; // 保持插入排序 @@ -60,9 +60,18 @@ class HttpHandle { /** * 获取请求头部信息 * @param name 头部信息名称 - * @return 如果获取不到将返回 NULL + * @return 如果获取不到将返回"" */ - std::string getReqHeader(const std::string &name); + std::string getReqHeader(const char *name) const; + + /** + * 获取请求头部信息 + * @param name 头部信息名称 + * @return 如果获取不到将返回"" + */ + std::string getReqHeader(const std::string &name) const { + return getReqHeader(name.c_str()); + } /** * 获取请求数据 @@ -116,6 +125,29 @@ class HttpHandle { setResData(data.dump()); } + /** + * 从 Accept-Language 获取第一个语言类型 + * @note 非严格 html 协议,仅返回排在最前面的语言类型 + */ + std::string getLanguage() const; + + /** + * 多语言翻译 + * @param msgid 待翻译的字符串 + */ + std::string _tr(const char *msgid) const { + return MOHelper::translate(getLanguage(), msgid); + } + + /** + * 多语言翻译 + * @param ctx 翻译上下文 + * @param msgid 待翻译的字符串 + */ + std::string _ctr(const char *ctx, const char *msgid) { + return MOHelper::translate(getLanguage(), ctx, msgid); + } + void operator()(); private: diff --git a/hikyuu_cpp/hikyuu_server/http/HttpServer.cpp b/hikyuu_cpp/hikyuu_server/http/HttpServer.cpp index 1bd3563f7..219907a8a 100644 --- a/hikyuu_cpp/hikyuu_server/http/HttpServer.cpp +++ b/hikyuu_cpp/hikyuu_server/http/HttpServer.cpp @@ -59,7 +59,7 @@ void HttpServer::start() { #if defined(_WIN32) // Windows 下设置控制台程序输出代码页为 UTF8 - auto g_old_cp = GetConsoleOutputCP(); + g_old_cp = GetConsoleOutputCP(); SetConsoleOutputCP(CP_UTF8); #endif diff --git a/hikyuu_cpp/hikyuu_server/i8n/POTFILES.in b/hikyuu_cpp/hikyuu_server/i8n/POTFILES.in new file mode 100644 index 000000000..69b1993b3 --- /dev/null +++ b/hikyuu_cpp/hikyuu_server/i8n/POTFILES.in @@ -0,0 +1,3 @@ +service/filter.cpp +service/user/SignHandle.h +service/user/UserHandle.h \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu_server/i8n/zh_CN.mo b/hikyuu_cpp/hikyuu_server/i8n/zh_CN.mo index 445c54130..b6a636080 100644 Binary files a/hikyuu_cpp/hikyuu_server/i8n/zh_CN.mo and b/hikyuu_cpp/hikyuu_server/i8n/zh_CN.mo differ diff --git a/hikyuu_cpp/hikyuu_server/i8n/zh_CN.po b/hikyuu_cpp/hikyuu_server/i8n/zh_CN.po new file mode 100644 index 000000000..45d3eac1b --- /dev/null +++ b/hikyuu_cpp/hikyuu_server/i8n/zh_CN.po @@ -0,0 +1,52 @@ +msgid "" +msgstr "" +"Project-Id-Version: Hikyuu\n" +"POT-Creation-Date: 2021-05-03 01:44+0800\n" +"PO-Revision-Date: 2021-05-03 01:44+0800\n" +"Last-Translator: \n" +"Language-Team: hikyuu.org\n" +"Language: zh_CN\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 2.4.3\n" +"X-Poedit-Basepath: ..\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Poedit-SourceCharset: UTF-8\n" +"X-Poedit-KeywordsList: _tr;_ctr:1c,2\n" +"X-Poedit-SearchPath-0: .\n" + +#: service/filter.cpp:30 +msgctxt "authorize" +msgid "Miss token" +msgstr "缺失令牌" + +#: service/filter.cpp:32 +msgctxt "authorize" +msgid "Invalid token" +msgstr "无效令牌" + +#: service/filter.cpp:66 +msgctxt "authorize" +msgid "token is expired" +msgstr "令牌过期" + +#: service/user/SignHandle.h:35 service/user/UserHandle.h:42 +msgctxt "user" +msgid "Duplicate user name" +msgstr "用户名重复" + +#: service/user/SignHandle.h:62 +msgctxt "user" +msgid "User does not exist" +msgstr "用户不存在" + +#: service/user/SignHandle.h:64 +msgctxt "user" +msgid "Wrong password" +msgstr "密码错误" + +#: service/user/UserHandle.h:63 +msgctxt "user" +msgid "No operation permission" +msgstr "没有操作权限" diff --git a/hikyuu_cpp/hikyuu_server/main.cpp b/hikyuu_cpp/hikyuu_server/main.cpp index 5b864760a..ee0ee5d01 100644 --- a/hikyuu_cpp/hikyuu_server/main.cpp +++ b/hikyuu_cpp/hikyuu_server/main.cpp @@ -13,11 +13,6 @@ #include "service/assist/AssistService.h" #include "service/trade/TradeService.h" -#if defined(_WIN32) || defined(_WIN64) -#include -#pragma comment(lib, "Kernel32.lib") -#endif - using namespace hku; #define HKU_SERVICE_API(name) "/hku/" #name "/v1" @@ -25,12 +20,8 @@ using namespace hku; int main(int argc, char* argv[]) { init_server_logger(); -#if defined(_WIN32) || defined(_WIN64) - LANGID lid = GetSystemDefaultLangID(); - if (lid == 0x0804) { - mo_init("i8n/zh_CN.mo"); - } -#endif + // 初始化多语言支持 + MOHelper::init(); LOG_INFO("start server ... You can press Ctrl-C stop"); diff --git a/hikyuu_cpp/hikyuu_server/po/POTFILES.in b/hikyuu_cpp/hikyuu_server/po/POTFILES.in deleted file mode 100644 index 2573a0ecb..000000000 --- a/hikyuu_cpp/hikyuu_server/po/POTFILES.in +++ /dev/null @@ -1 +0,0 @@ -service/user/UserHandle.h \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu_server/po/hikyuu.pot b/hikyuu_cpp/hikyuu_server/po/hikyuu.pot deleted file mode 100644 index 040246592..000000000 --- a/hikyuu_cpp/hikyuu_server/po/hikyuu.pot +++ /dev/null @@ -1,22 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the hikyuu package. -# FIRST AUTHOR , YEAR. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: hikyuu\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-05-01 22:18+0800\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: fasiondog \n" -"Language-Team: LANGUAGE \n" -"Language: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" - -#: service/user/UserHandle.h:41 -msgid "Duplicate user name" -msgstr "" diff --git a/hikyuu_cpp/hikyuu_server/po/zh_CN.po b/hikyuu_cpp/hikyuu_server/po/zh_CN.po deleted file mode 100644 index 0b553e9ff..000000000 --- a/hikyuu_cpp/hikyuu_server/po/zh_CN.po +++ /dev/null @@ -1,23 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the hikyuu package. -# FIRST AUTHOR , YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: hikyuu\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-05-01 22:18+0800\n" -"PO-Revision-Date: 2021-05-01 22:58+0800\n" -"Language-Team: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 2.4.3\n" -"Last-Translator: \n" -"Plural-Forms: nplurals=1; plural=0;\n" -"Language: zh_CN\n" - -#: service/user/UserHandle.h:41 -msgid "Duplicate user name" -msgstr "重复用户名" diff --git a/hikyuu_cpp/hikyuu_server/service/RestHandle.h b/hikyuu_cpp/hikyuu_server/service/RestHandle.h index c7127a798..3062cf647 100644 --- a/hikyuu_cpp/hikyuu_server/service/RestHandle.h +++ b/hikyuu_cpp/hikyuu_server/service/RestHandle.h @@ -8,7 +8,6 @@ #pragma once #include -#include "common/mo.h" #include "http/HttpHandle.h" #include "db/db.h" // 这里统一引入 #include "RestErrorCode.h" @@ -32,6 +31,7 @@ class NoAuthRestHandle : public HttpHandle { virtual void after_run() override { // 强制关闭连接,即仅有短连接 // nng_http_res_set_status(m_nng_res, NNG_HTTP_STATUS_OK); + std::string lang = getLanguage(); setResData(res); } diff --git a/hikyuu_cpp/hikyuu_server/service/filter.cpp b/hikyuu_cpp/hikyuu_server/service/filter.cpp index 26ae7099c..468d1e4d9 100644 --- a/hikyuu_cpp/hikyuu_server/service/filter.cpp +++ b/hikyuu_cpp/hikyuu_server/service/filter.cpp @@ -22,14 +22,14 @@ std::string createToken(uint64_t user_id) { } struct TokenExpiredException : public hku::exception { - TokenExpiredException() : hku::exception("token is expired") {} + TokenExpiredException(const std::string &msg) : hku::exception(msg) {} }; void AuthorizeFilter(HttpHandle *handle) { std::string token = handle->getReqHeader("hku_token"); - HTTP_CHECK(!token.empty(), RestErrorCode::MISS_TOKEN, "Miss token"); + HTTP_CHECK(!token.empty(), RestErrorCode::MISS_TOKEN, handle->_ctr("authorize", "Miss token")); - const char *errmsg = "Invalid token"; + std::string errmsg = handle->_ctr("authorize", "Invalid token"); if (!TokenCache::have(token)) { TokenModel token_record; DB::getConnect()->load(token_record, fmt::format(R"(token="{}")", token)); @@ -63,7 +63,7 @@ void AuthorizeFilter(HttpHandle *handle) { con->exec(fmt::format(R"(delete from {} where token="{}")", TokenModel::getTableName(), token)); } - throw TokenExpiredException(); + throw TokenExpiredException(handle->_ctr("authorize", "token is expired")); } RestHandle *rest_handle = dynamic_cast(handle); diff --git a/hikyuu_cpp/hikyuu_server/service/user/SignHandle.h b/hikyuu_cpp/hikyuu_server/service/user/SignHandle.h index 907726e46..976f8dd56 100644 --- a/hikyuu_cpp/hikyuu_server/service/user/SignHandle.h +++ b/hikyuu_cpp/hikyuu_server/service/user/SignHandle.h @@ -31,7 +31,8 @@ class SignupHandle : public NoAuthRestHandle { TransAction trans(con); int count = con->queryInt(fmt::format(R"(select count(id) from {} where name="{}")", UserModel::getTableName(), user.getName())); - HTTP_CHECK(count == 0, UserErrorCode::USER_NAME_REPETITION, "Duplicate user name"); + HTTP_CHECK(count == 0, UserErrorCode::USER_NAME_REPETITION, + _ctr("user", "Duplicate user name")); user.setUserId(DB::getNewUserId()); con->save(user, false); } @@ -57,9 +58,10 @@ class LoginHandle : public NoAuthRestHandle { UserModel user; auto con = DB::getConnect(); con->load(user, fmt::format(R"(name="{}")", req["user"].get())); - HTTP_CHECK(user.id() != 0, UserErrorCode::USER_NOT_EXIST, "User does not exist"); + HTTP_CHECK(user.id() != 0, UserErrorCode::USER_NOT_EXIST, + _ctr("user", "User does not exist")); HTTP_CHECK(user.getPassword() == req["password"].get(), - UserErrorCode::USER_WRONG_PASSWORD, "Wrong password"); + UserErrorCode::USER_WRONG_PASSWORD, _ctr("user", "Wrong password")); TokenModel token; { diff --git a/hikyuu_cpp/hikyuu_server/service/user/UserHandle.h b/hikyuu_cpp/hikyuu_server/service/user/UserHandle.h index 632577f32..07a058fbf 100644 --- a/hikyuu_cpp/hikyuu_server/service/user/UserHandle.h +++ b/hikyuu_cpp/hikyuu_server/service/user/UserHandle.h @@ -38,7 +38,8 @@ class AddUserHandle : public RestHandle { TransAction trans(con); int count = con->queryInt(fmt::format(R"(select count(id) from {} where name="{}")", UserModel::getTableName(), user.getName())); - HTTP_CHECK(count == 0, UserErrorCode::USER_NAME_REPETITION, _("Duplicate user name")); + HTTP_CHECK(count == 0, UserErrorCode::USER_NAME_REPETITION, + _ctr("user", "Duplicate user name")); user.setUserId(DB::getNewUserId()); con->save(user, false); } @@ -59,7 +60,7 @@ class RemoveUserHandle : public RestHandle { UserModel admin; con->load(admin, fmt::format("user_id={}", getCurrentUserId())); HTTP_CHECK(admin.getName() == "admin", UserErrorCode::USER_NO_RIGHT, - "No operation permission"); + _ctr("user", "No operation permission")); check_missing_param("user"); { diff --git a/hikyuu_cpp/hikyuu_server/xmake.lua b/hikyuu_cpp/hikyuu_server/xmake.lua index b49ead2e4..048530d01 100644 --- a/hikyuu_cpp/hikyuu_server/xmake.lua +++ b/hikyuu_cpp/hikyuu_server/xmake.lua @@ -56,7 +56,7 @@ target("hkuserver") --add_files("./main.cpp") after_build(function(target) - os.cp("$(projectdir)/hikyuu_cpp/hikyuu_server/i8n/", "$(buildir)/$(mode)/$(plat)/$(arch)/lib/i8n") + os.cp("$(projectdir)/hikyuu_cpp/hikyuu_server/i8n/", "$(buildir)/$(mode)/$(plat)/$(arch)/lib/") end) target_end()