diff --git a/Assets/Translations/de.json b/Assets/Translations/de.json index 46e11bbb..1ca40177 100644 --- a/Assets/Translations/de.json +++ b/Assets/Translations/de.json @@ -68,6 +68,17 @@ } }, "reset-scaling": "Skalierung zurücksetzen" + }, + "language": { + "section": { + "label": "Sprache", + "description": "Wählen Sie Ihre bevorzugte Sprache für die Anwendung." + }, + "select": { + "label": "Anwendungssprache", + "description": "Wählen Sie die in der Anwendungsoberfläche verwendete Sprache.", + "auto-detect": "Automatisch" + } } }, "audio": { diff --git a/Assets/Translations/en.json b/Assets/Translations/en.json index 6a4fdadc..50abdeca 100644 --- a/Assets/Translations/en.json +++ b/Assets/Translations/en.json @@ -68,6 +68,17 @@ "description": "Increase or decrease the size of the monospaced text." } } + }, + "language": { + "section": { + "label": "Language", + "description": "Choose your preferred language for the application." + }, + "select": { + "label": "Application Language", + "description": "Select the language used in the application's interface.", + "auto-detect": "Automatic" + } } }, "audio": { @@ -1213,7 +1224,7 @@ "scan-again": "Scan again" } }, - + "tooltips": { "refresh": "Refresh", "close": "Close", diff --git a/Assets/Translations/es.json b/Assets/Translations/es.json index db10d662..eb79d97e 100644 --- a/Assets/Translations/es.json +++ b/Assets/Translations/es.json @@ -68,6 +68,17 @@ } }, "reset-scaling": "Restablecer la escala" + }, + "language": { + "section": { + "label": "Idioma", + "description": "Elige tu idioma preferido para la aplicación." + }, + "select": { + "label": "Idioma de la aplicación", + "description": "Selecciona el idioma utilizado en la interfaz de la aplicación.", + "auto-detect": "Automático" + } } }, "audio": { diff --git a/Assets/Translations/fr.json b/Assets/Translations/fr.json index d99e42a5..1ef4161c 100644 --- a/Assets/Translations/fr.json +++ b/Assets/Translations/fr.json @@ -68,6 +68,17 @@ } }, "reset-scaling": "Réinitialiser l'échelle" + }, + "language": { + "section": { + "label": "Langue", + "description": "Choisissez votre langue préférée pour l'application." + }, + "select": { + "label": "Langue de l'application", + "description": "Sélectionnez la langue utilisée dans l'interface de l'application.", + "auto-detect": "Automatique" + } } }, "audio": { diff --git a/Assets/Translations/pt.json b/Assets/Translations/pt.json index 40ac59aa..2acdc07b 100644 --- a/Assets/Translations/pt.json +++ b/Assets/Translations/pt.json @@ -68,6 +68,17 @@ } }, "reset-scaling": "Redefinir escala" + }, + "language": { + "section": { + "label": "Idioma", + "description": "Escolha o seu idioma preferido para a aplicação." + }, + "select": { + "label": "Idioma da aplicação", + "description": "Selecione o idioma usado na interface da aplicação.", + "auto-detect": "Automático" + } } }, "audio": { diff --git a/Assets/Translations/zh-CN.json b/Assets/Translations/zh-CN.json index 608d7097..438191cf 100644 --- a/Assets/Translations/zh-CN.json +++ b/Assets/Translations/zh-CN.json @@ -68,6 +68,17 @@ "description": "增大或减小等宽文本的尺寸" } } + }, + "language": { + "section": { + "label": "语言", + "description": "选择您偏好的应用程序语言。" + }, + "select": { + "label": "应用程序语言", + "description": "选择应用程序界面中使用的语言。", + "auto-detect": "自动检测" + } } }, "audio": { diff --git a/Assets/settings-default.json b/Assets/settings-default.json index a62c77eb..57033237 100644 --- a/Assets/settings-default.json +++ b/Assets/settings-default.json @@ -66,7 +66,8 @@ "animationSpeed": 1, "animationDisabled": false, "compactLockScreen": false, - "lockOnSuspend": true + "lockOnSuspend": true, + "language": "" }, "location": { "name": "Tokyo", @@ -256,4 +257,4 @@ "battery": { "chargingMode": 0 } -} \ No newline at end of file +} diff --git a/Commons/I18n.qml b/Commons/I18n.qml index 6f49f097..4ec5e86f 100644 --- a/Commons/I18n.qml +++ b/Commons/I18n.qml @@ -7,10 +7,11 @@ import qs.Commons Singleton { id: root - property string debugForceLanguage: "" + property bool isLoaded: false property string langCode: "" + property string systemDetectedLangCode: "" property var availableLanguages: [] property var translations: ({}) property var fallbackTranslations: ({}) @@ -158,41 +159,40 @@ Singleton { return } - if (Settings.isDebug && debugForceLanguage !== "") { - Logger.d("I18n", `Debug mode: forcing language to "${debugForceLanguage}"`) - if (availableLanguages.includes(debugForceLanguage)) { - setLanguage(debugForceLanguage) - return - } else { - Logger.w("I18n", `Debug language "${debugForceLanguage}" not available in [${availableLanguages.join(', ')}]`) - } - } + var detectedLang = "" - // Detect user's favorite locale - languages + // First, determine the system's preferred language for (var i = 0; i < Qt.locale().uiLanguages.length; i++) { const fullUserLang = Qt.locale().uiLanguages[i] - // Try full code match (such as zh CN, en US) if (availableLanguages.includes(fullUserLang)) { - Logger.d("I18n", `Exact match found: "${fullUserLang}"`) - setLanguage(fullUserLang) - return + detectedLang = fullUserLang + break } - // If full code match fails, try short code matching (such as zh, en) const shortUserLang = fullUserLang.substring(0, 2) if (availableLanguages.includes(shortUserLang)) { - Logger.d("I18n", `Short code match found: "${shortUserLang}" from "${fullUserLang}"`) - setLanguage(shortUserLang) - return + detectedLang = shortUserLang + break } - - Logger.d("I18n", `No match for system language: "${fullUserLang}"`) } - // Fallback to first available language (preferably "en" if available) - const fallbackLang = availableLanguages.includes("en") ? "en" : availableLanguages[0] - setLanguage(fallbackLang) + // If no system language is found among available languages, fallback + if (detectedLang === "") { + detectedLang = availableLanguages.includes("en") ? "en" : availableLanguages[0] + } + + root.systemDetectedLangCode = detectedLang + Logger.d("I18n", `System detected language: "${root.systemDetectedLangCode}"`) + + // Now, apply the language: user-defined, then system-detected + if (Settings.data.general.language !== "" && availableLanguages.includes(Settings.data.general.language)) { + Logger.d("I18n", `User-defined language found: "${Settings.data.general.language}"`) + setLanguage(Settings.data.general.language) + } else { + Logger.d("I18n", `No user-defined language, using system detected: "${root.systemDetectedLangCode}"`) + setLanguage(root.systemDetectedLangCode) + } } // ------------------------------------------- diff --git a/Commons/Settings.qml b/Commons/Settings.qml index 44f30145..a1c239e5 100644 --- a/Commons/Settings.qml +++ b/Commons/Settings.qml @@ -192,6 +192,7 @@ Singleton { property bool animationDisabled: false property bool compactLockScreen: false property bool lockOnSuspend: true + property string language: "" } // location diff --git a/Modules/Settings/Tabs/GeneralTab.qml b/Modules/Settings/Tabs/GeneralTab.qml index 0a63a7fd..40b5276d 100644 --- a/Modules/Settings/Tabs/GeneralTab.qml +++ b/Modules/Settings/Tabs/GeneralTab.qml @@ -190,7 +190,42 @@ ColumnLayout { Layout.bottomMargin: Style.marginXL } - // Lock Screen + // Language selection + ColumnLayout { + spacing: Style.marginL + Layout.fillWidth: true + + NHeader { + label: I18n.tr("settings.general.language.section.label") + description: I18n.tr("settings.general.language.section.description") + } + + NComboBox { + Layout.fillWidth: true + label: I18n.tr("settings.general.language.select.label") + description: I18n.tr("settings.general.language.select.description") + model: [ + { "key": "", "name": I18n.tr("settings.general.language.select.auto-detect") + " (" + I18n.systemDetectedLangCode + ")" } + ].concat(I18n.availableLanguages.map(function(langCode) { + return { "key": langCode, "name": langCode } + })) + currentKey: Settings.data.general.language + onSelected: key => { + Settings.data.general.language = key + if (key === "") { + I18n.detectLanguage() // Re-detect system language if "Automatic" is selected + } else { + I18n.setLanguage(key) // Set specific language + } + } + } + } + + NDivider { + Layout.fillWidth: true + Layout.topMargin: Style.marginXL + Layout.bottomMargin: Style.marginXL + } ColumnLayout { spacing: Style.marginL Layout.fillWidth: true