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