From 0d6cc1a5f6680dfc9107d4736367acb114f14487 Mon Sep 17 00:00:00 2001 From: Gridgt Date: Tue, 30 Jun 2026 23:48:35 +0300 Subject: [PATCH] feat: add hybrid localization system (hardcoded + external JSON) - scanLangDir() discovers lang/*.json at startup, adds new languages - loadExternal() merges JSON translations over hardcoded dictionaries - Encoding: UTF-16 LE with BOM via Util::readFile (same as config.json) - de-DE.json included as external-only test language --- ScreenCapture/Lang.cpp | 62 +++++++++++++++++++++++++++++++++- ScreenCapture/Lang.h | 6 ++++ ScreenCapture/lang/de-DE.json | Bin 0 -> 5214 bytes 3 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 ScreenCapture/lang/de-DE.json diff --git a/ScreenCapture/Lang.cpp b/ScreenCapture/Lang.cpp index 4e6f284..c0afaeb 100644 --- a/ScreenCapture/Lang.cpp +++ b/ScreenCapture/Lang.cpp @@ -1,17 +1,20 @@ #include "pch.h" #include "Lang.h" #include "Setting.h" +#include "Util.h" std::unique_ptr lang; +std::map Lang::externalFiles; namespace { // 所有支持的语言(code + 自身语言名称);新增语言只需在此追加并补全对应字典 - const std::map supportedLangs = { + std::map supportedLangs = { { L"zh-CN", L"中文" }, { L"en-US", L"English" }, { L"id-ID", L"Bahasa Indonesia" }, { L"ru-RU", L"Русский" }, }; + } Lang::Lang() @@ -27,6 +30,7 @@ void Lang::init() if (!lang) { lang = std::make_unique(); } + scanLangDir(); lang->setLang(Setting::getLanguage()); } @@ -62,6 +66,62 @@ void Lang::setLang(const std::wstring& l) } lang->langCode = supported ? l : L"zh-CN"; lang->load(lang->langCode); + lang->loadExternal(lang->langCode); +} + +std::filesystem::path Lang::getLangDir() +{ + std::vector buf(MAX_PATH); + GetModuleFileName(nullptr, buf.data(), MAX_PATH); + return std::filesystem::path(buf.data()).parent_path() / L"lang"; +} + +void Lang::scanLangDir() +{ + auto langDir = getLangDir(); + if (!std::filesystem::exists(langDir)) return; + + for (auto& entry : std::filesystem::directory_iterator(langDir)) { + if (entry.path().extension() != L".json") continue; + + auto content = Util::readFile(entry.path().wstring()); + if (content.empty()) continue; + + try { + auto obj = JsonObject::Parse(content.data()); + if (!obj.HasKey(L"language") || !obj.HasKey(L"name")) continue; + auto code = std::wstring(obj.GetNamedString(L"language")); + auto name = std::wstring(obj.GetNamedString(L"name")); + if (code.empty() || name.empty()) continue; + + supportedLangs[code] = name; + externalFiles[code] = entry.path(); + } + catch (winrt::hresult_error const&) { + continue; + } + } +} + +void Lang::loadExternal(const std::wstring& l) +{ + auto it = externalFiles.find(l); + if (it == externalFiles.end()) return; + + auto content = Util::readFile(it->second.wstring()); + if (content.empty()) return; + + try { + auto obj = JsonObject::Parse(content.data()); + if (!obj.HasKey(L"translations")) return; + auto translations = obj.GetNamedObject(L"translations"); + for (const auto& [key, value] : translations) { + dic[std::wstring(key)] = std::wstring(value.GetString()); + } + } + catch (winrt::hresult_error const&) { + return; + } } void Lang::load(const std::wstring& l) diff --git a/ScreenCapture/Lang.h b/ScreenCapture/Lang.h index a85f212..8afe3c1 100644 --- a/ScreenCapture/Lang.h +++ b/ScreenCapture/Lang.h @@ -1,5 +1,7 @@ #pragma once +#include + /// /// 多语言管理(单例),与 Setting 同一套模式。 /// 字典直接硬编码在代码中(保持“单文件、零外部依赖”的定位)。 @@ -23,6 +25,10 @@ class Lang static const std::map& getLangs(); private: void load(const std::wstring& lang); + void loadExternal(const std::wstring& l); + static void scanLangDir(); + static std::filesystem::path getLangDir(); std::map dic; std::wstring langCode{ L"zh-CN" }; + static std::map externalFiles; }; diff --git a/ScreenCapture/lang/de-DE.json b/ScreenCapture/lang/de-DE.json new file mode 100644 index 0000000000000000000000000000000000000000..71e35ff3c0ca1a2d0bae51cca068fdfc5cfeab04 GIT binary patch literal 5214 zcmbVQ%Wm6N5S?|ug7B&Z8lj5-1+w!)b_2_WVH*k1O`uA$%vur!ii)ii{nG;7pZ zFR*LwcAeq(S_)aoNFH`}7LqIHl}acil?-xLTmB|LU8pj(1>Iph|&&v12- z@&RaR!K{?ihW`t8rfNZI(x$@-?+aWhc10cf`Wz9i^=&(+i9Ca!}qk@tVQMm z5i*zGu`e&_e$_R`3)hiM%N1d|JIB< z#5+N?r|j3{;vt?n@V+)r5AjT-p27RAN)F(bG*KZMXYgVC7{`-gv5!Y@l0={?qb#*M6Rc*}R4U!Ti*@>kDk;49-U^i_WCWA!Ux@!WfF?O3L5U-0N3Y9zN z<)J8#$XFE_xo5C8`&^3OuZv#8(63Ck-7$nYgEpOr@AN*ucPH>3u(GE6TI9bWPNB^b z?^)sbJ=Mon>y)fs8R~)MmvNxK_-3w=VACI?!FDpU-gVUV zY1{iZrPabLuz@#Ncld3WBOQ?UTxVP;VLSN35NKA9I;~ zlR34MK*8eVgRL?lNNMKwCKVIVQGdNZ&b~DbV79~B* z$-Ql)cpsQu!c-PIy5WvBh*qlB&`3!%mepf9fu%X^vrfnPL5Z0R@aO$(F~lCvN||VGG4q1|yUX-eV^!c}-9w!79y@#N(?{FW!M||l F-~Uw2G8+H@ literal 0 HcmV?d00001