mirror of https://github.com/WerWolv/ImHex
feat: Added support for choosing the system-native language
This commit is contained in:
parent
9f24b35b1f
commit
4ade751caf
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) { }
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ namespace hex::plugin::builtin {
|
|||
}
|
||||
|
||||
// Continue button
|
||||
const auto buttonSize = scaled({ 100, 50 });
|
||||
const auto buttonSize = scaled({ 130, 50 });
|
||||
ImGui::SetCursorPos(ImHexApi::System::getMainWindowSize() - buttonSize - scaled({ 10, 10 }));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, buttonFadeIn);
|
||||
if (ImGuiExt::DimmedButton(fmt::format("{} {}", "hex.ui.common.continue"_lang, ICON_VS_ARROW_RIGHT).c_str(), buttonSize))
|
||||
|
|
@ -236,7 +236,7 @@ namespace hex::plugin::builtin {
|
|||
|
||||
// Draw globe image
|
||||
const auto imageSize = s_compassTexture->getSize() / (1.5F * (1.0F / ImHexApi::System::getGlobalScale()));
|
||||
ImGui::SetCursorPos((ImGui::GetWindowSize() / 2 - imageSize / 2) - ImVec2(0, 50_scaled));
|
||||
ImGui::SetCursorPos((ImGui::GetWindowSize() / 2 - imageSize / 2) - ImVec2(0, 100_scaled));
|
||||
ImGui::Image(*s_globeTexture, imageSize);
|
||||
|
||||
ImGui::NewLine();
|
||||
|
|
@ -249,7 +249,9 @@ namespace hex::plugin::builtin {
|
|||
const auto availableSize = ImGui::GetContentRegionAvail();
|
||||
if (ImGui::BeginChild("##language_text", ImVec2(availableSize.x, 30_scaled))) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(ImGuiCol_Text, textFadeIn - textFadeOut));
|
||||
fonts::Default().push(1.2);
|
||||
ImGuiExt::TextFormattedCentered("{}", LocalizationManager::get(currLanguage->first, "hex.builtin.setting.interface.language"));
|
||||
fonts::Default().pop();
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
|
@ -260,6 +262,17 @@ namespace hex::plugin::builtin {
|
|||
ImGui::SetCursorPosX(availableSize.x / 3);
|
||||
if (ImGuiExt::BeginSubWindow(ICON_VS_GLOBE, nullptr, ImVec2(availableSize.x / 3, availableSize.y - ImGui::GetTextLineHeightWithSpacing() * 3))) {
|
||||
if (ImGui::BeginListBox("##language", ImGui::GetContentRegionAvail())) {
|
||||
{
|
||||
const auto osLanguage = hex::getOSLanguage();
|
||||
if (osLanguage.has_value()) {
|
||||
const auto &languageDefinition = LocalizationManager::getLanguageDefinition(osLanguage.value());
|
||||
if (ImGui::Selectable(fmt::format("Native ({})", languageDefinition.nativeName).c_str(), LocalizationManager::getSelectedLanguageId() == "native")) {
|
||||
LocalizationManager::setLanguage("native");
|
||||
}
|
||||
ImGui::Separator();
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &[langId, definition] : LocalizationManager::getLanguageDefinitions()) {
|
||||
if (ImGui::Selectable(definition.name.c_str(), langId == LocalizationManager::getSelectedLanguageId())) {
|
||||
LocalizationManager::setLanguage(langId);
|
||||
|
|
@ -271,7 +284,7 @@ namespace hex::plugin::builtin {
|
|||
ImGuiExt::EndSubWindow();
|
||||
|
||||
// Continue button
|
||||
const auto buttonSize = scaled({ 100, 50 });
|
||||
const auto buttonSize = scaled({ 130, 50 });
|
||||
ImGui::SetCursorPos(ImHexApi::System::getMainWindowSize() - buttonSize - scaled({ 10, 10 }));
|
||||
if (ImGuiExt::DimmedButton(fmt::format("{} {}", "hex.ui.common.continue"_lang, ICON_VS_ARROW_RIGHT).c_str(), buttonSize))
|
||||
page += 1;
|
||||
|
|
@ -397,7 +410,7 @@ namespace hex::plugin::builtin {
|
|||
ImGuiExt::TextFormattedCentered("hex.builtin.oobe.tutorial_question"_lang);
|
||||
|
||||
// Draw no button
|
||||
const auto buttonSize = scaled({ 100, 50 });
|
||||
const auto buttonSize = scaled({ 130, 50 });
|
||||
ImGui::SetCursorPos(ImHexApi::System::getMainWindowSize() - ImVec2(buttonSize.x * 2 + 20, buttonSize.y + 10));
|
||||
if (ImGuiExt::DimmedButton("hex.ui.common.no"_lang, buttonSize)) {
|
||||
oobeDone = true;
|
||||
|
|
|
|||
|
|
@ -830,15 +830,26 @@ namespace hex::plugin::builtin {
|
|||
ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.interface", "hex.builtin.setting.interface.style", "hex.builtin.setting.interface.show_header_command_palette", true);
|
||||
ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.interface", "hex.builtin.setting.interface.style", "hex.builtin.setting.interface.display_shortcut_highlights", true);
|
||||
|
||||
{
|
||||
std::vector<std::string> languageNames;
|
||||
std::vector<nlohmann::json> languageCodes;
|
||||
|
||||
{
|
||||
const auto osLanguage = hex::getOSLanguage();
|
||||
if (osLanguage.has_value()) {
|
||||
const auto &languageDefinition = LocalizationManager::getLanguageDefinition(osLanguage.value());
|
||||
languageNames.emplace_back(fmt::format("Native ({})", languageDefinition.nativeName));
|
||||
languageCodes.emplace_back("native");
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &[languageCode, definition] : LocalizationManager::getLanguageDefinitions()) {
|
||||
languageNames.emplace_back(fmt::format("{} ({})", definition.nativeName, definition.name));
|
||||
languageCodes.emplace_back(languageCode);
|
||||
}
|
||||
|
||||
ContentRegistry::Settings::add<Widgets::DropDown>("hex.builtin.setting.interface", "hex.builtin.setting.interface.language", "hex.builtin.setting.interface.language", languageNames, languageCodes, "en-US");
|
||||
}
|
||||
|
||||
ContentRegistry::Settings::add<Widgets::TextBox>("hex.builtin.setting.interface", "hex.builtin.setting.interface.language", "hex.builtin.setting.interface.wiki_explain_language", "en");
|
||||
ContentRegistry::Settings::add<FPSWidget>("hex.builtin.setting.interface", "hex.builtin.setting.interface.window", "hex.builtin.setting.interface.fps");
|
||||
|
|
|
|||
Loading…
Reference in New Issue