From b32734b9f5b68b23cb9cbfbee6e492ed0a8bdd0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=88=E6=9C=88?= <3600911665@qq.com> Date: Tue, 17 Jun 2025 12:29:21 +0800 Subject: [PATCH 01/20] Quickshell qstr seems not to be working, trying to implement custom translation Add Chinese (zh_CN) translations for Quickshell interface and settings --- .config/quickshell/GlobalStates.qml | 3 +- .../quickshell/modules/bar/ActiveWindow.qml | 5 +- .config/quickshell/modules/bar/Bar.qml | 4 +- .config/quickshell/modules/bar/Media.qml | 2 +- .../modules/cheatsheet/Cheatsheet.qml | 8 +- .../modules/common/ConfigOptions.qml | 1 + .../common/widgets/NotificationItem.qml | 4 +- .../modules/common/widgets/PrimaryTabBar.qml | 3 +- .../common/widgets/SelectionDialog.qml | 5 +- .../modules/mediaControls/MediaControls.qml | 6 +- .../OnScreenDisplayBrightness.qml | 6 +- .../onScreenDisplay/OnScreenDisplayVolume.qml | 6 +- .../onScreenKeyboard/OnScreenKeyboard.qml | 6 +- .../quickshell/modules/overview/Overview.qml | 17 +- .../modules/overview/SearchItem.qml | 2 +- .../modules/overview/SearchWidget.qml | 22 +- .../quickshell/modules/session/Session.qml | 24 +- .../quickshell/modules/sidebarLeft/AiChat.qml | 20 +- .../quickshell/modules/sidebarLeft/Anime.qml | 25 +- .../modules/sidebarLeft/SidebarLeft.qml | 9 +- .../sidebarLeft/SidebarLeftContent.qml | 7 +- .../modules/sidebarLeft/Translator.qml | 6 +- .../modules/sidebarLeft/aiChat/AiMessage.qml | 12 +- .../sidebarLeft/aiChat/MessageCodeBlock.qml | 4 +- .../sidebarLeft/aiChat/MessageTextBlock.qml | 2 +- .../sidebarLeft/aiChat/MessageThinkBlock.qml | 2 +- .../modules/sidebarLeft/anime/BooruImage.qml | 8 +- .../sidebarLeft/anime/BooruResponse.qml | 3 +- .../sidebarLeft/translator/TextCanvas.qml | 2 +- .../sidebarRight/CenterWidgetGroup.qml | 3 +- .../modules/sidebarRight/SidebarRight.qml | 15 +- .../sidebarRight/calendar/CalendarWidget.qml | 2 +- .../notifications/NotificationList.qml | 7 +- .../quickToggles/BluetoothToggle.qml | 4 +- .../sidebarRight/quickToggles/GameMode.qml | 3 +- .../quickToggles/IdleInhibitor.qml | 3 +- .../quickToggles/NetworkToggle.qml | 3 +- .../sidebarRight/quickToggles/NightLight.qml | 3 +- .../modules/sidebarRight/todo/TodoWidget.qml | 14 +- .../volumeMixer/AudioDeviceSelectorButton.qml | 5 +- .../sidebarRight/volumeMixer/VolumeMixer.qml | 7 +- .config/quickshell/services/Ai.qml | 55 +- .config/quickshell/services/Booru.qml | 25 +- .config/quickshell/services/Brightness.qml | 5 +- .config/quickshell/services/ConfigLoader.qml | 8 +- .../quickshell/services/KeyringStorage.qml | 4 +- .../quickshell/services/MprisController.qml | 7 +- .config/quickshell/services/Translation.qml | 170 +++++++ .config/quickshell/translations/en_US.json | 472 ++++++++++++++++++ .config/quickshell/translations/zh_CN.json | 472 ++++++++++++++++++ 50 files changed, 1324 insertions(+), 187 deletions(-) create mode 100644 .config/quickshell/services/Translation.qml create mode 100644 .config/quickshell/translations/en_US.json create mode 100644 .config/quickshell/translations/zh_CN.json diff --git a/.config/quickshell/GlobalStates.qml b/.config/quickshell/GlobalStates.qml index 1b879421b..4b6dd0dac 100644 --- a/.config/quickshell/GlobalStates.qml +++ b/.config/quickshell/GlobalStates.qml @@ -1,4 +1,5 @@ import "root:/modules/common/" +import "root:/services/" import QtQuick import Quickshell import Quickshell.Hyprland @@ -31,7 +32,7 @@ Singleton { GlobalShortcut { name: "workspaceNumber" - description: qsTr("Hold to show workspace numbers, release to show icons") + description: Translation.tr("Hold to show workspace numbers, release to show icons") onPressed: { workspaceShowNumbersTimer.start() diff --git a/.config/quickshell/modules/bar/ActiveWindow.qml b/.config/quickshell/modules/bar/ActiveWindow.qml index 95e25c6f2..0c4838fc6 100644 --- a/.config/quickshell/modules/bar/ActiveWindow.qml +++ b/.config/quickshell/modules/bar/ActiveWindow.qml @@ -1,5 +1,6 @@ import "root:/modules/common" import "root:/modules/common/widgets" +import "root:/services/" import QtQuick import QtQuick.Layouts import Quickshell.Wayland @@ -26,7 +27,7 @@ Item { font.pixelSize: Appearance.font.pixelSize.smaller color: Appearance.colors.colSubtext elide: Text.ElideRight - text: root.activeWindow?.activated ? root.activeWindow?.appId : qsTr("Desktop") + text: root.activeWindow?.activated ? root.activeWindow?.appId : Translation.tr("Desktop") } StyledText { @@ -34,7 +35,7 @@ Item { font.pixelSize: Appearance.font.pixelSize.small color: Appearance.colors.colOnLayer0 elide: Text.ElideRight - text: root.activeWindow?.activated ? root.activeWindow?.title : `${qsTr("Workspace")} ${monitor.activeWorkspace?.id}` + text: root.activeWindow?.activated ? root.activeWindow?.title : `${Translation.tr("Workspace")} ${monitor.activeWorkspace?.id}` } } diff --git a/.config/quickshell/modules/bar/Bar.qml b/.config/quickshell/modules/bar/Bar.qml index 9fb9b32ed..01945cab3 100644 --- a/.config/quickshell/modules/bar/Bar.qml +++ b/.config/quickshell/modules/bar/Bar.qml @@ -131,7 +131,7 @@ Scope { ScrollHint { reveal: barLeftSideMouseArea.hovered icon: "light_mode" - tooltipText: qsTr("Scroll to change brightness") + tooltipText: Translation.tr("Scroll to change brightness") side: "left" anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter @@ -329,7 +329,7 @@ Scope { ScrollHint { reveal: barRightSideMouseArea.hovered icon: "volume_up" - tooltipText: qsTr("Scroll to change volume") + tooltipText: Translation.tr("Scroll to change volume") side: "right" anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter diff --git a/.config/quickshell/modules/bar/Media.qml b/.config/quickshell/modules/bar/Media.qml index 3bd8a78a4..90d1c8801 100644 --- a/.config/quickshell/modules/bar/Media.qml +++ b/.config/quickshell/modules/bar/Media.qml @@ -13,7 +13,7 @@ Item { id: root property bool borderless: ConfigOptions.bar.borderless readonly property MprisPlayer activePlayer: MprisController.activePlayer - readonly property string cleanedTitle: StringUtils.cleanMusicTitle(activePlayer?.trackTitle) || qsTr("No media") + readonly property string cleanedTitle: StringUtils.cleanMusicTitle(activePlayer?.trackTitle) || Translation.tr("No media") Layout.fillHeight: true implicitWidth: rowLayout.implicitWidth + rowLayout.spacing * 2 diff --git a/.config/quickshell/modules/cheatsheet/Cheatsheet.qml b/.config/quickshell/modules/cheatsheet/Cheatsheet.qml index 79711f88f..83d9fd654 100644 --- a/.config/quickshell/modules/cheatsheet/Cheatsheet.qml +++ b/.config/quickshell/modules/cheatsheet/Cheatsheet.qml @@ -110,7 +110,7 @@ Scope { // Scope Layout.alignment: Qt.AlignHCenter font.family: Appearance.font.family.title font.pixelSize: Appearance.font.pixelSize.title - text: qsTr("Cheat sheet") + text: Translation.tr("Cheat sheet") } CheatsheetKeybinds {} } @@ -137,7 +137,7 @@ Scope { // Scope GlobalShortcut { name: "cheatsheetToggle" - description: qsTr("Toggles cheatsheet on press") + description: Translation.tr("Toggles cheatsheet on press") onPressed: { cheatsheetLoader.active = !cheatsheetLoader.active; @@ -146,7 +146,7 @@ Scope { // Scope GlobalShortcut { name: "cheatsheetOpen" - description: qsTr("Opens cheatsheet on press") + description: Translation.tr("Opens cheatsheet on press") onPressed: { cheatsheetLoader.active = true; @@ -155,7 +155,7 @@ Scope { // Scope GlobalShortcut { name: "cheatsheetClose" - description: qsTr("Closes cheatsheet on press") + description: Translation.tr("Closes cheatsheet on press") onPressed: { cheatsheetLoader.active = false; diff --git a/.config/quickshell/modules/common/ConfigOptions.qml b/.config/quickshell/modules/common/ConfigOptions.qml index 70e46f4ec..92d56b72c 100644 --- a/.config/quickshell/modules/common/ConfigOptions.qml +++ b/.config/quickshell/modules/common/ConfigOptions.qml @@ -91,6 +91,7 @@ Singleton { } property QtObject language: QtObject { + property string ui: "auto" // Interface language: "auto", "en", "zh-CN", "zh-TW", etc. property QtObject translator: QtObject { property string engine: "auto" // Run `trans -list-engines` for available engines. auto should use google property string targetLanguage: "auto" // Run `trans -list-all` for available languages diff --git a/.config/quickshell/modules/common/widgets/NotificationItem.qml b/.config/quickshell/modules/common/widgets/NotificationItem.qml index bf8a76871..62e37b8a4 100644 --- a/.config/quickshell/modules/common/widgets/NotificationItem.qml +++ b/.config/quickshell/modules/common/widgets/NotificationItem.qml @@ -97,7 +97,7 @@ Item { // Notification item area onPressAndHold: (mouse) => { if (mouse.button === Qt.LeftButton) { Hyprland.dispatch(`exec wl-copy '${StringUtils.shellSingleQuoteEscape(notificationObject.body)}'`) - notificationSummaryText.text = String.format(qsTr("{0} (copied)"), notificationObject.summary) + notificationSummaryText.text = String.format(Translation.tr("{0} (copied)"), notificationObject.summary) } } onDraggingChanged: () => { @@ -257,7 +257,7 @@ Item { // Notification item area NotificationActionButton { Layout.fillWidth: true - buttonText: qsTr("Close") + buttonText: Translation.tr("Close") urgency: notificationObject.urgency implicitWidth: (notificationObject.actions.length == 0) ? ((actionsFlickable.width - actionRowLayout.spacing) / 2) : (contentItem.implicitWidth + leftPadding + rightPadding) diff --git a/.config/quickshell/modules/common/widgets/PrimaryTabBar.qml b/.config/quickshell/modules/common/widgets/PrimaryTabBar.qml index cd048e8b6..c8843bc2a 100644 --- a/.config/quickshell/modules/common/widgets/PrimaryTabBar.qml +++ b/.config/quickshell/modules/common/widgets/PrimaryTabBar.qml @@ -1,4 +1,5 @@ import "root:/modules/common" +import "root:/services/" import QtQuick import QtQuick.Controls import QtQuick.Layouts @@ -7,7 +8,7 @@ import Quickshell ColumnLayout { id: root spacing: 0 - required property var tabButtonList // Something like [{"icon": "notifications", "name": qsTr("Notifications")}, {"icon": "volume_up", "name": qsTr("Volume mixer")}] + required property var tabButtonList // Something like [{"icon": "notifications", "name": Translation.tr("Notifications")}, {"icon": "volume_up", "name": Translation.tr("Volume mixer")}] required property var externalTrackedTab property bool enableIndicatorAnimation: false property color colIndicator: Appearance?.colors.colPrimary ?? "#65558F" diff --git a/.config/quickshell/modules/common/widgets/SelectionDialog.qml b/.config/quickshell/modules/common/widgets/SelectionDialog.qml index 9cf0940ed..9a6d0b7fa 100644 --- a/.config/quickshell/modules/common/widgets/SelectionDialog.qml +++ b/.config/quickshell/modules/common/widgets/SelectionDialog.qml @@ -1,6 +1,7 @@ import "root:/modules/common" import "root:/modules/common/widgets" import "root:/services" +import "root:/services/" import Qt5Compat.GraphicalEffects import QtQuick import QtQuick.Controls @@ -113,11 +114,11 @@ Item { Layout.alignment: Qt.AlignRight DialogButton { - buttonText: qsTr("Cancel") + buttonText: Translation.tr("Cancel") onClicked: root.canceled() } DialogButton { - buttonText: qsTr("OK") + buttonText: Translation.tr("OK") onClicked: root.selected( root.selectedId === -1 ? null : root.items[root.selectedId] diff --git a/.config/quickshell/modules/mediaControls/MediaControls.qml b/.config/quickshell/modules/mediaControls/MediaControls.qml index 4350658f6..047f14174 100644 --- a/.config/quickshell/modules/mediaControls/MediaControls.qml +++ b/.config/quickshell/modules/mediaControls/MediaControls.qml @@ -158,7 +158,7 @@ Scope { GlobalShortcut { name: "mediaControlsToggle" - description: qsTr("Toggles media controls on press") + description: Translation.tr("Toggles media controls on press") onPressed: { if (!mediaControlsLoader.active && Mpris.players.values.filter(player => isRealPlayer(player)).length === 0) { @@ -170,7 +170,7 @@ Scope { } GlobalShortcut { name: "mediaControlsOpen" - description: qsTr("Opens media controls on press") + description: Translation.tr("Opens media controls on press") onPressed: { mediaControlsLoader.active = true; @@ -179,7 +179,7 @@ Scope { } GlobalShortcut { name: "mediaControlsClose" - description: qsTr("Closes media controls on press") + description: Translation.tr("Closes media controls on press") onPressed: { mediaControlsLoader.active = false; diff --git a/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayBrightness.qml b/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayBrightness.qml index 765386bc8..a6ef42426 100644 --- a/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayBrightness.qml +++ b/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayBrightness.qml @@ -108,7 +108,7 @@ Scope { icon: "light_mode" rotateIcon: true scaleIcon: true - name: qsTr("Brightness") + name: Translation.tr("Brightness") } } } @@ -134,7 +134,7 @@ Scope { GlobalShortcut { name: "osdBrightnessTrigger" - description: qsTr("Triggers brightness OSD on press") + description: Translation.tr("Triggers brightness OSD on press") onPressed: { root.triggerOsd() @@ -142,7 +142,7 @@ Scope { } GlobalShortcut { name: "osdBrightnessHide" - description: qsTr("Hides brightness OSD on press") + description: Translation.tr("Hides brightness OSD on press") onPressed: { root.showOsdValues = false diff --git a/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayVolume.qml b/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayVolume.qml index 5d23b4405..def28b366 100644 --- a/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayVolume.qml +++ b/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayVolume.qml @@ -121,7 +121,7 @@ Scope { Layout.fillWidth: true value: Audio.sink?.audio.volume ?? 0 icon: Audio.sink?.audio.muted ? "volume_off" : "volume_up" - name: qsTr("Volume") + name: Translation.tr("Volume") } Item { @@ -185,7 +185,7 @@ Scope { } GlobalShortcut { name: "osdVolumeTrigger" - description: qsTr("Triggers volume OSD on press") + description: Translation.tr("Triggers volume OSD on press") onPressed: { root.triggerOsd() @@ -193,7 +193,7 @@ Scope { } GlobalShortcut { name: "osdVolumeHide" - description: qsTr("Hides volume OSD on press") + description: Translation.tr("Hides volume OSD on press") onPressed: { root.showOsdValues = false diff --git a/.config/quickshell/modules/onScreenKeyboard/OnScreenKeyboard.qml b/.config/quickshell/modules/onScreenKeyboard/OnScreenKeyboard.qml index e78f45b2e..079b7a3c0 100644 --- a/.config/quickshell/modules/onScreenKeyboard/OnScreenKeyboard.qml +++ b/.config/quickshell/modules/onScreenKeyboard/OnScreenKeyboard.qml @@ -141,7 +141,7 @@ Scope { // Scope GlobalShortcut { name: "oskToggle" - description: qsTr("Toggles on screen keyboard on press") + description: Translation.tr("Toggles on screen keyboard on press") onPressed: { oskLoader.active = !oskLoader.active; @@ -150,7 +150,7 @@ Scope { // Scope GlobalShortcut { name: "oskOpen" - description: qsTr("Opens on screen keyboard on press") + description: Translation.tr("Opens on screen keyboard on press") onPressed: { oskLoader.active = true; @@ -159,7 +159,7 @@ Scope { // Scope GlobalShortcut { name: "oskClose" - description: qsTr("Closes on screen keyboard on press") + description: Translation.tr("Closes on screen keyboard on press") onPressed: { oskLoader.active = false; diff --git a/.config/quickshell/modules/overview/Overview.qml b/.config/quickshell/modules/overview/Overview.qml index a7817e6e9..e24f2f38a 100644 --- a/.config/quickshell/modules/overview/Overview.qml +++ b/.config/quickshell/modules/overview/Overview.qml @@ -2,6 +2,7 @@ import "root:/" import "root:/services" import "root:/modules/common" import "root:/modules/common/widgets" +import "root:/services/" import QtQuick import QtQuick.Controls import QtQuick.Layouts @@ -151,7 +152,7 @@ Scope { GlobalShortcut { name: "overviewToggle" - description: qsTr("Toggles overview on press") + description: Translation.tr("Toggles overview on press") onPressed: { GlobalStates.overviewOpen = !GlobalStates.overviewOpen @@ -159,7 +160,7 @@ Scope { } GlobalShortcut { name: "overviewClose" - description: qsTr("Closes overview") + description: Translation.tr("Closes overview") onPressed: { GlobalStates.overviewOpen = false @@ -167,7 +168,7 @@ Scope { } GlobalShortcut { name: "overviewToggleRelease" - description: qsTr("Toggles overview on release") + description: Translation.tr("Toggles overview on release") onPressed: { GlobalStates.superReleaseMightTrigger = true @@ -183,9 +184,9 @@ Scope { } GlobalShortcut { name: "overviewToggleReleaseInterrupt" - description: qsTr("Interrupts possibility of overview being toggled on release. ") + - qsTr("This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. ") + - qsTr("To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything.") + description: Translation.tr("Interrupts possibility of overview being toggled on release. ") + + Translation.tr("This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. ") + + Translation.tr("To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything.") onPressed: { GlobalStates.superReleaseMightTrigger = false @@ -193,7 +194,7 @@ Scope { } GlobalShortcut { name: "overviewClipboardToggle" - description: qsTr("Toggle clipboard query on overview widget") + description: Translation.tr("Toggle clipboard query on overview widget") onPressed: { if (GlobalStates.overviewOpen && overviewScope.dontAutoCancelSearch) { @@ -216,7 +217,7 @@ Scope { GlobalShortcut { name: "overviewEmojiToggle" - description: qsTr("Toggle emoji query on overview widget") + description: Translation.tr("Toggle emoji query on overview widget") onPressed: { if (GlobalStates.overviewOpen && overviewScope.dontAutoCancelSearch) { diff --git a/.config/quickshell/modules/overview/SearchItem.qml b/.config/quickshell/modules/overview/SearchItem.qml index d23cb4c09..f70e25f3e 100644 --- a/.config/quickshell/modules/overview/SearchItem.qml +++ b/.config/quickshell/modules/overview/SearchItem.qml @@ -162,7 +162,7 @@ RippleButton { StyledText { font.pixelSize: Appearance.font.pixelSize.smaller color: Appearance.colors.colSubtext - visible: root.itemType && root.itemType != qsTr("App") + visible: root.itemType && root.itemType != Translation.tr("App") text: root.itemType } RowLayout { diff --git a/.config/quickshell/modules/overview/SearchWidget.qml b/.config/quickshell/modules/overview/SearchWidget.qml index 614efa91e..5d43f07ad 100644 --- a/.config/quickshell/modules/overview/SearchWidget.qml +++ b/.config/quickshell/modules/overview/SearchWidget.qml @@ -220,7 +220,7 @@ Item { // Wrapper color: activeFocus ? Appearance.m3colors.m3onSurface : Appearance.m3colors.m3onSurfaceVariant selectedTextColor: Appearance.m3colors.m3onSecondaryContainer selectionColor: Appearance.colors.colSecondaryContainer - placeholderText: qsTr("Search, calculate or run") + placeholderText: Translation.tr("Search, calculate or run") placeholderTextColor: Appearance.m3colors.m3outline implicitWidth: root.searchingText == "" ? Appearance.sizes.searchWidthCollapsed : Appearance.sizes.searchWidth @@ -329,8 +329,8 @@ Item { // Wrapper nonAppResultsTimer.restart(); const mathResultObject = { name: root.mathResult, - clickActionName: qsTr("Copy"), - type: qsTr("Math result"), + clickActionName: Translation.tr("Copy"), + type: Translation.tr("Math result"), fontType: "monospace", materialSymbol: 'calculate', execute: () => { @@ -339,8 +339,8 @@ Item { // Wrapper } const commandResultObject = { name: searchingText.replace("file://", ""), - clickActionName: qsTr("Run"), - type: qsTr("Run command"), + clickActionName: Translation.tr("Run"), + type: Translation.tr("Run command"), fontType: "monospace", materialSymbol: 'terminal', execute: () => { @@ -353,8 +353,8 @@ Item { // Wrapper if (actionString.startsWith(root.searchingText) || root.searchingText.startsWith(actionString)) { return { name: root.searchingText.startsWith(actionString) ? root.searchingText : actionString, - clickActionName: qsTr("Run"), - type: qsTr("Action"), + clickActionName: Translation.tr("Run"), + type: Translation.tr("Action"), materialSymbol: 'settings_suggest', execute: () => { action.execute(root.searchingText.split(" ").slice(1).join(" ")) @@ -371,8 +371,8 @@ Item { // Wrapper result = result.concat( AppSearch.fuzzyQuery(root.searchingText) .map((entry) => { - entry.clickActionName = qsTr("Launch"); - entry.type = qsTr("App"); + entry.clickActionName = Translation.tr("Launch"); + entry.type = Translation.tr("App"); return entry; }) ); @@ -393,8 +393,8 @@ Item { // Wrapper ///////////////// Web search //////////////// result.push({ name: root.searchingText, - clickActionName: qsTr("Search"), - type: qsTr("Search the web"), + clickActionName: Translation.tr("Search"), + type: Translation.tr("Search the web"), materialSymbol: 'travel_explore', execute: () => { let url = ConfigOptions.search.engineBaseUrl + root.searchingText diff --git a/.config/quickshell/modules/session/Session.qml b/.config/quickshell/modules/session/Session.qml index 94e6123ff..0ab3221ed 100644 --- a/.config/quickshell/modules/session/Session.qml +++ b/.config/quickshell/modules/session/Session.qml @@ -70,7 +70,7 @@ Scope { font.family: Appearance.font.family.title font.pixelSize: Appearance.font.pixelSize.title font.weight: Font.DemiBold - text: qsTr("Session") + text: Translation.tr("Session") } StyledText { // Small instruction @@ -78,7 +78,7 @@ Scope { horizontalAlignment: Text.AlignHCenter font.family: Appearance.font.family.title font.pixelSize: Appearance.font.pixelSize.normal - text: qsTr("Arrow keys to navigate, Enter to select\nEsc or click anywhere to cancel") + text: Translation.tr("Arrow keys to navigate, Enter to select\nEsc or click anywhere to cancel") } } @@ -91,7 +91,7 @@ Scope { id: sessionLock focus: sessionRoot.visible buttonIcon: "lock" - buttonText: qsTr("Lock") + buttonText: Translation.tr("Lock") onClicked: { Hyprland.dispatch("exec loginctl lock-session"); sessionRoot.hide() } onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } KeyNavigation.right: sessionSleep @@ -100,7 +100,7 @@ Scope { SessionActionButton { id: sessionSleep buttonIcon: "dark_mode" - buttonText: qsTr("Sleep") + buttonText: Translation.tr("Sleep") onClicked: { Hyprland.dispatch("exec systemctl suspend || loginctl suspend"); sessionRoot.hide() } onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } KeyNavigation.left: sessionLock @@ -110,7 +110,7 @@ Scope { SessionActionButton { id: sessionLogout buttonIcon: "logout" - buttonText: qsTr("Logout") + buttonText: Translation.tr("Logout") onClicked: { Hyprland.dispatch("exec pkill Hyprland"); sessionRoot.hide() } onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } KeyNavigation.left: sessionSleep @@ -120,7 +120,7 @@ Scope { SessionActionButton { id: sessionTaskManager buttonIcon: "browse_activity" - buttonText: qsTr("Task Manager") + buttonText: Translation.tr("Task Manager") onClicked: { Hyprland.dispatch(`exec ${ConfigOptions.apps.taskManager}`); sessionRoot.hide() } onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } KeyNavigation.left: sessionLogout @@ -130,7 +130,7 @@ Scope { SessionActionButton { id: sessionHibernate buttonIcon: "downloading" - buttonText: qsTr("Hibernate") + buttonText: Translation.tr("Hibernate") onClicked: { Hyprland.dispatch("exec systemctl hibernate || loginctl hibernate"); sessionRoot.hide() } onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } KeyNavigation.up: sessionLock @@ -139,7 +139,7 @@ Scope { SessionActionButton { id: sessionShutdown buttonIcon: "power_settings_new" - buttonText: qsTr("Shutdown") + buttonText: Translation.tr("Shutdown") onClicked: { Hyprland.dispatch("exec systemctl poweroff || loginctl poweroff"); sessionRoot.hide() } onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } KeyNavigation.left: sessionHibernate @@ -149,7 +149,7 @@ Scope { SessionActionButton { id: sessionReboot buttonIcon: "restart_alt" - buttonText: qsTr("Reboot") + buttonText: Translation.tr("Reboot") onClicked: { Hyprland.dispatch("exec reboot || loginctl reboot"); sessionRoot.hide() } onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } KeyNavigation.left: sessionShutdown @@ -159,7 +159,7 @@ Scope { SessionActionButton { id: sessionFirmwareReboot buttonIcon: "settings_applications" - buttonText: qsTr("Reboot to firmware settings") + buttonText: Translation.tr("Reboot to firmware settings") onClicked: { Hyprland.dispatch("exec systemctl reboot --firmware-setup || loginctl reboot --firmware-setup"); sessionRoot.hide() } onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } KeyNavigation.up: sessionTaskManager @@ -209,7 +209,7 @@ Scope { GlobalShortcut { name: "sessionToggle" - description: qsTr("Toggles session screen on press") + description: Translation.tr("Toggles session screen on press") onPressed: { sessionLoader.active = !sessionLoader.active; @@ -218,7 +218,7 @@ Scope { GlobalShortcut { name: "sessionOpen" - description: qsTr("Opens session screen on press") + description: Translation.tr("Opens session screen on press") onPressed: { sessionLoader.active = true; diff --git a/.config/quickshell/modules/sidebarLeft/AiChat.qml b/.config/quickshell/modules/sidebarLeft/AiChat.qml index 422dc41b8..d510a6312 100644 --- a/.config/quickshell/modules/sidebarLeft/AiChat.qml +++ b/.config/quickshell/modules/sidebarLeft/AiChat.qml @@ -44,21 +44,21 @@ Item { property var allCommands: [ { name: "model", - description: qsTr("Choose model"), + description: Translation.tr("Choose model"), execute: (args) => { Ai.setModel(args[0]); } }, { name: "clear", - description: qsTr("Clear chat history"), + description: Translation.tr("Clear chat history"), execute: () => { Ai.clearMessages(); } }, { name: "key", - description: qsTr("Set API key"), + description: Translation.tr("Set API key"), execute: (args) => { if (args[0] == "get") { Ai.printApiKey() @@ -69,7 +69,7 @@ Item { }, { name: "temp", - description: qsTr("Set temperature (randomness) of the model. Values range between 0 to 2 for Gemini, 0 to 1 for other models. Default is 0.5."), + description: Translation.tr("Set temperature (randomness) of the model. Values range between 0 to 2 for Gemini, 0 to 1 for other models. Default is 0.5."), execute: (args) => { // console.log(args) if (args.length == 0 || args[0] == "get") { @@ -82,7 +82,7 @@ Item { }, { name: "test", - description: qsTr("Markdown test"), + description: Translation.tr("Markdown test"), execute: () => { Ai.addMessage(` @@ -148,7 +148,7 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) if (commandObj) { commandObj.execute(args); } else { - Ai.addMessage(qsTr("Unknown command: ") + command, Ai.interfaceRole); + Ai.addMessage(Translation.tr("Unknown command: ") + command, Ai.interfaceRole); } } else { @@ -235,7 +235,7 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) font.family: Appearance.font.family.title color: Appearance.m3colors.m3outline horizontalAlignment: Text.AlignHCenter - text: qsTr("Large language models") + text: Translation.tr("Large language models") } StyledText { id: widgetDescriptionText @@ -244,7 +244,7 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) color: Appearance.m3colors.m3outline horizontalAlignment: Text.AlignLeft wrapMode: Text.Wrap - text: qsTr("Type /key to get started with online models\nCtrl+O to expand the sidebar\nCtrl+P to detach sidebar into a window") + text: Translation.tr("Type /key to get started with online models\nCtrl+O to expand the sidebar\nCtrl+P to detach sidebar into a window") } } } @@ -366,7 +366,7 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) Layout.fillWidth: true padding: 10 color: activeFocus ? Appearance.m3colors.m3onSurface : Appearance.m3colors.m3onSurfaceVariant - placeholderText: StringUtils.format(qsTr('Message the model... "{0}" for commands'), root.commandPrefix) + placeholderText: StringUtils.format(Translation.tr('Message the model... "{0}" for commands'), root.commandPrefix) background: null @@ -510,7 +510,7 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) id: toolTip extraVisibleCondition: false alternativeVisibleCondition: mouseArea.containsMouse // Show tooltip when hovered - content: StringUtils.format(qsTr("Current model: {0}\nSet it with {1}model MODEL"), + content: StringUtils.format(Translation.tr("Current model: {0}\nSet it with {1}model MODEL"), Ai.getModel().name, root.commandPrefix) } diff --git a/.config/quickshell/modules/sidebarLeft/Anime.qml b/.config/quickshell/modules/sidebarLeft/Anime.qml index 1300d5482..a0e110980 100644 --- a/.config/quickshell/modules/sidebarLeft/Anime.qml +++ b/.config/quickshell/modules/sidebarLeft/Anime.qml @@ -6,6 +6,7 @@ import "root:/modules/common/functions/fuzzysort.js" as Fuzzy import "root:/modules/common/functions/string_utils.js" as StringUtils import "root:/modules/common/functions/file_utils.js" as FileUtils import "./anime/" +import "root:/services/" import Qt.labs.platform import QtQuick import QtQuick.Controls @@ -39,21 +40,21 @@ Item { property var allCommands: [ { name: "mode", - description: qsTr("Set the current API provider"), + description: Translation.tr("Set the current API provider"), execute: (args) => { Booru.setProvider(args[0]); } }, { name: "clear", - description: qsTr("Clear the current list of images"), + description: Translation.tr("Clear the current list of images"), execute: () => { Booru.clearResponses(); } }, { name: "next", - description: qsTr("Get the next page of results"), + description: Translation.tr("Get the next page of results"), execute: () => { if (root.responses.length > 0) { const lastResponse = root.responses[root.responses.length - 1]; @@ -63,14 +64,14 @@ Item { }, { name: "safe", - description: qsTr("Disable NSFW content"), + description: Translation.tr("Disable NSFW content"), execute: () => { PersistentStateManager.setState("booru.allowNsfw", false); } }, { name: "lewd", - description: qsTr("Allow NSFW content"), + description: Translation.tr("Allow NSFW content"), execute: () => { PersistentStateManager.setState("booru.allowNsfw", true); } @@ -86,7 +87,7 @@ Item { if (commandObj) { commandObj.execute(args); } else { - Booru.addSystemMessage(qsTr("Unknown command: ") + command); + Booru.addSystemMessage(Translation.tr("Unknown command: ") + command); } } else if (inputText.trim() == "+") { @@ -208,7 +209,7 @@ Item { font.family: Appearance.font.family.title color: Appearance.m3colors.m3outline horizontalAlignment: Text.AlignHCenter - text: qsTr("Anime boorus") + text: Translation.tr("Anime boorus") } } } @@ -245,7 +246,7 @@ Item { font.pixelSize: Appearance.font.pixelSize.smaller color: Appearance.m3colors.m3inverseOnSurface wrapMode: Text.Wrap - text: StringUtils.format(qsTr("{0} queries pending"), Booru.runningRequests) + text: StringUtils.format(Translation.tr("{0} queries pending"), Booru.runningRequests) } } } @@ -381,7 +382,7 @@ Item { padding: 10 color: activeFocus ? Appearance.m3colors.m3onSurface : Appearance.m3colors.m3onSurfaceVariant renderType: Text.NativeRendering - placeholderText: StringUtils.format(qsTr('Enter tags, or "{0}" for commands'), root.commandPrefix) + placeholderText: StringUtils.format(Translation.tr('Enter tags, or "{0}" for commands'), root.commandPrefix) background: null @@ -544,8 +545,8 @@ Item { id: toolTip extraVisibleCondition: false alternativeVisibleCondition: mouseArea.containsMouse // Show tooltip when hovered - // content: qsTr("The current API used. Endpoint: ") + Booru.providers[Booru.currentProvider].url + qsTr("\nSet with /mode PROVIDER") - content: StringUtils.format(qsTr("Current API endpoint: {0}\nSet it with {1}mode PROVIDER"), + // content: Translation.tr("The current API used. Endpoint: ") + Booru.providers[Booru.currentProvider].url + Translation.tr("\nSet with /mode PROVIDER") + content: StringUtils.format(Translation.tr("Current API endpoint: {0}\nSet it with {1}mode PROVIDER"), Booru.providers[Booru.currentProvider].url, root.commandPrefix) } @@ -586,7 +587,7 @@ Item { Layout.alignment: Qt.AlignVCenter font.pixelSize: Appearance.font.pixelSize.smaller color: nsfwSwitch.enabled ? Appearance.colors.colOnLayer1 : Appearance.m3colors.m3outline - text: qsTr("Allow NSFW") + text: Translation.tr("Allow NSFW") } StyledSwitch { id: nsfwSwitch diff --git a/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml b/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml index ce0b22802..bed1d85fa 100644 --- a/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml +++ b/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml @@ -2,6 +2,7 @@ import "root:/" import "root:/services" import "root:/modules/common" import "root:/modules/common/widgets" +import "root:/services/" import QtQuick import QtQuick.Controls import QtQuick.Layouts @@ -168,7 +169,7 @@ Scope { // Scope GlobalShortcut { name: "sidebarLeftToggle" - description: qsTr("Toggles left sidebar on press") + description: Translation.tr("Toggles left sidebar on press") onPressed: { GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen; @@ -177,7 +178,7 @@ Scope { // Scope GlobalShortcut { name: "sidebarLeftOpen" - description: qsTr("Opens left sidebar on press") + description: Translation.tr("Opens left sidebar on press") onPressed: { GlobalStates.sidebarLeftOpen = true; @@ -186,7 +187,7 @@ Scope { // Scope GlobalShortcut { name: "sidebarLeftClose" - description: qsTr("Closes left sidebar on press") + description: Translation.tr("Closes left sidebar on press") onPressed: { GlobalStates.sidebarLeftOpen = false; @@ -195,7 +196,7 @@ Scope { // Scope GlobalShortcut { name: "sidebarLeftToggleDetach" - description: qsTr("Detach left sidebar into a window/Attach it back") + description: Translation.tr("Detach left sidebar into a window/Attach it back") onPressed: { root.detach = !root.detach; diff --git a/.config/quickshell/modules/sidebarLeft/SidebarLeftContent.qml b/.config/quickshell/modules/sidebarLeft/SidebarLeftContent.qml index 8d67c2a7f..aec68beaf 100644 --- a/.config/quickshell/modules/sidebarLeft/SidebarLeftContent.qml +++ b/.config/quickshell/modules/sidebarLeft/SidebarLeftContent.qml @@ -2,6 +2,7 @@ import "root:/" import "root:/services" import "root:/modules/common" import "root:/modules/common/widgets" +import "root:/services/" import QtQuick import QtQuick.Controls import QtQuick.Layouts @@ -18,9 +19,9 @@ Item { required property var scopeRoot anchors.fill: parent property var tabButtonList: [ - ...(ConfigOptions.policies.ai !== 0 ? [{"icon": "neurology", "name": qsTr("Intelligence")}] : []), - {"icon": "translate", "name": qsTr("Translator")}, - ...(ConfigOptions.policies.weeb === 1 ? [{"icon": "bookmark_heart", "name": qsTr("Anime")}] : []) + ...(ConfigOptions.policies.ai !== 0 ? [{"icon": "neurology", "name": Translation.tr("Intelligence")}] : []), + {"icon": "translate", "name": Translation.tr("Translator")}, + ...(ConfigOptions.policies.weeb === 1 ? [{"icon": "bookmark_heart", "name": Translation.tr("Anime")}] : []) ] property int selectedTab: 0 diff --git a/.config/quickshell/modules/sidebarLeft/Translator.qml b/.config/quickshell/modules/sidebarLeft/Translator.qml index 37e5a37b1..e6400df16 100644 --- a/.config/quickshell/modules/sidebarLeft/Translator.qml +++ b/.config/quickshell/modules/sidebarLeft/Translator.qml @@ -123,7 +123,7 @@ Item { TextCanvas { // Content translation id: outputCanvas isInput: false - placeholderText: qsTr("Translation goes here...") + placeholderText: Translation.tr("Translation goes here...") property bool hasTranslation: (root.translatedText.trim().length > 0) text: hasTranslation ? root.translatedText : "" GroupButton { @@ -178,7 +178,7 @@ Item { TextCanvas { // Content input id: inputCanvas isInput: true - placeholderText: qsTr("Enter text to translate...") + placeholderText: Translation.tr("Enter text to translate...") onInputTextChanged: { translateTimer.restart(); } @@ -223,7 +223,7 @@ Item { z: 9999 sourceComponent: SelectionDialog { id: languageSelectorDialog - titleText: qsTr("Select Language") + titleText: Translation.tr("Select Language") items: root.languages defaultChoice: root.languageSelectorTarget ? root.targetLanguage : root.sourceLanguage onCanceled: () => { diff --git a/.config/quickshell/modules/sidebarLeft/aiChat/AiMessage.qml b/.config/quickshell/modules/sidebarLeft/aiChat/AiMessage.qml index 8d7952110..18e5935e7 100644 --- a/.config/quickshell/modules/sidebarLeft/aiChat/AiMessage.qml +++ b/.config/quickshell/modules/sidebarLeft/aiChat/AiMessage.qml @@ -151,7 +151,7 @@ Rectangle { color: Appearance.m3colors.m3onSecondaryContainer text: messageData?.role == 'assistant' ? Ai.models[messageData?.model].name : (messageData?.role == 'user' && SystemInfo.username) ? SystemInfo.username : - qsTr("Interface") + Translation.tr("Interface") } } } @@ -173,7 +173,7 @@ Rectangle { text: "visibility_off" } StyledToolTip { - content: qsTr("Not visible to model") + content: Translation.tr("Not visible to model") } } @@ -200,7 +200,7 @@ Rectangle { } StyledToolTip { - content: qsTr("Copy") + content: Translation.tr("Copy") } } AiMessageControlButton { @@ -215,7 +215,7 @@ Rectangle { } } StyledToolTip { - content: root.editing ? qsTr("Save") : qsTr("Edit") + content: root.editing ? Translation.tr("Save") : Translation.tr("Edit") } } AiMessageControlButton { @@ -226,7 +226,7 @@ Rectangle { root.renderMarkdown = !root.renderMarkdown } StyledToolTip { - content: qsTr("View Markdown source") + content: Translation.tr("View Markdown source") } } AiMessageControlButton { @@ -236,7 +236,7 @@ Rectangle { Ai.removeMessage(root.messageIndex) } StyledToolTip { - content: qsTr("Delete") + content: Translation.tr("Delete") } } } diff --git a/.config/quickshell/modules/sidebarLeft/aiChat/MessageCodeBlock.qml b/.config/quickshell/modules/sidebarLeft/aiChat/MessageCodeBlock.qml index ea7bb0ee3..d0a434dbd 100644 --- a/.config/quickshell/modules/sidebarLeft/aiChat/MessageCodeBlock.qml +++ b/.config/quickshell/modules/sidebarLeft/aiChat/MessageCodeBlock.qml @@ -87,7 +87,7 @@ ColumnLayout { } } StyledToolTip { - content: qsTr("Copy code") + content: Translation.tr("Copy code") } } AiMessageControlButton { @@ -111,7 +111,7 @@ ColumnLayout { } } StyledToolTip { - content: qsTr("Save to Downloads") + content: Translation.tr("Save to Downloads") } } } diff --git a/.config/quickshell/modules/sidebarLeft/aiChat/MessageTextBlock.qml b/.config/quickshell/modules/sidebarLeft/aiChat/MessageTextBlock.qml index faa6c5590..189d1cfd1 100644 --- a/.config/quickshell/modules/sidebarLeft/aiChat/MessageTextBlock.qml +++ b/.config/quickshell/modules/sidebarLeft/aiChat/MessageTextBlock.qml @@ -124,7 +124,7 @@ ColumnLayout { wrapMode: TextEdit.Wrap color: messageData.thinking ? Appearance.colors.colSubtext : Appearance.colors.colOnLayer1 textFormat: renderMarkdown ? TextEdit.MarkdownText : TextEdit.PlainText - text: qsTr("Waiting for response...") + text: Translation.tr("Waiting for response...") onTextChanged: { if (!root.editing) return diff --git a/.config/quickshell/modules/sidebarLeft/aiChat/MessageThinkBlock.qml b/.config/quickshell/modules/sidebarLeft/aiChat/MessageThinkBlock.qml index 1ae941b78..c05d33cfd 100644 --- a/.config/quickshell/modules/sidebarLeft/aiChat/MessageThinkBlock.qml +++ b/.config/quickshell/modules/sidebarLeft/aiChat/MessageThinkBlock.qml @@ -99,7 +99,7 @@ Item { id: thinkBlockLanguage Layout.fillWidth: false Layout.alignment: Qt.AlignLeft - text: root.completed ? qsTr("Chain of Thought") : (qsTr("Thinking") + ".".repeat(Math.random() * 4)) + text: root.completed ? Translation.tr("Chain of Thought") : (Translation.tr("Thinking") + ".".repeat(Math.random() * 4)) } Item { Layout.fillWidth: true } RippleButton { // Expand button diff --git a/.config/quickshell/modules/sidebarLeft/anime/BooruImage.qml b/.config/quickshell/modules/sidebarLeft/anime/BooruImage.qml index 4e115bc76..6cdee99c0 100644 --- a/.config/quickshell/modules/sidebarLeft/anime/BooruImage.qml +++ b/.config/quickshell/modules/sidebarLeft/anime/BooruImage.qml @@ -153,7 +153,7 @@ Button { MenuButton { id: openFileLinkButton Layout.fillWidth: true - buttonText: qsTr("Open file link") + buttonText: Translation.tr("Open file link") onClicked: { root.showActions = false Hyprland.dispatch("keyword cursor:no_warps true") @@ -165,7 +165,7 @@ Button { id: sourceButton visible: root.imageData.source && root.imageData.source.length > 0 Layout.fillWidth: true - buttonText: StringUtils.format(qsTr("Go to source ({0})"), StringUtils.getDomain(root.imageData.source)) + buttonText: StringUtils.format(Translation.tr("Go to source ({0})"), StringUtils.getDomain(root.imageData.source)) enabled: root.imageData.source && root.imageData.source.length > 0 onClicked: { root.showActions = false @@ -177,10 +177,10 @@ Button { MenuButton { id: downloadButton Layout.fillWidth: true - buttonText: qsTr("Download") + buttonText: Translation.tr("Download") onClicked: { root.showActions = false - Hyprland.dispatch(`exec curl '${root.imageData.file_url}' -o '${root.imageData.is_nsfw ? root.nsfwPath : root.downloadPath}/${root.fileName}' && notify-send '${qsTr("Download complete")}' '${root.downloadPath}/${root.fileName}' -a 'Shell'`) + Hyprland.dispatch(`exec curl '${root.imageData.file_url}' -o '${root.imageData.is_nsfw ? root.nsfwPath : root.downloadPath}/${root.fileName}' && notify-send '${Translation.tr("Download complete")}' '${root.downloadPath}/${root.fileName}' -a 'Shell'`) } } } diff --git a/.config/quickshell/modules/sidebarLeft/anime/BooruResponse.qml b/.config/quickshell/modules/sidebarLeft/anime/BooruResponse.qml index 7a207582f..58df4fa47 100644 --- a/.config/quickshell/modules/sidebarLeft/anime/BooruResponse.qml +++ b/.config/quickshell/modules/sidebarLeft/anime/BooruResponse.qml @@ -4,6 +4,7 @@ import "root:/modules/common" import "root:/modules/common/widgets" import "root:/modules/common/functions/string_utils.js" as StringUtils import "../" +import "root:/services/" import QtQuick import QtQuick.Controls import QtQuick.Layouts @@ -94,7 +95,7 @@ Rectangle { font.pixelSize: Appearance.font.pixelSize.smaller color: Appearance.colors.colOnLayer2 // text: `Page ${root.responseData.page}` - text: StringUtils.format(qsTr("Page {0}"), root.responseData.page) + text: StringUtils.format(Translation.tr("Page {0}"), root.responseData.page) } } } diff --git a/.config/quickshell/modules/sidebarLeft/translator/TextCanvas.qml b/.config/quickshell/modules/sidebarLeft/translator/TextCanvas.qml index dad25020f..5b81a29d2 100644 --- a/.config/quickshell/modules/sidebarLeft/translator/TextCanvas.qml +++ b/.config/quickshell/modules/sidebarLeft/translator/TextCanvas.qml @@ -78,7 +78,7 @@ Rectangle { visible: root.isInput Layout.leftMargin: 10 sourceComponent: Text { - text: qsTr("%1 characters").arg(inputLoader.item.text.length) + text: Translation.tr("%1 characters").arg(inputLoader.item.text.length) color: Appearance.colors.colOnLayer1 font.pixelSize: Appearance.font.pixelSize.smaller } diff --git a/.config/quickshell/modules/sidebarRight/CenterWidgetGroup.qml b/.config/quickshell/modules/sidebarRight/CenterWidgetGroup.qml index 1b426da43..76e478880 100644 --- a/.config/quickshell/modules/sidebarRight/CenterWidgetGroup.qml +++ b/.config/quickshell/modules/sidebarRight/CenterWidgetGroup.qml @@ -5,6 +5,7 @@ import "./calendar" import "./notifications" import "./todo" import "./volumeMixer" +import "root:/services/" import Qt5Compat.GraphicalEffects import QtQuick import QtQuick.Controls @@ -17,7 +18,7 @@ Rectangle { color: Appearance.colors.colLayer1 property int selectedTab: 0 - property var tabButtonList: [{"icon": "notifications", "name": qsTr("Notifications")}, {"icon": "volume_up", "name": qsTr("Volume mixer")}] + property var tabButtonList: [{"icon": "notifications", "name": Translation.tr("Notifications")}, {"icon": "volume_up", "name": Translation.tr("Volume mixer")}] Keys.onPressed: (event) => { if (event.key === Qt.Key_PageDown || event.key === Qt.Key_PageUp) { diff --git a/.config/quickshell/modules/sidebarRight/SidebarRight.qml b/.config/quickshell/modules/sidebarRight/SidebarRight.qml index ed9788f0e..042322870 100644 --- a/.config/quickshell/modules/sidebarRight/SidebarRight.qml +++ b/.config/quickshell/modules/sidebarRight/SidebarRight.qml @@ -4,6 +4,7 @@ import "root:/modules/common" import "root:/modules/common/widgets" import "root:/modules/common/functions/string_utils.js" as StringUtils import "./quickToggles/" +import "root:/services/" import QtQuick import QtQuick.Controls import QtQuick.Layouts @@ -119,7 +120,7 @@ Scope { StyledText { font.pixelSize: Appearance.font.pixelSize.normal color: Appearance.colors.colOnLayer0 - text: StringUtils.format(qsTr("Uptime: {0}"), DateTime.uptime) + text: StringUtils.format(Translation.tr("Uptime: {0}"), DateTime.uptime) textFormat: Text.MarkdownText } @@ -136,7 +137,7 @@ Scope { Quickshell.reload(true) } StyledToolTip { - content: qsTr("Reload Hyprland & Quickshell") + content: Translation.tr("Reload Hyprland & Quickshell") } } QuickToggleButton { @@ -147,7 +148,7 @@ Scope { Hyprland.dispatch(`global quickshell:sidebarRightClose`) } StyledToolTip { - content: qsTr("Plasma Settings") + content: Translation.tr("Plasma Settings") } } QuickToggleButton { @@ -157,7 +158,7 @@ Scope { Hyprland.dispatch("global quickshell:sessionOpen") } StyledToolTip { - content: qsTr("Session") + content: Translation.tr("Session") } } } @@ -218,7 +219,7 @@ Scope { GlobalShortcut { name: "sidebarRightToggle" - description: qsTr("Toggles right sidebar on press") + description: Translation.tr("Toggles right sidebar on press") onPressed: { GlobalStates.sidebarRightOpen = !GlobalStates.sidebarRightOpen; @@ -227,7 +228,7 @@ Scope { } GlobalShortcut { name: "sidebarRightOpen" - description: qsTr("Opens right sidebar on press") + description: Translation.tr("Opens right sidebar on press") onPressed: { GlobalStates.sidebarRightOpen = true; @@ -236,7 +237,7 @@ Scope { } GlobalShortcut { name: "sidebarRightClose" - description: qsTr("Closes right sidebar on press") + description: Translation.tr("Closes right sidebar on press") onPressed: { GlobalStates.sidebarRightOpen = false; diff --git a/.config/quickshell/modules/sidebarRight/calendar/CalendarWidget.qml b/.config/quickshell/modules/sidebarRight/calendar/CalendarWidget.qml index 1f11d87a2..7fd72db40 100644 --- a/.config/quickshell/modules/sidebarRight/calendar/CalendarWidget.qml +++ b/.config/quickshell/modules/sidebarRight/calendar/CalendarWidget.qml @@ -48,7 +48,7 @@ Item { CalendarHeaderButton { clip: true buttonText: `${monthShift != 0 ? "• " : ""}${viewingDate.toLocaleDateString(Qt.locale(), "MMMM yyyy")}` - tooltipText: (monthShift === 0) ? "" : qsTr("Jump to current month") + tooltipText: (monthShift === 0) ? "" : Translation.tr("Jump to current month") onClicked: { monthShift = 0; } diff --git a/.config/quickshell/modules/sidebarRight/notifications/NotificationList.qml b/.config/quickshell/modules/sidebarRight/notifications/NotificationList.qml index 491b7683f..461a671b9 100644 --- a/.config/quickshell/modules/sidebarRight/notifications/NotificationList.qml +++ b/.config/quickshell/modules/sidebarRight/notifications/NotificationList.qml @@ -1,6 +1,7 @@ import "root:/modules/common" import "root:/modules/common/widgets" import "root:/services" +import "root:/services/" import Qt5Compat.GraphicalEffects import QtQuick import QtQuick.Controls @@ -61,7 +62,7 @@ Item { font.pixelSize: Appearance.font.pixelSize.normal color: Appearance.m3colors.m3outline horizontalAlignment: Text.AlignHCenter - text: qsTr("No notifications") + text: Translation.tr("No notifications") } } } @@ -101,7 +102,7 @@ Item { NotificationStatusButton { buttonIcon: "notifications_paused" - buttonText: qsTr("Silent") + buttonText: Translation.tr("Silent") toggled: Notifications.silent onClicked: () => { Notifications.silent = !Notifications.silent; @@ -109,7 +110,7 @@ Item { } NotificationStatusButton { buttonIcon: "clear_all" - buttonText: qsTr("Clear") + buttonText: Translation.tr("Clear") onClicked: () => { Notifications.discardAllNotifications() } diff --git a/.config/quickshell/modules/sidebarRight/quickToggles/BluetoothToggle.qml b/.config/quickshell/modules/sidebarRight/quickToggles/BluetoothToggle.qml index 083ecc036..bd0295c40 100644 --- a/.config/quickshell/modules/sidebarRight/quickToggles/BluetoothToggle.qml +++ b/.config/quickshell/modules/sidebarRight/quickToggles/BluetoothToggle.qml @@ -28,9 +28,9 @@ QuickToggleButton { } } StyledToolTip { - content: StringUtils.format(qsTr("{0} | Right-click to configure"), + content: StringUtils.format(Translation.tr("{0} | Right-click to configure"), (Bluetooth.bluetoothEnabled && Bluetooth.bluetoothDeviceName.length > 0) ? - Bluetooth.bluetoothDeviceName : qsTr("Bluetooth")) + Bluetooth.bluetoothDeviceName : Translation.tr("Bluetooth")) } } diff --git a/.config/quickshell/modules/sidebarRight/quickToggles/GameMode.qml b/.config/quickshell/modules/sidebarRight/quickToggles/GameMode.qml index 1cd56acc7..de6f210c5 100644 --- a/.config/quickshell/modules/sidebarRight/quickToggles/GameMode.qml +++ b/.config/quickshell/modules/sidebarRight/quickToggles/GameMode.qml @@ -1,6 +1,7 @@ import "root:/modules/common" import "root:/modules/common/widgets" import "../" +import "root:/services/" import Quickshell import Quickshell.Io import Quickshell.Hyprland @@ -21,6 +22,6 @@ QuickToggleButton { } StyledToolTip { - content: qsTr("Game mode") + content: Translation.tr("Game mode") } } \ No newline at end of file diff --git a/.config/quickshell/modules/sidebarRight/quickToggles/IdleInhibitor.qml b/.config/quickshell/modules/sidebarRight/quickToggles/IdleInhibitor.qml index b48d3467f..0cb848fb7 100644 --- a/.config/quickshell/modules/sidebarRight/quickToggles/IdleInhibitor.qml +++ b/.config/quickshell/modules/sidebarRight/quickToggles/IdleInhibitor.qml @@ -1,6 +1,7 @@ import "root:/modules/common" import "root:/modules/common/widgets" import "../" +import "root:/services/" import Quickshell.Io import Quickshell import Quickshell.Hyprland @@ -27,6 +28,6 @@ QuickToggleButton { } } StyledToolTip { - content: qsTr("Keep system awake") + content: Translation.tr("Keep system awake") } } diff --git a/.config/quickshell/modules/sidebarRight/quickToggles/NetworkToggle.qml b/.config/quickshell/modules/sidebarRight/quickToggles/NetworkToggle.qml index 5271e3769..0ba09b72e 100644 --- a/.config/quickshell/modules/sidebarRight/quickToggles/NetworkToggle.qml +++ b/.config/quickshell/modules/sidebarRight/quickToggles/NetworkToggle.qml @@ -3,6 +3,7 @@ import "root:/modules/common" import "root:/modules/common/widgets" import "root:/modules/common/functions/string_utils.js" as StringUtils import "../" +import "root:/services/" import QtQuick import Quickshell import Quickshell.Io @@ -28,6 +29,6 @@ QuickToggleButton { } } StyledToolTip { - content: StringUtils.format(qsTr("{0} | Right-click to configure"), Network.networkName) + content: StringUtils.format(Translation.tr("{0} | Right-click to configure"), Network.networkName) } } diff --git a/.config/quickshell/modules/sidebarRight/quickToggles/NightLight.qml b/.config/quickshell/modules/sidebarRight/quickToggles/NightLight.qml index 72df3e1ef..f71d80a2a 100644 --- a/.config/quickshell/modules/sidebarRight/quickToggles/NightLight.qml +++ b/.config/quickshell/modules/sidebarRight/quickToggles/NightLight.qml @@ -1,6 +1,7 @@ import "root:/modules/common" import "root:/modules/common/widgets" import "../" +import "root:/services/" import Quickshell.Io import Quickshell @@ -37,6 +38,6 @@ QuickToggleButton { } } StyledToolTip { - content: qsTr("Night Light") + content: Translation.tr("Night Light") } } diff --git a/.config/quickshell/modules/sidebarRight/todo/TodoWidget.qml b/.config/quickshell/modules/sidebarRight/todo/TodoWidget.qml index 9f5f4fd08..ea5701176 100644 --- a/.config/quickshell/modules/sidebarRight/todo/TodoWidget.qml +++ b/.config/quickshell/modules/sidebarRight/todo/TodoWidget.qml @@ -10,7 +10,7 @@ import QtQuick.Layouts Item { id: root property int currentTab: 0 - property var tabButtonList: [{"icon": "checklist", "name": qsTr("Unfinished")}, {"name": qsTr("Done"), "icon": "check_circle"}] + property var tabButtonList: [{"icon": "checklist", "name": Translation.tr("Unfinished")}, {"name": Translation.tr("Done"), "icon": "check_circle"}] property bool showAddDialog: false property int dialogMargins: 20 property int fabSize: 48 @@ -134,7 +134,7 @@ Item { TaskList { listBottomPadding: root.fabSize + root.fabMargins * 2 emptyPlaceholderIcon: "check_circle" - emptyPlaceholderText: qsTr("Nothing here!") + emptyPlaceholderText: Translation.tr("Nothing here!") taskList: Todo.list .map(function(item, i) { return Object.assign({}, item, {originalIndex: i}); }) .filter(function(item) { return !item.done; }) @@ -142,7 +142,7 @@ Item { TaskList { listBottomPadding: root.fabSize + root.fabMargins * 2 emptyPlaceholderIcon: "checklist" - emptyPlaceholderText: qsTr("Finished tasks will go here") + emptyPlaceholderText: Translation.tr("Finished tasks will go here") taskList: Todo.list .map(function(item, i) { return Object.assign({}, item, {originalIndex: i}); }) .filter(function(item) { return item.done; }) @@ -252,7 +252,7 @@ Item { Layout.alignment: Qt.AlignLeft color: Appearance.m3colors.m3onSurface font.pixelSize: Appearance.font.pixelSize.larger - text: qsTr("Add task") + text: Translation.tr("Add task") } TextField { @@ -265,7 +265,7 @@ Item { renderType: Text.NativeRendering selectedTextColor: Appearance.m3colors.m3onSecondaryContainer selectionColor: Appearance.colors.colSecondaryContainer - placeholderText: qsTr("Task description") + placeholderText: Translation.tr("Task description") placeholderTextColor: Appearance.m3colors.m3outline focus: root.showAddDialog onAccepted: dialog.addTask() @@ -293,11 +293,11 @@ Item { spacing: 5 DialogButton { - buttonText: qsTr("Cancel") + buttonText: Translation.tr("Cancel") onClicked: root.showAddDialog = false } DialogButton { - buttonText: qsTr("Add") + buttonText: Translation.tr("Add") enabled: todoInput.text.length > 0 onClicked: dialog.addTask() } diff --git a/.config/quickshell/modules/sidebarRight/volumeMixer/AudioDeviceSelectorButton.qml b/.config/quickshell/modules/sidebarRight/volumeMixer/AudioDeviceSelectorButton.qml index cc956cc03..0e5f37bbb 100644 --- a/.config/quickshell/modules/sidebarRight/volumeMixer/AudioDeviceSelectorButton.qml +++ b/.config/quickshell/modules/sidebarRight/volumeMixer/AudioDeviceSelectorButton.qml @@ -1,6 +1,7 @@ import "root:/modules/common" import "root:/modules/common/widgets" import "root:/services" +import "root:/services/" import Qt5Compat.GraphicalEffects import QtQuick import QtQuick.Controls @@ -40,14 +41,14 @@ GroupButton { Layout.fillWidth: true elide: Text.ElideRight font.pixelSize: Appearance.font.pixelSize.normal - text: input ? qsTr("Input") : qsTr("Output") + text: input ? Translation.tr("Input") : Translation.tr("Output") color: Appearance.colors.colOnLayer2 } StyledText { Layout.fillWidth: true elide: Text.ElideRight font.pixelSize: Appearance.font.pixelSize.smaller - text: (input ? Pipewire.defaultAudioSource?.description : Pipewire.defaultAudioSink?.description) ?? qsTr("Unknown") + text: (input ? Pipewire.defaultAudioSource?.description : Pipewire.defaultAudioSink?.description) ?? Translation.tr("Unknown") color: Appearance.m3colors.m3outline } } diff --git a/.config/quickshell/modules/sidebarRight/volumeMixer/VolumeMixer.qml b/.config/quickshell/modules/sidebarRight/volumeMixer/VolumeMixer.qml index 2e1570f30..c32d599fc 100644 --- a/.config/quickshell/modules/sidebarRight/volumeMixer/VolumeMixer.qml +++ b/.config/quickshell/modules/sidebarRight/volumeMixer/VolumeMixer.qml @@ -1,6 +1,7 @@ import "root:/modules/common" import "root:/modules/common/widgets" import "root:/services" +import "root:/services/" import Qt5Compat.GraphicalEffects import QtQuick import QtQuick.Controls @@ -108,7 +109,7 @@ Item { font.pixelSize: Appearance.font.pixelSize.normal color: Appearance.m3colors.m3outline horizontalAlignment: Text.AlignHCenter - text: qsTr("No audio source") + text: Translation.tr("No audio source") } } } @@ -262,13 +263,13 @@ Item { Layout.alignment: Qt.AlignRight DialogButton { - buttonText: qsTr("Cancel") + buttonText: Translation.tr("Cancel") onClicked: { root.showDeviceSelector = false } } DialogButton { - buttonText: qsTr("OK") + buttonText: Translation.tr("OK") onClicked: { root.showDeviceSelector = false if (root.selectedDevice) { diff --git a/.config/quickshell/services/Ai.qml b/.config/quickshell/services/Ai.qml index f545b878f..a3980f3c6 100644 --- a/.config/quickshell/services/Ai.qml +++ b/.config/quickshell/services/Ai.qml @@ -4,6 +4,7 @@ pragma ComponentBehavior: Bound import "root:/modules/common/functions/string_utils.js" as StringUtils import "root:/modules/common/functions/object_utils.js" as ObjectUtils import "root:/modules/common" +import "root:/services/" import Quickshell; import Quickshell.Io; import Qt.labs.platform @@ -53,14 +54,14 @@ Singleton { "gemini-2.0-flash-search": { "name": "Gemini 2.0 Flash (Search)", "icon": "google-gemini-symbolic", - "description": qsTr("Online | Google's model\nGives up-to-date information with search."), + "description": Translation.tr("Online | Google's model\nGives up-to-date information with search."), "homepage": "https://aistudio.google.com", "endpoint": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:streamGenerateContent", "model": "gemini-2.0-flash", "requires_key": true, "key_id": "gemini", "key_get_link": "https://aistudio.google.com/app/apikey", - "key_get_description": qsTr("**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key"), + "key_get_description": Translation.tr("**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key"), "api_format": "gemini", "tools": [ { @@ -71,14 +72,14 @@ Singleton { "gemini-2.0-flash-tools": { "name": "Gemini 2.0 Flash (Tools)", "icon": "google-gemini-symbolic", - "description": qsTr("Experimental | Online | Google's model\nCan do a little more but doesn't search quickly"), + "description": Translation.tr("Experimental | Online | Google's model\nCan do a little more but doesn't search quickly"), "homepage": "https://aistudio.google.com", "endpoint": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:streamGenerateContent", "model": "gemini-2.0-flash", "requires_key": true, "key_id": "gemini", "key_get_link": "https://aistudio.google.com/app/apikey", - "key_get_description": qsTr("**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key"), + "key_get_description": Translation.tr("**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key"), "api_format": "gemini", "tools": [ { @@ -116,14 +117,14 @@ Singleton { "gemini-2.5-flash-search": { "name": "Gemini 2.5 Flash (Search)", "icon": "google-gemini-symbolic", - "description": qsTr("Online | Google's model\nGives up-to-date information with search."), + "description": Translation.tr("Online | Google's model\nGives up-to-date information with search."), "homepage": "https://aistudio.google.com", "endpoint": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-05-20:streamGenerateContent", "model": "gemini-2.5-flash-preview-05-20", "requires_key": true, "key_id": "gemini", "key_get_link": "https://aistudio.google.com/app/apikey", - "key_get_description": qsTr("**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key"), + "key_get_description": Translation.tr("**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key"), "api_format": "gemini", "tools": [ { @@ -134,14 +135,14 @@ Singleton { "gemini-2.5-flash-tools": { "name": "Gemini 2.5 Flash (Tools)", "icon": "google-gemini-symbolic", - "description": qsTr("Experimental | Online | Google's model\nCan do a little more but doesn't search quickly"), + "description": Translation.tr("Experimental | Online | Google's model\nCan do a little more but doesn't search quickly"), "homepage": "https://aistudio.google.com", "endpoint": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-05-20:streamGenerateContent", "model": "gemini-2.5-flash-preview-05-20", "requires_key": true, "key_id": "gemini", "key_get_link": "https://aistudio.google.com/app/apikey", - "key_get_description": qsTr("**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key"), + "key_get_description": Translation.tr("**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key"), "api_format": "gemini", "tools": [ { @@ -179,26 +180,26 @@ Singleton { "openrouter-llama4-maverick": { "name": "Llama 4 Maverick", "icon": "ollama-symbolic", - "description": StringUtils.format(qsTr("Online via {0} | {1}'s model"), "OpenRouter", "Meta"), + "description": StringUtils.format(Translation.tr("Online via {0} | {1}'s model"), "OpenRouter", "Meta"), "homepage": "https://openrouter.ai/meta-llama/llama-4-maverick:free", "endpoint": "https://openrouter.ai/api/v1/chat/completions", "model": "meta-llama/llama-4-maverick:free", "requires_key": true, "key_id": "openrouter", "key_get_link": "https://openrouter.ai/settings/keys", - "key_get_description": qsTr("**Pricing**: free. Data use policy varies depending on your OpenRouter account settings.\n\n**Instructions**: Log into OpenRouter account, go to Keys on the topright menu, click Create API Key"), + "key_get_description": Translation.tr("**Pricing**: free. Data use policy varies depending on your OpenRouter account settings.\n\n**Instructions**: Log into OpenRouter account, go to Keys on the topright menu, click Create API Key"), }, "openrouter-deepseek-r1": { "name": "DeepSeek R1", "icon": "deepseek-symbolic", - "description": StringUtils.format(qsTr("Online via {0} | {1}'s model"), "OpenRouter", "DeepSeek"), + "description": StringUtils.format(Translation.tr("Online via {0} | {1}'s model"), "OpenRouter", "DeepSeek"), "homepage": "https://openrouter.ai/deepseek/deepseek-r1:free", "endpoint": "https://openrouter.ai/api/v1/chat/completions", "model": "deepseek/deepseek-r1:free", "requires_key": true, "key_id": "openrouter", "key_get_link": "https://openrouter.ai/settings/keys", - "key_get_description": qsTr("**Pricing**: free. Data use policy varies depending on your OpenRouter account settings.\n\n**Instructions**: Log into OpenRouter account, go to Keys on the topright menu, click Create API Key"), + "key_get_description": Translation.tr("**Pricing**: free. Data use policy varies depending on your OpenRouter account settings.\n\n**Instructions**: Log into OpenRouter account, go to Keys on the topright menu, click Create API Key"), }, } property var modelList: Object.keys(root.models) @@ -244,7 +245,7 @@ Singleton { root.models[safeModelName] = { "name": guessModelName(model), "icon": guessModelLogo(model), - "description": StringUtils.format(qsTr("Local Ollama model | {0}"), model), + "description": StringUtils.format(Translation.tr("Local Ollama model | {0}"), model), "homepage": `https://ollama.com/library/${model}`, "endpoint": "http://localhost:11434/v1/chat/completions", "model": model, @@ -283,8 +284,8 @@ Singleton { function addApiKeyAdvice(model) { root.addMessage( - StringUtils.format(qsTr('To set an API key, pass it with the command\n\nTo view the key, pass "get" with the command
\n\n### For {0}:\n\n**Link**: {1}\n\n{2}'), - model.name, model.key_get_link, model.key_get_description ?? qsTr("No further instruction provided")), + StringUtils.format(Translation.tr('To set an API key, pass it with the command\n\nTo view the key, pass "get" with the command
\n\n### For {0}:\n\n**Link**: {1}\n\n{2}'), + model.name, model.key_get_link, model.key_get_description ?? Translation.tr("No further instruction provided")), Ai.interfaceRole ); } @@ -314,7 +315,7 @@ Singleton { } } } else { - if (feedback) root.addMessage(qsTr("Invalid model. Supported: \n```\n") + modelList.join("\n```\n```\n"), Ai.interfaceRole) + "\n```" + if (feedback) root.addMessage(Translation.tr("Invalid model. Supported: \n```\n") + modelList.join("\n```\n```\n"), Ai.interfaceRole) + "\n```" } } @@ -324,18 +325,18 @@ Singleton { function setTemperature(value) { if (value == NaN || value < 0 || value > 2) { - root.addMessage(qsTr("Temperature must be between 0 and 2"), Ai.interfaceRole); + root.addMessage(Translation.tr("Temperature must be between 0 and 2"), Ai.interfaceRole); return; } PersistentStateManager.setState("ai.temperature", value); root.temperature = value; - root.addMessage(StringUtils.format(qsTr("Temperature set to {0}"), value), Ai.interfaceRole); + root.addMessage(StringUtils.format(Translation.tr("Temperature set to {0}"), value), Ai.interfaceRole); } function setApiKey(key) { const model = models[currentModelId]; if (!model.requires_key) { - root.addMessage(StringUtils.format(qsTr("{0} does not require an API key"), model.name), Ai.interfaceRole); + root.addMessage(StringUtils.format(Translation.tr("{0} does not require an API key"), model.name), Ai.interfaceRole); return; } if (!key || key.length === 0) { @@ -344,7 +345,7 @@ Singleton { return; } KeyringStorage.setNestedField(["apiKeys", model.key_id], key.trim()); - root.addMessage(StringUtils.format(qsTr("API key set for {0}"), model.name, Ai.interfaceRole)); + root.addMessage(StringUtils.format(Translation.tr("API key set for {0}"), model.name, Ai.interfaceRole)); } function printApiKey() { @@ -352,17 +353,17 @@ Singleton { if (model.requires_key) { const key = root.apiKeys[model.key_id]; if (key) { - root.addMessage(StringUtils.format(qsTr("API key:\n\n```txt\n{0}\n```"), key), Ai.interfaceRole); + root.addMessage(StringUtils.format(Translation.tr("API key:\n\n```txt\n{0}\n```"), key), Ai.interfaceRole); } else { - root.addMessage(StringUtils.format(qsTr("No API key set for {0}"), model.name), Ai.interfaceRole); + root.addMessage(StringUtils.format(Translation.tr("No API key set for {0}"), model.name), Ai.interfaceRole); } } else { - root.addMessage(StringUtils.format(qsTr("{0} does not require an API key"), model.name), Ai.interfaceRole); + root.addMessage(StringUtils.format(Translation.tr("{0} does not require an API key"), model.name), Ai.interfaceRole); } } function printTemperature() { - root.addMessage(StringUtils.format(qsTr("Temperature: {0}"), root.temperature), Ai.interfaceRole); + root.addMessage(StringUtils.format(Translation.tr("Temperature: {0}"), root.temperature), Ai.interfaceRole); } function clearMessages() { @@ -701,7 +702,7 @@ Singleton { root.setModel("gemini-2.0-flash-search", false); root.postResponseHook = () => root.setModel("gemini-2.0-flash-tools", false); } - addFunctionOutputMessage(name, qsTr("Switched to search mode. Continue with the user's request.")) + addFunctionOutputMessage(name, Translation.tr("Switched to search mode. Continue with the user's request.")) requester.makeRequest(); } else if (name === "get_shell_config") { const configJson = ObjectUtils.toPlainObject(ConfigOptions) @@ -709,7 +710,7 @@ Singleton { requester.makeRequest(); } else if (name === "set_shell_config") { if (!args.key || !args.value) { - addFunctionOutputMessage(name, qsTr("Invalid arguments. Must provide `key` and `value`.")); + addFunctionOutputMessage(name, Translation.tr("Invalid arguments. Must provide `key` and `value`.")); return; } const key = args.key; @@ -717,7 +718,7 @@ Singleton { ConfigLoader.setLiveConfigValue(key, value); ConfigLoader.saveConfig(); } - else root.addMessage(qsTr("Unknown function call: {0}"), "assistant"); + else root.addMessage(Translation.tr("Unknown function call: {0}"), "assistant"); } } diff --git a/.config/quickshell/services/Booru.qml b/.config/quickshell/services/Booru.qml index 49256bfa7..b50c1976b 100644 --- a/.config/quickshell/services/Booru.qml +++ b/.config/quickshell/services/Booru.qml @@ -2,6 +2,7 @@ pragma Singleton pragma ComponentBehavior: Bound import "root:/modules/common" +import "root:/services/" import Quickshell; import Quickshell.Io; import Qt.labs.platform @@ -16,18 +17,18 @@ Singleton { signal tagSuggestion(string query, var suggestions) - property string failMessage: qsTr("That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number") + property string failMessage: Translation.tr("That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number") property var responses: [] property int runningRequests: 0 property var defaultUserAgent: ConfigOptions?.networking?.userAgent || "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36" property var providerList: Object.keys(providers).filter(provider => provider !== "system" && providers[provider].api) property var providers: { - "system": { "name": qsTr("System") }, + "system": { "name": Translation.tr("System") }, "yandere": { "name": "yande.re", "url": "https://yande.re", "api": "https://yande.re/post.json", - "description": qsTr("All-rounder | Good quality, decent quantity"), + "description": Translation.tr("All-rounder | Good quality, decent quantity"), "mapFunc": (response) => { return response.map(item => { return { @@ -61,7 +62,7 @@ Singleton { "name": "Konachan", "url": "https://konachan.com", "api": "https://konachan.com/post.json", - "description": qsTr("For desktop wallpapers | Good quality"), + "description": Translation.tr("For desktop wallpapers | Good quality"), "mapFunc": (response) => { return response.map(item => { return { @@ -95,7 +96,7 @@ Singleton { "name": "Zerochan", "url": "https://www.zerochan.net", "api": "https://www.zerochan.net/?json", - "description": qsTr("Clean stuff | Excellent quality, no NSFW"), + "description": Translation.tr("Clean stuff | Excellent quality, no NSFW"), "mapFunc": (response) => { response = response.items return response.map(item => { @@ -122,7 +123,7 @@ Singleton { "name": "Danbooru", "url": "https://danbooru.donmai.us", "api": "https://danbooru.donmai.us/posts.json", - "description": qsTr("The popular one | Best quantity, but quality can vary wildly"), + "description": Translation.tr("The popular one | Best quantity, but quality can vary wildly"), "mapFunc": (response) => { return response.map(item => { return { @@ -157,7 +158,7 @@ Singleton { "name": "Gelbooru", "url": "https://gelbooru.com", "api": "https://gelbooru.com/index.php?page=dapi&s=post&q=index&json=1", - "description": qsTr("The hentai one | Great quantity, a lot of NSFW, quality varies wildly"), + "description": Translation.tr("The hentai one | Great quantity, a lot of NSFW, quality varies wildly"), "mapFunc": (response) => { response = response.post return response.map(item => { @@ -192,7 +193,7 @@ Singleton { "name": "waifu.im", "url": "https://waifu.im", "api": "https://api.waifu.im/search", - "description": qsTr("Waifus only | Excellent quality, limited quantity"), + "description": Translation.tr("Waifus only | Excellent quality, limited quantity"), "mapFunc": (response) => { response = response.images return response.map(item => { @@ -223,7 +224,7 @@ Singleton { "name": "Alcy", "url": "https://t.alcy.cc", "api": "https://t.alcy.cc/", - "description": qsTr("Large images | God tier quality, no NSFW."), + "description": Translation.tr("Large images | God tier quality, no NSFW."), "fixedTags": [ { "name": "ycy", @@ -287,10 +288,10 @@ Singleton { provider = provider.toLowerCase() if (providerList.indexOf(provider) !== -1) { PersistentStateManager.setState("booru.provider", provider) - root.addSystemMessage(qsTr("Provider set to ") + providers[provider].name - + (provider == "zerochan" ? qsTr(". Notes for Zerochan:\n- You must enter a color\n- Set your zerochan username in `sidebar.booru.zerochan.username` config option. You [might be banned for not doing so](https://www.zerochan.net/api#:~:text=The%20request%20may%20still%20be%20completed%20successfully%20without%20this%20custom%20header%2C%20but%20your%20project%20may%20be%20banned%20for%20being%20anonymous.)!") : "")) + root.addSystemMessage(Translation.tr("Provider set to ") + providers[provider].name + + (provider == "zerochan" ? Translation.tr(". Notes for Zerochan:\n- You must enter a color\n- Set your zerochan username in `sidebar.booru.zerochan.username` config option. You [might be banned for not doing so](https://www.zerochan.net/api#:~:text=The%20request%20may%20still%20be%20completed%20successfully%20without%20this%20custom%20header%2C%20but%20your%20project%20may%20be%20banned%20for%20being%20anonymous.)!") : "")) } else { - root.addSystemMessage(qsTr("Invalid API provider. Supported: \n- ") + providerList.join("\n- ")) + root.addSystemMessage(Translation.tr("Invalid API provider. Supported: \n- ") + providerList.join("\n- ")) } } diff --git a/.config/quickshell/services/Brightness.qml b/.config/quickshell/services/Brightness.qml index aba664aa9..a81c01d5d 100644 --- a/.config/quickshell/services/Brightness.qml +++ b/.config/quickshell/services/Brightness.qml @@ -4,6 +4,7 @@ pragma ComponentBehavior: Bound // From https://github.com/caelestia-dots/shell/ (`quickshell` branch) with modifications. // License: GPLv3 +import "root:/services/" import Quickshell import Quickshell.Io import Quickshell.Hyprland @@ -140,13 +141,13 @@ Singleton { GlobalShortcut { name: "brightnessIncrease" - description: qsTr("Increase brightness") + description: Translation.tr("Increase brightness") onPressed: root.increaseBrightness() } GlobalShortcut { name: "brightnessDecrease" - description: qsTr("Decrease brightness") + description: Translation.tr("Decrease brightness") onPressed: root.decreaseBrightness() } } diff --git a/.config/quickshell/services/ConfigLoader.qml b/.config/quickshell/services/ConfigLoader.qml index 347e8f400..01a6625e9 100644 --- a/.config/quickshell/services/ConfigLoader.qml +++ b/.config/quickshell/services/ConfigLoader.qml @@ -44,7 +44,7 @@ Singleton { } catch (e) { console.error("[ConfigLoader] Error reading file:", e); console.log("[ConfigLoader] File content was:", fileContent); - Hyprland.dispatch(`exec notify-send "${qsTr("Shell configuration failed to load")}" "${root.filePath}"`) + Hyprland.dispatch(`exec notify-send "${Translation.tr("Shell configuration failed to load")}" "${root.filePath}"`) return; } @@ -105,7 +105,7 @@ Singleton { } else { root.applyConfig(configFileView.text()) if (!root.preventNextNotification) { - // Hyprland.dispatch(`exec notify-send "${qsTr("Shell configuration reloaded")}" "${root.filePath}"`) + // Hyprland.dispatch(`exec notify-send "${Translation.tr("Shell configuration reloaded")}" "${root.filePath}"`) } else { root.preventNextNotification = false; } @@ -129,9 +129,9 @@ Singleton { if(error == FileViewError.FileNotFound) { console.log("[ConfigLoader] File not found, creating new file.") root.saveConfig() - Hyprland.dispatch(`exec notify-send "${qsTr("Shell configuration created")}" "${root.filePath}"`) + Hyprland.dispatch(`exec notify-send "${Translation.tr("Shell configuration created")}" "${root.filePath}"`) } else { - Hyprland.dispatch(`exec notify-send "${qsTr("Shell configuration failed to load")}" "${root.filePath}"`) + Hyprland.dispatch(`exec notify-send "${Translation.tr("Shell configuration failed to load")}" "${root.filePath}"`) } } } diff --git a/.config/quickshell/services/KeyringStorage.qml b/.config/quickshell/services/KeyringStorage.qml index 3f3956f98..3ec7560b7 100644 --- a/.config/quickshell/services/KeyringStorage.qml +++ b/.config/quickshell/services/KeyringStorage.qml @@ -20,14 +20,14 @@ Singleton { property var properties: { "application": "illogical-impulse", - "explanation": qsTr("For storing API keys and other sensitive information"), + "explanation": Translation.tr("For storing API keys and other sensitive information"), } property var propertiesAsArgs: Object.keys(root.properties).reduce( function(arr, key) { return arr.concat([key, root.properties[key]]); }, [] ) - property string keyringLabel: StringUtils.format(qsTr("{0} Safe Storage"), "illogical-impulse") + property string keyringLabel: StringUtils.format(Translation.tr("{0} Safe Storage"), "illogical-impulse") function setNestedField(path, value) { if (!root.keyringData) root.keyringData = {}; diff --git a/.config/quickshell/services/MprisController.qml b/.config/quickshell/services/MprisController.qml index 96aa5e80b..90d72ed5a 100644 --- a/.config/quickshell/services/MprisController.qml +++ b/.config/quickshell/services/MprisController.qml @@ -4,6 +4,7 @@ pragma ComponentBehavior: Bound // From https://git.outfoxxed.me/outfoxxed/nixnew // It does not have a license, but the author is okay with redistribution. +import "root:/services/" import QtQml.Models import QtQuick import Quickshell @@ -85,9 +86,9 @@ Singleton { this.activeTrack = { uniqueId: this.activePlayer?.uniqueId ?? 0, artUrl: this.activePlayer?.trackArtUrl ?? "", - title: this.activePlayer?.trackTitle || qsTr("Unknown Title"), - artist: this.activePlayer?.trackArtist || qsTr("Unknown Artist"), - album: this.activePlayer?.trackAlbum || qsTr("Unknown Album"), + title: this.activePlayer?.trackTitle || Translation.tr("Unknown Title"), + artist: this.activePlayer?.trackArtist || Translation.tr("Unknown Artist"), + album: this.activePlayer?.trackAlbum || Translation.tr("Unknown Album"), }; this.trackChanged(__reverse); diff --git a/.config/quickshell/services/Translation.qml b/.config/quickshell/services/Translation.qml new file mode 100644 index 000000000..97aff7c5c --- /dev/null +++ b/.config/quickshell/services/Translation.qml @@ -0,0 +1,170 @@ +pragma Singleton + +import QtQuick +import Quickshell +import Quickshell.Io +import "root:/modules/common/" + +Singleton { + id: root + + property var translations: ({}) + property string currentLanguage: "en_US" + property var availableLanguages: ["en_US"] + property bool isScanning: false + property bool isLoading: false + + Process { + id: scanLanguagesProcess + command: ["find", Qt.resolvedUrl(Directories.config + "/quickshell/translations/").toString().replace("file://", ""), "-name", "*.json", "-exec", "basename", "{}", ".json", ";"] + running: false + + stdout: SplitParser { + onRead: data => { + if (data.trim().length === 0) return + + var files = data.trim().split('\n') + + for (var i = 0; i < files.length; i++) { + var lang = files[i].trim() + if (lang.length > 0 && root.availableLanguages.indexOf(lang) === -1) { + root.availableLanguages.push(lang) + } + } + } + } + + onExited: (exitCode, exitStatus) => { + root.isScanning = false + if (exitCode !== 0) { + root.availableLanguages = ["en_US"] + } + root.loadTranslations() + } + } + + FileView { + id: translationFileView + onLoaded: { + var textContent = "" + try { + textContent = text() + } catch (e) { + root.translations = {} + root.isLoading = false + return + } + + if (textContent.length === 0) { + root.translations = {} + root.isLoading = false + return + } + + try { + var jsonData = JSON.parse(textContent) + root.translations = jsonData + root.isLoading = false + } catch (e) { + root.translations = {} + root.isLoading = false + } + } + onLoadFailed: (error) => { + root.translations = {} + root.isLoading = false + } + } + + function detectSystemLanguage() { + var locale = Qt.locale().name + return locale + } + + function getLanguageCode() { + var configLang = "auto" + try { + configLang = ConfigOptions.language.ui + } catch (e) { + configLang = "auto" + } + + if (configLang === "auto") { + return detectSystemLanguage() + } else { + if (root.availableLanguages.indexOf(configLang) !== -1) { + return configLang + } else { + return detectSystemLanguage() + } + } + } + + function loadTranslations() { + if (root.isScanning) { + return + } + + var targetLang = getLanguageCode() + root.currentLanguage = targetLang + + // Use empty translations for English (default language) + if (targetLang === "en_US" || targetLang === "en") { + root.translations = {} + return + } + + // Check if target language is available + if (root.availableLanguages.indexOf(targetLang) === -1) { + root.currentLanguage = "en_US" + root.translations = {} + return + } + + // Load translation file + root.isLoading = true + var translationsPath = Qt.resolvedUrl(Directories.config + "/quickshell/translations/" + targetLang + ".json") + translationFileView.path = translationsPath + } + + function tr(text) { + if (!text) { + return "" + } + + var key = text.toString() + + if (root.isLoading) { + return key + } + + if (root.currentLanguage === "en_US" || root.currentLanguage === "en" || !root.translations) { + return key + } + + if (root.translations.hasOwnProperty(key)) { + var translation = root.translations[key] + if (translation && translation.toString().trim().length > 0) { + return translation.toString() + } else { + return translation.toString() + } + } + + return key // Fallback to key name + } + + function reloadTranslations() { + root.scanLanguages() + } + + function scanLanguages() { + var translationsDir = Qt.resolvedUrl(Directories.config + "/quickshell/translations/").toString().replace("file://", "") + root.isScanning = true + scanLanguagesProcess.running = true + } + + Component.onCompleted: { + root.scanLanguages() + } +} diff --git a/.config/quickshell/translations/en_US.json b/.config/quickshell/translations/en_US.json new file mode 100644 index 000000000..6f7c8ec50 --- /dev/null +++ b/.config/quickshell/translations/en_US.json @@ -0,0 +1,472 @@ +{ + "%1 characters": "%1 characters", + "**Pricing**: free. Data use policy varies depending on your OpenRouter account settings.\n\n**Instructions**: Log into OpenRouter account, go to Keys on the topright menu, click Create API Key": "**Pricing**: free. Data use policy varies depending on your OpenRouter account settings.\n\n**Instructions**: Log into OpenRouter account, go to Keys on the topright menu, click Create API Key", + "**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key": "**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key", + ". Notes for Zerochan:\n- You must enter a color\n- Set your zerochan username in `sidebar.booru.zerochan.username` config option. You [might be banned for not doing so](https://www.zerochan.net/api#:~:text=The%20request%20may%20still%20be%20completed%20successfully%20without%20this%20custom%20header%2C%20but%20your%20project%20may%20be%20banned%20for%20being%20anonymous.)!": ". Notes for Zerochan:\n- You must enter a color\n- Set your zerochan username in `sidebar.booru.zerochan.username` config option. You [might be banned for not doing so](https://www.zerochan.net/api#:~:text=The%20request%20may%20still%20be%20completed%20successfully%20without%20this%20custom%20header%2C%20but%20your%20project%20may%20be%20banned%20for%20being%20anonymous.)!", + "No further instruction provided": "No further instruction provided", + "API key set for {0}": "API key set for {0}", + "API key:\n\n```txt\n{0}\n```": "API key:\n\n```txt\n{0}\n```", + "Action": "Action", + "Add": "Add", + "Add task": "Add task", + "All-rounder | Good quality, decent quantity": "All-rounder | Good quality, decent quantity", + "Allow NSFW": "Allow NSFW", + "Allow NSFW content": "Allow NSFW content", + "Anime": "Anime", + "Anime boorus": "Anime boorus", + "App": "App", + "Arrow keys to navigate, Enter to select\nEsc or click anywhere to cancel": "Arrow keys to navigate, Enter to select\nEsc or click anywhere to cancel", + "Bluetooth": "Bluetooth", + "Brightness": "Brightness", + "Cancel": "Cancel", + "Chain of Thought": "Chain of Thought", + "Cheat sheet": "Cheat sheet", + "Choose model": "Choose model", + "Clean stuff | Excellent quality, no NSFW": "Clean stuff | Excellent quality, no NSFW", + "Clear": "Clear", + "Clear chat history": "Clear chat history", + "Clear the current list of images": "Clear the current list of images", + "Close": "Close", + "Closes cheatsheet on press": "Closes cheatsheet on press", + "Closes left sidebar on press": "Closes left sidebar on press", + "Closes media controls on press": "Closes media controls on press", + "Closes on screen keyboard on press": "Closes on screen keyboard on press", + "Closes overview": "Closes overview", + "Closes right sidebar on press": "Closes right sidebar on press", + "Copy": "Copy", + "Copy code": "Copy code", + "Ctrl+O to expand the sidebar\nCtrl+P to detach sidebar into a window": "Ctrl+O to expand the sidebar\nCtrl+P to detach sidebar into a window", + "Current API endpoint: {0}\nSet it with {1}mode PROVIDER": "Current API endpoint: {0}\nSet it with {1}mode PROVIDER", + "Current model: {0}\nSet it with {1}model MODEL": "Current model: {0}\nSet it with {1}model MODEL", + "Decrease brightness": "Decrease brightness", + "Delete": "Delete", + "Desktop": "Desktop", + "Detach left sidebar into a window/Attach it back": "Detach left sidebar into a window/Attach it back", + "Disable NSFW content": "Disable NSFW content", + "Done": "Done", + "Download": "Download", + "Download complete": "Download complete", + "Edit": "Edit", + "Enter text to translate...": "Enter text to translate...", + "Experimental | Online | Google's model\nCan do a little more but doesn't search quickly": "Experimental | Online | Google's model\nCan do a little more but doesn't search quickly", + "Finished tasks will go here": "Finished tasks will go here", + "For desktop wallpapers | Good quality": "For desktop wallpapers | Good quality", + "For storing API keys and other sensitive information": "For storing API keys and other sensitive information", + "Game mode": "Game mode", + "Get the next page of results": "Get the next page of results", + "Go to source ({0})": "Go to source ({0})", + "Hibernate": "Hibernate", + "Hides brightness OSD on press": "Hides brightness OSD on press", + "Hides volume OSD on press": "Hides volume OSD on press", + "Hold to show workspace numbers, release to show icons": "Hold to show workspace numbers, release to show icons", + "Increase brightness": "Increase brightness", + "Input": "Input", + "Intelligence": "Intelligence", + "Interface": "Interface", + "Interrupts possibility of overview being toggled on release. ": "Interrupts possibility of overview being toggled on release. ", + "Invalid API provider. Supported: \n- ": "Invalid API provider. Supported: \n- ", + "Invalid arguments. Must provide `key` and `value`.": "Invalid arguments. Must provide `key` and `value`.", + "Invalid model. Supported: \n```\n": "Invalid model. Supported: \n```\n", + "Jump to current month": "Jump to current month", + "Keep system awake": "Keep system awake", + "Large images | God tier quality, no NSFW.": "Large images | God tier quality, no NSFW.", + "Large language models": "Large language models", + "Launch": "Launch", + "Local Ollama model | {0}": "Local Ollama model | {0}", + "Lock": "Lock", + "Logout": "Logout", + "Markdown test": "Markdown test", + "Math result": "Math result", + "Night Light": "Night Light", + "No API key set for {0}": "No API key set for {0}", + "No audio source": "No audio source", + "No media": "No media", + "No notifications": "No notifications", + "Not visible to model": "Not visible to model", + "Nothing here!": "Nothing here!", + "Notifications": "Notifications", + "OK": "OK", + "Online via {0} | {1}'s model": "Online via {0} | {1}'s model", + "Online | Google's model\nGives up-to-date information with search.": "Online | Google's model\nGives up-to-date information with search.", + "Open file link": "Open file link", + "Opens cheatsheet on press": "Opens cheatsheet on press", + "Opens left sidebar on press": "Opens left sidebar on press", + "Opens media controls on press": "Opens media controls on press", + "Opens on screen keyboard on press": "Opens on screen keyboard on press", + "Opens right sidebar on press": "Opens right sidebar on press", + "Opens session screen on press": "Opens session screen on press", + "Output": "Output", + "Page {0}": "Page {0}", + "Plasma Settings": "Plasma Settings", + "Provider set to ": "Provider set to ", + "Reboot": "Reboot", + "Reboot to firmware settings": "Reboot to firmware settings", + "Reload Hyprland & Quickshell": "Reload Hyprland & Quickshell", + "Run": "Run", + "Run command": "Run command", + "Save": "Save", + "Save to Downloads": "Save to Downloads", + "Scroll to change brightness": "Scroll to change brightness", + "Scroll to change volume": "Scroll to change volume", + "Search": "Search", + "Search the web": "Search the web", + "Search, calculate or run": "Search, calculate or run", + "Select Language": "Select Language", + "Session": "Session", + "Set API key": "Set API key", + "Set temperature (randomness) of the model. Values range between 0 to 2 for Gemini, 0 to 1 for other models. Default is 0.5.": "Set temperature (randomness) of the model. Values range between 0 to 2 for Gemini, 0 to 1 for other models. Default is 0.5.", + "Set the current API provider": "Set the current API provider", + "Shell configuration created": "Shell configuration created", + "Shell configuration failed to load": "Shell configuration failed to load", + "Shell configuration reloaded": "Shell configuration reloaded", + "Shutdown": "Shutdown", + "Silent": "Silent", + "Sleep": "Sleep", + "Switched to search mode. Continue with the user's request.": "Switched to search mode. Continue with the user's request.", + "System": "System", + "Task Manager": "Task Manager", + "Task description": "Task description", + "Temperature must be between 0 and 2": "Temperature must be between 0 and 2", + "Temperature set to {0}": "Temperature set to {0}", + "Temperature: {0}": "Temperature: {0}", + "That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number": "That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number", + "The current API used. Endpoint: ": "The current API used. Endpoint: ", + "The hentai one | Great quantity, a lot of NSFW, quality varies wildly": "The hentai one | Great quantity, a lot of NSFW, quality varies wildly", + "The popular one | Best quantity, but quality can vary wildly": "The popular one | Best quantity, but quality can vary wildly", + "Thinking": "Thinking", + "This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. ": "This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. ", + "To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything.": "To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything.", + "Toggle clipboard query on overview widget": "Toggle clipboard query on overview widget", + "Toggle emoji query on overview widget": "Toggle emoji query on overview widget", + "Toggles cheatsheet on press": "Toggles cheatsheet on press", + "Toggles left sidebar on press": "Toggles left sidebar on press", + "Toggles media controls on press": "Toggles media controls on press", + "Toggles on screen keyboard on press": "Toggles on screen keyboard on press", + "Toggles overview on press": "Toggles overview on press", + "Toggles overview on release": "Toggles overview on release", + "Toggles right sidebar on press": "Toggles right sidebar on press", + "Toggles session screen on press": "Toggles session screen on press", + "Translation goes here...": "Translation goes here...", + "Translator": "Translator", + "Triggers brightness OSD on press": "Triggers brightness OSD on press", + "Triggers volume OSD on press": "Triggers volume OSD on press", + "Unfinished": "Unfinished", + "Unknown": "Unknown", + "Unknown Album": "Unknown Album", + "Unknown Artist": "Unknown Artist", + "Unknown Title": "Unknown Title", + "Unknown command: ": "Unknown command: ", + "Unknown function call: {0}": "Unknown function call: {0}", + "Uptime: {0}": "Uptime: {0}", + "Use casual tone. No user knowledge is to be assumed except basic Linux literacy. Be brief and concise: When explaining concepts, use bullet points (prefer minus sign (-) over asterisk (*)) and highlight keywords in **bold** to pinpoint the main concepts instead of long paragraphs. You are also encouraged to split your response with h2 headers, each header title beginning with an emoji, like `## 🐧 Linux`. When making changes to the user's config, you must get the config to know what values there are before setting.": "Use casual tone. No user knowledge is to be assumed except basic Linux literacy. Be brief and concise: When explaining concepts, use bullet points (prefer minus sign (-) over asterisk (*)) and highlight keywords in **bold** to pinpoint the main concepts instead of long paragraphs. You are also encouraged to split your response with h2 headers, each header title beginning with an emoji, like `## 🐧 Linux`. When making changes to the user's config, you must get the config to know what values there are before setting.", + "View Markdown source": "View Markdown source", + "Volume": "Volume", + "Volume mixer": "Volume mixer", + "Waifus only | Excellent quality, limited quantity": "Waifus only | Excellent quality, limited quantity", + "Waiting for response...": "Waiting for response...", + "Workspace": "Workspace", + "\nSet with /mode PROVIDER": "\nSet with /mode PROVIDER", + "about": "About", + "accessed": "Accessed", + "account": "Account", + "active": "Active", + "active_state": "Active", + "addon": "Add-on", + "addons": "Add-ons", + "address": "Address", + "admin": "Administrator", + "align": "Align", + "always_on_top": "Always on Top", + "animate": "Animate", + "app": "App", + "appearance": "Appearance", + "application": "Application", + "applications": "Applications", + "apply": "Apply", + "apps": "Apps", + "auto": "Auto", + "background": "Background", + "balanced": "Balanced", + "bar": "Bar", + "bars": "Bars", + "battery": "Battery", + "bluetooth": "Bluetooth", + "blur": "Blur", + "border": "Border", + "bottom": "Bottom", + "brightness": "Brightness", + "bring_to_front": "Bring to Front", + "bytes": "{0} bytes", + "cancel": "Cancel", + "center": "Center", + "characters": "{0} characters", + "charging": "Charging", + "clear_all": "Clear All", + "clicked": "Clicked", + "close": "Close", + "collapse": "Collapse", + "color": "Color", + "command": "Command", + "connected": "Connected", + "connecting": "Connecting...", + "context_menu": "Context Menu", + "copy": "Copy", + "cpu": "CPU", + "created": "Created", + "critical": "Critical", + "customize": "Customize", + "cut": "Cut", + "dark": "Dark", + "dashboard": "Dashboard", + "date": "Date", + "debug": "Debug", + "default": "Default", + "degrees": "{0}°", + "delete": "Delete", + "demo": "Demo", + "desktop": "Desktop", + "dialog": "Dialog", + "disabled": "Disabled", + "discharging": "Discharging", + "disconnected": "Disconnected", + "disk": "Disk", + "display": "Display", + "distribute": "Distribute", + "dock": "Dock", + "documents": "Documents", + "double_clicked": "Double Clicked", + "downloads": "Downloads", + "dragged": "Dragged", + "dropdown": "Dropdown", + "dropped": "Dropped", + "effect": "Effect", + "email": "Email", + "enabled": "Enabled", + "error": "Error", + "ethernet": "Ethernet", + "example": "Example", + "execute": "Execute", + "exit": "Exit", + "expand": "Expand", + "extension": "Extension", + "extensions": "Extensions", + "fan": "Fan", + "favorites": "Favorites", + "file": "File", + "files": "Files", + "filter": "Filter", + "flip": "Flip", + "floating": "Floating", + "focus": "Focus", + "folder": "Folder", + "folders": "Folders", + "foreground": "Foreground", + "format": "Format", + "full": "Full", + "fullscreen": "Fullscreen", + "gigabytes": "{0} GB", + "gigahertz": "{0} GHz", + "glow": "Glow", + "group": "Group", + "guest": "Guest", + "headphones": "Headphones", + "help": "Help", + "hertz": "{0} Hz", + "hibernate": "Hibernate", + "hidden": "Hidden", + "hide": "Hide", + "highlight": "Highlight", + "hint": "Hint", + "home": "Home", + "hour": "Hour", + "hours": "Hours", + "hover": "Hover", + "inactive": "Inactive", + "info": "Info", + "input": "Input", + "install": "Install", + "justify": "Justify", + "keybinds": "Key Bindings", + "keyboard": "Keyboard", + "kilobytes": "{0} KB", + "kilohertz": "{0} kHz", + "landscape": "Landscape", + "language": "Language", + "launcher": "Launcher", + "left": "Left", + "light": "Light", + "loading": "Loading...", + "lock": "Lock", + "log": "Log", + "logout": "Logout", + "long_pressed": "Long Pressed", + "low": "Low", + "manual": "Manual", + "margin": "Margin", + "maximize": "Maximize", + "megabytes": "{0} MB", + "megahertz": "{0} MHz", + "memory": "Memory", + "menu": "Menu", + "menubar": "Menu Bar", + "microphone": "Microphone", + "minimize": "Minimize", + "minute": "Minute", + "minutes": "Minutes", + "mirror": "Mirror", + "modal": "Modal", + "modified": "Modified", + "monitor": "Monitor", + "mouse": "Mouse", + "move": "Move", + "music": "Music", + "mute": "Mute", + "network": "Network", + "next": "Next", + "no": "No", + "no_notifications": "No notifications", + "notifications": "Notifications", + "off": "Off", + "ok": "OK", + "on": "On", + "opacity": "Opacity", + "open": "Open", + "orientation": "Orientation", + "outline": "Outline", + "output": "Output", + "overview": "Overview", + "owner": "Owner", + "package": "Package", + "packages": "Packages", + "padding": "Padding", + "panel": "Panel", + "panels": "Panels", + "panned": "Panned", + "password": "Password", + "paste": "Paste", + "pause": "Pause", + "percent": "{0}%", + "performance": "Performance", + "permissions": "Permissions", + "phone": "Phone", + "pictures": "Pictures", + "pinched": "Pinched", + "pinned": "Pinned", + "pixels": "{0}px", + "placeholder": "Placeholder", + "play": "Play", + "plugin": "Plugin", + "plugins": "Plugins", + "popup": "Popup", + "portrait": "Portrait", + "power": "Power", + "power_saver": "Power Saver", + "pressed": "Pressed", + "previous": "Previous", + "profile": "Profile", + "properties": "Properties", + "quiet": "Quiet", + "quit": "Quit", + "read": "Read", + "recent": "Recent", + "recording": "Recording", + "refresh": "Refresh", + "refresh_rate": "Refresh Rate", + "reload": "Reload", + "rename": "Rename", + "reset": "Reset", + "resize": "Resize", + "resolution": "Resolution", + "restart": "Restart", + "restore": "Restore", + "right": "Right", + "right_clicked": "Right Clicked", + "rotate": "Rotate", + "rotated": "Rotated", + "sample": "Sample", + "save": "Save", + "scale": "Scale", + "screenshot": "Screenshot", + "scrolled": "Scrolled", + "search": "Search", + "second": "Second", + "seconds": "Seconds", + "selection": "Selection", + "send_to_back": "Send to Back", + "settings": "Settings", + "shadow": "Shadow", + "shortcuts": "Shortcuts", + "show": "Show", + "shutdown": "Shutdown", + "sidebar": "Sidebar", + "silent": "Silent", + "size": "Size", + "snapped": "Snapped", + "software": "Software", + "space": "Space", + "speaker": "Speaker", + "statusbar": "Status Bar", + "sticky": "Sticky", + "stop": "Stop", + "style": "Style", + "success": "Success", + "suspend": "Suspend", + "swiped": "Swiped", + "systray": "System Tray", + "tapped": "Tapped", + "taskbar": "Taskbar", + "temperature": "Temperature", + "terabytes": "{0} TB", + "terminal": "Terminal", + "test": "Test", + "theme": "Theme", + "tiled": "Tiled", + "time": "Time", + "tips": "Tips", + "today": "Today", + "tomorrow": "Tomorrow", + "toolbar": "Toolbar", + "tooltip": "Tooltip", + "top": "Top", + "touchpad": "Touchpad", + "trace": "Trace", + "transform": "Transform", + "transition": "Transition", + "transparency": "Transparency", + "trash": "Trash", + "tutorial": "Tutorial", + "type": "Type", + "uninstall": "Uninstall", + "unlock": "Unlock", + "unmute": "Unmute", + "unpinned": "Unpinned", + "unsticky": "Unsticky", + "update": "Update", + "upgrade": "Upgrade", + "user": "User", + "username": "Username", + "verbose": "Verbose", + "version": "Version", + "videos": "Videos", + "visible": "Visible", + "volume": "Volume", + "warning": "Warning", + "welcome": "Welcome", + "widget": "Widget", + "widgets": "Widgets", + "wifi": "Wi-Fi", + "window": "Window", + "windowed": "Windowed", + "windows": "Windows", + "workspace": "Workspace", + "workspaces": "Workspaces", + "write": "Write", + "yes": "Yes", + "yesterday": "Yesterday", + "zoomed": "Zoomed", + "{0} (copied)": "{0} (copied)", + "{0} Safe Storage": "{0} Safe Storage", + "{0} does not require an API key": "{0} does not require an API key", + "{0} queries pending": "{0} queries pending", + "{0} | Right-click to configure": "{0} | Right-click to configure" +} \ No newline at end of file diff --git a/.config/quickshell/translations/zh_CN.json b/.config/quickshell/translations/zh_CN.json new file mode 100644 index 000000000..f73a6949f --- /dev/null +++ b/.config/quickshell/translations/zh_CN.json @@ -0,0 +1,472 @@ +{ + "%1 characters": "%1 个字符", + "**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key": "**价格**:免费。数据用于训练。\n\n**说明**:登录 Google 账户,允许 AI Studio 创建 Google Cloud 项目或其他要求,然后返回并点击获取 API 密钥", + "**Pricing**: free. Data use policy varies depending on your OpenRouter account settings.\n\n**Instructions**: Log into OpenRouter account, go to Keys on the topright menu, click Create API Key": "**价格**:免费。数据使用政策取决于您的 OpenRouter 账户设置。\n\n**说明**:登录 OpenRouter 账户,在右上角菜单中选择 Keys,点击创建 API 密钥", + ". Notes for Zerochan:\n- You must enter a color\n- Set your zerochan username in `sidebar.booru.zerochan.username` config option. You [might be banned for not doing so](https://www.zerochan.net/api#:~:text=The%20request%20may%20still%20be%20completed%20successfully%20without%20this%20custom%20header%2C%20but%20your%20project%20may%20be%20banned%20for%20being%20anonymous.)!": ". Notes for Zerochan:\n- You must enter a color\n- Set your zerochan username in `sidebar.booru.zerochan.username` config option. You [might be banned for not doing so](https://www.zerochan.net/api#:~:text=The%20request%20may%20still%20be%20completed%20successfully%20without%20this%20custom%20header%2C%20but%20your%20project%20may%20be%20banned%20for%20being%20anonymous.)!", + "No further instruction provided": "未提供进一步说明", + "API key set for {0}": "已为 {0} 设置 API 密钥", + "API key:\n\n```txt\n{0}\n```": "API 密钥:\n\n```txt\n{0}\n```", + "Action": "操作", + "Add": "添加", + "Add task": "添加任务", + "All-rounder | Good quality, decent quantity": "全能型 | 质量好,数量适中", + "Allow NSFW": "允许 NSFW", + "Allow NSFW content": "允许 NSFW 内容", + "Anime": "动漫", + "Anime boorus": "动漫图库", + "App": "应用", + "Arrow keys to navigate, Enter to select\nEsc or click anywhere to cancel": "方向键导航,回车选择\nEsc 或点击任意地方取消", + "Bluetooth": "蓝牙", + "Brightness": "亮度", + "Cancel": "取消", + "Chain of Thought": "思维链", + "Cheat sheet": "快捷键表", + "Choose model": "选择模型", + "Clean stuff | Excellent quality, no NSFW": "清洁内容 | 优秀质量,无 NSFW", + "Clear": "清除", + "Clear chat history": "清除聊天记录", + "Clear the current list of images": "清除当前图片列表", + "Close": "关闭", + "Closes cheatsheet on press": "Closes cheatsheet on press", + "Closes left sidebar on press": "Closes left sidebar on press", + "Closes media controls on press": "Closes media controls on press", + "Closes on screen keyboard on press": "Closes on screen keyboard on press", + "Closes overview": "Closes overview", + "Closes right sidebar on press": "Closes right sidebar on press", + "Copy": "复制", + "Copy code": "复制代码", + "Ctrl+O to expand the sidebar\nCtrl+P to detach sidebar into a window": "Ctrl+O 展开侧边栏\nCtrl+P 将侧边栏分离为窗口", + "Current API endpoint: {0}\nSet it with {1}mode PROVIDER": "当前 API 端点:{0}\n使用 {1}mode PROVIDER 设置", + "Current model: {0}\nSet it with {1}model MODEL": "当前模型:{0}\n使用 {1}model MODEL 设置", + "Decrease brightness": "Decrease brightness", + "Delete": "删除", + "Desktop": "桌面", + "Detach left sidebar into a window/Attach it back": "Detach left sidebar into a window/Attach it back", + "Disable NSFW content": "禁用 NSFW 内容", + "Done": "完成", + "Download": "下载", + "Download complete": "下载完成", + "Edit": "编辑", + "Enter text to translate...": "输入要翻译的文本...", + "Experimental | Online | Google's model\nCan do a little more but doesn't search quickly": "实验性 | 在线 | Google 模型\n功能更多但搜索速度较慢", + "Finished tasks will go here": "已完成的任务将显示在这里", + "For desktop wallpapers | Good quality": "桌面壁纸专用 | 质量好", + "For storing API keys and other sensitive information": "用于存储 API 密钥和其他敏感信息", + "Game mode": "游戏模式", + "Get the next page of results": "获取下一页结果", + "Go to source ({0})": "转到源 ({0})", + "Hibernate": "休眠", + "Hides brightness OSD on press": "Hides brightness OSD on press", + "Hides volume OSD on press": "Hides volume OSD on press", + "Hold to show workspace numbers, release to show icons": "Hold to show workspace numbers, release to show icons", + "Increase brightness": "Increase brightness", + "Input": "输入", + "Intelligence": "智能", + "Interface": "界面", + "Interrupts possibility of overview being toggled on release. ": "Interrupts possibility of overview being toggled on release. ", + "Invalid API provider. Supported: \n- ": "无效的 API 提供商。支持的:\n- ", + "Invalid arguments. Must provide `key` and `value`.": "参数无效。必须提供 `key` 和 `value`。", + "Invalid model. Supported: \n```\n": "无效模型。支持的:\n```\n", + "Jump to current month": "跳转到当前月份", + "Keep system awake": "保持系统唤醒", + "Large images | God tier quality, no NSFW.": "大尺寸图片 | 顶级质量,无 NSFW", + "Large language models": "大语言模型", + "Launch": "启动", + "Local Ollama model | {0}": "本地 Ollama 模型 | {0}", + "Lock": "锁定", + "Logout": "注销", + "Markdown test": "Markdown 测试", + "Math result": "数学结果", + "Night Light": "护眼模式", + "No API key set for {0}": "未为 {0} 设置 API 密钥", + "No audio source": "无音频源", + "No media": "无媒体", + "No notifications": "无通知", + "Not visible to model": "对模型不可见", + "Nothing here!": "这里什么都没有!", + "Notifications": "通知", + "OK": "确定", + "Online via {0} | {1}'s model": "通过 {0} 在线 | {1} 模型", + "Online | Google's model\nGives up-to-date information with search.": "在线 | Google 模型\n通过搜索提供最新信息。", + "Open file link": "打开文件链接", + "Opens cheatsheet on press": "Opens cheatsheet on press", + "Opens left sidebar on press": "Opens left sidebar on press", + "Opens media controls on press": "Opens media controls on press", + "Opens on screen keyboard on press": "Opens on screen keyboard on press", + "Opens right sidebar on press": "Opens right sidebar on press", + "Opens session screen on press": "Opens session screen on press", + "Output": "输出", + "Page {0}": "第 {0} 页", + "Plasma Settings": "Plasma 设置", + "Provider set to ": "提供商设置为", + "Reboot": "重启", + "Reboot to firmware settings": "Reboot to firmware settings", + "Reload Hyprland & Quickshell": "重新加载 Hyprland 和 Quickshell", + "Run": "运行", + "Run command": "Run command", + "Save": "保存", + "Save to Downloads": "保存到下载文件夹", + "Scroll to change brightness": "滚动调节亮度", + "Scroll to change volume": "滚动调节音量", + "Search": "搜索", + "Search the web": "搜索网络", + "Search, calculate or run": "搜索、计算或运行", + "Select Language": "选择语言", + "Session": "会话", + "Set API key": "设置 API 密钥", + "Set temperature (randomness) of the model. Values range between 0 to 2 for Gemini, 0 to 1 for other models. Default is 0.5.": "设置模型的温度(随机性)。Gemini 模型范围为 0 到 2,其他模型为 0 到 1。默认值为 0.5。", + "Set the current API provider": "设置当前 API 提供商", + "Shell configuration created": "Shell 配置已创建", + "Shell configuration failed to load": "Shell 配置加载失败", + "Shell configuration reloaded": "Shell 配置已重新加载", + "Shutdown": "关机", + "Silent": "静音", + "Sleep": "睡眠", + "Switched to search mode. Continue with the user's request.": "已切换到搜索模式。继续处理用户请求。", + "System": "系统", + "Task Manager": "任务管理器", + "Task description": "任务描述", + "Temperature must be between 0 and 2": "温度必须在 0 到 2 之间", + "Temperature set to {0}": "温度设置为 {0}", + "Temperature: {0}": "温度:{0}", + "That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number": "没有找到结果。提示:\n- 检查您的标签和 NSFW 设置\n- 如果没有想到标签,请输入页码", + "The current API used. Endpoint: ": "当前使用的 API。端点:", + "The hentai one | Great quantity, a lot of NSFW, quality varies wildly": "成人向 | 数量巨大,大量 NSFW,质量参差不齐", + "The popular one | Best quantity, but quality can vary wildly": "最受欢迎 | 数量最多,但质量参差不齐", + "Thinking": "思考中", + "This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. ": "This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. ", + "To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything.": "To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything.", + "Toggle clipboard query on overview widget": "Toggle clipboard query on overview widget", + "Toggle emoji query on overview widget": "Toggle emoji query on overview widget", + "Toggles cheatsheet on press": "Toggles cheatsheet on press", + "Toggles left sidebar on press": "Toggles left sidebar on press", + "Toggles media controls on press": "Toggles media controls on press", + "Toggles on screen keyboard on press": "Toggles on screen keyboard on press", + "Toggles overview on press": "Toggles overview on press", + "Toggles overview on release": "Toggles overview on release", + "Toggles right sidebar on press": "Toggles right sidebar on press", + "Toggles session screen on press": "Toggles session screen on press", + "Translation goes here...": "翻译结果会显示在这里...", + "Translator": "翻译器", + "Triggers brightness OSD on press": "Triggers brightness OSD on press", + "Triggers volume OSD on press": "Triggers volume OSD on press", + "Unfinished": "未完成", + "Unknown": "未知", + "Unknown Album": "未知专辑", + "Unknown Artist": "未知艺术家", + "Unknown Title": "未知标题", + "Unknown command: ": "未知命令:", + "Unknown function call: {0}": "未知函数调用:{0}", + "Uptime: {0}": "运行时间:{0}", + "Use casual tone. No user knowledge is to be assumed except basic Linux literacy. Be brief and concise: When explaining concepts, use bullet points (prefer minus sign (-) over asterisk (*)) and highlight keywords in **bold** to pinpoint the main concepts instead of long paragraphs. You are also encouraged to split your response with h2 headers, each header title beginning with an emoji, like `## 🐧 Linux`. When making changes to the user's config, you must get the config to know what values there are before setting.": "Use casual tone. No user knowledge is to be assumed except basic Linux literacy. Be brief and concise: When explaining concepts, use bullet points (prefer minus sign (-) over asterisk (*)) and highlight keywords in **bold** to pinpoint the main concepts instead of long paragraphs. You are also encouraged to split your response with h2 headers, each header title beginning with an emoji, like `## 🐧 Linux`. When making changes to the user's config, you must get the config to know what values there are before setting.", + "View Markdown source": "查看 Markdown 源码", + "Volume": "音量", + "Volume mixer": "音量混合器", + "Waifus only | Excellent quality, limited quantity": "仅限角色 | 优秀质量,数量有限", + "Waiting for response...": "等待响应...", + "Workspace": "工作区", + "\nSet with /mode PROVIDER": "\n使用 /mode PROVIDER 设置", + "about": "关于", + "accessed": "访问时间", + "account": "账户", + "active": "活动", + "active_state": "活动状态", + "addon": "附加组件", + "addons": "附加组件", + "address": "地址", + "admin": "管理员", + "align": "对齐", + "always_on_top": "总在最前", + "animate": "动画", + "app": "应用", + "appearance": "外观", + "application": "应用程序", + "applications": "应用程序", + "apply": "应用", + "apps": "应用", + "auto": "自动", + "background": "背景", + "balanced": "平衡", + "bar": "栏", + "bars": "栏", + "battery": "电池", + "bluetooth": "蓝牙", + "blur": "模糊", + "border": "边框", + "bottom": "底部", + "brightness": "亮度", + "bring_to_front": "置于顶层", + "bytes": "{0} 字节", + "cancel": "取消", + "center": "居中", + "characters": "{0} 个字符", + "charging": "充电中", + "clear_all": "清除全部", + "clicked": "点击", + "close": "关闭", + "collapse": "折叠", + "color": "颜色", + "command": "命令", + "connected": "已连接", + "connecting": "连接中...", + "context_menu": "右键菜单", + "copy": "复制", + "cpu": "处理器", + "created": "创建时间", + "critical": "电量危险", + "customize": "自定义", + "cut": "剪切", + "dark": "深色", + "dashboard": "仪表板", + "date": "日期", + "debug": "调试", + "default": "默认", + "degrees": "{0}°", + "delete": "删除", + "demo": "演示", + "desktop": "桌面", + "dialog": "对话框", + "disabled": "已禁用", + "discharging": "放电中", + "disconnected": "已断开", + "disk": "磁盘", + "display": "显示", + "distribute": "分布", + "dock": "停靠栏", + "documents": "文档", + "double_clicked": "双击", + "downloads": "下载", + "dragged": "拖拽", + "dropdown": "下拉菜单", + "dropped": "拖放", + "effect": "效果", + "email": "邮箱", + "enabled": "已启用", + "error": "错误", + "ethernet": "以太网", + "example": "示例", + "execute": "执行", + "exit": "退出", + "expand": "展开", + "extension": "扩展", + "extensions": "扩展", + "fan": "风扇", + "favorites": "收藏夹", + "file": "文件", + "files": "文件", + "filter": "滤镜", + "flip": "翻转", + "floating": "浮动", + "focus": "焦点", + "folder": "文件夹", + "folders": "文件夹", + "foreground": "前景", + "format": "格式", + "full": "已充满", + "fullscreen": "全屏", + "gigabytes": "{0} GB", + "gigahertz": "{0} GHz", + "glow": "发光", + "group": "群组", + "guest": "访客", + "headphones": "耳机", + "help": "帮助", + "hertz": "{0} Hz", + "hibernate": "休眠", + "hidden": "隐藏", + "hide": "隐藏", + "highlight": "高亮", + "hint": "提示", + "home": "主目录", + "hour": "小时", + "hours": "小时", + "hover": "悬停", + "inactive": "非活动", + "info": "信息", + "input": "输入", + "install": "安装", + "justify": "两端对齐", + "keybinds": "按键绑定", + "keyboard": "键盘", + "kilobytes": "{0} KB", + "kilohertz": "{0} kHz", + "landscape": "横向", + "language": "语言", + "launcher": "启动器", + "left": "左对齐", + "light": "浅色", + "loading": "加载中...", + "lock": "锁定", + "log": "日志", + "logout": "注销", + "long_pressed": "长按", + "low": "电量低", + "manual": "手册", + "margin": "边距", + "maximize": "最大化", + "megabytes": "{0} MB", + "megahertz": "{0} MHz", + "memory": "内存", + "menu": "菜单", + "menubar": "菜单栏", + "microphone": "麦克风", + "minimize": "最小化", + "minute": "分钟", + "minutes": "分钟", + "mirror": "镜像", + "modal": "模态框", + "modified": "修改时间", + "monitor": "显示器", + "mouse": "鼠标", + "move": "移动", + "music": "音乐", + "mute": "静音", + "network": "网络", + "next": "下一个", + "no": "否", + "no_notifications": "无通知", + "notifications": "通知", + "off": "关", + "ok": "确定", + "on": "开", + "opacity": "不透明度", + "open": "打开", + "orientation": "方向", + "outline": "轮廓", + "output": "输出", + "overview": "概览", + "owner": "所有者", + "package": "软件包", + "packages": "软件包", + "padding": "内边距", + "panel": "面板", + "panels": "面板", + "panned": "平移", + "password": "密码", + "paste": "粘贴", + "pause": "暂停", + "percent": "{0}%", + "performance": "性能", + "permissions": "权限", + "phone": "电话", + "pictures": "图片", + "pinched": "捏合", + "pinned": "固定", + "pixels": "{0}像素", + "placeholder": "占位符", + "play": "播放", + "plugin": "插件", + "plugins": "插件", + "popup": "弹出窗口", + "portrait": "纵向", + "power": "电源", + "power_saver": "节能", + "pressed": "按下", + "previous": "上一个", + "profile": "配置文件", + "properties": "属性", + "quiet": "安静", + "quit": "退出", + "read": "读取", + "recent": "最近", + "recording": "录制", + "refresh": "刷新", + "refresh_rate": "刷新率", + "reload": "重新加载", + "rename": "重命名", + "reset": "重置", + "resize": "调整大小", + "resolution": "分辨率", + "restart": "重启", + "restore": "还原", + "right": "右对齐", + "right_clicked": "右键点击", + "rotate": "旋转", + "rotated": "旋转", + "sample": "样本", + "save": "保存", + "scale": "缩放", + "screenshot": "截图", + "scrolled": "滚动", + "search": "搜索", + "second": "秒", + "seconds": "秒", + "selection": "选择", + "send_to_back": "置于底层", + "settings": "设置", + "shadow": "阴影", + "shortcuts": "快捷键", + "show": "显示", + "shutdown": "关机", + "sidebar": "侧边栏", + "silent": "静默", + "size": "大小", + "snapped": "贴靠", + "software": "软件", + "space": "间距", + "speaker": "扬声器", + "statusbar": "状态栏", + "sticky": "粘性", + "stop": "停止", + "style": "样式", + "success": "成功", + "suspend": "挂起", + "swiped": "滑动", + "systray": "系统托盘", + "tapped": "轻触", + "taskbar": "任务栏", + "temperature": "温度", + "terabytes": "{0} TB", + "terminal": "终端", + "test": "测试", + "theme": "主题", + "tiled": "平铺", + "time": "时间", + "tips": "提示", + "today": "今天", + "tomorrow": "明天", + "toolbar": "工具栏", + "tooltip": "工具提示", + "top": "顶部", + "touchpad": "触摸板", + "trace": "跟踪", + "transform": "变换", + "transition": "过渡", + "transparency": "透明度", + "trash": "回收站", + "tutorial": "教程", + "type": "类型", + "uninstall": "卸载", + "unlock": "解锁", + "unmute": "取消静音", + "unpinned": "取消固定", + "unsticky": "取消粘性", + "update": "更新", + "upgrade": "升级", + "user": "用户", + "username": "用户名", + "verbose": "详细", + "version": "版本", + "videos": "视频", + "visible": "可见", + "volume": "音量", + "warning": "警告", + "welcome": "欢迎", + "widget": "组件", + "widgets": "组件", + "wifi": "Wi-Fi", + "window": "窗口", + "windowed": "窗口化", + "windows": "窗口", + "workspace": "工作区", + "workspaces": "工作区", + "write": "写入", + "yes": "是", + "yesterday": "昨天", + "zoomed": "缩放", + "{0} (copied)": "{0}(已复制)", + "{0} Safe Storage": "{0} 安全存储", + "{0} does not require an API key": "{0} 不需要 API 密钥", + "{0} queries pending": "{0} 个查询等待中", + "{0} | Right-click to configure": "{0} | 右键点击进行配置" +} \ No newline at end of file From 89e25e3504b2e76ff24803ea96914893a595bd1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=88=E6=9C=88?= <3600911665@qq.com> Date: Tue, 17 Jun 2025 14:29:33 +0800 Subject: [PATCH 02/20] Add translation management tool suite and update Chinese translations - Introduced a comprehensive guide for the translation management tool suite, detailing components, usage, and best practices. - Added `translation-manager.py`, `translation-cleaner.py`, and `manage-translations.sh` scripts for managing translations. - Updated the Chinese translation file (`zh_CN.json`) to improve existing translations and remove unused keys. - Enhanced documentation with examples and troubleshooting tips for better user experience. --- .config/quickshell/translations/en_US.json | 332 +-------------- .../quickshell/translations/tools/README.md | 111 +++++ .../translations/tools/manage-translations.sh | 149 +++++++ .../translations/tools/translation-cleaner.py | 196 +++++++++ .../translations/tools/translation-manager.py | 290 +++++++++++++ .../tools/translation-tools-guide.md | 246 +++++++++++ .../translations/tools/翻译管理脚本说明.md | 287 +++++++++++++ .config/quickshell/translations/zh_CN.json | 398 +++--------------- 8 files changed, 1348 insertions(+), 661 deletions(-) create mode 100644 .config/quickshell/translations/tools/README.md create mode 100755 .config/quickshell/translations/tools/manage-translations.sh create mode 100755 .config/quickshell/translations/tools/translation-cleaner.py create mode 100755 .config/quickshell/translations/tools/translation-manager.py create mode 100644 .config/quickshell/translations/tools/translation-tools-guide.md create mode 100644 .config/quickshell/translations/tools/翻译管理脚本说明.md diff --git a/.config/quickshell/translations/en_US.json b/.config/quickshell/translations/en_US.json index 6f7c8ec50..34887b99b 100644 --- a/.config/quickshell/translations/en_US.json +++ b/.config/quickshell/translations/en_US.json @@ -35,7 +35,6 @@ "Closes right sidebar on press": "Closes right sidebar on press", "Copy": "Copy", "Copy code": "Copy code", - "Ctrl+O to expand the sidebar\nCtrl+P to detach sidebar into a window": "Ctrl+O to expand the sidebar\nCtrl+P to detach sidebar into a window", "Current API endpoint: {0}\nSet it with {1}mode PROVIDER": "Current API endpoint: {0}\nSet it with {1}mode PROVIDER", "Current model: {0}\nSet it with {1}model MODEL": "Current model: {0}\nSet it with {1}model MODEL", "Decrease brightness": "Decrease brightness", @@ -48,7 +47,6 @@ "Download complete": "Download complete", "Edit": "Edit", "Enter text to translate...": "Enter text to translate...", - "Experimental | Online | Google's model\nCan do a little more but doesn't search quickly": "Experimental | Online | Google's model\nCan do a little more but doesn't search quickly", "Finished tasks will go here": "Finished tasks will go here", "For desktop wallpapers | Good quality": "For desktop wallpapers | Good quality", "For storing API keys and other sensitive information": "For storing API keys and other sensitive information", @@ -63,10 +61,7 @@ "Input": "Input", "Intelligence": "Intelligence", "Interface": "Interface", - "Interrupts possibility of overview being toggled on release. ": "Interrupts possibility of overview being toggled on release. ", - "Invalid API provider. Supported: \n- ": "Invalid API provider. Supported: \n- ", "Invalid arguments. Must provide `key` and `value`.": "Invalid arguments. Must provide `key` and `value`.", - "Invalid model. Supported: \n```\n": "Invalid model. Supported: \n```\n", "Jump to current month": "Jump to current month", "Keep system awake": "Keep system awake", "Large images | God tier quality, no NSFW.": "Large images | God tier quality, no NSFW.", @@ -86,8 +81,6 @@ "Nothing here!": "Nothing here!", "Notifications": "Notifications", "OK": "OK", - "Online via {0} | {1}'s model": "Online via {0} | {1}'s model", - "Online | Google's model\nGives up-to-date information with search.": "Online | Google's model\nGives up-to-date information with search.", "Open file link": "Open file link", "Opens cheatsheet on press": "Opens cheatsheet on press", "Opens left sidebar on press": "Opens left sidebar on press", @@ -98,7 +91,6 @@ "Output": "Output", "Page {0}": "Page {0}", "Plasma Settings": "Plasma Settings", - "Provider set to ": "Provider set to ", "Reboot": "Reboot", "Reboot to firmware settings": "Reboot to firmware settings", "Reload Hyprland & Quickshell": "Reload Hyprland & Quickshell", @@ -122,19 +114,15 @@ "Shutdown": "Shutdown", "Silent": "Silent", "Sleep": "Sleep", - "Switched to search mode. Continue with the user's request.": "Switched to search mode. Continue with the user's request.", "System": "System", "Task Manager": "Task Manager", "Task description": "Task description", "Temperature must be between 0 and 2": "Temperature must be between 0 and 2", "Temperature set to {0}": "Temperature set to {0}", "Temperature: {0}": "Temperature: {0}", - "That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number": "That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number", - "The current API used. Endpoint: ": "The current API used. Endpoint: ", "The hentai one | Great quantity, a lot of NSFW, quality varies wildly": "The hentai one | Great quantity, a lot of NSFW, quality varies wildly", "The popular one | Best quantity, but quality can vary wildly": "The popular one | Best quantity, but quality can vary wildly", "Thinking": "Thinking", - "This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. ": "This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. ", "To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything.": "To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything.", "Toggle clipboard query on overview widget": "Toggle clipboard query on overview widget", "Toggle emoji query on overview widget": "Toggle emoji query on overview widget", @@ -155,318 +143,34 @@ "Unknown Album": "Unknown Album", "Unknown Artist": "Unknown Artist", "Unknown Title": "Unknown Title", - "Unknown command: ": "Unknown command: ", "Unknown function call: {0}": "Unknown function call: {0}", "Uptime: {0}": "Uptime: {0}", - "Use casual tone. No user knowledge is to be assumed except basic Linux literacy. Be brief and concise: When explaining concepts, use bullet points (prefer minus sign (-) over asterisk (*)) and highlight keywords in **bold** to pinpoint the main concepts instead of long paragraphs. You are also encouraged to split your response with h2 headers, each header title beginning with an emoji, like `## 🐧 Linux`. When making changes to the user's config, you must get the config to know what values there are before setting.": "Use casual tone. No user knowledge is to be assumed except basic Linux literacy. Be brief and concise: When explaining concepts, use bullet points (prefer minus sign (-) over asterisk (*)) and highlight keywords in **bold** to pinpoint the main concepts instead of long paragraphs. You are also encouraged to split your response with h2 headers, each header title beginning with an emoji, like `## 🐧 Linux`. When making changes to the user's config, you must get the config to know what values there are before setting.", "View Markdown source": "View Markdown source", "Volume": "Volume", "Volume mixer": "Volume mixer", "Waifus only | Excellent quality, limited quantity": "Waifus only | Excellent quality, limited quantity", "Waiting for response...": "Waiting for response...", "Workspace": "Workspace", - "\nSet with /mode PROVIDER": "\nSet with /mode PROVIDER", - "about": "About", - "accessed": "Accessed", - "account": "Account", - "active": "Active", - "active_state": "Active", - "addon": "Add-on", - "addons": "Add-ons", - "address": "Address", - "admin": "Administrator", - "align": "Align", - "always_on_top": "Always on Top", - "animate": "Animate", - "app": "App", - "appearance": "Appearance", - "application": "Application", - "applications": "Applications", - "apply": "Apply", - "apps": "Apps", - "auto": "Auto", - "background": "Background", - "balanced": "Balanced", - "bar": "Bar", - "bars": "Bars", - "battery": "Battery", - "bluetooth": "Bluetooth", - "blur": "Blur", - "border": "Border", - "bottom": "Bottom", - "brightness": "Brightness", - "bring_to_front": "Bring to Front", - "bytes": "{0} bytes", - "cancel": "Cancel", - "center": "Center", - "characters": "{0} characters", - "charging": "Charging", - "clear_all": "Clear All", - "clicked": "Clicked", - "close": "Close", - "collapse": "Collapse", - "color": "Color", - "command": "Command", - "connected": "Connected", - "connecting": "Connecting...", - "context_menu": "Context Menu", - "copy": "Copy", - "cpu": "CPU", - "created": "Created", - "critical": "Critical", - "customize": "Customize", - "cut": "Cut", - "dark": "Dark", - "dashboard": "Dashboard", - "date": "Date", - "debug": "Debug", - "default": "Default", - "degrees": "{0}°", - "delete": "Delete", - "demo": "Demo", - "desktop": "Desktop", - "dialog": "Dialog", - "disabled": "Disabled", - "discharging": "Discharging", - "disconnected": "Disconnected", - "disk": "Disk", - "display": "Display", - "distribute": "Distribute", - "dock": "Dock", - "documents": "Documents", - "double_clicked": "Double Clicked", - "downloads": "Downloads", - "dragged": "Dragged", - "dropdown": "Dropdown", - "dropped": "Dropped", - "effect": "Effect", - "email": "Email", - "enabled": "Enabled", - "error": "Error", - "ethernet": "Ethernet", - "example": "Example", - "execute": "Execute", - "exit": "Exit", - "expand": "Expand", - "extension": "Extension", - "extensions": "Extensions", - "fan": "Fan", - "favorites": "Favorites", - "file": "File", - "files": "Files", - "filter": "Filter", - "flip": "Flip", - "floating": "Floating", - "focus": "Focus", - "folder": "Folder", - "folders": "Folders", - "foreground": "Foreground", - "format": "Format", - "full": "Full", - "fullscreen": "Fullscreen", - "gigabytes": "{0} GB", - "gigahertz": "{0} GHz", - "glow": "Glow", - "group": "Group", - "guest": "Guest", - "headphones": "Headphones", - "help": "Help", - "hertz": "{0} Hz", - "hibernate": "Hibernate", - "hidden": "Hidden", - "hide": "Hide", - "highlight": "Highlight", - "hint": "Hint", - "home": "Home", - "hour": "Hour", - "hours": "Hours", - "hover": "Hover", - "inactive": "Inactive", - "info": "Info", - "input": "Input", - "install": "Install", - "justify": "Justify", - "keybinds": "Key Bindings", - "keyboard": "Keyboard", - "kilobytes": "{0} KB", - "kilohertz": "{0} kHz", - "landscape": "Landscape", - "language": "Language", - "launcher": "Launcher", - "left": "Left", - "light": "Light", - "loading": "Loading...", - "lock": "Lock", - "log": "Log", - "logout": "Logout", - "long_pressed": "Long Pressed", - "low": "Low", - "manual": "Manual", - "margin": "Margin", - "maximize": "Maximize", - "megabytes": "{0} MB", - "megahertz": "{0} MHz", - "memory": "Memory", - "menu": "Menu", - "menubar": "Menu Bar", - "microphone": "Microphone", - "minimize": "Minimize", - "minute": "Minute", - "minutes": "Minutes", - "mirror": "Mirror", - "modal": "Modal", - "modified": "Modified", - "monitor": "Monitor", - "mouse": "Mouse", - "move": "Move", - "music": "Music", - "mute": "Mute", - "network": "Network", - "next": "Next", - "no": "No", - "no_notifications": "No notifications", - "notifications": "Notifications", - "off": "Off", - "ok": "OK", - "on": "On", - "opacity": "Opacity", - "open": "Open", - "orientation": "Orientation", - "outline": "Outline", - "output": "Output", - "overview": "Overview", - "owner": "Owner", - "package": "Package", - "packages": "Packages", - "padding": "Padding", - "panel": "Panel", - "panels": "Panels", - "panned": "Panned", - "password": "Password", - "paste": "Paste", - "pause": "Pause", - "percent": "{0}%", - "performance": "Performance", - "permissions": "Permissions", - "phone": "Phone", - "pictures": "Pictures", - "pinched": "Pinched", - "pinned": "Pinned", - "pixels": "{0}px", - "placeholder": "Placeholder", - "play": "Play", - "plugin": "Plugin", - "plugins": "Plugins", - "popup": "Popup", - "portrait": "Portrait", - "power": "Power", - "power_saver": "Power Saver", - "pressed": "Pressed", - "previous": "Previous", - "profile": "Profile", - "properties": "Properties", - "quiet": "Quiet", - "quit": "Quit", - "read": "Read", - "recent": "Recent", - "recording": "Recording", - "refresh": "Refresh", - "refresh_rate": "Refresh Rate", - "reload": "Reload", - "rename": "Rename", - "reset": "Reset", - "resize": "Resize", - "resolution": "Resolution", - "restart": "Restart", - "restore": "Restore", - "right": "Right", - "right_clicked": "Right Clicked", - "rotate": "Rotate", - "rotated": "Rotated", - "sample": "Sample", - "save": "Save", - "scale": "Scale", - "screenshot": "Screenshot", - "scrolled": "Scrolled", - "search": "Search", - "second": "Second", - "seconds": "Seconds", - "selection": "Selection", - "send_to_back": "Send to Back", - "settings": "Settings", - "shadow": "Shadow", - "shortcuts": "Shortcuts", - "show": "Show", - "shutdown": "Shutdown", - "sidebar": "Sidebar", - "silent": "Silent", - "size": "Size", - "snapped": "Snapped", - "software": "Software", - "space": "Space", - "speaker": "Speaker", - "statusbar": "Status Bar", - "sticky": "Sticky", - "stop": "Stop", - "style": "Style", - "success": "Success", - "suspend": "Suspend", - "swiped": "Swiped", - "systray": "System Tray", - "tapped": "Tapped", - "taskbar": "Taskbar", - "temperature": "Temperature", - "terabytes": "{0} TB", - "terminal": "Terminal", - "test": "Test", - "theme": "Theme", - "tiled": "Tiled", - "time": "Time", - "tips": "Tips", - "today": "Today", - "tomorrow": "Tomorrow", - "toolbar": "Toolbar", - "tooltip": "Tooltip", - "top": "Top", - "touchpad": "Touchpad", - "trace": "Trace", - "transform": "Transform", - "transition": "Transition", - "transparency": "Transparency", - "trash": "Trash", - "tutorial": "Tutorial", - "type": "Type", - "uninstall": "Uninstall", - "unlock": "Unlock", - "unmute": "Unmute", - "unpinned": "Unpinned", - "unsticky": "Unsticky", - "update": "Update", - "upgrade": "Upgrade", - "user": "User", - "username": "Username", - "verbose": "Verbose", - "version": "Version", - "videos": "Videos", - "visible": "Visible", - "volume": "Volume", - "warning": "Warning", - "welcome": "Welcome", - "widget": "Widget", - "widgets": "Widgets", - "wifi": "Wi-Fi", - "window": "Window", - "windowed": "Windowed", - "windows": "Windows", - "workspace": "Workspace", - "workspaces": "Workspaces", - "write": "Write", - "yes": "Yes", - "yesterday": "Yesterday", - "zoomed": "Zoomed", "{0} (copied)": "{0} (copied)", "{0} Safe Storage": "{0} Safe Storage", "{0} does not require an API key": "{0} does not require an API key", "{0} queries pending": "{0} queries pending", - "{0} | Right-click to configure": "{0} | Right-click to configure" + "{0} | Right-click to configure": "{0} | Right-click to configure", + "Set with /mode PROVIDER": "Set with /mode PROVIDER", + "Invalid API provider. Supported: \n-": "Invalid API provider. Supported: \n-", + "Unknown command:": "Unknown command:", + "Type /key to get started with online models\nCtrl+O to expand the sidebar\nCtrl+P to detach sidebar into a window": "Type /key to get started with online models\nCtrl+O to expand the sidebar\nCtrl+P to detach sidebar into a window", + "This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key.": "This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key.", + "The current API used. Endpoint:": "The current API used. Endpoint:", + "Provider set to": "Provider set to", + "Invalid model. Supported: \n```": "Invalid model. Supported: \n```", + "Interrupts possibility of overview being toggled on release.": "Interrupts possibility of overview being toggled on release.", + "Enter tags, or \"{0}\" for commands": "Enter tags, or \"{0}\" for commands", + "That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number": "That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number", + "Online | Google's model\nGives up-to-date information with search.": "Online | Google's model\nGives up-to-date information with search.", + "Online via {0} | {1}'s model": "Online via {0} | {1}'s model", + "Switched to search mode. Continue with the user's request.": "Switched to search mode. Continue with the user's request.", + "Experimental | Online | Google's model\nCan do a little more but doesn't search quickly": "Experimental | Online | Google's model\nCan do a little more but doesn't search quickly", + "Message the model... \"{0}\" for commands": "Message the model... \"{0}\" for commands", + "To set an API key, pass it with the command\n\nTo view the key, pass \"get\" with the command
\n\n### For {0}:\n\n**Link**: {1}\n\n{2}": "To set an API key, pass it with the command\n\nTo view the key, pass \"get\" with the command
\n\n### For {0}:\n\n**Link**: {1}\n\n{2}" } \ No newline at end of file diff --git a/.config/quickshell/translations/tools/README.md b/.config/quickshell/translations/tools/README.md new file mode 100644 index 000000000..1840f6c3d --- /dev/null +++ b/.config/quickshell/translations/tools/README.md @@ -0,0 +1,111 @@ +# Translation Management Tools + +This directory contains a toolset for managing project translation files. + +## Directory Structure + +``` +translations/ +├── tools/ # Translation management tools directory +│ ├── translation-manager.py # Main translation manager +│ ├── translation-cleaner.py # Translation maintenance tool +│ ├── manage-translations.sh # Convenient wrapper script +│ ├── translation-tools-guide.md # Detailed usage documentation +│ └── README.md # This file +├── en_US.json # English translation file +├── zh_CN.json # Chinese translation file +└── ... # Other language files +``` + +## Quick Start + +### Running from tools directory + +```bash +# Enter tools directory +cd .config/quickshell/translations/tools + +# Check current translation status +./manage-translations.sh status + +# Update all translation files +./manage-translations.sh update + +# Update specific language +./manage-translations.sh update -l zh_CN + +# Clean unused keys +./manage-translations.sh clean + +# Sync all language files +./manage-translations.sh sync +``` + +### Running from project root directory + +```bash +# Run from project root directory (recommended to use relative paths) +.config/quickshell/translations/tools/manage-translations.sh status +.config/quickshell/translations/tools/manage-translations.sh update +``` + +## Tool Description + +### 🛠️ `manage-translations.sh` - Main Entry Point +Convenient command-line interface that integrates all translation management functions. + +### 🔍 `translation-manager.py` - Core Manager +- Extract translatable texts +- Compare translation file differences +- Interactive translation updates + +### 🧹 `translation-cleaner.py` - Maintenance Tool +- Clean unused translation keys +- Sync language file structure +- Create backup files + +## Common Workflows + +### After adding new translatable texts +```bash +./manage-translations.sh update +``` + +### Clean up after code refactoring +```bash +./manage-translations.sh clean +``` + +### Add new language +```bash +./manage-translations.sh update -l new_language_code +``` + +### Check translation status +```bash +./manage-translations.sh status +``` + +## Documentation + +- 📖 [Detailed Usage Guide](./translation-tools-guide.md) + +## Important Notes + +1. **Running Location**: Tools automatically detect relative paths, can be run from tools directory or project root +2. **Backup**: Cleanup operations automatically create backup files +3. **Encoding**: All files use UTF-8 encoding +4. **Permissions**: Ensure scripts have execution permissions + +## Supported Translation Formats + +The tool recognizes translatable texts in the following formats: +```qml +qsTr("Your text here") +qsTr('Single quotes work too') +i18n.t("JavaScript translations") +``` + +--- + +If you have any issues, please refer to the detailed documentation or check error messages in script output. diff --git a/.config/quickshell/translations/tools/manage-translations.sh b/.config/quickshell/translations/tools/manage-translations.sh new file mode 100755 index 000000000..c20896a93 --- /dev/null +++ b/.config/quickshell/translations/tools/manage-translations.sh @@ -0,0 +1,149 @@ +#!/bin/bash +# Translation management script - convenient wrapper + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +TRANSLATIONS_DIR="$(dirname "$SCRIPT_DIR")" +SOURCE_DIR="$(dirname "$(dirname "$TRANSLATIONS_DIR")")" + +show_help() { + echo "Translation Management Tool - Convenient Wrapper" + echo "" + echo "Usage: $0 [options] " + echo "" + echo "Commands:" + echo " extract Extract translatable texts to temporary file" + echo " update Update translation files (add missing/remove extra keys)" + echo " clean Clean unused translation keys" + echo " sync Sync keys across all language files" + echo " status Show translation status" + echo "" + echo "Options:" + echo " -l, --lang LANG Specify language (e.g.: zh_CN)" + echo " -t, --trans-dir DIR Translation files directory (default: $TRANSLATIONS_DIR)" + echo " -s, --source-dir DIR Source code directory (default: $SOURCE_DIR)" + echo " -h, --help Show this help message" + echo "" + echo "Examples:" + echo " $0 extract # Extract translatable texts" + echo " $0 update -l zh_CN # Update Chinese translations" + echo " $0 update # Update all translations" + echo " $0 clean # Clean unused keys" + echo " $0 sync # Sync keys across all languages" + echo " $0 status # Show translation status" +} + +show_status() { + echo "Analyzing translation status..." + + # Extract current text count + echo "=== Current Project Status ===" + python3 "$SCRIPT_DIR/translation-manager.py" \ + --translations-dir "$TRANSLATIONS_DIR" \ + --source-dir "$SOURCE_DIR" \ + --extract-only | grep "Extracted" + + echo "" + echo "=== Translation File Status ===" + + if [ -d "$TRANSLATIONS_DIR" ]; then + for file in "$TRANSLATIONS_DIR"/*.json; do + if [ -f "$file" ]; then + lang=$(basename "$file" .json) + count=$(jq 'length' "$file" 2>/dev/null || echo "error") + echo " $lang: $count keys" + fi + done + else + echo " Translation directory does not exist: $TRANSLATIONS_DIR" + fi +} + +# Parse command line arguments +LANG_CODE="" +COMMAND="" + +while [[ $# -gt 0 ]]; do + case $1 in + -l|--lang) + LANG_CODE="$2" + shift 2 + ;; + -t|--trans-dir) + TRANSLATIONS_DIR="$2" + shift 2 + ;; + -s|--source-dir) + SOURCE_DIR="$2" + shift 2 + ;; + -h|--help) + show_help + exit 0 + ;; + extract|update|clean|sync|status) + if [ -n "$COMMAND" ]; then + echo "Error: Only one command can be specified" + exit 1 + fi + COMMAND="$1" + shift + ;; + *) + echo "Unknown option: $1" + show_help + exit 1 + ;; + esac +done + +if [ -z "$COMMAND" ]; then + echo "Error: A command must be specified" + show_help + exit 1 +fi + +# Check dependencies +if ! command -v python3 >/dev/null 2>&1; then + echo "Error: python3 is required" + exit 1 +fi + +if [ "$COMMAND" = "status" ] && ! command -v jq >/dev/null 2>&1; then + echo "Warning: jq is not installed, status display may be incomplete" +fi + +# Build base arguments +BASE_ARGS="--translations-dir $TRANSLATIONS_DIR --source-dir $SOURCE_DIR" + +case $COMMAND in + extract) + echo "Extracting translatable texts..." + python3 "$SCRIPT_DIR/translation-manager.py" $BASE_ARGS --extract-only --show-temp + ;; + update) + echo "Updating translation files..." + if [ -n "$LANG_CODE" ]; then + python3 "$SCRIPT_DIR/translation-manager.py" $BASE_ARGS --language "$LANG_CODE" + else + python3 "$SCRIPT_DIR/translation-manager.py" $BASE_ARGS + fi + ;; + clean) + echo "Cleaning unused translation keys..." + python3 "$SCRIPT_DIR/translation-cleaner.py" $BASE_ARGS --clean + ;; + sync) + echo "Syncing translation keys..." + python3 "$SCRIPT_DIR/translation-cleaner.py" $BASE_ARGS --sync + ;; + status) + show_status + ;; + *) + echo "Unknown command: $COMMAND" + show_help + exit 1 + ;; +esac diff --git a/.config/quickshell/translations/tools/translation-cleaner.py b/.config/quickshell/translations/tools/translation-cleaner.py new file mode 100755 index 000000000..e53082fda --- /dev/null +++ b/.config/quickshell/translations/tools/translation-cleaner.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Translation File Maintenance Helper +Used to clean and organize translation files, removing unused keys +""" + +import os +import sys +import json +import argparse +import importlib.util +from pathlib import Path +from typing import Dict, Set, List + +# Import from the same directory using importlib +current_dir = os.path.dirname(os.path.abspath(__file__)) +manager_path = os.path.join(current_dir, 'translation-manager.py') +spec = importlib.util.spec_from_file_location("translation_manager", manager_path) +translation_manager = importlib.util.module_from_spec(spec) +spec.loader.exec_module(translation_manager) +TranslationManager = translation_manager.TranslationManager + +def clean_translation_files(translations_dir: str, source_dir: str, backup: bool = True): + """Clean translation files by removing unused keys""" + print("Starting translation file cleanup...") + + # Create manager + manager = TranslationManager(translations_dir, source_dir) + + # Extract currently used texts + print("Extracting currently used translatable texts...") + current_texts = manager.extract_translatable_texts() + print(f"Extracted {len(current_texts)} currently used texts") + + # Get all language files + languages = manager.get_available_languages() + if not languages: + print("No translation files found") + return + + print(f"Found language files: {', '.join(languages)}") + + total_removed = 0 + + for lang in languages: + print(f"\nProcessing language: {lang}") + + # Load translation file + translations = manager.load_translation_file(lang) + original_count = len(translations) + + if backup: + # Create backup + backup_file = Path(translations_dir) / f"{lang}.json.backup" + with open(backup_file, 'w', encoding='utf-8') as f: + json.dump(translations, f, ensure_ascii=False, indent=2) + print(f"Created backup: {backup_file}") + + # Find unused keys + unused_keys = set(translations.keys()) - current_texts + + if unused_keys: + print(f"Found {len(unused_keys)} unused keys:") + for i, key in enumerate(sorted(unused_keys)[:10], 1): # Only show first 10 + print(f" {i}. \"{key[:50]}{'...' if len(key) > 50 else ''}\"") + if len(unused_keys) > 10: + print(f" ... and {len(unused_keys) - 10} more keys") + + response = input(f"Delete these {len(unused_keys)} unused keys? (y/n): ") + if response.lower().strip() in ['y', 'yes']: + # Delete unused keys + for key in unused_keys: + del translations[key] + + # Save cleaned file + manager.save_translation_file(lang, translations) + removed_count = len(unused_keys) + total_removed += removed_count + print(f"Deleted {removed_count} keys") + else: + print("Skipped deletion") + else: + print("No unused keys found") + + new_count = len(translations) + print(f"Original key count: {original_count}, after cleanup: {new_count}") + + print(f"\nCleanup completed! Total deleted {total_removed} unused keys.") + +def sync_translations(translations_dir: str, source_lang: str = "en_US", target_langs: List[str] = None): + """Sync translation keys to ensure all language files have the same keys""" + print(f"Starting translation key sync using {source_lang} as reference...") + + translations_path = Path(translations_dir) + + # Load source language file + source_file = translations_path / f"{source_lang}.json" + if not source_file.exists(): + print(f"Error: Source language file does not exist: {source_file}") + return + + with open(source_file, 'r', encoding='utf-8') as f: + source_translations = json.load(f) + + source_keys = set(source_translations.keys()) + print(f"Source language {source_lang} has {len(source_keys)} keys") + + # Get target language list + if target_langs is None: + target_langs = [] + for file_path in translations_path.glob("*.json"): + lang_code = file_path.stem + if lang_code != source_lang: + target_langs.append(lang_code) + + if not target_langs: + print("No target language files found") + return + + print(f"Target languages: {', '.join(target_langs)}") + + for target_lang in target_langs: + print(f"\nSyncing language: {target_lang}") + + target_file = translations_path / f"{target_lang}.json" + if target_file.exists(): + with open(target_file, 'r', encoding='utf-8') as f: + target_translations = json.load(f) + else: + target_translations = {} + + target_keys = set(target_translations.keys()) + + # Find missing and extra keys + missing_keys = source_keys - target_keys + extra_keys = target_keys - source_keys + + print(f" Missing keys: {len(missing_keys)}") + print(f" Extra keys: {len(extra_keys)}") + + # Add missing keys + if missing_keys: + for key in missing_keys: + # Use source language value as placeholder by default + target_translations[key] = source_translations[key] + print(f" Added {len(missing_keys)} missing keys") + + # Ask whether to delete extra keys + if extra_keys: + response = input(f" Delete {len(extra_keys)} extra keys? (y/n): ") + if response.lower().strip() in ['y', 'yes']: + for key in extra_keys: + del target_translations[key] + print(f" Deleted {len(extra_keys)} extra keys") + + # Save file + with open(target_file, 'w', encoding='utf-8') as f: + json.dump(target_translations, f, ensure_ascii=False, indent=2) + + print(f" Saved: {target_file}") + +def main(): + parser = argparse.ArgumentParser(description="Translation File Maintenance Helper") + parser.add_argument("--translations-dir", "-t", + default=".config/quickshell/translations", + help="Translation files directory") + parser.add_argument("--source-dir", "-s", + default=".config/quickshell", + help="Source code directory") + parser.add_argument("--clean", "-c", action="store_true", + help="Clean unused translation keys") + parser.add_argument("--sync", action="store_true", + help="Sync translation keys") + parser.add_argument("--source-lang", default="en_US", + help="Source language for syncing (default: en_US)") + parser.add_argument("--no-backup", action="store_true", + help="Do not create backup files when cleaning") + + args = parser.parse_args() + + # Convert to absolute paths + translations_dir = os.path.abspath(args.translations_dir) + source_dir = os.path.abspath(args.source_dir) + + if args.clean: + clean_translation_files(translations_dir, source_dir, backup=not args.no_backup) + elif args.sync: + sync_translations(translations_dir, args.source_lang) + else: + print("Please specify an operation:") + print(" --clean: Clean unused translation keys") + print(" --sync: Sync translation keys") + +if __name__ == "__main__": + main() diff --git a/.config/quickshell/translations/tools/translation-manager.py b/.config/quickshell/translations/tools/translation-manager.py new file mode 100755 index 000000000..ffcb26092 --- /dev/null +++ b/.config/quickshell/translations/tools/translation-manager.py @@ -0,0 +1,290 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Translation File Management Script +Used to update and extract translatable texts, manage JSON translation file key comparison +""" + +import os +import json +import re +import sys +import argparse +from pathlib import Path +from typing import Dict, Set, List, Tuple +import tempfile +import subprocess + +class TranslationManager: + def __init__(self, translations_dir: str, source_dir: str): + self.translations_dir = Path(translations_dir) + self.source_dir = Path(source_dir) + self.temp_extracted_file = None + + # Ensure translation directory exists + self.translations_dir.mkdir(parents=True, exist_ok=True) + + def extract_translatable_texts(self) -> Set[str]: + """Extract translatable texts from source code""" + translatable_texts = set() + + # Search patterns: Translation.tr("text") or Translation.tr('text') + # Improved regex that handles nested quotes correctly + patterns = [ + r'Translation\.tr\s*\(\s*(["\'])(((?!\1)[^\\]|\\.)*)(\1)\s*\)', # Double or single quotes with escape support + r'Translation\.tr\s*\(\s*`([^`]*(?:\\.[^`]*)*?)`\s*\)', # Backticks (template strings) + ] + + # Search all .qml and .js files + file_extensions = ['*.qml', '*.js'] + + for ext in file_extensions: + for file_path in self.source_dir.rglob(ext): + try: + with open(file_path, 'r', encoding='utf-8') as f: + content = f.read() + + for pattern in patterns: + matches = re.findall(pattern, content, re.MULTILINE | re.DOTALL) + for match in matches: + # Handle different match group structures + if isinstance(match, tuple): + # For improved regex, text is in the second group + if len(match) >= 3: + text = match[1] # Second group is the text content + else: + text = match[0] if match else "" + else: + text = match + + # Decode escape characters + try: + clean_text = text.encode().decode('unicode_escape') + except: + clean_text = text + + # Clean text (remove extra whitespace) + clean_text = clean_text.strip() + if clean_text: + translatable_texts.add(clean_text) + + except (UnicodeDecodeError, IOError) as e: + print(f"Warning: Cannot read file {file_path}: {e}") + + return translatable_texts + + def create_temp_translation_file(self, texts: Set[str]) -> str: + """Create temporary JSON file containing extracted texts""" + temp_data = {} + for text in sorted(texts): + temp_data[text] = text # Key and value are the same, indicating untranslated + + # Create temporary file + with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False, encoding='utf-8') as f: + json.dump(temp_data, f, ensure_ascii=False, indent=2) + self.temp_extracted_file = f.name + + return self.temp_extracted_file + + def load_translation_file(self, lang_code: str) -> Dict[str, str]: + """Load translation file for specified language""" + file_path = self.translations_dir / f"{lang_code}.json" + if file_path.exists(): + try: + with open(file_path, 'r', encoding='utf-8') as f: + return json.load(f) + except (json.JSONDecodeError, IOError) as e: + print(f"Warning: Cannot load translation file {file_path}: {e}") + return {} + return {} + + def save_translation_file(self, lang_code: str, translations: Dict[str, str]): + """Save translation file""" + file_path = self.translations_dir / f"{lang_code}.json" + try: + with open(file_path, 'w', encoding='utf-8') as f: + json.dump(translations, f, ensure_ascii=False, indent=2) + print(f"Translation file saved: {file_path}") + except IOError as e: + print(f"Error: Cannot save translation file {file_path}: {e}") + + def get_available_languages(self) -> List[str]: + """Get list of available languages""" + languages = [] + for file_path in self.translations_dir.glob("*.json"): + lang_code = file_path.stem + languages.append(lang_code) + return sorted(languages) + + def compare_translations(self, extracted_texts: Set[str], target_lang: str) -> Tuple[Set[str], Set[str]]: + """Compare extracted texts with existing translation file""" + existing_translations = self.load_translation_file(target_lang) + existing_keys = set(existing_translations.keys()) + + missing_keys = extracted_texts - existing_keys # Missing keys + extra_keys = existing_keys - extracted_texts # Extra keys + + return missing_keys, extra_keys + + def interactive_update(self, lang_code: str, missing_keys: Set[str], extra_keys: Set[str]): + """Interactively update translation file""" + translations = self.load_translation_file(lang_code) + modified = False + + # Handle missing keys + if missing_keys: + print(f"\nFound {len(missing_keys)} missing translation keys:") + for i, key in enumerate(sorted(missing_keys), 1): + print(f"{i}. \"{key}\"") + + if self.ask_yes_no(f"\nAdd these {len(missing_keys)} missing keys?"): + for key in missing_keys: + translations[key] = key # Default value is the key itself + modified = True + print(f"Added {len(missing_keys)} keys") + + # Handle extra keys + if extra_keys: + print(f"\nFound {len(extra_keys)} extra translation keys:") + for i, key in enumerate(sorted(extra_keys), 1): + print(f"{i}. \"{key}\" -> \"{translations.get(key, '')}\"") + + if self.ask_yes_no(f"\nDelete these {len(extra_keys)} extra keys?"): + for key in extra_keys: + if key in translations: + del translations[key] + modified = True + print(f"Deleted {len(extra_keys)} keys") + + # Save changes + if modified: + self.save_translation_file(lang_code, translations) + else: + print("No changes made") + + def ask_yes_no(self, question: str) -> bool: + """Ask user for confirmation""" + while True: + response = input(f"{question} (y/n): ").lower().strip() + if response in ['y', 'yes']: + return True + elif response in ['n', 'no']: + return False + else: + print("Please enter y/yes or n/no") + + def cleanup(self): + """Clean up temporary files""" + if self.temp_extracted_file and os.path.exists(self.temp_extracted_file): + os.unlink(self.temp_extracted_file) + +def main(): + parser = argparse.ArgumentParser(description="Translation file management tool") + parser.add_argument("--translations-dir", "-t", + default=".config/quickshell/translations", + help="Translation files directory (default: .config/quickshell/translations)") + parser.add_argument("--source-dir", "-s", + default=".config/quickshell", + help="Source code directory (default: .config/quickshell)") + parser.add_argument("--language", "-l", + help="Specify language code to process (e.g., zh_CN)") + parser.add_argument("--extract-only", "-e", action="store_true", + help="Only extract translatable texts to temporary file") + parser.add_argument("--show-temp", action="store_true", + help="Show temporary extracted file content") + + args = parser.parse_args() + + # Convert to absolute paths + translations_dir = os.path.abspath(args.translations_dir) + source_dir = os.path.abspath(args.source_dir) + + print(f"Translation directory: {translations_dir}") + print(f"Source code directory: {source_dir}") + + # Check if directories exist + if not os.path.exists(source_dir): + print(f"Error: Source code directory does not exist: {source_dir}") + sys.exit(1) + + # Create manager + manager = TranslationManager(translations_dir, source_dir) + + try: + # Extract translatable texts + print("\nExtracting translatable texts...") + extracted_texts = manager.extract_translatable_texts() + print(f"Extracted {len(extracted_texts)} translatable texts") + + # Create temporary file + temp_file = manager.create_temp_translation_file(extracted_texts) + print(f"Created temporary file: {temp_file}") + + if args.show_temp: + print("\nTemporary file contents:") + with open(temp_file, 'r', encoding='utf-8') as f: + print(f.read()) + + if args.extract_only: + print("Extract-only mode, program finished") + return + + # Get available languages + available_languages = manager.get_available_languages() + + if args.language: + target_languages = [args.language] + else: + print(f"\nAvailable languages: {', '.join(available_languages) if available_languages else 'None'}") + + if not available_languages: + print("No existing translation files found") + lang_input = input("Enter language code to create (e.g.: zh_CN): ").strip() + if lang_input: + target_languages = [lang_input] + else: + print("No language specified, program finished") + return + else: + print("Choose language to process:") + for i, lang in enumerate(available_languages, 1): + print(f"{i}. {lang}") + print("a. Process all languages") + + choice = input("Please choose (enter number, language code, or 'a'): ").strip() + + if choice.lower() == 'a': + target_languages = available_languages + elif choice.isdigit() and 1 <= int(choice) <= len(available_languages): + target_languages = [available_languages[int(choice) - 1]] + elif choice in available_languages: + target_languages = [choice] + else: + print("Invalid choice, program finished") + return + + # Process each language + for lang in target_languages: + print(f"\n{'='*50}") + print(f"Processing language: {lang}") + print('='*50) + + missing_keys, extra_keys = manager.compare_translations(extracted_texts, lang) + + if not missing_keys and not extra_keys: + print(f"Translation file for language {lang} is already up to date") + continue + + print(f"Analysis results:") + print(f" Missing keys: {len(missing_keys)}") + print(f" Extra keys: {len(extra_keys)}") + + if missing_keys or extra_keys: + manager.interactive_update(lang, missing_keys, extra_keys) + + finally: + # Clean up temporary files + manager.cleanup() + +if __name__ == "__main__": + main() diff --git a/.config/quickshell/translations/tools/translation-tools-guide.md b/.config/quickshell/translations/tools/translation-tools-guide.md new file mode 100644 index 000000000..527576329 --- /dev/null +++ b/.config/quickshell/translations/tools/translation-tools-guide.md @@ -0,0 +1,246 @@ +# Translation Management Tool Suite + +This tool suite is used to manage project translation files, automatically extract translatable texts, compare differences between different language files, and provide maintenance functions. + +## Tool Components + +### 1. `translation-manager.py` - Main Translation Manager +- Extract translatable texts +- Compare and update translation files +- Interactive adding/removing translation keys + +### 2. `translation-cleaner.py` - Translation File Maintenance Tool +- Clean unused translation keys +- Sync key structure across different language files + +### 3. `manage-translations.sh` - Convenient Wrapper Script +- Provide unified command-line interface +- Display translation status +- Simplify common operations + +## Quick Start + +### Check Translation Status +```bash +./manage-translations.sh status +``` + +### Extract Translatable Texts +```bash +./manage-translations.sh extract +``` + +### Update Translation Files +```bash +# Update all languages +./manage-translations.sh update + +# Update specific language +./manage-translations.sh update -l zh_CN +``` + +### Clean Unused Keys +```bash +./manage-translations.sh clean +``` + +### Sync Keys Across Languages +```bash +./manage-translations.sh sync +``` + +## Detailed Usage + +### translation-manager.py + +The main translation management tool that extracts translatable texts from source code and manages translation files. + +#### Command Line Options +```bash +python3 translation-manager.py [options] + +Options: + -h, --help Show help message + -t, --translations-dir DIR Translation files directory (default: .config/quickshell/translations) + -s, --source-dir DIR Source code directory (default: .config/quickshell) + -l, --language LANG Specify language code to process (e.g., zh_CN) + -e, --extract-only Only extract translatable texts to temporary file + --show-temp Show temporary extracted file content +``` + +#### Features +1. **Text Extraction**: Uses regex patterns to extract translatable texts from QML and JavaScript files +2. **Smart Filtering**: Automatically removes duplicates and cleans up extracted texts +3. **Interactive Updates**: Guides users through adding missing keys and removing extra ones +4. **Backup Support**: Creates backups before making changes + +#### Supported Text Patterns +- `qsTr("text")` and `qsTr('text')` +- `i18n.t("text")` and `i18n.t('text')` +- Supports nested quotes and escape characters +- Handles multiline strings + +### translation-cleaner.py + +A maintenance tool for cleaning up and synchronizing translation files. + +#### Command Line Options +```bash +python3 translation-cleaner.py [options] + +Options: + -h, --help Show help message + -t, --translations-dir DIR Translation files directory + -s, --source-dir DIR Source code directory + -c, --clean Clean unused translation keys + --sync Sync translation keys + --source-lang LANG Source language for syncing (default: en_US) + --no-backup Do not create backup files when cleaning +``` + +#### Features +1. **Unused Key Cleanup**: Identifies and removes translation keys that are no longer used in the source code +2. **Key Synchronization**: Ensures all language files have the same set of keys +3. **Backup Protection**: Creates backup files before making destructive changes +4. **Interactive Confirmation**: Asks for user confirmation before deleting keys + +### manage-translations.sh + +A convenient wrapper script that provides a unified interface to all translation tools. + +#### Commands +```bash +./manage-translations.sh [options] + +Commands: + extract Extract translatable texts to temporary file + update Update translation files (add missing/remove extra keys) + clean Clean unused translation keys + sync Sync keys across all language files + status Show translation status + +Options: + -l, --lang LANG Specify language (e.g.: zh_CN) + -t, --trans-dir DIR Translation files directory + -s, --source-dir DIR Source code directory + -h, --help Show help message +``` + +## Workflow Examples + +### Initial Setup +1. Create translation directory structure +2. Extract all translatable texts: `./manage-translations.sh extract` +3. Create initial translation files: `./manage-translations.sh update` + +### Regular Maintenance +1. Check status: `./manage-translations.sh status` +2. Update translations after code changes: `./manage-translations.sh update` +3. Clean up unused keys periodically: `./manage-translations.sh clean` + +### Adding New Language +1. Create new language file: `./manage-translations.sh update -l new_lang` +2. Sync keys if needed: `./manage-translations.sh sync` + +## File Structure + +``` +translations/ +├── tools/ # Translation management tools +│ ├── translation-manager.py # Main extraction and update tool +│ ├── translation-cleaner.py # Cleanup and sync tool +│ ├── manage-translations.sh # Wrapper script +│ └── translation-tools-guide.md # This documentation +├── en_US.json # English translations (reference) +├── zh_CN.json # Chinese translations +└── [other_lang].json # Other language files +``` + +## Configuration + +### Text Extraction Patterns + +The tool uses regex patterns to identify translatable texts. Current patterns include: + +1. **QML qsTr patterns**: + - `qsTr("text")` and `qsTr('text')` + - Supports escaped quotes and nested quotes + +2. **JavaScript i18n patterns**: + - `i18n.t("text")` and `i18n.t('text')` + - Supports template literals and complex expressions + +3. **Custom patterns** can be added by modifying the patterns list in `translation-manager.py` + +### File Extensions + +By default, the tool processes: +- `.qml` files (QML/QtQuick) +- `.js` files (JavaScript) + +Additional file types can be added by modifying the file extension filters. + +## Best Practices + +1. **Regular Updates**: Run `./manage-translations.sh status` regularly to check for new translatable texts +2. **Clean Periodically**: Use `./manage-translations.sh clean` to remove unused keys +3. **Backup Important**: Always backup translation files before major changes +4. **Consistent Patterns**: Use consistent function calls (`qsTr`, `i18n.t`) for translatable texts +5. **Review Changes**: Always review the changes before confirming deletions or additions + +## Troubleshooting + +### Common Issues + +1. **Missing Texts**: If some translatable texts are not extracted, check if they match the supported patterns +2. **Path Issues**: Ensure the source and translation directory paths are correct +3. **Permission Errors**: Make sure the script has write permissions to the translation directory +4. **Encoding Issues**: All files should be saved in UTF-8 encoding + +### Getting Help + +For additional help: +- Use `--help` option with any tool +- Check the console output for error messages +- Verify file paths and permissions + +## Advanced Usage + +### Custom Regex Patterns + +To add support for new translation function patterns, modify the `patterns` list in `TranslationManager.extract_translatable_texts()`: + +```python +patterns = [ + # Existing patterns... + r'customTranslate\s*\(\s*(["\'])((?:\\.|(?!\1)[^\\])*?)\1\s*\)', # Custom pattern +] +``` + +### Batch Processing + +For processing multiple projects or directories: + +```bash +# Process multiple directories +for dir in project1 project2 project3; do + ./manage-translations.sh -s "$dir" -t "$dir/translations" update +done +``` + +### Integration with Build Systems + +The tools can be integrated into build systems to automatically update translations: + +```bash +# In your build script +./manage-translations.sh update --non-interactive +``` + +## Version History + +- **v1.0**: Initial version with basic extraction and update functionality +- **v1.1**: Added improved regex patterns for better text extraction +- **v1.2**: Added cleaning and synchronization tools +- **v1.3**: Added English output and improved user interface +- **v1.4**: Moved all tools to dedicated tools directory and improved documentation diff --git a/.config/quickshell/translations/tools/翻译管理脚本说明.md b/.config/quickshell/translations/tools/翻译管理脚本说明.md new file mode 100644 index 000000000..fa9850cf8 --- /dev/null +++ b/.config/quickshell/translations/tools/翻译管理脚本说明.md @@ -0,0 +1,287 @@ +# 翻译管理工具套件 + +这套工具用于管理项目的翻译文件,自动提取可翻译文本,比较不同语言文件之间的差异,并提供维护功能。 + +## 工具组成 + +### 1. `translation-manager.py` - 主要翻译管理器 +- 提取可翻译文本 +- 比较和更新翻译文件 +- 交互式添加/删除翻译键 + +### 2. `translation-cleaner.py` - 翻译文件维护工具 +- 清理不再使用的翻译键 +- 同步不同语言文件的键结构 + +### 3. `manage-translations.sh` - 便捷包装脚本 +- 提供统一的命令行界面 +- 显示翻译状态 +- 简化常用操作 + +## 快速开始 + +### 使用便捷脚本(推荐) + +```bash +# 进入工具目录 +cd .config/quickshell/translations/tools + +# 查看帮助 +./manage-translations.sh --help + +# 显示当前翻译状态 +./manage-translations.sh status + +# 提取可翻译文本 +./manage-translations.sh extract + +# 更新所有翻译文件 +./manage-translations.sh update + +# 更新特定语言 +./manage-translations.sh update -l zh_CN + +# 清理不再使用的键 +./manage-translations.sh clean + +# 同步所有语言文件的键 +./manage-translations.sh sync +``` + +或者从项目根目录运行: +```bash +# 从项目根目录运行 +.config/quickshell/translations/tools/manage-translations.sh status +.config/quickshell/translations/tools/manage-translations.sh update +``` + +## 详细使用说明 + +### 翻译管理器 (`translation-manager.py`) + +基本用法: +```bash +# 处理所有语言 +./translation-manager.py + +# 指定特定语言 +./translation-manager.py --language zh_CN + +# 仅提取可翻译文本 +./translation-manager.py --extract-only + +# 显示提取的文本 +./translation-manager.py --extract-only --show-temp +``` + +参数说明: +- `--translations-dir`, `-t`: 翻译文件目录(默认:`.config/quickshell/translations`) +- `--source-dir`, `-s`: 源代码目录(默认:`.config/quickshell`) +- `--language`, `-l`: 指定要处理的语言代码 +- `--extract-only`, `-e`: 仅提取可翻译文本 +- `--show-temp`: 显示临时提取文件的内容 + +### 翻译清理器 (`translation-cleaner.py`) + +```bash +# 清理不再使用的翻译键 +./translation-cleaner.py --clean + +# 同步翻译键(以 en_US 为基准) +./translation-cleaner.py --sync + +# 指定不同的源语言进行同步 +./translation-cleaner.py --sync --source-lang zh_CN + +# 清理时不创建备份 +./translation-cleaner.py --clean --no-backup +``` + +## 工作流程 + +### 日常翻译更新流程 + +1. **检查状态**: + ```bash + ./manage-translations.sh status + ``` + +2. **更新翻译**: + ```bash + ./manage-translations.sh update + ``` + +3. **清理无用键**(可选): + ```bash + ./manage-translations.sh clean + ``` + +### 新增语言流程 + +1. **创建新语言文件**: + ```bash + ./manage-translations.sh update -l new_lang + ``` + +2. **同步键结构**: + ```bash + ./manage-translations.sh sync + ``` + +### 大规模重构后的清理流程 + +1. **备份翻译文件**: + ```bash + cp -r .config/quickshell/translations .config/quickshell/translations.backup + ``` + +2. **清理无用键**: + ```bash + ./manage-translations.sh clean + ``` + +3. **同步所有语言**: + ```bash + ./manage-translations.sh sync + ``` + +## 支持的翻译文本格式 + +工具可以识别以下格式的可翻译文本: + +```qml +// 基本格式 +Translation.tr("Hello, world!") +Translation.tr('Hello, world!') +Translation.tr(`Hello, world!`) + +// 带换行符 +Translation.tr("Line 1\nLine 2") + +// 带转义字符 +Translation.tr("Say \"Hello\"") + +// 带参数占位符 +Translation.tr("Hello, %1!").arg(name) +Translation.tr("{0} files selected").arg(count) +``` + +## 示例输出 + +### 状态显示 +``` +$ ./manage-translations.sh status +正在分析翻译状态... +=== 当前项目状态 === +提取到 166 个可翻译文本 + +=== 翻译文件状态 === + en_US: 470 个键 + zh_CN: 470 个键 +``` + +### 更新翻译 +``` +$ ./manage-translations.sh update -l zh_CN +更新翻译文件... +================================================== +处理语言: zh_CN +================================================== +分析结果: + 缺少的键: 5 + 多余的键: 20 + +发现 5 个缺少的翻译键: +1. "New feature text" +2. "Another new text" +... + +是否添加这 5 个缺少的键? (y/n): y +已添加 5 个键 + +发现 20 个多余的翻译键: +1. "Removed old text" -> "已删除的旧文本" +... + +是否删除这 20 个多余的键? (y/n): y +已删除 20 个键 + +已保存翻译文件 +``` + +### 清理无用键 +``` +$ ./manage-translations.sh clean +清理不再使用的翻译键... +处理语言: zh_CN +发现 50 个不再使用的键: + 1. "old_unused_text" + 2. "deprecated_message" + ... + +是否删除这 50 个不再使用的键? (y/n): y +已删除 50 个键 +原始键数: 470, 清理后: 420 +``` + +## 高级功能 + +### 自定义目录结构 + +```bash +# 使用自定义目录 +./translation-manager.py \ + --translations-dir /path/to/translations \ + --source-dir /path/to/source +``` + +### 批量处理 + +```bash +# 创建处理脚本 +cat > update-all-translations.sh << 'EOF' +#!/bin/bash +for lang in zh_CN ja_JP ko_KR; do + echo "Processing $lang..." + ./manage-translations.sh update -l $lang +done +EOF + +chmod +x update-all-translations.sh +./update-all-translations.sh +``` + +## 注意事项 + +1. **备份重要**:在执行清理操作前,工具会自动创建备份,但建议手动备份重要文件 + +2. **文本提取限制**: + - 只支持静态字符串,不支持动态构建的字符串 + - 必须使用 `Translation.tr()` 格式 + +3. **文件编码**:所有文件必须使用 UTF-8 编码 + +4. **键名规范**:建议使用英文作为键名,避免使用特殊字符 + +## 故障排除 + +### 常见问题 + +**Q: 提取的文本数量与预期不符?** +A: 检查是否所有可翻译文本都使用了 `Translation.tr()` 格式,确保没有动态构建的字符串。 + +**Q: 同步后某些翻译丢失?** +A: 检查源语言文件是否包含所有必要的键,考虑使用不同的源语言进行同步。 + +**Q: 清理操作删除了需要的键?** +A: 从自动创建的备份文件中恢复,检查源代码中是否正确使用了 `Translation.tr()`。 + +### 恢复备份 + +```bash +# 恢复单个文件 +cp .config/quickshell/translations/zh_CN.json.backup .config/quickshell/translations/zh_CN.json + +# 恢复所有文件 +cp .config/quickshell/translations.backup/* .config/quickshell/translations/ +``` diff --git a/.config/quickshell/translations/zh_CN.json b/.config/quickshell/translations/zh_CN.json index f73a6949f..a0d099397 100644 --- a/.config/quickshell/translations/zh_CN.json +++ b/.config/quickshell/translations/zh_CN.json @@ -27,28 +27,26 @@ "Clear chat history": "清除聊天记录", "Clear the current list of images": "清除当前图片列表", "Close": "关闭", - "Closes cheatsheet on press": "Closes cheatsheet on press", - "Closes left sidebar on press": "Closes left sidebar on press", - "Closes media controls on press": "Closes media controls on press", - "Closes on screen keyboard on press": "Closes on screen keyboard on press", - "Closes overview": "Closes overview", - "Closes right sidebar on press": "Closes right sidebar on press", + "Closes cheatsheet on press": "按下时关闭快捷键表", + "Closes left sidebar on press": "按下时关闭左侧边栏", + "Closes media controls on press": "按下时关闭媒体控制", + "Closes on screen keyboard on press": "按下时关闭屏幕键盘", + "Closes overview": "关闭概览", + "Closes right sidebar on press": "按下时关闭右侧边栏", "Copy": "复制", "Copy code": "复制代码", - "Ctrl+O to expand the sidebar\nCtrl+P to detach sidebar into a window": "Ctrl+O 展开侧边栏\nCtrl+P 将侧边栏分离为窗口", "Current API endpoint: {0}\nSet it with {1}mode PROVIDER": "当前 API 端点:{0}\n使用 {1}mode PROVIDER 设置", "Current model: {0}\nSet it with {1}model MODEL": "当前模型:{0}\n使用 {1}model MODEL 设置", - "Decrease brightness": "Decrease brightness", + "Decrease brightness": "降低亮度", "Delete": "删除", "Desktop": "桌面", - "Detach left sidebar into a window/Attach it back": "Detach left sidebar into a window/Attach it back", + "Detach left sidebar into a window/Attach it back": "将左侧边栏分离为窗口/重新附加", "Disable NSFW content": "禁用 NSFW 内容", "Done": "完成", "Download": "下载", "Download complete": "下载完成", "Edit": "编辑", "Enter text to translate...": "输入要翻译的文本...", - "Experimental | Online | Google's model\nCan do a little more but doesn't search quickly": "实验性 | 在线 | Google 模型\n功能更多但搜索速度较慢", "Finished tasks will go here": "已完成的任务将显示在这里", "For desktop wallpapers | Good quality": "桌面壁纸专用 | 质量好", "For storing API keys and other sensitive information": "用于存储 API 密钥和其他敏感信息", @@ -56,17 +54,14 @@ "Get the next page of results": "获取下一页结果", "Go to source ({0})": "转到源 ({0})", "Hibernate": "休眠", - "Hides brightness OSD on press": "Hides brightness OSD on press", - "Hides volume OSD on press": "Hides volume OSD on press", - "Hold to show workspace numbers, release to show icons": "Hold to show workspace numbers, release to show icons", - "Increase brightness": "Increase brightness", + "Hides brightness OSD on press": "按下时隐藏亮度显示", + "Hides volume OSD on press": "按下时隐藏音量显示", + "Hold to show workspace numbers, release to show icons": "按住显示工作区编号,松开显示图标", + "Increase brightness": "提高亮度", "Input": "输入", "Intelligence": "智能", "Interface": "界面", - "Interrupts possibility of overview being toggled on release. ": "Interrupts possibility of overview being toggled on release. ", - "Invalid API provider. Supported: \n- ": "无效的 API 提供商。支持的:\n- ", "Invalid arguments. Must provide `key` and `value`.": "参数无效。必须提供 `key` 和 `value`。", - "Invalid model. Supported: \n```\n": "无效模型。支持的:\n```\n", "Jump to current month": "跳转到当前月份", "Keep system awake": "保持系统唤醒", "Large images | God tier quality, no NSFW.": "大尺寸图片 | 顶级质量,无 NSFW", @@ -86,24 +81,21 @@ "Nothing here!": "这里什么都没有!", "Notifications": "通知", "OK": "确定", - "Online via {0} | {1}'s model": "通过 {0} 在线 | {1} 模型", - "Online | Google's model\nGives up-to-date information with search.": "在线 | Google 模型\n通过搜索提供最新信息。", "Open file link": "打开文件链接", - "Opens cheatsheet on press": "Opens cheatsheet on press", - "Opens left sidebar on press": "Opens left sidebar on press", - "Opens media controls on press": "Opens media controls on press", - "Opens on screen keyboard on press": "Opens on screen keyboard on press", - "Opens right sidebar on press": "Opens right sidebar on press", - "Opens session screen on press": "Opens session screen on press", + "Opens cheatsheet on press": "按下时打开快捷键表", + "Opens left sidebar on press": "按下时打开左侧边栏", + "Opens media controls on press": "按下时打开媒体控制", + "Opens on screen keyboard on press": "按下时打开屏幕键盘", + "Opens right sidebar on press": "按下时打开右侧边栏", + "Opens session screen on press": "按下时打开会话屏幕", "Output": "输出", "Page {0}": "第 {0} 页", "Plasma Settings": "Plasma 设置", - "Provider set to ": "提供商设置为", "Reboot": "重启", - "Reboot to firmware settings": "Reboot to firmware settings", + "Reboot to firmware settings": "重启到固件设置", "Reload Hyprland & Quickshell": "重新加载 Hyprland 和 Quickshell", "Run": "运行", - "Run command": "Run command", + "Run command": "运行命令", "Save": "保存", "Save to Downloads": "保存到下载文件夹", "Scroll to change brightness": "滚动调节亮度", @@ -122,351 +114,63 @@ "Shutdown": "关机", "Silent": "静音", "Sleep": "睡眠", - "Switched to search mode. Continue with the user's request.": "已切换到搜索模式。继续处理用户请求。", "System": "系统", "Task Manager": "任务管理器", "Task description": "任务描述", "Temperature must be between 0 and 2": "温度必须在 0 到 2 之间", "Temperature set to {0}": "温度设置为 {0}", "Temperature: {0}": "温度:{0}", - "That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number": "没有找到结果。提示:\n- 检查您的标签和 NSFW 设置\n- 如果没有想到标签,请输入页码", - "The current API used. Endpoint: ": "当前使用的 API。端点:", "The hentai one | Great quantity, a lot of NSFW, quality varies wildly": "成人向 | 数量巨大,大量 NSFW,质量参差不齐", "The popular one | Best quantity, but quality can vary wildly": "最受欢迎 | 数量最多,但质量参差不齐", "Thinking": "思考中", - "This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. ": "This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. ", - "To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything.": "To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything.", - "Toggle clipboard query on overview widget": "Toggle clipboard query on overview widget", - "Toggle emoji query on overview widget": "Toggle emoji query on overview widget", - "Toggles cheatsheet on press": "Toggles cheatsheet on press", - "Toggles left sidebar on press": "Toggles left sidebar on press", - "Toggles media controls on press": "Toggles media controls on press", - "Toggles on screen keyboard on press": "Toggles on screen keyboard on press", - "Toggles overview on press": "Toggles overview on press", - "Toggles overview on release": "Toggles overview on release", - "Toggles right sidebar on press": "Toggles right sidebar on press", - "Toggles session screen on press": "Toggles session screen on press", + "To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything.": "为确保此功能正常工作,请在自动触发的子映射中使用 binditn = MODKEYS, catchall,该子映射包含所有内容。", + "Toggle clipboard query on overview widget": "在概览组件中切换剪贴板查询", + "Toggle emoji query on overview widget": "在概览组件中切换表情符号查询", + "Toggles cheatsheet on press": "按下时切换快捷键表", + "Toggles left sidebar on press": "按下时切换左侧边栏", + "Toggles media controls on press": "按下时切换媒体控制", + "Toggles on screen keyboard on press": "按下时切换屏幕键盘", + "Toggles overview on press": "按下时切换概览", + "Toggles overview on release": "松开时切换概览", + "Toggles right sidebar on press": "按下时切换右侧边栏", + "Toggles session screen on press": "按下时切换会话屏幕", "Translation goes here...": "翻译结果会显示在这里...", "Translator": "翻译器", - "Triggers brightness OSD on press": "Triggers brightness OSD on press", - "Triggers volume OSD on press": "Triggers volume OSD on press", + "Triggers brightness OSD on press": "按下时触发亮度显示", + "Triggers volume OSD on press": "按下时触发音量显示", "Unfinished": "未完成", "Unknown": "未知", "Unknown Album": "未知专辑", "Unknown Artist": "未知艺术家", "Unknown Title": "未知标题", - "Unknown command: ": "未知命令:", "Unknown function call: {0}": "未知函数调用:{0}", "Uptime: {0}": "运行时间:{0}", - "Use casual tone. No user knowledge is to be assumed except basic Linux literacy. Be brief and concise: When explaining concepts, use bullet points (prefer minus sign (-) over asterisk (*)) and highlight keywords in **bold** to pinpoint the main concepts instead of long paragraphs. You are also encouraged to split your response with h2 headers, each header title beginning with an emoji, like `## 🐧 Linux`. When making changes to the user's config, you must get the config to know what values there are before setting.": "Use casual tone. No user knowledge is to be assumed except basic Linux literacy. Be brief and concise: When explaining concepts, use bullet points (prefer minus sign (-) over asterisk (*)) and highlight keywords in **bold** to pinpoint the main concepts instead of long paragraphs. You are also encouraged to split your response with h2 headers, each header title beginning with an emoji, like `## 🐧 Linux`. When making changes to the user's config, you must get the config to know what values there are before setting.", "View Markdown source": "查看 Markdown 源码", "Volume": "音量", "Volume mixer": "音量混合器", "Waifus only | Excellent quality, limited quantity": "仅限角色 | 优秀质量,数量有限", "Waiting for response...": "等待响应...", "Workspace": "工作区", - "\nSet with /mode PROVIDER": "\n使用 /mode PROVIDER 设置", - "about": "关于", - "accessed": "访问时间", - "account": "账户", - "active": "活动", - "active_state": "活动状态", - "addon": "附加组件", - "addons": "附加组件", - "address": "地址", - "admin": "管理员", - "align": "对齐", - "always_on_top": "总在最前", - "animate": "动画", - "app": "应用", - "appearance": "外观", - "application": "应用程序", - "applications": "应用程序", - "apply": "应用", - "apps": "应用", - "auto": "自动", - "background": "背景", - "balanced": "平衡", - "bar": "栏", - "bars": "栏", - "battery": "电池", - "bluetooth": "蓝牙", - "blur": "模糊", - "border": "边框", - "bottom": "底部", - "brightness": "亮度", - "bring_to_front": "置于顶层", - "bytes": "{0} 字节", - "cancel": "取消", - "center": "居中", - "characters": "{0} 个字符", - "charging": "充电中", - "clear_all": "清除全部", - "clicked": "点击", - "close": "关闭", - "collapse": "折叠", - "color": "颜色", - "command": "命令", - "connected": "已连接", - "connecting": "连接中...", - "context_menu": "右键菜单", - "copy": "复制", - "cpu": "处理器", - "created": "创建时间", - "critical": "电量危险", - "customize": "自定义", - "cut": "剪切", - "dark": "深色", - "dashboard": "仪表板", - "date": "日期", - "debug": "调试", - "default": "默认", - "degrees": "{0}°", - "delete": "删除", - "demo": "演示", - "desktop": "桌面", - "dialog": "对话框", - "disabled": "已禁用", - "discharging": "放电中", - "disconnected": "已断开", - "disk": "磁盘", - "display": "显示", - "distribute": "分布", - "dock": "停靠栏", - "documents": "文档", - "double_clicked": "双击", - "downloads": "下载", - "dragged": "拖拽", - "dropdown": "下拉菜单", - "dropped": "拖放", - "effect": "效果", - "email": "邮箱", - "enabled": "已启用", - "error": "错误", - "ethernet": "以太网", - "example": "示例", - "execute": "执行", - "exit": "退出", - "expand": "展开", - "extension": "扩展", - "extensions": "扩展", - "fan": "风扇", - "favorites": "收藏夹", - "file": "文件", - "files": "文件", - "filter": "滤镜", - "flip": "翻转", - "floating": "浮动", - "focus": "焦点", - "folder": "文件夹", - "folders": "文件夹", - "foreground": "前景", - "format": "格式", - "full": "已充满", - "fullscreen": "全屏", - "gigabytes": "{0} GB", - "gigahertz": "{0} GHz", - "glow": "发光", - "group": "群组", - "guest": "访客", - "headphones": "耳机", - "help": "帮助", - "hertz": "{0} Hz", - "hibernate": "休眠", - "hidden": "隐藏", - "hide": "隐藏", - "highlight": "高亮", - "hint": "提示", - "home": "主目录", - "hour": "小时", - "hours": "小时", - "hover": "悬停", - "inactive": "非活动", - "info": "信息", - "input": "输入", - "install": "安装", - "justify": "两端对齐", - "keybinds": "按键绑定", - "keyboard": "键盘", - "kilobytes": "{0} KB", - "kilohertz": "{0} kHz", - "landscape": "横向", - "language": "语言", - "launcher": "启动器", - "left": "左对齐", - "light": "浅色", - "loading": "加载中...", - "lock": "锁定", - "log": "日志", - "logout": "注销", - "long_pressed": "长按", - "low": "电量低", - "manual": "手册", - "margin": "边距", - "maximize": "最大化", - "megabytes": "{0} MB", - "megahertz": "{0} MHz", - "memory": "内存", - "menu": "菜单", - "menubar": "菜单栏", - "microphone": "麦克风", - "minimize": "最小化", - "minute": "分钟", - "minutes": "分钟", - "mirror": "镜像", - "modal": "模态框", - "modified": "修改时间", - "monitor": "显示器", - "mouse": "鼠标", - "move": "移动", - "music": "音乐", - "mute": "静音", - "network": "网络", - "next": "下一个", - "no": "否", - "no_notifications": "无通知", - "notifications": "通知", - "off": "关", - "ok": "确定", - "on": "开", - "opacity": "不透明度", - "open": "打开", - "orientation": "方向", - "outline": "轮廓", - "output": "输出", - "overview": "概览", - "owner": "所有者", - "package": "软件包", - "packages": "软件包", - "padding": "内边距", - "panel": "面板", - "panels": "面板", - "panned": "平移", - "password": "密码", - "paste": "粘贴", - "pause": "暂停", - "percent": "{0}%", - "performance": "性能", - "permissions": "权限", - "phone": "电话", - "pictures": "图片", - "pinched": "捏合", - "pinned": "固定", - "pixels": "{0}像素", - "placeholder": "占位符", - "play": "播放", - "plugin": "插件", - "plugins": "插件", - "popup": "弹出窗口", - "portrait": "纵向", - "power": "电源", - "power_saver": "节能", - "pressed": "按下", - "previous": "上一个", - "profile": "配置文件", - "properties": "属性", - "quiet": "安静", - "quit": "退出", - "read": "读取", - "recent": "最近", - "recording": "录制", - "refresh": "刷新", - "refresh_rate": "刷新率", - "reload": "重新加载", - "rename": "重命名", - "reset": "重置", - "resize": "调整大小", - "resolution": "分辨率", - "restart": "重启", - "restore": "还原", - "right": "右对齐", - "right_clicked": "右键点击", - "rotate": "旋转", - "rotated": "旋转", - "sample": "样本", - "save": "保存", - "scale": "缩放", - "screenshot": "截图", - "scrolled": "滚动", - "search": "搜索", - "second": "秒", - "seconds": "秒", - "selection": "选择", - "send_to_back": "置于底层", - "settings": "设置", - "shadow": "阴影", - "shortcuts": "快捷键", - "show": "显示", - "shutdown": "关机", - "sidebar": "侧边栏", - "silent": "静默", - "size": "大小", - "snapped": "贴靠", - "software": "软件", - "space": "间距", - "speaker": "扬声器", - "statusbar": "状态栏", - "sticky": "粘性", - "stop": "停止", - "style": "样式", - "success": "成功", - "suspend": "挂起", - "swiped": "滑动", - "systray": "系统托盘", - "tapped": "轻触", - "taskbar": "任务栏", - "temperature": "温度", - "terabytes": "{0} TB", - "terminal": "终端", - "test": "测试", - "theme": "主题", - "tiled": "平铺", - "time": "时间", - "tips": "提示", - "today": "今天", - "tomorrow": "明天", - "toolbar": "工具栏", - "tooltip": "工具提示", - "top": "顶部", - "touchpad": "触摸板", - "trace": "跟踪", - "transform": "变换", - "transition": "过渡", - "transparency": "透明度", - "trash": "回收站", - "tutorial": "教程", - "type": "类型", - "uninstall": "卸载", - "unlock": "解锁", - "unmute": "取消静音", - "unpinned": "取消固定", - "unsticky": "取消粘性", - "update": "更新", - "upgrade": "升级", - "user": "用户", - "username": "用户名", - "verbose": "详细", - "version": "版本", - "videos": "视频", - "visible": "可见", - "volume": "音量", - "warning": "警告", - "welcome": "欢迎", - "widget": "组件", - "widgets": "组件", - "wifi": "Wi-Fi", - "window": "窗口", - "windowed": "窗口化", - "windows": "窗口", - "workspace": "工作区", - "workspaces": "工作区", - "write": "写入", - "yes": "是", - "yesterday": "昨天", - "zoomed": "缩放", "{0} (copied)": "{0}(已复制)", "{0} Safe Storage": "{0} 安全存储", "{0} does not require an API key": "{0} 不需要 API 密钥", "{0} queries pending": "{0} 个查询等待中", - "{0} | Right-click to configure": "{0} | 右键点击进行配置" + "{0} | Right-click to configure": "{0} | 右键点击进行配置", + "Set with /mode PROVIDER": "使用 /mode PROVIDER 设置", + "Invalid API provider. Supported: \n-": "无效的 API 提供商。支持的:\n-", + "Unknown command:": "未知命令:", + "Type /key to get started with online models\nCtrl+O to expand the sidebar\nCtrl+P to detach sidebar into a window": "输入 /key 开始使用在线模型\nCtrl+O 展开侧边栏\nCtrl+P 将侧边栏分离为窗口", + "This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key.": "这是必要的,因为在按住键时,quickshell 中的 GlobalShortcut.onReleased 会在您是否按下其他键时触发。", + "The current API used. Endpoint:": "当前使用的 API。端点:", + "Provider set to": "提供商设置为", + "Invalid model. Supported: \n```": "无效模型。支持的:\n```", + "Interrupts possibility of overview being toggled on release.": "中断松开时切换概览的可能性。", + "Switched to search mode. Continue with the user's request.": "已切换到搜索模式。继续处理用户请求。", + "Message the model... \"{0}\" for commands": "与模型对话... \"{0}\" 查看命令", + "Experimental | Online | Google's model\nCan do a little more but doesn't search quickly": "实验性 | 在线 | Google 模型\n功能更多但搜索速度较慢", + "To set an API key, pass it with the command\n\nTo view the key, pass \"get\" with the command
\n\n### For {0}:\n\n**Link**: {1}\n\n{2}": "要设置 API 密钥,请将其与命令一起传递\n\n要查看密钥,请将 \"get\" 与命令一起传递
\n\n### 对于 {0}:\n\n**链接**:{1}\n\n{2}", + "Enter tags, or \"{0}\" for commands": "输入标签,或 \"{0}\" 查看命令", + "Online via {0} | {1}'s model": "通过 {0} 在线 | {1} 的模型", + "That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number": "没有找到结果。提示:\n- 检查您的标签和 NSFW 设置\n- 如果没有想到标签,请输入页码", + "Online | Google's model\nGives up-to-date information with search.": "在线 | Google 模型\n通过搜索提供最新信息。" } \ No newline at end of file From ccabd24db270092165042dd770fdc34abf5a8399 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=88=E6=9C=88?= <3600911665@qq.com> Date: Tue, 17 Jun 2025 15:47:57 +0800 Subject: [PATCH 03/20] Add imports to service modules to support session functionality --- .config/quickshell/modules/session/Session.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/.config/quickshell/modules/session/Session.qml b/.config/quickshell/modules/session/Session.qml index 0ab3221ed..ae4dfee5e 100644 --- a/.config/quickshell/modules/session/Session.qml +++ b/.config/quickshell/modules/session/Session.qml @@ -1,4 +1,5 @@ import "root:/modules/common" +import "root:/services/" import "root:/modules/common/widgets" import "root:/modules/common/functions/color_utils.js" as ColorUtils import QtQuick From fb0d3f7f405d168c350429ec2eb2c479218e262d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=88=E6=9C=88?= <3600911665@qq.com> Date: Sat, 12 Jul 2025 22:46:24 +0800 Subject: [PATCH 04/20] i18n:Replace qstr with Translation.tr and update the translation file --- .config/quickshell/ii/Translation.qml | 170 ++++++++++++++++++ .../ii/modules/sidebarLeft/AiChat.qml | 6 +- .../ii/modules/sidebarLeft/DescriptionBox.qml | 2 +- .../sidebarLeft/SidebarLeftContent.qml | 6 +- .../sidebarRight/BottomWidgetGroup.qml | 4 +- .../notifications/NotificationList.qml | 2 +- .config/quickshell/translations/en_US.json | 13 +- .config/quickshell/translations/zh_CN.json | 13 +- 8 files changed, 196 insertions(+), 20 deletions(-) create mode 100644 .config/quickshell/ii/Translation.qml diff --git a/.config/quickshell/ii/Translation.qml b/.config/quickshell/ii/Translation.qml new file mode 100644 index 000000000..97aff7c5c --- /dev/null +++ b/.config/quickshell/ii/Translation.qml @@ -0,0 +1,170 @@ +pragma Singleton + +import QtQuick +import Quickshell +import Quickshell.Io +import "root:/modules/common/" + +Singleton { + id: root + + property var translations: ({}) + property string currentLanguage: "en_US" + property var availableLanguages: ["en_US"] + property bool isScanning: false + property bool isLoading: false + + Process { + id: scanLanguagesProcess + command: ["find", Qt.resolvedUrl(Directories.config + "/quickshell/translations/").toString().replace("file://", ""), "-name", "*.json", "-exec", "basename", "{}", ".json", ";"] + running: false + + stdout: SplitParser { + onRead: data => { + if (data.trim().length === 0) return + + var files = data.trim().split('\n') + + for (var i = 0; i < files.length; i++) { + var lang = files[i].trim() + if (lang.length > 0 && root.availableLanguages.indexOf(lang) === -1) { + root.availableLanguages.push(lang) + } + } + } + } + + onExited: (exitCode, exitStatus) => { + root.isScanning = false + if (exitCode !== 0) { + root.availableLanguages = ["en_US"] + } + root.loadTranslations() + } + } + + FileView { + id: translationFileView + onLoaded: { + var textContent = "" + try { + textContent = text() + } catch (e) { + root.translations = {} + root.isLoading = false + return + } + + if (textContent.length === 0) { + root.translations = {} + root.isLoading = false + return + } + + try { + var jsonData = JSON.parse(textContent) + root.translations = jsonData + root.isLoading = false + } catch (e) { + root.translations = {} + root.isLoading = false + } + } + onLoadFailed: (error) => { + root.translations = {} + root.isLoading = false + } + } + + function detectSystemLanguage() { + var locale = Qt.locale().name + return locale + } + + function getLanguageCode() { + var configLang = "auto" + try { + configLang = ConfigOptions.language.ui + } catch (e) { + configLang = "auto" + } + + if (configLang === "auto") { + return detectSystemLanguage() + } else { + if (root.availableLanguages.indexOf(configLang) !== -1) { + return configLang + } else { + return detectSystemLanguage() + } + } + } + + function loadTranslations() { + if (root.isScanning) { + return + } + + var targetLang = getLanguageCode() + root.currentLanguage = targetLang + + // Use empty translations for English (default language) + if (targetLang === "en_US" || targetLang === "en") { + root.translations = {} + return + } + + // Check if target language is available + if (root.availableLanguages.indexOf(targetLang) === -1) { + root.currentLanguage = "en_US" + root.translations = {} + return + } + + // Load translation file + root.isLoading = true + var translationsPath = Qt.resolvedUrl(Directories.config + "/quickshell/translations/" + targetLang + ".json") + translationFileView.path = translationsPath + } + + function tr(text) { + if (!text) { + return "" + } + + var key = text.toString() + + if (root.isLoading) { + return key + } + + if (root.currentLanguage === "en_US" || root.currentLanguage === "en" || !root.translations) { + return key + } + + if (root.translations.hasOwnProperty(key)) { + var translation = root.translations[key] + if (translation && translation.toString().trim().length > 0) { + return translation.toString() + } else { + return translation.toString() + } + } + + return key // Fallback to key name + } + + function reloadTranslations() { + root.scanLanguages() + } + + function scanLanguages() { + var translationsDir = Qt.resolvedUrl(Directories.config + "/quickshell/translations/").toString().replace("file://", "") + root.isScanning = true + scanLanguagesProcess.running = true + } + + Component.onCompleted: { + root.scanLanguages() + } +} diff --git a/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml b/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml index 87d9b8ffc..e8a096a93 100644 --- a/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml +++ b/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml @@ -73,7 +73,7 @@ Item { }, { name: "save", - description: qsTr("Save chat"), + description: Translation.tr("Save chat"), execute: (args) => { const joinedArgs = args.join(" ") if (joinedArgs.trim().length == 0) { @@ -85,7 +85,7 @@ Item { }, { name: "load", - description: qsTr("Load chat"), + description: Translation.tr("Load chat"), execute: (args) => { const joinedArgs = args.join(" ") if (joinedArgs.trim().length == 0) { @@ -97,7 +97,7 @@ Item { }, { name: "clear", - description: qsTr("Clear chat history"), + description: Translation.tr("Clear chat history"), execute: () => { Ai.clearMessages(); } diff --git a/.config/quickshell/ii/modules/sidebarLeft/DescriptionBox.qml b/.config/quickshell/ii/modules/sidebarLeft/DescriptionBox.qml index cb2f7e24b..2ac78f5b9 100644 --- a/.config/quickshell/ii/modules/sidebarLeft/DescriptionBox.qml +++ b/.config/quickshell/ii/modules/sidebarLeft/DescriptionBox.qml @@ -47,7 +47,7 @@ Item { // Tag suggestion description } StyledText { visible: root.showArrows && root.showTab - text: qsTr("or") + text: Translation.tr("or") font.pixelSize: Appearance.font.pixelSize.smaller } KeyboardKey { diff --git a/.config/quickshell/ii/modules/sidebarLeft/SidebarLeftContent.qml b/.config/quickshell/ii/modules/sidebarLeft/SidebarLeftContent.qml index c2fd96148..a40b855ae 100644 --- a/.config/quickshell/ii/modules/sidebarLeft/SidebarLeftContent.qml +++ b/.config/quickshell/ii/modules/sidebarLeft/SidebarLeftContent.qml @@ -19,9 +19,9 @@ Item { required property var scopeRoot anchors.fill: parent property var tabButtonList: [ - ...(Config.options.policies.ai !== 0 ? [{"icon": "neurology", "name": qsTr("Intelligence")}] : []), - {"icon": "translate", "name": qsTr("Translator")}, - ...(Config.options.policies.weeb === 1 ? [{"icon": "bookmark_heart", "name": qsTr("Anime")}] : []) + ...(Config.options.policies.ai !== 0 ? [{"icon": "neurology", "name": Translation.tr("Intelligence")}] : []), + {"icon": "translate", "name": Translation.tr("Translator")}, + ...(Config.options.policies.weeb === 1 ? [{"icon": "bookmark_heart", "name": Translation.tr("Anime")}] : []) ] property int selectedTab: 0 diff --git a/.config/quickshell/ii/modules/sidebarRight/BottomWidgetGroup.qml b/.config/quickshell/ii/modules/sidebarRight/BottomWidgetGroup.qml index efc34d9f7..d2c4af9c6 100644 --- a/.config/quickshell/ii/modules/sidebarRight/BottomWidgetGroup.qml +++ b/.config/quickshell/ii/modules/sidebarRight/BottomWidgetGroup.qml @@ -17,8 +17,8 @@ Rectangle { property int selectedTab: 0 property bool collapsed: Persistent.states.sidebar.bottomGroup.collapsed property var tabs: [ - {"type": "calendar", "name": "Calendar", "icon": "calendar_month", "widget": calendarWidget}, - {"type": "todo", "name": "To Do", "icon": "done_outline", "widget": todoWidget} + {"type": "calendar", "name": Translation.tr("Calendar"), "icon": "calendar_month", "widget": calendarWidget}, + {"type": "todo", "name": Translation.tr("To Do"), "icon": "done_outline", "widget": todoWidget} ] Behavior on implicitHeight { diff --git a/.config/quickshell/ii/modules/sidebarRight/notifications/NotificationList.qml b/.config/quickshell/ii/modules/sidebarRight/notifications/NotificationList.qml index 461a671b9..c334b0529 100644 --- a/.config/quickshell/ii/modules/sidebarRight/notifications/NotificationList.qml +++ b/.config/quickshell/ii/modules/sidebarRight/notifications/NotificationList.qml @@ -85,7 +85,7 @@ Item { anchors.verticalCenter: parent.verticalCenter anchors.leftMargin: 10 horizontalAlignment: Text.AlignHCenter - text: `${Notifications.list.length} notifications` + text: `${Notifications.list.length} ${Translation.tr("notifications")}` opacity: Notifications.list.length > 0 ? 1 : 0 visible: opacity > 0 diff --git a/.config/quickshell/translations/en_US.json b/.config/quickshell/translations/en_US.json index b294db6ea..0e6254fe5 100644 --- a/.config/quickshell/translations/en_US.json +++ b/.config/quickshell/translations/en_US.json @@ -96,8 +96,6 @@ "Run command": "Run command", "Save": "Save", "Save to Downloads": "Save to Downloads", - "Scroll to change brightness": "Scroll to change brightness", - "Scroll to change volume": "Scroll to change volume", "Search": "Search", "Search the web": "Search the web", "Search, calculate or run": "Search, calculate or run", @@ -106,8 +104,6 @@ "Set API key": "Set API key", "Set temperature (randomness) of the model. Values range between 0 to 2 for Gemini, 0 to 1 for other models. Default is 0.5.": "Set temperature (randomness) of the model. Values range between 0 to 2 for Gemini, 0 to 1 for other models. Default is 0.5.", "Set the current API provider": "Set the current API provider", - "Shell configuration created": "Shell configuration created", - "Shell configuration failed to load": "Shell configuration failed to load", "Shutdown": "Shutdown", "Silent": "Silent", "Sleep": "Sleep", @@ -169,5 +165,12 @@ "Experimental | Online | Google's model\nCan do a little more but doesn't search quickly": "Experimental | Online | Google's model\nCan do a little more but doesn't search quickly", "Message the model... \"{0}\" for commands": "Message the model... \"{0}\" for commands", "To set an API key, pass it with the command\n\nTo view the key, pass \"get\" with the command
\n\n### For {0}:\n\n**Link**: {1}\n\n{2}": "To set an API key, pass it with the command\n\nTo view the key, pass \"get\" with the command
\n\n### For {0}:\n\n**Link**: {1}\n\n{2}", - "Settings": "Settings" + "Settings": "Settings", + "Save chat": "Save chat", + "Load chat": "Load chat", + "or": "or", + "Set the system prompt for the model.": "Set the system prompt for the model.", + "To Do": "To Do", + "Calendar": "Calendar", + "notifications": "notifications" } \ No newline at end of file diff --git a/.config/quickshell/translations/zh_CN.json b/.config/quickshell/translations/zh_CN.json index 1c2f1b7bf..878dc5662 100644 --- a/.config/quickshell/translations/zh_CN.json +++ b/.config/quickshell/translations/zh_CN.json @@ -96,8 +96,6 @@ "Run command": "运行命令", "Save": "保存", "Save to Downloads": "保存到下载文件夹", - "Scroll to change brightness": "滚动调节亮度", - "Scroll to change volume": "滚动调节音量", "Search": "搜索", "Search the web": "搜索网络", "Search, calculate or run": "搜索、计算或运行", @@ -106,8 +104,6 @@ "Set API key": "设置 API 密钥", "Set temperature (randomness) of the model. Values range between 0 to 2 for Gemini, 0 to 1 for other models. Default is 0.5.": "设置模型的温度(随机性)。Gemini 模型范围为 0 到 2,其他模型为 0 到 1。默认值为 0.5。", "Set the current API provider": "设置当前 API 提供商", - "Shell configuration created": "Shell 配置已创建", - "Shell configuration failed to load": "Shell 配置加载失败", "Shutdown": "关机", "Silent": "静音", "Sleep": "睡眠", @@ -169,5 +165,12 @@ "Online via {0} | {1}'s model": "通过 {0} 在线 | {1} 的模型", "That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number": "没有找到结果。提示:\n- 检查您的标签和 NSFW 设置\n- 如果没有想到标签,请输入页码", "Online | Google's model\nGives up-to-date information with search.": "在线 | Google 模型\n通过搜索提供最新信息。", - "Settings": "设置" + "Settings": "设置", + "Save chat": "保存对话", + "Load chat": "加载对话", + "or": "或", + "Set the system prompt for the model.": "为模型设置系统提示。", + "To Do": "待办", + "Calendar": "日历", + "notifications": "条通知" } \ No newline at end of file From 99af57fbc6675bcf9187f23acb28589956c97e2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=88=E6=9C=88?= <3600911665@qq.com> Date: Sun, 13 Jul 2025 13:14:02 +0800 Subject: [PATCH 05/20] Updated Chinese translation --- .config/quickshell/translations/zh_CN.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/quickshell/translations/zh_CN.json b/.config/quickshell/translations/zh_CN.json index 878dc5662..0242aa2d2 100644 --- a/.config/quickshell/translations/zh_CN.json +++ b/.config/quickshell/translations/zh_CN.json @@ -58,7 +58,7 @@ "Hold to show workspace numbers, release to show icons": "按住显示工作区编号,松开显示图标", "Increase brightness": "提高亮度", "Input": "输入", - "Intelligence": "智能", + "Intelligence": "智能体", "Interface": "界面", "Invalid arguments. Must provide `key` and `value`.": "参数无效。必须提供 `key` 和 `value`。", "Jump to current month": "跳转到当前月份", From bdb7657e22e175307a28493c1b9c831f9dca5b0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=88=E6=9C=88?= <3600911665@qq.com> Date: Sun, 13 Jul 2025 13:19:53 +0800 Subject: [PATCH 06/20] i18n:Update the translation tool guide --- .../tools/translation-tools-guide.md | 395 ++++++++++-------- 1 file changed, 218 insertions(+), 177 deletions(-) diff --git a/.config/quickshell/translations/tools/translation-tools-guide.md b/.config/quickshell/translations/tools/translation-tools-guide.md index 527576329..96165b99b 100644 --- a/.config/quickshell/translations/tools/translation-tools-guide.md +++ b/.config/quickshell/translations/tools/translation-tools-guide.md @@ -1,246 +1,287 @@ # Translation Management Tool Suite -This tool suite is used to manage project translation files, automatically extract translatable texts, compare differences between different language files, and provide maintenance functions. +This suite is used to manage project translation files, automatically extract translatable texts, compare differences between language files, and provide maintenance functions. ## Tool Components ### 1. `translation-manager.py` - Main Translation Manager - Extract translatable texts - Compare and update translation files -- Interactive adding/removing translation keys +- Interactive addition/removal of translation keys ### 2. `translation-cleaner.py` - Translation File Maintenance Tool - Clean unused translation keys -- Sync key structure across different language files +- Synchronize key structure across different language files ### 3. `manage-translations.sh` - Convenient Wrapper Script -- Provide unified command-line interface -- Display translation status -- Simplify common operations +- Provides a unified command-line interface +- Displays translation status +- Simplifies common operations ## Quick Start -### Check Translation Status +### Using the Wrapper Script (Recommended) + ```bash +# Enter the tools directory +cd .config/quickshell/translations/tools + +# Show help +./manage-translations.sh --help + +# Show current translation status ./manage-translations.sh status -``` -### Extract Translatable Texts -```bash +# Extract translatable texts ./manage-translations.sh extract -``` -### Update Translation Files -```bash -# Update all languages +# Update all translation files ./manage-translations.sh update -# Update specific language +# Update a specific language ./manage-translations.sh update -l zh_CN -``` -### Clean Unused Keys -```bash +# Clean unused keys ./manage-translations.sh clean + +# Synchronize keys across all language files +./manage-translations.sh sync ``` -### Sync Keys Across Languages +Or run from the project root: ```bash -./manage-translations.sh sync +# Run from the project root +.config/quickshell/translations/tools/manage-translations.sh status +.config/quickshell/translations/tools/manage-translations.sh update ``` ## Detailed Usage -### translation-manager.py +### Translation Manager (`translation-manager.py`) -The main translation management tool that extracts translatable texts from source code and manages translation files. - -#### Command Line Options +Basic usage: ```bash -python3 translation-manager.py [options] +# Process all languages +./translation-manager.py -Options: - -h, --help Show help message - -t, --translations-dir DIR Translation files directory (default: .config/quickshell/translations) - -s, --source-dir DIR Source code directory (default: .config/quickshell) - -l, --language LANG Specify language code to process (e.g., zh_CN) - -e, --extract-only Only extract translatable texts to temporary file - --show-temp Show temporary extracted file content +# Specify a particular language +./translation-manager.py --language zh_CN + +# Extract translatable texts only +./translation-manager.py --extract-only + +# Show extracted texts +./translation-manager.py --extract-only --show-temp ``` -#### Features -1. **Text Extraction**: Uses regex patterns to extract translatable texts from QML and JavaScript files -2. **Smart Filtering**: Automatically removes duplicates and cleans up extracted texts -3. **Interactive Updates**: Guides users through adding missing keys and removing extra ones -4. **Backup Support**: Creates backups before making changes +Parameter description: +- `--translations-dir`, `-t`: Translation files directory (default: `.config/quickshell/translations`) +- `--source-dir`, `-s`: Source code directory (default: `.config/quickshell`) +- `--language`, `-l`: Specify the language code to process +- `--extract-only`, `-e`: Only extract translatable texts +- `--show-temp`: Show the content of the temporary extraction file -#### Supported Text Patterns -- `qsTr("text")` and `qsTr('text')` -- `i18n.t("text")` and `i18n.t('text')` -- Supports nested quotes and escape characters -- Handles multiline strings +### Translation Cleaner (`translation-cleaner.py`) -### translation-cleaner.py - -A maintenance tool for cleaning up and synchronizing translation files. - -#### Command Line Options ```bash -python3 translation-cleaner.py [options] +# Clean unused translation keys +./translation-cleaner.py --clean -Options: - -h, --help Show help message - -t, --translations-dir DIR Translation files directory - -s, --source-dir DIR Source code directory - -c, --clean Clean unused translation keys - --sync Sync translation keys - --source-lang LANG Source language for syncing (default: en_US) - --no-backup Do not create backup files when cleaning +# Synchronize translation keys (using en_US as the base) +./translation-cleaner.py --sync + +# Specify a different source language for syncing +./translation-cleaner.py --sync --source-lang zh_CN + +# Clean without creating backups +./translation-cleaner.py --clean --no-backup ``` -#### Features -1. **Unused Key Cleanup**: Identifies and removes translation keys that are no longer used in the source code -2. **Key Synchronization**: Ensures all language files have the same set of keys -3. **Backup Protection**: Creates backup files before making destructive changes -4. **Interactive Confirmation**: Asks for user confirmation before deleting keys +## Workflow -### manage-translations.sh +### Regular Translation Update Workflow -A convenient wrapper script that provides a unified interface to all translation tools. +1. **Check status**: + ```bash + ./manage-translations.sh status + ``` + +2. **Update translations**: + ```bash + ./manage-translations.sh update + ``` + +3. **Clean unused keys** (optional): + ```bash + ./manage-translations.sh clean + ``` + +### Adding a New Language + +1. **Create a new language file**: + ```bash + ./manage-translations.sh update -l new_lang + ``` + +2. **Synchronize key structure**: + ```bash + ./manage-translations.sh sync + ``` + +### Cleanup After Large Refactoring + +1. **Backup translation files**: + ```bash + cp -r .config/quickshell/translations .config/quickshell/translations.backup + ``` + +2. **Clean unused keys**: + ```bash + ./manage-translations.sh clean + ``` + +3. **Synchronize all languages**: + ```bash + ./manage-translations.sh sync + ``` + +## Supported Translatable Text Formats + +The tool recognizes the following formats for translatable texts: + +```qml +// Basic format +Translation.tr("Hello, world!") +Translation.tr('Hello, world!') +Translation.tr(`Hello, world!`) + +// With line breaks +Translation.tr("Line 1\nLine 2") + +// With escape characters +Translation.tr("Say \"Hello\"") + +// With parameter placeholders +Translation.tr("Hello, %1!").arg(name) +Translation.tr("{0} files selected").arg(count) +``` + +## Example Output + +### Status Display +``` +$ ./manage-translations.sh status +Analyzing translation status... +=== Current Project Status === +166 translatable texts extracted + +=== Translation File Status === + en_US: 470 keys + zh_CN: 470 keys +``` + +### Update Translations +``` +$ ./manage-translations.sh update -l zh_CN +Updating translation files... +================================================== +Processing language: zh_CN +================================================== +Analysis result: + Missing keys: 5 + Extra keys: 20 + +Found 5 missing translation keys: +1. "New feature text" +2. "Another new text" +... + +Add these 5 missing keys? (y/n): y +5 keys added + +Found 20 extra translation keys: +1. "Removed old text" -> "已删除的旧文本" +... + +Delete these 20 extra keys? (y/n): y +20 keys deleted + +Translation file saved +``` + +### Clean Unused Keys +``` +$ ./manage-translations.sh clean +Cleaning unused translation keys... +Processing language: zh_CN +Found 50 unused keys: + 1. "old_unused_text" + 2. "deprecated_message" + ... + +Delete these 50 unused keys? (y/n): y +50 keys deleted +Original key count: 470, after cleaning: 420 +``` + +## Advanced Features + +### Custom Directory Structure -#### Commands ```bash -./manage-translations.sh [options] - -Commands: - extract Extract translatable texts to temporary file - update Update translation files (add missing/remove extra keys) - clean Clean unused translation keys - sync Sync keys across all language files - status Show translation status - -Options: - -l, --lang LANG Specify language (e.g.: zh_CN) - -t, --trans-dir DIR Translation files directory - -s, --source-dir DIR Source code directory - -h, --help Show help message +# Use custom directories +./translation-manager.py \ + --translations-dir /path/to/translations \ + --source-dir /path/to/source ``` -## Workflow Examples +### Batch Processing -### Initial Setup -1. Create translation directory structure -2. Extract all translatable texts: `./manage-translations.sh extract` -3. Create initial translation files: `./manage-translations.sh update` +```bash +# Create a processing script +cat > update-all-translations.sh << 'EOF' +#!/bin/bash +for lang in zh_CN ja_JP ko_KR; do + echo "Processing $lang..." + ./manage-translations.sh update -l $lang +done +EOF -### Regular Maintenance -1. Check status: `./manage-translations.sh status` -2. Update translations after code changes: `./manage-translations.sh update` -3. Clean up unused keys periodically: `./manage-translations.sh clean` - -### Adding New Language -1. Create new language file: `./manage-translations.sh update -l new_lang` -2. Sync keys if needed: `./manage-translations.sh sync` - -## File Structure - -``` -translations/ -├── tools/ # Translation management tools -│ ├── translation-manager.py # Main extraction and update tool -│ ├── translation-cleaner.py # Cleanup and sync tool -│ ├── manage-translations.sh # Wrapper script -│ └── translation-tools-guide.md # This documentation -├── en_US.json # English translations (reference) -├── zh_CN.json # Chinese translations -└── [other_lang].json # Other language files +chmod +x update-all-translations.sh +./update-all-translations.sh ``` -## Configuration +## Notes -### Text Extraction Patterns +1. **Backup is important**: The tool automatically creates backups before cleaning, but it is recommended to manually back up important files -The tool uses regex patterns to identify translatable texts. Current patterns include: +2. **Text extraction limitations**: + - Only supports static strings, not dynamically constructed strings + - Must use the `Translation.tr()` format -1. **QML qsTr patterns**: - - `qsTr("text")` and `qsTr('text')` - - Supports escaped quotes and nested quotes +3. **File encoding**: All files must use UTF-8 encoding -2. **JavaScript i18n patterns**: - - `i18n.t("text")` and `i18n.t('text')` - - Supports template literals and complex expressions - -3. **Custom patterns** can be added by modifying the patterns list in `translation-manager.py` - -### File Extensions - -By default, the tool processes: -- `.qml` files (QML/QtQuick) -- `.js` files (JavaScript) - -Additional file types can be added by modifying the file extension filters. - -## Best Practices - -1. **Regular Updates**: Run `./manage-translations.sh status` regularly to check for new translatable texts -2. **Clean Periodically**: Use `./manage-translations.sh clean` to remove unused keys -3. **Backup Important**: Always backup translation files before major changes -4. **Consistent Patterns**: Use consistent function calls (`qsTr`, `i18n.t`) for translatable texts -5. **Review Changes**: Always review the changes before confirming deletions or additions +4. **Key naming conventions**: It is recommended to use English for key names and avoid special characters ## Troubleshooting ### Common Issues -1. **Missing Texts**: If some translatable texts are not extracted, check if they match the supported patterns -2. **Path Issues**: Ensure the source and translation directory paths are correct -3. **Permission Errors**: Make sure the script has write permissions to the translation directory -4. **Encoding Issues**: All files should be saved in UTF-8 encoding +**Q: The number of extracted texts does not match expectations?** +A: Check whether all translatable texts use the `Translation.tr()` format and ensure there are no dynamically constructed strings. -### Getting Help +**Q: Some translations are missing after syncing?** +A: Check whether the source language file contains all necessary keys, and consider using a different source language for syncing. -For additional help: -- Use `--help` option with any tool -- Check the console output for error messages -- Verify file paths and permissions +**Q: The cleaning operation deleted needed keys?** +A: Restore from the automatically created backup file and check whether `Translation.tr()` is used correctly in the source code. -## Advanced Usage - -### Custom Regex Patterns - -To add support for new translation function patterns, modify the `patterns` list in `TranslationManager.extract_translatable_texts()`: - -```python -patterns = [ - # Existing patterns... - r'customTranslate\s*\(\s*(["\'])((?:\\.|(?!\1)[^\\])*?)\1\s*\)', # Custom pattern -] -``` - -### Batch Processing - -For processing multiple projects or directories: +### Restore Backup ```bash -# Process multiple directories -for dir in project1 project2 project3; do - ./manage-translations.sh -s "$dir" -t "$dir/translations" update -done +# Restore a single file +cp .config/quickshell/translations/zh_CN.json.backup .config/quickshell/translations/zh_CN.json + +# Restore all files +cp .config/quickshell/translations.backup/* .config/quickshell/translations/ ``` - -### Integration with Build Systems - -The tools can be integrated into build systems to automatically update translations: - -```bash -# In your build script -./manage-translations.sh update --non-interactive -``` - -## Version History - -- **v1.0**: Initial version with basic extraction and update functionality -- **v1.1**: Added improved regex patterns for better text extraction -- **v1.2**: Added cleaning and synchronization tools -- **v1.3**: Added English output and improved user interface -- **v1.4**: Moved all tools to dedicated tools directory and improved documentation From 0c2b807447a94ac7bce58c4f117a9c2a3485c5c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=88=E6=9C=88?= <3600911665@qq.com> Date: Sun, 13 Jul 2025 15:24:29 +0800 Subject: [PATCH 07/20] i18n: update translations/tools/README.md --- .../quickshell/translations/tools/README.md | 305 ++++++++++++++---- 1 file changed, 241 insertions(+), 64 deletions(-) diff --git a/.config/quickshell/translations/tools/README.md b/.config/quickshell/translations/tools/README.md index 1840f6c3d..e41c742e9 100644 --- a/.config/quickshell/translations/tools/README.md +++ b/.config/quickshell/translations/tools/README.md @@ -1,111 +1,288 @@ -# Translation Management Tools +# Translation Management Tool Suite -This directory contains a toolset for managing project translation files. +This suite is used to manage project translation files, automatically extract translatable texts, compare differences between language files, and provide maintenance functions. -## Directory Structure +## Tool Components -``` -translations/ -├── tools/ # Translation management tools directory -│ ├── translation-manager.py # Main translation manager -│ ├── translation-cleaner.py # Translation maintenance tool -│ ├── manage-translations.sh # Convenient wrapper script -│ ├── translation-tools-guide.md # Detailed usage documentation -│ └── README.md # This file -├── en_US.json # English translation file -├── zh_CN.json # Chinese translation file -└── ... # Other language files -``` +### 1. `translation-manager.py` - Main Translation Manager +- Extract translatable texts +- Compare and update translation files +- Interactive addition/removal of translation keys + +### 2. `translation-cleaner.py` - Translation File Maintenance Tool +- Clean unused translation keys +- Synchronize key structure across different language files + +### 3. `manage-translations.sh` - Convenient Wrapper Script +- Provides a unified command-line interface +- Displays translation status +- Simplifies common operations ## Quick Start -### Running from tools directory +### Using the Wrapper Script (Recommended) ```bash -# Enter tools directory +# Enter the tools directory cd .config/quickshell/translations/tools -# Check current translation status +# Show help +./manage-translations.sh --help + +# Show current translation status ./manage-translations.sh status +# Extract translatable texts +./manage-translations.sh extract + # Update all translation files ./manage-translations.sh update -# Update specific language +# Update a specific language ./manage-translations.sh update -l zh_CN # Clean unused keys ./manage-translations.sh clean -# Sync all language files +# Synchronize keys across all language files ./manage-translations.sh sync ``` -### Running from project root directory - +Or run from the project root: ```bash -# Run from project root directory (recommended to use relative paths) +# Run from the project root .config/quickshell/translations/tools/manage-translations.sh status .config/quickshell/translations/tools/manage-translations.sh update ``` -## Tool Description +## Detailed Usage -### 🛠️ `manage-translations.sh` - Main Entry Point -Convenient command-line interface that integrates all translation management functions. +### Translation Manager (`translation-manager.py`) -### 🔍 `translation-manager.py` - Core Manager -- Extract translatable texts -- Compare translation file differences -- Interactive translation updates - -### 🧹 `translation-cleaner.py` - Maintenance Tool -- Clean unused translation keys -- Sync language file structure -- Create backup files - -## Common Workflows - -### After adding new translatable texts +Basic usage: ```bash -./manage-translations.sh update +# Process all languages +./translation-manager.py + +# Specify a particular language +./translation-manager.py --language zh_CN + +# Extract translatable texts only +./translation-manager.py --extract-only + +# Show extracted texts +./translation-manager.py --extract-only --show-temp ``` -### Clean up after code refactoring +Parameter description: +- `--translations-dir`, `-t`: Translation files directory (default: `.config/quickshell/translations`) +- `--source-dir`, `-s`: Source code directory (default: `.config/quickshell`) +- `--language`, `-l`: Specify the language code to process +- `--extract-only`, `-e`: Only extract translatable texts +- `--show-temp`: Show the content of the temporary extraction file + +### Translation Cleaner (`translation-cleaner.py`) + ```bash -./manage-translations.sh clean +# Clean unused translation keys +./translation-cleaner.py --clean + +# Synchronize translation keys (using en_US as the base) +./translation-cleaner.py --sync + +# Specify a different source language for syncing +./translation-cleaner.py --sync --source-lang zh_CN + +# Clean without creating backups +./translation-cleaner.py --clean --no-backup ``` -### Add new language -```bash -./manage-translations.sh update -l new_language_code -``` +## Workflow -### Check translation status -```bash -./manage-translations.sh status -``` +### Regular Translation Update Workflow -## Documentation +1. **Check status**: + ```bash + ./manage-translations.sh status + ``` -- 📖 [Detailed Usage Guide](./translation-tools-guide.md) +2. **Update translations**: + ```bash + ./manage-translations.sh update + ``` -## Important Notes +3. **Clean unused keys** (optional): + ```bash + ./manage-translations.sh clean + ``` -1. **Running Location**: Tools automatically detect relative paths, can be run from tools directory or project root -2. **Backup**: Cleanup operations automatically create backup files -3. **Encoding**: All files use UTF-8 encoding -4. **Permissions**: Ensure scripts have execution permissions +### Adding a New Language -## Supported Translation Formats +1. **Create a new language file**: + ```bash + ./manage-translations.sh update -l new_lang + ``` + +2. **Synchronize key structure**: + ```bash + ./manage-translations.sh sync + ``` + +### Cleanup After Large Refactoring + +1. **Backup translation files**: + ```bash + cp -r .config/quickshell/translations .config/quickshell/translations.backup + ``` + +2. **Clean unused keys**: + ```bash + ./manage-translations.sh clean + ``` + +3. **Synchronize all languages**: + ```bash + ./manage-translations.sh sync + ``` + +## Supported Translatable Text Formats + +The tool recognizes the following formats for translatable texts: -The tool recognizes translatable texts in the following formats: ```qml -qsTr("Your text here") -qsTr('Single quotes work too') -i18n.t("JavaScript translations") +// Basic format +Translation.tr("Hello, world!") +Translation.tr('Hello, world!') +Translation.tr(`Hello, world!`) + +// With line breaks +Translation.tr("Line 1\nLine 2") + +// With escape characters +Translation.tr("Say \"Hello\"") + +// With parameter placeholders +Translation.tr("Hello, %1!").arg(name) +Translation.tr("{0} files selected").arg(count) ``` ---- +## Example Output + +### Status Display +``` +$ ./manage-translations.sh status +Analyzing translation status... +=== Current Project Status === +166 translatable texts extracted + +=== Translation File Status === + en_US: 470 keys + zh_CN: 470 keys +``` + +### Update Translations +``` +$ ./manage-translations.sh update -l zh_CN +Updating translation files... +================================================== +Processing language: zh_CN +================================================== +Analysis result: + Missing keys: 5 + Extra keys: 20 + +Found 5 missing translation keys: +1. "New feature text" +2. "Another new text" +... + +Add these 5 missing keys? (y/n): y +5 keys added + +Found 20 extra translation keys: +1. "Removed old text" -> "已删除的旧文本" +... + +Delete these 20 extra keys? (y/n): y +20 keys deleted + +Translation file saved +``` + +### Clean Unused Keys +``` +$ ./manage-translations.sh clean +Cleaning unused translation keys... +Processing language: zh_CN +Found 50 unused keys: + 1. "old_unused_text" + 2. "deprecated_message" + ... + +Delete these 50 unused keys? (y/n): y +50 keys deleted +Original key count: 470, after cleaning: 420 +``` + +## Advanced Features + +### Custom Directory Structure + +```bash +# Use custom directories +./translation-manager.py \ + --translations-dir /path/to/translations \ + --source-dir /path/to/source +``` + +### Batch Processing + +```bash +# Create a processing script +cat > update-all-translations.sh << 'EOF' +#!/bin/bash +for lang in zh_CN ja_JP ko_KR; do + echo "Processing $lang..." + ./manage-translations.sh update -l $lang +done +EOF + +chmod +x update-all-translations.sh +./update-all-translations.sh +``` + +## Notes + +1. **Backup is important**: The tool automatically creates backups before cleaning, but it is recommended to manually back up important files + +2. **Text extraction limitations**: + - Only supports static strings, not dynamically constructed strings + - Must use the `Translation.tr()` format + +3. **File encoding**: All files must use UTF-8 encoding + +4. **Key naming conventions**: It is recommended to use English for key names and avoid special characters + +## Troubleshooting + +### Common Issues + +**Q: The number of extracted texts does not match expectations?** +A: Check whether all translatable texts use the `Translation.tr()` format and ensure there are no dynamically constructed strings. + +**Q: Some translations are missing after syncing?** +A: Check whether the source language file contains all necessary keys, and consider using a different source language for syncing. + +**Q: The cleaning operation deleted needed keys?** +A: Restore from the automatically created backup file and check whether `Translation.tr()` is used correctly in the source code. + +### Restore Backup + +```bash +# Restore a single file +cp .config/quickshell/translations/zh_CN.json.backup .config/quickshell/translations/zh_CN.json + +# Restore all files +cp .config/quickshell/translations.backup/* .config/quickshell/translations/ +``` -If you have any issues, please refer to the detailed documentation or check error messages in script output. From 8f3b2474d20b6871ad18a2cce9468334291fb871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=88=E6=9C=88?= <3600911665@qq.com> Date: Mon, 14 Jul 2025 00:33:03 +0800 Subject: [PATCH 08/20] i18n: Add multilingual support for the Settings interface and update the Simplified Chinese translation file. --- .../widgets/LightDarkPreferenceButton.qml | 3 +- .../quickshell/ii/modules/settings/About.qml | 22 +-- .../ii/modules/settings/AdvancedConfig.qml | 12 +- .../ii/modules/settings/InterfaceConfig.qml | 100 ++++++------- .../ii/modules/settings/ServicesConfig.qml | 62 ++++----- .../ii/modules/settings/StyleConfig.qml | 56 ++++---- .config/quickshell/ii/settings.qml | 14 +- .config/quickshell/ii/welcome.qml | 56 ++++---- .config/quickshell/translations/en_US.json | 131 +++++++++++++++++- .config/quickshell/translations/zh_CN.json | 131 +++++++++++++++++- 10 files changed, 423 insertions(+), 164 deletions(-) diff --git a/.config/quickshell/ii/modules/common/widgets/LightDarkPreferenceButton.qml b/.config/quickshell/ii/modules/common/widgets/LightDarkPreferenceButton.qml index 1733bca37..69db75674 100644 --- a/.config/quickshell/ii/modules/common/widgets/LightDarkPreferenceButton.qml +++ b/.config/quickshell/ii/modules/common/widgets/LightDarkPreferenceButton.qml @@ -1,3 +1,4 @@ +import "root:/" import "root:/modules/common" import "root:/modules/common/widgets" import "root:/modules/common/functions/color_utils.js" as ColorUtils @@ -115,7 +116,7 @@ GroupButton { } StyledText { Layout.fillWidth: true - text: dark ? "Dark" : "Light" + text: dark ? Translation.tr("Dark") : Translation.tr("Light") color: lightDarkButtonRoot.toggled ? Appearance.m3colors.m3onPrimary : Appearance.colors.colOnLayer2 horizontalAlignment: Text.AlignHCenter } diff --git a/.config/quickshell/ii/modules/settings/About.qml b/.config/quickshell/ii/modules/settings/About.qml index dc1c93d70..4db99b68b 100644 --- a/.config/quickshell/ii/modules/settings/About.qml +++ b/.config/quickshell/ii/modules/settings/About.qml @@ -12,7 +12,7 @@ ContentPage { forceWidth: true ContentSection { - title: "Distro" + title: Translation.tr("Distro") RowLayout { Layout.alignment: Qt.AlignHCenter @@ -48,21 +48,21 @@ ContentPage { RippleButtonWithIcon { materialIcon: "auto_stories" - mainText: "Documentation" + mainText: Translation.tr("Documentation") onClicked: { Qt.openUrlExternally(SystemInfo.documentationUrl) } } RippleButtonWithIcon { materialIcon: "support" - mainText: "Help & Support" + mainText: Translation.tr("Help & Support") onClicked: { Qt.openUrlExternally(SystemInfo.supportUrl) } } RippleButtonWithIcon { materialIcon: "bug_report" - mainText: "Report a Bug" + mainText: Translation.tr("Report a Bug") onClicked: { Qt.openUrlExternally(SystemInfo.bugReportUrl) } @@ -70,7 +70,7 @@ ContentPage { RippleButtonWithIcon { materialIcon: "policy" materialIconFill: false - mainText: "Privacy Policy" + mainText: Translation.tr("Privacy Policy") onClicked: { Qt.openUrlExternally(SystemInfo.privacyPolicyUrl) } @@ -80,7 +80,7 @@ ContentPage { } ContentSection { - title: "Dotfiles" + title: Translation.tr("Dotfiles") RowLayout { Layout.alignment: Qt.AlignHCenter @@ -95,7 +95,7 @@ ContentPage { Layout.alignment: Qt.AlignVCenter // spacing: 10 StyledText { - text: "illogical-impulse" + text: Translation.tr("illogical-impulse") font.pixelSize: Appearance.font.pixelSize.title } StyledText { @@ -116,7 +116,7 @@ ContentPage { RippleButtonWithIcon { materialIcon: "auto_stories" - mainText: "Documentation" + mainText: Translation.tr("Documentation") onClicked: { Qt.openUrlExternally("https://end-4.github.io/dots-hyprland-wiki/en/ii-qs/02usage/") } @@ -124,21 +124,21 @@ ContentPage { RippleButtonWithIcon { materialIcon: "adjust" materialIconFill: false - mainText: "Issues" + mainText: Translation.tr("Issues") onClicked: { Qt.openUrlExternally("https://github.com/end-4/dots-hyprland/issues") } } RippleButtonWithIcon { materialIcon: "forum" - mainText: "Discussions" + mainText: Translation.tr("Discussions") onClicked: { Qt.openUrlExternally("https://github.com/end-4/dots-hyprland/discussions") } } RippleButtonWithIcon { materialIcon: "favorite" - mainText: "Donate" + mainText: Translation.tr("Donate") onClicked: { Qt.openUrlExternally("https://github.com/sponsors/end-4") } diff --git a/.config/quickshell/ii/modules/settings/AdvancedConfig.qml b/.config/quickshell/ii/modules/settings/AdvancedConfig.qml index d0f22d4d7..c72922319 100644 --- a/.config/quickshell/ii/modules/settings/AdvancedConfig.qml +++ b/.config/quickshell/ii/modules/settings/AdvancedConfig.qml @@ -9,35 +9,35 @@ ContentPage { forceWidth: true ContentSection { - title: "Color generation" + title: Translation.tr("Color generation") ConfigRow { uniform: true ConfigSwitch { - text: "Shell & utilities" + text: Translation.tr("Shell & utilities") checked: Config.options.appearance.wallpaperTheming.enableAppsAndShell onCheckedChanged: { Config.options.appearance.wallpaperTheming.enableAppsAndShell = checked; } } ConfigSwitch { - text: "Qt apps" + text: Translation.tr("Qt apps") checked: Config.options.appearance.wallpaperTheming.enableQtApps onCheckedChanged: { Config.options.appearance.wallpaperTheming.enableQtApps = checked; } StyledToolTip { - content: "Shell & utilities theming must also be enabled" + content: Translation.tr("Shell & utilities theming must also be enabled") } } ConfigSwitch { - text: "Terminal" + text: Translation.tr("Terminal") checked: Config.options.appearance.wallpaperTheming.enableTerminal onCheckedChanged: { Config.options.appearance.wallpaperTheming.enableTerminal = checked; } StyledToolTip { - content: "Shell & utilities theming must also be enabled" + content: Translation.tr("Shell & utilities theming must also be enabled") } } diff --git a/.config/quickshell/ii/modules/settings/InterfaceConfig.qml b/.config/quickshell/ii/modules/settings/InterfaceConfig.qml index 2d692442c..9a269143a 100644 --- a/.config/quickshell/ii/modules/settings/InterfaceConfig.qml +++ b/.config/quickshell/ii/modules/settings/InterfaceConfig.qml @@ -8,13 +8,13 @@ import "root:/modules/common/widgets/" ContentPage { forceWidth: true ContentSection { - title: "Policies" + title: Translation.tr("Policies") ConfigRow { ColumnLayout { // Weeb policy ContentSubsectionLabel { - text: "Weeb" + text: Translation.tr("Weeb") } ConfigSelectionArray { currentValue: Config.options.policies.weeb @@ -24,15 +24,15 @@ ContentPage { } options: [ { - displayName: "No", + displayName: Translation.tr("No"), value: 0 }, { - displayName: "Yes", + displayName: Translation.tr("Yes"), value: 1 }, { - displayName: "Closet", + displayName: Translation.tr("Closet"), value: 2 } ] @@ -42,7 +42,7 @@ ContentPage { ColumnLayout { // AI policy ContentSubsectionLabel { - text: "AI" + text: Translation.tr("AI") } ConfigSelectionArray { currentValue: Config.options.policies.ai @@ -52,15 +52,15 @@ ContentPage { } options: [ { - displayName: "No", + displayName: Translation.tr("No"), value: 0 }, { - displayName: "Yes", + displayName: Translation.tr("Yes"), value: 1 }, { - displayName: "Local only", + displayName: Translation.tr("Local only"), value: 2 } ] @@ -70,7 +70,7 @@ ContentPage { } ContentSection { - title: "Bar" + title: Translation.tr("Bar") ConfigSelectionArray { currentValue: Config.options.bar.cornerStyle @@ -80,57 +80,57 @@ ContentPage { } options: [ { - displayName: "Hug", + displayName: Translation.tr("Hug"), value: 0 }, { - displayName: "Float", + displayName: Translation.tr("Float"), value: 1 }, { - displayName: "Plain rectangle", + displayName: Translation.tr("Plain rectangle"), value: 2 } ] } ContentSubsection { - title: "Appearance" + title: Translation.tr("Appearance") ConfigRow { uniform: true ConfigSwitch { - text: 'Borderless' + text: Translation.tr('Borderless') checked: Config.options.bar.borderless onCheckedChanged: { Config.options.bar.borderless = checked; } } ConfigSwitch { - text: 'Show background' + text: Translation.tr('Show background') checked: Config.options.bar.showBackground onCheckedChanged: { Config.options.bar.showBackground = checked; } StyledToolTip { - content: "Note: turning off can hurt readability" + content: Translation.tr("Note: turning off can hurt readability") } } } } ContentSubsection { - title: "Buttons" + title: Translation.tr("Buttons") ConfigRow { uniform: true ConfigSwitch { - text: "Screen snip" + text: Translation.tr("Screen snip") checked: Config.options.bar.utilButtons.showScreenSnip onCheckedChanged: { Config.options.bar.utilButtons.showScreenSnip = checked; } } ConfigSwitch { - text: "Color picker" + text: Translation.tr("Color picker") checked: Config.options.bar.utilButtons.showColorPicker onCheckedChanged: { Config.options.bar.utilButtons.showColorPicker = checked; @@ -140,14 +140,14 @@ ContentPage { ConfigRow { uniform: true ConfigSwitch { - text: "Mic toggle" + text: Translation.tr("Mic toggle") checked: Config.options.bar.utilButtons.showMicToggle onCheckedChanged: { Config.options.bar.utilButtons.showMicToggle = checked; } } ConfigSwitch { - text: "Keyboard toggle" + text: Translation.tr("Keyboard toggle") checked: Config.options.bar.utilButtons.showKeyboardToggle onCheckedChanged: { Config.options.bar.utilButtons.showKeyboardToggle = checked; @@ -157,7 +157,7 @@ ContentPage { ConfigRow { uniform: true ConfigSwitch { - text: "Dark/Light toggle" + text: Translation.tr("Dark/Light toggle") checked: Config.options.bar.utilButtons.showDarkModeToggle onCheckedChanged: { Config.options.bar.utilButtons.showDarkModeToggle = checked; @@ -171,20 +171,20 @@ ContentPage { } ContentSubsection { - title: "Workspaces" - tooltip: "Tip: Hide icons and always show numbers for\nthe classic illogical-impulse experience" + title: Translation.tr("Workspaces") + tooltip: Translation.tr("Tip: Hide icons and always show numbers for\nthe classic illogical-impulse experience") ConfigRow { uniform: true ConfigSwitch { - text: 'Show app icons' + text: Translation.tr('Show app icons') checked: Config.options.bar.workspaces.showAppIcons onCheckedChanged: { Config.options.bar.workspaces.showAppIcons = checked; } } ConfigSwitch { - text: 'Always show numbers' + text: Translation.tr('Always show numbers') checked: Config.options.bar.workspaces.alwaysShowNumbers onCheckedChanged: { Config.options.bar.workspaces.alwaysShowNumbers = checked; @@ -192,7 +192,7 @@ ContentPage { } } ConfigSpinBox { - text: "Workspaces shown" + text: Translation.tr("Workspaces shown") value: Config.options.bar.workspaces.shown from: 1 to: 30 @@ -202,7 +202,7 @@ ContentPage { } } ConfigSpinBox { - text: "Number show delay when pressing Super (ms)" + text: Translation.tr("Number show delay when pressing Super (ms)") value: Config.options.bar.workspaces.showNumberDelay from: 0 to: 1000 @@ -214,9 +214,9 @@ ContentPage { } ContentSubsection { - title: "Weather" + title: Translation.tr("Weather") ConfigSwitch { - text: "Enable" + text: Translation.tr("Enable") checked: Config.options.bar.weather.enable onCheckedChanged: { Config.options.bar.weather.enable = checked; @@ -226,12 +226,12 @@ ContentPage { } ContentSection { - title: "Battery" + title: Translation.tr("Battery") ConfigRow { uniform: true ConfigSpinBox { - text: "Low warning" + text: Translation.tr("Low warning") value: Config.options.battery.low from: 0 to: 100 @@ -241,7 +241,7 @@ ContentPage { } } ConfigSpinBox { - text: "Critical warning" + text: Translation.tr("Critical warning") value: Config.options.battery.critical from: 0 to: 100 @@ -254,17 +254,17 @@ ContentPage { ConfigRow { uniform: true ConfigSwitch { - text: "Automatic suspend" + text: Translation.tr("Automatic suspend") checked: Config.options.battery.automaticSuspend onCheckedChanged: { Config.options.battery.automaticSuspend = checked; } StyledToolTip { - content: "Automatically suspends the system when battery is low" + content: Translation.tr("Automatically suspends the system when battery is low") } } ConfigSpinBox { - text: "Suspend at" + text: Translation.tr("Suspend at") value: Config.options.battery.suspend from: 0 to: 100 @@ -277,10 +277,10 @@ ContentPage { } ContentSection { - title: "Dock" + title: Translation.tr("Dock") ConfigSwitch { - text: "Enable" + text: Translation.tr("Enable") checked: Config.options.dock.enable onCheckedChanged: { Config.options.dock.enable = checked; @@ -290,14 +290,14 @@ ContentPage { ConfigRow { uniform: true ConfigSwitch { - text: "Hover to reveal" + text: Translation.tr("Hover to reveal") checked: Config.options.dock.hoverToReveal onCheckedChanged: { Config.options.dock.hoverToReveal = checked; } } ConfigSwitch { - text: "Pinned on startup" + text: Translation.tr("Pinned on startup") checked: Config.options.dock.pinnedOnStartup onCheckedChanged: { Config.options.dock.pinnedOnStartup = checked; @@ -307,9 +307,9 @@ ContentPage { } ContentSection { - title: "On-screen display" + title: Translation.tr("On-screen display") ConfigSpinBox { - text: "Timeout (ms)" + text: Translation.tr("Timeout (ms)") value: Config.options.osd.timeout from: 100 to: 3000 @@ -321,9 +321,9 @@ ContentPage { } ContentSection { - title: "Overview" + title: Translation.tr("Overview") ConfigSpinBox { - text: "Scale (%)" + text: Translation.tr("Scale (%)") value: Config.options.overview.scale * 100 from: 1 to: 100 @@ -335,7 +335,7 @@ ContentPage { ConfigRow { uniform: true ConfigSpinBox { - text: "Rows" + text: Translation.tr("Rows") value: Config.options.overview.rows from: 1 to: 20 @@ -345,7 +345,7 @@ ContentPage { } } ConfigSpinBox { - text: "Columns" + text: Translation.tr("Columns") value: Config.options.overview.columns from: 1 to: 20 @@ -358,16 +358,16 @@ ContentPage { } ContentSection { - title: "Screenshot tool" + title: Translation.tr("Screenshot tool") ConfigSwitch { - text: 'Show regions of potential interest' + text: Translation.tr('Show regions of potential interest') checked: Config.options.screenshotTool.showContentRegions onCheckedChanged: { Config.options.screenshotTool.showContentRegions = checked; } StyledToolTip { - content: "Such regions could be images or parts of the screen that have some containment.\nMight not always be accurate.\nThis is done with an image processing algorithm run locally and no AI is used." + content: Translation.tr("Such regions could be images or parts of the screen that have some containment.\nMight not always be accurate.\nThis is done with an image processing algorithm run locally and no AI is used.") } } } diff --git a/.config/quickshell/ii/modules/settings/ServicesConfig.qml b/.config/quickshell/ii/modules/settings/ServicesConfig.qml index a9e34512b..cc0c6185b 100644 --- a/.config/quickshell/ii/modules/settings/ServicesConfig.qml +++ b/.config/quickshell/ii/modules/settings/ServicesConfig.qml @@ -9,22 +9,22 @@ ContentPage { forceWidth: true ContentSection { - title: "Audio" + title: Translation.tr("Audio") ConfigSwitch { - text: "Earbang protection" + text: Translation.tr("Earbang protection") checked: Config.options.audio.protection.enable onCheckedChanged: { Config.options.audio.protection.enable = checked; } StyledToolTip { - content: "Prevents abrupt increments and restricts volume limit" + content: Translation.tr("Prevents abrupt increments and restricts volume limit") } } ConfigRow { // uniform: true ConfigSpinBox { - text: "Max allowed increase" + text: Translation.tr("Max allowed increase") value: Config.options.audio.protection.maxAllowedIncrease from: 0 to: 100 @@ -34,7 +34,7 @@ ContentPage { } } ConfigSpinBox { - text: "Volume limit" + text: Translation.tr("Volume limit") value: Config.options.audio.protection.maxAllowed from: 0 to: 100 @@ -46,10 +46,10 @@ ContentPage { } } ContentSection { - title: "AI" + title: Translation.tr("AI") MaterialTextField { Layout.fillWidth: true - placeholderText: "System prompt" + placeholderText: Translation.tr("System prompt") text: Config.options.ai.systemPrompt wrapMode: TextEdit.Wrap onTextChanged: { @@ -61,12 +61,12 @@ ContentPage { } ContentSection { - title: "Battery" + title: Translation.tr("Battery") ConfigRow { uniform: true ConfigSpinBox { - text: "Low warning" + text: Translation.tr("Low warning") value: Config.options.battery.low from: 0 to: 100 @@ -76,7 +76,7 @@ ContentPage { } } ConfigSpinBox { - text: "Critical warning" + text: Translation.tr("Critical warning") value: Config.options.battery.critical from: 0 to: 100 @@ -89,17 +89,17 @@ ContentPage { ConfigRow { uniform: true ConfigSwitch { - text: "Automatic suspend" + text: Translation.tr("Automatic suspend") checked: Config.options.battery.automaticSuspend onCheckedChanged: { Config.options.battery.automaticSuspend = checked; } StyledToolTip { - content: "Automatically suspends the system when battery is low" + content: Translation.tr("Automatically suspends the system when battery is low") } } ConfigSpinBox { - text: "Suspend at" + text: Translation.tr("Suspend at") value: Config.options.battery.suspend from: 0 to: 100 @@ -112,10 +112,10 @@ ContentPage { } ContentSection { - title: "Networking" + title: Translation.tr("Networking") MaterialTextField { Layout.fillWidth: true - placeholderText: "User agent (for services that require it)" + placeholderText: Translation.tr("User agent (for services that require it)") text: Config.options.networking.userAgent wrapMode: TextEdit.Wrap onTextChanged: { @@ -125,9 +125,9 @@ ContentPage { } ContentSection { - title: "Resources" + title: Translation.tr("Resources") ConfigSpinBox { - text: "Polling interval (ms)" + text: Translation.tr("Polling interval (ms)") value: Config.options.resources.updateInterval from: 100 to: 10000 @@ -139,27 +139,27 @@ ContentPage { } ContentSection { - title: "Search" + title: Translation.tr("Search") ConfigSwitch { - text: "Use Levenshtein distance-based algorithm instead of fuzzy" + text: Translation.tr("Use Levenshtein distance-based algorithm instead of fuzzy") checked: Config.options.search.sloppy onCheckedChanged: { Config.options.search.sloppy = checked; } StyledToolTip { - content: "Could be better if you make a ton of typos,\nbut results can be weird and might not work with acronyms\n(e.g. \"GIMP\" might not give you the paint program)" + content: Translation.tr("Could be better if you make a ton of typos,\nbut results can be weird and might not work with acronyms\n(e.g. \"GIMP\" might not give you the paint program)") } } ContentSubsection { - title: "Prefixes" + title: Translation.tr("Prefixes") ConfigRow { uniform: true MaterialTextField { Layout.fillWidth: true - placeholderText: "Action" + placeholderText: Translation.tr("Action") text: Config.options.search.prefix.action wrapMode: TextEdit.Wrap onTextChanged: { @@ -168,7 +168,7 @@ ContentPage { } MaterialTextField { Layout.fillWidth: true - placeholderText: "Clipboard" + placeholderText: Translation.tr("Clipboard") text: Config.options.search.prefix.clipboard wrapMode: TextEdit.Wrap onTextChanged: { @@ -177,7 +177,7 @@ ContentPage { } MaterialTextField { Layout.fillWidth: true - placeholderText: "Emojis" + placeholderText: Translation.tr("Emojis") text: Config.options.search.prefix.emojis wrapMode: TextEdit.Wrap onTextChanged: { @@ -187,10 +187,10 @@ ContentPage { } } ContentSubsection { - title: "Web search" + title: Translation.tr("Web search") MaterialTextField { Layout.fillWidth: true - placeholderText: "Base URL" + placeholderText: Translation.tr("Base URL") text: Config.options.search.engineBaseUrl wrapMode: TextEdit.Wrap onTextChanged: { @@ -201,10 +201,10 @@ ContentPage { } ContentSection { - title: "Time" + title: Translation.tr("Time") ContentSubsection { - title: "Format" + title: Translation.tr("Format") tooltip: "" ConfigSelectionArray { @@ -215,15 +215,15 @@ ContentPage { } options: [ { - displayName: "24h", + displayName: Translation.tr("24h"), value: "hh:mm" }, { - displayName: "12h am/pm", + displayName: Translation.tr("12h am/pm"), value: "h:mm ap" }, { - displayName: "12h AM/PM", + displayName: Translation.tr("12h AM/PM"), value: "h:mm AP" }, ] diff --git a/.config/quickshell/ii/modules/settings/StyleConfig.qml b/.config/quickshell/ii/modules/settings/StyleConfig.qml index bc1998c81..31dea05b6 100644 --- a/.config/quickshell/ii/modules/settings/StyleConfig.qml +++ b/.config/quickshell/ii/modules/settings/StyleConfig.qml @@ -28,7 +28,7 @@ ContentPage { } ContentSection { - title: "Colors & Wallpaper" + title: Translation.tr("Colors & Wallpaper") // Light/Dark mode preference ButtonGroup { @@ -44,7 +44,7 @@ ContentPage { // Material palette selection ContentSubsection { - title: "Material palette" + title: Translation.tr("Material palette") ConfigSelectionArray { currentValue: Config.options.appearance.palette.type configOptionName: "appearance.palette.type" @@ -53,15 +53,15 @@ ContentPage { Quickshell.execDetached(["bash", "-c", `${Directories.wallpaperSwitchScriptPath} --noswitch`]) } options: [ - {"value": "auto", "displayName": "Auto"}, - {"value": "scheme-content", "displayName": "Content"}, - {"value": "scheme-expressive", "displayName": "Expressive"}, - {"value": "scheme-fidelity", "displayName": "Fidelity"}, - {"value": "scheme-fruit-salad", "displayName": "Fruit Salad"}, - {"value": "scheme-monochrome", "displayName": "Monochrome"}, - {"value": "scheme-neutral", "displayName": "Neutral"}, - {"value": "scheme-rainbow", "displayName": "Rainbow"}, - {"value": "scheme-tonal-spot", "displayName": "Tonal Spot"} + {"value": "auto", "displayName": Translation.tr("Auto")}, + {"value": "scheme-content", "displayName": Translation.tr("Content")}, + {"value": "scheme-expressive", "displayName": Translation.tr("Expressive")}, + {"value": "scheme-fidelity", "displayName": Translation.tr("Fidelity")}, + {"value": "scheme-fruit-salad", "displayName": Translation.tr("Fruit Salad")}, + {"value": "scheme-monochrome", "displayName": Translation.tr("Monochrome")}, + {"value": "scheme-neutral", "displayName": Translation.tr("Neutral")}, + {"value": "scheme-rainbow", "displayName": Translation.tr("Rainbow")}, + {"value": "scheme-tonal-spot", "displayName": Translation.tr("Tonal Spot")} ] } } @@ -69,26 +69,26 @@ ContentPage { // Wallpaper selection ContentSubsection { - title: "Wallpaper" + title: Translation.tr("Wallpaper") RowLayout { Layout.alignment: Qt.AlignHCenter RippleButtonWithIcon { id: rndWallBtn buttonRadius: Appearance.rounding.small materialIcon: "wallpaper" - mainText: konachanWallProc.running ? "Be patient..." : "Random: Konachan" + mainText: konachanWallProc.running ? Translation.tr("Be patient...") : Translation.tr("Random: Konachan") onClicked: { console.log(konachanWallProc.command.join(" ")) konachanWallProc.running = true; } StyledToolTip { - content: "Random SFW Anime wallpaper from Konachan\nImage is saved to ~/Pictures/Wallpapers" + content: Translation.tr("Random SFW Anime wallpaper from Konachan\nImage is saved to ~/Pictures/Wallpapers") } } RippleButtonWithIcon { materialIcon: "wallpaper" StyledToolTip { - content: "Pick wallpaper image on your system" + content: Translation.tr("Pick wallpaper image on your system") } onClicked: { Quickshell.execDetached(`${Directories.wallpaperSwitchScriptPath}`) @@ -98,7 +98,7 @@ ContentPage { spacing: 10 StyledText { font.pixelSize: Appearance.font.pixelSize.small - text: "Choose file" + text: Translation.tr("Choose file") color: Appearance.colors.colOnSecondaryContainer } RowLayout { @@ -126,7 +126,7 @@ ContentPage { StyledText { Layout.topMargin: 5 Layout.alignment: Qt.AlignHCenter - text: "Alternatively use /dark, /light, /img in the launcher" + text: Translation.tr("Alternatively use /dark, /light, /img in the launcher") font.pixelSize: Appearance.font.pixelSize.smaller color: Appearance.colors.colSubtext } @@ -134,27 +134,27 @@ ContentPage { } ContentSection { - title: "Decorations & Effects" + title: Translation.tr("Decorations & Effects") ContentSubsection { - title: "Transparency" + title: Translation.tr("Transparency") ConfigRow { ConfigSwitch { - text: "Enable" + text: Translation.tr("Enable") checked: Config.options.appearance.transparency onCheckedChanged: { Config.options.appearance.transparency = checked; } StyledToolTip { - content: "Might look ass. Unsupported." + content: Translation.tr("Might look ass. Unsupported.") } } } } ContentSubsection { - title: "Fake screen rounding" + title: Translation.tr("Fake screen rounding") ButtonGroup { id: fakeScreenRoundingButtonGroup @@ -163,7 +163,7 @@ ContentPage { SelectionGroupButton { property int value: 0 leftmost: true - buttonText: "No" + buttonText: Translation.tr("No") toggled: (fakeScreenRoundingButtonGroup.selectedPolicy === value) onClicked: { Config.options.appearance.fakeScreenRounding = value; @@ -171,7 +171,7 @@ ContentPage { } SelectionGroupButton { property int value: 1 - buttonText: "Yes" + buttonText: Translation.tr("Yes") toggled: (fakeScreenRoundingButtonGroup.selectedPolicy === value) onClicked: { Config.options.appearance.fakeScreenRounding = value; @@ -180,7 +180,7 @@ ContentPage { SelectionGroupButton { property int value: 2 rightmost: true - buttonText: "When not fullscreen" + buttonText: Translation.tr("When not fullscreen") toggled: (fakeScreenRoundingButtonGroup.selectedPolicy === value) onClicked: { Config.options.appearance.fakeScreenRounding = value; @@ -190,19 +190,19 @@ ContentPage { } ContentSubsection { - title: "Shell windows" + title: Translation.tr("Shell windows") ConfigRow { uniform: true ConfigSwitch { - text: "Title bar" + text: Translation.tr("Title bar") checked: Config.options.windows.showTitlebar onCheckedChanged: { Config.options.windows.showTitlebar = checked; } } ConfigSwitch { - text: "Center title" + text: Translation.tr("Center title") checked: Config.options.windows.centerTitle onCheckedChanged: { Config.options.windows.centerTitle = checked; diff --git a/.config/quickshell/ii/settings.qml b/.config/quickshell/ii/settings.qml index e76f8dcb6..91bd61f79 100644 --- a/.config/quickshell/ii/settings.qml +++ b/.config/quickshell/ii/settings.qml @@ -28,27 +28,27 @@ ApplicationWindow { property bool showNextTime: false property var pages: [ { - name: "Style", + name: Translation.tr("Style"), icon: "palette", component: "modules/settings/StyleConfig.qml" }, { - name: "Interface", + name: Translation.tr("Interface"), icon: "cards", component: "modules/settings/InterfaceConfig.qml" }, { - name: "Services", + name: Translation.tr("Services"), icon: "settings", component: "modules/settings/ServicesConfig.qml" }, { - name: "Advanced", + name: Translation.tr("Advanced"), icon: "construction", component: "modules/settings/AdvancedConfig.qml" }, { - name: "About", + name: Translation.tr("About"), icon: "info", component: "modules/settings/About.qml" } @@ -110,7 +110,7 @@ ApplicationWindow { leftMargin: 12 } color: Appearance.colors.colOnLayer0 - text: "Settings" + text: Translation.tr("Settings") font.pixelSize: Appearance.font.pixelSize.title font.family: Appearance.font.family.title } @@ -162,7 +162,7 @@ ApplicationWindow { FloatingActionButton { id: fab iconText: "edit" - buttonText: "Edit config" + buttonText: Translation.tr("Edit config") expanded: navRail.expanded onClicked: { Qt.openUrlExternally(`${Directories.config}/illogical-impulse/config.json`); diff --git a/.config/quickshell/ii/welcome.qml b/.config/quickshell/ii/welcome.qml index 7c1dff4a2..40d496530 100644 --- a/.config/quickshell/ii/welcome.qml +++ b/.config/quickshell/ii/welcome.qml @@ -27,7 +27,7 @@ ApplicationWindow { property bool showNextTime: false visible: true onClosing: Qt.quit() - title: "illogical-impulse Welcome" + title: Translation.tr("illogical-impulse Welcome") Component.onCompleted: { MaterialThemeLoader.reapplyTheme() @@ -70,7 +70,7 @@ ApplicationWindow { leftMargin: 12 } color: Appearance.colors.colOnLayer0 - text: "Yooooo hi there" + text: Translation.tr("Yooooo hi there") font.pixelSize: Appearance.font.pixelSize.title font.family: Appearance.font.family.title } @@ -80,7 +80,7 @@ ApplicationWindow { anchors.right: parent.right StyledText { font.pixelSize: Appearance.font.pixelSize.smaller - text: "Show next time" + text: Translation.tr("Show next time") } StyledSwitch { id: showNextTimeSwitch @@ -123,7 +123,7 @@ ApplicationWindow { anchors.fill: parent ContentSection { - title: "Bar style" + title: Translation.tr("Bar style") ConfigSelectionArray { currentValue: Config.options.bar.cornerStyle @@ -132,15 +132,15 @@ ApplicationWindow { Config.options.bar.cornerStyle = newValue; // Update local copy } options: [ - { displayName: "Hug", value: 0 }, - { displayName: "Float", value: 1 }, - { displayName: "Plain rectangle", value: 2 } + { displayName: Translation.tr("Hug"), value: 0 }, + { displayName: Translation.tr("Float"), value: 1 }, + { displayName: Translation.tr("Plain rectangle"), value: 2 } ] } } ContentSection { - title: "Style & wallpaper" + title: Translation.tr("Style & wallpaper") ButtonGroup { Layout.fillWidth: true @@ -159,19 +159,19 @@ ApplicationWindow { Layout.alignment: Qt.AlignHCenter buttonRadius: Appearance.rounding.small materialIcon: "wallpaper" - mainText: konachanWallProc.running ? "Be patient..." : "Random: Konachan" + mainText: konachanWallProc.running ? Translation.tr("Be patient...") : Translation.tr("Random: Konachan") onClicked: { console.log(konachanWallProc.command.join(" ")) konachanWallProc.running = true; } StyledToolTip { - content: "Random SFW Anime wallpaper from Konachan\nImage is saved to ~/Pictures/Wallpapers" + content: Translation.tr("Random SFW Anime wallpaper from Konachan\nImage is saved to ~/Pictures/Wallpapers") } } RippleButtonWithIcon { materialIcon: "wallpaper" StyledToolTip { - content: "Pick wallpaper image on your system" + content: Translation.tr("Pick wallpaper image on your system") } onClicked: { Quickshell.execDetached([`${Directories.wallpaperSwitchScriptPath}`]) @@ -181,7 +181,7 @@ ApplicationWindow { spacing: 10 StyledText { font.pixelSize: Appearance.font.pixelSize.small - text: "Choose file" + text: Translation.tr("Choose file") color: Appearance.colors.colOnSecondaryContainer } RowLayout { @@ -207,19 +207,19 @@ ApplicationWindow { StyledText { Layout.alignment: Qt.AlignHCenter - text: "Change any time later with /dark, /light, /img in the launcher" + text: Translation.tr("Change any time later with /dark, /light, /img in the launcher") font.pixelSize: Appearance.font.pixelSize.smaller color: Appearance.colors.colSubtext } } ContentSection { - title: "Policies" + title: Translation.tr("Policies") ConfigRow { ColumnLayout { // Weeb policy ContentSubsectionLabel { - text: "Weeb" + text: Translation.tr("Weeb") } ConfigSelectionArray { currentValue: Config.options.policies.weeb @@ -228,16 +228,16 @@ ApplicationWindow { Config.options.policies.weeb = newValue; } options: [ - { displayName: "No", value: 0 }, - { displayName: "Yes", value: 1 }, - { displayName: "Closet", value: 2 } + { displayName: Translation.tr("No"), value: 0 }, + { displayName: Translation.tr("Yes"), value: 1 }, + { displayName: Translation.tr("Closet"), value: 2 } ] } } ColumnLayout { // AI policy ContentSubsectionLabel { - text: "AI" + text: Translation.tr("AI") } ConfigSelectionArray { currentValue: Config.options.policies.ai @@ -246,9 +246,9 @@ ApplicationWindow { Config.options.policies.ai = newValue; } options: [ - { displayName: "No", value: 0 }, - { displayName: "Yes", value: 1 }, - { displayName: "Local only", value: 2 } + { displayName: Translation.tr("No"), value: 0 }, + { displayName: Translation.tr("Yes"), value: 1 }, + { displayName: Translation.tr("Local only"), value: 2 } ] } } @@ -256,7 +256,7 @@ ApplicationWindow { } ContentSection { - title: "Info" + title: Translation.tr("Info") Flow { Layout.fillWidth: true @@ -272,7 +272,7 @@ ApplicationWindow { spacing: 10 StyledText { font.pixelSize: Appearance.font.pixelSize.small - text: "Keybinds" + text: Translation.tr("Keybinds") color: Appearance.colors.colOnSecondaryContainer } RowLayout { @@ -294,14 +294,14 @@ ApplicationWindow { RippleButtonWithIcon { materialIcon: "help" - mainText: "Usage" + mainText: Translation.tr("Usage") onClicked: { Qt.openUrlExternally("https://end-4.github.io/dots-hyprland-wiki/en/ii-qs/02usage/") } } RippleButtonWithIcon { materialIcon: "construction" - mainText: "Configuration" + mainText: Translation.tr("Configuration") onClicked: { Qt.openUrlExternally("https://end-4.github.io/dots-hyprland-wiki/en/ii-qs/03config/") } @@ -310,7 +310,7 @@ ApplicationWindow { } ContentSection { - title: "Useless buttons" + title: Translation.tr("Useless buttons") Flow { Layout.fillWidth: true @@ -318,7 +318,7 @@ ApplicationWindow { RippleButtonWithIcon { nerdIcon: "󰊤" - mainText: "GitHub" + mainText: Translation.tr("GitHub") onClicked: { Qt.openUrlExternally("https://github.com/end-4/dots-hyprland") } diff --git a/.config/quickshell/translations/en_US.json b/.config/quickshell/translations/en_US.json index 0e6254fe5..b757d3d34 100644 --- a/.config/quickshell/translations/en_US.json +++ b/.config/quickshell/translations/en_US.json @@ -172,5 +172,134 @@ "Set the system prompt for the model.": "Set the system prompt for the model.", "To Do": "To Do", "Calendar": "Calendar", - "notifications": "notifications" + "notifications": "notifications", + "Advanced": "Advanced", + "About": "About", + "Services": "Services", + "Style": "Style", + "Edit config": "Edit config", + "Colors & Wallpaper": "Colors & Wallpaper", + "Light": "Light", + "Dark": "Dark", + "Material palette": "Material palette", + "Fidelity": "Fidelity", + "Fruit Salad": "Fruit Salad", + "Alternatively use /dark, /light, /img in the launcher": "Alternatively use /dark, /light, /img in the launcher", + "Fake screen rounding": "Fake screen rounding", + "When not fullscreen": "When not fullscreen", + "Choose file": "Choose file", + "Random SFW Anime wallpaper from Konachan\nImage is saved to ~/Pictures/Wallpapers": "Random SFW Anime wallpaper from Konachan\nImage is saved to ~/Pictures/Wallpapers", + "Be patient...": "Be patient...", + "Decorations & Effects": "Decorations & Effects", + "Tonal Spot": "Tonal Spot", + "Shell windows": "Shell windows", + "Auto": "Auto", + "Wallpaper": "Wallpaper", + "Content": "Content", + "Title bar": "Title bar", + "Transparency": "Transparency", + "Expressive": "Expressive", + "Yes": "Yes", + "Enable": "Enable", + "Rainbow": "Rainbow", + "Might look ass. Unsupported.": "Might look ass. Unsupported.", + "Monochrome": "Monochrome", + "Random: Konachan": "Random: Konachan", + "Center title": "Center title", + "Neutral": "Neutral", + "Pick wallpaper image on your system": "Pick wallpaper image on your system", + "No": "No", + "AI": "AI", + "Local only": "Local only", + "Policies": "Policies", + "Weeb": "Weeb", + "Closet": "Closet", + "Bar style": "Bar style", + "Show next time": "Show next time", + "Usage": "Usage", + "Plain rectangle": "Plain rectangle", + "Useless buttons": "Useless buttons", + "GitHub": "GitHub", + "Style & wallpaper": "Style & wallpaper", + "Configuration": "Configuration", + "Change any time later with /dark, /light, /img in the launcher": "Change any time later with /dark, /light, /img in the launcher", + "Keybinds": "Keybinds", + "Float": "Float", + "Hug": "Hug", + "Yooooo hi there": "Yooooo hi there", + "illogical-impulse Welcome": "illogical-impulse Welcome", + "Info": "Info", + "Volume limit": "Volume limit", + "Prevents abrupt increments and restricts volume limit": "Prevents abrupt increments and restricts volume limit", + "Resources": "Resources", + "12h am/pm": "12h am/pm", + "Base URL": "Base URL", + "Audio": "Audio", + "Networking": "Networking", + "Format": "Format", + "Time": "Time", + "Battery": "Battery", + "Prefixes": "Prefixes", + "Emojis": "Emojis", + "Earbang protection": "Earbang protection", + "Automatically suspends the system when battery is low": "Automatically suspends the system when battery is low", + "Automatic suspend": "Automatic suspend", + "Suspend at": "Suspend at", + "Max allowed increase": "Max allowed increase", + "Web search": "Web search", + "Polling interval (ms)": "Polling interval (ms)", + "Clipboard": "Clipboard", + "Low warning": "Low warning", + "24h": "24h", + "Use Levenshtein distance-based algorithm instead of fuzzy": "Use Levenshtein distance-based algorithm instead of fuzzy", + "System prompt": "System prompt", + "12h AM/PM": "12h AM/PM", + "Could be better if you make a ton of typos,\nbut results can be weird and might not work with acronyms\n(e.g. \"GIMP\" might not give you the paint program)": "Could be better if you make a ton of typos,\nbut results can be weird and might not work with acronyms\n(e.g. \"GIMP\" might not give you the paint program)", + "Critical warning": "Critical warning", + "User agent (for services that require it)": "User agent (for services that require it)", + "Such regions could be images or parts of the screen that have some containment.\nMight not always be accurate.\nThis is done with an image processing algorithm run locally and no AI is used.": "Such regions could be images or parts of the screen that have some containment.\nMight not always be accurate.\nThis is done with an image processing algorithm run locally and no AI is used.", + "Note: turning off can hurt readability": "Note: turning off can hurt readability", + "Workspaces shown": "Workspaces shown", + "Dark/Light toggle": "Dark/Light toggle", + "Dock": "Dock", + "Weather": "Weather", + "Pinned on startup": "Pinned on startup", + "Tip: Hide icons and always show numbers for\nthe classic illogical-impulse experience": "Tip: Hide icons and always show numbers for\nthe classic illogical-impulse experience", + "Appearance": "Appearance", + "Always show numbers": "Always show numbers", + "Buttons": "Buttons", + "Keyboard toggle": "Keyboard toggle", + "Scale (%)": "Scale (%)", + "Overview": "Overview", + "Rows": "Rows", + "Borderless": "Borderless", + "Screenshot tool": "Screenshot tool", + "Number show delay when pressing Super (ms)": "Number show delay when pressing Super (ms)", + "Timeout (ms)": "Timeout (ms)", + "Show app icons": "Show app icons", + "Workspaces": "Workspaces", + "Columns": "Columns", + "On-screen display": "On-screen display", + "Screen snip": "Screen snip", + "Mic toggle": "Mic toggle", + "Hover to reveal": "Hover to reveal", + "Bar": "Bar", + "Show background": "Show background", + "Show regions of potential interest": "Show regions of potential interest", + "Color picker": "Color picker", + "Help & Support": "Help & Support", + "Discussions": "Discussions", + "Color generation": "Color generation", + "Dotfiles": "Dotfiles", + "Distro": "Distro", + "Privacy Policy": "Privacy Policy", + "Documentation": "Documentation", + "Shell & utilities theming must also be enabled": "Shell & utilities theming must also be enabled", + "illogical-impulse": "illogical-impulse", + "Donate": "Donate", + "Terminal": "Terminal", + "Shell & utilities": "Shell & utilities", + "Qt apps": "Qt apps", + "Report a Bug": "Report a Bug", + "Issues": "Issues" } \ No newline at end of file diff --git a/.config/quickshell/translations/zh_CN.json b/.config/quickshell/translations/zh_CN.json index 0242aa2d2..fe4e577d6 100644 --- a/.config/quickshell/translations/zh_CN.json +++ b/.config/quickshell/translations/zh_CN.json @@ -172,5 +172,134 @@ "Set the system prompt for the model.": "为模型设置系统提示。", "To Do": "待办", "Calendar": "日历", - "notifications": "条通知" + "notifications": "条通知", + "Advanced": "高级", + "About": "关于", + "Services": "服务", + "Style": "样式", + "Edit config": "编辑配置", + "Colors & Wallpaper": "颜色和壁纸", + "Light": "浅色", + "Dark": "深色", + "Material palette": "颜色主题", + "Fidelity": "保真度", + "Fruit Salad": "水果沙拉", + "Alternatively use /dark, /light, /img in the launcher": "或者在启动器中使用 /dark、/light、/img", + "Fake screen rounding": "伪造屏幕圆角", + "When not fullscreen": "非全屏时", + "Choose file": "选择文件", + "Random SFW Anime wallpaper from Konachan\nImage is saved to ~/Pictures/Wallpapers": "随机 Konachan SFW 动漫壁纸\n图片保存到 ~/Pictures/Wallpapers", + "Be patient...": "请耐心等待...", + "Decorations & Effects": "装饰与特效", + "Tonal Spot": "色调点", + "Shell windows": "Shell 窗口", + "Auto": "自动", + "Wallpaper": "壁纸", + "Content": "内容", + "Title bar": "标题栏", + "Transparency": "透明度", + "Expressive": "表现力", + "Yes": "是", + "Enable": "启用", + "Rainbow": "彩虹", + "Might look ass. Unsupported.": "可能效果很差。不支持。", + "Monochrome": "单色", + "Random: Konachan": "随机:Konachan", + "Center title": "标题居中", + "Neutral": "中性", + "Pick wallpaper image on your system": "在系统中选择壁纸图片", + "No": "否", + "AI": "AI", + "Local only": "仅本地", + "Policies": "策略", + "Weeb": "二次元", + "Closet": "隐藏", + "Bar style": "Bar 样式", + "Show next time": "下次显示", + "Usage": "用法", + "Plain rectangle": "纯矩形", + "Useless buttons": "无用按钮", + "GitHub": "GitHub", + "Style & wallpaper": "样式与壁纸", + "Configuration": "配置", + "Change any time later with /dark, /light, /img in the launcher": "之后可在启动器用 /dark、/light、/img 更改", + "Keybinds": "快捷键", + "Float": "浮动", + "Hug": "贴合", + "Yooooo hi there": "哟嗬,您好呀", + "illogical-impulse Welcome": "illogical-impulse 欢迎页", + "Info": "信息", + "Volume limit": "音量限制", + "Prevents abrupt increments and restricts volume limit": "防止骤增并限制音量", + "Resources": "资源", + "12h am/pm": "12小时 上午/下午", + "Base URL": "基础 URL", + "Audio": "声音", + "Networking": "网络", + "Format": "格式", + "Time": "时间", + "Battery": "电池", + "Prefixes": "前缀", + "Emojis": "表情符号", + "Earbang protection": "防爆音保护", + "Automatically suspends the system when battery is low": "电池电量低时自动挂起系统", + "Automatic suspend": "自动挂起", + "Suspend at": "挂起阈值", + "Max allowed increase": "最大允许增幅", + "Web search": "网页搜索", + "Polling interval (ms)": "轮询间隔(毫秒)", + "Clipboard": "剪贴板", + "Low warning": "低电量警告", + "24h": "24小时制", + "Use Levenshtein distance-based algorithm instead of fuzzy": "使用 Levenshtein 距离算法替代模糊匹配", + "System prompt": "系统提示词", + "12h AM/PM": "12小时 AM/PM", + "Could be better if you make a ton of typos,\nbut results can be weird and might not work with acronyms\n(e.g. \"GIMP\" might not give you the paint program)": "如果你经常打错字可能更好用,但结果可能很奇怪,并且可能无法匹配缩写(如 \"GIMP\" 可能搜不到绘图程序)", + "Critical warning": "临界警告", + "User agent (for services that require it)": "用户代理(部分服务需要)", + "Such regions could be images or parts of the screen that have some containment.\nMight not always be accurate.\nThis is done with an image processing algorithm run locally and no AI is used.": "这些区域可能是图片或屏幕中具有一定包容性的部分。\n可能并不总是准确。\n这是通过本地运行的图像处理算法实现的,没有使用 AI。", + "Note: turning off can hurt readability": "注意:关闭后可能影响可读性", + "Workspaces shown": "显示的工作区数", + "Dark/Light toggle": "深浅色切换", + "Dock": "停靠栏", + "Weather": "天气", + "Pinned on startup": "启动时固定", + "Tip: Hide icons and always show numbers for\nthe classic illogical-impulse experience": "提示:隐藏图标并始终显示数字以获得经典体验", + "Appearance": "外观", + "Always show numbers": "总是显示数字", + "Buttons": "按钮", + "Keyboard toggle": "键盘切换", + "Scale (%)": "缩放比例(%)", + "Overview": "概览", + "Rows": "行数", + "Borderless": "无边框", + "Screenshot tool": "截图工具", + "Number show delay when pressing Super (ms)": "按下 Super 时数字显示延迟(ms)", + "Timeout (ms)": "超时时间(ms)", + "Show app icons": "显示应用图标", + "Workspaces": "工作区", + "Columns": "列数", + "On-screen display": "屏幕显示", + "Screen snip": "屏幕截图", + "Mic toggle": "麦克风切换", + "Hover to reveal": "悬停显示", + "Bar": "Bar", + "Show background": "显示背景", + "Show regions of potential interest": "显示可能感兴趣的区域", + "Color picker": "取色器", + "Help & Support": "帮助与支持", + "Discussions": "讨论区", + "Color generation": "配色生成", + "Dotfiles": "配置文件", + "Distro": "发行版", + "Privacy Policy": "隐私政策", + "Documentation": "文档", + "Shell & utilities theming must also be enabled": "必须同时启用 Shell 与工具主题", + "illogical-impulse": "illogical-impulse", + "Donate": "捐助", + "Terminal": "终端", + "Shell & utilities": "Shell 与工具", + "Qt apps": "Qt 应用", + "Report a Bug": "报告问题", + "Issues": "问题追踪" } \ No newline at end of file From 2eff8d6db41a9e1e6ae2d9ef5dfbf9455988e298 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=88=E6=9C=88?= <3600911665@qq.com> Date: Mon, 14 Jul 2025 15:08:40 +0800 Subject: [PATCH 09/20] i18n: Fix translatable string extraction tool bug. --- .config/quickshell/ii/screenshot.qml | 2 +- .config/quickshell/translations/en_US.json | 3 ++- .../translations/tools/translation-cleaner.py | 5 ++--- .../translations/tools/translation-manager.py | 19 +++++++++++++++---- .config/quickshell/translations/zh_CN.json | 3 ++- 5 files changed, 22 insertions(+), 10 deletions(-) diff --git a/.config/quickshell/ii/screenshot.qml b/.config/quickshell/ii/screenshot.qml index f15558719..d4a55de08 100644 --- a/.config/quickshell/ii/screenshot.qml +++ b/.config/quickshell/ii/screenshot.qml @@ -444,7 +444,7 @@ ShellRoot { } } StyledText { - text: "Drag or click a region • LMB: Copy • RMB: Edit" + text: Translation.tr("Drag or click a region • LMB: Copy • RMB: Edit") color: root.genericContentForeground } } diff --git a/.config/quickshell/translations/en_US.json b/.config/quickshell/translations/en_US.json index b757d3d34..f1da949c9 100644 --- a/.config/quickshell/translations/en_US.json +++ b/.config/quickshell/translations/en_US.json @@ -301,5 +301,6 @@ "Shell & utilities": "Shell & utilities", "Qt apps": "Qt apps", "Report a Bug": "Report a Bug", - "Issues": "Issues" + "Issues": "Issues", + "Drag or click a region • LMB: Copy • RMB: Edit": "Drag or click a region • LMB: Copy • RMB: Edit" } \ No newline at end of file diff --git a/.config/quickshell/translations/tools/translation-cleaner.py b/.config/quickshell/translations/tools/translation-cleaner.py index e53082fda..afc290ad1 100755 --- a/.config/quickshell/translations/tools/translation-cleaner.py +++ b/.config/quickshell/translations/tools/translation-cleaner.py @@ -154,10 +154,9 @@ def sync_translations(translations_dir: str, source_lang: str = "en_US", target_ del target_translations[key] print(f" Deleted {len(extra_keys)} extra keys") - # Save file - with open(target_file, 'w', encoding='utf-8') as f: + # Save file (ensure UTF-8, fix for special chars) + with open(target_file, 'w', encoding='utf-8', newline='') as f: json.dump(target_translations, f, ensure_ascii=False, indent=2) - print(f" Saved: {target_file}") def main(): diff --git a/.config/quickshell/translations/tools/translation-manager.py b/.config/quickshell/translations/tools/translation-manager.py index ffcb26092..1b1a604ab 100755 --- a/.config/quickshell/translations/tools/translation-manager.py +++ b/.config/quickshell/translations/tools/translation-manager.py @@ -56,11 +56,22 @@ class TranslationManager: text = match[0] if match else "" else: text = match - - # Decode escape characters + try: - clean_text = text.encode().decode('unicode_escape') - except: + if '\\u' in text or '\\x' in text: + clean_text = bytes(text, "utf-8").decode("unicode_escape") + else: + clean_text = ( + text.replace('\\n', '\n') + .replace('\\t', '\t') + .replace('\\r', '\r') + .replace('\\"', '"') + .replace('\\\'', "'") + .replace('\\f', '\f') + .replace('\\b', '\b') + .replace('\\\\', '\\') + ) + except Exception: clean_text = text # Clean text (remove extra whitespace) diff --git a/.config/quickshell/translations/zh_CN.json b/.config/quickshell/translations/zh_CN.json index fe4e577d6..a51b8a790 100644 --- a/.config/quickshell/translations/zh_CN.json +++ b/.config/quickshell/translations/zh_CN.json @@ -301,5 +301,6 @@ "Shell & utilities": "Shell 与工具", "Qt apps": "Qt 应用", "Report a Bug": "报告问题", - "Issues": "问题追踪" + "Issues": "问题追踪", + "Drag or click a region • LMB: Copy • RMB: Edit": "拖动或点击一个区域 • 鼠标左键:复制 • 鼠标右键:编辑" } \ No newline at end of file From b98e843a9d2f10946e1c8b8e73e28372460fe0a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=88=E6=9C=88?= <3600911665@qq.com> Date: Wed, 16 Jul 2025 16:48:21 +0800 Subject: [PATCH 10/20] i18n: removed duplicate translation services and fixed imports --- .config/quickshell/ii/GlobalStates.qml | 2 +- .../ii/modules/bar/ActiveWindow.qml | 2 +- .config/quickshell/ii/modules/bar/Media.qml | 1 + .../modules/common/widgets/PrimaryTabBar.qml | 2 +- .../common/widgets/SelectionDialog.qml | 2 +- .../modules/mediaControls/MediaControls.qml | 1 + .../OnScreenDisplayBrightness.qml | 1 + .../onScreenDisplay/OnScreenDisplayVolume.qml | 1 + .../ii/modules/overview/Overview.qml | 1 - .../quickshell/ii/modules/session/Session.qml | 1 + .../quickshell/ii/modules/settings/About.qml | 1 + .../ii/modules/settings/AdvancedConfig.qml | 1 + .../ii/modules/settings/InterfaceConfig.qml | 1 + .../ii/modules/settings/ServicesConfig.qml | 1 + .../ii/modules/settings/StyleConfig.qml | 1 + .../ii/modules/sidebarLeft/DescriptionBox.qml | 1 + .../ii/modules/sidebarLeft/SidebarLeft.qml | 1 - .../sidebarRight/BottomWidgetGroup.qml | 1 + .../sidebarRight/CenterWidgetGroup.qml | 2 +- .../ii/modules/sidebarRight/SidebarRight.qml | 1 - .../sidebarRight/calendar/CalendarWidget.qml | 1 + .../notifications/NotificationList.qml | 2 +- .../quickToggles/BluetoothToggle.qml | 1 + .../sidebarRight/quickToggles/GameMode.qml | 2 +- .../quickToggles/IdleInhibitor.qml | 2 +- .../quickToggles/NetworkToggle.qml | 2 +- .../sidebarRight/quickToggles/NightLight.qml | 2 +- .../modules/sidebarRight/todo/TodoWidget.qml | 1 + .../volumeMixer/AudioDeviceSelectorButton.qml | 2 +- .../sidebarRight/volumeMixer/VolumeMixer.qml | 2 +- .config/quickshell/ii/screenshot.qml | 2 +- .config/quickshell/ii/services/Ai.qml | 2 +- .config/quickshell/ii/services/Booru.qml | 2 +- .config/quickshell/ii/services/Brightness.qml | 2 +- .../quickshell/ii/services/KeyringStorage.qml | 2 +- .../ii/services/MprisController.qml | 2 +- .../quickshell/ii/services/Translation.qml | 170 ------------------ .config/quickshell/ii/settings.qml | 2 +- .config/quickshell/ii/welcome.qml | 2 +- .../translations/tools/translation-cleaner.py | 2 +- 40 files changed, 36 insertions(+), 194 deletions(-) delete mode 100644 .config/quickshell/ii/services/Translation.qml diff --git a/.config/quickshell/ii/GlobalStates.qml b/.config/quickshell/ii/GlobalStates.qml index 037ffe0c4..d473af35d 100644 --- a/.config/quickshell/ii/GlobalStates.qml +++ b/.config/quickshell/ii/GlobalStates.qml @@ -1,5 +1,5 @@ import "root:/modules/common/" -import "root:/services/" +import "root:/" import QtQuick import Quickshell import Quickshell.Hyprland diff --git a/.config/quickshell/ii/modules/bar/ActiveWindow.qml b/.config/quickshell/ii/modules/bar/ActiveWindow.qml index b0f8e0492..9187848e6 100644 --- a/.config/quickshell/ii/modules/bar/ActiveWindow.qml +++ b/.config/quickshell/ii/modules/bar/ActiveWindow.qml @@ -1,7 +1,7 @@ import "root:/services" import "root:/modules/common" import "root:/modules/common/widgets" -import "root:/services/" +import "root:/" import QtQuick import QtQuick.Layouts import Quickshell.Wayland diff --git a/.config/quickshell/ii/modules/bar/Media.qml b/.config/quickshell/ii/modules/bar/Media.qml index 3c6e76ae1..9d5486230 100644 --- a/.config/quickshell/ii/modules/bar/Media.qml +++ b/.config/quickshell/ii/modules/bar/Media.qml @@ -1,6 +1,7 @@ import "root:/modules/common" import "root:/modules/common/widgets" import "root:/services" +import "root:/" import "root:/modules/common/functions/string_utils.js" as StringUtils import QtQuick import QtQuick.Layouts diff --git a/.config/quickshell/ii/modules/common/widgets/PrimaryTabBar.qml b/.config/quickshell/ii/modules/common/widgets/PrimaryTabBar.qml index c8843bc2a..0c35781fe 100644 --- a/.config/quickshell/ii/modules/common/widgets/PrimaryTabBar.qml +++ b/.config/quickshell/ii/modules/common/widgets/PrimaryTabBar.qml @@ -1,5 +1,5 @@ import "root:/modules/common" -import "root:/services/" +import "root:/" import QtQuick import QtQuick.Controls import QtQuick.Layouts diff --git a/.config/quickshell/ii/modules/common/widgets/SelectionDialog.qml b/.config/quickshell/ii/modules/common/widgets/SelectionDialog.qml index 9a6d0b7fa..5170c6c7f 100644 --- a/.config/quickshell/ii/modules/common/widgets/SelectionDialog.qml +++ b/.config/quickshell/ii/modules/common/widgets/SelectionDialog.qml @@ -1,7 +1,7 @@ import "root:/modules/common" import "root:/modules/common/widgets" import "root:/services" -import "root:/services/" +import "root:/" import Qt5Compat.GraphicalEffects import QtQuick import QtQuick.Controls diff --git a/.config/quickshell/ii/modules/mediaControls/MediaControls.qml b/.config/quickshell/ii/modules/mediaControls/MediaControls.qml index 045a9a75b..f918ff70a 100644 --- a/.config/quickshell/ii/modules/mediaControls/MediaControls.qml +++ b/.config/quickshell/ii/modules/mediaControls/MediaControls.qml @@ -1,6 +1,7 @@ import "root:/modules/common" import "root:/modules/common/widgets" import "root:/services" +import "root:/" import "root:/modules/common/functions/string_utils.js" as StringUtils import "root:/modules/common/functions/file_utils.js" as FileUtils import Qt5Compat.GraphicalEffects diff --git a/.config/quickshell/ii/modules/onScreenDisplay/OnScreenDisplayBrightness.qml b/.config/quickshell/ii/modules/onScreenDisplay/OnScreenDisplayBrightness.qml index 32dbefa7b..00f9e68e9 100644 --- a/.config/quickshell/ii/modules/onScreenDisplay/OnScreenDisplayBrightness.qml +++ b/.config/quickshell/ii/modules/onScreenDisplay/OnScreenDisplayBrightness.qml @@ -1,3 +1,4 @@ +import "root:/" import "root:/services/" import "root:/modules/common" import "root:/modules/common/widgets" diff --git a/.config/quickshell/ii/modules/onScreenDisplay/OnScreenDisplayVolume.qml b/.config/quickshell/ii/modules/onScreenDisplay/OnScreenDisplayVolume.qml index 9850f5ffc..5ae1575e2 100644 --- a/.config/quickshell/ii/modules/onScreenDisplay/OnScreenDisplayVolume.qml +++ b/.config/quickshell/ii/modules/onScreenDisplay/OnScreenDisplayVolume.qml @@ -1,3 +1,4 @@ +import "root:/" import "root:/services/" import "root:/modules/common" import "root:/modules/common/widgets" diff --git a/.config/quickshell/ii/modules/overview/Overview.qml b/.config/quickshell/ii/modules/overview/Overview.qml index a4a784d0f..1e90674db 100644 --- a/.config/quickshell/ii/modules/overview/Overview.qml +++ b/.config/quickshell/ii/modules/overview/Overview.qml @@ -2,7 +2,6 @@ import "root:/" import "root:/services" import "root:/modules/common" import "root:/modules/common/widgets" -import "root:/services/" import QtQuick import QtQuick.Controls import QtQuick.Layouts diff --git a/.config/quickshell/ii/modules/session/Session.qml b/.config/quickshell/ii/modules/session/Session.qml index dda7ad6d9..6d98d98e7 100644 --- a/.config/quickshell/ii/modules/session/Session.qml +++ b/.config/quickshell/ii/modules/session/Session.qml @@ -1,4 +1,5 @@ import "root:/modules/common" +import "root:/" import "root:/services/" import "root:/modules/common/widgets" import "root:/modules/common/functions/color_utils.js" as ColorUtils diff --git a/.config/quickshell/ii/modules/settings/About.qml b/.config/quickshell/ii/modules/settings/About.qml index 4db99b68b..e2937dfe5 100644 --- a/.config/quickshell/ii/modules/settings/About.qml +++ b/.config/quickshell/ii/modules/settings/About.qml @@ -4,6 +4,7 @@ import QtQuick.Layouts import Quickshell import Quickshell.Io import Quickshell.Widgets +import "root:/" import "root:/services/" import "root:/modules/common/" import "root:/modules/common/widgets/" diff --git a/.config/quickshell/ii/modules/settings/AdvancedConfig.qml b/.config/quickshell/ii/modules/settings/AdvancedConfig.qml index c72922319..cd256f38b 100644 --- a/.config/quickshell/ii/modules/settings/AdvancedConfig.qml +++ b/.config/quickshell/ii/modules/settings/AdvancedConfig.qml @@ -1,6 +1,7 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts +import "root:/" import "root:/services/" import "root:/modules/common/" import "root:/modules/common/widgets/" diff --git a/.config/quickshell/ii/modules/settings/InterfaceConfig.qml b/.config/quickshell/ii/modules/settings/InterfaceConfig.qml index 9a269143a..30a2a37b1 100644 --- a/.config/quickshell/ii/modules/settings/InterfaceConfig.qml +++ b/.config/quickshell/ii/modules/settings/InterfaceConfig.qml @@ -1,6 +1,7 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts +import "root:/" import "root:/services/" import "root:/modules/common/" import "root:/modules/common/widgets/" diff --git a/.config/quickshell/ii/modules/settings/ServicesConfig.qml b/.config/quickshell/ii/modules/settings/ServicesConfig.qml index cc0c6185b..550a36f19 100644 --- a/.config/quickshell/ii/modules/settings/ServicesConfig.qml +++ b/.config/quickshell/ii/modules/settings/ServicesConfig.qml @@ -1,6 +1,7 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts +import "root:/" import "root:/services/" import "root:/modules/common/" import "root:/modules/common/widgets/" diff --git a/.config/quickshell/ii/modules/settings/StyleConfig.qml b/.config/quickshell/ii/modules/settings/StyleConfig.qml index 31dea05b6..b710abcea 100644 --- a/.config/quickshell/ii/modules/settings/StyleConfig.qml +++ b/.config/quickshell/ii/modules/settings/StyleConfig.qml @@ -5,6 +5,7 @@ import Quickshell import Quickshell.Io import Quickshell.Widgets import Quickshell.Hyprland +import "root:/" import "root:/services/" import "root:/modules/common/" import "root:/modules/common/widgets/" diff --git a/.config/quickshell/ii/modules/sidebarLeft/DescriptionBox.qml b/.config/quickshell/ii/modules/sidebarLeft/DescriptionBox.qml index 2ac78f5b9..835b679da 100644 --- a/.config/quickshell/ii/modules/sidebarLeft/DescriptionBox.qml +++ b/.config/quickshell/ii/modules/sidebarLeft/DescriptionBox.qml @@ -1,3 +1,4 @@ +import "root:/" import "root:/services" import "root:/modules/common" import "root:/modules/common/widgets" diff --git a/.config/quickshell/ii/modules/sidebarLeft/SidebarLeft.qml b/.config/quickshell/ii/modules/sidebarLeft/SidebarLeft.qml index 97a82791e..751287f1e 100644 --- a/.config/quickshell/ii/modules/sidebarLeft/SidebarLeft.qml +++ b/.config/quickshell/ii/modules/sidebarLeft/SidebarLeft.qml @@ -2,7 +2,6 @@ import "root:/" import "root:/services" import "root:/modules/common" import "root:/modules/common/widgets" -import "root:/services/" import QtQuick import QtQuick.Controls import QtQuick.Layouts diff --git a/.config/quickshell/ii/modules/sidebarRight/BottomWidgetGroup.qml b/.config/quickshell/ii/modules/sidebarRight/BottomWidgetGroup.qml index d2c4af9c6..0dd718847 100644 --- a/.config/quickshell/ii/modules/sidebarRight/BottomWidgetGroup.qml +++ b/.config/quickshell/ii/modules/sidebarRight/BottomWidgetGroup.qml @@ -1,5 +1,6 @@ import "root:/modules/common" import "root:/modules/common/widgets" +import "root:/" import "root:/services" import "./calendar" import "./todo" diff --git a/.config/quickshell/ii/modules/sidebarRight/CenterWidgetGroup.qml b/.config/quickshell/ii/modules/sidebarRight/CenterWidgetGroup.qml index 76e478880..126b98c0b 100644 --- a/.config/quickshell/ii/modules/sidebarRight/CenterWidgetGroup.qml +++ b/.config/quickshell/ii/modules/sidebarRight/CenterWidgetGroup.qml @@ -5,7 +5,7 @@ import "./calendar" import "./notifications" import "./todo" import "./volumeMixer" -import "root:/services/" +import "root:/" import Qt5Compat.GraphicalEffects import QtQuick import QtQuick.Controls diff --git a/.config/quickshell/ii/modules/sidebarRight/SidebarRight.qml b/.config/quickshell/ii/modules/sidebarRight/SidebarRight.qml index bd4d5d55f..c73a262e4 100644 --- a/.config/quickshell/ii/modules/sidebarRight/SidebarRight.qml +++ b/.config/quickshell/ii/modules/sidebarRight/SidebarRight.qml @@ -5,7 +5,6 @@ import "root:/modules/common/widgets" import "root:/modules/common/functions/string_utils.js" as StringUtils import "root:/modules/common/functions/file_utils.js" as FileUtils import "./quickToggles/" -import "root:/services/" import QtQuick import QtQuick.Controls import QtQuick.Layouts diff --git a/.config/quickshell/ii/modules/sidebarRight/calendar/CalendarWidget.qml b/.config/quickshell/ii/modules/sidebarRight/calendar/CalendarWidget.qml index 7fd72db40..0c79fcef3 100644 --- a/.config/quickshell/ii/modules/sidebarRight/calendar/CalendarWidget.qml +++ b/.config/quickshell/ii/modules/sidebarRight/calendar/CalendarWidget.qml @@ -1,4 +1,5 @@ import "root:/modules/common" +import "root:/" import "root:/modules/common/widgets" import "./calendar_layout.js" as CalendarLayout import QtQuick diff --git a/.config/quickshell/ii/modules/sidebarRight/notifications/NotificationList.qml b/.config/quickshell/ii/modules/sidebarRight/notifications/NotificationList.qml index c334b0529..d26693f7e 100644 --- a/.config/quickshell/ii/modules/sidebarRight/notifications/NotificationList.qml +++ b/.config/quickshell/ii/modules/sidebarRight/notifications/NotificationList.qml @@ -1,7 +1,7 @@ import "root:/modules/common" import "root:/modules/common/widgets" import "root:/services" -import "root:/services/" +import "root:/" import Qt5Compat.GraphicalEffects import QtQuick import QtQuick.Controls diff --git a/.config/quickshell/ii/modules/sidebarRight/quickToggles/BluetoothToggle.qml b/.config/quickshell/ii/modules/sidebarRight/quickToggles/BluetoothToggle.qml index 37c3650c3..bdbfa9596 100644 --- a/.config/quickshell/ii/modules/sidebarRight/quickToggles/BluetoothToggle.qml +++ b/.config/quickshell/ii/modules/sidebarRight/quickToggles/BluetoothToggle.qml @@ -1,4 +1,5 @@ import "../" +import "root:/" import "root:/services" import "root:/modules/common" import "root:/modules/common/widgets" diff --git a/.config/quickshell/ii/modules/sidebarRight/quickToggles/GameMode.qml b/.config/quickshell/ii/modules/sidebarRight/quickToggles/GameMode.qml index ffee4de1c..0c635dbcd 100644 --- a/.config/quickshell/ii/modules/sidebarRight/quickToggles/GameMode.qml +++ b/.config/quickshell/ii/modules/sidebarRight/quickToggles/GameMode.qml @@ -1,7 +1,7 @@ import "root:/modules/common" import "root:/modules/common/widgets" import "../" -import "root:/services/" +import "root:/" import Quickshell import Quickshell.Io import Quickshell.Hyprland diff --git a/.config/quickshell/ii/modules/sidebarRight/quickToggles/IdleInhibitor.qml b/.config/quickshell/ii/modules/sidebarRight/quickToggles/IdleInhibitor.qml index ced345f77..2e24b3b12 100644 --- a/.config/quickshell/ii/modules/sidebarRight/quickToggles/IdleInhibitor.qml +++ b/.config/quickshell/ii/modules/sidebarRight/quickToggles/IdleInhibitor.qml @@ -1,7 +1,7 @@ import "root:/modules/common" import "root:/modules/common/widgets" import "../" -import "root:/services/" +import "root:/" import Quickshell.Io import Quickshell import Quickshell.Hyprland diff --git a/.config/quickshell/ii/modules/sidebarRight/quickToggles/NetworkToggle.qml b/.config/quickshell/ii/modules/sidebarRight/quickToggles/NetworkToggle.qml index a90fe2aaa..6ed954c20 100644 --- a/.config/quickshell/ii/modules/sidebarRight/quickToggles/NetworkToggle.qml +++ b/.config/quickshell/ii/modules/sidebarRight/quickToggles/NetworkToggle.qml @@ -3,7 +3,7 @@ import "root:/modules/common" import "root:/modules/common/widgets" import "root:/modules/common/functions/string_utils.js" as StringUtils import "../" -import "root:/services/" +import "root:/" import QtQuick import Quickshell import Quickshell.Io diff --git a/.config/quickshell/ii/modules/sidebarRight/quickToggles/NightLight.qml b/.config/quickshell/ii/modules/sidebarRight/quickToggles/NightLight.qml index f71d80a2a..3cdfad137 100644 --- a/.config/quickshell/ii/modules/sidebarRight/quickToggles/NightLight.qml +++ b/.config/quickshell/ii/modules/sidebarRight/quickToggles/NightLight.qml @@ -1,7 +1,7 @@ import "root:/modules/common" import "root:/modules/common/widgets" import "../" -import "root:/services/" +import "root:/" import Quickshell.Io import Quickshell diff --git a/.config/quickshell/ii/modules/sidebarRight/todo/TodoWidget.qml b/.config/quickshell/ii/modules/sidebarRight/todo/TodoWidget.qml index fa72061f5..f59adcc32 100644 --- a/.config/quickshell/ii/modules/sidebarRight/todo/TodoWidget.qml +++ b/.config/quickshell/ii/modules/sidebarRight/todo/TodoWidget.qml @@ -1,6 +1,7 @@ import "root:/modules/common" import "root:/modules/common/widgets" import "root:/services" +import "root:/" import "root:/modules/common/functions/color_utils.js" as ColorUtils import QtQuick import QtQuick.Controls diff --git a/.config/quickshell/ii/modules/sidebarRight/volumeMixer/AudioDeviceSelectorButton.qml b/.config/quickshell/ii/modules/sidebarRight/volumeMixer/AudioDeviceSelectorButton.qml index 0e5f37bbb..70e3b7ac6 100644 --- a/.config/quickshell/ii/modules/sidebarRight/volumeMixer/AudioDeviceSelectorButton.qml +++ b/.config/quickshell/ii/modules/sidebarRight/volumeMixer/AudioDeviceSelectorButton.qml @@ -1,7 +1,7 @@ import "root:/modules/common" import "root:/modules/common/widgets" import "root:/services" -import "root:/services/" +import "root:/" import Qt5Compat.GraphicalEffects import QtQuick import QtQuick.Controls diff --git a/.config/quickshell/ii/modules/sidebarRight/volumeMixer/VolumeMixer.qml b/.config/quickshell/ii/modules/sidebarRight/volumeMixer/VolumeMixer.qml index ec842353d..7c74afd64 100644 --- a/.config/quickshell/ii/modules/sidebarRight/volumeMixer/VolumeMixer.qml +++ b/.config/quickshell/ii/modules/sidebarRight/volumeMixer/VolumeMixer.qml @@ -1,7 +1,7 @@ import "root:/modules/common" import "root:/modules/common/widgets" import "root:/services" -import "root:/services/" +import "root:/" import Qt5Compat.GraphicalEffects import QtQuick import QtQuick.Controls diff --git a/.config/quickshell/ii/screenshot.qml b/.config/quickshell/ii/screenshot.qml index d4a55de08..99b3b1c26 100644 --- a/.config/quickshell/ii/screenshot.qml +++ b/.config/quickshell/ii/screenshot.qml @@ -6,6 +6,7 @@ //@ pragma Env QT_SCALE_FACTOR=1 pragma ComponentBehavior: "Bound" +import "./" import "./modules/common/" import "./modules/common/widgets" import "./modules/common/functions/string_utils.js" as StringUtils @@ -18,7 +19,6 @@ import Quickshell.Io import Quickshell.Widgets import Quickshell.Wayland import Quickshell.Hyprland -import "./services/" ShellRoot { id: root diff --git a/.config/quickshell/ii/services/Ai.qml b/.config/quickshell/ii/services/Ai.qml index 7c12d5b72..93764ca8b 100644 --- a/.config/quickshell/ii/services/Ai.qml +++ b/.config/quickshell/ii/services/Ai.qml @@ -4,7 +4,7 @@ pragma ComponentBehavior: Bound import "root:/modules/common/functions/string_utils.js" as StringUtils import "root:/modules/common/functions/object_utils.js" as ObjectUtils import "root:/modules/common" -import "root:/services/" +import "root:/" import Quickshell; import Quickshell.Io; import Qt.labs.platform diff --git a/.config/quickshell/ii/services/Booru.qml b/.config/quickshell/ii/services/Booru.qml index 5efd1fe33..ce2493f9b 100644 --- a/.config/quickshell/ii/services/Booru.qml +++ b/.config/quickshell/ii/services/Booru.qml @@ -2,7 +2,7 @@ pragma Singleton pragma ComponentBehavior: Bound import "root:/modules/common" -import "root:/services/" +import "root:/" import Quickshell; import Quickshell.Io; import Qt.labs.platform diff --git a/.config/quickshell/ii/services/Brightness.qml b/.config/quickshell/ii/services/Brightness.qml index a81c01d5d..129da7b23 100644 --- a/.config/quickshell/ii/services/Brightness.qml +++ b/.config/quickshell/ii/services/Brightness.qml @@ -4,7 +4,7 @@ pragma ComponentBehavior: Bound // From https://github.com/caelestia-dots/shell/ (`quickshell` branch) with modifications. // License: GPLv3 -import "root:/services/" +import "root:/" import Quickshell import Quickshell.Io import Quickshell.Hyprland diff --git a/.config/quickshell/ii/services/KeyringStorage.qml b/.config/quickshell/ii/services/KeyringStorage.qml index 3ec7560b7..8138f28bb 100644 --- a/.config/quickshell/ii/services/KeyringStorage.qml +++ b/.config/quickshell/ii/services/KeyringStorage.qml @@ -1,6 +1,6 @@ pragma Singleton pragma ComponentBehavior: Bound - +import "root:/" import "root:/modules/common" import "root:/modules/common/functions/string_utils.js" as StringUtils import Quickshell; diff --git a/.config/quickshell/ii/services/MprisController.qml b/.config/quickshell/ii/services/MprisController.qml index 90d72ed5a..ebc78898d 100644 --- a/.config/quickshell/ii/services/MprisController.qml +++ b/.config/quickshell/ii/services/MprisController.qml @@ -4,7 +4,7 @@ pragma ComponentBehavior: Bound // From https://git.outfoxxed.me/outfoxxed/nixnew // It does not have a license, but the author is okay with redistribution. -import "root:/services/" +import "root:/" import QtQml.Models import QtQuick import Quickshell diff --git a/.config/quickshell/ii/services/Translation.qml b/.config/quickshell/ii/services/Translation.qml deleted file mode 100644 index 97aff7c5c..000000000 --- a/.config/quickshell/ii/services/Translation.qml +++ /dev/null @@ -1,170 +0,0 @@ -pragma Singleton - -import QtQuick -import Quickshell -import Quickshell.Io -import "root:/modules/common/" - -Singleton { - id: root - - property var translations: ({}) - property string currentLanguage: "en_US" - property var availableLanguages: ["en_US"] - property bool isScanning: false - property bool isLoading: false - - Process { - id: scanLanguagesProcess - command: ["find", Qt.resolvedUrl(Directories.config + "/quickshell/translations/").toString().replace("file://", ""), "-name", "*.json", "-exec", "basename", "{}", ".json", ";"] - running: false - - stdout: SplitParser { - onRead: data => { - if (data.trim().length === 0) return - - var files = data.trim().split('\n') - - for (var i = 0; i < files.length; i++) { - var lang = files[i].trim() - if (lang.length > 0 && root.availableLanguages.indexOf(lang) === -1) { - root.availableLanguages.push(lang) - } - } - } - } - - onExited: (exitCode, exitStatus) => { - root.isScanning = false - if (exitCode !== 0) { - root.availableLanguages = ["en_US"] - } - root.loadTranslations() - } - } - - FileView { - id: translationFileView - onLoaded: { - var textContent = "" - try { - textContent = text() - } catch (e) { - root.translations = {} - root.isLoading = false - return - } - - if (textContent.length === 0) { - root.translations = {} - root.isLoading = false - return - } - - try { - var jsonData = JSON.parse(textContent) - root.translations = jsonData - root.isLoading = false - } catch (e) { - root.translations = {} - root.isLoading = false - } - } - onLoadFailed: (error) => { - root.translations = {} - root.isLoading = false - } - } - - function detectSystemLanguage() { - var locale = Qt.locale().name - return locale - } - - function getLanguageCode() { - var configLang = "auto" - try { - configLang = ConfigOptions.language.ui - } catch (e) { - configLang = "auto" - } - - if (configLang === "auto") { - return detectSystemLanguage() - } else { - if (root.availableLanguages.indexOf(configLang) !== -1) { - return configLang - } else { - return detectSystemLanguage() - } - } - } - - function loadTranslations() { - if (root.isScanning) { - return - } - - var targetLang = getLanguageCode() - root.currentLanguage = targetLang - - // Use empty translations for English (default language) - if (targetLang === "en_US" || targetLang === "en") { - root.translations = {} - return - } - - // Check if target language is available - if (root.availableLanguages.indexOf(targetLang) === -1) { - root.currentLanguage = "en_US" - root.translations = {} - return - } - - // Load translation file - root.isLoading = true - var translationsPath = Qt.resolvedUrl(Directories.config + "/quickshell/translations/" + targetLang + ".json") - translationFileView.path = translationsPath - } - - function tr(text) { - if (!text) { - return "" - } - - var key = text.toString() - - if (root.isLoading) { - return key - } - - if (root.currentLanguage === "en_US" || root.currentLanguage === "en" || !root.translations) { - return key - } - - if (root.translations.hasOwnProperty(key)) { - var translation = root.translations[key] - if (translation && translation.toString().trim().length > 0) { - return translation.toString() - } else { - return translation.toString() - } - } - - return key // Fallback to key name - } - - function reloadTranslations() { - root.scanLanguages() - } - - function scanLanguages() { - var translationsDir = Qt.resolvedUrl(Directories.config + "/quickshell/translations/").toString().replace("file://", "") - root.isScanning = true - scanLanguagesProcess.running = true - } - - Component.onCompleted: { - root.scanLanguages() - } -} diff --git a/.config/quickshell/ii/settings.qml b/.config/quickshell/ii/settings.qml index 91bd61f79..ed511a04a 100644 --- a/.config/quickshell/ii/settings.qml +++ b/.config/quickshell/ii/settings.qml @@ -13,7 +13,7 @@ import QtQuick.Window import Quickshell import Quickshell.Io import Quickshell.Hyprland -import "root:/services/" +import "root:/" import "root:/modules/common/" import "root:/modules/common/widgets/" import "root:/modules/common/functions/color_utils.js" as ColorUtils diff --git a/.config/quickshell/ii/welcome.qml b/.config/quickshell/ii/welcome.qml index 40d496530..9e643b776 100644 --- a/.config/quickshell/ii/welcome.qml +++ b/.config/quickshell/ii/welcome.qml @@ -12,7 +12,7 @@ import QtQuick.Window import Quickshell import Quickshell.Io import Quickshell.Hyprland -import "root:/services/" +import "root:/" import "root:/modules/common/" import "root:/modules/common/widgets/" import "root:/modules/common/functions/color_utils.js" as ColorUtils diff --git a/.config/quickshell/translations/tools/translation-cleaner.py b/.config/quickshell/translations/tools/translation-cleaner.py index afc290ad1..642101d15 100755 --- a/.config/quickshell/translations/tools/translation-cleaner.py +++ b/.config/quickshell/translations/tools/translation-cleaner.py @@ -52,7 +52,7 @@ def clean_translation_files(translations_dir: str, source_dir: str, backup: bool if backup: # Create backup - backup_file = Path(translations_dir) / f"{lang}.json.backup" + backup_file = Path(translations_dir) / f"{lang}.json.bak" with open(backup_file, 'w', encoding='utf-8') as f: json.dump(translations, f, ensure_ascii=False, indent=2) print(f"Created backup: {backup_file}") From 2ad60a40a8bebe75ae063ef721cb50ee9e431e98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=88=E6=9C=88?= <3600911665@qq.com> Date: Wed, 16 Jul 2025 21:11:27 +0800 Subject: [PATCH 11/20] i18n: Refactor string formatting to use arg() method for translations --- .../ii/modules/sidebarLeft/AiChat.qml | 7 +-- .../ii/modules/sidebarLeft/Anime.qml | 9 ++-- .../modules/sidebarLeft/anime/BooruImage.qml | 2 +- .../sidebarLeft/anime/BooruResponse.qml | 2 +- .../ii/modules/sidebarRight/SidebarRight.qml | 2 +- .../quickToggles/BluetoothToggle.qml | 2 +- .../quickToggles/NetworkToggle.qml | 2 +- .config/quickshell/ii/services/Ai.qml | 37 ++++++++------- .../quickshell/ii/services/KeyringStorage.qml | 2 +- .config/quickshell/translations/en_US.json | 46 ++++++++++--------- .config/quickshell/translations/zh_CN.json | 46 ++++++++++--------- 11 files changed, 85 insertions(+), 72 deletions(-) diff --git a/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml b/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml index e8a096a93..afd9f41c7 100644 --- a/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml +++ b/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml @@ -377,7 +377,7 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) Layout.fillWidth: true padding: 10 color: activeFocus ? Appearance.m3colors.m3onSurface : Appearance.m3colors.m3onSurfaceVariant - placeholderText: StringUtils.format(Translation.tr('Message the model... "{0}" for commands'), root.commandPrefix) + placeholderText: Translation.tr('Message the model... "%1" for commands').arg(root.commandPrefix) background: null @@ -577,8 +577,9 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) id: toolTip extraVisibleCondition: false alternativeVisibleCondition: mouseArea.containsMouse // Show tooltip when hovered - content: StringUtils.format(Translation.tr("Current model: {0}\nSet it with {1}model MODEL"), - Ai.getModel().name, root.commandPrefix) + content: Translation.tr("Current model: %1\nSet it with %2model MODEL") + .arg(Ai.getModel().name) + .arg(root.commandPrefix) } MouseArea { diff --git a/.config/quickshell/ii/modules/sidebarLeft/Anime.qml b/.config/quickshell/ii/modules/sidebarLeft/Anime.qml index 7498dceb2..304d8f32d 100644 --- a/.config/quickshell/ii/modules/sidebarLeft/Anime.qml +++ b/.config/quickshell/ii/modules/sidebarLeft/Anime.qml @@ -242,7 +242,7 @@ Item { font.pixelSize: Appearance.font.pixelSize.smaller color: Appearance.m3colors.m3inverseOnSurface wrapMode: Text.Wrap - text: StringUtils.format(Translation.tr("{0} queries pending"), Booru.runningRequests) + text: Translation.tr("%1 queries pending").arg(Booru.runningRequests) } } } @@ -354,7 +354,7 @@ Item { padding: 10 color: activeFocus ? Appearance.m3colors.m3onSurface : Appearance.m3colors.m3onSurfaceVariant renderType: Text.NativeRendering - placeholderText: StringUtils.format(Translation.tr('Enter tags, or "{0}" for commands'), root.commandPrefix) + placeholderText: Translation.tr('Enter tags, or "%1" for commands').arg(root.commandPrefix) background: null @@ -518,8 +518,9 @@ Item { extraVisibleCondition: false alternativeVisibleCondition: mouseArea.containsMouse // Show tooltip when hovered // content: Translation.tr("The current API used. Endpoint: ") + Booru.providers[Booru.currentProvider].url + Translation.tr("\nSet with /mode PROVIDER") - content: StringUtils.format(Translation.tr("Current API endpoint: {0}\nSet it with {1}mode PROVIDER"), - Booru.providers[Booru.currentProvider].url, root.commandPrefix) + content: Translation.tr("Current API endpoint: %1\nSet it with %2mode PROVIDER") + .arg(Booru.providers[Booru.currentProvider].url) + .arg(root.commandPrefix) } MouseArea { diff --git a/.config/quickshell/ii/modules/sidebarLeft/anime/BooruImage.qml b/.config/quickshell/ii/modules/sidebarLeft/anime/BooruImage.qml index a260dec5b..9ead31167 100644 --- a/.config/quickshell/ii/modules/sidebarLeft/anime/BooruImage.qml +++ b/.config/quickshell/ii/modules/sidebarLeft/anime/BooruImage.qml @@ -165,7 +165,7 @@ Button { id: sourceButton visible: root.imageData.source && root.imageData.source.length > 0 Layout.fillWidth: true - buttonText: StringUtils.format(Translation.tr("Go to source ({0})"), StringUtils.getDomain(root.imageData.source)) + buttonText: Translation.tr("Go to source (%1)").arg(StringUtils.getDomain(root.imageData.source)) enabled: root.imageData.source && root.imageData.source.length > 0 onClicked: { root.showActions = false diff --git a/.config/quickshell/ii/modules/sidebarLeft/anime/BooruResponse.qml b/.config/quickshell/ii/modules/sidebarLeft/anime/BooruResponse.qml index 574d57e2d..3676e24ff 100644 --- a/.config/quickshell/ii/modules/sidebarLeft/anime/BooruResponse.qml +++ b/.config/quickshell/ii/modules/sidebarLeft/anime/BooruResponse.qml @@ -95,7 +95,7 @@ Rectangle { font.pixelSize: Appearance.font.pixelSize.smaller color: Appearance.colors.colOnLayer2 // text: `Page ${root.responseData.page}` - text: StringUtils.format(Translation.tr("Page {0}"), root.responseData.page) + text: Translation.tr("Page %1").arg(root.responseData.page) } } } diff --git a/.config/quickshell/ii/modules/sidebarRight/SidebarRight.qml b/.config/quickshell/ii/modules/sidebarRight/SidebarRight.qml index c73a262e4..9c9af649a 100644 --- a/.config/quickshell/ii/modules/sidebarRight/SidebarRight.qml +++ b/.config/quickshell/ii/modules/sidebarRight/SidebarRight.qml @@ -124,7 +124,7 @@ Scope { StyledText { font.pixelSize: Appearance.font.pixelSize.normal color: Appearance.colors.colOnLayer0 - text: StringUtils.format(Translation.tr("Uptime: {0}"), DateTime.uptime) + text: Translation.tr("Uptime: %1").arg(DateTime.uptime) textFormat: Text.MarkdownText } diff --git a/.config/quickshell/ii/modules/sidebarRight/quickToggles/BluetoothToggle.qml b/.config/quickshell/ii/modules/sidebarRight/quickToggles/BluetoothToggle.qml index bdbfa9596..2b35f8ce2 100644 --- a/.config/quickshell/ii/modules/sidebarRight/quickToggles/BluetoothToggle.qml +++ b/.config/quickshell/ii/modules/sidebarRight/quickToggles/BluetoothToggle.qml @@ -29,7 +29,7 @@ QuickToggleButton { } } StyledToolTip { - content: StringUtils.format(Translation.tr("{0} | Right-click to configure"), + content: Translation.tr("%1 | Right-click to configure").arg( (Bluetooth.bluetoothEnabled && Bluetooth.bluetoothDeviceName.length > 0) ? Bluetooth.bluetoothDeviceName : Translation.tr("Bluetooth")) diff --git a/.config/quickshell/ii/modules/sidebarRight/quickToggles/NetworkToggle.qml b/.config/quickshell/ii/modules/sidebarRight/quickToggles/NetworkToggle.qml index 6ed954c20..06238f72b 100644 --- a/.config/quickshell/ii/modules/sidebarRight/quickToggles/NetworkToggle.qml +++ b/.config/quickshell/ii/modules/sidebarRight/quickToggles/NetworkToggle.qml @@ -29,6 +29,6 @@ QuickToggleButton { } } StyledToolTip { - content: StringUtils.format(Translation.tr("{0} | Right-click to configure"), Network.networkName) + content: Translation.tr("%1 | Right-click to configure").arg(Network.networkName) } } diff --git a/.config/quickshell/ii/services/Ai.qml b/.config/quickshell/ii/services/Ai.qml index 93764ca8b..31724d8e3 100644 --- a/.config/quickshell/ii/services/Ai.qml +++ b/.config/quickshell/ii/services/Ai.qml @@ -185,7 +185,7 @@ Singleton { "openrouter-llama4-maverick": { "name": "Llama 4 Maverick", "icon": "ollama-symbolic", - "description": StringUtils.format(Translation.tr("Online via {0} | {1}'s model"), "OpenRouter", "Meta"), + "description": Translation.tr("Online via %1 | %2's model").arg("OpenRouter").arg("Meta"), "homepage": "https://openrouter.ai/meta-llama/llama-4-maverick:free", "endpoint": "https://openrouter.ai/api/v1/chat/completions", "model": "meta-llama/llama-4-maverick:free", @@ -197,7 +197,7 @@ Singleton { "openrouter-deepseek-r1": { "name": "DeepSeek R1", "icon": "deepseek-symbolic", - "description": StringUtils.format(Translation.tr("Online via {0} | {1}'s model"), "OpenRouter", "DeepSeek"), + "description": Translation.tr("Online via %1 | %2's model").arg("OpenRouter").arg("DeepSeek"), "homepage": "https://openrouter.ai/deepseek/deepseek-r1:free", "endpoint": "https://openrouter.ai/api/v1/chat/completions", "model": "deepseek/deepseek-r1:free", @@ -250,7 +250,7 @@ Singleton { root.models[safeModelName] = { "name": guessModelName(model), "icon": guessModelLogo(model), - "description": StringUtils.format(Translation.tr("Local Ollama model | {0}"), model), + "description": Translation.tr("Local Ollama model | %1").arg(model), "homepage": `https://ollama.com/library/${model}`, "endpoint": "http://localhost:11434/v1/chat/completions", "model": model, @@ -314,12 +314,12 @@ Singleton { onLoadedChanged: { if (!promptLoader.loaded) return; Config.options.ai.systemPrompt = promptLoader.text(); - root.addMessage(StringUtils.format("Loaded the following system prompt\n\n---\n\n{0}", Config.options.ai.systemPrompt), root.interfaceRole); + root.addMessage(Translation.tr("Loaded the following system prompt\n\n---\n\n%1").arg(Config.options.ai.systemPrompt), root.interfaceRole); } } function printPrompt() { - root.addMessage(StringUtils.format("The current system prompt is\n\n---\n\n{0}", Config.options.ai.systemPrompt), root.interfaceRole); + root.addMessage(Translation.tr("The current system prompt is\n\n---\n\n%1").arg(Config.options.ai.systemPrompt), root.interfaceRole); } function loadPrompt(filePath) { @@ -352,8 +352,8 @@ Singleton { function addApiKeyAdvice(model) { root.addMessage( - StringUtils.format(Translation.tr('To set an API key, pass it with the command\n\nTo view the key, pass "get" with the command
\n\n### For {0}:\n\n**Link**: {1}\n\n{2}'), - model.name, model.key_get_link, model.key_get_description ?? Translation.tr("No further instruction provided")), + Translation.tr('To set an API key, pass it with the command\n\nTo view the key, pass "get" with the command
\n\n### For %1:\n\n**Link**: %2\n\n%3') + .arg(model.name).arg(model.key_get_link).arg(model.key_get_description ?? Translation.tr("No further instruction provided")), Ai.interfaceRole ); } @@ -371,11 +371,14 @@ Singleton { if (model?.requires_key) KeyringStorage.fetchKeyringData(); // See if policy prevents online models if (Config.options.policies.ai === 2 && !model.endpoint.includes("localhost")) { - root.addMessage(StringUtils.format(StringUtils.format("Online models disallowed\n\nControlled by `policies.ai` config option"), model.name), root.interfaceRole); + root.addMessage( + Translation.tr("Online models disallowed for %1\n\nControlled by `policies.ai` config option").arg(model.name), + root.interfaceRole + ); return; } if (setPersistentState) Persistent.states.ai.model = modelId; - if (feedback) root.addMessage(StringUtils.format("Model set to {0}", model.name), root.interfaceRole); + if (feedback) root.addMessage(Translation.tr("Model set to %1").arg(model.name), root.interfaceRole); if (model.requires_key) { // If key not there show advice if (root.apiKeysLoaded && (!root.apiKeys[model.key_id] || root.apiKeys[model.key_id].length === 0)) { @@ -398,13 +401,13 @@ Singleton { } Persistent.states.ai.temperature = value; root.temperature = value; - root.addMessage(StringUtils.format(Translation.tr("Temperature set to {0}"), value), Ai.interfaceRole); + root.addMessage(Translation.tr("Temperature set to %1").arg(value), Ai.interfaceRole); } function setApiKey(key) { const model = models[currentModelId]; if (!model.requires_key) { - root.addMessage(StringUtils.format(Translation.tr("{0} does not require an API key"), model.name), Ai.interfaceRole); + root.addMessage(Translation.tr("%1 does not require an API key").arg(model.name), Ai.interfaceRole); return; } if (!key || key.length === 0) { @@ -413,7 +416,7 @@ Singleton { return; } KeyringStorage.setNestedField(["apiKeys", model.key_id], key.trim()); - root.addMessage(StringUtils.format(Translation.tr("API key set for {0}"), model.name, Ai.interfaceRole)); + root.addMessage(Translation.tr("API key set for %1").arg(model.name), Ai.interfaceRole); } function printApiKey() { @@ -421,17 +424,17 @@ Singleton { if (model.requires_key) { const key = root.apiKeys[model.key_id]; if (key) { - root.addMessage(StringUtils.format(Translation.tr("API key:\n\n```txt\n{0}\n```"), key), Ai.interfaceRole); + root.addMessage(Translation.tr("API key:\n\n```txt\n%1\n```").arg(key), Ai.interfaceRole); } else { - root.addMessage(StringUtils.format(Translation.tr("No API key set for {0}"), model.name), Ai.interfaceRole); + root.addMessage(Translation.tr("No API key set for %1").arg(model.name), Ai.interfaceRole); } } else { - root.addMessage(StringUtils.format(Translation.tr("{0} does not require an API key"), model.name), Ai.interfaceRole); + root.addMessage(Translation.tr("%1 does not require an API key").arg(model.name), Ai.interfaceRole); } } function printTemperature() { - root.addMessage(StringUtils.format(Translation.tr("Temperature: {0}"), root.temperature), Ai.interfaceRole); + root.addMessage(Translation.tr("Temperature: %1").arg(root.temperature), Ai.interfaceRole); } function clearMessages() { @@ -799,7 +802,7 @@ Singleton { const value = args.value; Config.setNestedValue(key, value); } - else root.addMessage(Translation.tr("Unknown function call: {0}"), "assistant"); + else root.addMessage(Translation.tr("Unknown function call: %1").arg(name), "assistant"); } function chatToJson() { diff --git a/.config/quickshell/ii/services/KeyringStorage.qml b/.config/quickshell/ii/services/KeyringStorage.qml index 8138f28bb..33cfcd3b5 100644 --- a/.config/quickshell/ii/services/KeyringStorage.qml +++ b/.config/quickshell/ii/services/KeyringStorage.qml @@ -27,7 +27,7 @@ Singleton { return arr.concat([key, root.properties[key]]); }, [] ) - property string keyringLabel: StringUtils.format(Translation.tr("{0} Safe Storage"), "illogical-impulse") + property string keyringLabel: Translation.tr("%1 Safe Storage").arg("illogical-impulse") function setNestedField(path, value) { if (!root.keyringData) root.keyringData = {}; diff --git a/.config/quickshell/translations/en_US.json b/.config/quickshell/translations/en_US.json index f1da949c9..3f4118e17 100644 --- a/.config/quickshell/translations/en_US.json +++ b/.config/quickshell/translations/en_US.json @@ -4,8 +4,6 @@ "**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key": "**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key", ". Notes for Zerochan:\n- You must enter a color\n- Set your zerochan username in `sidebar.booru.zerochan.username` config option. You [might be banned for not doing so](https://www.zerochan.net/api#:~:text=The%20request%20may%20still%20be%20completed%20successfully%20without%20this%20custom%20header%2C%20but%20your%20project%20may%20be%20banned%20for%20being%20anonymous.)!": ". Notes for Zerochan:\n- You must enter a color\n- Set your zerochan username in `sidebar.booru.zerochan.username` config option. You [might be banned for not doing so](https://www.zerochan.net/api#:~:text=The%20request%20may%20still%20be%20completed%20successfully%20without%20this%20custom%20header%2C%20but%20your%20project%20may%20be%20banned%20for%20being%20anonymous.)!", "No further instruction provided": "No further instruction provided", - "API key set for {0}": "API key set for {0}", - "API key:\n\n```txt\n{0}\n```": "API key:\n\n```txt\n{0}\n```", "Action": "Action", "Add": "Add", "Add task": "Add task", @@ -35,8 +33,6 @@ "Closes right sidebar on press": "Closes right sidebar on press", "Copy": "Copy", "Copy code": "Copy code", - "Current API endpoint: {0}\nSet it with {1}mode PROVIDER": "Current API endpoint: {0}\nSet it with {1}mode PROVIDER", - "Current model: {0}\nSet it with {1}model MODEL": "Current model: {0}\nSet it with {1}model MODEL", "Decrease brightness": "Decrease brightness", "Delete": "Delete", "Desktop": "Desktop", @@ -51,7 +47,6 @@ "For storing API keys and other sensitive information": "For storing API keys and other sensitive information", "Game mode": "Game mode", "Get the next page of results": "Get the next page of results", - "Go to source ({0})": "Go to source ({0})", "Hibernate": "Hibernate", "Hides brightness OSD on press": "Hides brightness OSD on press", "Hides volume OSD on press": "Hides volume OSD on press", @@ -66,13 +61,11 @@ "Large images | God tier quality, no NSFW.": "Large images | God tier quality, no NSFW.", "Large language models": "Large language models", "Launch": "Launch", - "Local Ollama model | {0}": "Local Ollama model | {0}", "Lock": "Lock", "Logout": "Logout", "Markdown test": "Markdown test", "Math result": "Math result", "Night Light": "Night Light", - "No API key set for {0}": "No API key set for {0}", "No audio source": "No audio source", "No media": "No media", "No notifications": "No notifications", @@ -88,7 +81,6 @@ "Opens right sidebar on press": "Opens right sidebar on press", "Opens session screen on press": "Opens session screen on press", "Output": "Output", - "Page {0}": "Page {0}", "Reboot": "Reboot", "Reboot to firmware settings": "Reboot to firmware settings", "Reload Hyprland & Quickshell": "Reload Hyprland & Quickshell", @@ -111,8 +103,6 @@ "Task Manager": "Task Manager", "Task description": "Task description", "Temperature must be between 0 and 2": "Temperature must be between 0 and 2", - "Temperature set to {0}": "Temperature set to {0}", - "Temperature: {0}": "Temperature: {0}", "The hentai one | Great quantity, a lot of NSFW, quality varies wildly": "The hentai one | Great quantity, a lot of NSFW, quality varies wildly", "The popular one | Best quantity, but quality can vary wildly": "The popular one | Best quantity, but quality can vary wildly", "Thinking": "Thinking", @@ -136,18 +126,12 @@ "Unknown Album": "Unknown Album", "Unknown Artist": "Unknown Artist", "Unknown Title": "Unknown Title", - "Unknown function call: {0}": "Unknown function call: {0}", - "Uptime: {0}": "Uptime: {0}", "View Markdown source": "View Markdown source", "Volume": "Volume", "Volume mixer": "Volume mixer", "Waifus only | Excellent quality, limited quantity": "Waifus only | Excellent quality, limited quantity", "Waiting for response...": "Waiting for response...", "Workspace": "Workspace", - "{0} Safe Storage": "{0} Safe Storage", - "{0} does not require an API key": "{0} does not require an API key", - "{0} queries pending": "{0} queries pending", - "{0} | Right-click to configure": "{0} | Right-click to configure", "Set with /mode PROVIDER": "Set with /mode PROVIDER", "Invalid API provider. Supported: \n-": "Invalid API provider. Supported: \n-", "Unknown command:": "Unknown command:", @@ -157,14 +141,10 @@ "Provider set to": "Provider set to", "Invalid model. Supported: \n```": "Invalid model. Supported: \n```", "Interrupts possibility of overview being toggled on release.": "Interrupts possibility of overview being toggled on release.", - "Enter tags, or \"{0}\" for commands": "Enter tags, or \"{0}\" for commands", "That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number": "That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number", "Online | Google's model\nGives up-to-date information with search.": "Online | Google's model\nGives up-to-date information with search.", - "Online via {0} | {1}'s model": "Online via {0} | {1}'s model", "Switched to search mode. Continue with the user's request.": "Switched to search mode. Continue with the user's request.", "Experimental | Online | Google's model\nCan do a little more but doesn't search quickly": "Experimental | Online | Google's model\nCan do a little more but doesn't search quickly", - "Message the model... \"{0}\" for commands": "Message the model... \"{0}\" for commands", - "To set an API key, pass it with the command\n\nTo view the key, pass \"get\" with the command
\n\n### For {0}:\n\n**Link**: {1}\n\n{2}": "To set an API key, pass it with the command\n\nTo view the key, pass \"get\" with the command
\n\n### For {0}:\n\n**Link**: {1}\n\n{2}", "Settings": "Settings", "Save chat": "Save chat", "Load chat": "Load chat", @@ -302,5 +282,29 @@ "Qt apps": "Qt apps", "Report a Bug": "Report a Bug", "Issues": "Issues", - "Drag or click a region • LMB: Copy • RMB: Edit": "Drag or click a region • LMB: Copy • RMB: Edit" + "Drag or click a region • LMB: Copy • RMB: Edit": "Drag or click a region • LMB: Copy • RMB: Edit", + "Current model: %1\nSet it with %2model MODEL": "Current model: %1\nSet it with %2model MODEL", + "Message the model... \"%1\" for commands": "Message the model... \"%1\" for commands", + "No API key set for %1": "No API key set for %1", + "Loaded the following system prompt\n\n---\n\n%1": "Loaded the following system prompt\n\n---\n\n%1", + "%1 | Right-click to configure": "%1 | Right-click to configure", + "API key set for %1": "API key set for %1", + "Online models disallowed for %1\n\nControlled by `policies.ai` config option": "Online models disallowed for %1\n\nControlled by `policies.ai` config option", + "Online via %1 | %2's model": "Online via %1 | %2's model", + "Current API endpoint: %1\nSet it with %2mode PROVIDER": "Current API endpoint: %1\nSet it with %2mode PROVIDER", + "Go to source (%1)": "Go to source (%1)", + "Temperature set to %1": "Temperature set to %1", + "To set an API key, pass it with the command\n\nTo view the key, pass \"get\" with the command
\n\n### For %1:\n\n**Link**: %2\n\n%3": "To set an API key, pass it with the command\n\nTo view the key, pass \"get\" with the command
\n\n### For %1:\n\n**Link**: %2\n\n%3", + "Enter tags, or \"%1\" for commands": "Enter tags, or \"%1\" for commands", + "%1 queries pending": "%1 queries pending", + "API key:\n\n```txt\n%1\n```": "API key:\n\n```txt\n%1\n```", + "Uptime: %1": "Uptime: %1", + "%1 Safe Storage": "%1 Safe Storage", + "%1 does not require an API key": "%1 does not require an API key", + "Temperature: %1": "Temperature: %1", + "Model set to %1": "Model set to %1", + "Page %1": "Page %1", + "Local Ollama model | %1": "Local Ollama model | %1", + "The current system prompt is\n\n---\n\n%1": "The current system prompt is\n\n---\n\n%1", + "Unknown function call: %1": "Unknown function call: %1" } \ No newline at end of file diff --git a/.config/quickshell/translations/zh_CN.json b/.config/quickshell/translations/zh_CN.json index a51b8a790..71e64d7ea 100644 --- a/.config/quickshell/translations/zh_CN.json +++ b/.config/quickshell/translations/zh_CN.json @@ -4,8 +4,8 @@ "**Pricing**: free. Data use policy varies depending on your OpenRouter account settings.\n\n**Instructions**: Log into OpenRouter account, go to Keys on the topright menu, click Create API Key": "**价格**:免费。数据使用政策取决于您的 OpenRouter 账户设置。\n\n**说明**:登录 OpenRouter 账户,在右上角菜单中选择 Keys,点击创建 API 密钥", ". Notes for Zerochan:\n- You must enter a color\n- Set your zerochan username in `sidebar.booru.zerochan.username` config option. You [might be banned for not doing so](https://www.zerochan.net/api#:~:text=The%20request%20may%20still%20be%20completed%20successfully%20without%20this%20custom%20header%2C%20but%20your%20project%20may%20be%20banned%20for%20being%20anonymous.)!": ". Notes for Zerochan:\n- You must enter a color\n- Set your zerochan username in `sidebar.booru.zerochan.username` config option. You [might be banned for not doing so](https://www.zerochan.net/api#:~:text=The%20request%20may%20still%20be%20completed%20successfully%20without%20this%20custom%20header%2C%20but%20your%20project%20may%20be%20banned%20for%20being%20anonymous.)!", "No further instruction provided": "未提供进一步说明", - "API key set for {0}": "已为 {0} 设置 API 密钥", - "API key:\n\n```txt\n{0}\n```": "API 密钥:\n\n```txt\n{0}\n```", + "API key set for %1": "已为 %1 设置 API 密钥", + "API key:\n\n```txt\n%1\n```": "API 密钥:\n\n```txt\n%1\n```", "Action": "操作", "Add": "添加", "Add task": "添加任务", @@ -35,8 +35,7 @@ "Closes right sidebar on press": "按下时关闭右侧边栏", "Copy": "复制", "Copy code": "复制代码", - "Current API endpoint: {0}\nSet it with {1}mode PROVIDER": "当前 API 端点:{0}\n使用 {1}mode PROVIDER 设置", - "Current model: {0}\nSet it with {1}model MODEL": "当前模型:{0}\n使用 {1}model MODEL 设置", + "Current API endpoint: %1\nSet it with %2mode PROVIDER": "当前 API 端点:%1\n使用 %2mode PROVIDER 设置", "Decrease brightness": "降低亮度", "Delete": "删除", "Desktop": "桌面", @@ -51,7 +50,7 @@ "For storing API keys and other sensitive information": "用于存储 API 密钥和其他敏感信息", "Game mode": "游戏模式", "Get the next page of results": "获取下一页结果", - "Go to source ({0})": "转到源 ({0})", + "Go to source (%1)": "转到源 (%1)", "Hibernate": "休眠", "Hides brightness OSD on press": "按下时隐藏亮度显示", "Hides volume OSD on press": "按下时隐藏音量显示", @@ -66,13 +65,13 @@ "Large images | God tier quality, no NSFW.": "大尺寸图片 | 顶级质量,无 NSFW", "Large language models": "大语言模型", "Launch": "启动", - "Local Ollama model | {0}": "本地 Ollama 模型 | {0}", + "Local Ollama model | %1": "本地 Ollama 模型 | %1", "Lock": "锁定", "Logout": "注销", "Markdown test": "Markdown 测试", "Math result": "数学结果", "Night Light": "护眼模式", - "No API key set for {0}": "未为 {0} 设置 API 密钥", + "No API key set for %1": "未为 %1 设置 API 密钥", "No audio source": "无音频源", "No media": "无媒体", "No notifications": "无通知", @@ -88,7 +87,7 @@ "Opens right sidebar on press": "按下时打开右侧边栏", "Opens session screen on press": "按下时打开会话屏幕", "Output": "输出", - "Page {0}": "第 {0} 页", + "Page %1": "第 %1 页", "Reboot": "重启", "Reboot to firmware settings": "重启到固件设置", "Reload Hyprland & Quickshell": "重新加载 Hyprland 和 Quickshell", @@ -111,8 +110,8 @@ "Task Manager": "任务管理器", "Task description": "任务描述", "Temperature must be between 0 and 2": "温度必须在 0 到 2 之间", - "Temperature set to {0}": "温度设置为 {0}", - "Temperature: {0}": "温度:{0}", + "Temperature set to %1": "温度设置为 %1", + "Temperature: %1": "温度:%1", "The hentai one | Great quantity, a lot of NSFW, quality varies wildly": "成人向 | 数量巨大,大量 NSFW,质量参差不齐", "The popular one | Best quantity, but quality can vary wildly": "最受欢迎 | 数量最多,但质量参差不齐", "Thinking": "思考中", @@ -136,18 +135,18 @@ "Unknown Album": "未知专辑", "Unknown Artist": "未知艺术家", "Unknown Title": "未知标题", - "Unknown function call: {0}": "未知函数调用:{0}", - "Uptime: {0}": "运行时间:{0}", + "Unknown function call: %1": "未知函数调用:%1", + "Uptime: %1": "运行时间:%1", "View Markdown source": "查看 Markdown 源码", "Volume": "音量", "Volume mixer": "音量混合器", "Waifus only | Excellent quality, limited quantity": "仅限角色 | 优秀质量,数量有限", "Waiting for response...": "等待响应...", "Workspace": "工作区", - "{0} Safe Storage": "{0} 安全存储", - "{0} does not require an API key": "{0} 不需要 API 密钥", - "{0} queries pending": "{0} 个查询等待中", - "{0} | Right-click to configure": "{0} | 右键点击进行配置", + "%1 Safe Storage": "%1 安全存储", + "%1 does not require an API key": "%1 不需要 API 密钥", + "%1 queries pending": "%1 个查询等待中", + "%1 | Right-click to configure": "%1 | 右键点击进行配置", "Set with /mode PROVIDER": "使用 /mode PROVIDER 设置", "Invalid API provider. Supported: \n-": "无效的 API 提供商。支持的:\n-", "Unknown command:": "未知命令:", @@ -158,11 +157,10 @@ "Invalid model. Supported: \n```": "无效模型。支持的:\n```", "Interrupts possibility of overview being toggled on release.": "中断松开时切换概览的可能性。", "Switched to search mode. Continue with the user's request.": "已切换到搜索模式。继续处理用户请求。", - "Message the model... \"{0}\" for commands": "与模型对话... \"{0}\" 查看命令", "Experimental | Online | Google's model\nCan do a little more but doesn't search quickly": "实验性 | 在线 | Google 模型\n功能更多但搜索速度较慢", - "To set an API key, pass it with the command\n\nTo view the key, pass \"get\" with the command
\n\n### For {0}:\n\n**Link**: {1}\n\n{2}": "要设置 API 密钥,请将其与命令一起传递\n\n要查看密钥,请将 \"get\" 与命令一起传递
\n\n### 对于 {0}:\n\n**链接**:{1}\n\n{2}", - "Enter tags, or \"{0}\" for commands": "输入标签,或 \"{0}\" 查看命令", - "Online via {0} | {1}'s model": "通过 {0} 在线 | {1} 的模型", + "To set an API key, pass it with the command\n\nTo view the key, pass \"get\" with the command
\n\n### For %1:\n\n**Link**: %2\n\n%3": "要设置 API 密钥,请将其与命令一起传递\n\n要查看密钥,请将 \"get\" 与命令一起传递
\n\n### 对于 %1:\n\n**链接**:%2\n\n%3", + "Enter tags, or \"%1\" for commands": "输入标签,或 \"%1\" 查看命令", + "Online via %1 | %2's model": "通过 %1 在线 | %2 的模型", "That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number": "没有找到结果。提示:\n- 检查您的标签和 NSFW 设置\n- 如果没有想到标签,请输入页码", "Online | Google's model\nGives up-to-date information with search.": "在线 | Google 模型\n通过搜索提供最新信息。", "Settings": "设置", @@ -302,5 +300,11 @@ "Qt apps": "Qt 应用", "Report a Bug": "报告问题", "Issues": "问题追踪", - "Drag or click a region • LMB: Copy • RMB: Edit": "拖动或点击一个区域 • 鼠标左键:复制 • 鼠标右键:编辑" + "Drag or click a region • LMB: Copy • RMB: Edit": "拖动或点击一个区域 • 鼠标左键:复制 • 鼠标右键:编辑", + "Current model: %1\nSet it with %2model MODEL": "当前模型:%1\n使用 %2model MODEL 设置", + "Message the model... \"%1\" for commands": "与模型对话... \"%1\" 查看命令", + "The current system prompt is\n\n---\n\n%1": "The current system prompt is\n\n---\n\n%1", + "Model set to %1": "Model set to %1", + "Online models disallowed for %1\n\nControlled by `policies.ai` config option": "Online models disallowed for %1\n\nControlled by `policies.ai` config option", + "Loaded the following system prompt\n\n---\n\n%1": "Loaded the following system prompt\n\n---\n\n%1" } \ No newline at end of file From 8a68cf207abe820da21d21586d1d5649cd19e5a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=88=E6=9C=88?= <3600911665@qq.com> Date: Wed, 16 Jul 2025 23:36:47 +0800 Subject: [PATCH 12/20] i18n: Refactor translation management tools and add ignore feature for dynamic resources - Updated README.md to reflect changes in translation management tools and added ignore mark feature for dynamic resources. - Created Chinese translation guide for the translation management tools. - Created English translation guide for the translation management tools. - Enhanced translation-cleaner.py to skip keys marked with /*keep*/ during cleanup. - Improved translation-manager.py to create backups only when updating keys interactively. - Updated zh_CN.json with new translations and added ignore marks for dynamic values. --- .config/quickshell/ii/Translation.qml | 9 ++- .../ii/modules/sidebarLeft/AiChat.qml | 6 +- .../sidebarRight/calendar/CalendarWidget.qml | 2 +- .../notifications/NotificationList.qml | 2 +- .config/quickshell/translations/en_US.json | 14 ++++- .../quickshell/translations/tools/README.md | 33 +++++------ .../translation-tools-guide-zh_CN.md} | 33 ++++++----- .../{ => guide}/translation-tools-guide.md | 32 +++++------ .../translations/tools/translation-cleaner.py | 31 ++++++----- .../translations/tools/translation-manager.py | 55 +++++++++++++------ .config/quickshell/translations/zh_CN.json | 26 ++++++--- 11 files changed, 145 insertions(+), 98 deletions(-) rename .config/quickshell/translations/tools/{翻译管理脚本说明.md => guide/translation-tools-guide-zh_CN.md} (89%) rename .config/quickshell/translations/tools/{ => guide}/translation-tools-guide.md (87%) diff --git a/.config/quickshell/ii/Translation.qml b/.config/quickshell/ii/Translation.qml index 97aff7c5c..74a3e6ba8 100644 --- a/.config/quickshell/ii/Translation.qml +++ b/.config/quickshell/ii/Translation.qml @@ -145,12 +145,17 @@ Singleton { if (root.translations.hasOwnProperty(key)) { var translation = root.translations[key] if (translation && translation.toString().trim().length > 0) { - return translation.toString() + var str = translation.toString().trim() + if (str.endsWith("/*keep*/")) { + return str.substring(0, str.length - 8).trim() + } else { + return str + } } else { return translation.toString() } } - + return key // Fallback to key name } diff --git a/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml b/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml index afd9f41c7..26f8e5837 100644 --- a/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml +++ b/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml @@ -419,7 +419,7 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) return { name: `${messageInputField.text.trim().split(" ").length == 1 ? (root.commandPrefix + "prompt ") : ""}${file.target}`, displayName: `${FileUtils.trimFileExt(FileUtils.fileNameForPath(file.target))}`, - description: `Load prompt from ${file.target}`, + description: Translation.tr("Load prompt from %1").arg(file.target), } }) } else if (messageInputField.text.startsWith(`${root.commandPrefix}save`)) { @@ -438,7 +438,7 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) return { name: `${messageInputField.text.trim().split(" ").length == 1 ? (root.commandPrefix + "save ") : ""}${chatName}`, displayName: `${chatName}`, - description: `Save chat from ${chatName}`, + description: Translation.tr("Save chat from %1").arg(chatName), } }) } else if (messageInputField.text.startsWith(`${root.commandPrefix}load`)) { @@ -457,7 +457,7 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) return { name: `${messageInputField.text.trim().split(" ").length == 1 ? (root.commandPrefix + "load ") : ""}${chatName}`, displayName: `${chatName}`, - description: `Load chat from ${file.target}`, + description: Translation.tr(`Load chat from %1`).arg(file.target), } }) } else if(messageInputField.text.startsWith(root.commandPrefix)) { diff --git a/.config/quickshell/ii/modules/sidebarRight/calendar/CalendarWidget.qml b/.config/quickshell/ii/modules/sidebarRight/calendar/CalendarWidget.qml index 0c79fcef3..3aac36045 100644 --- a/.config/quickshell/ii/modules/sidebarRight/calendar/CalendarWidget.qml +++ b/.config/quickshell/ii/modules/sidebarRight/calendar/CalendarWidget.qml @@ -93,7 +93,7 @@ Item { Repeater { model: CalendarLayout.weekDays delegate: CalendarDayButton { - day: modelData.day + day: Translation.tr(modelData.day) isToday: modelData.today bold: true enabled: false diff --git a/.config/quickshell/ii/modules/sidebarRight/notifications/NotificationList.qml b/.config/quickshell/ii/modules/sidebarRight/notifications/NotificationList.qml index d26693f7e..4c95b9a5b 100644 --- a/.config/quickshell/ii/modules/sidebarRight/notifications/NotificationList.qml +++ b/.config/quickshell/ii/modules/sidebarRight/notifications/NotificationList.qml @@ -85,7 +85,7 @@ Item { anchors.verticalCenter: parent.verticalCenter anchors.leftMargin: 10 horizontalAlignment: Text.AlignHCenter - text: `${Notifications.list.length} ${Translation.tr("notifications")}` + text: Translation.tr("%1 notifications").arg(Notifications.list.length) opacity: Notifications.list.length > 0 ? 1 : 0 visible: opacity > 0 diff --git a/.config/quickshell/translations/en_US.json b/.config/quickshell/translations/en_US.json index 3f4118e17..a9ee4e93e 100644 --- a/.config/quickshell/translations/en_US.json +++ b/.config/quickshell/translations/en_US.json @@ -1,4 +1,11 @@ { + "Mo": "Mo/*keep*/", + "Tu": "Tu/*keep*/", + "We": "We/*keep*/", + "Th": "Th/*keep*/", + "Fr": "Fr/*keep*/", + "Sa": "Sa/*keep*/", + "Su": "Su/*keep*/", "%1 characters": "%1 characters", "**Pricing**: free. Data use policy varies depending on your OpenRouter account settings.\n\n**Instructions**: Log into OpenRouter account, go to Keys on the topright menu, click Create API Key": "**Pricing**: free. Data use policy varies depending on your OpenRouter account settings.\n\n**Instructions**: Log into OpenRouter account, go to Keys on the topright menu, click Create API Key", "**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key": "**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key", @@ -152,7 +159,6 @@ "Set the system prompt for the model.": "Set the system prompt for the model.", "To Do": "To Do", "Calendar": "Calendar", - "notifications": "notifications", "Advanced": "Advanced", "About": "About", "Services": "Services", @@ -306,5 +312,9 @@ "Page %1": "Page %1", "Local Ollama model | %1": "Local Ollama model | %1", "The current system prompt is\n\n---\n\n%1": "The current system prompt is\n\n---\n\n%1", - "Unknown function call: %1": "Unknown function call: %1" + "Unknown function call: %1": "Unknown function call: %1", + "%1 notifications": "%1 notifications", + "Save chat from %1": "Save chat from %1", + "Load chat from %1": "Load chat from %1", + "Load prompt from %1": "Load prompt from %1" } \ No newline at end of file diff --git a/.config/quickshell/translations/tools/README.md b/.config/quickshell/translations/tools/README.md index e41c742e9..94c213631 100644 --- a/.config/quickshell/translations/tools/README.md +++ b/.config/quickshell/translations/tools/README.md @@ -163,7 +163,6 @@ Translation.tr("Say \"Hello\"") // With parameter placeholders Translation.tr("Hello, %1!").arg(name) -Translation.tr("{0} files selected").arg(count) ``` ## Example Output @@ -235,20 +234,15 @@ Original key count: 470, after cleaning: 420 --source-dir /path/to/source ``` -### Batch Processing +### Ignore Mark Feature -```bash -# Create a processing script -cat > update-all-translations.sh << 'EOF' -#!/bin/bash -for lang in zh_CN ja_JP ko_KR; do - echo "Processing $lang..." - ./manage-translations.sh update -l $lang -done -EOF +For dynamic resources or special texts that should not be automatically cleaned, you can add `/*keep*/` at the end of the translation value. The tool will automatically ignore these keys and will not delete them during cleaning or syncing. -chmod +x update-all-translations.sh -./update-all-translations.sh +Example: +```json +{ + "dynamic_key": "Some dynamic value /*keep*/" +} ``` ## Notes @@ -256,7 +250,8 @@ chmod +x update-all-translations.sh 1. **Backup is important**: The tool automatically creates backups before cleaning, but it is recommended to manually back up important files 2. **Text extraction limitations**: - - Only supports static strings, not dynamically constructed strings + - ~~Only supports static strings, not dynamically constructed strings~~ + - Dynamic resources (such as variable concatenation or runtime-generated text) cannot be automatically extracted. You need to manually add them to the translation file and use the `/*keep*/` mark for ignore management. - Must use the `Translation.tr()` format 3. **File encoding**: All files must use UTF-8 encoding @@ -267,13 +262,16 @@ chmod +x update-all-translations.sh ### Common Issues -**Q: The number of extracted texts does not match expectations?** +**Q: Text does not appear after adding Translation.tr?** +A: You need to import the translation feature in your QML file using `import "root:/"`, otherwise the translation text will not be displayed correctly. + +**Q: The number of extracted texts does not match expectations?** A: Check whether all translatable texts use the `Translation.tr()` format and ensure there are no dynamically constructed strings. -**Q: Some translations are missing after syncing?** +**Q: Some translations are missing after syncing?** A: Check whether the source language file contains all necessary keys, and consider using a different source language for syncing. -**Q: The cleaning operation deleted needed keys?** +**Q: The cleaning operation deleted needed keys?** A: Restore from the automatically created backup file and check whether `Translation.tr()` is used correctly in the source code. ### Restore Backup @@ -285,4 +283,3 @@ cp .config/quickshell/translations/zh_CN.json.backup .config/quickshell/translat # Restore all files cp .config/quickshell/translations.backup/* .config/quickshell/translations/ ``` - diff --git a/.config/quickshell/translations/tools/翻译管理脚本说明.md b/.config/quickshell/translations/tools/guide/translation-tools-guide-zh_CN.md similarity index 89% rename from .config/quickshell/translations/tools/翻译管理脚本说明.md rename to .config/quickshell/translations/tools/guide/translation-tools-guide-zh_CN.md index fa9850cf8..821533bea 100644 --- a/.config/quickshell/translations/tools/翻译管理脚本说明.md +++ b/.config/quickshell/translations/tools/guide/translation-tools-guide-zh_CN.md @@ -163,7 +163,6 @@ Translation.tr("Say \"Hello\"") // 带参数占位符 Translation.tr("Hello, %1!").arg(name) -Translation.tr("{0} files selected").arg(count) ``` ## 示例输出 @@ -235,29 +234,25 @@ $ ./manage-translations.sh clean --source-dir /path/to/source ``` -### 批量处理 - -```bash -# 创建处理脚本 -cat > update-all-translations.sh << 'EOF' -#!/bin/bash -for lang in zh_CN ja_JP ko_KR; do - echo "Processing $lang..." - ./manage-translations.sh update -l $lang -done -EOF - -chmod +x update-all-translations.sh -./update-all-translations.sh -``` ## 注意事项 1. **备份重要**:在执行清理操作前,工具会自动创建备份,但建议手动备份重要文件 2. **文本提取限制**: - - 只支持静态字符串,不支持动态构建的字符串 + - ~~只支持静态字符串,不支持动态构建的字符串~~ + - 动态资源(如变量拼接、运行时生成的文本)无法自动提取,需要在翻译文件中手动添加,并使用 `/*keep*/` 标记进行忽略管理。 - 必须使用 `Translation.tr()` 格式 +### 忽略标记功能 + +对于动态资源或特殊文本,如果不希望被自动清理,可在翻译值末尾添加 `/*keep*/`,工具会自动忽略这些键,不会在清理和同步时删除。 + +示例: +```json +{ + "dynamic_key": "Some dynamic value /*keep*/" +} +``` 3. **文件编码**:所有文件必须使用 UTF-8 编码 @@ -267,6 +262,10 @@ chmod +x update-all-translations.sh ### 常见问题 + +**Q: 添加了 Translation.tr 后文字不显示?** +A: 需要在 QML 文件中使用 `import "root:/"` 导入翻译功能,否则无法正常显示翻译文本。 + **Q: 提取的文本数量与预期不符?** A: 检查是否所有可翻译文本都使用了 `Translation.tr()` 格式,确保没有动态构建的字符串。 diff --git a/.config/quickshell/translations/tools/translation-tools-guide.md b/.config/quickshell/translations/tools/guide/translation-tools-guide.md similarity index 87% rename from .config/quickshell/translations/tools/translation-tools-guide.md rename to .config/quickshell/translations/tools/guide/translation-tools-guide.md index 96165b99b..94c213631 100644 --- a/.config/quickshell/translations/tools/translation-tools-guide.md +++ b/.config/quickshell/translations/tools/guide/translation-tools-guide.md @@ -163,7 +163,6 @@ Translation.tr("Say \"Hello\"") // With parameter placeholders Translation.tr("Hello, %1!").arg(name) -Translation.tr("{0} files selected").arg(count) ``` ## Example Output @@ -235,20 +234,15 @@ Original key count: 470, after cleaning: 420 --source-dir /path/to/source ``` -### Batch Processing +### Ignore Mark Feature -```bash -# Create a processing script -cat > update-all-translations.sh << 'EOF' -#!/bin/bash -for lang in zh_CN ja_JP ko_KR; do - echo "Processing $lang..." - ./manage-translations.sh update -l $lang -done -EOF +For dynamic resources or special texts that should not be automatically cleaned, you can add `/*keep*/` at the end of the translation value. The tool will automatically ignore these keys and will not delete them during cleaning or syncing. -chmod +x update-all-translations.sh -./update-all-translations.sh +Example: +```json +{ + "dynamic_key": "Some dynamic value /*keep*/" +} ``` ## Notes @@ -256,7 +250,8 @@ chmod +x update-all-translations.sh 1. **Backup is important**: The tool automatically creates backups before cleaning, but it is recommended to manually back up important files 2. **Text extraction limitations**: - - Only supports static strings, not dynamically constructed strings + - ~~Only supports static strings, not dynamically constructed strings~~ + - Dynamic resources (such as variable concatenation or runtime-generated text) cannot be automatically extracted. You need to manually add them to the translation file and use the `/*keep*/` mark for ignore management. - Must use the `Translation.tr()` format 3. **File encoding**: All files must use UTF-8 encoding @@ -267,13 +262,16 @@ chmod +x update-all-translations.sh ### Common Issues -**Q: The number of extracted texts does not match expectations?** +**Q: Text does not appear after adding Translation.tr?** +A: You need to import the translation feature in your QML file using `import "root:/"`, otherwise the translation text will not be displayed correctly. + +**Q: The number of extracted texts does not match expectations?** A: Check whether all translatable texts use the `Translation.tr()` format and ensure there are no dynamically constructed strings. -**Q: Some translations are missing after syncing?** +**Q: Some translations are missing after syncing?** A: Check whether the source language file contains all necessary keys, and consider using a different source language for syncing. -**Q: The cleaning operation deleted needed keys?** +**Q: The cleaning operation deleted needed keys?** A: Restore from the automatically created backup file and check whether `Translation.tr()` is used correctly in the source code. ### Restore Backup diff --git a/.config/quickshell/translations/tools/translation-cleaner.py b/.config/quickshell/translations/tools/translation-cleaner.py index 642101d15..b26e3fbd9 100755 --- a/.config/quickshell/translations/tools/translation-cleaner.py +++ b/.config/quickshell/translations/tools/translation-cleaner.py @@ -50,29 +50,34 @@ def clean_translation_files(translations_dir: str, source_dir: str, backup: bool translations = manager.load_translation_file(lang) original_count = len(translations) - if backup: - # Create backup - backup_file = Path(translations_dir) / f"{lang}.json.bak" - with open(backup_file, 'w', encoding='utf-8') as f: - json.dump(translations, f, ensure_ascii=False, indent=2) - print(f"Created backup: {backup_file}") - - # Find unused keys - unused_keys = set(translations.keys()) - current_texts - + # Find unused keys, skip those whose value ends with /*keep*/ + unused_keys = set() + for k in translations.keys(): + v = translations[k] + if k not in current_texts: + if isinstance(v, str) and v.strip().endswith('/*keep*/'): + continue + unused_keys.add(k) + if unused_keys: print(f"Found {len(unused_keys)} unused keys:") for i, key in enumerate(sorted(unused_keys)[:10], 1): # Only show first 10 print(f" {i}. \"{key[:50]}{'...' if len(key) > 50 else ''}\"") if len(unused_keys) > 10: print(f" ... and {len(unused_keys) - 10} more keys") - + response = input(f"Delete these {len(unused_keys)} unused keys? (y/n): ") if response.lower().strip() in ['y', 'yes']: + if backup: + # Create backup only when user confirms deletion + backup_file = Path(translations_dir) / f"{lang}.json.bak" + with open(backup_file, 'w', encoding='utf-8') as f: + json.dump(translations, f, ensure_ascii=False, indent=2) + print(f"Created backup: {backup_file}") # Delete unused keys for key in unused_keys: del translations[key] - + # Save cleaned file manager.save_translation_file(lang, translations) removed_count = len(unused_keys) @@ -82,7 +87,7 @@ def clean_translation_files(translations_dir: str, source_dir: str, backup: bool print("Skipped deletion") else: print("No unused keys found") - + new_count = len(translations) print(f"Original key count: {original_count}, after cleanup: {new_count}") diff --git a/.config/quickshell/translations/tools/translation-manager.py b/.config/quickshell/translations/tools/translation-manager.py index 1b1a604ab..a3105446b 100755 --- a/.config/quickshell/translations/tools/translation-manager.py +++ b/.config/quickshell/translations/tools/translation-manager.py @@ -138,35 +138,52 @@ class TranslationManager: return missing_keys, extra_keys def interactive_update(self, lang_code: str, missing_keys: Set[str], extra_keys: Set[str]): - """Interactively update translation file""" + """Interactively update translation file, create backup only if updating""" translations = self.load_translation_file(lang_code) modified = False - + backup_created = False + # Handle missing keys if missing_keys: print(f"\nFound {len(missing_keys)} missing translation keys:") for i, key in enumerate(sorted(missing_keys), 1): print(f"{i}. \"{key}\"") - + if self.ask_yes_no(f"\nAdd these {len(missing_keys)} missing keys?"): + if not backup_created: + backup_file = self.translations_dir / f"{lang_code}.json.bak" + with open(backup_file, 'w', encoding='utf-8') as f: + json.dump(translations, f, ensure_ascii=False, indent=2) + print(f"Created backup: {backup_file}") + backup_created = True for key in missing_keys: translations[key] = key # Default value is the key itself modified = True print(f"Added {len(missing_keys)} keys") - + # Handle extra keys if extra_keys: - print(f"\nFound {len(extra_keys)} extra translation keys:") - for i, key in enumerate(sorted(extra_keys), 1): - print(f"{i}. \"{key}\" -> \"{translations.get(key, '')}\"") - - if self.ask_yes_no(f"\nDelete these {len(extra_keys)} extra keys?"): - for key in extra_keys: - if key in translations: - del translations[key] - modified = True - print(f"Deleted {len(extra_keys)} keys") - + # Only show extra keys that are not marked with /*keep*/ + filtered_extra_keys = [key for key in extra_keys if not (isinstance(translations.get(key, ""), str) and translations.get(key, "").strip().endswith('/*keep*/'))] + if filtered_extra_keys: + print(f"\nFound {len(filtered_extra_keys)} extra translation keys:") + for i, key in enumerate(sorted(filtered_extra_keys), 1): + print(f"{i}. \"{key}\" -> \"{translations.get(key, '')}\"") + if self.ask_yes_no(f"\nDelete these {len(filtered_extra_keys)} extra keys?"): + if not backup_created: + backup_file = self.translations_dir / f"{lang_code}.json.bak" + with open(backup_file, 'w', encoding='utf-8') as f: + json.dump(translations, f, ensure_ascii=False, indent=2) + print(f"Created backup: {backup_file}") + backup_created = True + deleted_count = 0 + for key in filtered_extra_keys: + if key in translations: + del translations[key] + modified = True + deleted_count += 1 + print(f"Deleted {deleted_count} keys") + # Save changes if modified: self.save_translation_file(lang_code, translations) @@ -288,7 +305,13 @@ def main(): print(f"Analysis results:") print(f" Missing keys: {len(missing_keys)}") - print(f" Extra keys: {len(extra_keys)}") + # Load translation file for current lang to get values + current_translations = manager.load_translation_file(lang) + filtered_extra_keys = [key for key in extra_keys if not (isinstance(current_translations.get(key, ""), str) and current_translations.get(key, "").strip().endswith('/*keep*/'))] + ignored_extra_keys = [key for key in extra_keys if (isinstance(current_translations.get(key, ""), str) and current_translations.get(key, "").strip().endswith('/*keep*/'))] + print(f" Extra keys: {len(filtered_extra_keys)}") + if ignored_extra_keys: + print(f" Ignored keys: {len(ignored_extra_keys)} (marked with /*keep*/)") if missing_keys or extra_keys: manager.interactive_update(lang, missing_keys, extra_keys) diff --git a/.config/quickshell/translations/zh_CN.json b/.config/quickshell/translations/zh_CN.json index 71e64d7ea..c3749d2f8 100644 --- a/.config/quickshell/translations/zh_CN.json +++ b/.config/quickshell/translations/zh_CN.json @@ -1,4 +1,11 @@ { + "Mo": "一/*keep*/", + "Tu": "二/*keep*/", + "We": "三/*keep*/", + "Th": "四/*keep*/", + "Fr": "五/*keep*/", + "Sa": "六/*keep*/", + "Su": "日/*keep*/", "%1 characters": "%1 个字符", "**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key": "**价格**:免费。数据用于训练。\n\n**说明**:登录 Google 账户,允许 AI Studio 创建 Google Cloud 项目或其他要求,然后返回并点击获取 API 密钥", "**Pricing**: free. Data use policy varies depending on your OpenRouter account settings.\n\n**Instructions**: Log into OpenRouter account, go to Keys on the topright menu, click Create API Key": "**价格**:免费。数据使用政策取决于您的 OpenRouter 账户设置。\n\n**说明**:登录 OpenRouter 账户,在右上角菜单中选择 Keys,点击创建 API 密钥", @@ -96,7 +103,7 @@ "Save": "保存", "Save to Downloads": "保存到下载文件夹", "Search": "搜索", - "Search the web": "搜索网络", + "Search the web": "在网络上搜索", "Search, calculate or run": "搜索、计算或运行", "Select Language": "选择语言", "Session": "会话", @@ -170,7 +177,6 @@ "Set the system prompt for the model.": "为模型设置系统提示。", "To Do": "待办", "Calendar": "日历", - "notifications": "条通知", "Advanced": "高级", "About": "关于", "Services": "服务", @@ -212,7 +218,7 @@ "Policies": "策略", "Weeb": "二次元", "Closet": "隐藏", - "Bar style": "Bar 样式", + "Bar style": "条栏样式", "Show next time": "下次显示", "Usage": "用法", "Plain rectangle": "纯矩形", @@ -281,7 +287,7 @@ "Screen snip": "屏幕截图", "Mic toggle": "麦克风切换", "Hover to reveal": "悬停显示", - "Bar": "Bar", + "Bar": "条栏", "Show background": "显示背景", "Show regions of potential interest": "显示可能感兴趣的区域", "Color picker": "取色器", @@ -303,8 +309,12 @@ "Drag or click a region • LMB: Copy • RMB: Edit": "拖动或点击一个区域 • 鼠标左键:复制 • 鼠标右键:编辑", "Current model: %1\nSet it with %2model MODEL": "当前模型:%1\n使用 %2model MODEL 设置", "Message the model... \"%1\" for commands": "与模型对话... \"%1\" 查看命令", - "The current system prompt is\n\n---\n\n%1": "The current system prompt is\n\n---\n\n%1", - "Model set to %1": "Model set to %1", - "Online models disallowed for %1\n\nControlled by `policies.ai` config option": "Online models disallowed for %1\n\nControlled by `policies.ai` config option", - "Loaded the following system prompt\n\n---\n\n%1": "Loaded the following system prompt\n\n---\n\n%1" + "The current system prompt is\n\n---\n\n%1": "当前系统提示词为\n\n---\n\n%1", + "Model set to %1": "模型已设置为 %1", + "Online models disallowed for %1\n\nControlled by `policies.ai` config option": "%1 禁止使用在线模型\n\n由 `policies.ai` 配置项控制", + "Loaded the following system prompt\n\n---\n\n%1": "已加载以下系统提示词\n\n---\n\n%1", + "%1 notifications": "%1 条通知", + "Save chat from %1": "保存聊天记录到 %1", + "Load chat from %1": "从 %1 加载聊天记录", + "Load prompt from %1": "从 %1 加载提示词" } \ No newline at end of file From 2b2733679cff0403be9c038b109f26d0edf8ba22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=88=E6=9C=88?= <3600911665@qq.com> Date: Wed, 16 Jul 2025 23:46:02 +0800 Subject: [PATCH 13/20] i18n: Update translation for online model disallowance message to remove model name reference --- .config/quickshell/ii/services/Ai.qml | 2 +- .config/quickshell/translations/en_US.json | 4 ++-- .config/quickshell/translations/zh_CN.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.config/quickshell/ii/services/Ai.qml b/.config/quickshell/ii/services/Ai.qml index 31724d8e3..db47ea490 100644 --- a/.config/quickshell/ii/services/Ai.qml +++ b/.config/quickshell/ii/services/Ai.qml @@ -372,7 +372,7 @@ Singleton { // See if policy prevents online models if (Config.options.policies.ai === 2 && !model.endpoint.includes("localhost")) { root.addMessage( - Translation.tr("Online models disallowed for %1\n\nControlled by `policies.ai` config option").arg(model.name), + Translation.tr("Online models disallowed for \n\nControlled by `policies.ai` config option"), root.interfaceRole ); return; diff --git a/.config/quickshell/translations/en_US.json b/.config/quickshell/translations/en_US.json index a9ee4e93e..71b5e6081 100644 --- a/.config/quickshell/translations/en_US.json +++ b/.config/quickshell/translations/en_US.json @@ -295,7 +295,6 @@ "Loaded the following system prompt\n\n---\n\n%1": "Loaded the following system prompt\n\n---\n\n%1", "%1 | Right-click to configure": "%1 | Right-click to configure", "API key set for %1": "API key set for %1", - "Online models disallowed for %1\n\nControlled by `policies.ai` config option": "Online models disallowed for %1\n\nControlled by `policies.ai` config option", "Online via %1 | %2's model": "Online via %1 | %2's model", "Current API endpoint: %1\nSet it with %2mode PROVIDER": "Current API endpoint: %1\nSet it with %2mode PROVIDER", "Go to source (%1)": "Go to source (%1)", @@ -316,5 +315,6 @@ "%1 notifications": "%1 notifications", "Save chat from %1": "Save chat from %1", "Load chat from %1": "Load chat from %1", - "Load prompt from %1": "Load prompt from %1" + "Load prompt from %1": "Load prompt from %1", + "Online models disallowed for \n\nControlled by `policies.ai` config option": "Online models disallowed for \n\nControlled by `policies.ai` config option" } \ No newline at end of file diff --git a/.config/quickshell/translations/zh_CN.json b/.config/quickshell/translations/zh_CN.json index c3749d2f8..489f3ad56 100644 --- a/.config/quickshell/translations/zh_CN.json +++ b/.config/quickshell/translations/zh_CN.json @@ -311,7 +311,7 @@ "Message the model... \"%1\" for commands": "与模型对话... \"%1\" 查看命令", "The current system prompt is\n\n---\n\n%1": "当前系统提示词为\n\n---\n\n%1", "Model set to %1": "模型已设置为 %1", - "Online models disallowed for %1\n\nControlled by `policies.ai` config option": "%1 禁止使用在线模型\n\n由 `policies.ai` 配置项控制", + "Online models disallowed for \n\nControlled by `policies.ai` config option": "禁止使用在线模型\n\n由 `policies.ai` 配置项控制", "Loaded the following system prompt\n\n---\n\n%1": "已加载以下系统提示词\n\n---\n\n%1", "%1 notifications": "%1 条通知", "Save chat from %1": "保存聊天记录到 %1", From 9056b551ab3e50648223befb92c2317eb774091d Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 17 Jul 2025 01:35:49 +0700 Subject: [PATCH 14/20] add vietnamese translation --- .config/quickshell/translations/vi_VN.json | 313 +++++++++++++++++++++ 1 file changed, 313 insertions(+) create mode 100644 .config/quickshell/translations/vi_VN.json diff --git a/.config/quickshell/translations/vi_VN.json b/.config/quickshell/translations/vi_VN.json new file mode 100644 index 000000000..27ce9f434 --- /dev/null +++ b/.config/quickshell/translations/vi_VN.json @@ -0,0 +1,313 @@ +{ + "Unknown function call: %1": "Hàm không xác định: %1", + "Show next time": "Hiển thị lần sau", + "Fidelity": "Fidelity", + "Open file link": "Mở liên kết tệp", + "Interrupts possibility of overview being toggled on release.": "Ngăn mở overview khi nhả nút.", + "No audio source": "Không có nguồn âm thanh", + "Might look ass. Unsupported.": "Có thể rất tệ. Không được hỗ trợ.", + "Jump to current month": "Nhảy đến tháng hiện tại", + "Delete": "Xóa", + "**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key": "**Giá**: miễn phí. Dữ liệu được sử dụng cho mục đích huấn luyện. **Hướng dẫn**: Đăng nhập vào tài khoản Google, cho phép AI Studio tạo dự án Google Cloud gì đó, quay lại rồi ấn Get API key", + "Rainbow": "Rainbow", + "%1 does not require an API key": "%1 không cần API key", + "Choose model": "Chọn model", + "Prevents abrupt increments and restricts volume limit": "Chặn thay đổi đột ngột và giới hạn âm lượng", + "%1 characters": "%1 ký tự", + "Change any time later with /dark, /light, /img in the launcher": "Thay đổi bất cứ lúc nào sau này với /dark, /light, /img trong launcher", + "Tonal Spot": "Tonal Spot", + "Neutral": "Neutral", + "To Do": "Cần làm", + "Auto": "Tự động", + "Polling interval (ms)": "Thời gian lặp lại (ms)", + "Closes on screen keyboard on press": "Đóng bàn phím trên màn hình khi ấn", + "Toggles right sidebar on press": "Mở/đóng sidebar phải khi ấn", + "Center title": "Căn giữa tiêu đề", + "Lock": "Khóa màn hình", + "Screen snip": "Chụp màn hình (chọn vùng)", + "User agent (for services that require it)": "User agent (nếu cần)", + "Report a Bug": "Báo lỗi", + "Shutdown": "Tắt máy", + "Keyboard toggle": "Mở/đóng bàn phím ảo", + "The hentai one | Great quantity, a lot of NSFW, quality varies wildly": "Cái nhiều hentai nhất | Số lượng rất tốt, rất nhiều NSFW, chất lượng có thể khác nhau nhiều", + "Download": "Tải xuống", + "Note: turning off can hurt readability": "Ghi chú: nếu tắt có thể khó đọc", + "Local Ollama model | %1": "Model Ollama trên máy | %1", + "Silent": "Im lặng", + "Columns": "Số cột", + "Set with /mode PROVIDER": "Set with /mode PROVIDER", + "Save chat to %1": "Lưu trò chuyện vào %1", + "Issues": "Các vấn đề", + "Policies": "Chính sách", + "Load chat from %1": "Tải trò chuyện từ %1", + "Unknown Album": "Album không xác định", + "Yes": "Có", + "Battery": "Pin", + "Material palette": "Kiểu material", + "Chain of Thought": "Dòng suy nghĩ", + "This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key.": "Cần cái này vì GlobalShortcut.onReleased cho một phím của Quickshell được kích hoạt kể cả khi ban ân phím khác trước khi thả phím đó.", + "Low warning": "Cảnh báo thấp", + ". Notes for Zerochan:\n- You must enter a color\n- Set your zerochan username in `sidebar.booru.zerochan.username` config option. You [might be banned for not doing so](https://www.zerochan.net/api#:~:text=The%20request%20may%20still%20be%20completed%20successfully%20without%20this%20custom%20header%2C%20but%20your%20project%20may%20be%20banned%20for%20being%20anonymous.)!": ". Với Zerochan:\n- Hãy nhập tên một màu (bằng tiếng Anh)\n- Đặt username Zerochan trong tùy chọn `sidebar.booru.zerochan.username`. Bạn [có thể bị ban nếu không tuân thủ](https://www.zerochan.net/api#:~:text=The%20request%20may%20still%20be%20completed%20successfully%20without%20this%20custom%20header%2C%20but%20your%20project%20may%20be%20banned%20for%20being%20anonymous.)!", + "Brightness": "Độ sáng", + "Yooooo hi there": "Yooooo chào bạn", + "Triggers volume OSD on press": "Hiển thị âm lượng khi ấn", + "Colors & Wallpaper": "Màu sắc & Hình nền", + "No media": "Không có nhạc/video", + "Critical warning": "Cảnh báo rất thấp", + "Mic toggle": "Bật/tắt mic", + "12h AM/PM": "12h AM/PM", + "Online models disallowed\n\nControlled by `policies.ai` config option": "Model trên mạng không được cho phép\n\nTheo cài đặt `policies.ai`", + "Large language models": "Mô hình ngôn ngữ lớn", + "Markdown test": "Test markdown", + "Temperature: %1": "Nhiệt độ: %1", + "Opens media controls on press": "Mở điều khiển nhạc/video khi ấn", + "Edit": "Sửa", + "Closes overview": "Đóng overview", + "Waifus only | Excellent quality, limited quantity": "Chỉ waifus | Chất lượng xuất sắc, số lượng hạn chế", + "Cheat sheet": "Bảng tra cứu", + "Current model: %1\nSet it with %2model MODEL": "Model đang chọn: %1\nChọn với lệnh %2model MODEL", + "Provider set to": "Đã đặt nhà cung cấp thành", + "Clear": "Xóa hết", + "GitHub": "GitHub", + "App": "Ứng dụng", + "Title bar": "Thanh tiêu đề", + "Web search": "Tìm kiếm web", + "Invalid model. Supported: \n```": "Model không hợp lệ. Các lựa chọn: \n```", + "Calendar": "Lịch", + "Done": "Đã xong", + "Monochrome": "Monochrome", + "Show regions of potential interest": "Hiển thị vùng thông minh", + "Dark/Light toggle": "Chuyển chế độ sáng/tối", + "Unknown command:": "Lệnh không xác định:", + "Allow NSFW content": "Cho phép nội dung NSFW", + "Closes cheatsheet on press": "Đóng bảng tra cứu khi ấn", + "Set temperature (randomness) of the model. Values range between 0 to 2 for Gemini, 0 to 1 for other models. Default is 0.5.": "Chỉnh giá trị nhiệt độ (sự ngẫu nhiên) của model. Giá trị 0-2 với Gemini, 0-1 với các model khác. Mặc định là 0.5.", + "Invalid API provider. Supported: \n-": "Nhà cung cấp API không hợp lệ. Các lựa chọn: \n-", + "Shell windows": "Cửa sổ của shell", + "Loaded the following system prompt\n\n---\n\n%1": "Đã tải chỉ dẫn hệ thống sau đây\n\n---\n\n%1", + "Clipboard": "Clipboard", + "Toggles left sidebar on press": "Mở/đóng sidebar trái khi ấn", + "For storing API keys and other sensitive information": "Để lưu trữ API key và các thông tin nhạy cảm khác", + "Wallpaper": "Hình nền", + "Decorations & Effects": "Trang trí & Hiệu ứng", + "AI": "AI", + "Large images | God tier quality, no NSFW.": "Ảnh kích thước lớn | Chất lượng cực tốt, không có NSFW.", + "When not fullscreen": "Khi không toàn màn hình", + "Resources": "Tài nguyên", + "Light": "Sáng", + "Weeb": "Wibu", + "Disable NSFW content": "Tắt nội dung NSFW", + "OK": "OK", + "Screenshot tool": "Công cụ chụp màn hình", + "Enable": "Bật", + "Select Language": "Chọn ngôn ngữ", + "System": "Hệ thống", + "Emojis": "Emoji", + "The current system prompt is\n\n---\n\n%1": "Chỉ dẫn hệ thống hiện tại như sau\n\n---\n\n%1", + "Closes right sidebar on press": "Đóng sidebar phải khi ấn", + "Translator": "Dịch", + "Sleep": "Ngủ", + "Action": "Hành động", + "Audio": "Âm thanh", + "Show background": "Hiện nền", + "All-rounder | Good quality, decent quantity": "Tốt đều | Chất lượng tốt, số lượng ổn", + "Documentation": "Tài liệu", + "Terminal": "Terminal", + "Distro": "Distro", + "Clear chat history": "Xóa lịch sử trò chuyện", + "Float": "Nổi", + "No further instruction provided": "No further instruction provided", + "Choose file": "Chọn tệp", + "Opens right sidebar on press": "Mở sidebar phải khi ấn", + "Set the system prompt for the model.": "Đặt chỉ dẫn hệ thống cho model.", + "Closes left sidebar on press": "Đóng sidebar trái khi ấn", + "Unknown Title": "Bài hát không rõ tên", + "Math result": "Kết quả phép tính", + "Logout": "Đăng xuất", + "Privacy Policy": "Chính sách quyền riêng tư", + "Style": "Phong cách", + "Borderless": "Không viền", + "Set API key": "Đặt API key", + "Clean stuff | Excellent quality, no NSFW": "Sạch sẽ | Chất lượng xuất sắc, không có NSFW", + "Experimental | Online | Google's model\nCan do a little more but doesn't search quickly": "Thử nghiệm | Trực tuyến | Mô hình của Google\nCó thể làm nhiều hơn một chút nhưng không tìm kiếm nhanh chóng", + "Toggles cheatsheet on press": "Mở/đóng bảng tra cứu khi ấn", + "Thinking": "Đang nghĩ", + "Earbang protection": "Bảo vệ tai", + "Advanced": "Nâng cao", + "Could be better if you make a ton of typos,\nbut results can be weird and might not work with acronyms\n(e.g. \"GIMP\" might not give you the paint program)": "Có thể tốt hơn nếu bạn gõ lệch phím nhiều,\nnhưng kết quả có thể hơi lạ và không hoạt động tốt với từ viết tắt\n(ví dụ tìm \"GIMP\" có thể không ra cái chương trình vẽ)", + "Shell & utilities theming must also be enabled": "Cần Shell & công cụ cũng bật", + "Desktop": "Màn hình chính", + "Anime": "Anime", + "Qt apps": "Các ứng dụng Qt", + "Style & wallpaper": "Phong cách & hình nền", + "Finished tasks will go here": "Việc đã xong sẽ hiện ở đây", + "Weather": "Thời tiết", + "Settings": "Cài đặt", + "Shell & utilities": "Shell & tiện ích", + "Toggles overview on release": "Mở/đóng overview khi nhả nút", + "Unfinished": "Chưa hoàn thành", + "Random: Konachan": "Ngẫu nhiên: Konachan", + "Opens left sidebar on press": "Mở sidebar trái khi ấn", + "Pick wallpaper image on your system": "Chọn hình nền trên máy", + "Volume": "Âm lượng", + "Add": "Thêm", + "Hibernate": "Ngủ đông", + "Run": "Chạy", + "Keep system awake": "Giữ hệ thống bật", + "To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything.": "Để đảm bảo luôn hoạt động, dùng binditn = MODKEYS, catchall trong một submap luôn được kích hoạt bao trùm mọi thứ.", + "Plain rectangle": "Hình chữ nhật", + "%1 queries pending": "%1 lượt gọi đang chờ", + "Temperature set to %1": "Nhiệt độ đã được đặt thành %1", + "Notifications": "Thông báo", + "System prompt": "Chỉ dẫn hệ thống", + "Hover to reveal": "Đặt chuột vào để hiện", + "No": "Không", + "Bar": "Bar", + "Search the web": "Tìm kiếm web", + "Page %1": "Trang %1", + "Reboot": "Khởi động lại", + "Such regions could be images or parts of the screen that have some containment.\nMight not always be accurate.\nThis is done with an image processing algorithm run locally and no AI is used.": "Các vùng có thể là hình ảnh hoặc phần của màn hình cớ vẻ được bao chứa.\nKhông luôn chính xác.\nSử dụng một thuật toán xử lý ảnh chạy trên máy, không dùng AI.", + "Show app icons": "Hiện biểu tượng ứng dụng", + "Closet": "Nghiện mà ngại", + "Set the current API provider": "Đặt nguồn cung cấp API", + "Cancel": "Hủy", + "Networking": "Mạng", + "Overview": "Overview", + "Search, calculate or run": "Tìm, tính hoặc chạy", + "Useless buttons": "Mấy nút vô dụng", + "Transparency": "Sự trong suốt", + "Temperature must be between 0 and 2": "Nhiệt độ phải trong khoảng từ 0 đến 2", + "Automatically suspends the system when battery is low": "Tự động ngủ khi pin thấp", + "Current API endpoint: %1\nSet it with %2mode PROVIDER": "Endpoint API hiện tại: %1\nĐặt với lệnh %2mode PROVIDER", + "Decrease brightness": "Giảm độ sáng", + "Services": "Các dịch vụ", + "Reload Hyprland & Quickshell": "Tải lại Hyprland & Quickshell", + "Automatic suspend": "Tự động ngủ", + "illogical-impulse Welcome": "illogical-impulse - Xin chào", + "Interface": "Giao diện", + "Load chat": "Tải cuộc trò chuyện", + "Opens session screen on press": "Mở màn hình session khi ấn", + "Number show delay when pressing Super (ms)": "Thời gian chờ hiện số khi nhấn Super (ms)", + "Clear the current list of images": "Xóa danh sách hình ảnh hiện tại", + "Fake screen rounding": "Giả bo tròn màn hình", + "Tip: Hide icons and always show numbers for\nthe classic illogical-impulse experience": "Mẹo: Ẩn biểu tượng và luôn hiển thị số nếu\nmuốn giống trải nghiệm illogical-impulse gốc", + "Launch": "Chạy", + "%1 notifications": "%1 thông báo", + "Hides brightness OSD on press": "Ngừng hiển thị độ sáng khi nhấn", + "%1 | Right-click to configure": "%1 | Ấn chuột phải để chỉnh", + "Unknown Artist": "Nghệ sĩ không xác định", + "Appearance": "Giao diện", + "Task Manager": "Quản lí ứng dụng đang chạy", + "To set an API key, pass it with the command\n\nTo view the key, pass \"get\" with the command
\n\n### For %1:\n\n**Link**: %2\n\n%3": "Để đặt API key, viết nó sau lệnh\n\nĐể xem lại, viết \"get\" sau lệnh
\n\n### Với %1:\n\n**Link**: %2\n\n%3", + "Hold to show workspace numbers, release to show icons": "Giữ để hiện số workspace, thả ra để hiện biểu tượng", + "Opens cheatsheet on press": "Mở bảng tra cứu khi ấn", + "Invalid arguments. Must provide `key` and `value`.": "Biến không hợp lệ. cần cả `key` và `value`.", + "About": "Giới thiệu", + "illogical-impulse": "illogical-impulse", + "Triggers brightness OSD on press": "Hiện độ sáng/âm lượng khi ấn", + "Help & Support": "Trợ giúp", + "Enter tags, or \"%1\" for commands": "Nhập tag hoặc \"%1\" để xem các lệnh", + "Format": "Định dạng", + "Content": "Nội dung", + "Edit config": "Sửa config", + "Bluetooth": "Bluetooth", + "Be patient...": "Bình tĩnh...", + "Toggles session screen on press": "Mở/đóng màn hình session khi ấn", + "Discussions": "Thảo luận", + "Anime boorus": "Các booru anime", + "That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number": "Quả này không được. Mẹo:\n- Kiểm tra tag và cài đặt NSFW\n- Nếu không nghĩ ra tag nào có thể nhập số trang", + "Task description": "Mô tả công việc", + "Max allowed increase": "Thay đổi tối đa", + "Rows": "Số hàng", + "Switched to search mode. Continue with the user's request.": "Đã chuyển sang chế độ tìm kiếm. Tiếp tục với yêu cầu của người dùng.", + "Use Levenshtein distance-based algorithm instead of fuzzy": "Sử dụng thuật toán dùng khoảng cách Levenshtein thay vì fuzzy", + "Copy": "Sao chép", + "12h am/pm": "12h am/pm", + "Unknown": "Không xác định", + "Hides volume OSD on press": "Ngừng hiển thị âm lượng khi nhấn", + "Waiting for response...": "Đang chờ phản hồi...", + "Workspace": "Workspace", + "Random SFW Anime wallpaper from Konachan\nImage is saved to ~/Pictures/Wallpapers": "Hình nền Anime SFW ngẫu nhiên từ Konachan\nẢnh được lưu vào ~/Pictures/Wallpapers", + "Toggles on screen keyboard on press": "Mở/đóng bàn phím ảo khi ấn", + "Online via %1 | %2's model": "Trực tuyến qua %1 | Mô hình của %2", + "Always show numbers": "Luôn hiện số", + "or": "hoặc", + "Drag or click a region • LMB: Copy • RMB: Edit": "Kéo thả hoặc chọn vùng • Chuột trái: Sao chép • Chuột phải: Chỉnh sửa", + "Local only": "Chỉ trên máy", + "Donate": "Ủng hộ", + "Online | Google's model\nGives up-to-date information with search.": "Trực tuyến | Mô hình của Google\nCó thể tìm kiếm để cung cấp thông tin cập nhật.", + "Run command": "Chạy lệnh", + "Dotfiles": "Dotfiles", + "Volume limit": "Giới hạn âm lượng", + "On-screen display": "Âm lượng/độ sáng", + "Reboot to firmware settings": "Khởi động lại vào cài đặt firmware", + "Workspaces shown": "Số workspace hiển thị", + "Save": "Lưu", + "The popular one | Best quantity, but quality can vary wildly": "Phổ biến | Số lượng tốt nhất, nhưng chất lượng không biết đâu vào đâu", + "Save chat": "Lưu cuộc trò chuyện", + "Intelligence": "Trí tuệ", + "Translation goes here...": "Bản dịch sẽ hiện ở đây...", + "Toggle clipboard query on overview widget": "Mở/đóng tìm kiếm clipboard trên overview", + "Search": "Tìm kiếm", + "Timeout (ms)": "Thời gian chờ (ms)", + "24h": "24h", + "Color picker": "Chọn màu", + "Save to Downloads": "Lưu vào Downloads", + "No notifications": "Không có thông báo", + "Closes media controls on press": "Đóng điều khiển đa phương tiện khi nhấn", + "Game mode": "Chế độ game", + "Alternatively use /dark, /light, /img in the launcher": "Có thể dùng /dark, /light, /img trong launcher", + "Info": "Thông tin", + "Dock": "Dock", + "Pinned on startup": "Ghim khi khởi động", + "Suspend at": "Tạm dừng ở", + "Fruit Salad": "Fruit Salad", + "API key:\n\n```txt\n%1\n```": "API key:\n\n```txt\n%1\n```", + "Increase brightness": "Tăng độ sáng", + "API key set for %1": "API key đã đặt cho %1", + "Not visible to model": "Không hiển thị cho mô hình", + "Expressive": "Expressive", + "Enter text to translate...": "Nhập văn bản để dịch...", + "Usage": "Cách dùng", + "Message the model... \"%1\" for commands": "Hỏi model... \"%1\" để xem lệnh", + "Keybinds": "Phím tắt", + "Model set to %1": "Đã đặt model thành %1", + "Scale (%)": "Tỉ lệ (%)", + "Type /key to get started with online models\nCtrl+O to expand the sidebar\nCtrl+P to detach sidebar into a window": "Gõ /key để bắt đầu dùng các mô hình trực tuyến\nCtrl+O để mở rộng sidebar\nCtrl+P để nhấc sidebar thành cửa sổ", + "Toggle emoji query on overview widget": "Mở/đóng tìm kiếm emoji trên overview", + "Output": "Đầu ra", + "Uptime: %1": "Máy đã chạy được: %1", + "For desktop wallpapers | Good quality": "Cho hình nền máy tính | Chất lượng tốt", + "Nothing here!": "Không có gì!", + "Close": "Đóng", + "Arrow keys to navigate, Enter to select\nEsc or click anywhere to cancel": "Phím mũi tên để chọn, Enter để xác nhận\nEsc hoặc ấn bất kỳ đâu để thoát", + "Copy code": "Sao chép code", + "Load prompt from %1": "Tải chỉ dẫn từ %1", + "Time": "Thời gian", + "**Pricing**: free. Data use policy varies depending on your OpenRouter account settings.\n\n**Instructions**: Log into OpenRouter account, go to Keys on the topright menu, click Create API Key": "**Giá**: miễn phí. Chính sách sử dụng dữ liệu tùy thuộc vào cài đặt tài khoản OpenRouter của bạn.\n\n**Hướng dẫn**: Đăng nhập vào tài khoản OpenRouter, mở Keys ở menu góc trên bên phải, ấn Create API Key", + "Bar style": "Phong cách bar", + "Configuration": "Cài đặt", + "Prefixes": "Kí tự đầu", + "No API key set for %1": "Không có API key cho %1", + "Add task": "Thêm công việc", + "Volume mixer": "Trộn âm lượng", + "Toggles media controls on press": "Mở/đóng điều khiển đa phương tiện khi ấn", + "Go to source (%1)": "Đi đến nguồn (%1)", + "The current API used. Endpoint:": "API đang sử dụng. Endpoint:", + "View Markdown source": "Xem nguồn Markdown", + "Input": "Đầu vào", + "Opens on screen keyboard on press": "Mở bàn phím ảo khi ấn", + "Allow NSFW": "Cho phép NSFW", + "Session": "Phiên làm việc", + "Detach left sidebar into a window/Attach it back": "Nhấc sidebar trái thành cửa sổ/Đặt nó lại", + "Night Light": "Lọc ánh sáng xanh", + "Workspaces": "Các workspace", + "Toggles overview on press": "Mở/đóng overview khi ấn", + "Dark": "Tối", + "Base URL": "Base URL", + "Hug": "Ôm", + "Buttons": "Các nút", + "Get the next page of results": "Lấy trang kết quả tiếp theo", + "%1 Safe Storage": "Lưu trữ an toàn %1", + "Color generation": "Chỉnh màu" +} From fec499050604c7c12df73909c9259321dfbeb3a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=88=E6=9C=88?= <3600911665@qq.com> Date: Thu, 17 Jul 2025 08:45:25 +0800 Subject: [PATCH 15/20] translations: Update chat save message in Chinese translations --- .config/quickshell/translations/en_US.json | 4 ++-- .config/quickshell/translations/zh_CN.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.config/quickshell/translations/en_US.json b/.config/quickshell/translations/en_US.json index 71b5e6081..9446fd01d 100644 --- a/.config/quickshell/translations/en_US.json +++ b/.config/quickshell/translations/en_US.json @@ -313,8 +313,8 @@ "The current system prompt is\n\n---\n\n%1": "The current system prompt is\n\n---\n\n%1", "Unknown function call: %1": "Unknown function call: %1", "%1 notifications": "%1 notifications", - "Save chat from %1": "Save chat from %1", "Load chat from %1": "Load chat from %1", "Load prompt from %1": "Load prompt from %1", - "Online models disallowed for \n\nControlled by `policies.ai` config option": "Online models disallowed for \n\nControlled by `policies.ai` config option" + "Online models disallowed for \n\nControlled by `policies.ai` config option": "Online models disallowed for \n\nControlled by `policies.ai` config option", + "Save chat to %1": "Save chat to %1" } \ No newline at end of file diff --git a/.config/quickshell/translations/zh_CN.json b/.config/quickshell/translations/zh_CN.json index 489f3ad56..957a1e006 100644 --- a/.config/quickshell/translations/zh_CN.json +++ b/.config/quickshell/translations/zh_CN.json @@ -314,7 +314,7 @@ "Online models disallowed for \n\nControlled by `policies.ai` config option": "禁止使用在线模型\n\n由 `policies.ai` 配置项控制", "Loaded the following system prompt\n\n---\n\n%1": "已加载以下系统提示词\n\n---\n\n%1", "%1 notifications": "%1 条通知", - "Save chat from %1": "保存聊天记录到 %1", + "Save chat to %1": "保存聊天记录到 %1", "Load chat from %1": "从 %1 加载聊天记录", "Load prompt from %1": "从 %1 加载提示词" } \ No newline at end of file From 4660a154d05e17851faff32bfe53103dd8a331d8 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 17 Jul 2025 13:09:35 +0700 Subject: [PATCH 16/20] translations: add untranslated text, fix some originals --- .../quickshell/ii/modules/sidebarRight/BottomWidgetGroup.qml | 3 ++- .../ii/modules/sidebarRight/volumeMixer/VolumeMixer.qml | 2 +- .config/quickshell/ii/services/Ai.qml | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.config/quickshell/ii/modules/sidebarRight/BottomWidgetGroup.qml b/.config/quickshell/ii/modules/sidebarRight/BottomWidgetGroup.qml index 0dd718847..a8eda901b 100644 --- a/.config/quickshell/ii/modules/sidebarRight/BottomWidgetGroup.qml +++ b/.config/quickshell/ii/modules/sidebarRight/BottomWidgetGroup.qml @@ -98,7 +98,8 @@ Rectangle { property int remainingTasks: Todo.list.filter(task => !task.done).length; Layout.margins: 10 Layout.leftMargin: 0 - text: `${DateTime.collapsedCalendarFormat} • ${remainingTasks} task${remainingTasks > 1 ? "s" : ""}` + // text: `${DateTime.collapsedCalendarFormat} • ${remainingTasks} task${remainingTasks > 1 ? "s" : ""}` + text: Translation.tr("%1 • %2 tasks").arg(DateTime.collapsedCalendarFormat).arg(remainingTasks) font.pixelSize: Appearance.font.pixelSize.large color: Appearance.colors.colOnLayer1 } diff --git a/.config/quickshell/ii/modules/sidebarRight/volumeMixer/VolumeMixer.qml b/.config/quickshell/ii/modules/sidebarRight/volumeMixer/VolumeMixer.qml index 7c74afd64..68cc3444f 100644 --- a/.config/quickshell/ii/modules/sidebarRight/volumeMixer/VolumeMixer.qml +++ b/.config/quickshell/ii/modules/sidebarRight/volumeMixer/VolumeMixer.qml @@ -179,7 +179,7 @@ Item { Layout.alignment: Qt.AlignLeft color: Appearance.m3colors.m3onSurface font.pixelSize: Appearance.font.pixelSize.larger - text: `Select ${root.deviceSelectorInput ? "input" : "output"} device` + text: root.deviceSelectorInput ? Translation.tr("Select input device") : Translation.tr("Select output device") } Rectangle { diff --git a/.config/quickshell/ii/services/Ai.qml b/.config/quickshell/ii/services/Ai.qml index db47ea490..406724939 100644 --- a/.config/quickshell/ii/services/Ai.qml +++ b/.config/quickshell/ii/services/Ai.qml @@ -372,7 +372,7 @@ Singleton { // See if policy prevents online models if (Config.options.policies.ai === 2 && !model.endpoint.includes("localhost")) { root.addMessage( - Translation.tr("Online models disallowed for \n\nControlled by `policies.ai` config option"), + Translation.tr("Online models disallowed\n\nControlled by `policies.ai` config option"), root.interfaceRole ); return; From 1429b5977b8ec5a228e368a0e435bda079dc64fd Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 17 Jul 2025 13:13:21 +0700 Subject: [PATCH 17/20] translations: adjust vietnamese --- .config/quickshell/translations/vi_VN.json | 53 ++++++++++++---------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/.config/quickshell/translations/vi_VN.json b/.config/quickshell/translations/vi_VN.json index 27ce9f434..e46230028 100644 --- a/.config/quickshell/translations/vi_VN.json +++ b/.config/quickshell/translations/vi_VN.json @@ -1,7 +1,7 @@ { "Unknown function call: %1": "Hàm không xác định: %1", "Show next time": "Hiển thị lần sau", - "Fidelity": "Fidelity", + "Fidelity": "Khá giống gốc", "Open file link": "Mở liên kết tệp", "Interrupts possibility of overview being toggled on release.": "Ngăn mở overview khi nhả nút.", "No audio source": "Không có nguồn âm thanh", @@ -9,14 +9,14 @@ "Jump to current month": "Nhảy đến tháng hiện tại", "Delete": "Xóa", "**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key": "**Giá**: miễn phí. Dữ liệu được sử dụng cho mục đích huấn luyện. **Hướng dẫn**: Đăng nhập vào tài khoản Google, cho phép AI Studio tạo dự án Google Cloud gì đó, quay lại rồi ấn Get API key", - "Rainbow": "Rainbow", + "Rainbow": "Cầu vồng", "%1 does not require an API key": "%1 không cần API key", "Choose model": "Chọn model", "Prevents abrupt increments and restricts volume limit": "Chặn thay đổi đột ngột và giới hạn âm lượng", "%1 characters": "%1 ký tự", "Change any time later with /dark, /light, /img in the launcher": "Thay đổi bất cứ lúc nào sau này với /dark, /light, /img trong launcher", "Tonal Spot": "Tonal Spot", - "Neutral": "Neutral", + "Neutral": "Trung tính", "To Do": "Cần làm", "Auto": "Tự động", "Polling interval (ms)": "Thời gian lặp lại (ms)", @@ -36,7 +36,6 @@ "Silent": "Im lặng", "Columns": "Số cột", "Set with /mode PROVIDER": "Set with /mode PROVIDER", - "Save chat to %1": "Lưu trò chuyện vào %1", "Issues": "Các vấn đề", "Policies": "Chính sách", "Load chat from %1": "Tải trò chuyện từ %1", @@ -52,15 +51,14 @@ "Yooooo hi there": "Yooooo chào bạn", "Triggers volume OSD on press": "Hiển thị âm lượng khi ấn", "Colors & Wallpaper": "Màu sắc & Hình nền", - "No media": "Không có nhạc/video", + "No media": "Không có media", "Critical warning": "Cảnh báo rất thấp", "Mic toggle": "Bật/tắt mic", "12h AM/PM": "12h AM/PM", - "Online models disallowed\n\nControlled by `policies.ai` config option": "Model trên mạng không được cho phép\n\nTheo cài đặt `policies.ai`", "Large language models": "Mô hình ngôn ngữ lớn", "Markdown test": "Test markdown", "Temperature: %1": "Nhiệt độ: %1", - "Opens media controls on press": "Mở điều khiển nhạc/video khi ấn", + "Opens media controls on press": "Mở điều khiển media khi ấn", "Edit": "Sửa", "Closes overview": "Đóng overview", "Waifus only | Excellent quality, limited quantity": "Chỉ waifus | Chất lượng xuất sắc, số lượng hạn chế", @@ -75,7 +73,7 @@ "Invalid model. Supported: \n```": "Model không hợp lệ. Các lựa chọn: \n```", "Calendar": "Lịch", "Done": "Đã xong", - "Monochrome": "Monochrome", + "Monochrome": "Đen trắng", "Show regions of potential interest": "Hiển thị vùng thông minh", "Dark/Light toggle": "Chuyển chế độ sáng/tối", "Unknown command:": "Lệnh không xác định:", @@ -129,7 +127,7 @@ "Borderless": "Không viền", "Set API key": "Đặt API key", "Clean stuff | Excellent quality, no NSFW": "Sạch sẽ | Chất lượng xuất sắc, không có NSFW", - "Experimental | Online | Google's model\nCan do a little more but doesn't search quickly": "Thử nghiệm | Trực tuyến | Mô hình của Google\nCó thể làm nhiều hơn một chút nhưng không tìm kiếm nhanh chóng", + "Experimental | Online | Google's model\nCan do a little more but doesn't search quickly": "Thử nghiệm | Trực tuyến | Model của Google\nCó thể làm nhiều hơn một chút nhưng không tìm kiếm nhanh chóng", "Toggles cheatsheet on press": "Mở/đóng bảng tra cứu khi ấn", "Thinking": "Đang nghĩ", "Earbang protection": "Bảo vệ tai", @@ -145,7 +143,7 @@ "Settings": "Cài đặt", "Shell & utilities": "Shell & tiện ích", "Toggles overview on release": "Mở/đóng overview khi nhả nút", - "Unfinished": "Chưa hoàn thành", + "Unfinished": "Chưa xong", "Random: Konachan": "Ngẫu nhiên: Konachan", "Opens left sidebar on press": "Mở sidebar trái khi ấn", "Pick wallpaper image on your system": "Chọn hình nền trên máy", @@ -156,7 +154,7 @@ "Keep system awake": "Giữ hệ thống bật", "To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything.": "Để đảm bảo luôn hoạt động, dùng binditn = MODKEYS, catchall trong một submap luôn được kích hoạt bao trùm mọi thứ.", "Plain rectangle": "Hình chữ nhật", - "%1 queries pending": "%1 lượt gọi đang chờ", + "%1 queries pending": "%1 lệnh gọi đang chờ", "Temperature set to %1": "Nhiệt độ đã được đặt thành %1", "Notifications": "Thông báo", "System prompt": "Chỉ dẫn hệ thống", @@ -208,7 +206,7 @@ "Help & Support": "Trợ giúp", "Enter tags, or \"%1\" for commands": "Nhập tag hoặc \"%1\" để xem các lệnh", "Format": "Định dạng", - "Content": "Nội dung", + "Content": "Giống gốc", "Edit config": "Sửa config", "Bluetooth": "Bluetooth", "Be patient...": "Bình tĩnh...", @@ -229,13 +227,13 @@ "Workspace": "Workspace", "Random SFW Anime wallpaper from Konachan\nImage is saved to ~/Pictures/Wallpapers": "Hình nền Anime SFW ngẫu nhiên từ Konachan\nẢnh được lưu vào ~/Pictures/Wallpapers", "Toggles on screen keyboard on press": "Mở/đóng bàn phím ảo khi ấn", - "Online via %1 | %2's model": "Trực tuyến qua %1 | Mô hình của %2", + "Online via %1 | %2's model": "Trực tuyến qua %1 | Model của %2", "Always show numbers": "Luôn hiện số", "or": "hoặc", "Drag or click a region • LMB: Copy • RMB: Edit": "Kéo thả hoặc chọn vùng • Chuột trái: Sao chép • Chuột phải: Chỉnh sửa", "Local only": "Chỉ trên máy", "Donate": "Ủng hộ", - "Online | Google's model\nGives up-to-date information with search.": "Trực tuyến | Mô hình của Google\nCó thể tìm kiếm để cung cấp thông tin cập nhật.", + "Online | Google's model\nGives up-to-date information with search.": "Trực tuyến | Model của Google\nCó thể tìm kiếm để cung cấp thông tin cập nhật.", "Run command": "Chạy lệnh", "Dotfiles": "Dotfiles", "Volume limit": "Giới hạn âm lượng", @@ -254,31 +252,31 @@ "Color picker": "Chọn màu", "Save to Downloads": "Lưu vào Downloads", "No notifications": "Không có thông báo", - "Closes media controls on press": "Đóng điều khiển đa phương tiện khi nhấn", + "Closes media controls on press": "Đóng điều khiển media khi nhấn", "Game mode": "Chế độ game", "Alternatively use /dark, /light, /img in the launcher": "Có thể dùng /dark, /light, /img trong launcher", "Info": "Thông tin", "Dock": "Dock", "Pinned on startup": "Ghim khi khởi động", "Suspend at": "Tạm dừng ở", - "Fruit Salad": "Fruit Salad", + "Fruit Salad": "Salad hoa quả", "API key:\n\n```txt\n%1\n```": "API key:\n\n```txt\n%1\n```", "Increase brightness": "Tăng độ sáng", "API key set for %1": "API key đã đặt cho %1", - "Not visible to model": "Không hiển thị cho mô hình", - "Expressive": "Expressive", + "Not visible to model": "Không hiển thị cho model", + "Expressive": "Biểu cảm", "Enter text to translate...": "Nhập văn bản để dịch...", "Usage": "Cách dùng", "Message the model... \"%1\" for commands": "Hỏi model... \"%1\" để xem lệnh", "Keybinds": "Phím tắt", "Model set to %1": "Đã đặt model thành %1", "Scale (%)": "Tỉ lệ (%)", - "Type /key to get started with online models\nCtrl+O to expand the sidebar\nCtrl+P to detach sidebar into a window": "Gõ /key để bắt đầu dùng các mô hình trực tuyến\nCtrl+O để mở rộng sidebar\nCtrl+P để nhấc sidebar thành cửa sổ", + "Type /key to get started with online models\nCtrl+O to expand the sidebar\nCtrl+P to detach sidebar into a window": "Gõ /key để bắt đầu dùng các model trực tuyến\nCtrl+O để mở rộng sidebar\nCtrl+P để nhấc sidebar thành cửa sổ", "Toggle emoji query on overview widget": "Mở/đóng tìm kiếm emoji trên overview", "Output": "Đầu ra", - "Uptime: %1": "Máy đã chạy được: %1", + "Uptime: %1": "Máy bật được %1", "For desktop wallpapers | Good quality": "Cho hình nền máy tính | Chất lượng tốt", - "Nothing here!": "Không có gì!", + "Nothing here!": "Không có gì ở đây!", "Close": "Đóng", "Arrow keys to navigate, Enter to select\nEsc or click anywhere to cancel": "Phím mũi tên để chọn, Enter để xác nhận\nEsc hoặc ấn bất kỳ đâu để thoát", "Copy code": "Sao chép code", @@ -291,14 +289,14 @@ "No API key set for %1": "Không có API key cho %1", "Add task": "Thêm công việc", "Volume mixer": "Trộn âm lượng", - "Toggles media controls on press": "Mở/đóng điều khiển đa phương tiện khi ấn", + "Toggles media controls on press": "Mở/đóng điều khiển media khi ấn", "Go to source (%1)": "Đi đến nguồn (%1)", "The current API used. Endpoint:": "API đang sử dụng. Endpoint:", "View Markdown source": "Xem nguồn Markdown", "Input": "Đầu vào", "Opens on screen keyboard on press": "Mở bàn phím ảo khi ấn", "Allow NSFW": "Cho phép NSFW", - "Session": "Phiên làm việc", + "Session": "Session", "Detach left sidebar into a window/Attach it back": "Nhấc sidebar trái thành cửa sổ/Đặt nó lại", "Night Light": "Lọc ánh sáng xanh", "Workspaces": "Các workspace", @@ -309,5 +307,10 @@ "Buttons": "Các nút", "Get the next page of results": "Lấy trang kết quả tiếp theo", "%1 Safe Storage": "Lưu trữ an toàn %1", - "Color generation": "Chỉnh màu" -} + "Color generation": "Chỉnh màu", + "Select output device": "Chọn đầu ra", + "Select input device": "Chọn đầu vào", + "Save chat from %1": "Lưu cuộc trò chuyện vào %1", + "%1 • %2 tasks": "%1 • %2 việc cần làm", + "Online models disallowed\n\nControlled by `policies.ai` config option": "Model trực tuyến không được cho phép\n\nCài đặt bởi lựa chọn `policies.ai`" +} \ No newline at end of file From 9ca67f0095ac70f0fb4fd3bb64c5e44de3503b6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=88=E6=9C=88?= <3600911665@qq.com> Date: Thu, 17 Jul 2025 14:25:57 +0800 Subject: [PATCH 18/20] translations: Update Chinese language files --- .config/quickshell/translations/zh_CN.json | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.config/quickshell/translations/zh_CN.json b/.config/quickshell/translations/zh_CN.json index 957a1e006..36bdd2dcc 100644 --- a/.config/quickshell/translations/zh_CN.json +++ b/.config/quickshell/translations/zh_CN.json @@ -311,10 +311,14 @@ "Message the model... \"%1\" for commands": "与模型对话... \"%1\" 查看命令", "The current system prompt is\n\n---\n\n%1": "当前系统提示词为\n\n---\n\n%1", "Model set to %1": "模型已设置为 %1", - "Online models disallowed for \n\nControlled by `policies.ai` config option": "禁止使用在线模型\n\n由 `policies.ai` 配置项控制", "Loaded the following system prompt\n\n---\n\n%1": "已加载以下系统提示词\n\n---\n\n%1", "%1 notifications": "%1 条通知", "Save chat to %1": "保存聊天记录到 %1", "Load chat from %1": "从 %1 加载聊天记录", - "Load prompt from %1": "从 %1 加载提示词" + "Load prompt from %1": "从 %1 加载提示词", + "Select output device": "选择输出设备", + "%1 • %2 tasks": "%1 • %2 个任务", + "Online models disallowed\n\nControlled by `policies.ai` config option": "禁止在线模型\n\n由 `policies.ai` 配置项控制", + "Select input device": "选择输入设备" + } \ No newline at end of file From 1e7c84a6c1dbe6698b13137971155c20782913d5 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 17 Jul 2025 13:56:50 +0700 Subject: [PATCH 19/20] translations: update `qsTr`s to Translation.tr and allow translation of notifications --- .config/quickshell/ii/modules/bar/Bar.qml | 10 ++--- .../ii/modules/cheatsheet/Cheatsheet.qml | 4 +- .../quickshell/ii/modules/common/Config.qml | 2 +- .../sidebarLeft/aiChat/MessageCodeBlock.qml | 6 ++- .../modules/sidebarLeft/anime/BooruImage.qml | 2 +- .../quickToggles/CloudflareWarp.qml | 14 +++++-- .config/quickshell/ii/services/Battery.qml | 19 +++++++-- .config/quickshell/ii/services/Weather.qml | 39 +++++++++---------- .config/quickshell/translations/en_US.json | 26 ++++++++++++- .config/quickshell/translations/vi_VN.json | 23 ++++++++++- 10 files changed, 104 insertions(+), 41 deletions(-) diff --git a/.config/quickshell/ii/modules/bar/Bar.qml b/.config/quickshell/ii/modules/bar/Bar.qml index 08383c10e..23948029d 100644 --- a/.config/quickshell/ii/modules/bar/Bar.qml +++ b/.config/quickshell/ii/modules/bar/Bar.qml @@ -169,7 +169,7 @@ Scope { ScrollHint { reveal: barLeftSideMouseArea.hovered icon: "light_mode" - tooltipText: qsTr("Scroll to change brightness") + tooltipText: Translation.tr("Scroll to change brightness") side: "left" anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter @@ -380,7 +380,7 @@ Scope { ScrollHint { reveal: barRightSideMouseArea.hovered icon: "volume_up" - tooltipText: qsTr("Scroll to change volume") + tooltipText: Translation.tr("Scroll to change volume") side: "right" anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter @@ -586,7 +586,7 @@ Scope { GlobalShortcut { name: "barToggle" - description: qsTr("Toggles bar on press") + description: Translation.tr("Toggles bar on press") onPressed: { GlobalStates.barOpen = !GlobalStates.barOpen; @@ -595,7 +595,7 @@ Scope { GlobalShortcut { name: "barOpen" - description: qsTr("Opens bar on press") + description: Translation.tr("Opens bar on press") onPressed: { GlobalStates.barOpen = true; @@ -604,7 +604,7 @@ Scope { GlobalShortcut { name: "barClose" - description: qsTr("Closes bar on press") + description: Translation.tr("Closes bar on press") onPressed: { GlobalStates.barOpen = false; diff --git a/.config/quickshell/ii/modules/cheatsheet/Cheatsheet.qml b/.config/quickshell/ii/modules/cheatsheet/Cheatsheet.qml index a0b22779c..bc6b67ac9 100644 --- a/.config/quickshell/ii/modules/cheatsheet/Cheatsheet.qml +++ b/.config/quickshell/ii/modules/cheatsheet/Cheatsheet.qml @@ -19,11 +19,11 @@ Scope { // Scope property var tabButtonList: [ { "icon": "keyboard", - "name": qsTr("Keybinds") + "name": Translation.tr("Keybinds") }, { "icon": "experiment", - "name": qsTr("Elements") + "name": Translation.tr("Elements") }, ] property int selectedTab: 0 diff --git a/.config/quickshell/ii/modules/common/Config.qml b/.config/quickshell/ii/modules/common/Config.qml index 33ebc94c6..1e73cea51 100644 --- a/.config/quickshell/ii/modules/common/Config.qml +++ b/.config/quickshell/ii/modules/common/Config.qml @@ -59,7 +59,7 @@ Singleton { } property JsonObject ai: JsonObject { - property string systemPrompt: qsTr("## Style\n- Use casual tone, don't be formal! Make sure you answer precisely without hallucination and prefer bullet points over walls of text. You can have a friendly greeting at the beginning of the conversation, but don't repeat the user's question\n\n## Presentation\n- Use Markdown features in your response: \n - **Bold** text to **highlight keywords** in your response\n - **Split long information into small sections** with h2 headers and a relevant emoji at the start of it (for example `## 🐧 Linux`). Bullet points are preferred over long paragraphs, unless you're offering writing support or instructed otherwise by the user.\n- Asked to compare different options? You should firstly use a table to compare the main aspects, then elaborate or include relevant comments from online forums *after* the table. Make sure to provide a final recommendation for the user's use case!\n- Use LaTeX formatting for mathematical and scientific notations whenever appropriate. Enclose all LaTeX '$$' delimiters. NEVER generate LaTeX code in a latex block unless the user explicitly asks for it. DO NOT use LaTeX for regular documents (resumes, letters, essays, CVs, etc.).\n\nThanks!\n\n## Tools\nMay or may not be available depending on the user's settings. If they're available, follow these guidelines:\n\n### Search\n- When user asks for information that might benefit from up-to-date information, use this to get search access\n\n### Shell configuration\n- Always fetch the config options to see the available keys before setting\n- Avoid unnecessarily asking the user to confirm the changes they explicitly asked for, just do it\n") + property string systemPrompt: "## Style\n- Use casual tone, don't be formal! Make sure you answer precisely without hallucination and prefer bullet points over walls of text. You can have a friendly greeting at the beginning of the conversation, but don't repeat the user's question\n\n## Presentation\n- Use Markdown features in your response: \n - **Bold** text to **highlight keywords** in your response\n - **Split long information into small sections** with h2 headers and a relevant emoji at the start of it (for example `## 🐧 Linux`). Bullet points are preferred over long paragraphs, unless you're offering writing support or instructed otherwise by the user.\n- Asked to compare different options? You should firstly use a table to compare the main aspects, then elaborate or include relevant comments from online forums *after* the table. Make sure to provide a final recommendation for the user's use case!\n- Use LaTeX formatting for mathematical and scientific notations whenever appropriate. Enclose all LaTeX '$$' delimiters. NEVER generate LaTeX code in a latex block unless the user explicitly asks for it. DO NOT use LaTeX for regular documents (resumes, letters, essays, CVs, etc.).\n\nThanks!\n\n## Tools\nMay or may not be available depending on the user's settings. If they're available, follow these guidelines:\n\n### Search\n- When user asks for information that might benefit from up-to-date information, use this to get search access\n\n### Shell configuration\n- Always fetch the config options to see the available keys before setting\n- Avoid unnecessarily asking the user to confirm the changes they explicitly asked for, just do it\n" } property JsonObject appearance: JsonObject { diff --git a/.config/quickshell/ii/modules/sidebarLeft/aiChat/MessageCodeBlock.qml b/.config/quickshell/ii/modules/sidebarLeft/aiChat/MessageCodeBlock.qml index 957958ef4..eac41af56 100644 --- a/.config/quickshell/ii/modules/sidebarLeft/aiChat/MessageCodeBlock.qml +++ b/.config/quickshell/ii/modules/sidebarLeft/aiChat/MessageCodeBlock.qml @@ -99,7 +99,11 @@ ColumnLayout { Quickshell.execDetached(["bash", "-c", `echo '${StringUtils.shellSingleQuoteEscape(segmentContent)}' > '${downloadPath}/code.${segmentLang || "txt"}'` ]) - Quickshell.execDetached(["bash", "-c", `notify-send 'Code saved to file' '${downloadPath}/code.${segmentLang || "txt"}' -a Shell`]) + Quickshell.execDetached(["notify-send", + Translation.tr("Code saved to file"), + Translation.tr("Saved to %1").arg(`${downloadPath}/code.${segmentLang || "txt"}`), + "-a", "Shell" + ]) saveCodeButton.activated = true saveIconTimer.restart() } diff --git a/.config/quickshell/ii/modules/sidebarLeft/anime/BooruImage.qml b/.config/quickshell/ii/modules/sidebarLeft/anime/BooruImage.qml index 9ead31167..e8b24d196 100644 --- a/.config/quickshell/ii/modules/sidebarLeft/anime/BooruImage.qml +++ b/.config/quickshell/ii/modules/sidebarLeft/anime/BooruImage.qml @@ -181,7 +181,7 @@ Button { onClicked: { root.showActions = false Quickshell.execDetached(["bash", "-c", - `curl '${root.imageData.file_url}' -o '${root.imageData.is_nsfw ? root.nsfwPath : root.downloadPath}/${root.fileName}' && notify-send '${qsTr("Download complete")}' '${root.downloadPath}/${root.fileName}' -a 'Shell'` + `curl '${root.imageData.file_url}' -o '${root.imageData.is_nsfw ? root.nsfwPath : root.downloadPath}/${root.fileName}' && notify-send '${Translation.tr("Download complete")}' '${root.downloadPath}/${root.fileName}' -a 'Shell'` ]) } } diff --git a/.config/quickshell/ii/modules/sidebarRight/quickToggles/CloudflareWarp.qml b/.config/quickshell/ii/modules/sidebarRight/quickToggles/CloudflareWarp.qml index cdaf84b76..d9cf0fa6a 100644 --- a/.config/quickshell/ii/modules/sidebarRight/quickToggles/CloudflareWarp.qml +++ b/.config/quickshell/ii/modules/sidebarRight/quickToggles/CloudflareWarp.qml @@ -40,7 +40,11 @@ QuickToggleButton { command: ["warp-cli", "connect"] onExited: (exitCode, exitStatus) => { if (exitCode !== 0) { - Quickshell.execDetached(["notify-send", "Cloudflare WARP", "Connection failed. Please inspect manually with the warp-cli command", "-a", "Shell"]) + Quickshell.execDetached(["notify-send", + Translation.tr("Cloudflare WARP"), + Translation.tr("Connection failed. Please inspect manually with the warp-cli command") + , "-a", "Shell" + ]) } } } @@ -53,7 +57,11 @@ QuickToggleButton { if (exitCode === 0) { connectProc.running = true } else { - Quickshell.execDetached(["notify-send", "Cloudflare WARP", "Registration failed. Please inspect manually with the warp-cli command", "-a", "Shell"]) + Quickshell.execDetached(["notify-send", + Translation.tr("Cloudflare WARP"), + Translation.tr("Registration failed. Please inspect manually with the warp-cli command"), + "-a", "Shell" + ]) } } } @@ -80,6 +88,6 @@ QuickToggleButton { } } StyledToolTip { - content: qsTr("Cloudflare WARP (1.1.1.1)") + content: Translation.tr("Cloudflare WARP (1.1.1.1)") } } diff --git a/.config/quickshell/ii/services/Battery.qml b/.config/quickshell/ii/services/Battery.qml index 6a432f3a4..abe7700d4 100644 --- a/.config/quickshell/ii/services/Battery.qml +++ b/.config/quickshell/ii/services/Battery.qml @@ -23,13 +23,24 @@ Singleton { property bool isSuspendingAndNotCharging: allowAutomaticSuspend && isSuspending && !isCharging onIsLowAndNotChargingChanged: { - if (available && isLowAndNotCharging) - Quickshell.execDetached(["bash", "-c", `notify-send "Low battery" "Consider plugging in your device" -u critical -a "Shell"`]); + if (available && isLowAndNotCharging) Quickshell.execDetached([ + "notify-send", + Translation.tr("Low battery"), + Translation.tr("Consider plugging in your device"), + "-u", "critical", + "-a", "Shell" + ]) } onIsCriticalAndNotChargingChanged: { - if (available && isCriticalAndNotCharging) - Quickshell.execDetached(["bash", "-c", `notify-send "Critically low battery" "🙏 I beg for pleas charg\nAutomatic suspend triggers at ${Config.options.battery.suspend}%" -u critical -a "Shell"`]); + if (available && isCriticalAndNotCharging) Quickshell.execDetached([ + "notify-send", + Translation.tr("Critically low battery"), + Translation.tr("Please charge!\nAutomatic suspend triggers at %1").arg(Config.options.battery.suspend), + "-u", "critical", + "-a", "Shell" + ]); + } onIsSuspendingAndNotChargingChanged: { diff --git a/.config/quickshell/ii/services/Weather.qml b/.config/quickshell/ii/services/Weather.qml index eb1a00af4..a72e7e2ed 100644 --- a/.config/quickshell/ii/services/Weather.qml +++ b/.config/quickshell/ii/services/Weather.qml @@ -17,25 +17,25 @@ Singleton { property bool gpsActive: Config.options.bar.weather.enableGPS property var location: ({ - valid: false, - lat: 0, - lon: 0 - }) + valid: false, + lat: 0, + lon: 0 + }) property var data: ({ - uv: 0, - humidity: 0, - sunrise: 0, - sunset: 0, - windDir: 0, - wCode: 0, - city: 0, - wind: 0, - precip: 0, - visib: 0, - press: 0, - temp: 0 - }) + uv: 0, + humidity: 0, + sunrise: 0, + sunset: 0, + windDir: 0, + wCode: 0, + city: 0, + wind: 0, + precip: 0, + visib: 0, + press: 0, + temp: 0 + }) function refineData(data) { let temp = {}; @@ -90,8 +90,7 @@ Singleton { } Component.onCompleted: { - if (!root.gpsActive) - return; + if (!root.gpsActive) return; console.info("[WeatherService] Starting the GPS service."); positionSource.start(); } @@ -139,7 +138,7 @@ Singleton { positionSource.stop(); root.location.valid = false; root.gpsActive = false; - Quickshell.execDetached(["bash", "-c", `notify-send WeatherService 'Can not find a GPS service. Using the fallback method instead.'`]); + Quickshell.execDetached(["notify-send", Translation.tr("Weather Service"), Translation.tr("Cannot find a GPS service. Using the fallback method instead."), "-a", "Shell"]); console.error("[WeatherService] Could not aquire a valid backend plugin."); } } diff --git a/.config/quickshell/translations/en_US.json b/.config/quickshell/translations/en_US.json index 9446fd01d..7141cdaa5 100644 --- a/.config/quickshell/translations/en_US.json +++ b/.config/quickshell/translations/en_US.json @@ -315,6 +315,28 @@ "%1 notifications": "%1 notifications", "Load chat from %1": "Load chat from %1", "Load prompt from %1": "Load prompt from %1", - "Online models disallowed for \n\nControlled by `policies.ai` config option": "Online models disallowed for \n\nControlled by `policies.ai` config option", - "Save chat to %1": "Save chat to %1" + "Save chat to %1": "Save chat to %1", + "Weather Service": "Weather Service", + "Cannot find a GPS service. Using the fallback method instead.": "Cannot find a GPS service. Using the fallback method instead.", + "Critically low battery": "Critically low battery", + "Select output device": "Select output device", + "Code saved to file": "Code saved to file", + "Online models disallowed\n\nControlled by `policies.ai` config option": "Online models disallowed\n\nControlled by `policies.ai` config option", + "Opens bar on press": "Opens bar on press", + "Scroll to change volume": "Scroll to change volume", + "Toggles bar on press": "Toggles bar on press", + "Elements": "Elements", + "%1 • %2 tasks": "%1 • %2 tasks", + "Download complete": "Download complete", + "Please charge!\nAutomatic suspend triggers at %1": "Please charge!\nAutomatic suspend triggers at %1", + "Cloudflare WARP": "Cloudflare WARP", + "Cloudflare WARP (1.1.1.1)": "Cloudflare WARP (1.1.1.1)", + "Closes bar on press": "Closes bar on press", + "Scroll to change brightness": "Scroll to change brightness", + "Connection failed. Please inspect manually with the warp-cli command": "Connection failed. Please inspect manually with the warp-cli command", + "Select input device": "Select input device", + "Registration failed. Please inspect manually with the warp-cli command": "Registration failed. Please inspect manually with the warp-cli command", + "Consider plugging in your device": "Consider plugging in your device", + "Low battery": "Low battery", + "Saved to %1": "Saved to %1" } \ No newline at end of file diff --git a/.config/quickshell/translations/vi_VN.json b/.config/quickshell/translations/vi_VN.json index e46230028..9c2bc979d 100644 --- a/.config/quickshell/translations/vi_VN.json +++ b/.config/quickshell/translations/vi_VN.json @@ -310,7 +310,26 @@ "Color generation": "Chỉnh màu", "Select output device": "Chọn đầu ra", "Select input device": "Chọn đầu vào", - "Save chat from %1": "Lưu cuộc trò chuyện vào %1", "%1 • %2 tasks": "%1 • %2 việc cần làm", - "Online models disallowed\n\nControlled by `policies.ai` config option": "Model trực tuyến không được cho phép\n\nCài đặt bởi lựa chọn `policies.ai`" + "Online models disallowed\n\nControlled by `policies.ai` config option": "Model trực tuyến không được cho phép\n\nCài đặt bởi lựa chọn `policies.ai`", + "Download complete": "Đã tải xong", + "Code saved to file": "Code đã lưu vào file", + "Critically low battery": "Pin rất thấp", + "Scroll to change brightness": "Cuộn để thay đổi độ sáng", + "Cloudflare WARP": "Cloudflare WARP", + "Toggles bar on press": "Mở/đóng bar khi ấn", + "Saved to %1": "Đã lưu vào %1", + "Elements": "Nguyên tố", + "Save chat to %1": "Lưu chat vào %1", + "Connection failed. Please inspect manually with the warp-cli command": "Kết nối không thành công. Hãy xem lại với lệnh warp-cli", + "Weather Service": "Thời tiết", + "Registration failed. Please inspect manually with the warp-cli command": "Đăng ký không thành công. Hãy xem lại với lệnh warp-cli", + "Consider plugging in your device": "Hãy cắm nguồn thiết bị của bạn", + "Cloudflare WARP (1.1.1.1)": "Cloudflare WARP (1.1.1.1)", + "Cannot find a GPS service. Using the fallback method instead.": "Không tìm thấy dịch vụ GPS. Đang sử dụng phương pháp dự phòng.", + "Opens bar on press": "Mở bar khi ấn", + "Low battery": "Pin yếu", + "Scroll to change volume": "Cuộn để thay đổi âm lượng", + "Please charge!\nAutomatic suspend triggers at %1": "Hãy sạc pin!\nHệ thống sẽ tự động ngủ khi pin xuống %1", + "Closes bar on press": "Đóng bar khi ấn" } \ No newline at end of file From 9978c87b67dd5e5931d7fd77261e307b526bfc35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=88=E6=9C=88?= <3600911665@qq.com> Date: Thu, 17 Jul 2025 16:03:27 +0800 Subject: [PATCH 20/20] translations: update translations/zh_CN.json --- .config/quickshell/translations/zh_CN.json | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/.config/quickshell/translations/zh_CN.json b/.config/quickshell/translations/zh_CN.json index 36bdd2dcc..c2c2fdf89 100644 --- a/.config/quickshell/translations/zh_CN.json +++ b/.config/quickshell/translations/zh_CN.json @@ -319,6 +319,24 @@ "Select output device": "选择输出设备", "%1 • %2 tasks": "%1 • %2 个任务", "Online models disallowed\n\nControlled by `policies.ai` config option": "禁止在线模型\n\n由 `policies.ai` 配置项控制", - "Select input device": "选择输入设备" - + "Select input device": "选择输入设备", + "Low battery": "电量低", + "Opens bar on press": "按下时打开条栏", + "Registration failed. Please inspect manually with the warp-cli command": "注册失败。请使用 warp-cli 命令手动检查", + "Code saved to file": "代码已保存到文件", + "Consider plugging in your device": "请考虑连接您的设备", + "Weather Service": "天气服务", + "Please charge!\nAutomatic suspend triggers at %1": "请充电!\n自动挂起将在 %1 时触发", + "Cloudflare WARP (1.1.1.1)": "Cloudflare WARP (1.1.1.1)", + "Cloudflare WARP": "Cloudflare WARP", + "Closes bar on press": "按下时关闭条栏", + "Download complete": "下载完成", + "Critically low battery": "电量极低", + "Scroll to change brightness": "滚动以调节亮度", + "Saved to %1": "已保存到 %1", + "Cannot find a GPS service. Using the fallback method instead.": "无法找到 GPS 服务。正在使用备用方法。", + "Elements": "元素", + "Scroll to change volume": "滚动以调节音量", + "Connection failed. Please inspect manually with the warp-cli command": "连接失败。请使用 warp-cli 命令手动检查", + "Toggles bar on press": "按下时切换条栏" } \ No newline at end of file