mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-25 14:12:49 +00:00
feat(locale): load every locale in locale.xml on request
Locale::LoadFromFile can keep the other locales' phrases too, and GetPhrase(id, locale) answers in one of them, falling back to the default. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,12 +3,20 @@
|
|||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
std::unordered_map<std::string, std::string> g_Phrases;
|
std::unordered_map<std::string, std::string> g_Phrases; // the default locale's
|
||||||
|
// The other locales' phrases (only loaded with allLocales), by locale
|
||||||
|
std::unordered_map<std::string, std::unordered_map<std::string, std::string>> g_Others;
|
||||||
|
std::vector<std::string> g_Locales;
|
||||||
bool g_Loaded = false;
|
bool g_Loaded = false;
|
||||||
const std::string g_Empty;
|
const std::string g_Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Locale::LoadFromFile(const std::string& filepath, const std::string& locale) {
|
void Locale::LoadFromFile(const std::string& filepath, const std::string& locale, bool allLocales) {
|
||||||
|
g_Phrases.clear();
|
||||||
|
g_Others.clear();
|
||||||
|
g_Locales.clear();
|
||||||
|
g_Loaded = false;
|
||||||
|
|
||||||
tinyxml2::XMLDocument doc;
|
tinyxml2::XMLDocument doc;
|
||||||
if (doc.LoadFile(filepath.c_str()) != tinyxml2::XML_SUCCESS) {
|
if (doc.LoadFile(filepath.c_str()) != tinyxml2::XML_SUCCESS) {
|
||||||
LOG("Failed to load locale file: %s", filepath.c_str());
|
LOG("Failed to load locale file: %s", filepath.c_str());
|
||||||
@@ -18,6 +26,18 @@ void Locale::LoadFromFile(const std::string& filepath, const std::string& locale
|
|||||||
auto* root = doc.FirstChildElement("localization");
|
auto* root = doc.FirstChildElement("localization");
|
||||||
if (!root) return;
|
if (!root) return;
|
||||||
|
|
||||||
|
g_Locales.push_back(locale);
|
||||||
|
if (allLocales) {
|
||||||
|
if (auto* locales = root->FirstChildElement("locales")) {
|
||||||
|
for (auto* entry = locales->FirstChildElement("locale"); entry; entry = entry->NextSiblingElement("locale")) {
|
||||||
|
const char* name = entry->GetText();
|
||||||
|
if (!name || locale == name || g_Others.contains(name)) continue;
|
||||||
|
g_Locales.push_back(name);
|
||||||
|
g_Others[name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto* phrases = root->FirstChildElement("phrases");
|
auto* phrases = root->FirstChildElement("phrases");
|
||||||
if (!phrases) return;
|
if (!phrases) return;
|
||||||
|
|
||||||
@@ -27,18 +47,20 @@ void Locale::LoadFromFile(const std::string& filepath, const std::string& locale
|
|||||||
|
|
||||||
for (auto* trans = phrase->FirstChildElement("translation"); trans; trans = trans->NextSiblingElement("translation")) {
|
for (auto* trans = phrase->FirstChildElement("translation"); trans; trans = trans->NextSiblingElement("translation")) {
|
||||||
const char* loc = trans->Attribute("locale");
|
const char* loc = trans->Attribute("locale");
|
||||||
if (loc && locale == loc) {
|
const char* text = trans->GetText();
|
||||||
const char* text = trans->GetText();
|
if (!loc || !text) continue;
|
||||||
if (text) {
|
if (locale == loc) {
|
||||||
g_Phrases[id] = text;
|
g_Phrases[id] = text;
|
||||||
}
|
if (!allLocales) break;
|
||||||
break;
|
} else if (allLocales) {
|
||||||
|
const auto other = g_Others.find(loc);
|
||||||
|
if (other != g_Others.end()) other->second[id] = text;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
g_Loaded = true;
|
g_Loaded = true;
|
||||||
LOG("Loaded %zu locale phrases from %s (locale: %s)", g_Phrases.size(), filepath.c_str(), locale.c_str());
|
LOG("Loaded %zu locale phrases from %s (locale: %s, %zu other locale(s))", g_Phrases.size(), filepath.c_str(), locale.c_str(), g_Others.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string& Locale::GetPhrase(const std::string& phraseId) {
|
const std::string& Locale::GetPhrase(const std::string& phraseId) {
|
||||||
@@ -47,6 +69,17 @@ const std::string& Locale::GetPhrase(const std::string& phraseId) {
|
|||||||
return g_Empty;
|
return g_Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::string& Locale::GetPhrase(const std::string& phraseId, const std::string& locale) {
|
||||||
|
if (const auto other = g_Others.find(locale); other != g_Others.end()) {
|
||||||
|
if (const auto it = other->second.find(phraseId); it != other->second.end()) return it->second;
|
||||||
|
}
|
||||||
|
return GetPhrase(phraseId);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<std::string>& Locale::GetLocales() {
|
||||||
|
return g_Locales;
|
||||||
|
}
|
||||||
|
|
||||||
bool Locale::IsLoaded() {
|
bool Locale::IsLoaded() {
|
||||||
return g_Loaded;
|
return g_Loaded;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,17 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace Locale {
|
namespace Locale {
|
||||||
void LoadFromFile(const std::string& filepath, const std::string& locale = "en_US");
|
/**
|
||||||
|
* Load the client's locale.xml: the phrases of `locale` (the default, which GetPhrase(id) answers from) and, with
|
||||||
|
* `allLocales`, those of every other locale the file lists too. Replaces what an earlier load read. Call before
|
||||||
|
* other threads read phrases; lookups only read afterwards.
|
||||||
|
*/
|
||||||
|
void LoadFromFile(const std::string& filepath, const std::string& locale = "en_US", bool allLocales = false);
|
||||||
const std::string& GetPhrase(const std::string& phraseId);
|
const std::string& GetPhrase(const std::string& phraseId);
|
||||||
|
// The phrase in `locale`, else in the default locale; empty when neither has it
|
||||||
|
const std::string& GetPhrase(const std::string& phraseId, const std::string& locale);
|
||||||
|
// The locales loaded ("en_US", "de_DE", ...), the default first; empty before a load
|
||||||
|
const std::vector<std::string>& GetLocales();
|
||||||
bool IsLoaded();
|
bool IsLoaded();
|
||||||
std::vector<std::string> GetPhraseIdsWithPrefix(const std::string& prefix);
|
std::vector<std::string> GetPhraseIdsWithPrefix(const std::string& prefix);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user