diff --git a/.config/quickshell/ii/Translation.qml b/.config/quickshell/ii/Translation.qml new file mode 100644 index 00000000..97aff7c5 --- /dev/null +++ b/.config/quickshell/ii/Translation.qml @@ -0,0 +1,170 @@ +pragma Singleton + +import QtQuick +import Quickshell +import Quickshell.Io +import "root:/modules/common/" + +Singleton { + id: root + + property var translations: ({}) + property string currentLanguage: "en_US" + property var availableLanguages: ["en_US"] + property bool isScanning: false + property bool isLoading: false + + Process { + id: scanLanguagesProcess + command: ["find", Qt.resolvedUrl(Directories.config + "/quickshell/translations/").toString().replace("file://", ""), "-name", "*.json", "-exec", "basename", "{}", ".json", ";"] + running: false + + stdout: SplitParser { + onRead: data => { + if (data.trim().length === 0) return + + var files = data.trim().split('\n') + + for (var i = 0; i < files.length; i++) { + var lang = files[i].trim() + if (lang.length > 0 && root.availableLanguages.indexOf(lang) === -1) { + root.availableLanguages.push(lang) + } + } + } + } + + onExited: (exitCode, exitStatus) => { + root.isScanning = false + if (exitCode !== 0) { + root.availableLanguages = ["en_US"] + } + root.loadTranslations() + } + } + + FileView { + id: translationFileView + onLoaded: { + var textContent = "" + try { + textContent = text() + } catch (e) { + root.translations = {} + root.isLoading = false + return + } + + if (textContent.length === 0) { + root.translations = {} + root.isLoading = false + return + } + + try { + var jsonData = JSON.parse(textContent) + root.translations = jsonData + root.isLoading = false + } catch (e) { + root.translations = {} + root.isLoading = false + } + } + onLoadFailed: (error) => { + root.translations = {} + root.isLoading = false + } + } + + function detectSystemLanguage() { + var locale = Qt.locale().name + return locale + } + + function getLanguageCode() { + var configLang = "auto" + try { + configLang = ConfigOptions.language.ui + } catch (e) { + configLang = "auto" + } + + if (configLang === "auto") { + return detectSystemLanguage() + } else { + if (root.availableLanguages.indexOf(configLang) !== -1) { + return configLang + } else { + return detectSystemLanguage() + } + } + } + + function loadTranslations() { + if (root.isScanning) { + return + } + + var targetLang = getLanguageCode() + root.currentLanguage = targetLang + + // Use empty translations for English (default language) + if (targetLang === "en_US" || targetLang === "en") { + root.translations = {} + return + } + + // Check if target language is available + if (root.availableLanguages.indexOf(targetLang) === -1) { + root.currentLanguage = "en_US" + root.translations = {} + return + } + + // Load translation file + root.isLoading = true + var translationsPath = Qt.resolvedUrl(Directories.config + "/quickshell/translations/" + targetLang + ".json") + translationFileView.path = translationsPath + } + + function tr(text) { + if (!text) { + return "" + } + + var key = text.toString() + + if (root.isLoading) { + return key + } + + if (root.currentLanguage === "en_US" || root.currentLanguage === "en" || !root.translations) { + return key + } + + if (root.translations.hasOwnProperty(key)) { + var translation = root.translations[key] + if (translation && translation.toString().trim().length > 0) { + return translation.toString() + } else { + return translation.toString() + } + } + + return key // Fallback to key name + } + + function reloadTranslations() { + root.scanLanguages() + } + + function scanLanguages() { + var translationsDir = Qt.resolvedUrl(Directories.config + "/quickshell/translations/").toString().replace("file://", "") + root.isScanning = true + scanLanguagesProcess.running = true + } + + Component.onCompleted: { + root.scanLanguages() + } +} diff --git a/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml b/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml index 87d9b8ff..e8a096a9 100644 --- a/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml +++ b/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml @@ -73,7 +73,7 @@ Item { }, { name: "save", - description: qsTr("Save chat"), + description: Translation.tr("Save chat"), execute: (args) => { const joinedArgs = args.join(" ") if (joinedArgs.trim().length == 0) { @@ -85,7 +85,7 @@ Item { }, { name: "load", - description: qsTr("Load chat"), + description: Translation.tr("Load chat"), execute: (args) => { const joinedArgs = args.join(" ") if (joinedArgs.trim().length == 0) { @@ -97,7 +97,7 @@ Item { }, { name: "clear", - description: qsTr("Clear chat history"), + description: Translation.tr("Clear chat history"), execute: () => { Ai.clearMessages(); } diff --git a/.config/quickshell/ii/modules/sidebarLeft/DescriptionBox.qml b/.config/quickshell/ii/modules/sidebarLeft/DescriptionBox.qml index cb2f7e24..2ac78f5b 100644 --- a/.config/quickshell/ii/modules/sidebarLeft/DescriptionBox.qml +++ b/.config/quickshell/ii/modules/sidebarLeft/DescriptionBox.qml @@ -47,7 +47,7 @@ Item { // Tag suggestion description } StyledText { visible: root.showArrows && root.showTab - text: qsTr("or") + text: Translation.tr("or") font.pixelSize: Appearance.font.pixelSize.smaller } KeyboardKey { diff --git a/.config/quickshell/ii/modules/sidebarLeft/SidebarLeftContent.qml b/.config/quickshell/ii/modules/sidebarLeft/SidebarLeftContent.qml index c2fd9614..a40b855a 100644 --- a/.config/quickshell/ii/modules/sidebarLeft/SidebarLeftContent.qml +++ b/.config/quickshell/ii/modules/sidebarLeft/SidebarLeftContent.qml @@ -19,9 +19,9 @@ Item { required property var scopeRoot anchors.fill: parent property var tabButtonList: [ - ...(Config.options.policies.ai !== 0 ? [{"icon": "neurology", "name": qsTr("Intelligence")}] : []), - {"icon": "translate", "name": qsTr("Translator")}, - ...(Config.options.policies.weeb === 1 ? [{"icon": "bookmark_heart", "name": qsTr("Anime")}] : []) + ...(Config.options.policies.ai !== 0 ? [{"icon": "neurology", "name": Translation.tr("Intelligence")}] : []), + {"icon": "translate", "name": Translation.tr("Translator")}, + ...(Config.options.policies.weeb === 1 ? [{"icon": "bookmark_heart", "name": Translation.tr("Anime")}] : []) ] property int selectedTab: 0 diff --git a/.config/quickshell/ii/modules/sidebarRight/BottomWidgetGroup.qml b/.config/quickshell/ii/modules/sidebarRight/BottomWidgetGroup.qml index efc34d9f..d2c4af9c 100644 --- a/.config/quickshell/ii/modules/sidebarRight/BottomWidgetGroup.qml +++ b/.config/quickshell/ii/modules/sidebarRight/BottomWidgetGroup.qml @@ -17,8 +17,8 @@ Rectangle { property int selectedTab: 0 property bool collapsed: Persistent.states.sidebar.bottomGroup.collapsed property var tabs: [ - {"type": "calendar", "name": "Calendar", "icon": "calendar_month", "widget": calendarWidget}, - {"type": "todo", "name": "To Do", "icon": "done_outline", "widget": todoWidget} + {"type": "calendar", "name": Translation.tr("Calendar"), "icon": "calendar_month", "widget": calendarWidget}, + {"type": "todo", "name": Translation.tr("To Do"), "icon": "done_outline", "widget": todoWidget} ] Behavior on implicitHeight { diff --git a/.config/quickshell/ii/modules/sidebarRight/notifications/NotificationList.qml b/.config/quickshell/ii/modules/sidebarRight/notifications/NotificationList.qml index 461a671b..c334b052 100644 --- a/.config/quickshell/ii/modules/sidebarRight/notifications/NotificationList.qml +++ b/.config/quickshell/ii/modules/sidebarRight/notifications/NotificationList.qml @@ -85,7 +85,7 @@ Item { anchors.verticalCenter: parent.verticalCenter anchors.leftMargin: 10 horizontalAlignment: Text.AlignHCenter - text: `${Notifications.list.length} notifications` + text: `${Notifications.list.length} ${Translation.tr("notifications")}` opacity: Notifications.list.length > 0 ? 1 : 0 visible: opacity > 0 diff --git a/.config/quickshell/translations/en_US.json b/.config/quickshell/translations/en_US.json index b294db6e..0e6254fe 100644 --- a/.config/quickshell/translations/en_US.json +++ b/.config/quickshell/translations/en_US.json @@ -96,8 +96,6 @@ "Run command": "Run command", "Save": "Save", "Save to Downloads": "Save to Downloads", - "Scroll to change brightness": "Scroll to change brightness", - "Scroll to change volume": "Scroll to change volume", "Search": "Search", "Search the web": "Search the web", "Search, calculate or run": "Search, calculate or run", @@ -106,8 +104,6 @@ "Set API key": "Set API key", "Set temperature (randomness) of the model. Values range between 0 to 2 for Gemini, 0 to 1 for other models. Default is 0.5.": "Set temperature (randomness) of the model. Values range between 0 to 2 for Gemini, 0 to 1 for other models. Default is 0.5.", "Set the current API provider": "Set the current API provider", - "Shell configuration created": "Shell configuration created", - "Shell configuration failed to load": "Shell configuration failed to load", "Shutdown": "Shutdown", "Silent": "Silent", "Sleep": "Sleep", @@ -169,5 +165,12 @@ "Experimental | Online | Google's model\nCan do a little more but doesn't search quickly": "Experimental | Online | Google's model\nCan do a little more but doesn't search quickly", "Message the model... \"{0}\" for commands": "Message the model... \"{0}\" for commands", "To set an API key, pass it with the command\n\nTo view the key, pass \"get\" with the command
\n\n### For {0}:\n\n**Link**: {1}\n\n{2}": "To set an API key, pass it with the command\n\nTo view the key, pass \"get\" with the command
\n\n### For {0}:\n\n**Link**: {1}\n\n{2}", - "Settings": "Settings" + "Settings": "Settings", + "Save chat": "Save chat", + "Load chat": "Load chat", + "or": "or", + "Set the system prompt for the model.": "Set the system prompt for the model.", + "To Do": "To Do", + "Calendar": "Calendar", + "notifications": "notifications" } \ No newline at end of file diff --git a/.config/quickshell/translations/zh_CN.json b/.config/quickshell/translations/zh_CN.json index 1c2f1b7b..878dc566 100644 --- a/.config/quickshell/translations/zh_CN.json +++ b/.config/quickshell/translations/zh_CN.json @@ -96,8 +96,6 @@ "Run command": "运行命令", "Save": "保存", "Save to Downloads": "保存到下载文件夹", - "Scroll to change brightness": "滚动调节亮度", - "Scroll to change volume": "滚动调节音量", "Search": "搜索", "Search the web": "搜索网络", "Search, calculate or run": "搜索、计算或运行", @@ -106,8 +104,6 @@ "Set API key": "设置 API 密钥", "Set temperature (randomness) of the model. Values range between 0 to 2 for Gemini, 0 to 1 for other models. Default is 0.5.": "设置模型的温度(随机性)。Gemini 模型范围为 0 到 2,其他模型为 0 到 1。默认值为 0.5。", "Set the current API provider": "设置当前 API 提供商", - "Shell configuration created": "Shell 配置已创建", - "Shell configuration failed to load": "Shell 配置加载失败", "Shutdown": "关机", "Silent": "静音", "Sleep": "睡眠", @@ -169,5 +165,12 @@ "Online via {0} | {1}'s model": "通过 {0} 在线 | {1} 的模型", "That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number": "没有找到结果。提示:\n- 检查您的标签和 NSFW 设置\n- 如果没有想到标签,请输入页码", "Online | Google's model\nGives up-to-date information with search.": "在线 | Google 模型\n通过搜索提供最新信息。", - "Settings": "设置" + "Settings": "设置", + "Save chat": "保存对话", + "Load chat": "加载对话", + "or": "或", + "Set the system prompt for the model.": "为模型设置系统提示。", + "To Do": "待办", + "Calendar": "日历", + "notifications": "条通知" } \ No newline at end of file