diff --git a/Assets/Translations/de.json b/Assets/Translations/de.json index fdc7180d..b3d0b367 100644 --- a/Assets/Translations/de.json +++ b/Assets/Translations/de.json @@ -1064,6 +1064,9 @@ } }, "session-menu": { + "title": "Sitzungsmenü", + "click-again": "Erneut klicken für sofortige Ausführung", + "action-in-seconds": "{action} in {seconds} Sekunden...", "lock": "Sperren", "suspend": "Ruhezustand", "reboot": "Neu starten", @@ -1111,5 +1114,69 @@ "shut-down": "Herunterfahren", "restart": "Neu starten", "suspend": "Ruhezustand" + }, + "toast": { + "night-light": { + "enabled": "Aktiviert", + "disabled": "Deaktiviert", + "not-installed": "wlsunset nicht installiert", + "forced": "Aktivierung erzwungen", + "normal": "Normaler Modus" + }, + "keep-awake": { + "enabled": "Aktiviert", + "disabled": "Deaktiviert" + }, + "matugen": { + "enabled": "Aktiviert", + "disabled": "Deaktiviert", + "not-installed": "Nicht installiert" + }, + "recording": { + "stopping": "Aufnahme wird gestoppt…", + "started": "Aufnahme gestartet", + "saved": "Aufnahme gespeichert", + "failed-start": "Aufnahme konnte nicht gestartet werden", + "failed-gpu": "gpu-screen-recorder unerwartet beendet.", + "failed-general": "Der Rekorder wurde mit einem Fehler beendet.", + "no-portals": "Desktop-Portale laufen nicht", + "no-portals-desc": "Starten Sie xdg-desktop-portal und ein Compositor-Portal (wlr/hyprland/gnome/kde)." + }, + "clipboard": { + "unavailable": "Zwischenablage-Verlauf nicht verfügbar", + "unavailable-desc": "Die 'cliphist' Anwendung ist nicht installiert. Bitte installieren Sie sie, um Zwischenablage-Verlauf-Features zu nutzen." + }, + "ipc": { + "powerpanel-deprecated": "PowerPanel wurde in SessionMenu umbenannt, dieser IPC-Aufruf wird bald veraltet sein. Bitte verwenden Sie stattdessen \"ipc call sessionMenu toggle\".", + "sidepanel-deprecated": "SidePanel wurde in ControlCenter umbenannt, dieser IPC-Aufruf wird bald veraltet sein. Bitte verwenden Sie stattdessen \"ipc call controlCenter toggle\"." + }, + "wifi": { + "enabled": "Aktiviert", + "disabled": "Deaktiviert", + "connected": "Verbunden mit '{ssid}'", + "disconnected": "Getrennt von '{ssid}'" + }, + "bluetooth": { + "enabled": "Aktiviert", + "disabled": "Deaktiviert" + }, + "do-not-disturb": { + "enabled": "'Nicht stören' aktiviert", + "disabled": "'Nicht stören' deaktiviert", + "enabled-desc": "Sie finden diese Benachrichtigungen in Ihrem Verlauf.", + "disabled-desc": "Alle Benachrichtigungen werden angezeigt." + }, + "power-profile": { + "changed": "Energieprofil geändert", + "profile-name": "\"{profile}\"" + }, + "audio": { + "muted": "Stummgeschaltet", + "unmuted": "Lautgeschaltet" + }, + "battery": { + "low": "Niedriger Batteriestand", + "low-desc": "Batterie ist bei {percent}%. Bitte schließen Sie das Ladegerät an." + } } } \ No newline at end of file diff --git a/Assets/Translations/en.json b/Assets/Translations/en.json index dcc0c456..2430106f 100644 --- a/Assets/Translations/en.json +++ b/Assets/Translations/en.json @@ -663,6 +663,10 @@ "tooltip": { "placeholder": "Placeholder" }, + "file-picker": { + "select-folder": "Select Folder", + "select-file": "Select File" + }, "datetime-tokens": { "common": { "12hour-time-minutes": "12-hour time with minutes", @@ -886,7 +890,25 @@ "description": "Apply selected wallpaper to all monitors at once." }, "search": "Search:" - } + }, + "transitions": { + "none": "None", + "random": "Random", + "fade": "Fade", + "disc": "Disc", + "stripes": "Stripes", + "wipe": "Wipe" + }, + "fill-modes": { + "center": "Center", + "crop": "Crop (Fill)", + "fit": "Fit (Contain)", + "stretch": "Stretch" + }, + "no-match": "No match found.", + "no-wallpaper": "No wallpaper found.", + "try-different-search": "Try a different search query.", + "configure-directory": "Configure your wallpaper directory with images." }, "bluetooth": { "panel": { @@ -1004,6 +1026,30 @@ "test": "Test" }, "options": { + "bar": { + "position": { + "top": "Top", + "bottom": "Bottom", + "left": "Left", + "right": "Right" + }, + "density": { + "compact": "Compact", + "default": "Default", + "comfortable": "Comfortable" + } + }, + "launcher": { + "position": { + "center": "Center (default)", + "top_left": "Top left", + "top_right": "Top right", + "bottom_left": "Bottom left", + "bottom_right": "Bottom right", + "bottom_center": "Bottom center", + "top_center": "Top center" + } + }, "display-mode": { "on-hover": "On hover", "always-show": "Always show", @@ -1064,6 +1110,11 @@ } }, "session-menu": { + "title": "Session Menu", + "click-again": "Click again to execute immediately", + "action-in-seconds": "{action} in {seconds} seconds...", + "lock-subtitle": "Lock your session", + "end-subtitle": "End your session", "lock": "Lock", "suspend": "Suspend", "reboot": "Reboot", @@ -1111,5 +1162,98 @@ "shut-down": "Shut down", "restart": "Restart", "suspend": "Suspend" + }, + "toast": { + "night-light": { + "enabled": "Enabled", + "disabled": "Disabled", + "not-installed": "wlsunset not installed", + "forced": "Forced activation", + "normal": "Normal mode" + }, + "keep-awake": { + "enabled": "Enabled", + "disabled": "Disabled" + }, + "matugen": { + "enabled": "Enabled", + "disabled": "Disabled", + "not-installed": "Not installed" + }, + "recording": { + "stopping": "Stopping recording…", + "started": "Recording started", + "saved": "Recording saved", + "failed-start": "Failed to start recording", + "failed-gpu": "gpu-screen-recorder exited unexpectedly.", + "failed-general": "The recorder exited with an error.", + "no-portals": "Desktop portals not running", + "no-portals-desc": "Start xdg-desktop-portal and a compositor portal (wlr/hyprland/gnome/kde)." + }, + "clipboard": { + "unavailable": "Clipboard history unavailable", + "unavailable-desc": "The 'cliphist' application is not installed. Please install it to use clipboard history features." + }, + "ipc": { + "powerpanel-deprecated": "PowerPanel has been renamed to SessionMenu, this IPC call will be deprecated soon. Please use \"ipc call sessionMenu toggle\" instead.", + "sidepanel-deprecated": "SidePanel has been renamed to ControlCenter, this IPC call will be deprecated soon. Please use \"ipc call controlCenter toggle\" instead." + }, + "wifi": { + "enabled": "Enabled", + "disabled": "Disabled", + "connected": "Connected to '{ssid}'", + "disconnected": "Disconnected from '{ssid}'" + }, + "bluetooth": { + "enabled": "Enabled", + "disabled": "Disabled" + }, + "do-not-disturb": { + "enabled": "'Do not disturb' enabled", + "disabled": "'Do not disturb' disabled", + "enabled-desc": "You'll find these notifications in your history.", + "disabled-desc": "Showing all notifications." + }, + "power-profile": { + "changed": "Power profile changed", + "profile-name": "\"{profile}\"" + }, + "audio": { + "muted": "Muted", + "unmuted": "Unmuted" + }, + "battery": { + "low": "Low Battery", + "low-desc": "Battery is at {percent}%. Please connect the charger." + } + }, + "weather": { + "clear-sky": "Clear sky", + "mainly-clear": "Mainly clear", + "partly-cloudy": "Partly cloudy", + "overcast": "Overcast", + "fog": "Fog", + "drizzle": "Drizzle", + "snow": "Snow", + "rain-showers": "Rain showers", + "thunderstorm": "Thunderstorm", + "unknown": "Unknown" + }, + + "authentication": { + "failed": "Authentication failed", + "error": "Authentication error" + }, + "general": { + "no-results": "No results", + "no-summary": "No summary", + "unknown": "Unknown" + }, + "battery": { + "no-battery-detected": "No battery detected.", + "charging-rate": "Charging rate: {rate} W.", + "discharging-rate": "Discharging rate: {rate} W.", + "charging": "Charging.", + "discharging": "Discharging." } } diff --git a/Bin/check-i18n.sh b/Bin/check-i18n.sh deleted file mode 100644 index 77372b79..00000000 --- a/Bin/check-i18n.sh +++ /dev/null @@ -1,65 +0,0 @@ -#!/bin/bash - -# Comprehensive i18n checker for Noctalia Shell -# Finds hardcoded strings that should be internationalized - -check_file() { - local file="$1" - - # Check for hardcoded strings in common properties - # Includes: label, text, title, description, tooltip, tooltipText, placeholder, placeholderText - local property_issues=$(grep -n -E '(label|text|title|description|tooltip|tooltipText|placeholder|placeholderText):\s*"[^"]{3,}"' "$file" | grep -v 'I18n.tr') - - # Check for hardcoded strings in dialog titles and button texts - local dialog_issues=$(grep -n -E '(dialog\.|Dialog\.|title:|buttonText:)\s*"[^"]{3,}"' "$file" | grep -v 'I18n.tr') - - # Check for hardcoded strings in model name properties (for combo boxes) - local model_issues=$(grep -n -E 'name:\s*"[^"]{3,}"' "$file" | grep -v 'I18n.tr') - - # Check for hardcoded strings in common UI text patterns - local ui_issues=$(grep -n -E '"[^"]*\b(click|open|close|enable|disable|show|hide|settings|cancel|apply|ok|save|load|start|stop|play|pause|next|previous|volume|brightness|wifi|bluetooth|notification|wallpaper|profile|power|session|menu|panel|dialog|button|toggle|slider|checkbox|radio|combo|input|search|filter|sort|refresh|update|delete|remove|add|create|edit|modify|copy|paste|cut|undo|redo|help|about|info|warning|error|success|failed|loading|connecting|connected|disconnected|scanning|pairing|recording|playing|paused|stopped|muted|unmuted|enabled|disabled|on|off|yes|no|true|false)\b[^"]*"' "$file" | grep -v 'I18n.tr' | grep -v '//' | grep -v '/*') - - # Combine all issues - local all_issues="$property_issues" - if [[ -n "$dialog_issues" ]]; then - all_issues="$all_issues"$'\n'"$dialog_issues" - fi - if [[ -n "$model_issues" ]]; then - all_issues="$all_issues"$'\n'"$model_issues" - fi - if [[ -n "$ui_issues" ]]; then - all_issues="$all_issues"$'\n'"$ui_issues" - fi - - # Remove empty lines and duplicates - all_issues=$(echo "$all_issues" | grep -v '^$' | sort -u) - - if [[ -n "$all_issues" ]]; then - echo "$file" - echo "$all_issues" | while IFS= read -r line; do - echo " $line" - done - echo - fi -} - -echo "Comprehensive i18n Checker" -echo "=========================" -echo "Scanning QML files for hardcoded strings..." -echo - -found_issues=false - -while IFS= read -r -d '' file; do - if check_file "$file" | grep -q .; then - check_file "$file" - found_issues=true - fi -done < <(find . -name "*.qml" -not -path "./Assets/*" -print0) - -if [[ "$found_issues" == false ]]; then - echo "No hardcoded strings found! All strings appear to be internationalized." -else - echo "Note: Review each match manually - some may be false positives" - echo "(property names, IDs, technical values, comments shouldn't be translated)" -fi diff --git a/Modules/Bar/Widgets/Battery.qml b/Modules/Bar/Widgets/Battery.qml index 1d2d3f7e..336ee925 100644 --- a/Modules/Bar/Widgets/Battery.qml +++ b/Modules/Bar/Widgets/Battery.qml @@ -54,7 +54,9 @@ Item { // Only notify once we are a below threshold if (!charging && !root.hasNotifiedLowBattery && percent <= warningThreshold) { root.hasNotifiedLowBattery = true - ToastService.showWarning("Low Battery", `Battery is at ${Math.round(percent)}%. Please connect the charger.`) + ToastService.showWarning(I18n.tr("toast.battery.low"), I18n.tr("toast.battery.low-desc", { + "percent": Math.round(percent) + })) } else if (root.hasNotifiedLowBattery && (charging || percent > warningThreshold + 5)) { // Reset when charging starts or when battery recovers 5% above threshold root.hasNotifiedLowBattery = false diff --git a/Modules/Notification/Notification.qml b/Modules/Notification/Notification.qml index 4cf82f73..fce49571 100644 --- a/Modules/Notification/Notification.qml +++ b/Modules/Notification/Notification.qml @@ -293,7 +293,7 @@ Variants { } NText { - text: model.summary || "No summary" + text: model.summary || I18n.tr("general.no-summary") font.pointSize: Style.fontSizeL * scaling font.weight: Style.fontWeightMedium color: Color.mOnSurface diff --git a/Modules/Notification/NotificationHistoryPanel.qml b/Modules/Notification/NotificationHistoryPanel.qml index cdc1bcfa..f5bd43d7 100644 --- a/Modules/Notification/NotificationHistoryPanel.qml +++ b/Modules/Notification/NotificationHistoryPanel.qml @@ -216,7 +216,7 @@ NPanel { // Summary NText { - text: model.summary || "No summary" + text: model.summary || I18n.tr("general.no-summary") font.pointSize: Style.fontSizeM * scaling font.weight: Font.Medium color: Color.mOnSurface diff --git a/Modules/SessionMenu/SessionMenu.qml b/Modules/SessionMenu/SessionMenu.qml index a24f0343..0a9c2271 100644 --- a/Modules/SessionMenu/SessionMenu.qml +++ b/Modules/SessionMenu/SessionMenu.qml @@ -31,7 +31,7 @@ NPanel { "action": "lock", "icon": "lock", "title": I18n.tr("session-menu.lock"), - "subtitle": "Lock your session" + "subtitle": I18n.tr("session-menu.lock-subtitle") }, { "action": "suspend", "icon": "suspend", @@ -46,7 +46,7 @@ NPanel { "action": "logout", "icon": "logout", "title": I18n.tr("session-menu.logout"), - "subtitle": "End your session" + "subtitle": I18n.tr("session-menu.end-subtitle") }, { "action": "shutdown", "icon": "shutdown", @@ -263,7 +263,10 @@ NPanel { Layout.preferredHeight: Style.baseWidgetSize * 0.8 * scaling NText { - text: timerActive ? `${pendingAction.charAt(0).toUpperCase() + pendingAction.slice(1)} in ${Math.ceil(timeRemaining / 1000)} seconds...` : "Session Menu" + text: timerActive ? I18n.tr("session-menu.action-in-seconds", { + "action": pendingAction.charAt(0).toUpperCase() + pendingAction.slice(1), + "seconds": Math.ceil(timeRemaining / 1000) + }) : I18n.tr("session-menu.title") font.weight: Style.fontWeightBold font.pointSize: Style.fontSizeL * scaling color: timerActive ? Color.mPrimary : Color.mOnSurface @@ -419,7 +422,7 @@ NPanel { NText { text: { if (buttonRoot.pending) { - return "Click again to execute immediately" + return I18n.tr("session-menu.click-again") } return buttonRoot.subtitle } diff --git a/Modules/Settings/Tabs/BarTab.qml b/Modules/Settings/Tabs/BarTab.qml index 17407631..88715105 100644 --- a/Modules/Settings/Tabs/BarTab.qml +++ b/Modules/Settings/Tabs/BarTab.qml @@ -49,24 +49,19 @@ ColumnLayout { Layout.fillWidth: true label: I18n.tr("settings.bar.appearance.position.label") description: I18n.tr("settings.bar.appearance.position.description") - model: ListModel { - ListElement { - key: "top" - name: "Top" - } - ListElement { - key: "bottom" - name: "Bottom" - } - ListElement { - key: "left" - name: "Left" - } - ListElement { - key: "right" - name: "Right" - } - } + model: [{ + "key": "top", + "name": I18n.tr("options.bar.position.top") + }, { + "key": "bottom", + "name": I18n.tr("options.bar.position.bottom") + }, { + "key": "left", + "name": I18n.tr("options.bar.position.left") + }, { + "key": "right", + "name": I18n.tr("options.bar.position.right") + }] currentKey: Settings.data.bar.position onSelected: key => Settings.data.bar.position = key } @@ -75,20 +70,16 @@ ColumnLayout { Layout.fillWidth: true label: I18n.tr("settings.bar.appearance.density.label") description: I18n.tr("settings.bar.appearance.density.description") - model: ListModel { - ListElement { - key: "compact" - name: "Compact" - } - ListElement { - key: "default" - name: "Default" - } - ListElement { - key: "comfortable" - name: "Comfortable" - } - } + model: [{ + "key": "compact", + "name": I18n.tr("options.bar.density.compact") + }, { + "key": "default", + "name": I18n.tr("options.bar.density.default") + }, { + "key": "comfortable", + "name": I18n.tr("options.bar.density.comfortable") + }] currentKey: Settings.data.bar.density onSelected: key => Settings.data.bar.density = key } diff --git a/Modules/Settings/Tabs/ColorSchemeTab.qml b/Modules/Settings/Tabs/ColorSchemeTab.qml index d7ef97ad..303b7098 100644 --- a/Modules/Settings/Tabs/ColorSchemeTab.qml +++ b/Modules/Settings/Tabs/ColorSchemeTab.qml @@ -65,10 +65,10 @@ ColumnLayout { // Matugen exists, enable it Settings.data.colorSchemes.useWallpaperColors = true MatugenService.generateFromWallpaper() - ToastService.showNotice("Matugen", "Enabled") + ToastService.showNotice(I18n.tr("settings.color-scheme.color-source.enable-matugen.label"), I18n.tr("toast.matugen.enabled")) } else { // Matugen not found - ToastService.showWarning("Matugen", "Not installed") + ToastService.showWarning(I18n.tr("settings.color-scheme.color-source.enable-matugen.label"), I18n.tr("toast.matugen.not-installed")) } } @@ -130,7 +130,7 @@ ColumnLayout { matugenCheck.running = true } else { Settings.data.colorSchemes.useWallpaperColors = false - ToastService.showNotice("Matugen", "Disabled") + ToastService.showNotice(I18n.tr("settings.color-scheme.color-source.enable-matugen.label"), I18n.tr("toast.matugen.disabled")) if (Settings.data.colorSchemes.predefinedScheme) { diff --git a/Modules/Settings/Tabs/DisplayTab.qml b/Modules/Settings/Tabs/DisplayTab.qml index 2146f825..e4431285 100644 --- a/Modules/Settings/Tabs/DisplayTab.qml +++ b/Modules/Settings/Tabs/DisplayTab.qml @@ -38,10 +38,10 @@ ColumnLayout { if (exitCode === 0) { Settings.data.nightLight.enabled = true NightLightService.apply() - ToastService.showNotice("Night light", "Enabled") + ToastService.showNotice(I18n.tr("settings.display.night-light.section.label"), I18n.tr("toast.night-light.enabled")) } else { Settings.data.nightLight.enabled = false - ToastService.showWarning("Night light", "wlsunset not installed") + ToastService.showWarning(I18n.tr("settings.display.night-light.section.label"), I18n.tr("toast.night-light.not-installed")) } } @@ -234,7 +234,7 @@ ColumnLayout { Settings.data.nightLight.enabled = false Settings.data.nightLight.forced = false NightLightService.apply() - ToastService.showNotice("Night light", "Disabled") + ToastService.showNotice(I18n.tr("settings.display.night-light.section.label"), I18n.tr("toast.night-light.disabled")) } } } diff --git a/Modules/Settings/Tabs/LauncherTab.qml b/Modules/Settings/Tabs/LauncherTab.qml index fc456dee..1a77f880 100644 --- a/Modules/Settings/Tabs/LauncherTab.qml +++ b/Modules/Settings/Tabs/LauncherTab.qml @@ -19,36 +19,28 @@ ColumnLayout { label: I18n.tr("settings.launcher.settings.position.label") description: I18n.tr("settings.launcher.settings.position.description") Layout.fillWidth: true - model: ListModel { - ListElement { - key: "center" - name: "Center (default)" - } - ListElement { - key: "top_left" - name: "Top left" - } - ListElement { - key: "top_right" - name: "Top right" - } - ListElement { - key: "bottom_left" - name: "Bottom left" - } - ListElement { - key: "bottom_right" - name: "Bottom right" - } - ListElement { - key: "bottom_center" - name: "Bottom center" - } - ListElement { - key: "top_center" - name: "Top center" - } - } + model: [{ + "key": "center", + "name": I18n.tr("options.launcher.position.center") + }, { + "key": "top_left", + "name": I18n.tr("options.launcher.position.top_left") + }, { + "key": "top_right", + "name": I18n.tr("options.launcher.position.top_right") + }, { + "key": "bottom_left", + "name": I18n.tr("options.launcher.position.bottom_left") + }, { + "key": "bottom_right", + "name": I18n.tr("options.launcher.position.bottom_right") + }, { + "key": "bottom_center", + "name": I18n.tr("options.launcher.position.bottom_center") + }, { + "key": "top_center", + "name": I18n.tr("options.launcher.position.top_center") + }] currentKey: Settings.data.appLauncher.position onSelected: function (key) { Settings.data.appLauncher.position = key diff --git a/Modules/Settings/Tabs/NotificationsTab.qml b/Modules/Settings/Tabs/NotificationsTab.qml index 85a2375e..64a5805c 100644 --- a/Modules/Settings/Tabs/NotificationsTab.qml +++ b/Modules/Settings/Tabs/NotificationsTab.qml @@ -49,32 +49,25 @@ ColumnLayout { NComboBox { label: I18n.tr("settings.notifications.settings.location.label") description: I18n.tr("settings.notifications.settings.location.description") - model: ListModel { - ListElement { - key: "top" - name: "Top" - } - ListElement { - key: "top_left" - name: "Top left" - } - ListElement { - key: "top_right" - name: "Top right" - } - ListElement { - key: "bottom" - name: "Bottom" - } - ListElement { - key: "bottom_left" - name: "Bottom left" - } - ListElement { - key: "bottom_right" - name: "Bottom right" - } - } + model: [{ + "key": "top", + "name": I18n.tr("options.launcher.position.top_center") + }, { + "key": "top_left", + "name": I18n.tr("options.launcher.position.top_left") + }, { + "key": "top_right", + "name": I18n.tr("options.launcher.position.top_right") + }, { + "key": "bottom", + "name": I18n.tr("options.launcher.position.bottom_center") + }, { + "key": "bottom_left", + "name": I18n.tr("options.launcher.position.bottom_left") + }, { + "key": "bottom_right", + "name": I18n.tr("options.launcher.position.bottom_right") + }] currentKey: Settings.data.notifications.location || "top_right" onSelected: key => Settings.data.notifications.location = key } diff --git a/Modules/Settings/Tabs/ScreenRecorderTab.qml b/Modules/Settings/Tabs/ScreenRecorderTab.qml index d3f77e67..4a42eda4 100644 --- a/Modules/Settings/Tabs/ScreenRecorderTab.qml +++ b/Modules/Settings/Tabs/ScreenRecorderTab.qml @@ -76,36 +76,28 @@ ColumnLayout { NComboBox { label: I18n.tr("settings.screen-recorder.video.frame-rate.label") description: I18n.tr("settings.screen-recorder.video.frame-rate.description") - model: ListModel { - ListElement { - key: "30" - name: "30 FPS" - } - ListElement { - key: "60" - name: "60 FPS" - } - ListElement { - key: "100" - name: "100 FPS" - } - ListElement { - key: "120" - name: "120 FPS" - } - ListElement { - key: "144" - name: "144 FPS" - } - ListElement { - key: "165" - name: "165 FPS" - } - ListElement { - key: "240" - name: "240 FPS" - } - } + model: [{ + "key": "30", + "name": I18n.tr("options.frame-rates.30-fps") + }, { + "key": "60", + "name": I18n.tr("options.frame-rates.60-fps") + }, { + "key": "100", + "name": I18n.tr("options.frame-rates.100-fps") + }, { + "key": "120", + "name": I18n.tr("options.frame-rates.120-fps") + }, { + "key": "144", + "name": I18n.tr("options.frame-rates.144-fps") + }, { + "key": "165", + "name": I18n.tr("options.frame-rates.165-fps") + }, { + "key": "240", + "name": I18n.tr("options.frame-rates.240-fps") + }] currentKey: Settings.data.screenRecorder.frameRate onSelected: key => Settings.data.screenRecorder.frameRate = key } @@ -114,24 +106,19 @@ ColumnLayout { NComboBox { label: I18n.tr("settings.screen-recorder.video.video-quality.label") description: I18n.tr("settings.screen-recorder.video.video-quality.description") - model: ListModel { - ListElement { - key: "medium" - name: "Medium" - } - ListElement { - key: "high" - name: "High" - } - ListElement { - key: "very_high" - name: "Very high" - } - ListElement { - key: "ultra" - name: "Ultra" - } - } + model: [{ + "key": "medium", + "name": I18n.tr("options.screen-recording.quality.medium") + }, { + "key": "high", + "name": I18n.tr("options.screen-recording.quality.high") + }, { + "key": "very_high", + "name": I18n.tr("options.screen-recording.quality.very-high") + }, { + "key": "ultra", + "name": I18n.tr("options.screen-recording.quality.ultra") + }] currentKey: Settings.data.screenRecorder.quality onSelected: key => Settings.data.screenRecorder.quality = key } @@ -140,28 +127,22 @@ ColumnLayout { NComboBox { label: I18n.tr("settings.screen-recorder.video.video-codec.label") description: I18n.tr("settings.screen-recorder.video.video-codec.description") - model: ListModel { - ListElement { - key: "h264" - name: "H264" - } - ListElement { - key: "hevc" - name: "HEVC" - } - ListElement { - key: "av1" - name: "AV1" - } - ListElement { - key: "vp8" - name: "VP8" - } - ListElement { - key: "vp9" - name: "VP9" - } - } + model: [{ + "key": "h264", + "name": I18n.tr("options.screen-recording.codecs.h264") + }, { + "key": "hevc", + "name": I18n.tr("options.screen-recording.codecs.hevc") + }, { + "key": "av1", + "name": I18n.tr("options.screen-recording.codecs.av1") + }, { + "key": "vp8", + "name": I18n.tr("options.screen-recording.codecs.vp8") + }, { + "key": "vp9", + "name": I18n.tr("options.screen-recording.codecs.vp9") + }] currentKey: Settings.data.screenRecorder.videoCodec onSelected: key => Settings.data.screenRecorder.videoCodec = key } @@ -170,16 +151,13 @@ ColumnLayout { NComboBox { label: I18n.tr("settings.screen-recorder.video.color-range.label") description: I18n.tr("settings.screen-recorder.video.color-range.description") - model: ListModel { - ListElement { - key: "limited" - name: "Limited" - } - ListElement { - key: "full" - name: "Full" - } - } + model: [{ + "key": "limited", + "name": I18n.tr("options.screen-recording.color-range.limited") + }, { + "key": "full", + "name": I18n.tr("options.screen-recording.color-range.full") + }] currentKey: Settings.data.screenRecorder.colorRange onSelected: key => Settings.data.screenRecorder.colorRange = key } @@ -205,20 +183,16 @@ ColumnLayout { NComboBox { label: I18n.tr("settings.screen-recorder.audio.audio-source.label") description: I18n.tr("settings.screen-recorder.audio.audio-source.description") - model: ListModel { - ListElement { - key: "default_output" - name: "System output" - } - ListElement { - key: "default_input" - name: "Microphone input" - } - ListElement { - key: "both" - name: "System output + microphone input" - } - } + model: [{ + "key": "default_output", + "name": I18n.tr("options.screen-recording.audio-sources.system-output") + }, { + "key": "default_input", + "name": I18n.tr("options.screen-recording.audio-sources.microphone-input") + }, { + "key": "both", + "name": I18n.tr("options.screen-recording.audio-sources.both") + }] currentKey: Settings.data.screenRecorder.audioSource onSelected: key => Settings.data.screenRecorder.audioSource = key } @@ -227,16 +201,13 @@ ColumnLayout { NComboBox { label: I18n.tr("settings.screen-recorder.audio.audio-codec.label") description: I18n.tr("settings.screen-recorder.audio.audio-codec.description") - model: ListModel { - ListElement { - key: "opus" - name: "Opus" - } - ListElement { - key: "aac" - name: "AAC" - } - } + model: [{ + "key": "opus", + "name": I18n.tr("options.screen-recording.audio-codecs.opus") + }, { + "key": "aac", + "name": I18n.tr("options.screen-recording.audio-codecs.aac") + }] currentKey: Settings.data.screenRecorder.audioCodec onSelected: key => Settings.data.screenRecorder.audioCodec = key } diff --git a/Modules/Wallpaper/WallpaperPanel.qml b/Modules/Wallpaper/WallpaperPanel.qml index 95ad2f49..6266aa2b 100644 --- a/Modules/Wallpaper/WallpaperPanel.qml +++ b/Modules/Wallpaper/WallpaperPanel.qml @@ -436,13 +436,13 @@ NPanel { Layout.alignment: Qt.AlignHCenter } NText { - text: (wallpaperPanel.filterText && wallpaperPanel.filterText.length > 0) ? "No match found." : "No wallpaper found." + text: (wallpaperPanel.filterText && wallpaperPanel.filterText.length > 0) ? I18n.tr("wallpaper.no-match") : I18n.tr("wallpaper.no-wallpaper") color: Color.mOnSurface font.weight: Style.fontWeightBold Layout.alignment: Qt.AlignHCenter } NText { - text: (wallpaperPanel.filterText && wallpaperPanel.filterText.length > 0) ? "Try a different search query." : "Configure your wallpaper directory with images." + text: (wallpaperPanel.filterText && wallpaperPanel.filterText.length > 0) ? I18n.tr("wallpaper.try-different-search") : I18n.tr("wallpaper.configure-directory") color: Color.mOnSurfaceVariant wrapMode: Text.WordWrap Layout.alignment: Qt.AlignHCenter diff --git a/Services/AudioService.qml b/Services/AudioService.qml index 8438d03e..e44dd493 100644 --- a/Services/AudioService.qml +++ b/Services/AudioService.qml @@ -62,7 +62,7 @@ Singleton { function onMutedChanged() { root._muted = (sink?.audio.muted ?? true) Logger.log("AudioService", "OnMuteChanged:", root._muted) - ToastService.showNotice("Audio Output", root._muted ? "Muted" : "Unmuted") + ToastService.showNotice(I18n.tr("settings.audio.devices.output-device.label"), root._muted ? I18n.tr("toast.audio.muted") : I18n.tr("toast.audio.unmuted")) } } @@ -80,7 +80,7 @@ Singleton { function onMutedChanged() { root._inputMuted = (source?.audio.muted ?? true) Logger.log("AudioService", "OnInputMuteChanged:", root._inputMuted) - ToastService.showNotice("Microphone", root._inputMuted ? "Muted" : "Unmuted") + ToastService.showNotice(I18n.tr("settings.audio.devices.input-device.label"), root._inputMuted ? I18n.tr("toast.audio.muted") : I18n.tr("toast.audio.unmuted")) } } diff --git a/Services/BluetoothService.qml b/Services/BluetoothService.qml index e25e2639..43ac4dd8 100644 --- a/Services/BluetoothService.qml +++ b/Services/BluetoothService.qml @@ -66,9 +66,9 @@ Singleton { } lastAdapterState = adapter.enabled if (adapter.enabled) { - ToastService.showNotice("Bluetooth", "Enabled") + ToastService.showNotice(I18n.tr("bluetooth.panel.title"), I18n.tr("toast.bluetooth.enabled")) } else { - ToastService.showNotice("Bluetooth", "Disabled") + ToastService.showNotice(I18n.tr("bluetooth.panel.title"), I18n.tr("toast.bluetooth.disabled")) } } } diff --git a/Services/ClipboardService.qml b/Services/ClipboardService.qml index d48dfb17..f8804edb 100644 --- a/Services/ClipboardService.qml +++ b/Services/ClipboardService.qml @@ -70,7 +70,7 @@ Singleton { root.cliphistAvailable = false // Show toast notification if feature is enabled but cliphist is missing if (Settings.data.appLauncher.enableClipboardHistory) { - ToastService.showWarning("Clipboard history unavailable", "The 'cliphist' application is not installed. Please install it to use clipboard history features.", false, 6000) + ToastService.showWarning(I18n.tr("toast.clipboard.unavailable"), I18n.tr("toast.clipboard.unavailable-desc"), false, 6000) } } } diff --git a/Services/IPCService.qml b/Services/IPCService.qml index 5031bbbc..1d01f3fd 100644 --- a/Services/IPCService.qml +++ b/Services/IPCService.qml @@ -114,7 +114,7 @@ Item { target: "powerPanel" function toggle() { sessionMenuPanel.toggle() - ToastService.showWarning("IPC", "PowerPanel has been renamed to SessionMenu, this IPC call will be deprecated soon. Please use \"ipc call sessionMenu toggle\" instead.", 8000) + ToastService.showWarning("IPC", I18n.tr("toast.ipc.powerpanel-deprecated"), 8000) } } IpcHandler { @@ -130,7 +130,7 @@ Item { function toggle() { // Will attempt to open the panel next to the bar button if any. controlCenterPanel.toggle(BarService.lookupWidget("ControlCenter")) - ToastService.showWarning("IPC", "SidePanel has been renamed to ControlCenter, this IPC call will be deprecated soon. Please use \"ipc call controlCenter toggle\" instead.", 8000) + ToastService.showWarning("IPC", I18n.tr("toast.ipc.sidepanel-deprecated"), 8000) } } IpcHandler { diff --git a/Services/IdleInhibitorService.qml b/Services/IdleInhibitorService.qml index c804226e..9a2504a6 100644 --- a/Services/IdleInhibitorService.qml +++ b/Services/IdleInhibitorService.qml @@ -163,13 +163,13 @@ Singleton { if (activeInhibitors.includes("manual")) { removeInhibitor("manual") Settings.data.ui.idleInhibitorEnabled = false - ToastService.showNotice("Keep awake", "Disabled") + ToastService.showNotice(I18n.tr("tooltips.keep-awake"), I18n.tr("toast.keep-awake.disabled")) Logger.log("IdleInhibitor", "Manual inhibition disabled and saved to settings") return false } else { addInhibitor("manual", "Manually activated by user") Settings.data.ui.idleInhibitorEnabled = true - ToastService.showNotice("Keep awake", "Enabled") + ToastService.showNotice(I18n.tr("tooltips.keep-awake"), I18n.tr("toast.keep-awake.enabled")) Logger.log("IdleInhibitor", "Manual inhibition enabled and saved to settings") return true } diff --git a/Services/NetworkService.qml b/Services/NetworkService.qml index 2287fc45..0caaf025 100644 --- a/Services/NetworkService.qml +++ b/Services/NetworkService.qml @@ -48,9 +48,9 @@ Singleton { target: Settings.data.network function onWifiEnabledChanged() { if (Settings.data.network.wifiEnabled) { - ToastService.showNotice("Wi-Fi", "Enabled") + ToastService.showNotice(I18n.tr("wifi.panel.title"), I18n.tr("toast.wifi.enabled")) } else { - ToastService.showNotice("Wi-Fi", "Disabled") + ToastService.showNotice(I18n.tr("wifi.panel.title"), I18n.tr("toast.wifi.disabled")) } } } @@ -492,7 +492,9 @@ Singleton { root.connecting = false root.connectingTo = "" Logger.log("Network", `Connected to network: '${connectProcess.ssid}'`) - ToastService.showNotice("Wi-Fi", `Connected to '${connectProcess.ssid}'`) + ToastService.showNotice(I18n.tr("wifi.panel.title"), I18n.tr("toast.wifi.connected", { + "ssid": connectProcess.ssid + })) // Still do a scan to get accurate signal and security info delayedScanTimer.interval = 5000 @@ -533,7 +535,9 @@ Singleton { stdout: StdioCollector { onStreamFinished: { Logger.log("Network", `Disconnected from network: '${disconnectProcess.ssid}'`) - ToastService.showNotice("Wi-Fi", `Disconnected from '${disconnectProcess.ssid}'`) + ToastService.showNotice(I18n.tr("wifi.panel.title"), I18n.tr("toast.wifi.disconnected", { + "ssid": disconnectProcess.ssid + })) // Immediately update UI on successful disconnect root.updateNetworkStatus(disconnectProcess.ssid, false) diff --git a/Services/NightLightService.qml b/Services/NightLightService.qml index ba37ca21..a52be4d8 100644 --- a/Services/NightLightService.qml +++ b/Services/NightLightService.qml @@ -63,12 +63,12 @@ Singleton { apply() // Toast: night light toggled const enabled = !!Settings.data.nightLight.enabled - ToastService.showNotice("Night light", enabled ? "Enabled" : "Disabled") + ToastService.showNotice(I18n.tr("settings.display.night-light.section.label"), enabled ? I18n.tr("toast.night-light.enabled") : I18n.tr("toast.night-light.disabled")) } function onForcedChanged() { apply() if (Settings.data.nightLight.enabled) { - ToastService.showNotice("Night Light", Settings.data.nightLight.forced ? "Forced activation" : "Normal mode") + ToastService.showNotice(I18n.tr("settings.display.night-light.section.label"), Settings.data.nightLight.forced ? I18n.tr("toast.night-light.forced") : I18n.tr("toast.night-light.normal")) } } function onNightTempChanged() { diff --git a/Services/NotificationService.qml b/Services/NotificationService.qml index 8f0999dd..fcad5e85 100644 --- a/Services/NotificationService.qml +++ b/Services/NotificationService.qml @@ -386,7 +386,7 @@ Singleton { target: Settings.data.notifications function onDoNotDisturbChanged() { const enabled = Settings.data.notifications.doNotDisturb - ToastService.showNotice(enabled ? "'Do not disturb' enabled" : "'Do not disturb' disabled", enabled ? "You'll find these notifications in your history." : "Showing all notifications.") + ToastService.showNotice(enabled ? I18n.tr("toast.do-not-disturb.enabled") : I18n.tr("toast.do-not-disturb.disabled"), enabled ? I18n.tr("toast.do-not-disturb.enabled-desc") : I18n.tr("toast.do-not-disturb.disabled-desc")) } } } diff --git a/Services/PowerProfileService.qml b/Services/PowerProfileService.qml index e14f7f7c..98acfb71 100644 --- a/Services/PowerProfileService.qml +++ b/Services/PowerProfileService.qml @@ -78,7 +78,9 @@ Singleton { // Only show toast if we have a valid profile name (not "Unknown") const profileName = root.getName() if (profileName !== "Unknown") { - ToastService.showNotice("Power profile changed", `"${profileName}"`) + ToastService.showNotice(I18n.tr("toast.power-profile.changed"), I18n.tr("toast.power-profile.profile-name", { + "profile": profileName + })) } } } diff --git a/Services/ScreenRecorderService.qml b/Services/ScreenRecorderService.qml index e45a3785..17bd92e9 100644 --- a/Services/ScreenRecorderService.qml +++ b/Services/ScreenRecorderService.qml @@ -82,7 +82,7 @@ Singleton { return } - ToastService.showNotice("Stopping recording…", outputPath, 2000) + ToastService.showNotice(I18n.tr("toast.recording.stopping"), outputPath, 2000) Quickshell.execDetached(["sh", "-c", "pkill -SIGINT -f 'gpu-screen-recorder' || pkill -SIGINT -f 'com.dec05eba.gpu_screen_recorder'"]) @@ -110,9 +110,9 @@ Singleton { if (exitCode !== 0) { const err = String(stderr.text || "").trim() if (err.length > 0) - ToastService.showError("Failed to start recording", err, 7000) + ToastService.showError(I18n.tr("toast.recording.failed-start"), err, 7000) else - ToastService.showError("Failed to start recording", "gpu-screen-recorder exited unexpectedly.", 7000) + ToastService.showError(I18n.tr("toast.recording.failed-start"), I18n.tr("toast.recording.failed-gpu"), 7000) } } else if (isRecording) { // Process ended normally while recording @@ -120,13 +120,13 @@ Singleton { monitorTimer.running = false // Consider successful save if exitCode == 0 if (exitCode === 0) { - ToastService.showNotice("Recording saved", outputPath, 5000) + ToastService.showNotice(I18n.tr("toast.recording.saved"), outputPath, 5000) } else { const err2 = String(stderr.text || "").trim() if (err2.length > 0) - ToastService.showError("Recording failed", err2, 7000) + ToastService.showError(I18n.tr("toast.recording.failed-start"), err2, 7000) else - ToastService.showError("Recording failed", "The recorder exited with an error.", 7000) + ToastService.showError(I18n.tr("toast.recording.failed-start"), I18n.tr("toast.recording.failed-general"), 7000) } } } @@ -142,7 +142,7 @@ Singleton { } else { isPending = false hasActiveRecording = false - ToastService.showError("Desktop portals not running", "Start xdg-desktop-portal and a compositor portal (wlr/hyprland/gnome/kde).", 8000) + ToastService.showError(I18n.tr("toast.recording.no-portals"), I18n.tr("toast.recording.no-portals-desc"), 8000) } } } diff --git a/Widgets/NFilePicker.qml b/Widgets/NFilePicker.qml index e96cd561..54072943 100644 --- a/Widgets/NFilePicker.qml +++ b/Widgets/NFilePicker.qml @@ -15,7 +15,7 @@ Item { property bool multipleSelection: false property string pickerType: "file" // "file" or "folder" property var nameFilters: ["All files (*)"] // e.g., ["Image files (*.png *.jpg)", "Text files (*.txt)"] - property string title: pickerType === "folder" ? "Select Folder" : "Select File" + property string title: pickerType === "folder" ? I18n.tr("widgets.file-picker.select-folder") : I18n.tr("widgets.file-picker.select-file") property string acceptLabel: I18n.tr("placeholders.select") property string rejectLabel: I18n.tr("placeholders.cancel")