diff --git a/Onyx/Localizable.xcstrings b/Onyx/Localizable.xcstrings index 5df8e5f..8f9f6c1 100644 --- a/Onyx/Localizable.xcstrings +++ b/Onyx/Localizable.xcstrings @@ -1,1824 +1,1825 @@ { - "sourceLanguage": "en", - "strings": { - "%@": { - "comment": "A label showing the minimum RPM of a fan.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "%@" + "sourceLanguage" : "en", + "strings" : { + "%@" : { + "comment" : "A label showing the minimum RPM of a fan.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "%@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } } } }, - "%@ U/min": { - "comment": "A label that shows the current speed of a fan. The argument is the current speed of the fan.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "%@ U/min" + "%@ °C" : { + "comment" : "A label showing the temperature of the hottest fan in the report, in degrees Celsius. The argument is the temperature in degrees Celsius.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ °C" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "%@ U/min" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ °C" } } } }, - "%@ °C": { - "comment": "A label showing the temperature of the hottest fan in the report, in degrees Celsius. The argument is the temperature in degrees Celsius.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "%@ °C" + "%@ U/min" : { + "comment" : "A label that shows the current speed of a fan. The argument is the current speed of the fan.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ U/min" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "%@ °C" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ U/min" } } } }, - "%@°": { - "comment": "A temperature in °C.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "%@°" + "%@°" : { + "comment" : "A temperature in °C.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@°" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "%@°" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@°" } } } }, - "fans.approval.needed": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "macOS wartet auf deine Freigabe. Öffne die Systemeinstellungen und erlaube den Hintergrunddienst von Onyx." + "fans.approval.needed" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "macOS wartet auf deine Freigabe. Öffne die Systemeinstellungen und erlaube den Hintergrunddienst von Onyx." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "macOS is waiting for your approval. Open System Settings and allow Onyx's background service." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "macOS is waiting for your approval. Open System Settings and allow Onyx's background service." } } } }, - "fans.approval.open": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Systemeinstellungen öffnen" + "fans.approval.open" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Systemeinstellungen öffnen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Open System Settings" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Open System Settings" } } } }, - "fans.approval.recheck": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Erneut prüfen" + "fans.approval.recheck" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erneut prüfen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Check again" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Check again" } } } }, - "fans.auto": { - "comment": "A button to set a fan to automatic mode.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Auto" + "fans.auto" : { + "comment" : "A button to set a fan to automatic mode.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Auto" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Auto" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Auto" } } } }, - "fans.connecting": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Verbinde mit dem Hilfsdienst …" + "fans.connecting" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbinde mit dem Hilfsdienst …" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Connecting to the helper…" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecting to the helper…" } } } }, - "fans.fan %@": { - "comment": "A label showing the index of a fan. The argument is the index of the fan.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Lüfter %@" + "fans.fan %@" : { + "comment" : "A label showing the index of a fan. The argument is the index of the fan.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lüfter %@" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Fan %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fan %@" } } } }, - "fans.fan %lld": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Lüfter %lld" + "fans.fan %lld" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lüfter %lld" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Fan %lld" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fan %lld" } } } }, - "fans.hottest": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Wärmster Kern" + "fans.hottest" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wärmster Kern" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Hottest core" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hottest core" } } } }, - "fans.install": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Installieren" + "fans.install" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Installieren" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Install" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Install" } } } }, - "fans.intro": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Onyx kann die Lüfterdrehzahl fest vorgeben, statt sie macOS zu überlassen. Dafür wird ein Hilfsdienst mit Systemrechten eingerichtet — Schreibzugriff auf den SMC geht nicht anders." + "fans.intro" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Onyx kann die Lüfterdrehzahl fest vorgeben, statt sie macOS zu überlassen. Dafür wird ein Hilfsdienst mit Systemrechten eingerichtet — Schreibzugriff auf den SMC geht nicht anders." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Onyx can set a fixed fan speed instead of leaving it to macOS. This requires a helper service with system privileges — writing to the SMC is not possible otherwise." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Onyx can set a fixed fan speed instead of leaving it to macOS. This requires a helper service with system privileges — writing to the SMC is not possible otherwise." } } } }, - "fans.left": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Lüfter links" + "fans.left" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lüfter links" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Left fan" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Left fan" } } } }, - "fans.manual": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Manuell" + "fans.manual" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manuell" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Manual" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manual" } } } }, - "fans.mode.auto": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Automatik" + "fans.mode.auto" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatik" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Automatic" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatic" } } } }, - "fans.mode.manual": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Manuell" + "fans.mode.manual" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manuell" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Manual" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manual" } } } }, - "fans.noData": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Noch keine Lüfterdaten." + "fans.noData" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Noch keine Lüfterdaten." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "No fan data yet." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No fan data yet." } } } }, - "fans.noRange": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Kein Regelbereich" + "fans.noRange" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kein Regelbereich" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "No adjustable range" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No adjustable range" } } } }, - "fans.notInstalled": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Die Lüftersteuerung braucht einen kleinen privilegierten Dienst." + "fans.notInstalled" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Die Lüftersteuerung braucht einen kleinen privilegierten Dienst." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Fan control needs a small privileged service." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fan control needs a small privileged service." } } } }, - "fans.reason.noTemperature": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zurück auf Automatik: keine Temperatur messbar, damit lässt sich Sicherheit nicht beurteilen." + "fans.reason.noTemperature" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zurück auf Automatik: keine Temperatur messbar, damit lässt sich Sicherheit nicht beurteilen." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Back to automatic: no temperature reading, so safety cannot be judged." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Back to automatic: no temperature reading, so safety cannot be judged." } } } }, - "fans.reason.other": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zurück auf Automatik." + "fans.reason.other" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zurück auf Automatik." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Back to automatic." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Back to automatic." } } } }, - "fans.reason.temperature": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zurück auf Automatik: zu heiß für eine feste Drehzahl." + "fans.reason.temperature" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zurück auf Automatik: zu heiß für eine feste Drehzahl." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Back to automatic: too hot for a fixed speed." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Back to automatic: too hot for a fixed speed." } } } }, - "fans.reason.unknownLimits": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zurück auf Automatik: der erlaubte Drehzahlbereich ist nicht lesbar." + "fans.reason.unknownLimits" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zurück auf Automatik: der erlaubte Drehzahlbereich ist nicht lesbar." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Back to automatic: the permitted speed range cannot be read." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Back to automatic: the permitted speed range cannot be read." } } } }, - "fans.reason.watchdog": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zurück auf Automatik: Onyx hat sich zu lange nicht gemeldet." + "fans.reason.watchdog" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zurück auf Automatik: Onyx hat sich zu lange nicht gemeldet." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Back to automatic: Onyx went silent for too long." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Back to automatic: Onyx went silent for too long." } } } }, - "fans.right": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Lüfter rechts" + "fans.right" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lüfter rechts" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Right fan" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Right fan" } } } }, - "fans.safety": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Deshalb greift ein Sicherheitsnetz, das sich nicht abschalten lässt: nie unter das Minimum der Firmware, Rückfall auf Automatik ab 95 °C, Rückfall wenn Onyx sich fünf Sekunden nicht meldet, und Rückfall beim Beenden." + "fans.safety" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Deshalb greift ein Sicherheitsnetz, das sich nicht abschalten lässt: nie unter das Minimum der Firmware, Rückfall auf Automatik ab 95 °C, Rückfall wenn Onyx sich fünf Sekunden nicht meldet, und Rückfall beim Beenden." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "A safety net you cannot switch off is therefore always active: never below the firmware minimum, back to automatic above 95 °C, back to automatic if Onyx goes silent for five seconds, and back to automatic on quit." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "A safety net you cannot switch off is therefore always active: never below the firmware minimum, back to automatic above 95 °C, back to automatic if Onyx goes silent for five seconds, and back to automatic on quit." } } } }, - "fans.safety.active": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Das Sicherheitsnetz läuft im Hilfsdienst, nicht in Onyx. Es greift auch dann, wenn Onyx abstürzt oder beendet wird." + "fans.safety.active" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Das Sicherheitsnetz läuft im Hilfsdienst, nicht in Onyx. Es greift auch dann, wenn Onyx abstürzt oder beendet wird." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "The safety net runs in the helper, not in Onyx. It works even if Onyx crashes or is quit." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "The safety net runs in the helper, not in Onyx. It works even if Onyx crashes or is quit." } } } }, - "fans.uninstall": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Hilfsdienst entfernen" + "fans.uninstall" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hilfsdienst entfernen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Remove helper" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Remove helper" } } } }, - "fans.warning": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Eine zu niedrige Drehzahl kann den Rechner überhitzen lassen. Die Automatik regelt nach Temperatur, ein fester Wert nicht." + "fans.warning" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eine zu niedrige Drehzahl kann den Rechner überhitzen lassen. Die Automatik regelt nach Temperatur, ein fester Wert nicht." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "A fan speed set too low can let the Mac overheat. The automatic control adjusts to temperature; a fixed value does not." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "A fan speed set too low can let the Mac overheat. The automatic control adjusts to temperature; a fixed value does not." } } } }, - "menu.openPanel": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Panel öffnen" + "menu.openPanel" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Panel öffnen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Open Panel" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Open Panel" } } } }, - "menu.quit": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Onyx beenden" + "menu.quit" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Onyx beenden" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Quit Onyx" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Quit Onyx" } } } }, - "menu.settings": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Einstellungen …" + "menu.settings" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Einstellungen …" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Settings…" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Settings…" } } } }, - "metric.battery": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Akku" + "metric.battery" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Akku" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Battery" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Battery" } } } }, - "metric.cpu": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "CPU" + "metric.cpu" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "CPU" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "CPU" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "CPU" } } } }, - "metric.gpu": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "GPU" + "metric.gpu" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "GPU" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "GPU" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "GPU" } } } }, - "metric.memory": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Speicher" + "metric.memory" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Speicher" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Memory" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Memory" } } } }, - "metric.mode.temperature": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Temperatur" + "metric.mode.temperature" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Temperatur" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Temperature" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Temperature" } } } }, - "metric.mode.usage": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Auslastung" + "metric.mode.usage" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Auslastung" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Usage" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Usage" } } } }, - "metric.sensors": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sensoren" + "metric.sensors" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensoren" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sensors" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensors" } } } }, - "mixer.menubar.name": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Lautstärkemixer" + "mixer.menubar.name" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lautstärkemixer" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Volume mixer" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Volume mixer" } } } }, - "network.name": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Netzwerk" + "network.name" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Netzwerk" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Network" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Network" } } } }, - "onboarding.allow": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Erlauben" + "onboarding.allow" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erlauben" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Allow" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Allow" } } } }, - "onboarding.audio": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Systemton" + "onboarding.audio" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Systemton" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "System audio" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "System audio" } } } }, - "onboarding.audio.why": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Nötig für die Lautstärke einzelner Programme. Onyx leitet den Ton der geregelten Programme um — und nur deren." + "onboarding.audio.why" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nötig für die Lautstärke einzelner Programme. Onyx leitet den Ton der geregelten Programme um — und nur deren." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Needed for per-app volume. Onyx routes the audio of the apps you control, and only those." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Needed for per-app volume. Onyx routes the audio of the apps you control, and only those." } } } }, - "onboarding.calendar": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Kalender" + "onboarding.calendar" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kalender" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Calendar" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Calendar" } } } }, - "onboarding.calendar.why": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zeigt deine Termine im Panel. Nur lesend — Onyx ändert nie einen Kalender." + "onboarding.calendar.why" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zeigt deine Termine im Panel. Nur lesend — Onyx ändert nie einen Kalender." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Shows your appointments in the panel. Read-only — Onyx never changes a calendar." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Shows your appointments in the panel. Read-only — Onyx never changes a calendar." } } } }, - "onboarding.done": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Fertig" + "onboarding.done" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fertig" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Done" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Done" } } } }, - "onboarding.helper": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Lüftersteuerung" + "onboarding.helper" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lüftersteuerung" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Fan control" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fan control" } } } }, - "onboarding.helper.why": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Installiert einen kleinen privilegierten Dienst. Er setzt nur Lüfterdrehzahlen und fällt auf Automatik zurück, wenn Onyx schweigt." + "onboarding.helper.why" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Installiert einen kleinen privilegierten Dienst. Er setzt nur Lüfterdrehzahlen und fällt auf Automatik zurück, wenn Onyx schweigt." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Installs a small privileged service. It only sets fan speeds and falls back to automatic if Onyx goes silent." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Installs a small privileged service. It only sets fan speeds and falls back to automatic if Onyx goes silent." } } } }, - "onboarding.launchAtLogin": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Onyx beim Anmelden starten" + "onboarding.launchAtLogin" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Onyx beim Anmelden starten" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Start Onyx at login" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Start Onyx at login" } } } }, - "onboarding.location": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Standort" + "onboarding.location" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Standort" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Location" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Location" } } } }, - "onboarding.location.why": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Wetter für deinen Aufenthaltsort. Alternativ lässt sich je Widget ein fester Ort einstellen." + "onboarding.location.why" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wetter für deinen Aufenthaltsort. Alternativ lässt sich je Widget ein fester Ort einstellen." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Weather for where you are. You can also set a fixed place per widget instead." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Weather for where you are. You can also set a fixed place per widget instead." } } } }, - "onboarding.openSettings": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Systemeinstellungen öffnen" + "onboarding.openSettings" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Systemeinstellungen öffnen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Open System Settings" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Open System Settings" } } } }, - "onboarding.subtitle": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Onyx braucht vier Dinge vom System. Jedes für etwas anderes, und keines davon ist Pflicht — erlaube, was du nutzen möchtest." + "onboarding.subtitle" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Onyx braucht vier Dinge vom System. Jedes für etwas anderes, und keines davon ist Pflicht — erlaube, was du nutzen möchtest." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Onyx needs four things from the system. Each one is for something different, and none of them is required — grant what you want to use." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Onyx needs four things from the system. Each one is for something different, and none of them is required — grant what you want to use." } } } }, - "onboarding.title": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Willkommen bei Onyx" + "onboarding.title" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Willkommen bei Onyx" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Welcome to Onyx" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Welcome to Onyx" } } } }, - "settings.display.all": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Allen Displays" + "settings.display.all" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Allen Displays" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "All displays" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "All displays" } } } }, - "settings.display.builtInOnly": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Nur dem internen Display" + "settings.display.builtInOnly" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nur dem internen Display" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Built-in display only" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Built-in display only" } } } }, - "settings.display.dockIcon": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Symbol im Dock anzeigen" + "settings.display.dockIcon" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Symbol im Dock anzeigen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Show icon in the Dock" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show icon in the Dock" } } } }, - "settings.display.dockIcon.hint": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Standardmäßig aus. Onyx lebt in der Notch und in der Menüleiste und hat kein Hauptfenster — im Dock wäre es ein Symbol, das beim Anklicken nichts öffnet, und es stünde im Programmumschalter zwischen den Programmen, mit denen du arbeitest." + "settings.display.dockIcon.hint" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Standardmäßig aus. Onyx lebt in der Notch und in der Menüleiste und hat kein Hauptfenster — im Dock wäre es ein Symbol, das beim Anklicken nichts öffnet, und es stünde im Programmumschalter zwischen den Programmen, mit denen du arbeitest." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Off by default. Onyx lives in the notch and the menu bar and has no main window — in the Dock it would be an icon that opens nothing, and it would sit in the app switcher among the apps you actually work with." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Off by default. Onyx lives in the notch and the menu bar and has no main window — in the Dock it would be an icon that opens nothing, and it would sit in the app switcher among the apps you actually work with." } } } }, - "settings.display.hint": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Auf Displays ohne echte Notch erscheint oben mittig ein Balken, der sich genauso verhält. Ist der Deckel geschlossen und kein internes Display vorhanden, erscheint das Panel trotzdem auf einem externen — sonst wäre Onyx nicht erreichbar." + "settings.display.hint" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Auf Displays ohne echte Notch erscheint oben mittig ein Balken, der sich genauso verhält. Ist der Deckel geschlossen und kein internes Display vorhanden, erscheint das Panel trotzdem auf einem externen — sonst wäre Onyx nicht erreichbar." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "On displays without a real notch, a bar appears at the top centre and behaves the same. With the lid closed and no built-in display present, the panel still appears on an external one — otherwise Onyx would be unreachable." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "On displays without a real notch, a bar appears at the top centre and behaves the same. With the lid closed and no built-in display present, the panel still appears on an external one — otherwise Onyx would be unreachable." } } } }, - "settings.display.menuBarIcon": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Symbol in der Menüleiste anzeigen" + "settings.display.menuBarIcon" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Symbol in der Menüleiste anzeigen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Show icon in the menu bar" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show icon in the menu bar" } } } }, - "settings.display.menuBarIcon.hint": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Über das Symbol erreichst du Panel, Einstellungen und Beenden. Ohne es kommst du nur über die Notch an Onyx heran." + "settings.display.menuBarIcon.hint" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Über das Symbol erreichst du Panel, Einstellungen und Beenden. Ohne es kommst du nur über die Notch an Onyx heran." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "The icon gives you the panel, settings and quit. Without it, the notch is the only way to reach Onyx." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "The icon gives you the panel, settings and quit. Without it, the notch is the only way to reach Onyx." } } } }, - "settings.display.policy": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Panel anzeigen auf" + "settings.display.policy" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Panel anzeigen auf" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Show panel on" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show panel on" } } } }, - "settings.general": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Allgemein" + "settings.general" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Allgemein" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "General" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "General" } } } }, - "settings.launchAtLogin": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Beim Anmelden starten" + "settings.launchAtLogin" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Beim Anmelden starten" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Start at login" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Start at login" } } } }, - "settings.launchAtLogin.blocked": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Anmeldeobjekte wurden für Onyx in den Systemeinstellungen abgeschaltet." + "settings.launchAtLogin.blocked" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anmeldeobjekte wurden für Onyx in den Systemeinstellungen abgeschaltet." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Login items were turned off for Onyx in System Settings." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Login items were turned off for Onyx in System Settings." } } } }, - "settings.menubar.bars": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Balken" + "settings.menubar.bars" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Balken" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Bars" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bars" } } } }, - "settings.menubar.graph": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Verlauf" + "settings.menubar.graph" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verlauf" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Graph" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Graph" } } } }, - "settings.menubar.hint": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Jedes Modul erscheint als eigenes Symbol in der Menüleiste und teilt sich die Messung mit dem Widget im Panel. Standardmäßig ist keines an — die Menüleiste ist knapp, auf einem Notch-Display zusätzlich in der Mitte blockiert." + "settings.menubar.hint" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jedes Modul erscheint als eigenes Symbol in der Menüleiste und teilt sich die Messung mit dem Widget im Panel. Standardmäßig ist keines an — die Menüleiste ist knapp, auf einem Notch-Display zusätzlich in der Mitte blockiert." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Each module appears as its own menu bar icon and shares its measurement with the panel widget. None are on by default — the menu bar is tight, and on a notched display the middle is blocked as well." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Each module appears as its own menu bar icon and shares its measurement with the panel widget. None are on by default — the menu bar is tight, and on a notched display the middle is blocked as well." } } } }, - "settings.menubar.symbol": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Nur Symbol" + "settings.menubar.symbol" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nur Symbol" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Symbol only" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Symbol only" } } } }, - "settings.menubar.value": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Wert" + "settings.menubar.value" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wert" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Value" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Value" } } } }, - "settings.menubar.valueAndGraph": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Wert und Verlauf" + "settings.menubar.valueAndGraph" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wert und Verlauf" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Value and graph" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Value and graph" } } } }, - "settings.onboarding.show": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Einrichtung erneut zeigen" + "settings.onboarding.show" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Einrichtung erneut zeigen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Show setup again" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show setup again" } } } }, - "settings.permissions.calendar": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Kalender" + "settings.permissions.calendar" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kalender" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Calendar" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Calendar" } } } }, - "settings.permissions.calendar.why": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Onyx zeigt deine Termine im Kalender-Widget an. Es wird ausschließlich gelesen, nie geschrieben." + "settings.permissions.calendar.why" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Onyx zeigt deine Termine im Kalender-Widget an. Es wird ausschließlich gelesen, nie geschrieben." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Onyx shows your events in the calendar widget. It only ever reads, never writes." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Onyx shows your events in the calendar widget. It only ever reads, never writes." } } } }, - "settings.permissions.granted": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Erteilt" + "settings.permissions.granted" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erteilt" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Granted" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Granted" } } } }, - "settings.permissions.location": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Standort" + "settings.permissions.location" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Standort" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Location" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Location" } } } }, - "settings.permissions.location.why": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Onyx zeigt das Wetter an deinem aktuellen Standort. Ohne Zugriff kannst du stattdessen einen festen Ort wählen." + "settings.permissions.location.why" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Onyx zeigt das Wetter an deinem aktuellen Standort. Ohne Zugriff kannst du stattdessen einen festen Ort wählen." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Onyx shows the weather for your current location. Without access you can pick a fixed place instead." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Onyx shows the weather for your current location. Without access you can pick a fixed place instead." } } } }, - "settings.permissions.openSystem": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Systemeinstellungen öffnen" + "settings.permissions.openSystem" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Systemeinstellungen öffnen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Open System Settings" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Open System Settings" } } } }, - "settings.permissions.request": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Anfragen" + "settings.permissions.request" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anfragen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Request" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Request" } } } }, - "settings.shelf.cleanup": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Automatisch löschen" + "settings.shelf.cleanup" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatisch löschen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Delete automatically" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delete automatically" } } } }, - "settings.shelf.count %@": { - "comment": "A label showing the number of items in the shelf. The argument is the number of items in the shelf.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "%@ Dateien abgelegt" + "settings.shelf.count %@" : { + "comment" : "A label showing the number of items in the shelf. The argument is the number of items in the shelf.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ Dateien abgelegt" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "%@ files parked" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ files parked" } } } }, - "settings.shelf.count %lld": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "%lld Dateien abgelegt" + "settings.shelf.count %lld" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld Dateien abgelegt" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "%lld files parked" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld files parked" } } } }, - "settings.shelf.days %@": { - "comment": "A label that lets the user select how many days to wait before automatically removing items from the shelf. The argument is the number of days.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Nach %@ Tagen" + "settings.shelf.days %@" : { + "comment" : "A label that lets the user select how many days to wait before automatically removing items from the shelf. The argument is the number of days.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nach %@ Tagen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "After %@ days" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "After %@ days" } } } }, - "settings.shelf.days %lld": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Nach %lld Tagen" + "settings.shelf.days %lld" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nach %lld Tagen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "After %lld days" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "After %lld days" } } } }, - "settings.shelf.never": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Nie" + "settings.shelf.never" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nie" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Never" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Never" } } } }, - "settings.shelf.reveal": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Im Finder zeigen" + "settings.shelf.reveal" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Im Finder zeigen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Show in Finder" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show in Finder" } } } }, - "settings.shelf.title": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Ablage" + "settings.shelf.title" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ablage" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Shelf" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Shelf" } } } }, - "settings.size.large": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Groß (2×2)" + "settings.size.large" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Groß (2×2)" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Large (2×2)" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Large (2×2)" } } } }, - "settings.size.medium": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Mittel (2×1)" + "settings.size.medium" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mittel (2×1)" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Medium (2×1)" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Medium (2×1)" } } } }, - "settings.size.small": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Klein (1×1)" + "settings.size.small" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Klein (1×1)" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Small (1×1)" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Small (1×1)" } } } }, - "settings.size.wide": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Breit (4×1)" + "settings.size.wide" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Breit (4×1)" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Wide (4×1)" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wide (4×1)" } } } }, - "settings.tab.display": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Anzeige" + "settings.tab.display" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anzeige" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Display" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Display" } } } }, - "settings.tab.fans": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Lüfter" + "settings.tab.fans" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lüfter" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Fans" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fans" } } } }, - "settings.tab.menubar": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Menüleiste" + "settings.tab.menubar" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Menüleiste" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Menu bar" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Menu bar" } } } }, - "settings.tab.permissions": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Berechtigungen" + "settings.tab.permissions" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Berechtigungen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Permissions" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Permissions" } } } }, - "settings.tab.sources": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Quellen" + "settings.tab.sources" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Quellen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sources" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sources" } } } }, - "settings.tab.widgets": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Widgets" + "settings.tab.widgets" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Widgets" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Widgets" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Widgets" } } } }, - "settings.widgets.add": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Hinzufügen" + "settings.widgets.add" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hinzufügen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Add" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Add" } } } }, - "settings.widgets.available": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Verfügbar" + "settings.widgets.available" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verfügbar" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Available" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Available" } } } }, - "settings.widgets.empty": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Keine Widgets im Panel. Das Panel bleibt leer, bis du eins hinzufügst." + "settings.widgets.empty" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine Widgets im Panel. Das Panel bleibt leer, bis du eins hinzufügst." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "No widgets in the panel. It stays empty until you add one." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No widgets in the panel. It stays empty until you add one." } } } }, - "settings.widgets.hint": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Die Reihenfolge bestimmt die Anordnung im Panel. Jedes Widget rückt an die erste Stelle, an die es passt." + "settings.widgets.hint" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Die Reihenfolge bestimmt die Anordnung im Panel. Jedes Widget rückt an die erste Stelle, an die es passt." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "The order determines the arrangement in the panel. Each widget takes the first spot it fits into." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "The order determines the arrangement in the panel. Each widget takes the first spot it fits into." } } } }, - "settings.widgets.hint.order": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Alle Kacheln sind gleich groß. Die Reihenfolge hier ist die Reihenfolge im Panel — zum Sortieren ziehen." + "settings.widgets.hint.order" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alle Kacheln sind gleich groß. Die Reihenfolge hier ist die Reihenfolge im Panel — zum Sortieren ziehen." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "All tiles are the same size. The order here is the order in the panel — drag to sort." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "All tiles are the same size. The order here is the order in the panel — drag to sort." } } } }, - "settings.widgets.inPanel": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Im Panel" + "settings.widgets.inPanel" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Im Panel" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "In the panel" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "In the panel" } } } }, - "settings.widgets.remove": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Aus dem Panel entfernen" + "settings.widgets.remove" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aus dem Panel entfernen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Remove from panel" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Remove from panel" } } } }, - "widget.fans.name": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Lüfter" + "widget.fans.name" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lüfter" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Fans" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fans" } } } } }, - "version": "1.1" -} + "version" : "1.1" +} \ No newline at end of file diff --git a/Onyx/OnboardingWindowController.swift b/Onyx/OnboardingWindowController.swift index 022b7de..c1d3a4e 100644 --- a/Onyx/OnboardingWindowController.swift +++ b/Onyx/OnboardingWindowController.swift @@ -63,11 +63,20 @@ final class OnboardingWindowController: NSObject, NSWindowDelegate { /// eine Zumutung. private func finish() { defaults.set(true, forKey: Self.doneKey) - window?.close() + hide() } - func windowWillClose(_ notification: Notification) { + /// Wie bei den Einstellungen: ausblenden statt schließen, damit ein + /// Hilfsprogramm mit „letztes Fenster zu = App beenden" Onyx nicht mit + /// wegräumt. + func windowShouldClose(_ sender: NSWindow) -> Bool { defaults.set(true, forKey: Self.doneKey) + hide() + return false + } + + private func hide() { + window?.orderOut(nil) // Zurück in den Hintergrund — es sei denn, ein anderes Fenster steht // noch offen. if !NSApp.windows.contains(where: { $0 !== window && $0.isVisible }) { diff --git a/Onyx/OnyxApp.swift b/Onyx/OnyxApp.swift index 5da57a2..3bcd41f 100644 --- a/Onyx/OnyxApp.swift +++ b/Onyx/OnyxApp.swift @@ -170,6 +170,15 @@ final class AppDelegate: NSObject, NSApplicationDelegate { launchAtLogin: launchAtLogin) } + /// Ausdrücklich `false`, auch wenn es der Standard ist. + /// + /// Onyx lebt in der Menüleiste und in der Notch. Dass das letzte Fenster + /// zugeht, sagt über das Beenden nichts aus — und die Voreinstellung darf + /// sich nicht unbemerkt ändern. + func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + false + } + func applicationWillTerminate(_ notification: Notification) { coordinator?.stop() menuBar?.stop() diff --git a/Onyx/SettingsWindowController.swift b/Onyx/SettingsWindowController.swift index 6b45487..0892a8f 100644 --- a/Onyx/SettingsWindowController.swift +++ b/Onyx/SettingsWindowController.swift @@ -73,7 +73,20 @@ final class SettingsWindowController: NSObject, NSWindowDelegate { window?.makeKeyAndOrderFront(nil) } - func windowWillClose(_ notification: Notification) { + /// Der rote Knopf blendet aus, statt zu schließen. + /// + /// Onyx ist eine App ohne Hauptfenster; „Fenster zu" heißt hier nie + /// „fertig". Manche Hilfsprogramme erzwingen aber genau diese Gleichung und + /// beenden eine App, sobald ihr letztes Fenster verschwindet — dann wäre + /// Onyx nach einem Blick in die Einstellungen weg, samt Menüleiste und + /// Notch. Ein Fenster, das nie wirklich schließt, gibt dafür keinen Anlass. + func windowShouldClose(_ sender: NSWindow) -> Bool { + hide() + return false + } + + private func hide() { + window?.orderOut(nil) // Zurück in den Hintergrund — es sei denn, der Dock-Eintrag ist // ausdrücklich gewünscht. if !keepsDockIcon { diff --git a/Packages/OnyxKit/Sources/OnyxNotch/NotchCoordinator.swift b/Packages/OnyxKit/Sources/OnyxNotch/NotchCoordinator.swift index dfa6ee0..8620f11 100644 --- a/Packages/OnyxKit/Sources/OnyxNotch/NotchCoordinator.swift +++ b/Packages/OnyxKit/Sources/OnyxNotch/NotchCoordinator.swift @@ -30,6 +30,7 @@ public final class NotchCoordinator { private var suppressionTimer: Timer? /// Läuft nur, solange ein Panel offen ist. Siehe `startPolling()`. private var pointerPollTimer: Timer? + private var pollInterval: TimeInterval = 0 /// Baut den Inhalt für einen Bildschirm. Ab Phase 2 liefert das die Widget-Ebene. /// Die `NotchPresentation` kommt mit, weil der Inhalt in den Animationsrahmen @@ -49,6 +50,9 @@ public final class NotchCoordinator { public func start() { rebuild() installMonitors() + // Sofort anlaufen lassen, nicht erst wenn etwas sichtbar wird: die + // Abfrage ist es, die überhaupt etwas sichtbar macht. + updatePolling() NotificationCenter.default.addObserver( self, selector: #selector(screensChanged), name: NSApplication.didChangeScreenParametersNotification, object: nil) @@ -150,39 +154,61 @@ public final class NotchCoordinator { } } - /// Fragt die Zeigerposition ab, solange ein Panel offen ist. + /// Fragt die Zeigerposition ab — immer, nur unterschiedlich oft. /// - /// Über dem eigenen Panel bekommt Onyx sonst gar nichts mit: der globale - /// Monitor feuert nur für Ereignisse, die an **fremde** Programme gehen, und - /// der lokale nur, wenn Onyx das aktive Programm ist — was es als - /// `.accessory`-App nie ist. Genau in dem Moment, in dem der Zeiger das - /// Panel wieder verlässt, käme das Ereignis also verspätet oder gar nicht, - /// und das Schließen wirkt hakelig. + /// **Warum überhaupt abgefragt wird und nicht nur zugehört:** /// - /// Abfragen ist hier das ehrlichere Mittel als noch mehr Monitore: es läuft - /// nur, während wirklich etwas offen ist, und kostet dabei einen - /// Rechteckvergleich pro Bild. + /// Über dem eigenen Panel bekommt Onyx über Ereignisse gar nichts mit: der + /// globale Monitor feuert nur für Ereignisse an **fremde** Programme, der + /// lokale nur, wenn Onyx das aktive Programm ist — was es als + /// `.accessory`-App nie ist. + /// + /// Und der Auslösebereich liegt in der Menüleiste. Dorthin stellt macOS + /// Mausbewegungen nicht verlässlich an fremde Programme zu: mal kommen sie + /// an, mal nicht, je nachdem, was gerade den Zeiger führt. Genau daher kam + /// das „manchmal geht die Notch nicht auf". Ein Verfahren, das eine + /// Zustellung voraussetzt, die es nicht gibt, kann nicht zuverlässig sein. + /// + /// Deshalb sind die Monitore nur noch Beschleunigung, und die Abfrage ist + /// die Wahrheit. Sie kostet einen Rechteckvergleich; bei zehn Abfragen je + /// Sekunde im Ruhezustand ist das nicht messbar. private func updatePolling() { let anyVisible = controllers.values.contains { $0.isVisible } + let interval = anyVisible ? Self.visiblePollInterval : Self.idlePollInterval + guard interval != pollInterval || pointerPollTimer == nil else { return } - if anyVisible, pointerPollTimer == nil { - let timer = Timer(timeInterval: 1.0 / 60.0, repeats: true) { _ in - MainActor.assumeIsolated { [weak self] in self?.pointerMoved() } - } - // `.common` statt der Standard-Schleife: sonst steht die Abfrage, - // während ein Menü offen ist oder gescrollt wird. - RunLoop.main.add(timer, forMode: .common) - pointerPollTimer = timer - } else if !anyVisible { - pointerPollTimer?.invalidate() - pointerPollTimer = nil + pointerPollTimer?.invalidate() + let timer = Timer(timeInterval: interval, repeats: true) { _ in + MainActor.assumeIsolated { [weak self] in self?.pointerMoved() } } + // `.common` statt der Standard-Schleife: sonst steht die Abfrage, + // während ein Menü offen ist oder gescrollt wird. + RunLoop.main.add(timer, forMode: .common) + pointerPollTimer = timer + pollInterval = interval } + /// Im Ruhezustand genügt ein Zehntel: dazu kommt ohnehin die Entprellzeit + /// von 220 ms, bevor sich etwas rührt. + private static let idlePollInterval: TimeInterval = 1.0 / 10.0 + /// Offen wird jedes Bild abgefragt, sonst wirkt das Schließen hakelig. + private static let visiblePollInterval: TimeInterval = 1.0 / 60.0 + private func refreshSuppression() { + // Einmal je Durchgang abfragen statt einmal je Bildschirm: die Sperre + // gilt für das ganze System. + let playing = NotchSuppression.isPreventingDisplaySleep for screen in NSScreen.screens { - controllers[screen.displayID]?.setSuppressed(screen.isShowingFullScreenWindow) + guard let controller = controllers[screen.displayID] else { continue } + let suppress = NotchSuppression.shouldSuppress( + hasFullScreenWindow: screen.isShowingFullScreenWindow, + preventsDisplaySleep: playing) + controller.setSuppressed(suppress) } + // Fällt die Unterdrückung weg, während der Zeiger schon in der Notch + // steht, käme sonst nichts — die Maschine wartet auf ein Ereignis, das + // erst bei der nächsten Bewegung kommt. + pointerMoved() } // MARK: - Zugriff für die App diff --git a/Packages/OnyxKit/Sources/OnyxNotch/NotchSuppression.swift b/Packages/OnyxKit/Sources/OnyxNotch/NotchSuppression.swift new file mode 100644 index 0000000..0b42cce --- /dev/null +++ b/Packages/OnyxKit/Sources/OnyxNotch/NotchSuppression.swift @@ -0,0 +1,36 @@ +import Foundation +import IOKit.pwr_mgt + +/// Wann das Panel wegbleibt. +public enum NotchSuppression { + + /// Beide Bedingungen zusammen, nicht eine davon. + /// + /// Die erste Fassung hat jedes Vollbildfenster als Grund gewertet. Damit war + /// die Notch tot, sobald jemand Xcode im Vollbild hatte — und das ist kein + /// Zustand, in dem man auf seinen Kalender verzichten will. Gemeint war + /// immer nur der Film über den ganzen Bildschirm. + public static func shouldSuppress(hasFullScreenWindow: Bool, + preventsDisplaySleep: Bool) -> Bool { + hasFullScreenWindow && preventsDisplaySleep + } + + /// Ob gerade etwas den Bildschirm wachhält. + /// + /// Das ist der verlässlichste Hinweis auf laufende Wiedergabe, den macOS + /// ohne Sonderrechte hergibt: jedes Programm, das ein Video abspielt, setzt + /// eine solche Sperre, damit der Bildschirm nicht mitten im Film dunkel + /// wird. Ein Textfenster im Vollbild tut das nicht. + public static var isPreventingDisplaySleep: Bool { + var assertions: Unmanaged? + guard IOPMCopyAssertionsStatus(&assertions) == kIOReturnSuccess, + let status = assertions?.takeRetainedValue() as? [String: Int] + else { return false } + + // Beide Namen kommen vor: der ältere von IOKit und der, den + // AVFoundation benutzt. + let names = [kIOPMAssertionTypeNoDisplaySleep as String, + kIOPMAssertionTypePreventUserIdleDisplaySleep as String] + return names.contains { (status[$0] ?? 0) > 0 } + } +} diff --git a/Packages/OnyxKit/Tests/OnyxNotchTests/SuppressionTests.swift b/Packages/OnyxKit/Tests/OnyxNotchTests/SuppressionTests.swift new file mode 100644 index 0000000..9338bb8 --- /dev/null +++ b/Packages/OnyxKit/Tests/OnyxNotchTests/SuppressionTests.swift @@ -0,0 +1,39 @@ +import Testing +@testable import OnyxNotch + +// Wann das Panel wegbleiben soll — und wann eben nicht. Die erste Fassung hat +// jedes Vollbildfenster als Grund gewertet. Damit war die Notch tot, sobald +// jemand Xcode im Vollbild hatte, und das ist kein Zustand, in dem man auf +// seinen Kalender verzichten will. + +@Suite("Unterdrückung des Panels") +struct SuppressionTests { + + @Test("Ein Vollbildvideo hält das Panel weg") + func fullScreenVideoSuppresses() { + #expect(NotchSuppression.shouldSuppress(hasFullScreenWindow: true, + preventsDisplaySleep: true)) + } + + @Test("Ein Vollbildfenster allein ist kein Grund") + func fullScreenWindowAloneIsNoReason() { + // Xcode, ein Browser, ein Editor — alles im Vollbild und alles + // Situationen, in denen man die Notch weiter benutzen will. + #expect(!NotchSuppression.shouldSuppress(hasFullScreenWindow: true, + preventsDisplaySleep: false)) + } + + @Test("Wiedergabe im Fenster ist auch kein Grund") + func windowedPlaybackIsNoReason() { + // Ein Video in einem Fenster verdeckt die Notch nicht — und wer währenddessen + // die Notch berührt, meint sie auch. + #expect(!NotchSuppression.shouldSuppress(hasFullScreenWindow: false, + preventsDisplaySleep: true)) + } + + @Test("Ohne beides bleibt das Panel erreichbar") + func normalDesktopIsFine() { + #expect(!NotchSuppression.shouldSuppress(hasFullScreenWindow: false, + preventsDisplaySleep: false)) + } +}