fix(i18n): keep service labels and recorded keys unambiguous

This commit is contained in:
devmobasa committed 2026-10-17 21:41:48 +02:00
1 parent c907eef09b
commit b972b32462
6 files changed
+119 -14

No files matched your search

+2 -2
View File
@@ -639,11 +639,11 @@ systemctl --user enable --now wayscriber.service
wayscriber --daemon-toggle
```
Prefer a GUI? Open `wayscriber-configurator`, go to the **Daemon** tab, and click **Install/Update Service**, then **Enable + Start**.
Prefer a GUI? Open `wayscriber-configurator`, go to the **Background Mode** tab, and click **Install Service** (**Update Service** when already installed), then **Start Service**.
**Bind a toggle key:**
Bind `wayscriber --daemon-toggle` to a global shortcut in your compositor or desktop environment. The configurator's **Daemon** tab can also do this: set a shortcut and click **Apply Shortcut** (GNOME: writes a GNOME custom shortcut; KDE/Plasma: writes the systemd drop-in env `WAYSCRIBER_PORTAL_SHORTCUT` for portal global shortcuts).
Bind `wayscriber --daemon-toggle` to a global shortcut in your compositor or desktop environment. The configurator's **Background Mode** tab can also do this: set a shortcut and click **Apply Shortcut** (GNOME: writes a GNOME custom shortcut; KDE/Plasma: writes the systemd drop-in env `WAYSCRIBER_PORTAL_SHORTCUT` for portal global shortcuts).
Hyprland Lua config (`~/.config/hypr/bindings.lua`):
```lua
+74 -1
View File
@@ -24,7 +24,7 @@ use adw::prelude::*;
use wayscriber::domain::Language;
use crate::messages::Message;
use crate::messages::{CommandMessage, Message};
use crate::models::{KeybindingField, LanguageOption, StartupRequest, TabId, UiTabId};
use super::super::state::ConfiguratorApp;
@@ -119,9 +119,82 @@ fn run_scenario() {
navigation_works_after_a_language_change(&app);
repeated_switches_leave_nothing_behind(&app, pages, bindings);
a_load_that_changes_the_language_keeps_its_resets(&app);
service_buttons_describe_service_actions_in_each_language(&app);
super::rebuild::assert_widget_paths_find_only_the_same_shape();
}
fn service_buttons_describe_service_actions_in_each_language(app: &Controller<ConfiguratorApp>) {
app.emit(Message::TabSelected(TabId::Daemon));
pump_until("the background service status", || {
app.model().daemon.status.is_some()
});
settle();
for (language, start, restart, stop) in [
(
LanguageOption::SimplifiedChinese,
"启动服务",
"重启",
"停止并禁用",
),
(
LanguageOption::English,
"Start Service",
"Restart",
"Stop & Disable",
),
] {
app.emit(Message::UiLanguageChanged(language));
settle();
for running in [false, true] {
let mut status = app.model().daemon.status.clone().expect("service status");
status.service_installed = true;
status.service_active = running;
status.service_enabled = running;
let request_id = app.model().daemon.latest_status_request_id;
let effects = app
.state()
.get_mut()
.model
.update_command(CommandMessage::DaemonStatusLoaded(request_id, Ok(status)));
assert!(effects.is_empty());
settle();
// The page hides the Restart/Stop row, not its buttons, so a button
// counts as shown only when every container up to the page is.
let page = app.widgets().stack.visible_child().expect("visible page");
let buttons: Vec<_> = visible_page_descendants(app)
.into_iter()
.filter(|widget| {
std::iter::successors(Some(widget.clone()), gtk::Widget::parent)
.take_while(|ancestor| *ancestor != page)
.all(|ancestor| ancestor.is_visible())
})
.filter_map(|widget| widget.downcast::<gtk::Button>().ok())
.filter_map(|button| button.label().map(|label| label.to_string()))
.collect();
let (shown, hidden) = if running {
(vec![restart, stop], vec![start])
} else {
(vec![start], vec![restart, stop])
};
for label in shown {
assert!(
buttons.iter().any(|button| button == label),
"missing {label:?}: {buttons:?}"
);
}
for label in hidden {
assert!(
!buttons.iter().any(|button| button == label),
"unexpected {label:?}: {buttons:?}"
);
}
}
}
}
fn open_edits_survive_a_language_change(app: &Controller<ConfiguratorApp>) {
app.emit(Message::TabSelected(TabId::Keybindings));
app.emit(Message::ShortcutRecordingStarted(KeybindingField::Undo));
+5 -1
View File
@@ -279,7 +279,11 @@ pub(super) fn start_group(
running_row.append(&stop);
body.append(&running_row);
let start = action_button(tr("Start"), DaemonAction::EnableAndStartService, sender);
let start = action_button(
tr("Start Service"),
DaemonAction::EnableAndStartService,
sender,
);
start.add_css_class("suggested-action");
body.append(&start);
group.add(&body);
@@ -430,10 +430,10 @@ fn named_key(keyval: u32) -> Option<&'static str> {
keyval::ESCAPE => "Escape",
keyval::RETURN | keyval::KP_ENTER => "Return",
keyval::BACKSPACE => "Backspace",
keyval::MENU => tr("Menu"),
keyval::DELETE | keyval::KP_DELETE => tr("Delete"),
keyval::MENU => "Menu",
keyval::DELETE | keyval::KP_DELETE => "Delete",
keyval::HOME | keyval::KP_HOME => "Home",
keyval::END | keyval::KP_END => tr("End"),
keyval::END | keyval::KP_END => "End",
keyval::PAGE_UP | keyval::KP_PAGE_UP => "PageUp",
keyval::PAGE_DOWN | keyval::KP_PAGE_DOWN => "PageDown",
keyval::LEFT | keyval::KP_LEFT => "ArrowLeft",
@@ -739,6 +739,34 @@ mod tests {
);
}
#[test]
fn named_keys_keep_keyboard_names_in_every_language() {
crate::test_env::in_language_child(
concat!(
module_path!(),
"::named_keys_keep_keyboard_names_in_every_language"
),
|| {
use wayscriber::domain::Language;
for language in [Language::English, Language::SimplifiedChinese] {
wayscriber::i18n::set_language(language);
for (keyval, name) in [
(keyval::END, "End"),
(keyval::KP_END, "End"),
(keyval::MENU, "Menu"),
(keyval::DELETE, "Delete"),
(keyval::KP_DELETE, "Delete"),
] {
let binding = chord(keyval, mods(true, false, false, false));
assert_eq!(binding.to_string(), format!("Ctrl+{name}"), "{language:?}");
assert!(binding.matches(name, true, false, false, false));
}
}
},
);
}
#[test]
fn latin1_and_legacy_letter_keysyms_record_as_characters() {
let eacute = chord(0x00e9, KeyboardModifiers::default());
+6 -6
View File
@@ -17,14 +17,14 @@ The service keeps the daemon running in the background; you only need a keybind
If you installed `wayscriber-configurator`, you can set this up entirely in GUI:
1. Open `wayscriber-configurator`.
2. Go to the `Daemon` tab.
3. Click `Install/Update Service`.
4. Click `Enable + Start`.
5. Set your shortcut and click `Apply Shortcut`.
2. Go to the **Background Mode** tab.
3. Click **Install Service** (**Update Service** when already installed).
4. Click **Start Service**.
5. Set your shortcut and click **Apply Shortcut**.
Desktop-specific shortcut handling:
- GNOME: creates/updates a GNOME custom shortcut that runs `wayscriber --daemon-toggle`.
- GNOME migrations: `Install/Update Service` and `Apply Shortcut` remove stale `~/.config/systemd/user/wayscriber.service.d/shortcut.conf` files so old portal settings do not override GNOME behavior.
- GNOME migrations: **Install Service** / **Update Service** and **Apply Shortcut** remove stale `~/.config/systemd/user/wayscriber.service.d/shortcut.conf` files so old portal settings do not override GNOME behavior.
- KDE/Plasma: writes a systemd user drop-in with `WAYSCRIBER_ENABLE_PORTAL_SHORTCUTS=1` and `WAYSCRIBER_PORTAL_SHORTCUT` for portal global shortcut handling.
- Hyprland: light passthrough controls use native Hyprland bindings, not the portal shortcut path.
@@ -180,7 +180,7 @@ GNOME setup works for the normal overlay toggle:
systemctl --user enable --now wayscriber.service
```
Then use the configurator's Daemon tab, or create a GNOME custom shortcut that runs:
Then use the configurator's **Background Mode** tab, or create a GNOME custom shortcut that runs:
```sh
wayscriber --daemon-toggle
+1 -1
View File
@@ -1065,7 +1065,6 @@
"Measure a screen region without capturing it" = "测量屏幕区域而不截图"
"Measure Mode" = "测量模式"
"Medium" = "中"
"Menu" = "菜单"
"Meters" = "计量器"
"Micro chip (top toolbar)" = "微标签片(顶部工具栏)"
"Micro toolbar (click to show the full toolbar)" = "微型工具栏(点击显示完整工具栏)"
@@ -1969,6 +1968,7 @@
"Standard" = "标准"
"Standard, pointy, curved, double" = "标准、尖头、弯曲、双向"
"Start" = "起点"
"Start Service" = "启动服务"
"Start shapes filled" = "形状默认填充"
"Startup profile" = "启动配置文件"
"Status Bar" = "状态栏"