feat: Added support for choosing the system-native language

This commit is contained in:
WerWolv
2025-08-17 15:50:27 +02:00
parent 9f24b35b1f
commit 4ade751caf
6 changed files with 104 additions and 14 deletions

View File

@@ -26,8 +26,6 @@ EXPORT_MODULE namespace hex {
struct LanguageDefinition {
LanguageId id;
std::string name, nativeName;
std::string flag;
std::string filePath;
LanguageId fallbackLanguageId;
std::vector<PathEntry> languageFilePaths;
@@ -38,6 +36,7 @@ EXPORT_MODULE namespace hex {
[[nodiscard]] const LanguageId& getSelectedLanguageId();
[[nodiscard]] const std::string& get(const LanguageId& languageId, const UnlocalizedString &unlocalizedString);
[[nodiscard]] const std::map<LanguageId, LanguageDefinition>& getLanguageDefinitions();
[[nodiscard]] const LanguageDefinition& getLanguageDefinition(const LanguageId &languageId);
}

View File

@@ -384,4 +384,5 @@ namespace hex {
[[nodiscard]] std::optional<ImColor> blendColors(const std::optional<ImColor> &a, const std::optional<ImColor> &b);
std::optional<std::chrono::system_clock::time_point> parseTime(std::string_view format, const std::string &timeString);
std::optional<std::string> getOSLanguage();
}

View File

@@ -10,6 +10,8 @@ namespace hex {
namespace LocalizationManager {
constexpr static auto FallbackLanguageId = "en-US";
namespace {
AutoReset<std::map<LanguageId, LanguageDefinition>> s_languageDefinitions;
@@ -29,6 +31,10 @@ namespace hex {
auto &definition = (*s_languageDefinitions)[item["code"].get<std::string>()];
if (definition.id.empty()) {
definition.id = item["code"].get<std::string>();
}
if (definition.name.empty() && item.contains("name")) {
definition.name = item["name"].get<std::string>();
}
@@ -80,7 +86,6 @@ namespace hex {
if (const auto it = s_languageDefinitions->find(languageId); it == s_languageDefinitions->end()) {
log::error("No language definition found for language: {}", languageId);
constexpr static auto FallbackLanguageId = "en-US";
if (languageId != FallbackLanguageId)
populateLocalization(FallbackLanguageId, localizations);
} else {
@@ -107,6 +112,12 @@ namespace hex {
}
void setLanguage(const LanguageId &languageId) {
if (languageId == "native") {
setLanguage(hex::getOSLanguage().value_or(FallbackLanguageId));
s_selectedLanguageId = languageId;
return;
}
if (*s_selectedLanguageId == languageId)
return;
@@ -139,6 +150,11 @@ namespace hex {
return *s_languageDefinitions;
}
const LanguageDefinition& getLanguageDefinition(const LanguageId &languageId) {
const auto bestMatch = findBestLanguageMatch(languageId);
return (*s_languageDefinitions)[bestMatch];
}
}
Lang::Lang(const char *unlocalizedString) : m_entryHash(LangConst::hash(unlocalizedString)), m_unlocalizedString(unlocalizedString) { }

View File

@@ -787,6 +787,56 @@ namespace hex {
return std::chrono::system_clock::from_time_t(std::mktime(&time));
}
std::optional<std::string> getOSLanguage() {
const static auto osLanguage = [] -> std::optional<std::string> {
#if defined(OS_WINDOWS)
const auto langId = ::GetUserDefaultUILanguage();
std::array<wchar_t, LOCALE_NAME_MAX_LENGTH> localeName;
if (::LCIDToLocaleName(MAKELCID(langId, SORT_DEFAULT), localeName.data(), localeName.size(), 0) > 0) {
return utf16ToUtf8(localeName.data());
}
return std::nullopt;
#elif defined(OS_MACOS)
const auto langs = CFLocaleCopyPreferredLanguages();
if (langs == nullptr || CFArrayGetCount(langs) == 0)
return std::nullopt;
ON_SCOPE_EXIT { CFRelease(langs); };
const auto lang = (CFStringRef)CFArrayGetValueAtIndex(langs, 0);
std::array<char, 256> buffer;
if (CFStringGetCString(lang, buffer.data(), buffer.size(), kCFStringEncodingUTF8)) {
return std::string(buffer.data());
}
return std::nullopt;
#elif defined(OS_LINUX)
auto lang = getEnvironmentVariable("LC_ALL");
if (!lang.has_value()) lang = getEnvironmentVariable("LC_MESSAGES");
if (!lang.has_value()) lang = getEnvironmentVariable("LANG");
if (lang.has_value() && !lang->empty() && *lang != "C" && *lang != "C.UTF-8") {
auto parts = wolv::util::splitString(*lang, ".");
if (parts.size() > 0)
return parts[0];
else
return *lang;
}
return std::nullopt;
#elif defined(OS_WEB)
return toLower(EM_ASM_INT({
return (int)navigator.language.length > 0 ? navigator.language : navigator.languages[0];
}));
#else
return std::nullopt;
#endif
}();
return osLanguage;
}
extern "C" void macOSCloseButtonPressed() {
EventCloseButtonPressed::post();
}