i18n: init localization for ANR, Permissions and Notifications (#12316)

Adds localization support for en, it, pl and jp

---------

Co-authored-by: Mihai Fufezan <mihai@fufexan.net>
Co-authored-by: Aaron Blasko <blaskoazzolaaaron@gmail.com>
This commit is contained in:
Vaxry 2025-11-16 14:51:14 +00:00 committed by GitHub
parent cb47eb1d11
commit e616e595ae
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 302 additions and 67 deletions

View file

@ -8,6 +8,7 @@
#include "./eventLoop/EventLoopManager.hpp"
#include "../config/ConfigValue.hpp"
#include "../xwayland/XSurface.hpp"
#include "../i18n/Engine.hpp"
using namespace Hyprutils::OS;
@ -83,7 +84,7 @@ void CANRManager::onTick() {
if (data->missedResponses >= *PANRTHRESHOLD) {
if (!data->isRunning() && !data->dialogSaidWait) {
data->runDialog("Application Not Responding", firstWindow->m_title, firstWindow->m_class, data->getPid());
data->runDialog(firstWindow->m_title, firstWindow->m_class, data->getPid());
for (const auto& w : g_pCompositor->m_windows) {
if (!w->m_isMapped)
@ -176,16 +177,29 @@ CANRManager::SANRData::~SANRData() {
killDialog();
}
void CANRManager::SANRData::runDialog(const std::string& title, const std::string& appName, const std::string appClass, pid_t dialogWmPID) {
void CANRManager::SANRData::runDialog(const std::string& appName, const std::string appClass, pid_t dialogWmPID) {
if (dialogBox && dialogBox->isRunning())
killDialog();
dialogBox = CAsyncDialogBox::create(title,
std::format("Application {} with class of {} is not responding.\nWhat do you want to do with it?", appName.empty() ? "unknown" : appName,
appClass.empty() ? "unknown" : appClass),
std::vector<std::string>{"Terminate", "Wait"});
const auto OPTION_TERMINATE_STR = I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_OPTION_TERMINATE, {});
const auto OPTION_WAIT_STR = I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_OPTION_WAIT, {});
dialogBox->open()->then([dialogWmPID, this](SP<CPromiseResult<std::string>> r) {
dialogBox =
CAsyncDialogBox::create(I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_TITLE, {}),
I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_CONTENT,
{
//
{"class", appClass.empty() ? I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_PROP_UNKNOWN, {}) : appClass}, //
{"title", appName.empty() ? I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_PROP_UNKNOWN, {}) : appName} //
}),
std::vector<std::string>{
//
OPTION_TERMINATE_STR, //
OPTION_WAIT_STR //
} //
);
dialogBox->open()->then([dialogWmPID, this, OPTION_TERMINATE_STR, OPTION_WAIT_STR](SP<CPromiseResult<std::string>> r) {
if (r->hasError()) {
Debug::log(ERR, "CANRManager::SANRData::runDialog: error spawning dialog");
return;
@ -193,9 +207,9 @@ void CANRManager::SANRData::runDialog(const std::string& title, const std::strin
const auto& result = r->result();
if (result.starts_with("Terminate"))
if (result.starts_with(OPTION_TERMINATE_STR))
::kill(dialogWmPID, SIGKILL);
else if (result.starts_with("Wait"))
else if (result.starts_with(OPTION_WAIT_STR))
dialogSaidWait = true;
else
Debug::log(ERR, "CANRManager::SANRData::runDialog: lambda: unrecognized result: {}", result);

View file

@ -39,7 +39,7 @@ class CANRManager {
bool dialogSaidWait = false;
SP<CAsyncDialogBox> dialogBox;
void runDialog(const std::string& title, const std::string& appName, const std::string appClass, pid_t dialogWmPID);
void runDialog(const std::string& appName, const std::string appClass, pid_t dialogWmPID);
bool isRunning();
void killDialog();
bool isDefunct() const;

View file

@ -5,6 +5,7 @@
#include "../../Compositor.hpp"
#include "../../config/ConfigValue.hpp"
#include "../../helpers/MiscFunctions.hpp"
#include "../../i18n/Engine.hpp"
#include <hyprutils/string/String.hpp>
using namespace Hyprutils::String;
@ -57,17 +58,6 @@ static const char* permissionToString(eDynamicPermissionType type) {
return "error";
}
static const char* permissionToHumanString(eDynamicPermissionType type) {
switch (type) {
case PERMISSION_TYPE_UNKNOWN: return "An application <b>{}</b> is requesting an unknown permission.";
case PERMISSION_TYPE_SCREENCOPY: return "An application <b>{}</b> is trying to capture your screen.<br/><br/>Do you want to allow it to do so?";
case PERMISSION_TYPE_PLUGIN: return "An application <b>{}</b> is trying to load a plugin: <b>{}</b>.<br/><br/>Do you want to load it?";
case PERMISSION_TYPE_KEYBOARD: return "A new keyboard has been plugged in: {}.<br/><br/>Do you want to allow it to operate?";
}
return "error";
}
static const char* specialPidToString(eSpecialPidTypes type) {
switch (type) {
case SPECIAL_PID_TYPE_CONFIG: return "config";
@ -244,39 +234,41 @@ void CDynamicPermissionManager::askForPermission(wl_client* client, const std::s
rule->m_pid = pid;
std::string description = "";
std::string appName = "";
if (binaryPath.empty())
description = std::format(std::runtime_format(permissionToHumanString(type)), std::format("unknown application (wayland client ID 0x{:x})", rc<uintptr_t>(client)));
appName = I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, {{"wayland_id", std::format("{:x}", rc<uintptr_t>(client))}});
else if (client) {
std::string binaryName = binaryPath.contains("/") ? binaryPath.substr(binaryPath.find_last_of('/') + 1) : binaryPath;
description = std::format(std::runtime_format(permissionToHumanString(type)), std::format("{}</b> ({})", binaryName, binaryPath));
appName = binaryPath.contains("/") ? binaryPath.substr(binaryPath.find_last_of('/') + 1) : binaryPath;
} else {
std::string lookup = "";
if (pid < 0)
lookup = specialPidToString(sc<eSpecialPidTypes>(pid));
appName = specialPidToString(sc<eSpecialPidTypes>(pid));
else {
const auto LOOKUP = binaryNameForPid(pid);
lookup = LOOKUP.value_or("Unknown");
}
if (type == PERMISSION_TYPE_PLUGIN) {
const auto LOOKUP = binaryNameForPid(pid);
description = std::format(std::runtime_format(permissionToHumanString(type)), lookup, binaryPath);
} else {
const auto LOOKUP = binaryNameForPid(pid);
description = std::format(std::runtime_format(permissionToHumanString(type)), lookup, binaryPath);
appName = LOOKUP.value_or(I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_UNKNOWN_NAME));
}
}
std::string description = "";
switch (rule->m_type) {
case PERMISSION_TYPE_SCREENCOPY: description = I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, {{"app", appName}}); break;
case PERMISSION_TYPE_PLUGIN: description = I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, {{"app", appName}, {"plugin", binaryPath}}); break;
case PERMISSION_TYPE_KEYBOARD: description = I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_REQUEST_KEYBOARD, {{"keyboard", binaryPath}}); break;
case PERMISSION_TYPE_UNKNOWN: description = I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_REQUEST_UNKNOWN, {{"app", appName}}); break;
}
std::vector<std::string> options;
const auto ALLOW = I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_ALLOW);
const auto ALLOW_AND_REMEMBER = I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER);
const auto ALLOW_ONCE = I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_ALLOW_ONCE);
const auto DENY = I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_DENY);
if (!binaryPath.empty() && client) {
description += "<br/><br/><i>Hint: you can set persistent rules for these in the Hyprland config file.</i>";
options = {"Deny", "Allow and remember app", "Allow once"};
description += std::format("<br/><br/><i>{}</i>", I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_PERSISTENCE_HINT));
options = {DENY, ALLOW_AND_REMEMBER, ALLOW_ONCE};
} else
options = {"Deny", "Allow"};
options = {DENY, ALLOW};
rule->m_dialogBox = CAsyncDialogBox::create("Permission request", description, options);
rule->m_dialogBox = CAsyncDialogBox::create(I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_TITLE), description, options);
rule->m_dialogBox->m_priority = true;
if (!rule->m_dialogBox) {
@ -286,7 +278,7 @@ void CDynamicPermissionManager::askForPermission(wl_client* client, const std::s
}
rule->m_promise = rule->m_dialogBox->open();
rule->m_promise->then([r = WP<CDynamicPermissionRule>(rule), binaryPath](SP<CPromiseResult<std::string>> pr) {
rule->m_promise->then([r = WP<CDynamicPermissionRule>(rule), binaryPath, ALLOW, ALLOW_AND_REMEMBER, ALLOW_ONCE, DENY](SP<CPromiseResult<std::string>> pr) {
if (!r)
return;
@ -303,15 +295,15 @@ void CDynamicPermissionManager::askForPermission(wl_client* client, const std::s
Debug::log(TRACE, "CDynamicPermissionRule: user returned {}", result);
if (result.starts_with("Allow once"))
if (result.starts_with(ALLOW_ONCE))
r->m_allowMode = PERMISSION_RULE_ALLOW_MODE_ALLOW;
else if (result.starts_with("Deny")) {
else if (result.starts_with(DENY)) {
r->m_allowMode = PERMISSION_RULE_ALLOW_MODE_DENY;
r->m_binaryPath = binaryPath;
} else if (result.starts_with("Allow and remember")) {
} else if (result.starts_with(ALLOW_AND_REMEMBER)) {
r->m_allowMode = PERMISSION_RULE_ALLOW_MODE_ALLOW;
r->m_binaryPath = binaryPath;
} else if (result.starts_with("Allow"))
} else if (result.starts_with(ALLOW))
r->m_allowMode = PERMISSION_RULE_ALLOW_MODE_ALLOW;
if (r->m_promiseResolverForExternal)