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