From a092c56871f47469f0212cbf4f395724aa27eb19 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Thu, 28 May 2026 13:37:36 +0200 Subject: [PATCH 01/91] openHABWidget: add interactive switch and sensor widgets Adds a widget extension with switch and sensor widgets. Switch widget: - Available in small (1 item), medium (2 items), and large (4 items) as separate widget registrations, so the configuration UI shows exactly the right number of item slots for the selected size - Interactive toggle with optimistic visual update on tap (Toggle with custom PowerButtonToggleStyle, not Button) - 600 ms pause in SetSwitchItemIntent.perform() prevents WidgetKit's immediate timeline refresh from racing openHAB's async command processing and overwriting the optimistic state flip - State label ('ON'/'OFF') lives inside the ToggleStyle so it always agrees with the button colour, never the lagging timeline entry - Medium/large use a row layout (bold label + compact circle toggle) with dividers; small shows a centred power button - Previews and the widget gallery work without a configured home by rendering a static non-interactive circle button Sensor widget: - Displays current state of Number/String items Common infrastructure: - Delete OpenHABWidgetLiveActivity.swift: importing ActivityKit without Live Activity entitlements caused an EXC_GUARD/LIBXPC crash at extension startup before any Swift code ran - SwitchSmall/Medium/LargeConfigurationAppIntent with matching SwitchSmall/Medium/LargeWidgetItemEntity types, each carrying an @IntentParameterDependency on its own configuration intent for home-filtered item selection in the picker - openHAB icon overlay inset 14 pt from leading/top edge on all sizes Signed-off-by: Tim Mueller-Seydlitz --- AppIntents/Intents/SetSwitchItemIntent.swift | 6 + AppIntents/OpenHABAppShortcutsProvider.swift | 102 ++++ AppIntents/SwitchItemEntity.swift | 22 +- AppIntents/iOS16/ActionMapper.swift | 48 -- AppIntents/iOS16/GetItemState.swift | 89 ---- AppIntents/iOS16/SetColorValue.swift | 144 ----- AppIntents/iOS16/SetContactStateValue.swift | 146 ------ AppIntents/iOS16/SetDateTimeValue.swift | 97 ---- AppIntents/iOS16/SetDimmerRollerValue.swift | 140 ----- AppIntents/iOS16/SetLocationValue.swift | 111 ---- AppIntents/iOS16/SetNumberValue.swift | 129 ----- AppIntents/iOS16/SetPlayerValue.swift | 117 ----- AppIntents/iOS16/SetStringValue.swift | 129 ----- AppIntents/iOS16/SetSwitchState.swift | 125 ----- CHANGELOG.md | 16 + CommonUI/Package.swift | 2 +- OpenHABCore/Package.swift | 2 +- .../OpenHABCore/Util/NWPathMonitoring.swift | 31 +- .../OpenHABCore/Util/WidgetItemRegistry.swift | 163 ++++++ Version.xcconfig | 4 +- openHAB.xcodeproj/project.pbxproj | 347 ++++++------ .../xcshareddata/swiftpm/Package.resolved | 2 +- openHAB/AppDelegate.swift | 7 + .../openHABIcon.imageset/Contents.json | 17 +- .../Supporting Files/Localizable.xcstrings | 82 +-- openHAB/UI/OpenHABWebViewController.swift | 35 +- openHAB/UI/ScreenSaver/ScreenSaverView.swift | 2 +- .../ApplicationSettingsView.swift | 2 +- .../UI/SettingsView/DebugSettingsView.swift | 4 +- .../UI/SwiftUI/Rows/ColorPickerRowView.swift | 4 +- .../Rows/ColorTemperaturePickerRowView.swift | 2 +- .../SwiftUI/Rows/DatePickerInputRowView.swift | 4 +- openHAB/UI/SwiftUI/Rows/ImageRowView.swift | 2 +- openHAB/UI/SwiftUI/Rows/MapRowView.swift | 109 ++-- .../UI/SwiftUI/Rows/SegmentedRowView.swift | 4 +- .../UI/SwiftUI/Rows/SelectionRowView.swift | 4 +- openHAB/UI/SwiftUI/Rows/SliderRowView.swift | 4 +- openHAB/UI/SwiftUI/Rows/SwitchRowView.swift | 2 +- .../UI/SwiftUI/Rows/TextInputRowView.swift | 4 +- openHAB/UI/SwiftUI/Rows/VideoRowView.swift | 2 +- .../UI/SwiftUI/SitemapNavigationView.swift | 100 +--- openHAB/UI/SwiftUI/ViewExtension.swift | 21 +- openHAB/WidgetItemMonitor.swift | 132 +++++ .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 35 ++ openHABWidget/Assets.xcassets/Contents.json | 6 + .../WidgetBackground.colorset/Contents.json | 11 + .../openHABIcon.imageset/Contents.json | 25 + .../openHABIcon.imageset/oh_logo_only.pdf | Bin 0 -> 4414 bytes .../oh_logo_only_dark.pdf | Bin 0 -> 4414 bytes openHABWidget/Info.plist | 11 + openHABWidget/OpenHABWidget.entitlements | 14 + openHABWidget/OpenHABWidgetBundle.swift | 23 + openHABWidget/OpenHABWidgetHelpers.swift | 31 ++ .../SensorConfigurationAppIntent.swift | 24 + openHABWidget/SensorWidgetEntryView.swift | 439 ++++++++++++++++ openHABWidget/SensorWidgetItemEntity.swift | 38 ++ openHABWidget/SensorWidgetView.swift | 39 ++ .../SwitchLargeConfigurationAppIntent.swift | 64 +++ openHABWidget/SwitchLargeWidget.swift | 81 +++ .../SwitchLargeWidgetItemEntity.swift | 38 ++ .../SwitchMediumWidgetItemEntity.swift | 38 ++ openHABWidget/SwitchWidgetEntryView.swift | 492 ++++++++++++++++++ openHABWidget/SwitchWidgetItemEntity.swift | 38 ++ .../WidgetConfigurationExtension.swift | 20 + 65 files changed, 2255 insertions(+), 1738 deletions(-) create mode 100644 AppIntents/OpenHABAppShortcutsProvider.swift delete mode 100644 AppIntents/iOS16/ActionMapper.swift delete mode 100644 AppIntents/iOS16/GetItemState.swift delete mode 100644 AppIntents/iOS16/SetColorValue.swift delete mode 100644 AppIntents/iOS16/SetContactStateValue.swift delete mode 100644 AppIntents/iOS16/SetDateTimeValue.swift delete mode 100644 AppIntents/iOS16/SetDimmerRollerValue.swift delete mode 100644 AppIntents/iOS16/SetLocationValue.swift delete mode 100644 AppIntents/iOS16/SetNumberValue.swift delete mode 100644 AppIntents/iOS16/SetPlayerValue.swift delete mode 100644 AppIntents/iOS16/SetStringValue.swift delete mode 100644 AppIntents/iOS16/SetSwitchState.swift create mode 100644 OpenHABCore/Sources/OpenHABCore/Util/WidgetItemRegistry.swift create mode 100644 openHAB/WidgetItemMonitor.swift create mode 100644 openHABWidget/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 openHABWidget/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 openHABWidget/Assets.xcassets/Contents.json create mode 100644 openHABWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json create mode 100644 openHABWidget/Assets.xcassets/openHABIcon.imageset/Contents.json create mode 100644 openHABWidget/Assets.xcassets/openHABIcon.imageset/oh_logo_only.pdf create mode 100644 openHABWidget/Assets.xcassets/openHABIcon.imageset/oh_logo_only_dark.pdf create mode 100644 openHABWidget/Info.plist create mode 100644 openHABWidget/OpenHABWidget.entitlements create mode 100644 openHABWidget/OpenHABWidgetBundle.swift create mode 100644 openHABWidget/OpenHABWidgetHelpers.swift create mode 100644 openHABWidget/SensorConfigurationAppIntent.swift create mode 100644 openHABWidget/SensorWidgetEntryView.swift create mode 100644 openHABWidget/SensorWidgetItemEntity.swift create mode 100644 openHABWidget/SensorWidgetView.swift create mode 100644 openHABWidget/SwitchLargeConfigurationAppIntent.swift create mode 100644 openHABWidget/SwitchLargeWidget.swift create mode 100644 openHABWidget/SwitchLargeWidgetItemEntity.swift create mode 100644 openHABWidget/SwitchMediumWidgetItemEntity.swift create mode 100644 openHABWidget/SwitchWidgetEntryView.swift create mode 100644 openHABWidget/SwitchWidgetItemEntity.swift create mode 100644 openHABWidget/WidgetConfigurationExtension.swift diff --git a/AppIntents/Intents/SetSwitchItemIntent.swift b/AppIntents/Intents/SetSwitchItemIntent.swift index c7fd3d04f..44f7e0a0b 100644 --- a/AppIntents/Intents/SetSwitchItemIntent.swift +++ b/AppIntents/Intents/SetSwitchItemIntent.swift @@ -74,6 +74,12 @@ struct SetSwitchItemIntent: AppIntent { throw ControlItemError.commandFailed(error.localizedDescription) } + // openHAB accepts commands asynchronously: it returns HTTP 200 before the item + // state has actually changed. Without a pause, WidgetKit's immediate timeline + // refresh races the server and fetches the pre-command state, overwriting the + // Toggle's optimistic visual flip and showing the wrong colour/text. + try? await Task.sleep(for: .milliseconds(600)) + return .result(dialog: "Sent \(action) to \(itemEntity.label)") } } diff --git a/AppIntents/OpenHABAppShortcutsProvider.swift b/AppIntents/OpenHABAppShortcutsProvider.swift new file mode 100644 index 000000000..f5fb5edf1 --- /dev/null +++ b/AppIntents/OpenHABAppShortcutsProvider.swift @@ -0,0 +1,102 @@ +// Copyright (c) 2010-2026 Contributors to the openHAB project +// +// See the NOTICE file(s) distributed with this work for additional +// information. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0 +// +// SPDX-License-Identifier: EPL-2.0 + +import AppIntents +internal import SFSafeSymbols + +/// App Shortcuts Provider for openHAB app +/// +/// This provider registers app shortcuts with iOS, enabling: +/// - Siri phrase suggestions (e.g., "Toggle Kitchen Light in openHAB") +/// - Shortcuts in Spotlight search +/// - Add to Home Screen functionality +/// - Siri Suggestions on lock screen and search +/// +/// ## Design Note +/// Due to iOS AppShortcuts limitation of one parameter per phrase, +/// shortcuts are designed for specific, common actions: +/// - Toggle switch items (most common switch operation) +/// - Get item state (universal query) +/// - Open/Close roller shutters (specific directional commands) +/// +/// The action is embedded in the phrase itself since we can only +/// pass the item name as a parameter. +/// +/// ## SF Symbol Names +/// Apple's AppShortcut requires literal strings for `systemImageName` - +/// constants and SFSafeSymbols cannot be used due to compile-time metadata extraction. +/// Symbol names use SF Symbols naming convention (e.g., "light.beacon.max"). +/// Reference: https://developer.apple.com/sf-symbols/ +/// +/// ## Note +/// This file was present in PR #742 but was missing from PR #1028. +/// Adding it restores iOS app shortcuts functionality. +/// +/// ## Usage +/// This struct is automatically discovered by iOS at build time. +/// No explicit registration is required - just having this file +/// in the app target is sufficient. +/// +/// ## References +/// - Apple Documentation: https://developer.apple.com/documentation/appintents/app-shortcuts +/// - PR #742: Original implementation +/// - PR #1028: Enhanced intent implementations +@available(iOS 17.0, macOS 13.0, watchOS 9.0, tvOS 16.0, *) +struct OpenHABAppShortcutsProvider: AppShortcutsProvider { + static var appShortcuts: [AppShortcut] { + // MARK: - Switch Control - Toggle Action + + // Note: Since AppShortcuts only allow one parameter per phrase, + // the action (toggle) is embedded in the phrase itself + + AppShortcut( + intent: SetSwitchItemIntent(), + phrases: [ + "Toggle \(\.$itemEntity) in \(.applicationName)", + "Toggle switch \(\.$itemEntity) in \(.applicationName)" + ], + shortTitle: "Toggle Switch", + systemImageName: "light.beacon.max" // SFSymbol: .lightBeaconMax + ) + + // Roller shutter and dimmer shortcuts are omitted: SetDimmerRollerValueIntent + // requires a value: Int parameter not present in the phrase, so Siri would + // prompt "what value?" after the phrase — poor UX. Dedicated open/close + // intents (no value parameter) would be needed to add shutter shortcuts. + } + + /// The color used for shortcut tiles in the Shortcuts app and Siri interface + /// Orange matches the openHAB brand color + static let shortcutTileColor: ShortcutTileColor = .orange +} + +//// MARK: - AppShortcut + SFSymbol Extension +// +// @available(iOS 16.0, macOS 13.0, watchOS 9.0, tvOS 16.0, *) +// extension AppShortcut { +// /// Creates an app shortcut with a type-safe SF Symbol. +// /// +// /// - Parameters: +// /// - intent: The intent to run when the shortcut is invoked. +// /// - phrases: The phrases that trigger this shortcut. +// /// - shortTitle: A short title displayed in the Shortcuts app. +// /// - systemImage: The SF Symbol to display for this shortcut. +// /// +// init(intent: Intent, phrases: [AppShortcutPhrase], shortTitle: LocalizedStringResource, systemImage: SFSymbol) where Intent : AppIntent { +// +// self.init( +// intent: intent, +// phrases: phrases, +// shortTitle: shortTitle, +// systemImageName: systemImage.rawValue +// ) +// } +// } diff --git a/AppIntents/SwitchItemEntity.swift b/AppIntents/SwitchItemEntity.swift index 0193c158c..9ce7e5033 100644 --- a/AppIntents/SwitchItemEntity.swift +++ b/AppIntents/SwitchItemEntity.swift @@ -14,7 +14,7 @@ import OpenHABCore // MARK: - DimmerItemEntity -@available(iOS 17.0, macOS 14.0, *) +@available(iOS 17.0, macOS 14.0, watchOS 10.0, *) struct DimmerItemEntity: ItemEntity { struct DimmerItemQuery: ItemEntityQuery { typealias EntityType = DimmerItemEntity @@ -31,7 +31,7 @@ struct DimmerItemEntity: ItemEntity { } } - static let typeDisplayRepresentation = TypeDisplayRepresentation(name: "Dimmer/Roller Item") + static let typeDisplayRepresentation = TypeDisplayRepresentation(name: "Dimmer or Roller Item") static let defaultQuery = DimmerItemQuery() var id: ItemIdentifier @@ -47,7 +47,7 @@ struct DimmerItemEntity: ItemEntity { // MARK: - ColorItemEntity -@available(iOS 17.0, macOS 14.0, *) +@available(iOS 17.0, macOS 14.0, watchOS 10.0, *) struct ColorItemEntity: ItemEntity { struct ColorItemQuery: ItemEntityQuery { typealias EntityType = ColorItemEntity @@ -80,7 +80,7 @@ struct ColorItemEntity: ItemEntity { // MARK: - NumberItemEntity -@available(iOS 17.0, macOS 14.0, *) +@available(iOS 17.0, macOS 14.0, watchOS 10.0, *) struct NumberItemEntity: ItemEntity { struct NumberItemQuery: ItemEntityQuery { typealias EntityType = NumberItemEntity @@ -113,7 +113,7 @@ struct NumberItemEntity: ItemEntity { // MARK: - StringItemEntity -@available(iOS 17.0, macOS 14.0, *) +@available(iOS 17.0, macOS 14.0, watchOS 10.0, *) struct StringItemEntity: ItemEntity { struct StringItemQuery: ItemEntityQuery { typealias EntityType = StringItemEntity @@ -146,7 +146,7 @@ struct StringItemEntity: ItemEntity { // MARK: - ContactItemEntity -@available(iOS 17.0, macOS 14.0, *) +@available(iOS 17.0, macOS 14.0, watchOS 10.0, *) struct ContactItemEntity: ItemEntity { struct ContactItemQuery: ItemEntityQuery { typealias EntityType = ContactItemEntity @@ -179,7 +179,7 @@ struct ContactItemEntity: ItemEntity { // MARK: - GenericItemEntity (for ItemStateIntent - all types) -@available(iOS 17.0, macOS 14.0, *) +@available(iOS 17.0, macOS 14.0, watchOS 10.0, *) struct GenericItemEntity: ItemEntity { struct GenericItemQuery: ItemEntityQuery { typealias EntityType = GenericItemEntity @@ -212,7 +212,7 @@ struct GenericItemEntity: ItemEntity { // MARK: - PlayerItemEntity -@available(iOS 17.0, macOS 14.0, *) +@available(iOS 17.0, macOS 14.0, watchOS 10.0, *) struct PlayerItemEntity: ItemEntity { struct PlayerItemQuery: ItemEntityQuery { typealias EntityType = PlayerItemEntity @@ -245,7 +245,7 @@ struct PlayerItemEntity: ItemEntity { // MARK: - DateTimeItemEntity -@available(iOS 17.0, macOS 14.0, *) +@available(iOS 17.0, macOS 14.0, watchOS 10.0, *) struct DateTimeItemEntity: ItemEntity { struct DateTimeItemQuery: ItemEntityQuery { typealias EntityType = DateTimeItemEntity @@ -278,7 +278,7 @@ struct DateTimeItemEntity: ItemEntity { // MARK: - LocationItemEntity -@available(iOS 17.0, macOS 14.0, *) +@available(iOS 17.0, macOS 14.0, watchOS 10.0, *) struct LocationItemEntity: ItemEntity { struct LocationItemQuery: ItemEntityQuery { typealias EntityType = LocationItemEntity @@ -311,7 +311,7 @@ struct LocationItemEntity: ItemEntity { // MARK: - SwitchItemEntity -@available(iOS 17.0, macOS 14.0, *) +@available(iOS 17.0, macOS 14.0, watchOS 10.0, *) struct SwitchItemEntity: ItemEntity { struct SwitchItemQuery: ItemEntityQuery { typealias EntityType = SwitchItemEntity diff --git a/AppIntents/iOS16/ActionMapper.swift b/AppIntents/iOS16/ActionMapper.swift deleted file mode 100644 index 719c423cc..000000000 --- a/AppIntents/iOS16/ActionMapper.swift +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) 2010-2026 Contributors to the openHAB project -// -// See the NOTICE file(s) distributed with this work for additional -// information. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0 -// -// SPDX-License-Identifier: EPL-2.0 - -import Foundation - -enum ActionMapper { - /// Provides the standard on/off action options for dynamic option providers - static var onOffOptions: [String] { - [ - String(localized: "on").capitalized, - String(localized: "off").capitalized - ] - } - - /// Provides on/off/toggle action options for dynamic option providers - static var onOffToggleOptions: [String] { - [ - String(localized: "on").capitalized, - String(localized: "off").capitalized, - String(localized: "toggle").capitalized - ] - } - - /// Maps a localized action label (e.g., "On", "Off", "Toggle") to an openHAB command (e.g., "ON", "OFF", "TOGGLE") - /// - Parameter localizedAction: The localized action string from user input - /// - Returns: The corresponding openHAB command, or nil if the action is not recognized - static func command(from localizedAction: String) -> String? { - let onLabel = String(localized: "on").capitalized - let offLabel = String(localized: "off").capitalized - let toggleLabel = String(localized: "toggle").capitalized - - let actionMap: [String: String] = [ - onLabel: "ON", - offLabel: "OFF", - toggleLabel: "TOGGLE" - ] - - return actionMap[localizedAction] - } -} diff --git a/AppIntents/iOS16/GetItemState.swift b/AppIntents/iOS16/GetItemState.swift deleted file mode 100644 index 5bdc02995..000000000 --- a/AppIntents/iOS16/GetItemState.swift +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) 2010-2026 Contributors to the openHAB project -// -// See the NOTICE file(s) distributed with this work for additional -// information. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0 -// -// SPDX-License-Identifier: EPL-2.0 - -import AppIntents -import Foundation -import OpenHABCore - -enum GetItemStateError: Error, CustomLocalizedStringResourceConvertible { - case itemNotFound(String) - - var localizedStringResource: LocalizedStringResource { - switch self { - case let .itemNotFound(itemName): - "Item '\(itemName)' not found" - } - } -} - -@available(iOS, introduced: 16.0, obsoleted: 17.0, message: "Use GetItemStateIntent for iOS 17+") -struct GetItemState: AppIntent, CustomIntentMigratedAppIntent, PredictableIntent { - struct StringOptionsProvider: DynamicOptionsProvider { - func results() async throws -> [String] { - await Preferences.prepareForAppExtensionAccess() - let allItems = await OpenHABItemCache.instance.getAllCachedItems() - return allItems.flatMap { $0.value.map(\.name) } - } - } - - static let intentClassName = "OpenHABGetItemStateIntent" - - static let title: LocalizedStringResource = "Get Item State" - static let description = IntentDescription("Retrieve the current state of an item") - - // swiftlint:disable type_contents_order - @Parameter(title: "Item", optionsProvider: StringOptionsProvider()) - var item: String - - @Parameter(title: "Home") - var home: Home? - - static var parameterSummary: some ParameterSummary { - Summary("Get \(\.$item) State") { - \.$home - } - } - - // swiftlint:enable type_contents_order - - static var predictionConfiguration: some IntentPredictionConfiguration { - IntentPrediction(parameters: (\.$item, \.$home)) { item, _ in - DisplayRepresentation( - title: "Get \(item) State", - subtitle: "" - ) - } - } - - func perform() async throws -> some IntentResult & ReturnsValue { - let homeId = try await HomeResolver.resolveHomeId( - selectedHome: home, - itemName: item - ) - - guard let openHABItem = await OpenHABItemCache.instance.getItemUncached(name: item, home: homeId) else { - throw GetItemStateError.itemNotFound(item) - } - - let state = openHABItem.state ?? "Unknown state" - - return .result( - value: state, - dialog: .responseSuccess(item: item, state: state) - ) - } -} - -private extension IntentDialog { - static func responseSuccess(item: String, state: String) -> Self { - "The state of \(item) is \(state)" - } -} diff --git a/AppIntents/iOS16/SetColorValue.swift b/AppIntents/iOS16/SetColorValue.swift deleted file mode 100644 index df363b8f3..000000000 --- a/AppIntents/iOS16/SetColorValue.swift +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (c) 2010-2026 Contributors to the openHAB project -// -// See the NOTICE file(s) distributed with this work for additional -// information. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0 -// -// SPDX-License-Identifier: EPL-2.0 - -import AppIntents -import Foundation -import OpenHABCore - -enum SetColorValueError: Error, CustomLocalizedStringResourceConvertible { - case itemNotFound(String) - case invalidValue(String, String) - case commandFailed(String) - - var localizedStringResource: LocalizedStringResource { - switch self { - case let .itemNotFound(itemName): - "Item '\(itemName)' not found" - case let .invalidValue(value, itemName): - "Invalid value: \(value) for \(itemName) must be HSB (0-360,0-100,0-100)" - case let .commandFailed(message): - "Command failed: \(message)" - } - } -} - -@available(iOS, introduced: 16.0, obsoleted: 17.0, message: "Use SetColorValueIntent for iOS 17+") -struct SetColorValue: AppIntent, CustomIntentMigratedAppIntent, PredictableIntent { - struct StringOptionsProvider: DynamicOptionsProvider { - func results() async throws -> [String] { - await Preferences.prepareForAppExtensionAccess() - let allItems = await OpenHABItemCache.instance.getAllCachedItems() - let items = allItems.flatMap(\.value).filter { $0.type == .color } - return items.map(\.name) - } - } - - static let intentClassName = "OpenHABSetColorValueIntent" - - static let title: LocalizedStringResource = "Set Color Control Value" - static let description = IntentDescription("Set the color of a color control item") - - // swiftlint:disable type_contents_order - @Parameter(title: "Item", optionsProvider: StringOptionsProvider()) - var item: String - - @Parameter(title: "Value", default: "240,100,100") - var value: String - - @Parameter(title: "Home") - var home: Home? - - static var parameterSummary: some ParameterSummary { - Summary("Set \(\.$item) to \(\.$value) (HSB)") { - \.$home - } - } - - // swiftlint:enable type_contents_order - - static var predictionConfiguration: some IntentPredictionConfiguration { - IntentPrediction(parameters: (\.$item, \.$value, \.$home)) { item, value, _ in - DisplayRepresentation( - title: "Set \(item) to \(value) (HSB)", - subtitle: "" - ) - } - } - - func perform() async throws -> some IntentResult & ProvidesDialog { - var colorValue = value - - let homeId = try await HomeResolver.resolveHomeId( - selectedHome: home, - itemName: item, - allowedTypes: [.color] - ) - - let hsb = colorValue.split(separator: ",") - guard hsb.count == 3, - let hue = Int(hsb[0]), (0 ... 360).contains(hue), - let sat = Int(hsb[1]), (0 ... 100).contains(sat), - let val = Int(hsb[2]), (0 ... 100).contains(val) else { - throw SetColorValueError.invalidValue(colorValue, item) - } - - colorValue = "\(hue),\(sat),\(val)" - - guard let items = await OpenHABItemCache.instance.getCachedItem(name: item, home: homeId), - !items.isEmpty else { - throw SetColorValueError.itemNotFound(item) - } - - let openHABItem = items[0] - - do { - try await OpenHABItemCache.instance.sendCommand(to: openHABItem, home: homeId, command: colorValue) - } catch { - throw SetColorValueError.commandFailed(error.localizedDescription) - } - - return .result(dialog: .responseSuccess(value: colorValue, item: item)) - } -} - -private extension IntentDialog { - static var itemParameterConfiguration: Self { - "Color Item Name" - } - - static var homeParameterConfiguration: Self { - "Home name" - } - - static var homeParameterDisambiguationSelection: Self { - "For which home do you want to get the value?" - } - - static func homeParameterDisambiguationIntro(count: Int, item: String) -> Self { - "There are \(count) configured homes with an item named '\(item)'." - } - - static func homeParameterConfirmation(home: Home) -> Self { - "Just to confirm, you wanted '\(home)'?" - } - - static func responseSuccess(value: String, item: String) -> Self { - "Sent the color value of \(value) to \(item)" - } - - static func responseFailureInvalidItem(item: String) -> Self { - "Sorry can't find \(item)" - } - - static func responseFailureInvalidValue(value: String, item: String) -> Self { - "Invalid value: \(value) for \(item) must be HSB (0-360,0-100,0-100)" - } -} diff --git a/AppIntents/iOS16/SetContactStateValue.swift b/AppIntents/iOS16/SetContactStateValue.swift deleted file mode 100644 index 7be3989d4..000000000 --- a/AppIntents/iOS16/SetContactStateValue.swift +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright (c) 2010-2026 Contributors to the openHAB project -// -// See the NOTICE file(s) distributed with this work for additional -// information. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0 -// -// SPDX-License-Identifier: EPL-2.0 - -import AppIntents -import Foundation -import OpenHABCore - -enum SetContactStateValueError: Error, CustomLocalizedStringResourceConvertible { - case itemNotFound(String) - case invalidState(String, String) - case commandFailed(String) - - var localizedStringResource: LocalizedStringResource { - switch self { - case let .itemNotFound(itemName): - "Item '\(itemName)' not found" - case let .invalidState(state, itemName): - "State invalid: \(state) for \(itemName)" - case let .commandFailed(message): - "Command failed: \(message)" - } - } -} - -@available(iOS, introduced: 16.0, obsoleted: 17.0, message: "Use ContactStateIntent for iOS 17+") -struct SetContactStateValue: AppIntent, CustomIntentMigratedAppIntent, PredictableIntent { - struct ItemOptionsProvider: DynamicOptionsProvider { - func results() async throws -> [String] { - await Preferences.prepareForAppExtensionAccess() - let allItems = await OpenHABItemCache.instance.getAllCachedItems() - let items = allItems.flatMap(\.value).filter { $0.type == .contact } - return items.map(\.name) - } - } - - struct StateOptionsProvider: DynamicOptionsProvider { - func results() throws -> [String] { - ActionMapper.onOffOptions - } - } - - static let intentClassName = "OpenHABSetContactStateValueIntent" - - static let title: LocalizedStringResource = "Set Contact State Value" - static let description = IntentDescription("Set the state of a contact open or closed") - - // swiftlint:disable type_contents_order - @Parameter(title: "Item", optionsProvider: ItemOptionsProvider()) - var item: String - - @Parameter(title: "State", optionsProvider: StateOptionsProvider()) - var state: String - - @Parameter(title: "Home") - var home: Home? - - static var parameterSummary: some ParameterSummary { - Summary("Set the state of \(\.$item) to \(\.$state)") { - \.$home - } - } - - // swiftlint:enable type_contents_order - - static var predictionConfiguration: some IntentPredictionConfiguration { - IntentPrediction(parameters: (\.$item, \.$state, \.$home)) { item, state, _ in - DisplayRepresentation( - title: "Set the state of \(item) to \(state)", - subtitle: "" - ) - } - } - - func perform() async throws -> some IntentResult & ProvidesDialog { - let homeId = try await HomeResolver.resolveHomeId( - selectedHome: home, - itemName: item, - allowedTypes: [.contact] - ) - - guard let realState = ActionMapper.command(from: state) else { - throw SetContactStateValueError.invalidState(state, item) - } - - guard let items = await OpenHABItemCache.instance.getCachedItem(name: item, home: homeId), - !items.isEmpty else { - throw SetContactStateValueError.itemNotFound(item) - } - - let openHABItem = items[0] - - do { - try await OpenHABItemCache.instance.sendCommand(to: openHABItem, home: homeId, command: realState) - } catch { - throw SetContactStateValueError.commandFailed(error.localizedDescription) - } - - return .result(dialog: .responseSuccess(item: item, state: state)) - } -} - -private extension IntentDialog { - static var itemParameterConfiguration: Self { - "Switch name" - } - - static var stateParameterConfiguration: Self { - "Action" - } - - static var homeParameterConfiguration: Self { - "Home name" - } - - static var homeParameterDisambiguationSelection: Self { - "For which home do you want to get the value?" - } - - static func homeParameterDisambiguationIntro(count: Int, item: String) -> Self { - "There are \(count) configured homes with an item named '\(item)'." - } - - static func homeParameterConfirmation(home: Home) -> Self { - "Just to confirm, you wanted '\(home)'?" - } - - static func responseSuccess(item: String, state: String) -> Self { - "The state of \(item) was set to \(state)" - } - - static func responseFailureInvalidItem(item: String) -> Self { - "Sorry can't find \(item)" - } - - static func responseFailureInvalidAction(state: String, item: String) -> Self { - "State invalid: \(state) for \(item)" - } -} diff --git a/AppIntents/iOS16/SetDateTimeValue.swift b/AppIntents/iOS16/SetDateTimeValue.swift deleted file mode 100644 index d8cca307e..000000000 --- a/AppIntents/iOS16/SetDateTimeValue.swift +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) 2010-2026 Contributors to the openHAB project -// -// See the NOTICE file(s) distributed with this work for additional -// information. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0 -// -// SPDX-License-Identifier: EPL-2.0 - -import AppIntents -import Foundation -import OpenHABCore - -enum SetDateTimeValueError: Error, CustomLocalizedStringResourceConvertible { - case itemNotFound(String) - case commandFailed(String) - - var localizedStringResource: LocalizedStringResource { - switch self { - case let .itemNotFound(itemName): - "Item '\(itemName)' not found" - case let .commandFailed(message): - "Command failed: \(message)" - } - } -} - -@available(iOS, introduced: 16.0, obsoleted: 17.0, message: "Use SetDateTimeValueIntent for iOS 17+") -struct SetDateTimeValue: AppIntent, PredictableIntent { - struct DateTimeOptionsProvider: DynamicOptionsProvider { - func results() async throws -> [String] { - await Preferences.prepareForAppExtensionAccess() - let allItems = await OpenHABItemCache.instance.getAllCachedItems() - let items = allItems.flatMap(\.value).filter { $0.type == .dateTime } - return items.map(\.name) - } - } - - static let title: LocalizedStringResource = "Set DateTime Control Value" - static let description = IntentDescription("Set the date and time of a DateTime control item") - - // swiftlint:disable type_contents_order - @Parameter(title: "Item", optionsProvider: DateTimeOptionsProvider()) - var item: String - - @Parameter(title: "Date and Time") - var value: Date - - @Parameter(title: "Home") - var home: Home? - - static var parameterSummary: some ParameterSummary { - Summary("Set \(\.$item) to \(\.$value)") { - \.$home - } - } - - // swiftlint:enable type_contents_order - - static var predictionConfiguration: some IntentPredictionConfiguration { - IntentPrediction(parameters: (\.$item, \.$value, \.$home)) { item, value, _ in - DisplayRepresentation( - title: "Set \(item) to \(value)", - subtitle: "" - ) - } - } - - func perform() async throws -> some IntentResult & ProvidesDialog { - let homeId = try await HomeResolver.resolveHomeId( - selectedHome: home, - itemName: item, - allowedTypes: [.dateTime] - ) - - guard let items = await OpenHABItemCache.instance.getCachedItem(name: item, home: homeId), - !items.isEmpty else { - throw SetDateTimeValueError.itemNotFound(item) - } - - let openHABItem = items[0] - - let formatter = ISO8601DateFormatter() - formatter.formatOptions = [.withInternetDateTime] - let command = formatter.string(from: value) - - do { - try await OpenHABItemCache.instance.sendCommand(to: openHABItem, home: homeId, command: command) - } catch { - throw SetDateTimeValueError.commandFailed(error.localizedDescription) - } - - return .result(dialog: "Sent the date \(command) to \(item)") - } -} diff --git a/AppIntents/iOS16/SetDimmerRollerValue.swift b/AppIntents/iOS16/SetDimmerRollerValue.swift deleted file mode 100644 index b86471f17..000000000 --- a/AppIntents/iOS16/SetDimmerRollerValue.swift +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright (c) 2010-2026 Contributors to the openHAB project -// -// See the NOTICE file(s) distributed with this work for additional -// information. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0 -// -// SPDX-License-Identifier: EPL-2.0 - -import AppIntents -import Foundation -import OpenHABCore - -enum SetDimmerRollerValueError: Error, CustomLocalizedStringResourceConvertible { - case itemNotFound(String) - case invalidValue(Int, String) - case commandFailed(String) - - var localizedStringResource: LocalizedStringResource { - switch self { - case let .itemNotFound(itemName): - "Item '\(itemName)' not found" - case let .invalidValue(value, itemName): - "Invalid value \(value) for \(itemName) (0-100)" - case let .commandFailed(message): - "Command failed: \(message)" - } - } -} - -@available(iOS, introduced: 16.0, obsoleted: 17.0, message: "Use SetDimmerRollerValueIntent for iOS 17+") -struct SetDimmerRollerValue: AppIntent, CustomIntentMigratedAppIntent, PredictableIntent { - struct StringOptionsProvider: DynamicOptionsProvider { - func results() async throws -> [String] { - await Preferences.prepareForAppExtensionAccess() - let allItems = await OpenHABItemCache.instance.getAllCachedItems() - let items = allItems.flatMap(\.value).filter { $0.type == .dimmer || $0.type == .rollershutter } - return items.map(\.name) - } - } - - static let intentClassName = "OpenHABSetDimmerRollerValueIntent" - - static let title: LocalizedStringResource = "Set Dimmer or Roller Shutter Value" - static let description = IntentDescription("Set the integer value of a dimmer or roller shutter") - - // swiftlint:disable type_contents_order - @Parameter(title: "Item", optionsProvider: StringOptionsProvider()) - var item: String - - @Parameter(title: "Value") - var value: Int - - @Parameter(title: "Home") - var home: Home? - - static var parameterSummary: some ParameterSummary { - Summary("Set \(\.$item) to \(\.$value)") { - \.$home - } - } - - // swiftlint:enable type_contents_order - - static var predictionConfiguration: some IntentPredictionConfiguration { - IntentPrediction(parameters: (\.$item, \.$value, \.$home)) { item, value, _ in - DisplayRepresentation( - title: "Set \(item) to \(value)", - subtitle: "" - ) - } - } - - func perform() async throws -> some IntentResult & ProvidesDialog { - let homeId = try await HomeResolver.resolveHomeId( - selectedHome: home, - itemName: item, - allowedTypes: [.dimmer, .rollershutter] - ) - - guard (0 ... 100).contains(value) else { - throw SetDimmerRollerValueError.invalidValue(value, item) - } - - guard let items = await OpenHABItemCache.instance.getCachedItem(name: item, home: homeId), - !items.isEmpty else { - throw SetDimmerRollerValueError.itemNotFound(item) - } - - let openHABItem = items[0] - - do { - try await OpenHABItemCache.instance.sendCommand(to: openHABItem, home: homeId, command: "\(value)") - } catch { - throw SetDimmerRollerValueError.commandFailed(error.localizedDescription) - } - - return .result(dialog: .responseSuccess(value: value, item: item)) - } -} - -private extension IntentDialog { - static var itemParameterConfiguration: Self { - "Dimmer/Roller Name" - } - - static var homeParameterConfiguration: Self { - "Home name" - } - - static var homeParameterDisambiguationSelection: Self { - "For which home do you want to get the value?" - } - - static func homeParameterDisambiguationIntro(count: Int, item: String) -> Self { - "There are \(count) configured homes with an item named '\(item)'." - } - - static func homeParameterConfirmation(home: Home) -> Self { - "Just to confirm, you wanted '\(home)'?" - } - - static func responseSuccess(value: Int, item: String) -> Self { - "Sent the value of \(value) to \(item)" - } - - static func responseFailureInvalidItem(item: String) -> Self { - "Sorry can't find \(item)" - } - - static func responseFailureEmptyValue(item: String) -> Self { - "Invalid empty value for \(item)" - } - - static func responseFailureInvalidValue(value: Int, item: String) -> Self { - "Invalid value \(value) for \(item) (0-100)" - } -} diff --git a/AppIntents/iOS16/SetLocationValue.swift b/AppIntents/iOS16/SetLocationValue.swift deleted file mode 100644 index faaef5d41..000000000 --- a/AppIntents/iOS16/SetLocationValue.swift +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) 2010-2026 Contributors to the openHAB project -// -// See the NOTICE file(s) distributed with this work for additional -// information. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0 -// -// SPDX-License-Identifier: EPL-2.0 - -import AppIntents -import Foundation -import OpenHABCore - -enum SetLocationValueError: Error, CustomLocalizedStringResourceConvertible { - case itemNotFound(String) - case invalidLatitude - case invalidLongitude - case commandFailed(String) - - var localizedStringResource: LocalizedStringResource { - switch self { - case let .itemNotFound(itemName): - "Item '\(itemName)' not found" - case .invalidLatitude: - "Latitude must be between -90 and 90" - case .invalidLongitude: - "Longitude must be between -180 and 180" - case let .commandFailed(message): - "Command failed: \(message)" - } - } -} - -@available(iOS, introduced: 16.0, obsoleted: 17.0, message: "Use SetLocationValueIntent for iOS 17+") -struct SetLocationValue: AppIntent, PredictableIntent { - struct LocationOptionsProvider: DynamicOptionsProvider { - func results() async throws -> [String] { - await Preferences.prepareForAppExtensionAccess() - let allItems = await OpenHABItemCache.instance.getAllCachedItems() - let items = allItems.flatMap(\.value).filter { $0.type == .location } - return items.map(\.name) - } - } - - static let title: LocalizedStringResource = "Set Location Control Value" - static let description = IntentDescription("Set the latitude and longitude of a location control item") - - // swiftlint:disable type_contents_order - @Parameter(title: "Item", optionsProvider: LocationOptionsProvider()) - var item: String - - @Parameter(title: "Latitude") - var latitude: Double - - @Parameter(title: "Longitude") - var longitude: Double - - @Parameter(title: "Home") - var home: Home? - - static var parameterSummary: some ParameterSummary { - Summary("Set \(\.$item) to \(\.$latitude), \(\.$longitude)") { - \.$home - } - } - - // swiftlint:enable type_contents_order - - static var predictionConfiguration: some IntentPredictionConfiguration { - IntentPrediction(parameters: (\.$item, \.$latitude, \.$longitude, \.$home)) { item, latitude, longitude, _ in - DisplayRepresentation( - title: "Set \(item) to \(latitude), \(longitude)", - subtitle: "" - ) - } - } - - func perform() async throws -> some IntentResult & ProvidesDialog { - let homeId = try await HomeResolver.resolveHomeId( - selectedHome: home, - itemName: item, - allowedTypes: [.location] - ) - - guard (-90 ... 90).contains(latitude) else { - throw SetLocationValueError.invalidLatitude - } - - guard (-180 ... 180).contains(longitude) else { - throw SetLocationValueError.invalidLongitude - } - - guard let items = await OpenHABItemCache.instance.getCachedItem(name: item, home: homeId), - !items.isEmpty else { - throw SetLocationValueError.itemNotFound(item) - } - - let openHABItem = items[0] - let command = "\(latitude),\(longitude)" - - do { - try await OpenHABItemCache.instance.sendCommand(to: openHABItem, home: homeId, command: command) - } catch { - throw SetLocationValueError.commandFailed(error.localizedDescription) - } - - return .result(dialog: "Sent location \(latitude), \(longitude) to \(item)") - } -} diff --git a/AppIntents/iOS16/SetNumberValue.swift b/AppIntents/iOS16/SetNumberValue.swift deleted file mode 100644 index c50c0725b..000000000 --- a/AppIntents/iOS16/SetNumberValue.swift +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (c) 2010-2026 Contributors to the openHAB project -// -// See the NOTICE file(s) distributed with this work for additional -// information. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0 -// -// SPDX-License-Identifier: EPL-2.0 - -import AppIntents -import Foundation -import OpenHABCore - -enum SetNumberValueError: Error, CustomLocalizedStringResourceConvertible { - case itemNotFound(String) - case commandFailed(String) - - var localizedStringResource: LocalizedStringResource { - switch self { - case let .itemNotFound(itemName): - "Item '\(itemName)' not found" - case let .commandFailed(message): - "Command failed: \(message)" - } - } -} - -@available(iOS, introduced: 16.0, obsoleted: 17.0, message: "Use SetNumberValueIntent for iOS 17+") -struct SetNumberValue: AppIntent, CustomIntentMigratedAppIntent, PredictableIntent { - struct StringOptionsProvider: DynamicOptionsProvider { - func results() async throws -> [String] { - await Preferences.prepareForAppExtensionAccess() - let allItems = await OpenHABItemCache.instance.getAllCachedItems() - let items = allItems.flatMap(\.value).filter { $0.type == .number || $0.type == .numberWithDimension } - return items.map(\.name) - } - } - - static let intentClassName = "OpenHABSetNumberValueIntent" - - static let title: LocalizedStringResource = "Set Number Control Value" - static let description = IntentDescription("Set the decimal value of a number control item") - - // swiftlint:disable type_contents_order - @Parameter(title: "Item", optionsProvider: StringOptionsProvider()) - var item: String - - @Parameter(title: "Value") - var value: Double - - @Parameter(title: "Home") - var home: Home? - - static var parameterSummary: some ParameterSummary { - Summary("Set \(\.$item) to \(\.$value)") { - \.$home - } - } - - // swiftlint:enable type_contents_order - - static var predictionConfiguration: some IntentPredictionConfiguration { - IntentPrediction(parameters: (\.$item, \.$value, \.$home)) { item, value, _ in - DisplayRepresentation( - title: "Set \(item) to \(value)", - subtitle: "" - ) - } - } - - func perform() async throws -> some IntentResult & ProvidesDialog { - let homeId = try await HomeResolver.resolveHomeId( - selectedHome: home, - itemName: item, - allowedTypes: [.number, .numberWithDimension] - ) - - guard let items = await OpenHABItemCache.instance.getCachedItem(name: item, home: homeId), - !items.isEmpty else { - throw SetNumberValueError.itemNotFound(item) - } - - let openHABItem = items[0] - - do { - try await OpenHABItemCache.instance.sendCommand(to: openHABItem, home: homeId, command: NumberState(value: value).commandString) - } catch { - throw SetNumberValueError.commandFailed(error.localizedDescription) - } - - return .result(dialog: .responseSuccess(value: value, item: item)) - } -} - -private extension IntentDialog { - static var itemParameterConfiguration: Self { - "Number Item Name" - } - - static var homeParameterConfiguration: Self { - "Home name" - } - - static var homeParameterDisambiguationSelection: Self { - "For which home do you want to get the value?" - } - - static func homeParameterDisambiguationIntro(count: Int, item: String) -> Self { - "There are \(count) configured homes with an item named '\(item)'." - } - - static func homeParameterConfirmation(home: Home) -> Self { - "Just to confirm, you wanted '\(home)'?" - } - - static func responseSuccess(value: Double, item: String) -> Self { - "Sent the number \(value) to \(item)" - } - - static func responseFailureInvalidItem(item: String) -> Self { - "Sorry can't find \(item)" - } - - static func responseFailureEmptyValue(item: String) -> Self { - "Invalid empty value for \(item)" - } -} diff --git a/AppIntents/iOS16/SetPlayerValue.swift b/AppIntents/iOS16/SetPlayerValue.swift deleted file mode 100644 index 053a707a5..000000000 --- a/AppIntents/iOS16/SetPlayerValue.swift +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright (c) 2010-2026 Contributors to the openHAB project -// -// See the NOTICE file(s) distributed with this work for additional -// information. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0 -// -// SPDX-License-Identifier: EPL-2.0 - -import AppIntents -import Foundation -import OpenHABCore - -@available(iOS, introduced: 16.0, obsoleted: 17.0) -enum SetPlayerValueError: Error, CustomLocalizedStringResourceConvertible { - case itemNotFound(String) - case invalidAction(String, String) - case commandFailed(String) - - var localizedStringResource: LocalizedStringResource { - switch self { - case let .itemNotFound(itemName): - "Item '\(itemName)' not found" - case let .invalidAction(action, itemName): - "Action invalid: \(action) for \(itemName)" - case let .commandFailed(message): - "Command failed: \(message)" - } - } -} - -@available(iOS, introduced: 16.0, obsoleted: 17.0, message: "Use SetPlayerValueIntent for iOS 17+") -struct SetPlayerValue: AppIntent, PredictableIntent { - // swiftlint:disable type_contents_order - - static let title: LocalizedStringResource = "Set Player Control Value" - static let description = IntentDescription("Send a player command such as play, pause, next, or previous") - - @Parameter(title: "Home") - var home: Home? - - struct ItemOptionsProvider: DynamicOptionsProvider { - func results() async throws -> [String] { - await Preferences.prepareForAppExtensionAccess() - let allItems = await OpenHABItemCache.instance.getAllCachedItems() - let items = allItems.flatMap(\.value).filter { $0.type == .player } - return items.map(\.name) - } - } - - @Parameter(title: "Item", optionsProvider: ItemOptionsProvider()) - var item: String - - struct ActionOptionsProvider: DynamicOptionsProvider { - func results() throws -> [String] { - ["Play", "Pause", "Next", "Previous", "Rewind", "Fast Forward"] - } - } - - @Parameter(title: "Action", optionsProvider: ActionOptionsProvider()) - var action: String - - static var parameterSummary: some ParameterSummary { - Summary("Send \(\.$action) to \(\.$item)") { - \.$home - } - } - - // swiftlint:enable type_contents_order - - static var predictionConfiguration: some IntentPredictionConfiguration { - IntentPrediction(parameters: (\.$item, \.$action, \.$home)) { item, action, _ in - DisplayRepresentation( - title: "Send \(action) to \(item)", - subtitle: "" - ) - } - } - - private static let actionMap: [String: String] = [ - "Play": "PLAY", - "Pause": "PAUSE", - "Next": "NEXT", - "Previous": "PREVIOUS", - "Rewind": "REWIND", - "Fast Forward": "FASTFORWARD" - ] - - func perform() async throws -> some IntentResult & ProvidesDialog { - let homeId = try await HomeResolver.resolveHomeId( - selectedHome: home, - itemName: item, - allowedTypes: [.player] - ) - - guard let command = Self.actionMap[action] else { - throw SetPlayerValueError.invalidAction(action, item) - } - - guard let items = await OpenHABItemCache.instance.getCachedItem(name: item, home: homeId), - !items.isEmpty else { - throw SetPlayerValueError.itemNotFound(item) - } - - let openHABItem = items[0] - - do { - try await OpenHABItemCache.instance.sendCommand(to: openHABItem, home: homeId, command: command) - } catch { - throw SetPlayerValueError.commandFailed(error.localizedDescription) - } - - return .result(dialog: "Sent the action of \(action) to player \(item)") - } -} diff --git a/AppIntents/iOS16/SetStringValue.swift b/AppIntents/iOS16/SetStringValue.swift deleted file mode 100644 index 492fafaa0..000000000 --- a/AppIntents/iOS16/SetStringValue.swift +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (c) 2010-2026 Contributors to the openHAB project -// -// See the NOTICE file(s) distributed with this work for additional -// information. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0 -// -// SPDX-License-Identifier: EPL-2.0 - -import AppIntents -import Foundation -import OpenHABCore - -enum SetStringValueError: Error, CustomLocalizedStringResourceConvertible { - case itemNotFound(String) - case commandFailed(String) - - var localizedStringResource: LocalizedStringResource { - switch self { - case let .itemNotFound(itemName): - "Item '\(itemName)' not found" - case let .commandFailed(message): - "Command failed: \(message)" - } - } -} - -@available(iOS, introduced: 16.0, obsoleted: 17.0, message: "Use SetStringValueIntent for iOS 17+") -struct SetStringValue: AppIntent, CustomIntentMigratedAppIntent, PredictableIntent { - struct StringOptionsProvider: DynamicOptionsProvider { - func results() async throws -> [String] { - await Preferences.prepareForAppExtensionAccess() - let allItems = await OpenHABItemCache.instance.getAllCachedItems() - let items = allItems.flatMap(\.value).filter { $0.type == .stringItem } - return items.map(\.name) - } - } - - static let intentClassName = "OpenHABSetStringValueIntent" - - static let title: LocalizedStringResource = "Set String Control Value" - static let description = IntentDescription("Set the string of a string control item") - - // swiftlint:disable type_contents_order - @Parameter(title: "Item", optionsProvider: StringOptionsProvider()) - var item: String - - @Parameter(title: "Value") - var value: String - - @Parameter(title: "Home") - var home: Home? - - static var parameterSummary: some ParameterSummary { - Summary("Set \(\.$item) to \(\.$value)") { - \.$home - } - } - - // swiftlint:enable type_contents_order - - static var predictionConfiguration: some IntentPredictionConfiguration { - IntentPrediction(parameters: (\.$item, \.$value, \.$home)) { item, value, _ in - DisplayRepresentation( - title: "Set \(item) to \(value)", - subtitle: "" - ) - } - } - - func perform() async throws -> some IntentResult & ProvidesDialog { - let homeId = try await HomeResolver.resolveHomeId( - selectedHome: home, - itemName: item, - allowedTypes: [.stringItem] - ) - - guard let items = await OpenHABItemCache.instance.getCachedItem(name: item, home: homeId), - !items.isEmpty else { - throw SetStringValueError.itemNotFound(item) - } - - let openHABItem = items[0] - - do { - try await OpenHABItemCache.instance.sendCommand(to: openHABItem, home: homeId, command: value) - } catch { - throw SetStringValueError.commandFailed(error.localizedDescription) - } - - return .result(dialog: .responseSuccess(value: value, item: item)) - } -} - -private extension IntentDialog { - static var itemParameterConfiguration: Self { - "String Item Name" - } - - static var homeParameterConfiguration: Self { - "Home name" - } - - static var homeParameterDisambiguationSelection: Self { - "For which home do you want to get the value?" - } - - static func homeParameterDisambiguationIntro(count: Int, item: String) -> Self { - "There are \(count) configured homes with an item named '\(item)'." - } - - static func homeParameterConfirmation(home: Home) -> Self { - "Just to confirm, you wanted '\(home)'?" - } - - static func responseSuccess(value: String, item: String) -> Self { - "Sent the string \(value) to \(item)" - } - - static func responseFailureInvalidItem(item: String) -> Self { - "Sorry can't find \(item)" - } - - static func responseFailureEmptyValue(item: String) -> Self { - "Invalid empty value for \(item)" - } -} diff --git a/AppIntents/iOS16/SetSwitchState.swift b/AppIntents/iOS16/SetSwitchState.swift deleted file mode 100644 index 9143a130e..000000000 --- a/AppIntents/iOS16/SetSwitchState.swift +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright (c) 2010-2026 Contributors to the openHAB project -// -// See the NOTICE file(s) distributed with this work for additional -// information. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0 -// -// SPDX-License-Identifier: EPL-2.0 - -import AppIntents -import Foundation -import OpenHABCore - -@available(iOS, introduced: 16.0, obsoleted: 17.0) -enum SetSwitchStateError: Error, CustomLocalizedStringResourceConvertible { - case itemNotFound(String) - case invalidAction(String, String) - case itemNotInHome(String, String) - case commandFailed(String) - - var localizedStringResource: LocalizedStringResource { - switch self { - case let .itemNotFound(itemName): - "Item '\(itemName)' not found" - case let .invalidAction(action, itemName): - "Action invalid: \(action) for \(itemName)" - case let .itemNotInHome(itemName, homeName): - "Item '\(itemName)' is not in home '\(homeName)'" - case let .commandFailed(message): - "Command failed: \(message)" - } - } -} - -// The @available(obsoleted: 17.0) annotation is intentionally absent: CustomIntentMigratedAppIntent -// requires this struct to be available on iOS 17+ so that stored shortcuts using the legacy -// SiriKit intent class (intentClassName) are migrated to this App Intent at runtime. Restricting -// availability to iOS 16 would break that migration path for iOS 17+ users. The duplicate entry -// in the Shortcuts picker is an accepted trade-off until migration is confirmed complete. -struct SetSwitchState: AppIntent, CustomIntentMigratedAppIntent, PredictableIntent { - // swiftlint:disable type_contents_order - - static let intentClassName = "OpenHABSetSwitchStateIntent" - static let title: LocalizedStringResource = "Set Switch State" - static let description = IntentDescription("Set the state of a switch on or off") - - @Parameter(title: "Home") - var home: Home? - - struct ItemOptionsProvider: DynamicOptionsProvider { - func results() async throws -> [String] { - await Preferences.prepareForAppExtensionAccess() - let allItems = await OpenHABItemCache.instance.getAllCachedItems() - let items = allItems.flatMap(\.value).filter { $0.type == .switchItem } - return items.map(\.name) - } - } - - @Parameter(title: "Item", optionsProvider: ItemOptionsProvider()) - var item: String - - struct ActionOptionsProvider: DynamicOptionsProvider { - func results() throws -> [String] { - ActionMapper.onOffOptions - } - } - - @Parameter(title: "Action", optionsProvider: ActionOptionsProvider()) - var action: String - - static var parameterSummary: some ParameterSummary { - Summary("Send \(\.$action) to \(\.$item)") { - \.$home - } - } - - // swiftlint:enable type_contents_order - - static var predictionConfiguration: some IntentPredictionConfiguration { - IntentPrediction(parameters: (\.$item, \.$action, \.$home)) { item, action, _ in - DisplayRepresentation( - title: "Send \(action) to \(item)", - subtitle: "" - ) - } - } - - func perform() async throws -> some IntentResult & ProvidesDialog { - let homeId = try await HomeResolver.resolveHomeId( - selectedHome: home, - itemName: item, - allowedTypes: [.switchItem] - ) - - guard let command = ActionMapper.command(from: action) else { - throw SetSwitchStateError.invalidAction(action, item) - } - guard let items = await OpenHABItemCache.instance.getCachedItem(name: item, home: homeId), - !items.isEmpty else { - throw SetSwitchStateError.itemNotFound(item) - } - - let openHABItem = items[0] - - do { - try await OpenHABItemCache.instance.sendCommand(to: openHABItem, home: homeId, command: command) - } catch { - throw SetSwitchStateError.commandFailed(error.localizedDescription) - } - - return .result(dialog: .responseSuccess(action: action, item: item)) - } -} - -private extension IntentDialog { - static func responseSuccess(action: String, item: String) -> Self { - "Sent the action of \(action) to switch \(item)" - } - - static func responseFailureInvalidAction(action: String, item: String) -> Self { - "Action invalid: \(action) for \(item)" - } -} diff --git a/CHANGELOG.md b/CHANGELOG.md index 7681e653b..bd2185bb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,22 @@ ## [Unreleased] +## [Version 3.2.60, Build 269] - 2026-05-27Z + +- ConnectionView: align icon with list rows, scale with Dynamic Type +- Fix home switching via Shortcuts not working +- UserDefaultsTests: use .isEmpty instead of == "" +- ConnectionView: drive currentHomeName from MainActorNetworkTracker +- AppIntents: add SetActiveHomeIntent.swift to openHAB target +- Remove iOS 16 SetActiveHome intent — iOS 17+ only +- Add App Intent to set the active home via Shortcuts +- NetworkTracker: expand single-line for-loops for SwiftLint compliance +- DrawerView: show home name and connection type instead of URL +- Store connection credentials in Keychain instead of UserDefaults +- Converge iOS/watchOS sitemap polling on shared SitemapPageLoader (#1218) +- Fix crash: AVAudioPlayer deallocated on background thread in AudioPlayerActor +- Fix IPv6 zone ID rejected by URL validation regex + ## [Version 3.2.59, Build 268] - 2026-05-27Z - Enable App Intents on watchOS with multi-home support (#1219) diff --git a/CommonUI/Package.swift b/CommonUI/Package.swift index 4b60695b3..4018f6eb2 100644 --- a/CommonUI/Package.swift +++ b/CommonUI/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "CommonUI", - platforms: [.iOS(.v16), .watchOS(.v10), .macOS(.v14)], + platforms: [.iOS(.v17), .watchOS(.v10), .macOS(.v14)], products: [ // Products define the executables and libraries a package produces, making them visible to other packages. diff --git a/OpenHABCore/Package.swift b/OpenHABCore/Package.swift index 82c3b7cad..fe4f3f64f 100644 --- a/OpenHABCore/Package.swift +++ b/OpenHABCore/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "OpenHABCore", - platforms: [.iOS(.v16), .watchOS(.v10), .macOS(.v14)], + platforms: [.iOS(.v17), .watchOS(.v10), .macOS(.v14)], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( diff --git a/OpenHABCore/Sources/OpenHABCore/Util/NWPathMonitoring.swift b/OpenHABCore/Sources/OpenHABCore/Util/NWPathMonitoring.swift index fc802cb89..fd4e3fbc7 100644 --- a/OpenHABCore/Sources/OpenHABCore/Util/NWPathMonitoring.swift +++ b/OpenHABCore/Sources/OpenHABCore/Util/NWPathMonitoring.swift @@ -22,15 +22,9 @@ final class RealPathMonitor: NWPathMonitoring, Sendable { } func startMonitoring(handler: @escaping (Bool) async -> Void) async { - if #available(iOS 17, watchOS 10, *) { - for await path in monitor { - Logger.nwPathMonitoring.debug("Path monitor update: \(path.debugDescription)") - await handler(path.status == .satisfied || path.status == .requiresConnection) - } - } else { - for await path in monitor.paths() { - await handler(path.status == .satisfied || path.status == .requiresConnection) - } + for await path in monitor { + Logger.nwPathMonitoring.debug("Path monitor update: \(path.debugDescription)") + await handler(path.status == .satisfied || path.status == .requiresConnection) } } @@ -47,22 +41,3 @@ public protocol NWPathMonitoring: AnyObject, Sendable { func startMonitoring(handler: @escaping (Bool) async -> Void) async func cancel() } - -// MARK: Extension for version iOS <17 - -// this line breaks availability checking, since watchos 10 is minimum for the app -// @available(watchOS, obsoleted: 10.0) -@available(iOS, obsoleted: 17.0) -extension NWPathMonitor { - func paths() -> AsyncStream { - AsyncStream { continuation in - pathUpdateHandler = { path in - continuation.yield(path) - } - continuation.onTermination = { [weak self] _ in - self?.cancel() - } - start(queue: DispatchQueue(label: "NSPathMonitor.paths")) - } - } -} diff --git a/OpenHABCore/Sources/OpenHABCore/Util/WidgetItemRegistry.swift b/OpenHABCore/Sources/OpenHABCore/Util/WidgetItemRegistry.swift new file mode 100644 index 000000000..8ee987af5 --- /dev/null +++ b/OpenHABCore/Sources/OpenHABCore/Util/WidgetItemRegistry.swift @@ -0,0 +1,163 @@ +// Copyright (c) 2010-2026 Contributors to the openHAB project +// +// See the NOTICE file(s) distributed with this work for additional +// information. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0 +// +// SPDX-License-Identifier: EPL-2.0 + +import Foundation +import os.log + +/// Manages tracking of which openHAB items have active widgets. +/// Uses shared UserDefaults to communicate between the app and widget extension. +public actor WidgetItemRegistry { + public static let shared = WidgetItemRegistry() + + private let defaults: UserDefaults? + private let itemsKey = "widgetTrackedItems" + private let timestampsKey = "widgetTrackedItemTimestamps" + + private init() { + defaults = UserDefaults(suiteName: "group.org.openhab.app") + } + + /// Register an item as being displayed in a widget + /// - Parameters: + /// - itemName: The name of the openHAB item + /// - homeId: The UUID of the home containing this item + public func registerItem(name itemName: String, homeId: UUID) { + guard let defaults else { + Logger.widgets.error("Failed to access shared UserDefaults") + return + } + + var items = getRegisteredItems() + let key = "\(homeId.uuidString):\(itemName)" + items.insert(key) + + // Update timestamp for this item + var timestamps = getTimestamps() + timestamps[key] = Date() + + defaults.set(Array(items), forKey: itemsKey) + setTimestamps(timestamps) + Logger.widgets.info("Registered widget item: \(itemName) for home \(homeId)") + } + + /// Unregister an item (when widget is removed) + /// - Parameters: + /// - itemName: The name of the openHAB item + /// - homeId: The UUID of the home containing this item + public func unregisterItem(name itemName: String, homeId: UUID) { + guard let defaults else { + Logger.widgets.error("Failed to access shared UserDefaults") + return + } + + var items = getRegisteredItems() + let key = "\(homeId.uuidString):\(itemName)" + items.remove(key) + + // Remove timestamp for this item + var timestamps = getTimestamps() + timestamps.removeValue(forKey: key) + + defaults.set(Array(items), forKey: itemsKey) + setTimestamps(timestamps) + Logger.widgets.info("Unregistered widget item: \(itemName) for home \(homeId)") + } + + /// Get all registered items grouped by home + /// - Returns: Dictionary mapping home UUIDs to arrays of item names + public func getItemsByHome() -> [UUID: [String]] { + let items = getRegisteredItems() + var result: [UUID: [String]] = [:] + + for item in items { + let components = item.split(separator: ":", maxSplits: 1) + guard components.count == 2, + let homeId = UUID(uuidString: String(components[0])) else { + continue + } + + let itemName = String(components[1]) + result[homeId, default: []].append(itemName) + } + + return result + } + + /// Get all registered item names (without home grouping) + /// - Returns: Array of all item names that have widgets + public func getAllItemNames() -> [String] { + let itemsByHome = getItemsByHome() + return Array(Set(itemsByHome.values.flatMap(\.self))) + } + + /// Clear all registered items (useful for testing or cleanup) + public func clearAll() { + defaults?.removeObject(forKey: itemsKey) + defaults?.removeObject(forKey: timestampsKey) + Logger.widgets.info("Cleared all registered widget items") + } + + /// Remove items that haven't been updated recently (stale widgets) + /// - Parameter olderThan: Time interval in seconds (default: 1 hour) + /// - Returns: Number of items removed + @discardableResult + public func removeStaleItems(olderThan interval: TimeInterval = 3600) -> Int { + guard let defaults else { return 0 } + + let cutoffDate = Date().addingTimeInterval(-interval) + var items = getRegisteredItems() + var timestamps = getTimestamps() + + var removedCount = 0 + var timestampsUpdated = false + + for itemKey in Array(items) { + // If no timestamp exists or timestamp is too old, remove it + if let timestamp = timestamps[itemKey], timestamp < cutoffDate { + items.remove(itemKey) + timestamps.removeValue(forKey: itemKey) + removedCount += 1 + Logger.widgets.debug("Removed stale widget item: \(itemKey)") + } else if timestamps[itemKey] == nil { + // Item has no timestamp, probably from old version - keep it but add timestamp + timestamps[itemKey] = Date() + timestampsUpdated = true + } + } + + if removedCount > 0 || timestampsUpdated { + defaults.set(Array(items), forKey: itemsKey) + setTimestamps(timestamps) + Logger.widgets.info("Removed \(removedCount) stale widget items") + } + + return removedCount + } + + private func getRegisteredItems() -> Set { + guard let defaults else { return [] } + let items = defaults.stringArray(forKey: itemsKey) ?? [] + return Set(items) + } + + private func getTimestamps() -> [String: Date] { + guard let defaults else { return [:] } + guard let data = defaults.data(forKey: timestampsKey) else { return [:] } + return (try? JSONDecoder().decode([String: Date].self, from: data)) ?? [:] + } + + private func setTimestamps(_ timestamps: [String: Date]) { + guard let defaults else { return } + if let data = try? JSONEncoder().encode(timestamps) { + defaults.set(data, forKey: timestampsKey) + } + } +} diff --git a/Version.xcconfig b/Version.xcconfig index 851c7d1f1..4a8ba3624 100644 --- a/Version.xcconfig +++ b/Version.xcconfig @@ -6,5 +6,5 @@ // https://developer.apple.com/documentation/xcode/adding-a-build-configuration-file-to-your-project -MARKETING_VERSION = 3.2.59 -CURRENT_PROJECT_VERSION = 268 +MARKETING_VERSION = 3.2.60 +CURRENT_PROJECT_VERSION = 269 diff --git a/openHAB.xcodeproj/project.pbxproj b/openHAB.xcodeproj/project.pbxproj index db082c2ed..0b9673c79 100644 --- a/openHAB.xcodeproj/project.pbxproj +++ b/openHAB.xcodeproj/project.pbxproj @@ -7,25 +7,9 @@ objects = { /* Begin PBXBuildFile section */ - 9F54768418A14674AD9E2FCA /* ContactState.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4042F06F5CA0082479C /* ContactState.swift */; }; - 3D3B2898C51F48BAA31210E4 /* ContactStateIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3552F03095D0082479C /* ContactStateIntent.swift */; }; - 791AFA47B71D4B6995369F3A /* GetItemStateIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3512F0307A40082479C /* GetItemStateIntent.swift */; }; - 5B292637B6E141DB98BCAA26 /* Home.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA448E772EF435B400F0893C /* Home.swift */; }; - B2FC662F25B2461887BB67DA /* ItemEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4332F0705580082479C /* ItemEntity.swift */; }; - 3F087E4025E845FFBCBE45E3 /* ItemEntityQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4352F07056F0082479C /* ItemEntityQuery.swift */; }; - C9DE79C32AF24FFEA0280115 /* ItemIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD5E85D2F02C3BC003215C0 /* ItemIdentifier.swift */; }; - 0648F50178784F149905E7CF /* PlayerAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4062F0800010082479C /* PlayerAction.swift */; }; - 1E4E7DB36BAC4804AD5E58E5 /* SetColorValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3542F03095D0082479C /* SetColorValueIntent.swift */; }; - BF0EB6DE70934966B1DC5479 /* SetDateTimeValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF40A2F0800030082479C /* SetDateTimeValueIntent.swift */; }; - E120F4A14B9942449F25E788 /* SetDimmerRollerValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3582F03098B0082479C /* SetDimmerRollerValueIntent.swift */; }; - CE7A58C0ED3A4A1ABCB20337 /* SetLocationValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF40C2F0800040082479C /* SetLocationValueIntent.swift */; }; - 61A606E5E6AE404584B0B1A0 /* SetNumberValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF35A2F0309A60082479C /* SetNumberValueIntent.swift */; }; - 598208789AC44F1D91DD5B17 /* SetPlayerValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4082F0800020082479C /* SetPlayerValueIntent.swift */; }; - 1C1CA8C8C8424BAF8018F9BE /* SetStringValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF35C2F0309B10082479C /* SetStringValueIntent.swift */; }; - 73F094A3C17641738D6215CD /* SetSwitchItemIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD5E85B2F02C272003215C0 /* SetSwitchItemIntent.swift */; }; - CF650A22059A4A00823EE656 /* SwitchAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4022F06F5950082479C /* SwitchAction.swift */; }; - 970584BAA2884C8287297BF6 /* SwitchItemEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAF58C7A2EF6E99500483AFD /* SwitchItemEntity.swift */; }; + 2AE557B26AF10D16E65EB541 /* openHABWidgetExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 396B3EC10279D6D55CD888E8 /* openHABWidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 2F399AC92F54599400F72A30 /* Flow in Frameworks */ = {isa = PBXBuildFile; productRef = 2F399AC82F54599400F72A30 /* Flow */; }; + 5111C4CC62E66D2FFA5C92EB /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 06A8490213945021806A13D1 /* SwiftUI.framework */; }; 6557AF8F2C0241C10094D0C8 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 6557AF8E2C0241C10094D0C8 /* PrivacyInfo.xcprivacy */; }; 6557AF902C0241C10094D0C8 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 6557AF8E2C0241C10094D0C8 /* PrivacyInfo.xcprivacy */; }; 6557AF922C039D140094D0C8 /* FirebaseMessaging in Frameworks */ = {isa = PBXBuildFile; productRef = 6557AF912C039D140094D0C8 /* FirebaseMessaging */; }; @@ -45,25 +29,13 @@ 93F8064727AE7A050035A6B0 /* SwiftMessages in Frameworks */ = {isa = PBXBuildFile; productRef = 93F8064627AE7A050035A6B0 /* SwiftMessages */; }; 93F8064A27AE7A2E0035A6B0 /* FlexColorPicker in Frameworks */ = {isa = PBXBuildFile; productRef = 93F8064927AE7A2E0035A6B0 /* FlexColorPicker */; }; 93F8065027AE7A830035A6B0 /* SideMenu in Frameworks */ = {isa = PBXBuildFile; productRef = 93F8064F27AE7A830035A6B0 /* SideMenu */; }; + AFE7BDC9A9901B49AF9D6635 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 16CFF4D57F6DF603FF1C1B9D /* Foundation.framework */; }; + B76C3BEEBF1F4730710AAB6E /* SDWebImageSVGCoder in Frameworks */ = {isa = PBXBuildFile; productRef = E92D1BEE08AD7DCB6F993340 /* SDWebImageSVGCoder */; }; + C314F9C9CB55D0AA25E4BB0A /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0F298FB19C2A149258FE7CC6 /* WidgetKit.framework */; }; + C380A8DFA36E43D49400A87E /* SFSafeSymbols in Frameworks */ = {isa = PBXBuildFile; productRef = 1F9D3602163D4BEBB384DDEE /* SFSafeSymbols */; }; DA10161B2DC7BAE500552D14 /* SFSafeSymbols in Frameworks */ = {isa = PBXBuildFile; productRef = DA10161A2DC7BAE500552D14 /* SFSafeSymbols */; }; DA28C362225241DE00AB409C /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA28C361225241DE00AB409C /* WebKit.framework */; settings = {ATTRIBUTES = (Required, ); }; }; DA2C4FD52B4F573300D1C533 /* SDWebImageSVGCoder in Frameworks */ = {isa = PBXBuildFile; productRef = DA2C4FD42B4F573300D1C533 /* SDWebImageSVGCoder */; }; - DA448E852EF435B400F0893C /* Home.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA448E772EF435B400F0893C /* Home.swift */; }; - DA4BF3522F0307A40082479C /* GetItemStateIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3512F0307A40082479C /* GetItemStateIntent.swift */; }; - DA4BF3562F03095D0082479C /* SetColorValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3542F03095D0082479C /* SetColorValueIntent.swift */; }; - DA4BF3572F03095D0082479C /* ContactStateIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3552F03095D0082479C /* ContactStateIntent.swift */; }; - DA4BF3592F03098B0082479C /* SetDimmerRollerValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3582F03098B0082479C /* SetDimmerRollerValueIntent.swift */; }; - DA4BF35B2F0309A60082479C /* SetNumberValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF35A2F0309A60082479C /* SetNumberValueIntent.swift */; }; - DA4BF35D2F0309B10082479C /* SetStringValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF35C2F0309B10082479C /* SetStringValueIntent.swift */; }; - 772F724744CC8D15A673C246 /* SetActiveHomeIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F77A157AAF1DD5AB45D6BA2 /* SetActiveHomeIntent.swift */; }; - DA4BF4032F06F5950082479C /* SwitchAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4022F06F5950082479C /* SwitchAction.swift */; }; - DA4BF4052F06F5CA0082479C /* ContactState.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4042F06F5CA0082479C /* ContactState.swift */; }; - DA4BF4072F0800010082479C /* PlayerAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4062F0800010082479C /* PlayerAction.swift */; }; - DA4BF4092F0800020082479C /* SetPlayerValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4082F0800020082479C /* SetPlayerValueIntent.swift */; }; - DA4BF40B2F0800030082479C /* SetDateTimeValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF40A2F0800030082479C /* SetDateTimeValueIntent.swift */; }; - DA4BF40D2F0800040082479C /* SetLocationValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF40C2F0800040082479C /* SetLocationValueIntent.swift */; }; - DA4BF4342F0705580082479C /* ItemEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4332F0705580082479C /* ItemEntity.swift */; }; - DA4BF4362F07056F0082479C /* ItemEntityQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4352F07056F0082479C /* ItemEntityQuery.swift */; }; DA4D4DB5233F9ACB00B37E37 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = DA4D4DB4233F9ACB00B37E37 /* README.md */; }; DA817E7A234BF39B00C91824 /* CHANGELOG.md in Resources */ = {isa = PBXBuildFile; fileRef = DA817E79234BF39B00C91824 /* CHANGELOG.md */; }; DA9A7EFD2D668D5900824156 /* SFSafeSymbols in Frameworks */ = {isa = PBXBuildFile; productRef = DA9A7EFC2D668D5900824156 /* SFSafeSymbols */; }; @@ -71,19 +43,24 @@ DABB5E332D98972F009A4B8A /* SDWebImageSVGCoder in Frameworks */ = {isa = PBXBuildFile; productRef = DABB5E322D98972F009A4B8A /* SDWebImageSVGCoder */; }; DAC949FA2E219F0D007E67B7 /* CommonUI in Frameworks */ = {isa = PBXBuildFile; productRef = DAC949F92E219F0D007E67B7 /* CommonUI */; }; DAC949FC2E219F30007E67B7 /* CommonUI in Frameworks */ = {isa = PBXBuildFile; productRef = DAC949FB2E219F30007E67B7 /* CommonUI */; }; - DAD5E85C2F02C272003215C0 /* SetSwitchItemIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD5E85B2F02C272003215C0 /* SetSwitchItemIntent.swift */; }; - DAD5E85E2F02C3C6003215C0 /* ItemIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD5E85D2F02C3BC003215C0 /* ItemIdentifier.swift */; }; DAEFAA7F2F63536A00AC300B /* OpenHABCore in Frameworks */ = {isa = PBXBuildFile; productRef = DAEFAA7E2F63536A00AC300B /* OpenHABCore */; }; DAEFAA812F63537600AC300B /* SDWebImageSVGCoder in Frameworks */ = {isa = PBXBuildFile; productRef = DAEFAA802F63537600AC300B /* SDWebImageSVGCoder */; }; - DAF58C7B2EF6E99500483AFD /* SwitchItemEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAF58C7A2EF6E99500483AFD /* SwitchItemEntity.swift */; }; DAFF80982E4F47830084513E /* SDWebImage in Frameworks */ = {isa = PBXBuildFile; productRef = DAFF80972E4F47830084513E /* SDWebImage */; }; DFB2622B18830A3600D3244D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DFB2622A18830A3600D3244D /* Foundation.framework */; }; DFB2622D18830A3600D3244D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DFB2622C18830A3600D3244D /* CoreGraphics.framework */; }; DFB2622F18830A3600D3244D /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DFB2622E18830A3600D3244D /* UIKit.framework */; }; DFE10414197415F900D94943 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DFE10413197415F900D94943 /* Security.framework */; }; + EE6B566C271031A1EB5780D2 /* OpenHABCore in Frameworks */ = {isa = PBXBuildFile; productRef = 2247FFEA7206BFFC71AB02C4 /* OpenHABCore */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 1124E731EFDB779420004716 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DFB2621F18830A3600D3244D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 6116C7DDD25EE245FE191A47; + remoteInfo = openHABWidgetExtension; + }; 657144532C1E438700C8A1F3 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = DFB2621F18830A3600D3244D /* Project object */; @@ -154,6 +131,7 @@ dstSubfolderSpec = 13; files = ( 657144552C1E438700C8A1F3 /* NotificationService.appex in Embed Foundation Extensions */, + 2AE557B26AF10D16E65EB541 /* openHABWidgetExtension.appex in Embed Foundation Extensions */, ); name = "Embed Foundation Extensions"; runOnlyForDeploymentPostprocessing = 0; @@ -181,8 +159,12 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 06A8490213945021806A13D1 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS18.0.sdk/System/Library/Frameworks/SwiftUI.framework; sourceTree = DEVELOPER_DIR; }; + 0F298FB19C2A149258FE7CC6 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS18.0.sdk/System/Library/Frameworks/WidgetKit.framework; sourceTree = DEVELOPER_DIR; }; 1224F78D228A89FC00750965 /* WatchMessageService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WatchMessageService.swift; sourceTree = ""; }; + 16CFF4D57F6DF603FF1C1B9D /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS18.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; 2F399AB92F5357E900F72A30 /* InputCommandFormatterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputCommandFormatterTests.swift; sourceTree = ""; }; + 396B3EC10279D6D55CD888E8 /* openHABWidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = openHABWidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 399449C421544C61AD83450C /* TextRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextRow.swift; sourceTree = ""; }; 6557AF8E2C0241C10094D0C8 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 6571444E2C1E438700C8A1F3 /* NotificationService.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NotificationService.appex; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -224,22 +206,6 @@ DA2E0B0D23DCC152009B0A99 /* MapView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MapView.swift; sourceTree = ""; }; DA2E0B0F23DCC439009B0A99 /* MapViewRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapViewRow.swift; sourceTree = ""; }; DA32D1B32C8C98C40018D974 /* IconWithAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconWithAction.swift; sourceTree = ""; }; - DA448E772EF435B400F0893C /* Home.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Home.swift; sourceTree = ""; }; - DA4BF3512F0307A40082479C /* GetItemStateIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetItemStateIntent.swift; sourceTree = ""; }; - DA4BF3542F03095D0082479C /* SetColorValueIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetColorValueIntent.swift; sourceTree = ""; }; - DA4BF3552F03095D0082479C /* ContactStateIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactStateIntent.swift; sourceTree = ""; }; - DA4BF3582F03098B0082479C /* SetDimmerRollerValueIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetDimmerRollerValueIntent.swift; sourceTree = ""; }; - DA4BF35A2F0309A60082479C /* SetNumberValueIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetNumberValueIntent.swift; sourceTree = ""; }; - DA4BF35C2F0309B10082479C /* SetStringValueIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetStringValueIntent.swift; sourceTree = ""; }; - 1F77A157AAF1DD5AB45D6BA2 /* SetActiveHomeIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetActiveHomeIntent.swift; sourceTree = ""; }; - DA4BF4022F06F5950082479C /* SwitchAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitchAction.swift; sourceTree = ""; }; - DA4BF4042F06F5CA0082479C /* ContactState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactState.swift; sourceTree = ""; }; - DA4BF4062F0800010082479C /* PlayerAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerAction.swift; sourceTree = ""; }; - DA4BF4082F0800020082479C /* SetPlayerValueIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetPlayerValueIntent.swift; sourceTree = ""; }; - DA4BF40A2F0800030082479C /* SetDateTimeValueIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetDateTimeValueIntent.swift; sourceTree = ""; }; - DA4BF40C2F0800040082479C /* SetLocationValueIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetLocationValueIntent.swift; sourceTree = ""; }; - DA4BF4332F0705580082479C /* ItemEntity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemEntity.swift; sourceTree = ""; }; - DA4BF4352F07056F0082479C /* ItemEntityQuery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemEntityQuery.swift; sourceTree = ""; }; DA4D4DB4233F9ACB00B37E37 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; DA4D4E0E2340A00200B37E37 /* Changes.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = Changes.md; sourceTree = ""; }; DA65871E236F83CD007E2E7F /* UserDefaultsExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsExtension.swift; sourceTree = ""; }; @@ -267,8 +233,6 @@ DAD085762AE4782E001D36BE /* openHABWatchSwiftUI Watch AppUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "openHABWatchSwiftUI Watch AppUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; DAD0857A2AE4782F001D36BE /* OpenHABWatchUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenHABWatchUITests.swift; sourceTree = ""; }; DAD0857C2AE4782F001D36BE /* OpenHABWatchLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenHABWatchLaunchTests.swift; sourceTree = ""; }; - DAD5E85B2F02C272003215C0 /* SetSwitchItemIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetSwitchItemIntent.swift; sourceTree = ""; }; - DAD5E85D2F02C3BC003215C0 /* ItemIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemIdentifier.swift; sourceTree = ""; }; DAF231D127BB6EEA00AB916C /* OpenHABSVGTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenHABSVGTests.swift; sourceTree = ""; }; DAF231D527BB702400AB916C /* valid_xmlns.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = valid_xmlns.svg; sourceTree = ""; }; DAF231D627BB702500AB916C /* invalid_xmlns.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = invalid_xmlns.svg; sourceTree = ""; }; @@ -283,7 +247,6 @@ DAF4581523DC483F0018B495 /* GenericRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenericRow.swift; sourceTree = ""; }; DAF4581723DC4A050018B495 /* ImageRow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageRow.swift; sourceTree = ""; }; DAF4581D23DC60020018B495 /* ImageRawRow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageRawRow.swift; sourceTree = ""; }; - DAF58C7A2EF6E99500483AFD /* SwitchItemEntity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitchItemEntity.swift; sourceTree = ""; }; DAFAF36E2F8892A3003E60EF /* AppIntentsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIntentsTests.swift; sourceTree = ""; }; DAFD2FE62E0D96700059A1EB /* OsLogRewriter */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = OsLogRewriter; sourceTree = ""; }; DFB2622718830A3600D3244D /* openHAB.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = openHAB.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -296,6 +259,13 @@ /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + 070DA7369C571A63C42BB463 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + OpenHABAppShortcutsProvider.swift, + ); + target = 6116C7DDD25EE245FE191A47 /* openHABWidgetExtension */; + }; 2FD4E1E82F66EF4800EBB340 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( @@ -350,16 +320,17 @@ ); target = DA0775142346705D0086C685 /* openHABWatch */; }; - DA4BF4322F0705070082479C /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { + 4D140F69148E28C6B524B346 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( - ActionMapper.swift, + Info.plist, ); - target = DFB2622618830A3600D3244D /* openHAB */; + target = 6116C7DDD25EE245FE191A47 /* openHABWidgetExtension */; }; /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ + 032C4C3A922FABCBEB20EAA3 /* AppIntents */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (070DA7369C571A63C42BB463 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = AppIntents; sourceTree = ""; }; 2FD4E1E22F66EF0200EBB340 /* fastlane */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = fastlane; sourceTree = ""; }; 2FD4E1E62F66EF4800EBB340 /* NotificationService */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (2FD4E1E82F66EF4800EBB340 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = NotificationService; sourceTree = ""; }; 2FD4E1EB2F66EF4F00EBB340 /* openHABWatchSwiftUI Watch AppUITests */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = "openHABWatchSwiftUI Watch AppUITests"; sourceTree = ""; }; @@ -368,11 +339,24 @@ 2FD4E2E02F66F9FE00EBB340 /* openHABTestsSwift */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (2FD4E2F02F66F9FE00EBB340 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = openHABTestsSwift; sourceTree = ""; }; 2FD4E2F42F66FA2900EBB340 /* TestPlans */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (2FD4E2F62F66FA2900EBB340 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = TestPlans; sourceTree = ""; }; 2FD4E52B2F670BA500EBB340 /* openHAB */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (2FD4E58D2F670BA500EBB340 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, 2FD4E58E2F670BA600EBB340 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = openHAB; sourceTree = ""; }; - DA4BF3532F0307D30082479C /* iOS16 */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (DA4BF4322F0705070082479C /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = iOS16; sourceTree = ""; }; + D41D0290EB2365F7E247EC0A /* openHABWidget */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (4D140F69148E28C6B524B346 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = openHABWidget; sourceTree = ""; }; DA8B15522F3BB74B007753FD /* openHABWatchTests */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = openHABWatchTests; sourceTree = ""; }; /* End PBXFileSystemSynchronizedRootGroup section */ /* Begin PBXFrameworksBuildPhase section */ + 2EBB199B3035138E609434CB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + AFE7BDC9A9901B49AF9D6635 /* Foundation.framework in Frameworks */, + C314F9C9CB55D0AA25E4BB0A /* WidgetKit.framework in Frameworks */, + 5111C4CC62E66D2FFA5C92EB /* SwiftUI.framework in Frameworks */, + EE6B566C271031A1EB5780D2 /* OpenHABCore in Frameworks */, + B76C3BEEBF1F4730710AAB6E /* SDWebImageSVGCoder in Frameworks */, + C380A8DFA36E43D49400A87E /* SFSafeSymbols in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 39C91164B60A5677322E8DE2 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -530,6 +514,16 @@ path = Model; sourceTree = ""; }; + 964901D665A05573A8E2E7B2 /* iOS */ = { + isa = PBXGroup; + children = ( + 16CFF4D57F6DF603FF1C1B9D /* Foundation.framework */, + 0F298FB19C2A149258FE7CC6 /* WidgetKit.framework */, + 06A8490213945021806A13D1 /* SwiftUI.framework */, + ); + name = iOS; + sourceTree = ""; + }; DA0775162346705D0086C685 /* openHABWatch */ = { isa = PBXGroup; children = ( @@ -580,41 +574,6 @@ path = openHABTestsSwift; sourceTree = ""; }; - DA448E7E2EF435B400F0893C /* AppIntents */ = { - isa = PBXGroup; - children = ( - DA4BF3652F0419F30082479C /* Intents */, - DA4BF3532F0307D30082479C /* iOS16 */, - DAD5E85D2F02C3BC003215C0 /* ItemIdentifier.swift */, - DA4BF4042F06F5CA0082479C /* ContactState.swift */, - DAF58C7A2EF6E99500483AFD /* SwitchItemEntity.swift */, - DA4BF4352F07056F0082479C /* ItemEntityQuery.swift */, - DA4BF4332F0705580082479C /* ItemEntity.swift */, - DA4BF4022F06F5950082479C /* SwitchAction.swift */, - DA4BF4062F0800010082479C /* PlayerAction.swift */, - DA448E772EF435B400F0893C /* Home.swift */, - ); - path = AppIntents; - sourceTree = ""; - }; - DA4BF3652F0419F30082479C /* Intents */ = { - isa = PBXGroup; - children = ( - DA4BF3542F03095D0082479C /* SetColorValueIntent.swift */, - DA4BF3552F03095D0082479C /* ContactStateIntent.swift */, - DA4BF3582F03098B0082479C /* SetDimmerRollerValueIntent.swift */, - DA4BF3512F0307A40082479C /* GetItemStateIntent.swift */, - DAD5E85B2F02C272003215C0 /* SetSwitchItemIntent.swift */, - DA4BF35C2F0309B10082479C /* SetStringValueIntent.swift */, - 1F77A157AAF1DD5AB45D6BA2 /* SetActiveHomeIntent.swift */, - DA4BF35A2F0309A60082479C /* SetNumberValueIntent.swift */, - DA4BF4082F0800020082479C /* SetPlayerValueIntent.swift */, - DA4BF40A2F0800030082479C /* SetDateTimeValueIntent.swift */, - DA4BF40C2F0800040082479C /* SetLocationValueIntent.swift */, - ); - path = Intents; - sourceTree = ""; - }; DA658720236F841F007E2E7F /* Views */ = { isa = PBXGroup; children = ( @@ -693,7 +652,7 @@ DFB2621E18830A3600D3244D = { isa = PBXGroup; children = ( - DA448E7E2EF435B400F0893C /* AppIntents */, + 032C4C3A922FABCBEB20EAA3 /* AppIntents */, DABB4CD22F45EFD100111AF3 /* openHABWatch.xctestplan */, 6557AF8E2C0241C10094D0C8 /* PrivacyInfo.xcprivacy */, DA4D4DB4233F9ACB00B37E37 /* README.md */, @@ -721,6 +680,7 @@ DAFD2FE62E0D96700059A1EB /* OsLogRewriter */, DA0DA9E12E0C9B74000C5D0A /* BuildTools */, DA83C81D2F48AF7600CDACED /* Version.xcconfig */, + D41D0290EB2365F7E247EC0A /* openHABWidget */, ); sourceTree = ""; }; @@ -735,6 +695,7 @@ DAD085762AE4782E001D36BE /* openHABWatchSwiftUI Watch AppUITests.xctest */, 6571444E2C1E438700C8A1F3 /* NotificationService.appex */, DA8B15512F3BB74B007753FD /* openHABWatchTests.xctest */, + 396B3EC10279D6D55CD888E8 /* openHABWidgetExtension.appex */, ); name = Products; sourceTree = ""; @@ -748,6 +709,7 @@ DFE10413197415F900D94943 /* Security.framework */, DFB2622E18830A3600D3244D /* UIKit.framework */, DFB2624C18830A3600D3244D /* XCTest.framework */, + 964901D665A05573A8E2E7B2 /* iOS */, ); name = Frameworks; sourceTree = ""; @@ -755,6 +717,32 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 6116C7DDD25EE245FE191A47 /* openHABWidgetExtension */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7E431775FC423F4117A62BF2 /* Build configuration list for PBXNativeTarget "openHABWidgetExtension" */; + buildPhases = ( + F706ED1570DF7DE91D2D6056 /* Sources */, + 2EBB199B3035138E609434CB /* Frameworks */, + BA216D3704F6BDD838E96877 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 032C4C3A922FABCBEB20EAA3 /* AppIntents */, + D41D0290EB2365F7E247EC0A /* openHABWidget */, + ); + name = openHABWidgetExtension; + packageProductDependencies = ( + 2247FFEA7206BFFC71AB02C4 /* OpenHABCore */, + E92D1BEE08AD7DCB6F993340 /* SDWebImageSVGCoder */, + 1F9D3602163D4BEBB384DDEE /* SFSafeSymbols */, + ); + productName = "$(TARGET_NAME)"; + productReference = 396B3EC10279D6D55CD888E8 /* openHABWidgetExtension.appex */; + productType = "com.apple.product-type.app-extension"; + }; 6571444D2C1E438700C8A1F3 /* NotificationService */ = { isa = PBXNativeTarget; buildConfigurationList = 657144582C1E438700C8A1F3 /* Build configuration list for PBXNativeTarget "NotificationService" */; @@ -921,8 +909,10 @@ dependencies = ( DA07753A2346705F0086C685 /* PBXTargetDependency */, 657144542C1E438700C8A1F3 /* PBXTargetDependency */, + 50C8570F40A5885164DA68E6 /* PBXTargetDependency */, ); fileSystemSynchronizedGroups = ( + 032C4C3A922FABCBEB20EAA3 /* AppIntents */, 2FD4E52B2F670BA500EBB340 /* openHAB */, ); name = openHAB; @@ -1046,6 +1036,7 @@ DAD085752AE4782D001D36BE /* openHABWatchSwiftUI Watch AppUITests */, 6571444D2C1E438700C8A1F3 /* NotificationService */, DA8B15502F3BB74B007753FD /* openHABWatchTests */, + 6116C7DDD25EE245FE191A47 /* openHABWidgetExtension */, ); }; /* End PBXProject section */ @@ -1065,6 +1056,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + BA216D3704F6BDD838E96877 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; DA0775132346705D0086C685 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -1154,24 +1152,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 9F54768418A14674AD9E2FCA /* ContactState.swift in Sources */, - 3D3B2898C51F48BAA31210E4 /* ContactStateIntent.swift in Sources */, - 791AFA47B71D4B6995369F3A /* GetItemStateIntent.swift in Sources */, - 5B292637B6E141DB98BCAA26 /* Home.swift in Sources */, - B2FC662F25B2461887BB67DA /* ItemEntity.swift in Sources */, - 3F087E4025E845FFBCBE45E3 /* ItemEntityQuery.swift in Sources */, - C9DE79C32AF24FFEA0280115 /* ItemIdentifier.swift in Sources */, - 0648F50178784F149905E7CF /* PlayerAction.swift in Sources */, - 1E4E7DB36BAC4804AD5E58E5 /* SetColorValueIntent.swift in Sources */, - BF0EB6DE70934966B1DC5479 /* SetDateTimeValueIntent.swift in Sources */, - E120F4A14B9942449F25E788 /* SetDimmerRollerValueIntent.swift in Sources */, - CE7A58C0ED3A4A1ABCB20337 /* SetLocationValueIntent.swift in Sources */, - 61A606E5E6AE404584B0B1A0 /* SetNumberValueIntent.swift in Sources */, - 598208789AC44F1D91DD5B17 /* SetPlayerValueIntent.swift in Sources */, - 1C1CA8C8C8424BAF8018F9BE /* SetStringValueIntent.swift in Sources */, - 73F094A3C17641738D6215CD /* SetSwitchItemIntent.swift in Sources */, - CF650A22059A4A00823EE656 /* SwitchAction.swift in Sources */, - 970584BAA2884C8287297BF6 /* SwitchItemEntity.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1207,31 +1187,25 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - DA448E852EF435B400F0893C /* Home.swift in Sources */, - DA4BF3522F0307A40082479C /* GetItemStateIntent.swift in Sources */, - DA4BF3562F03095D0082479C /* SetColorValueIntent.swift in Sources */, - DA4BF3572F03095D0082479C /* ContactStateIntent.swift in Sources */, - DA4BF3592F03098B0082479C /* SetDimmerRollerValueIntent.swift in Sources */, - DA4BF35B2F0309A60082479C /* SetNumberValueIntent.swift in Sources */, - DA4BF35D2F0309B10082479C /* SetStringValueIntent.swift in Sources */, - 772F724744CC8D15A673C246 /* SetActiveHomeIntent.swift in Sources */, - DA4BF4032F06F5950082479C /* SwitchAction.swift in Sources */, - DA4BF4052F06F5CA0082479C /* ContactState.swift in Sources */, - DA4BF4072F0800010082479C /* PlayerAction.swift in Sources */, - DA4BF4092F0800020082479C /* SetPlayerValueIntent.swift in Sources */, - DA4BF40B2F0800030082479C /* SetDateTimeValueIntent.swift in Sources */, - DA4BF40D2F0800040082479C /* SetLocationValueIntent.swift in Sources */, - DA4BF4342F0705580082479C /* ItemEntity.swift in Sources */, - DA4BF4362F07056F0082479C /* ItemEntityQuery.swift in Sources */, - DAD5E85C2F02C272003215C0 /* SetSwitchItemIntent.swift in Sources */, - DAD5E85E2F02C3C6003215C0 /* ItemIdentifier.swift in Sources */, - DAF58C7B2EF6E99500483AFD /* SwitchItemEntity.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F706ED1570DF7DE91D2D6056 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 50C8570F40A5885164DA68E6 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = openHABWidgetExtension; + target = 6116C7DDD25EE245FE191A47 /* openHABWidgetExtension */; + targetProxy = 1124E731EFDB779420004716 /* PBXContainerItemProxy */; + }; 657144542C1E438700C8A1F3 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 6571444D2C1E438700C8A1F3 /* NotificationService */; @@ -1270,6 +1244,33 @@ /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ + 302B3CB364B7849B15A8EF86 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = DA83C81D2F48AF7600CDACED /* Version.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_ENABLE_OBJC_WEAK = NO; + CODE_SIGN_ENTITLEMENTS = openHABWidget/OpenHABWidget.entitlements; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = openHABWidget/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = openHABWidget; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 openHAB e.V. All rights reserved."; + IPHONEOS_DEPLOYMENT_TARGET = 17.6; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.openhab.app.openHABWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; 657144562C1E438700C8A1F3 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -1289,7 +1290,7 @@ INFOPLIST_FILE = NotificationService/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = NotificationService; INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 openHAB e.V. All rights reserved."; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; + IPHONEOS_DEPLOYMENT_TARGET = 17.6; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1327,7 +1328,7 @@ INFOPLIST_FILE = NotificationService/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = NotificationService; INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 openHAB e.V. All rights reserved."; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; + IPHONEOS_DEPLOYMENT_TARGET = 17.6; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1345,6 +1346,34 @@ }; name = Release; }; + 7BB32D6084716CB8C70925BA /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = DA83C81D2F48AF7600CDACED /* Version.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_ENABLE_OBJC_WEAK = NO; + CODE_SIGN_ENTITLEMENTS = openHABWidget/OpenHABWidget.entitlements; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = openHABWidget/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = openHABWidget; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 openHAB e.V. All rights reserved."; + IPHONEOS_DEPLOYMENT_TARGET = 17.6; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.openhab.app.openHABWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; 933D7F0B22E7015100621A03 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -1358,7 +1387,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = openHABUITests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; + IPHONEOS_DEPLOYMENT_TARGET = 17.6; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1396,7 +1425,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = openHABUITests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; + IPHONEOS_DEPLOYMENT_TARGET = 17.6; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1455,7 +1484,7 @@ SWIFT_EMIT_LOC_STRINGS = YES; TARGETED_DEVICE_FAMILY = 4; VERSIONING_SYSTEM = "apple-generic"; - WATCHOS_DEPLOYMENT_TARGET = 10.0; + WATCHOS_DEPLOYMENT_TARGET = 10.6; }; name = Debug; }; @@ -1500,7 +1529,7 @@ SWIFT_EMIT_LOC_STRINGS = YES; TARGETED_DEVICE_FAMILY = 4; VERSIONING_SYSTEM = "apple-generic"; - WATCHOS_DEPLOYMENT_TARGET = 10.0; + WATCHOS_DEPLOYMENT_TARGET = 10.6; }; name = Release; }; @@ -1518,7 +1547,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = openHABTestsSwift/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; + IPHONEOS_DEPLOYMENT_TARGET = 17.6; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1553,7 +1582,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = openHABTestsSwift/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; + IPHONEOS_DEPLOYMENT_TARGET = 17.6; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1669,7 +1698,7 @@ SWIFT_EMIT_LOC_STRINGS = NO; TARGETED_DEVICE_FAMILY = 4; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/openHABWatch.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/openHABWatch"; - WATCHOS_DEPLOYMENT_TARGET = 9.0; + WATCHOS_DEPLOYMENT_TARGET = 10.6; }; name = Debug; }; @@ -1700,7 +1729,7 @@ SWIFT_EMIT_LOC_STRINGS = NO; TARGETED_DEVICE_FAMILY = 4; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/openHABWatch.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/openHABWatch"; - WATCHOS_DEPLOYMENT_TARGET = 9.0; + WATCHOS_DEPLOYMENT_TARGET = 10.6; }; name = Release; }; @@ -1728,7 +1757,7 @@ SWIFT_EMIT_LOC_STRINGS = NO; TARGETED_DEVICE_FAMILY = 4; TEST_TARGET_NAME = openHABWatch; - WATCHOS_DEPLOYMENT_TARGET = 9.0; + WATCHOS_DEPLOYMENT_TARGET = 10.6; }; name = Debug; }; @@ -1758,7 +1787,7 @@ SWIFT_EMIT_LOC_STRINGS = NO; TARGETED_DEVICE_FAMILY = 4; TEST_TARGET_NAME = openHABWatch; - WATCHOS_DEPLOYMENT_TARGET = 9.0; + WATCHOS_DEPLOYMENT_TARGET = 10.6; }; name = Release; }; @@ -1937,7 +1966,7 @@ INFOPLIST_FILE = "openHAB/Supporting Files/openHAB-Info.plist"; INFOPLIST_KEY_CFBundleDisplayName = openHAB; INFOPLIST_KEY_WKCompanionAppBundleIdentifier = ""; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; + IPHONEOS_DEPLOYMENT_TARGET = 17.6; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1983,7 +2012,7 @@ INFOPLIST_FILE = "openHAB/Supporting Files/openHAB-Info.plist"; INFOPLIST_KEY_CFBundleDisplayName = openHAB; INFOPLIST_KEY_WKCompanionAppBundleIdentifier = ""; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; + IPHONEOS_DEPLOYMENT_TARGET = 17.6; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2025,6 +2054,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 7E431775FC423F4117A62BF2 /* Build configuration list for PBXNativeTarget "openHABWidgetExtension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7BB32D6084716CB8C70925BA /* Release */, + 302B3CB364B7849B15A8EF86 /* Debug */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 933D7F0D22E7015100621A03 /* Build configuration list for PBXNativeTarget "openHABUITests" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -2190,6 +2228,16 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 1F9D3602163D4BEBB384DDEE /* SFSafeSymbols */ = { + isa = XCSwiftPackageProductDependency; + package = DA3B75AC2C59729200E219AB /* XCRemoteSwiftPackageReference "SFSafeSymbols" */; + productName = SFSafeSymbols; + }; + 2247FFEA7206BFFC71AB02C4 /* OpenHABCore */ = { + isa = XCSwiftPackageProductDependency; + package = 3A009E9A2F63003B00A0490E /* XCLocalSwiftPackageReference "CommonUI" */; + productName = OpenHABCore; + }; 2F399AC82F54599400F72A30 /* Flow */ = { isa = XCSwiftPackageProductDependency; package = 2F399AC72F54599400F72A30 /* XCRemoteSwiftPackageReference "SwiftUI-Flow" */; @@ -2312,6 +2360,11 @@ isa = XCSwiftPackageProductDependency; productName = SDWebImage; }; + E92D1BEE08AD7DCB6F993340 /* SDWebImageSVGCoder */ = { + isa = XCSwiftPackageProductDependency; + package = DA2C4FD32B4F573300D1C533 /* XCRemoteSwiftPackageReference "SDWebImageSVGCoder" */; + productName = SDWebImageSVGCoder; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = DFB2621F18830A3600D3244D /* Project object */; diff --git a/openHAB.xcworkspace/xcshareddata/swiftpm/Package.resolved b/openHAB.xcworkspace/xcshareddata/swiftpm/Package.resolved index c2772ead8..87269bbca 100644 --- a/openHAB.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/openHAB.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "668dbf2009df850e4ef082e371adec396e99832c3e3a6ccfdf654b717dc3b002", + "originHash" : "38e24fd8c6bebb97ff99908ff7a1ec5f95cb577960130e58c7cfb021b32ae037", "pins" : [ { "identity" : "abseil-cpp-binary", diff --git a/openHAB/AppDelegate.swift b/openHAB/AppDelegate.swift index 98f66806e..67f6b298c 100644 --- a/openHAB/AppDelegate.swift +++ b/openHAB/AppDelegate.swift @@ -119,6 +119,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { ScreenSaverManager.shared.startMonitoring(window: keyWindow, configuration: config) } + // Start monitoring items for widget updates after the app is configured. + WidgetItemMonitor.shared.startMonitoring() } @MainActor @@ -274,6 +276,11 @@ extension AppDelegate { ScreenSaverManager.shared.startMonitoring(window: keyWindow, configuration: config) } + + // Clean up stale widget items when app becomes active + Task { + await WidgetItemMonitor.shared.cleanupStaleItems() + } } func applicationWillTerminate(_ application: UIApplication) { diff --git a/openHAB/Images/Images.xcassets/openHABIcon.imageset/Contents.json b/openHAB/Images/Images.xcassets/openHABIcon.imageset/Contents.json index 3dd040950..b8cb219cb 100644 --- a/openHAB/Images/Images.xcassets/openHABIcon.imageset/Contents.json +++ b/openHAB/Images/Images.xcassets/openHABIcon.imageset/Contents.json @@ -1,22 +1,25 @@ { "images" : [ { - "idiom" : "universal", - "filename" : "oh_logo_only.pdf" + "filename" : "oh_logo_only.pdf", + "idiom" : "universal" }, { - "idiom" : "universal", - "filename" : "oh_logo_only_dark.pdf", "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } - ] + ], + "filename" : "oh_logo_only_dark.pdf", + "idiom" : "universal" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true } } \ No newline at end of file diff --git a/openHAB/Supporting Files/Localizable.xcstrings b/openHAB/Supporting Files/Localizable.xcstrings index 43507f7e0..c254781c4 100644 --- a/openHAB/Supporting Files/Localizable.xcstrings +++ b/openHAB/Supporting Files/Localizable.xcstrings @@ -4771,8 +4771,13 @@ } } }, + "Dimmer or Roller Item" : { + "comment" : "Display name for a dimmer or roller item type.", + "isCommentAutoGenerated" : true + }, "Dimmer/Roller Item" : { "comment" : "Display name for a dimmer or roller item type.", + "extractionState" : "stale", "isCommentAutoGenerated" : true, "localizations" : { "de" : { @@ -6618,8 +6623,8 @@ }, "fi" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Koti" } }, "fr" : { @@ -6636,8 +6641,8 @@ }, "nb" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Hjem" } }, "nl" : { @@ -6648,8 +6653,8 @@ }, "ru" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Дом" } } } @@ -9742,6 +9747,7 @@ }, "off" : { "comment" : "Label for the \"off\" state of a contact.", + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -9822,8 +9828,8 @@ }, "fi" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Pois" } }, "fr" : { @@ -9840,8 +9846,8 @@ }, "nb" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Av" } }, "nl" : { @@ -9852,8 +9858,8 @@ }, "ru" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Выкл." } } } @@ -10155,6 +10161,7 @@ }, "on" : { "comment" : "\"on\" in capitalized form.", + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -10229,14 +10236,14 @@ }, "es" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Activado" } }, "fi" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Päällä" } }, "fr" : { @@ -10253,8 +10260,8 @@ }, "nb" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "På" } }, "nl" : { @@ -10265,8 +10272,8 @@ }, "ru" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Вкл." } } } @@ -16232,6 +16239,7 @@ }, "toggle" : { "comment" : "Action to toggle a switch between on and off states", + "extractionState" : "stale", "isCommentAutoGenerated" : true, "localizations" : { "de" : { @@ -16259,48 +16267,52 @@ }, "es" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Alternar" } }, "fi" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Vaihda" } }, "fr" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Basculer" } }, "it" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Commuta" } }, "nb" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Veksle" } }, "nl" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Schakelen" } }, "ru" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Переключить" } } } }, + "Toggle Switch" : { + "comment" : "Short title for a toggle switch shortcut.", + "isCommentAutoGenerated" : true + }, "unable_to_add_certificate" : { "extractionState" : "manual", "localizations" : { @@ -17427,4 +17439,4 @@ } }, "version" : "1.1" -} \ No newline at end of file +} diff --git a/openHAB/UI/OpenHABWebViewController.swift b/openHAB/UI/OpenHABWebViewController.swift index 5582f8c9a..12d592a76 100644 --- a/openHAB/UI/OpenHABWebViewController.swift +++ b/openHAB/UI/OpenHABWebViewController.swift @@ -29,8 +29,6 @@ class OpenHABWebViewController: OpenHABViewController { private var commandQueue: [String] = [] private var acceptsCommands = false private var views: [UUID: WKWebView] = [:] - // TODO: remove myOhViews when we drop iOS 16 support - private var myOhViews: [UUID: WKWebView] = [:] private var etagChecker: ETagChecker? private var etagCheckerConfigURL: String? // Track which config the checker was created for private var lastLoadedURL: String? // Track the last successfully loaded URL from didFinish @@ -228,10 +226,8 @@ class OpenHABWebViewController: OpenHABViewController { request.cachePolicy = .reloadIgnoringLocalAndRemoteCacheData } - // TODO: remove this check once iOS 16 is dropped - let isCloudConnection = activeConfig.isCloudConnection - // create new (or resuse existing) - let newWebview = webView(for: Preferences.shared.currentHomePreferences.id, isCloudConnection: isCloudConnection) + // create new (or reuse existing) + let newWebview = webView(for: Preferences.shared.currentHomePreferences.id) if newWebview != webView { // Detach old instance webView.stopLoading() @@ -440,14 +436,7 @@ class OpenHABWebViewController: OpenHABViewController { } } - func webView(for id: UUID, isCloudConnection: Bool) -> WKWebView { - // TODO: remove all iOS < 17 code when we drop iOS 16 support - if #unavailable(iOS 17) { - if isCloudConnection, let myExsiting = myOhViews[id] { - Logger.viewController.info("Reusing cloud webview for id:\(id.uuidString)") - return myExsiting - } - } + func webView(for id: UUID) -> WKWebView { if let existing = views[id] { Logger.viewController.info("Reusing webview for id:\(id.uuidString)") return existing @@ -460,13 +449,7 @@ class OpenHABWebViewController: OpenHABViewController { config.userContentController.add(self, name: "pathChanged") config.userContentController.addUserScript(WKUserScript(source: js, injectionTime: .atDocumentStart, forMainFrameOnly: false)) - // iOS 17 allows Sandboxed profiles, which is fantastic, iOS 16 does not and agressively caches everything - if #available(iOS 17, *) { - config.websiteDataStore = WKWebsiteDataStore(forIdentifier: id) - } else if isCloudConnection { - // for cloud connections, create an instance that does not persist or share states (private) - config.websiteDataStore = .nonPersistent() - } + config.websiteDataStore = WKWebsiteDataStore(forIdentifier: id) let webview = WKWebView(frame: .zero, configuration: config) webview.navigationDelegate = self @@ -480,21 +463,13 @@ class OpenHABWebViewController: OpenHABViewController { // since ios 13 Safari sets the user agent to desktop mode on iPads so the view renders correctly with larger screens webview.customUserAgent = "Mozilla/5.0 (iPad; CPU OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Mobile/15E148 Safari/604.1" } - if #available(iOS 16.4, *) { - webview.isInspectable = true - } + webview.isInspectable = true // Avoid safe-area content insets which can leave a small gap at the bottom on iPad until a reload. webview.scrollView.contentInsetAdjustmentBehavior = .never webview.scrollView.contentInset = .zero webview.scrollView.scrollIndicatorInsets = .zero - if #unavailable(iOS 17) { - if isCloudConnection { - myOhViews[id] = webview - return webview - } - } views[id] = webview return webview } diff --git a/openHAB/UI/ScreenSaver/ScreenSaverView.swift b/openHAB/UI/ScreenSaver/ScreenSaverView.swift index b69f2621a..1319743e6 100644 --- a/openHAB/UI/ScreenSaver/ScreenSaverView.swift +++ b/openHAB/UI/ScreenSaver/ScreenSaverView.swift @@ -86,7 +86,7 @@ struct ScreenSaverView: View { .onDisappear { stopMovementTimer() } - .onChange(of: geometry.size) { _ in + .onChange(of: geometry.size) { currentAnchor = randomAnchor() } } diff --git a/openHAB/UI/SettingsView/ApplicationSettingsView.swift b/openHAB/UI/SettingsView/ApplicationSettingsView.swift index 110c4cf2e..75a41301f 100644 --- a/openHAB/UI/SettingsView/ApplicationSettingsView.swift +++ b/openHAB/UI/SettingsView/ApplicationSettingsView.swift @@ -47,7 +47,7 @@ struct ApplicationSettingsView: View { Text("Command Item \(selectedItemName?.isEmpty == false ? "(\(selectedItemName ?? ""))" : "")") } } - .onChange(of: selectedItemName) { newSelection in + .onChange(of: selectedItemName) { _, newSelection in handleItemSelectionChange(newSelection) } .onAppear { diff --git a/openHAB/UI/SettingsView/DebugSettingsView.swift b/openHAB/UI/SettingsView/DebugSettingsView.swift index f20fee9f7..ccbedbc25 100644 --- a/openHAB/UI/SettingsView/DebugSettingsView.swift +++ b/openHAB/UI/SettingsView/DebugSettingsView.swift @@ -28,7 +28,7 @@ struct DebugSettingsView: View { .task { @MainActor in updateSettingsSendCrashReports(Preferences.shared.sendCrashReports) } - .onChange(of: settingsSendCrashReports) { newValue in + .onChange(of: settingsSendCrashReports) { _, newValue in #if !DEBUG Logger.settingsView.debug("Detected change on settingsSendCrashReports") #endif @@ -59,7 +59,7 @@ struct DebugSettingsView: View { } Section(header: Text(LocalizedStringKey("debug"))) { Toggle("Sitemap Diagnostics Logging", isOn: $settingsSitemapDiagnosticsLogging) - .onChange(of: settingsSitemapDiagnosticsLogging) { newValue in + .onChange(of: settingsSitemapDiagnosticsLogging) { _, newValue in Preferences.shared.modifyApplicationPreferences { prefs in prefs.sitemapDiagnosticsLogging = newValue } diff --git a/openHAB/UI/SwiftUI/Rows/ColorPickerRowView.swift b/openHAB/UI/SwiftUI/Rows/ColorPickerRowView.swift index 174417392..20faecea6 100644 --- a/openHAB/UI/SwiftUI/Rows/ColorPickerRowView.swift +++ b/openHAB/UI/SwiftUI/Rows/ColorPickerRowView.swift @@ -418,11 +418,11 @@ private struct ColorPickerRowContent: View { .onAppear { applyServerState(displayState.effectiveState) } - .onChange(of: displayState.effectiveState) { newState in + .onChange(of: displayState.effectiveState) { _, newState in guard !isEditingColor, !isPresentingColorWheel else { return } applyServerState(newState) } - .onChange(of: selection) { _ in + .onChange(of: selection) { sendColorCommand() } .sheet(isPresented: $isPresentingColorWheel) { diff --git a/openHAB/UI/SwiftUI/Rows/ColorTemperaturePickerRowView.swift b/openHAB/UI/SwiftUI/Rows/ColorTemperaturePickerRowView.swift index 1cda47dbc..93ef1712f 100644 --- a/openHAB/UI/SwiftUI/Rows/ColorTemperaturePickerRowView.swift +++ b/openHAB/UI/SwiftUI/Rows/ColorTemperaturePickerRowView.swift @@ -250,7 +250,7 @@ private struct ColorTemperaturePickerRowContent: View { .onAppear { selectedTemperature = loadCurrentTemperature(state: displayState.effectiveState) } - .onChange(of: displayState.effectiveState) { newState in + .onChange(of: displayState.effectiveState) { _, newState in guard !isDraggingSlider else { return } if suppressNextServerSync { suppressNextServerSync = false diff --git a/openHAB/UI/SwiftUI/Rows/DatePickerInputRowView.swift b/openHAB/UI/SwiftUI/Rows/DatePickerInputRowView.swift index 2fb5c0497..0657e3f77 100644 --- a/openHAB/UI/SwiftUI/Rows/DatePickerInputRowView.swift +++ b/openHAB/UI/SwiftUI/Rows/DatePickerInputRowView.swift @@ -69,7 +69,7 @@ private struct DateInputRowContent: View { ) { EmptyView() } - .onChange(of: selectedDate) { newDate in + .onChange(of: selectedDate) { _, newDate in guard !suppressSendingNewValue else { suppressSendingNewValue = false return @@ -82,7 +82,7 @@ private struct DateInputRowContent: View { let newDate = DateFormatter.iso8601Full.date(from: displayState.effectiveState) ?? Date.now programmaticallySetDate(newDate) } - .onChange(of: displayState.effectiveState) { newState in + .onChange(of: displayState.effectiveState) { _, newState in guard !suppressNextServerSync else { suppressNextServerSync = false return diff --git a/openHAB/UI/SwiftUI/Rows/ImageRowView.swift b/openHAB/UI/SwiftUI/Rows/ImageRowView.swift index f3c7ef285..5d7b44be5 100644 --- a/openHAB/UI/SwiftUI/Rows/ImageRowView.swift +++ b/openHAB/UI/SwiftUI/Rows/ImageRowView.swift @@ -104,7 +104,7 @@ private struct ImageRowContent: View { .task(id: chartSyncToken) { syncChartDisplayURL() } - .onChange(of: input.refresh) { _ in + .onChange(of: input.refresh) { setupRefreshTimer() } } diff --git a/openHAB/UI/SwiftUI/Rows/MapRowView.swift b/openHAB/UI/SwiftUI/Rows/MapRowView.swift index 84f1d71bc..293e218d7 100644 --- a/openHAB/UI/SwiftUI/Rows/MapRowView.swift +++ b/openHAB/UI/SwiftUI/Rows/MapRowView.swift @@ -15,16 +15,37 @@ import MapKit import OpenHABCore import SwiftUI -private struct MapRowConfig { - let input: MediaRowInput -} +private struct MapContent: View { + let coordinate: CLLocationCoordinate2D + let height: CGFloat? -@MainActor -private func makeMapRowContent(_ config: MapRowConfig) -> MapRowContent { - MapRowContent(input: config.input) + @State private var cameraPosition = MapCameraPosition.region( + MKCoordinateRegion( + center: CLLocationCoordinate2D(latitude: 0, longitude: 0), + latitudinalMeters: 1000, + longitudinalMeters: 1000 + ) + ) + + var body: some View { + Map(position: $cameraPosition) { + Marker("", coordinate: coordinate) + } + .frame(height: height) + .clipShape(.rect(cornerRadius: 8)) + .onAppear { + cameraPosition = .region( + MKCoordinateRegion( + center: coordinate, + latitudinalMeters: 1000, + longitudinalMeters: 1000 + ) + ) + } + } } -private struct MapRowContent: View { +struct MapRowView: View { let input: MediaRowInput private var coordinate: CLLocationCoordinate2D? { @@ -35,92 +56,22 @@ private struct MapRowContent: View { return CLLocationCoordinate2D(latitude: latitude, longitude: longitude) } - var body: some View { - if #available(iOS 17.0, *) { - MapRowViewNew(input: input, coordinate: coordinate) - } else { - MapRowViewLegacy(input: input, coordinate: coordinate) - } - } -} - -struct MapRowViewLegacy: View { - let input: MediaRowInput - let coordinate: CLLocationCoordinate2D? - - private var region: MKCoordinateRegion { - let coordinate = coordinate ?? CLLocationCoordinate2D(latitude: 0, longitude: 0) - return MKCoordinateRegion( - center: coordinate, - latitudinalMeters: 1000.0, - longitudinalMeters: 1000.0 - ) - } - var body: some View { let displayState = input.displayState VStack(alignment: .leading, spacing: 8) { if !displayState.labelText.isEmpty, input.labelSourceRawValue == OpenHABWidget.LabelSource.sitemapDefinition.rawValue { - let labelText = displayState.labelText - Text(labelText) + Text(displayState.labelText) .ohTextToken(.rowLabel) .foregroundStyle(input.labelColor.isEmpty ? .primary : Color(fromString: input.labelColor)) } - Map(coordinateRegion: .constant(region), annotationItems: coordinate.map { [$0] } ?? []) { location in - MapMarker(coordinate: location, tint: .red) - } - .frame(height: input.preferredRowHeight.map { CGFloat($0) }) - .clipShape(.rect(cornerRadius: 8)) - } - } -} - -@available(iOS 17.0, *) -private struct MapRowViewNew: View { - let input: MediaRowInput - let coordinate: CLLocationCoordinate2D? - @State private var cameraPosition = MapCameraPosition.region( - MKCoordinateRegion( - center: CLLocationCoordinate2D(latitude: 0, longitude: 0), - latitudinalMeters: 1000, - longitudinalMeters: 1000 - ) - ) - - var body: some View { - VStack { if let coordinate { - Map(position: $cameraPosition) { - Marker("", coordinate: coordinate) - } - .frame(height: input.preferredRowHeight.map { CGFloat($0) }) - .onAppear { - cameraPosition = .region( - MKCoordinateRegion( - center: coordinate, - latitudinalMeters: 1000, - longitudinalMeters: 1000 - ) - ) - } + MapContent(coordinate: coordinate, height: input.preferredRowHeight.map { CGFloat($0) }) } } } } -struct MapRowView: View { - let input: MediaRowInput - - var body: some View { - makeMapRowContent( - MapRowConfig( - input: input - ) - ) - } -} - extension CLLocationCoordinate2D: @retroactive Identifiable { var id: String { "\(latitude),\(longitude)" diff --git a/openHAB/UI/SwiftUI/Rows/SegmentedRowView.swift b/openHAB/UI/SwiftUI/Rows/SegmentedRowView.swift index 3ec4f0ade..5e71e5e3b 100644 --- a/openHAB/UI/SwiftUI/Rows/SegmentedRowView.swift +++ b/openHAB/UI/SwiftUI/Rows/SegmentedRowView.swift @@ -89,10 +89,10 @@ private struct SegmentedRowContent: View { .onAppear { clearOptimisticSelection() } - .onChange(of: input.widgetId) { _ in + .onChange(of: input.widgetId) { clearOptimisticSelection() } - .onChange(of: widgetVersion) { _ in + .onChange(of: widgetVersion) { guard let optimisticBaseState, optimisticWidgetId == input.widgetId, let optimisticStartVersion, diff --git a/openHAB/UI/SwiftUI/Rows/SelectionRowView.swift b/openHAB/UI/SwiftUI/Rows/SelectionRowView.swift index a52897c2c..47d1ff745 100644 --- a/openHAB/UI/SwiftUI/Rows/SelectionRowView.swift +++ b/openHAB/UI/SwiftUI/Rows/SelectionRowView.swift @@ -37,10 +37,10 @@ private struct SelectionRowContent: View { .onAppear { clearOptimisticSelection() } - .onChange(of: input.widgetId) { _ in + .onChange(of: input.widgetId) { clearOptimisticSelection() } - .onChange(of: widgetVersion) { _ in + .onChange(of: widgetVersion) { guard let optimisticBaseState, optimisticWidgetId == input.widgetId, let optimisticStartVersion, diff --git a/openHAB/UI/SwiftUI/Rows/SliderRowView.swift b/openHAB/UI/SwiftUI/Rows/SliderRowView.swift index 1b058508d..e538d8e6a 100644 --- a/openHAB/UI/SwiftUI/Rows/SliderRowView.swift +++ b/openHAB/UI/SwiftUI/Rows/SliderRowView.swift @@ -109,13 +109,13 @@ private struct SliderRowContent: View { dragStartVersion = nil dragWidgetId = nil } - .onChange(of: input.widgetId) { _ in + .onChange(of: input.widgetId) { isEditing = false dragValue = nil dragStartVersion = nil dragWidgetId = nil } - .onChange(of: widgetVersion) { _ in + .onChange(of: widgetVersion) { guard isEditing else { return } // If server refresh advanced while dragging, only cancel when the server value diverges // meaningfully from the local drag value. This avoids jarring cancels on polling echoes. diff --git a/openHAB/UI/SwiftUI/Rows/SwitchRowView.swift b/openHAB/UI/SwiftUI/Rows/SwitchRowView.swift index 765183521..0216b00f1 100644 --- a/openHAB/UI/SwiftUI/Rows/SwitchRowView.swift +++ b/openHAB/UI/SwiftUI/Rows/SwitchRowView.swift @@ -62,7 +62,7 @@ private struct SwitchRowContent: View { .disabled(input.readOnly) } .contentShape(Rectangle()) - .onChange(of: displayState.effectiveState) { _ in + .onChange(of: displayState.effectiveState) { // Sync local state when server state changes localIsOn = nil } diff --git a/openHAB/UI/SwiftUI/Rows/TextInputRowView.swift b/openHAB/UI/SwiftUI/Rows/TextInputRowView.swift index 52d8a812c..c52cff84e 100644 --- a/openHAB/UI/SwiftUI/Rows/TextInputRowView.swift +++ b/openHAB/UI/SwiftUI/Rows/TextInputRowView.swift @@ -252,7 +252,7 @@ private struct TextInputRowContent: View { .alert("Enter new value", isPresented: $showInputAlert) { TextField("Enter text", text: $draftInputText) .keyboardType(inputHint == .number ? .numbersAndPunctuation : .default) - .onChange(of: draftInputText) { newValue in + .onChange(of: draftInputText) { _, newValue in filterDraftInput(newValue) } Button("Cancel", role: .cancel) {} @@ -268,7 +268,7 @@ private struct TextInputRowContent: View { .onAppear { inputText = displayState.effectiveState } - .onChange(of: displayState.effectiveState) { newState in + .onChange(of: displayState.effectiveState) { _, newState in inputText = newState } } diff --git a/openHAB/UI/SwiftUI/Rows/VideoRowView.swift b/openHAB/UI/SwiftUI/Rows/VideoRowView.swift index 23274516d..08c4a4ebd 100644 --- a/openHAB/UI/SwiftUI/Rows/VideoRowView.swift +++ b/openHAB/UI/SwiftUI/Rows/VideoRowView.swift @@ -103,7 +103,7 @@ private struct VideoRowContent: View { .onDisappear { cleanup() } - .onChange(of: input.url) { newValue in + .onChange(of: input.url) { _, newValue in if !newValue.isEmpty, let newURL = URL(string: newValue) { setupVideo(url: newURL) } else { diff --git a/openHAB/UI/SwiftUI/SitemapNavigationView.swift b/openHAB/UI/SwiftUI/SitemapNavigationView.swift index e0295329e..47b86b22a 100644 --- a/openHAB/UI/SwiftUI/SitemapNavigationView.swift +++ b/openHAB/UI/SwiftUI/SitemapNavigationView.swift @@ -19,7 +19,6 @@ struct SitemapNavigationView: View { @StateObject var viewModel = SitemapPageViewModel() @State private var hasSeenActivePhase = false @State private var isSearchPresented = false - @FocusState private var isLegacySearchFocused: Bool let onShowSideMenu: () -> Void var body: some View { @@ -29,7 +28,7 @@ struct SitemapNavigationView: View { SitemapPageView(viewModel: SitemapPageViewModel(pageUrl: nav.pageLink, title: nav.pageTitle)) } } - .onChange(of: scenePhase) { newPhase in + .onChange(of: scenePhase) { _, newPhase in switch newPhase { case .active: // Skip only the first activation to avoid racing the initial .task startup. @@ -59,24 +58,13 @@ struct SitemapNavigationView: View { } if viewModel.showSearchField { ToolbarItem(placement: .navigationBarTrailing) { - if #available(iOS 17.0, *) { - Button { - isSearchPresented = true - } label: { - Image(systemSymbol: .magnifyingglass) - } - .ohMinimumHitTarget() - .accessibilityLabel("Search") - } else { - Button { - isSearchPresented = true - isLegacySearchFocused = true - } label: { - Image(systemSymbol: .magnifyingglass) - } - .ohMinimumHitTarget() - .accessibilityLabel("Search") + Button { + isSearchPresented = true + } label: { + Image(systemSymbol: .magnifyingglass) } + .ohMinimumHitTarget() + .accessibilityLabel("Search") } } ToolbarItem(placement: .navigationBarTrailing) { @@ -90,73 +78,19 @@ struct SitemapNavigationView: View { } } - if viewModel.showSearchField { - if #available(iOS 17.0, *) { - if isSearchPresented { - page - .searchable( - text: $viewModel.searchText, - isPresented: $isSearchPresented, - placement: .navigationBarDrawer(displayMode: .always), - prompt: Text(String(localized: "search_items", comment: "")) - ) - .autocorrectionDisabled() - .textInputAutocapitalization(.never) - } else { - page - } - } else { - page - .safeAreaInset(edge: .bottom) { - if isSearchPresented { - legacySearchBar - } - } - } - } else { + if viewModel.showSearchField, isSearchPresented { page - } - } - - private var legacySearchBar: some View { - HStack(spacing: 8) { - Image(systemSymbol: .magnifyingglass) - .foregroundStyle(.secondary) - .ohTextToken(.secondary) - - TextField(String(localized: "search_items", comment: ""), text: $viewModel.searchText) - .textInputAutocapitalization(.never) + .searchable( + text: $viewModel.searchText, + isPresented: $isSearchPresented, + placement: .navigationBarDrawer(displayMode: .always), + prompt: Text(String(localized: "search_items", comment: "")) + ) .autocorrectionDisabled() - .focused($isLegacySearchFocused) - .ohTextToken(.secondary) - - if !viewModel.searchText.isEmpty { - Button { - viewModel.searchText = "" - } label: { - Image(systemSymbol: .xmarkCircleFill) - .foregroundStyle(.secondary) - } - .buttonStyle(.plain) - } - - Button { - isSearchPresented = false - isLegacySearchFocused = false - } label: { - Image(systemSymbol: .xmark) - .foregroundStyle(.secondary) - } - .buttonStyle(.plain) + .textInputAutocapitalization(.never) + } else { + page } - .padding(.horizontal, 10) - .padding(.vertical, 8) - .background( - Color(.secondarySystemBackground).opacity(0.6), - in: RoundedRectangle(cornerRadius: 10) - ) - .padding(.horizontal, 12) - .padding(.bottom, 6) } @ViewBuilder diff --git a/openHAB/UI/SwiftUI/ViewExtension.swift b/openHAB/UI/SwiftUI/ViewExtension.swift index 7a8efbe55..93e69062f 100644 --- a/openHAB/UI/SwiftUI/ViewExtension.swift +++ b/openHAB/UI/SwiftUI/ViewExtension.swift @@ -12,30 +12,15 @@ import SwiftUI extension View { - @ViewBuilder func sensoryHeavyFeedbackIfAvailable(trigger: Bool) -> some View { - if #available(iOS 17.0, *) { - sensoryFeedback(.impact(weight: .heavy, intensity: 0.9), trigger: trigger) - } else { - self - } + sensoryFeedback(.impact(weight: .heavy, intensity: 0.9), trigger: trigger) } - @ViewBuilder func sensoryStopFeedbackIfAvailable(trigger: Bool) -> some View { - if #available(iOS 17.0, *) { - sensoryFeedback(.impact(flexibility: .rigid), trigger: trigger) - } else { - self - } + sensoryFeedback(.impact(flexibility: .rigid), trigger: trigger) } - @ViewBuilder func sensorySelectionFeedbackIfAvailable(trigger: Bool) -> some View { - if #available(iOS 17.0, *) { - sensoryFeedback(.selection, trigger: trigger) - } else { - self - } + sensoryFeedback(.selection, trigger: trigger) } } diff --git a/openHAB/WidgetItemMonitor.swift b/openHAB/WidgetItemMonitor.swift new file mode 100644 index 000000000..39bec97fd --- /dev/null +++ b/openHAB/WidgetItemMonitor.swift @@ -0,0 +1,132 @@ +// Copyright (c) 2010-2026 Contributors to the openHAB project +// +// See the NOTICE file(s) distributed with this work for additional +// information. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0 +// +// SPDX-License-Identifier: EPL-2.0 + +import Foundation +import OpenHABCore +import os.log +import WidgetKit + +/// Monitors openHAB items that have active widgets and reloads widgets when items change +@MainActor +class WidgetItemMonitor { + static let shared = WidgetItemMonitor() + + private var monitoringTask: Task? + private var streamTask: Task? + private var isMonitoring = false + + private init() {} + + /// Start monitoring items that have widgets + func startMonitoring() { + guard !isMonitoring else { + return + } + + isMonitoring = true + Logger.widgets.info("Starting widget item monitoring") + + // Start network monitoring for SSE connection + monitoringTask = Task { + await ItemEventStream.startMonitoringNetwork() + } + + // Start listening to item state changes + // Run on background thread to avoid MainActor blocking + streamTask = Task.detached { [weak self] in + guard let self else { return } + + // Update tracked items initially + await updateTrackedItems() + + // Listen for state change events + for await message in await ItemEventStream.shared.stream() { + await handle(message) + } + } + } + + /// Stop monitoring + func stopMonitoring() { + guard isMonitoring else { return } + + Logger.widgets.info("Stopping widget item monitoring") + isMonitoring = false + + monitoringTask?.cancel() + streamTask?.cancel() + monitoringTask = nil + streamTask = nil + } + + /// Manually refresh the list of tracked items (call when widgets are added/removed) + func refreshTrackedItems() async { + await updateTrackedItems() + } + + /// Clean up stale widget items that haven't been refreshed recently + /// Widgets refresh every 5 minutes, so items not updated in 1+ hours are likely deleted + func cleanupStaleItems() async { + let removedCount = await WidgetItemRegistry.shared.removeStaleItems(olderThan: 3600) // 1 hour + + if removedCount > 0 { + Logger.widgets.info("Cleaned up \(removedCount) stale widget items") + await updateTrackedItems() + } + } + + private func updateTrackedItems() async { + let itemNames = await WidgetItemRegistry.shared.getAllItemNames() + + guard !itemNames.isEmpty else { + await ItemEventStream.trackItems([]) + return + } + + Logger.widgets.info("Tracking \(itemNames.count) widget items: \(itemNames.joined(separator: ", "))") + await ItemEventStream.trackItems(itemNames) + } + + private func handle(_ message: StreamOutput) async { + switch message { + case .connected: + await updateTrackedItems() + + case let .event(event): + switch event { + case let .state(item, state): + Logger.widgets.info("Item '\(item)' changed to '\(state)' - reloading widgets") + reloadWidgets(for: item) + + case .ready: + await updateTrackedItems() + + case .alive: + break + + case let .unknown(raw): + Logger.widgets.warning("Unknown SSE message: \(raw)") + } + + case let .disconnected(error): + if let error { + Logger.widgets.warning("SSE disconnected: \(error.localizedDescription)") + } + } + } + + private func reloadWidgets(for itemName: String) { + // Reload all switch and sensor widgets + // Note: This reloads all widgets of these types, not just the specific item + WidgetCenter.shared.reloadTimelines(ofKind: "OpenHABSwitchWidget") + WidgetCenter.shared.reloadTimelines(ofKind: "OpenHABSensorWidget") + } +} diff --git a/openHABWidget/Assets.xcassets/AccentColor.colorset/Contents.json b/openHABWidget/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 000000000..eb8789700 --- /dev/null +++ b/openHABWidget/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/openHABWidget/Assets.xcassets/AppIcon.appiconset/Contents.json b/openHABWidget/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..230588010 --- /dev/null +++ b/openHABWidget/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,35 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/openHABWidget/Assets.xcassets/Contents.json b/openHABWidget/Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/openHABWidget/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/openHABWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json b/openHABWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json new file mode 100644 index 000000000..eb8789700 --- /dev/null +++ b/openHABWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/openHABWidget/Assets.xcassets/openHABIcon.imageset/Contents.json b/openHABWidget/Assets.xcassets/openHABIcon.imageset/Contents.json new file mode 100644 index 000000000..4b0226226 --- /dev/null +++ b/openHABWidget/Assets.xcassets/openHABIcon.imageset/Contents.json @@ -0,0 +1,25 @@ +{ + "images" : [ + { + "filename" : "oh_logo_only.pdf", + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "oh_logo_only_dark.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/openHABWidget/Assets.xcassets/openHABIcon.imageset/oh_logo_only.pdf b/openHABWidget/Assets.xcassets/openHABIcon.imageset/oh_logo_only.pdf new file mode 100644 index 0000000000000000000000000000000000000000..c06a6cd7013aa83e1d94533abaf63827d99cb23a GIT binary patch literal 4414 zcmai&2{crH8^LyDHGh@au+1JU|Sj*0g8H{a;$yP#HBC=;G`L==A z(dX>m)^;!gfC40(D_BVhfEW-woyjhM9L;0`Ky--iWP%s%?v5oBv--*gfag42Kw6WqAVWU}z2HYV+kjbVHNE_t^P zvxRtAumdRVWBW8RNA z2e4EyIg(xmx-1=s#=y=Gql>v*n#b5c%$h~SeU?lR&SUM_U?uD>Vm3yb72QrWW0&TtA+3qqDmS@F z`p)gD^F|CUw`R98<~>VfY8vgL@~vcCJ-R)?C!}%C0cJAFBze0z*&nyaX<4#p+6dV$ zVvJ|;mj=sbn8)cpMvRUS$t8E%79aAlJE*H?#1C`jtO!-{8WCAfT~B(b1U)S9dJ#;SV$`REJtQNJ zJaS6o;AcRl3ofjH`L&6P-A~M}09}Z2L(a@Yk0Nyr@m_Z>)H{1RKH`S1wIb}Tz3I%L z%li99y>cQ31U^|m z!<`#X7@E8lZr?IEp1rvpe6}|gc2U~Y1Q8r_OO(f?3ar`f<46&9Nm-25DZnFBcUsvM z)gyUAB(^HX>iM`z<{r(6wf8qNk2{-Qf0?WlV3?_u{s{YexIfU1G>2z9)!#~_G6uyd z92r-AI2tyoBcp%P$?hqJyL~io<7lWFzciTOiT@=>+GtP3d(z#zBN2$b$-SZM9|AY? zzd!&WMp)+`S1*Dm8G!#3y9vRYbjiz+;0+*u8Z=3sWZJwpuvZ`^d&2(}Z*Ts0!5Ddw z98C#iz?v3RO9!w8Aetn1l9%ZPtRn%~t0+xx7$E;Ez#lOb{)q9b=ukhrAZKX_LDcsO z&y!Yl07Q%6Lv$pVpf&z~J!ahYH8UOIjiA)Y*YzG00mRk>16Y7pif4F0#*wCq7)j@sO;*RSD*Sw3+ZwKo}SX;a6d-B>=B%6%SB-|4WN7@2)K zJmUN@aJYuf=-V|=GueVZ7?ESa{b8}m=HQ{O)(r-JPI`6=`o>8oClGp5i(%kfopM^* zy)*5V>mOu-@wKB;4MyVLL%TuoBJ!G|`{}^igGi;T;(SzwB-6?^0oh&9%e7=l*>IRF zZw^atiO5uIx%+z2%&FSW)D-dQ96`$-f#-f8&bt<_4?uQ=GLq!z=;8-50qwJNq-P`==D(rs;eF-wm$m-yvU;pJt^p&6I2 z4I>jruA1&BzX;eiUlm`XWU5DJT28nxl|brv>DEQ9?i{YHPd2Bk^4PCNoh-q81n55P zqpKRf`}s(?yIn%-do~Hyn@daX-t<{+GNP8z6-n9i)T#p1oha-{_FCPLkt7!JlUk+Z>w5E&?gl*0kxVE6^baeyu~ z_zWlGZAO_h3`8~1BXyP6bYOMZYkHsSt{3PALIQe0XRjeI?6(1(Z#-~;l`R;p1`co1 zhtiLS5Sg#7aPs7;-;d+X=Sb5S;uNxC_SaN5Qs8E6(~OMcOi=sUD}ImvEPSUWwAJzwZq%$fL$(~SEg zacK(6K}RNHkG3~R#Iaa`+^%sPyA6oCi<)r?GS4#agmN__j6?2W$RgpK{;f7R z$%nUIt3THj7C)QFts5@NXCf+@Ruy8V>ndU^t}I@6EV1)69___%sb+^M6U_qb#W)gF zjgn4?b7S;`wKYc0qV%4r`K$SB7ML8k=Q&_;KeZ;q3R9;Qs22!9C&E)cr%ZL0CO(LF zOI}R5EQU!MPVr5#m&uZ_KNh6-z3^K#gK?8ulW9m^#km}v$*N}MFkS8R++^GhTskfi zcm2Iwo3YsQgph=%2QkHJ^A)ee3XTbC^cVQQs;)AuI{k+526`ARpTFIoIN!E*bgpiV zg&;<-rkLwNnUoBB#P0OBh=q!p$bjlC z*Doy#$_t5t>4Now0)pm(DTsDCfppLG&U9+}ETW^z#ftSY$ckz;``8Yeoc5vH%t@^B zCMu`2Y`I9fPbGisfpV$iy-Ew`yt9=(hinsMtmW08+B`M&)xhXOEFkvf#vgN3jI&C! z@*B_%)l`y zcZ}CwYFWxudQs}ArKIIP3y-RWimjG6t?6#Ij)5KFu2YVcJ%wX4k4uZCCwv-~0~OP4 zE5>+h&(>1b+1Ew3#kXNh$`O7Mb?h6j$v!7)Ljwo77Uw3bdsd^Tm?SPsJnYvkn#i#1 zk?c9plgG-&x576d&F|HBak5(2D_#aK6K(HzA!opRGH~&6a1&n_or>Q6j$NUv2Jp)z23sjnmfBY^^;L`{LCL^vxJveO_OkPIR00Algv- zvUZBLeu{;ZW#zO^v{f`vsLXp*i087Xk5}==t1L4wM zJg8>POXA=M^Fz66eenh-ye*w-Q*V7Y_;My%^p4ndOV&!)N8`?DLe@*;ZmyZN>GWyO zuEU+5JGZaOIm!86a<2FJ9{#OK?t>e9Y1mMi&FHhUzP3#@lV9o=V?6+mGb1ZqI$bdY zp$X5Z@d4v(rZ;EqO5X;-VQ}R z$wv||8(f~45AM%6+;do3_0H~-S#*0ln2gbC|kwAjKI3(@ST{UBej{g?U$|+=O2Ic zsGGk~xpRA}^j7`8z}YR^ZOHg~O(6BwZiW!!&HY;+Wa=;P+Pzht4;?VIL$F%ID6jM&=ROu|B zOVO0*uhC-x57*jOx}sD1r@|T}zdzYX?t74R)pxtj8Q0mm;?}pArm8o7uO78EwC*vB zpPTOYC4MX{iW+}Vueu(xle6u())7CygxSO-XT_-cUSaw6$xqzFX=Y+0^z-)r^79^r z{(xpAO!hbM?(yrMjRv{8nwlC|Zvr0J16UKl_P5I(ME_>uzZlyafEW_+M63qM53q*P zT;%r7ACP>BM&2+0qEEzo)8Kgz(P`2C0A$#n``;sKV#!!{lJie|_x{E0e_=Tc_G`rn z53D1`6gWrl@+Oiz0T@&UhLAx5HYYSL5#8|sOaYCOvylXhFJZmN0RRo#e~`W(S@QSG zzLSZN|9t){eLpXP6Br7>;9%%~4ge`92bTk!fL}H^N|u(|-T`?2wn63PXy5Gb zHaJqA)(8FF28I69S_EyI|I1GS{txl~!4Czc>EiEe;j~TqADg@!tzr5vKLzMN*cAR) zOZLJN-3eYleoB}U18Dn4>xxWBB${aUghAU#T~8+xO+i1$S~LwQAsz8Jd67?mp+@%1R&eiW-e9;zM7e{?y&E3cwl;ObXIqtWv$ zs3|Y*oUaZ1Q@gG1{gJj+1_!u-KrV6nVQ!3B#clHCXzT8|?27KLn)0Bv7y=(^V#v{t zshj~zeB_+)k`wuUA`{fK&Kvv^F!cm0-#$#ZgUM7qd}raFOty z+gIlaA6RbAYNpS9naI#E+%e6&nsM{Q&KR$t#wB~G@i2qL-Ns~J%p$u*@uEpRc&Cs) zp2=4VD3f6pr~3>xJVYQB-)mWX!prh1%dLngreaqws;VfI2T8y?ed#g@Nvv-`7?F+a zw_ay0xTn{s=hPH3lpWH`VLgf)v) z?jZ5)@{w9zj^eqeGor1%^^BvACb!-sEBP55Z>%*AL))P?OUT)Y&|-bg!eq8x^$8!J z{bqOGFu|9SMXA0cDn@+7*wKI(OFnHN-W~T#j?~eCiVviFaF>I@4kizVGJgo%)b|P= z1U5uF{k|B81o|GQueJ&0Hn zJPBk)4XULBvH^iLiLOKslPhQ}9&}Jqnx0UQ{I39i#8CJn#;>A7{O|%_q$UJbKPWtR zYSn?jT6ix47H^Ey`2Y2oao5|_WQZr6LY60Y9~B0Pt_%1vf!->f=LQ%>=mr|ng@uTD zX&utx;*hw6Gp(mZh6`i0xtMPSV+NT%vm3TH7-?xwk3_jJt(3sOjHd6kS&R+Mz8D;G zT=5^QrZLgn!2jXgmCF=~uJO}mz;)UfkMGn&dwfp6iZi?|v(14y01?I;YzdXoE6J}PaJ7#NQOOyxdQ4cJ}T$hT$WFDFg5zBkWDr%F>Xe!+ft4)s=qgFsPpAXSg zj^6)rJj~TLp?QKuocYeulB*|eri=91Ir*K;VB(v)FvMnB$ETf3iUqu?L8o-NW%1h6 z1%(9{v^qOkSW_sty-oK0$$A9gU+qnj_cQBx*FxvajOLP}dQ$5r$Q|46ljGG@7UAMch^W==>M0*rCAJwXd8?;>$c^W&FO+ zZl7Sbj&vW_R~I&!*3)xHixojbk8X0bxPX5IRZwBSL|)SLjDUG$DcK z+3D}nOP{ABr~#g;tGuHDszcw=dfjrqLem%I*A2KBEO+IwHQ;jnkt@tBfk-uASi=Pf z?Pw5zF?f}oJ4gLd98Vrwn#KUTpe3WPrn;d57k!InL>zm9+P7}8hkR#lJ<8TJR9-(t z5~yahxwW0e&<%ne-lO#o;Yg+HMIU)b^E_~%ZK|u18@ECxLy^H3hRqT z-f_WI8p%+*#zaQ7*NpT%I_8XmYg!s{<|4ElCtc!&^XQ7S)8kn41YbsglF zoyy14%0C%y5jBpvgdV`?LHbj%sh9M5XXG5%WrMy2=VSVx6qt(_t9miXaomfRYQAJ2 zYj0Ja@`U~!dPz};wXL3iLg>oPDn4r&cX%n_1>0a$P2ghFk#Vs#pI2u_xmJSN8a!7q zH%`_91B3#OkHwy7trL%9vIMvUvz@#P5^)tVWfx$aW!wwlXoMLBJw%a&!`OYBtuYc$ zV&190(iIZBn8>9YCcyS7 zPK$A&^n|oEhAtxXUaI-3`D*4HA9?8BXZ|R)I>Qo0*7Dc$2O|?@Q@*53wwEM6j(163 zOt~(KN*YY@PO+2D6t_DWp!dCCw~EfFL9M|gD7XAlw$6BEqjIROc6v@S<~AlB6M?xk z0dFx9eU%WD@Zu<{NNv9St!Vy90gc{#-?vqj29@XDv;Ww`7g^^P>`@YNBGlz-C@jCMm;mWdz&(R}#tJ&bXhsku03- zkW8B#`$*r6*w59U@yP8Fst@8U=8Se8a^C5eejH6Hqr*^{^%Bv&%s*VdAZUx&Sr@+m#uuWUClumSpDD zA?vIH-qd%4-rL_%zt`|EJ2JZt{Q#Xijy?4#{&CNlT+3X=Rt4l?Yg_r9HFR=#6?Cxd5% z$4+ut(nM-e@`Qzi#UXRI%7yanruWV1E;d;IwlL>OY(-bW$jq~nBB?R2x@CXGber-K zo|=m_lnvGm;T^FZD1&miPdJ%%^Bu|SbWMnVKgZ(ScvaV0)FgxWb@3;?x`ksI7F`lu zN4j#ES$J1@`=t0hdajOF33kGxYJ+N;_* z%XWb+9(kuNsW5R?fWmBhJEiP${N=J237eP|=aoX0b1D%k(JGv^UbTm}w4}2hagrVo+h0VLE!~8lb6xwxK5Y1o*9;h;<(ioyM-FSEv#b zoW+KF+mlaP5>_m>q6{r8Lg2 z!Q$$fzA(Ez4V?23?|lu?jr8L__ zV0BA%xpgP$Q0mVYtWTv+xpy3E z|I)s56Yc=_zUEl#^*wC20shH_wIp<))Oz^kMQ@vi>hZ6&i?MDXxAQ}*9XcJ+c)>CE z$k9HdETi&Td#8$(nO2RL8b;$}sV*y4*A`c9*X0q1iW2AH6k*$*ub7UB$O)9_hlIs3 z3;)aCMmje_mdW`W`8(bpy%@bejLcVmF;Bx-*ba4?);qd`9;m;rP@nqtE={G zgjif`rOd;pWW6$HWPjm8-a?Q-Z2Am&;rff^jQ4HBgLPwW0ll=*^eqf3$GL=F1-#xg zbSY7HYe9b)FK(hN<}6g#IR@ATXf2;#i>Dw%k@;GyKWRVSCX7$7>$kwOhJLn18n7MxMV? zv3GZ}B&PO||LnHS4tR8<+J8D`KSPlI&f)D((zVz3Z9gc_`;YGRFch)bs&@J*>^bdW zmnL(>fv7VN9;ue>^=}P26p`1LvIgxb6}KH@!>TmF`^n$RP)&y@MU=BTl{yQ^5+o(+ zThxf(ll7L>j;NI0$oIWDzpd)D8}i`)?aW9!~vcf49lX z$y58FzuO>?e_9KpZu5WnDaigqynpaRK&ZO-`&wD*CjF019!_nT{>x7R@((tJKh}~w z&;(b!$B&;9CImm~{!zOkVc s6d*7h9tX$CV<8wc90rA|0RO+sPYrsKsM7fHL1YmKd7!AMHcAKhZzLCn3;+NC literal 0 HcmV?d00001 diff --git a/openHABWidget/Info.plist b/openHABWidget/Info.plist new file mode 100644 index 000000000..0f118fb75 --- /dev/null +++ b/openHABWidget/Info.plist @@ -0,0 +1,11 @@ + + + + + NSExtension + + NSExtensionPointIdentifier + com.apple.widgetkit-extension + + + diff --git a/openHABWidget/OpenHABWidget.entitlements b/openHABWidget/OpenHABWidget.entitlements new file mode 100644 index 000000000..029463f3a --- /dev/null +++ b/openHABWidget/OpenHABWidget.entitlements @@ -0,0 +1,14 @@ + + + + + com.apple.security.application-groups + + group.org.openhab.app + + keychain-access-groups + + $(AppIdentifierPrefix)org.openhab.app + + + diff --git a/openHABWidget/OpenHABWidgetBundle.swift b/openHABWidget/OpenHABWidgetBundle.swift new file mode 100644 index 000000000..e534518b5 --- /dev/null +++ b/openHABWidget/OpenHABWidgetBundle.swift @@ -0,0 +1,23 @@ +// Copyright (c) 2010-2026 Contributors to the openHAB project +// +// See the NOTICE file(s) distributed with this work for additional +// information. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0 +// +// SPDX-License-Identifier: EPL-2.0 + +import SwiftUI +import WidgetKit + +@main +struct OpenHABWidgetBundle: WidgetBundle { + var body: some Widget { + SwitchSmallWidget() + SwitchMediumWidget() + SwitchLargeWidget() + SensorWidgetView() + } +} diff --git a/openHABWidget/OpenHABWidgetHelpers.swift b/openHABWidget/OpenHABWidgetHelpers.swift new file mode 100644 index 000000000..00618d5ed --- /dev/null +++ b/openHABWidget/OpenHABWidgetHelpers.swift @@ -0,0 +1,31 @@ +// Copyright (c) 2010-2026 Contributors to the openHAB project +// +// See the NOTICE file(s) distributed with this work for additional +// information. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0 +// +// SPDX-License-Identifier: EPL-2.0 + +import OpenHABCore +internal import SFSafeSymbols +import SwiftUI + +// MARK: - Item Type Icons + +/// Returns an SF Symbol icon for the given openHAB item type +func itemTypeIcon(for type: OpenHABItem.ItemType?) -> SFSymbol { + guard let type else { return .circleFill } + switch type { + case .switchItem: return .power + case .color: return .paintpalette + case .dimmer: return .lightMax + case .number: return .number + case .stringItem: return .textformat + case .contact: return .doorLeftHandOpen + case .rollershutter: return .blindsVerticalClosed + default: return .circleFill + } +} diff --git a/openHABWidget/SensorConfigurationAppIntent.swift b/openHABWidget/SensorConfigurationAppIntent.swift new file mode 100644 index 000000000..7cbffcfb4 --- /dev/null +++ b/openHABWidget/SensorConfigurationAppIntent.swift @@ -0,0 +1,24 @@ +// Copyright (c) 2010-2026 Contributors to the openHAB project +// +// See the NOTICE file(s) distributed with this work for additional +// information. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0 +// +// SPDX-License-Identifier: EPL-2.0 + +import AppIntents +import Foundation + +struct SensorConfigurationAppIntent: WidgetConfigurationIntent { + static let title: LocalizedStringResource = "Sensor Widget Configuration" + static let description = IntentDescription("Configure which sensor item to display in the widget.") + + @Parameter(title: "Home") + var home: Home? + + @Parameter(title: "Sensor Item") + var itemEntity: SensorWidgetItemEntity? +} diff --git a/openHABWidget/SensorWidgetEntryView.swift b/openHABWidget/SensorWidgetEntryView.swift new file mode 100644 index 000000000..e2755e7e7 --- /dev/null +++ b/openHABWidget/SensorWidgetEntryView.swift @@ -0,0 +1,439 @@ +// Copyright (c) 2010-2026 Contributors to the openHAB project +// +// See the NOTICE file(s) distributed with this work for additional +// information. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0 +// +// SPDX-License-Identifier: EPL-2.0 + +import AppIntents +import Foundation +import OpenHABCore +internal import os.log +internal import SFSafeSymbols +import SwiftUI +import WidgetKit + +// MARK: - Timeline Entry + +struct SensorEntry: TimelineEntry { + let date: Date + let configuration: SensorConfigurationAppIntent + let item: OpenHABItem? + let homeUUID: UUID? +} + +// MARK: - Timeline Provider + +struct SensorProvider: AppIntentTimelineProvider { + func placeholder(in context: Context) -> SensorEntry { + // Create a sample sensor item for placeholder + let sampleItem = OpenHABItem( + name: "LivingRoomTemperature", + type: "Number:Temperature", + state: "22.5 °C", + link: "", + label: "Living Room Temperature", + groupType: nil, + stateDescription: nil, + commandDescription: nil, + members: [], + category: "temperature", + options: nil + ) + + return SensorEntry( + date: Date(), + configuration: SensorConfigurationAppIntent(), + item: sampleItem, + homeUUID: nil + ) + } + + func snapshot(for configuration: SensorConfigurationAppIntent, in context: Context) async -> SensorEntry { + // Show placeholder data in widget gallery + if context.isPreview { + return placeholder(in: context) + } + return await createEntry(for: configuration) + } + + func timeline(for configuration: SensorConfigurationAppIntent, in context: Context) async -> Timeline { + let entry = await createEntry(for: configuration) + + // Refresh every 5 minutes + let nextUpdate = Calendar.current.date(byAdding: .minute, value: 5, to: Date())! + return Timeline(entries: [entry], policy: .after(nextUpdate)) + } + + private func createEntry(for configuration: SensorConfigurationAppIntent) async -> SensorEntry { + guard let itemEntity = configuration.itemEntity else { + return SensorEntry( + date: Date(), + configuration: configuration, + item: nil, + homeUUID: nil + ) + } + + // Get item from entity + let item = itemEntity.item + guard let homeUUID = itemEntity.homeId else { + return SensorEntry( + date: Date(), + configuration: configuration, + item: item, + homeUUID: nil + ) + } + + // Register this item for monitoring in the main app + await WidgetItemRegistry.shared.registerItem(name: item.name, homeId: homeUUID) + + // Refresh the item state from cache + let refreshedItem = await OpenHABItemCache.instance.getItemUncached(name: item.name, home: homeUUID) + + return SensorEntry( + date: Date(), + configuration: configuration, + item: refreshedItem ?? item, + homeUUID: homeUUID + ) + } +} + +// MARK: - Small Widget + +struct SensorSmallWidgetView: View { + let entry: SensorEntry + + var body: some View { + ZStack(alignment: .topLeading) { + VStack(spacing: 8) { + if let item = entry.item { + let itemLabel = item.label.isEmpty ? item.name : item.label + Text(itemLabel) + .font(.headline) + .lineLimit(1) + .minimumScaleFactor(0.7) + .padding(.top, 20) + + Spacer() + + if let itemState = item.state { + Text(itemState) + .font(.title2) + .fontWeight(.bold) + .lineLimit(2) + .minimumScaleFactor(0.5) + .multilineTextAlignment(.center) + } else { + Text("—") + .font(.title2) + .foregroundColor(.secondary) + } + + Spacer() + } else { + VStack { + Image(systemSymbol: .gear) + .font(.largeTitle) + .foregroundColor(.secondary) + Text("Configure Widget") + .font(.caption) + .foregroundColor(.secondary) + .multilineTextAlignment(.center) + } + } + } + .frame(maxHeight: .infinity) + .padding() + + // Widget indicator icon + Image("openHABIcon") + .resizable() + .scaledToFit() + .frame(width: 16, height: 16) + .opacity(0.5) + .padding(8) + } + } +} + +// MARK: - Medium Widget + +struct SensorMediumWidgetView: View { + let entry: SensorEntry + + var body: some View { + ZStack(alignment: .topLeading) { + HStack(spacing: 16) { + VStack(alignment: .leading, spacing: 8) { + if let item = entry.item { + let itemLabel = item.label.isEmpty ? item.name : item.label + Text(itemLabel) + .font(.title3) + .fontWeight(.semibold) + .lineLimit(2) + .padding(.top, 20) + + Spacer() + + if let itemState = item.state { + Text(itemState) + .font(.system(size: 36, weight: .bold)) + .foregroundColor(.primary) + .lineLimit(2) + } else { + Text("No Data") + .font(.title) + .foregroundColor(.secondary) + } + + Text(item.type?.rawValue ?? "Sensor") + .font(.caption) + .foregroundColor(.secondary) + } else { + VStack(alignment: .leading) { + Image(systemSymbol: .gear) + .font(.largeTitle) + .foregroundColor(.secondary) + Text("Configure Widget") + .font(.subheadline) + .foregroundColor(.secondary) + } + } + } + + Spacer() + } + .frame(maxHeight: .infinity) + .padding() + + // Widget indicator icon + Image("openHABIcon") + .resizable() + .scaledToFit() + .frame(width: 18, height: 18) + .opacity(0.5) + .padding(8) + } + } +} + +// MARK: - Large Widget + +struct SensorLargeWidgetView: View { + let entry: SensorEntry + + var body: some View { + ZStack(alignment: .topLeading) { + VStack(alignment: .leading, spacing: 16) { + if let item = entry.item { + let itemLabel = item.label.isEmpty ? item.name : item.label + Text(itemLabel) + .font(.title) + .fontWeight(.bold) + .padding(.top, 20) + + HStack { + VStack(alignment: .leading, spacing: 8) { + Text("Current Value") + .font(.caption) + .foregroundColor(.secondary) + + if let itemState = item.state { + Text(itemState) + .font(.system(size: 48, weight: .bold)) + .foregroundColor(.primary) + .lineLimit(2) + } else { + Text("—") + .font(.system(size: 48)) + .foregroundColor(.secondary) + } + } + + Spacer() + } + + HStack { + Image(systemSymbol: sensorIcon(for: item.type)) + .font(.caption) + Text(item.type?.rawValue ?? "Sensor") + .font(.caption) + } + .foregroundColor(.secondary) + + Spacer() + } else { + VStack(alignment: .center, spacing: 16) { + Image(systemSymbol: .gear) + .font(.system(size: 60)) + .foregroundColor(.secondary) + Text("Configure Widget") + .font(.title2) + .foregroundColor(.secondary) + Text("Long press the widget to configure which sensor to display") + .font(.caption) + .foregroundColor(.secondary) + .multilineTextAlignment(.center) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + } + .frame(maxHeight: .infinity) + .padding() + + // Widget indicator icon + Image("openHABIcon") + .resizable() + .scaledToFit() + .frame(width: 20, height: 20) + .opacity(0.5) + .padding(12) + } + } + + private func sensorIcon(for type: OpenHABItem.ItemType?) -> SFSymbol { + guard let type else { return .gaugeWithDotsNeedleBottom50percent } + switch type { + case .number, .numberWithDimension: + return .gaugeWithDotsNeedleBottom50percent + case .stringItem: + return .textQuote + default: + return .gaugeWithDotsNeedleBottom50percent + } + } +} + +// MARK: - Accessory Views + +struct SensorAccessoryCircularView: View { + let entry: SensorEntry + + var body: some View { + if let item = entry.item, let itemState = item.state { + ZStack { + AccessoryWidgetBackground() + VStack(spacing: 2) { + Image(systemSymbol: .gaugeWithDotsNeedleBottom50percent) + .font(.caption) + Text(itemState) + .font(.caption2) + .fontWeight(.bold) + .lineLimit(1) + .minimumScaleFactor(0.5) + } + } + } else { + ZStack { + AccessoryWidgetBackground() + Image(systemSymbol: .gear) + .font(.title3) + } + } + } +} + +struct SensorAccessoryRectangularView: View { + let entry: SensorEntry + + var body: some View { + if let item = entry.item { + let itemLabel = item.label.isEmpty ? item.name : item.label + VStack(alignment: .leading, spacing: 2) { + Text(itemLabel) + .font(.headline) + .lineLimit(1) + .minimumScaleFactor(0.7) + + if let itemState = item.state { + Text(itemState) + .font(.body) + .fontWeight(.semibold) + .lineLimit(1) + } else { + Text("No Data") + .font(.caption) + .foregroundColor(.secondary) + } + } + } else { + Text("Configure") + .font(.caption) + } + } +} + +struct SensorAccessoryInlineView: View { + let entry: SensorEntry + + var body: some View { + if let item = entry.item { + let itemLabel = item.label.isEmpty ? item.name : item.label + if let itemState = item.state { + Text("\(itemLabel): \(itemState)") + } else { + Text(itemLabel) + } + } else { + Text("Configure Widget") + } + } +} + +// MARK: - Widget View + +struct SensorWidgetEntryView: View { + var entry: SensorEntry + @Environment(\.widgetFamily) var family + + var body: some View { + switch family { + case .systemSmall: + SensorSmallWidgetView(entry: entry) + case .systemMedium: + SensorMediumWidgetView(entry: entry) + case .systemLarge: + SensorLargeWidgetView(entry: entry) + case .accessoryCircular: + SensorAccessoryCircularView(entry: entry) + case .accessoryRectangular: + SensorAccessoryRectangularView(entry: entry) + case .accessoryInline: + SensorAccessoryInlineView(entry: entry) + default: + SensorSmallWidgetView(entry: entry) + } + } +} + +// MARK: - Preview + +#Preview(as: .systemSmall) { + SensorWidgetView() +} timeline: { + SensorEntry( + date: .now, + configuration: SensorConfigurationAppIntent(), + item: OpenHABItem( + name: "LivingRoomTemperature", + type: "Number:Temperature", + state: "22.5 °C", + link: "", + label: "Living Room Temperature", + groupType: nil, + stateDescription: nil, + commandDescription: nil, + members: [], + category: "temperature", + options: nil + ), + homeUUID: nil + ) +} diff --git a/openHABWidget/SensorWidgetItemEntity.swift b/openHABWidget/SensorWidgetItemEntity.swift new file mode 100644 index 000000000..e5e29a991 --- /dev/null +++ b/openHABWidget/SensorWidgetItemEntity.swift @@ -0,0 +1,38 @@ +// Copyright (c) 2010-2026 Contributors to the openHAB project +// +// See the NOTICE file(s) distributed with this work for additional +// information. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0 +// +// SPDX-License-Identifier: EPL-2.0 + +import AppIntents +import OpenHABCore + +struct SensorWidgetItemEntity: ItemEntity { + struct SensorWidgetItemQuery: ItemEntityQuery { + typealias EntityType = SensorWidgetItemEntity + + @IntentParameterDependency(\.$home) + var intent + + var allowedTypes: [OpenHABItem.ItemType] = [.number, .numberWithDimension, .stringItem] + var selectedHome: Home? { intent?.home } + } + + static let typeDisplayRepresentation = TypeDisplayRepresentation(name: "Sensor Item") + static let defaultQuery = SensorWidgetItemQuery() + + var id: ItemIdentifier + var item: OpenHABItem + var homeName: String? + + init(id: ItemIdentifier, item: OpenHABItem, homeName: String? = nil) { + self.id = id + self.item = item + self.homeName = homeName + } +} diff --git a/openHABWidget/SensorWidgetView.swift b/openHABWidget/SensorWidgetView.swift new file mode 100644 index 000000000..d4360de54 --- /dev/null +++ b/openHABWidget/SensorWidgetView.swift @@ -0,0 +1,39 @@ +// Copyright (c) 2010-2026 Contributors to the openHAB project +// +// See the NOTICE file(s) distributed with this work for additional +// information. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0 +// +// SPDX-License-Identifier: EPL-2.0 + +import SwiftUI +import WidgetKit + +struct SensorWidgetView: Widget { + let kind = "OpenHABSensorWidget" + + var body: some WidgetConfiguration { + AppIntentConfiguration( + kind: kind, + intent: SensorConfigurationAppIntent.self, + provider: SensorProvider() + ) { entry in + SensorWidgetEntryView(entry: entry) + .containerBackground(.fill.tertiary, for: .widget) + } + .configurationDisplayName("openHAB Sensor") + .description("Display your openHAB sensor values") + .supportedFamilies([ + .systemSmall, + .systemMedium, + .systemLarge, + .accessoryCircular, + .accessoryRectangular, + .accessoryInline + ]) + .widgetContentMarginsDisabled() + } +} diff --git a/openHABWidget/SwitchLargeConfigurationAppIntent.swift b/openHABWidget/SwitchLargeConfigurationAppIntent.swift new file mode 100644 index 000000000..92da24245 --- /dev/null +++ b/openHABWidget/SwitchLargeConfigurationAppIntent.swift @@ -0,0 +1,64 @@ +// Copyright (c) 2010-2026 Contributors to the openHAB project +// +// See the NOTICE file(s) distributed with this work for additional +// information. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0 +// +// SPDX-License-Identifier: EPL-2.0 + +import AppIntents +import Foundation + +// MARK: - Small / accessory (1 item) + +struct SwitchSmallConfigurationAppIntent: WidgetConfigurationIntent { + static let title: LocalizedStringResource = "Switch Widget Configuration" + static let description = IntentDescription("Configure which switch item to control in the widget.") + + @Parameter(title: "Home") + var home: Home? + + @Parameter(title: "Switch Item") + var item1: SwitchWidgetItemEntity? +} + +// MARK: - Medium (2 items) + +struct SwitchMediumConfigurationAppIntent: WidgetConfigurationIntent { + static let title: LocalizedStringResource = "Switch Widget Configuration" + static let description = IntentDescription("Configure which switch items to control in the widget.") + + @Parameter(title: "Home") + var home: Home? + + @Parameter(title: "Switch Item 1") + var item1: SwitchMediumWidgetItemEntity? + + @Parameter(title: "Switch Item 2") + var item2: SwitchMediumWidgetItemEntity? +} + +// MARK: - Large (4 items) + +struct SwitchLargeConfigurationAppIntent: WidgetConfigurationIntent { + static let title: LocalizedStringResource = "Switch Widget Configuration" + static let description = IntentDescription("Configure which switch items to control in the widget.") + + @Parameter(title: "Home") + var home: Home? + + @Parameter(title: "Switch Item 1") + var item1: SwitchLargeWidgetItemEntity? + + @Parameter(title: "Switch Item 2") + var item2: SwitchLargeWidgetItemEntity? + + @Parameter(title: "Switch Item 3") + var item3: SwitchLargeWidgetItemEntity? + + @Parameter(title: "Switch Item 4") + var item4: SwitchLargeWidgetItemEntity? +} diff --git a/openHABWidget/SwitchLargeWidget.swift b/openHABWidget/SwitchLargeWidget.swift new file mode 100644 index 000000000..eed891e38 --- /dev/null +++ b/openHABWidget/SwitchLargeWidget.swift @@ -0,0 +1,81 @@ +// Copyright (c) 2010-2026 Contributors to the openHAB project +// +// See the NOTICE file(s) distributed with this work for additional +// information. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0 +// +// SPDX-License-Identifier: EPL-2.0 + +import SwiftUI +import WidgetKit + +// MARK: - Small + accessory widget (1 item) + +struct SwitchSmallWidget: Widget { + let kind = "OpenHABSwitchWidgetSmall" + + var body: some WidgetConfiguration { + AppIntentConfiguration( + kind: kind, + intent: SwitchSmallConfigurationAppIntent.self, + provider: SwitchSmallProvider() + ) { entry in + SwitchWidgetEntryView(entry: entry) + .containerBackground(.fill.tertiary, for: .widget) + } + .configurationDisplayName("openHAB Switch") + .description("Control an openHAB switch item") + .supportedFamilies([ + .systemSmall, + .accessoryCircular, + .accessoryRectangular, + .accessoryInline + ]) + .widgetContentMarginsDisabled() + } +} + +// MARK: - Medium widget (2 items) + +struct SwitchMediumWidget: Widget { + let kind = "OpenHABSwitchWidgetMedium" + + var body: some WidgetConfiguration { + AppIntentConfiguration( + kind: kind, + intent: SwitchMediumConfigurationAppIntent.self, + provider: SwitchMediumProvider() + ) { entry in + SwitchWidgetEntryView(entry: entry) + .containerBackground(.fill.tertiary, for: .widget) + } + .configurationDisplayName("openHAB Switch") + .description("Control two openHAB switch items") + .supportedFamilies([.systemMedium]) + .widgetContentMarginsDisabled() + } +} + +// MARK: - Large widget (4 items) + +struct SwitchLargeWidget: Widget { + let kind = "OpenHABSwitchWidgetLarge" + + var body: some WidgetConfiguration { + AppIntentConfiguration( + kind: kind, + intent: SwitchLargeConfigurationAppIntent.self, + provider: SwitchLargeProvider() + ) { entry in + SwitchWidgetEntryView(entry: entry) + .containerBackground(.fill.tertiary, for: .widget) + } + .configurationDisplayName("openHAB Switch") + .description("Control up to four openHAB switch items") + .supportedFamilies([.systemLarge]) + .widgetContentMarginsDisabled() + } +} diff --git a/openHABWidget/SwitchLargeWidgetItemEntity.swift b/openHABWidget/SwitchLargeWidgetItemEntity.swift new file mode 100644 index 000000000..908d3a5c5 --- /dev/null +++ b/openHABWidget/SwitchLargeWidgetItemEntity.swift @@ -0,0 +1,38 @@ +// Copyright (c) 2010-2026 Contributors to the openHAB project +// +// See the NOTICE file(s) distributed with this work for additional +// information. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0 +// +// SPDX-License-Identifier: EPL-2.0 + +import AppIntents +import OpenHABCore + +struct SwitchLargeWidgetItemEntity: ItemEntity { + struct SwitchLargeWidgetItemQuery: ItemEntityQuery { + typealias EntityType = SwitchLargeWidgetItemEntity + + @IntentParameterDependency(\.$home) + var intent + + var allowedTypes: [OpenHABItem.ItemType] = [.switchItem] + var selectedHome: Home? { intent?.home } + } + + static let typeDisplayRepresentation = TypeDisplayRepresentation(name: "Switch Item") + static let defaultQuery = SwitchLargeWidgetItemQuery() + + var id: ItemIdentifier + var item: OpenHABItem + var homeName: String? + + init(id: ItemIdentifier, item: OpenHABItem, homeName: String? = nil) { + self.id = id + self.item = item + self.homeName = homeName + } +} diff --git a/openHABWidget/SwitchMediumWidgetItemEntity.swift b/openHABWidget/SwitchMediumWidgetItemEntity.swift new file mode 100644 index 000000000..86ada8cbd --- /dev/null +++ b/openHABWidget/SwitchMediumWidgetItemEntity.swift @@ -0,0 +1,38 @@ +// Copyright (c) 2010-2026 Contributors to the openHAB project +// +// See the NOTICE file(s) distributed with this work for additional +// information. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0 +// +// SPDX-License-Identifier: EPL-2.0 + +import AppIntents +import OpenHABCore + +struct SwitchMediumWidgetItemEntity: ItemEntity { + struct SwitchMediumWidgetItemQuery: ItemEntityQuery { + typealias EntityType = SwitchMediumWidgetItemEntity + + @IntentParameterDependency(\.$home) + var intent + + var allowedTypes: [OpenHABItem.ItemType] = [.switchItem] + var selectedHome: Home? { intent?.home } + } + + static let typeDisplayRepresentation = TypeDisplayRepresentation(name: "Switch Item") + static let defaultQuery = SwitchMediumWidgetItemQuery() + + var id: ItemIdentifier + var item: OpenHABItem + var homeName: String? + + init(id: ItemIdentifier, item: OpenHABItem, homeName: String? = nil) { + self.id = id + self.item = item + self.homeName = homeName + } +} diff --git a/openHABWidget/SwitchWidgetEntryView.swift b/openHABWidget/SwitchWidgetEntryView.swift new file mode 100644 index 000000000..ed7967e3d --- /dev/null +++ b/openHABWidget/SwitchWidgetEntryView.swift @@ -0,0 +1,492 @@ +// Copyright (c) 2010-2026 Contributors to the openHAB project +// +// See the NOTICE file(s) distributed with this work for additional +// information. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0 +// +// SPDX-License-Identifier: EPL-2.0 + +import AppIntents +import Foundation +import OpenHABCore +internal import os.log +internal import SFSafeSymbols +import SwiftUI +import WidgetKit + +// MARK: - Timeline Entry + +struct SwitchEntry: TimelineEntry { + struct Slot { + let item: OpenHABItem + let homeUUID: UUID + } + + let date: Date + let home: Home? + /// Always contains exactly as many elements as the widget size supports (1, 2, or 4). + /// nil means the slot is not configured. + let slots: [Slot?] +} + +// MARK: - Helper Functions + +private func createToggleIntent(item: OpenHABItem, homeUUID: UUID, home: Home) -> SetSwitchItemIntent { + let intent = SetSwitchItemIntent() + intent.itemEntity = SwitchItemEntity(item, homeId: homeUUID, homeName: home.displayString) + intent.action = .toggle + intent.home = home + return intent +} + +private func sampleItem(name: String, label: String, state: String) -> OpenHABItem { + OpenHABItem( + name: name, type: "Switch", state: state, link: "", + label: label, groupType: nil, stateDescription: nil, + commandDescription: nil, members: [], category: "light", options: nil + ) +} + +// MARK: - Shared provider logic + +/// Common interface for all switch widget entity types, used so resolveSlot +/// can be a single function regardless of which configuration intent the entity +/// was declared in. +private protocol SwitchSlotResolvable { + var homeId: UUID? { get } + var item: OpenHABItem { get } +} + +private func resolveSlot(entity: (any SwitchSlotResolvable)?) async -> SwitchEntry.Slot? { + guard let entity, let homeUUID = entity.homeId else { return nil } + await WidgetItemRegistry.shared.registerItem(name: entity.item.name, homeId: homeUUID) + let refreshed = await OpenHABItemCache.instance.getItemUncached(name: entity.item.name, home: homeUUID) + return SwitchEntry.Slot(item: refreshed ?? entity.item, homeUUID: homeUUID) +} + +private let sampleSlots: [SwitchEntry.Slot?] = { + let uuid = UUID() + return [ + SwitchEntry.Slot(item: sampleItem(name: "LivingRoomLight", label: "Living Room", state: "ON"), homeUUID: uuid), + SwitchEntry.Slot(item: sampleItem(name: "BedroomLight", label: "Bedroom", state: "OFF"), homeUUID: uuid), + SwitchEntry.Slot(item: sampleItem(name: "KitchenLight", label: "Kitchen", state: "ON"), homeUUID: uuid), + SwitchEntry.Slot(item: sampleItem(name: "HallwayLight", label: "Hallway", state: "OFF"), homeUUID: uuid) + ] +}() + +// MARK: - Small / accessory provider (1 slot) + +struct SwitchSmallProvider: AppIntentTimelineProvider { + typealias Intent = SwitchSmallConfigurationAppIntent + + func placeholder(in context: Context) -> SwitchEntry { + SwitchEntry(date: Date(), home: nil, slots: [sampleSlots[0]]) + } + + func snapshot(for configuration: Intent, in context: Context) async -> SwitchEntry { + context.isPreview ? placeholder(in: context) : await createEntry(for: configuration) + } + + func timeline(for configuration: Intent, in context: Context) async -> Timeline { + let entry = await createEntry(for: configuration) + let next = Calendar.current.date(byAdding: .minute, value: 5, to: Date())! + return Timeline(entries: [entry], policy: .after(next)) + } + + private func createEntry(for configuration: Intent) async -> SwitchEntry { + let slot = await resolveSlot(entity: configuration.item1 as (any SwitchSlotResolvable)?) + return SwitchEntry(date: Date(), home: configuration.home, slots: [slot]) + } +} + +// MARK: - Medium provider (2 slots) + +struct SwitchMediumProvider: AppIntentTimelineProvider { + typealias Intent = SwitchMediumConfigurationAppIntent + + func placeholder(in context: Context) -> SwitchEntry { + SwitchEntry(date: Date(), home: nil, slots: Array(sampleSlots.prefix(2))) + } + + func snapshot(for configuration: Intent, in context: Context) async -> SwitchEntry { + context.isPreview ? placeholder(in: context) : await createEntry(for: configuration) + } + + func timeline(for configuration: Intent, in context: Context) async -> Timeline { + let entry = await createEntry(for: configuration) + let next = Calendar.current.date(byAdding: .minute, value: 5, to: Date())! + return Timeline(entries: [entry], policy: .after(next)) + } + + private func createEntry(for configuration: Intent) async -> SwitchEntry { + async let s1 = resolveSlot(entity: configuration.item1 as (any SwitchSlotResolvable)?) + async let s2 = resolveSlot(entity: configuration.item2 as (any SwitchSlotResolvable)?) + return await SwitchEntry(date: Date(), home: configuration.home, slots: [s1, s2]) + } +} + +// MARK: - Large provider (4 slots) + +struct SwitchLargeProvider: AppIntentTimelineProvider { + typealias Intent = SwitchLargeConfigurationAppIntent + + func placeholder(in context: Context) -> SwitchEntry { + SwitchEntry(date: Date(), home: nil, slots: sampleSlots) + } + + func snapshot(for configuration: Intent, in context: Context) async -> SwitchEntry { + context.isPreview ? placeholder(in: context) : await createEntry(for: configuration) + } + + func timeline(for configuration: Intent, in context: Context) async -> Timeline { + let entry = await createEntry(for: configuration) + let next = Calendar.current.date(byAdding: .minute, value: 5, to: Date())! + return Timeline(entries: [entry], policy: .after(next)) + } + + private func createEntry(for configuration: Intent) async -> SwitchEntry { + async let s1 = resolveSlot(entity: configuration.item1 as (any SwitchSlotResolvable)?) + async let s2 = resolveSlot(entity: configuration.item2 as (any SwitchSlotResolvable)?) + async let s3 = resolveSlot(entity: configuration.item3 as (any SwitchSlotResolvable)?) + async let s4 = resolveSlot(entity: configuration.item4 as (any SwitchSlotResolvable)?) + return await SwitchEntry(date: Date(), home: configuration.home, slots: [s1, s2, s3, s4]) + } +} + +// MARK: - Toggle Style + +private struct PowerButtonToggleStyle: ToggleStyle { + let diameter: CGFloat + var showStateLabel = true + + func makeBody(configuration: Configuration) -> some View { + VStack(spacing: 4) { + Image(systemSymbol: .power) + .font(diameter <= 44 ? .title3 : .title2) + .foregroundStyle(configuration.isOn ? Color.white : Color.primary) + .frame(width: diameter, height: diameter) + .background(configuration.isOn ? Color.green : Color(uiColor: .systemFill)) + .clipShape(Circle()) + if showStateLabel { + Text(configuration.isOn ? "ON" : "OFF") + .font(.caption2) + .fontWeight(.semibold) + .foregroundStyle(configuration.isOn ? Color.green : Color.secondary) + } + } + } +} + +// MARK: - Item Row (used in multi-item layouts) + +private struct SwitchItemRow: View { + let slot: SwitchEntry.Slot + let home: Home? + + var body: some View { + let item = slot.item + let isOn = item.state == "ON" + let label = item.label.isEmpty ? item.name : item.label + HStack { + Text(label) + .font(.subheadline) + .fontWeight(.semibold) + .lineLimit(1) + .minimumScaleFactor(0.8) + .frame(maxWidth: .infinity, alignment: .leading) + if let home { + Toggle(isOn: isOn, intent: createToggleIntent(item: item, homeUUID: slot.homeUUID, home: home)) {} + .toggleStyle(PowerButtonToggleStyle(diameter: 36, showStateLabel: false)) + } else { + // Static visual used in previews / widget gallery (no home configured yet) + Image(systemSymbol: .power) + .font(.title3) + .foregroundStyle(isOn ? Color.white : Color.primary) + .frame(width: 36, height: 36) + .background(isOn ? Color.green : Color(uiColor: .systemFill)) + .clipShape(Circle()) + } + } + } +} + +// MARK: - Unconfigured placeholder + +private struct UnconfiguredPlaceholder: View { + var body: some View { + VStack { + Image(systemSymbol: .gear) + .font(.largeTitle) + .foregroundColor(.secondary) + Text("Configure Widget") + .font(.caption) + .foregroundColor(.secondary) + .multilineTextAlignment(.center) + } + } +} + +// MARK: - openHAB icon overlay + +private struct OpenHABIconOverlay: View { + let size: CGFloat + var leadingPadding: CGFloat = 14 + var topPadding: CGFloat = 14 + + var body: some View { + Image("openHABIcon") + .resizable() + .scaledToFit() + .frame(width: size, height: size) + .opacity(0.5) + .padding(.leading, leadingPadding) + .padding(.top, topPadding) + } +} + +// MARK: - Small Widget (1 item) + +struct SwitchSmallWidgetView: View { + let entry: SwitchEntry + + var body: some View { + ZStack(alignment: .topLeading) { + if let slot = entry.slots.compactMap(\.self).first { + let label = slot.item.label.isEmpty ? slot.item.name : slot.item.label + VStack(spacing: 8) { + Text(label) + .font(.headline) + .lineLimit(1) + .minimumScaleFactor(0.7) + .padding(.top, 20) + Spacer() + if let home = entry.home { + Toggle( + isOn: slot.item.state == "ON", + intent: createToggleIntent(item: slot.item, homeUUID: slot.homeUUID, home: home) + ) {} + .toggleStyle(PowerButtonToggleStyle(diameter: 44)) + } else { + let isOn = slot.item.state == "ON" + Image(systemSymbol: .power) + .font(.title2) + .foregroundStyle(isOn ? Color.white : Color.primary) + .frame(width: 44, height: 44) + .background(isOn ? Color.green : Color(uiColor: .systemFill)) + .clipShape(Circle()) + } + } + .frame(maxHeight: .infinity) + .padding() + } else { + UnconfiguredPlaceholder() + .frame(maxWidth: .infinity, maxHeight: .infinity) + .padding() + } + + OpenHABIconOverlay(size: 16) + } + } +} + +// MARK: - Medium Widget (2 items) + +struct SwitchMediumWidgetView: View { + let entry: SwitchEntry + + var body: some View { + ZStack(alignment: .topLeading) { + let filledSlots = Array(entry.slots.compactMap(\.self)) + if filledSlots.isEmpty { + UnconfiguredPlaceholder() + .frame(maxWidth: .infinity, maxHeight: .infinity) + .padding() + } else { + VStack(alignment: .leading, spacing: 0) { + ForEach(filledSlots.indices, id: \.self) { index in + SwitchItemRow(slot: filledSlots[index], home: entry.home) + .padding(.horizontal) + .padding(.vertical, 10) + if index < filledSlots.count - 1 { + Divider() + .padding(.leading) + } + } + } + .frame(maxHeight: .infinity) + .padding(.top, 22) + } + + OpenHABIconOverlay(size: 18) + } + } +} + +// MARK: - Large Widget (4 items) + +struct SwitchLargeWidgetView: View { + let entry: SwitchEntry + + var body: some View { + ZStack(alignment: .topLeading) { + let filledSlots = Array(entry.slots.compactMap(\.self)) + if filledSlots.isEmpty { + VStack(alignment: .center, spacing: 16) { + Image(systemSymbol: .gear) + .font(.system(size: 60)) + .foregroundColor(.secondary) + Text("Configure Widget") + .font(.title2) + .foregroundColor(.secondary) + Text("Long press the widget to configure which switches to control") + .font(.caption) + .foregroundColor(.secondary) + .multilineTextAlignment(.center) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + .padding() + } else { + VStack(alignment: .leading, spacing: 0) { + ForEach(filledSlots.indices, id: \.self) { index in + SwitchItemRow(slot: filledSlots[index], home: entry.home) + .padding(.horizontal) + .padding(.vertical, 12) + if index < filledSlots.count - 1 { + Divider() + .padding(.leading) + } + } + } + .frame(maxHeight: .infinity) + .padding(.top, 22) + } + + OpenHABIconOverlay(size: 20) + } + } +} + +// MARK: - Accessory Views (share small provider / entry) + +struct SwitchAccessoryCircularView: View { + let entry: SwitchEntry + + var body: some View { + ZStack { + AccessoryWidgetBackground() + if let slot = entry.slots.compactMap(\.self).first { + VStack(spacing: 2) { + Image(systemSymbol: .switch2) + .font(.caption) + Text(slot.item.state ?? "?") + .font(.caption2) + .fontWeight(.bold) + .lineLimit(1) + .minimumScaleFactor(0.5) + } + } else { + Image(systemSymbol: .gear) + .font(.title3) + } + } + } +} + +struct SwitchAccessoryRectangularView: View { + let entry: SwitchEntry + + var body: some View { + if let slot = entry.slots.compactMap(\.self).first { + let label = slot.item.label.isEmpty ? slot.item.name : slot.item.label + VStack(alignment: .leading, spacing: 2) { + Text(label) + .font(.headline) + .lineLimit(1) + .minimumScaleFactor(0.7) + if let stateText = slot.item.state { + Text(stateText) + .font(.body) + .fontWeight(.semibold) + .lineLimit(1) + } else { + Text("No State") + .font(.caption) + .foregroundColor(.secondary) + } + } + } else { + Text("Configure") + .font(.caption) + } + } +} + +struct SwitchAccessoryInlineView: View { + let entry: SwitchEntry + + var body: some View { + if let slot = entry.slots.compactMap(\.self).first { + let label = slot.item.label.isEmpty ? slot.item.name : slot.item.label + if let stateText = slot.item.state { + Text("\(label): \(stateText)") + } else { + Text(label) + } + } else { + Text("Configure Widget") + } + } +} + +// MARK: - Unified Entry View (dispatches by family) + +struct SwitchWidgetEntryView: View { + var entry: SwitchEntry + @Environment(\.widgetFamily) var family + + var body: some View { + switch family { + case .systemSmall: + SwitchSmallWidgetView(entry: entry) + case .systemMedium: + SwitchMediumWidgetView(entry: entry) + case .systemLarge: + SwitchLargeWidgetView(entry: entry) + case .accessoryCircular: + SwitchAccessoryCircularView(entry: entry) + case .accessoryRectangular: + SwitchAccessoryRectangularView(entry: entry) + case .accessoryInline: + SwitchAccessoryInlineView(entry: entry) + default: + SwitchSmallWidgetView(entry: entry) + } + } +} + +extension SwitchWidgetItemEntity: SwitchSlotResolvable {} +extension SwitchMediumWidgetItemEntity: SwitchSlotResolvable {} +extension SwitchLargeWidgetItemEntity: SwitchSlotResolvable {} + +// MARK: - Previews + +#Preview("Small", as: .systemSmall) { + SwitchSmallWidget() +} timeline: { + SwitchEntry(date: .now, home: nil, slots: [sampleSlots[0]]) +} + +#Preview("Medium", as: .systemMedium) { + SwitchMediumWidget() +} timeline: { + SwitchEntry(date: .now, home: nil, slots: Array(sampleSlots.prefix(2))) +} + +#Preview("Large", as: .systemLarge) { + SwitchLargeWidget() +} timeline: { + SwitchEntry(date: .now, home: nil, slots: sampleSlots) +} diff --git a/openHABWidget/SwitchWidgetItemEntity.swift b/openHABWidget/SwitchWidgetItemEntity.swift new file mode 100644 index 000000000..33cf79338 --- /dev/null +++ b/openHABWidget/SwitchWidgetItemEntity.swift @@ -0,0 +1,38 @@ +// Copyright (c) 2010-2026 Contributors to the openHAB project +// +// See the NOTICE file(s) distributed with this work for additional +// information. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0 +// +// SPDX-License-Identifier: EPL-2.0 + +import AppIntents +import OpenHABCore + +struct SwitchWidgetItemEntity: ItemEntity { + struct SwitchWidgetItemQuery: ItemEntityQuery { + typealias EntityType = SwitchWidgetItemEntity + + @IntentParameterDependency(\.$home) + var intent + + var allowedTypes: [OpenHABItem.ItemType] = [.switchItem] + var selectedHome: Home? { intent?.home } + } + + static let typeDisplayRepresentation = TypeDisplayRepresentation(name: "Switch Item") + static let defaultQuery = SwitchWidgetItemQuery() + + var id: ItemIdentifier + var item: OpenHABItem + var homeName: String? + + init(id: ItemIdentifier, item: OpenHABItem, homeName: String? = nil) { + self.id = id + self.item = item + self.homeName = homeName + } +} diff --git a/openHABWidget/WidgetConfigurationExtension.swift b/openHABWidget/WidgetConfigurationExtension.swift new file mode 100644 index 000000000..ab5d2b60a --- /dev/null +++ b/openHABWidget/WidgetConfigurationExtension.swift @@ -0,0 +1,20 @@ +// Copyright (c) 2010-2026 Contributors to the openHAB project +// +// See the NOTICE file(s) distributed with this work for additional +// information. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0 +// +// SPDX-License-Identifier: EPL-2.0 + +import Foundation +import SwiftUI +import WidgetKit + +extension WidgetConfiguration { + func widgetContentMarginsDisabled() -> some WidgetConfiguration { + contentMarginsDisabled() + } +} From 687d341bb0e5aecc0c4f7e0ad86568fb3b376d16 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Thu, 28 May 2026 13:46:07 +0200 Subject: [PATCH 02/91] ItemEventStream: backport SSE watchdog and self-contained network monitor Port the two ItemEventStream improvements from feature/migration2SSE-minimal without pulling in the unrelated sitemap-polling changes: - Add networkMonitoringTask field: the network monitor now lives entirely inside the actor (started once via startMonitoringNetworkIfNeeded) instead of running an infinite for-await loop in the caller's task. startMonitoringNetwork() returns immediately after the first call. - Add 30-second alive watchdog: a background Task inside listen(using:) checks lastEventTime every 10 s and reconnects if no event (including ALIVE heartbeats) has been received for 30 s. This recovers silently dead connections that occur after a server restart without a TCP RST. Also fix WidgetItemMonitor.reloadWidgets(for:) which was still reloading the deleted 'OpenHABSwitchWidget' kind; update to the three separate kinds introduced when small/medium/large were split into separate widget structs. Signed-off-by: Tim Mueller-Seydlitz --- .../OpenHABCore/Util/ItemEventStream.swift | 53 +++++++++++++++++-- openHAB/WidgetItemMonitor.swift | 12 +++-- 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/OpenHABCore/Sources/OpenHABCore/Util/ItemEventStream.swift b/OpenHABCore/Sources/OpenHABCore/Util/ItemEventStream.swift index 8c777e02e..a7cf15b8f 100644 --- a/OpenHABCore/Sources/OpenHABCore/Util/ItemEventStream.swift +++ b/OpenHABCore/Sources/OpenHABCore/Util/ItemEventStream.swift @@ -65,9 +65,11 @@ public actor EventStream { private var trackedItems: Set = [] private var continuations = [UUID: AsyncStream>.Continuation]() private var listenTask: Task? + private var networkMonitoringTask: Task? private var currentConfig: ConnectionConfiguration? private var sessionUUID: String? private var service: OpenAPIService? + private var lastEventTime = Date.now private let jsonDecoder = JSONDecoder() public func stream() -> AsyncStream> { @@ -88,6 +90,21 @@ public actor EventStream { await sendTrackedItemsIfPossible() } + public func startMonitoringNetworkIfNeeded(initialConnection: ConnectionInfo?) { + updateConnection(initialConnection) + + // Keep network monitoring alive for the actor lifetime. Stop/restart + // behaviour is handled by updateConnection cancelling listenTask. + guard networkMonitoringTask == nil else { return } + + networkMonitoringTask = Task { [weak self] in + guard let self else { return } + for await conn in await NetworkTracker.shared.activeConnectionStream() { + await updateConnection(conn) + } + } + } + private func cleanupContinuation(_ id: UUID) { continuations[id]?.finish() continuations.removeValue(forKey: id) @@ -141,8 +158,20 @@ public actor EventStream { let eventStream = try response.ok.body.text_event_hyphen_stream.asDecodedServerSentEvents() self.service = service broadcast(.connected) + lastEventTime = .now + + let watchdog = Task { [weak self] in + while !Task.isCancelled { + try? await Task.sleep(for: .seconds(10)) + guard !Task.isCancelled else { return } + guard let self else { return } + await checkAliveWatchdog() + } + } + defer { watchdog.cancel() } for try await sse in eventStream { + lastEventTime = .now for rawMessage in parse(sse) { if let message = rawMessage as? Event { broadcast(.event(message)) @@ -164,6 +193,24 @@ public actor EventStream { } } + /// Reconnects if no SSE events have been received for 30 seconds. + /// The server sends ALIVE heartbeats every ~10 s, so 30 s of silence + /// indicates a silently dead connection (e.g. after a server restart). + private func checkAliveWatchdog() { + guard Date.now.timeIntervalSince(lastEventTime) > 30 else { return } + Logger.restAPI.warning("Item SSE watchdog: no events for 30 s, reconnecting") + restartListening() + } + + private func restartListening() { + guard let cfg = currentConfig else { + broadcast(.disconnected(nil)) + return + } + listenTask?.cancel() + listenTask = Task { await listen(using: cfg) } + } + private func broadcast(_ msg: StreamOutput) { // the "ready" message carries the UUID string we need to store if case let .event(raw as StateStreamMessage) = msg, @@ -212,9 +259,7 @@ public extension ItemEventStream { await shared.trackItems(items) } - static func startMonitoringNetwork() async { - for await conn in await NetworkTracker.shared.activeConnectionStream() { - await shared.updateConnection(conn) - } + static func startMonitoringNetwork(initialConnection: ConnectionInfo? = nil) async { + await shared.startMonitoringNetworkIfNeeded(initialConnection: initialConnection) } } diff --git a/openHAB/WidgetItemMonitor.swift b/openHAB/WidgetItemMonitor.swift index 39bec97fd..4e945784b 100644 --- a/openHAB/WidgetItemMonitor.swift +++ b/openHAB/WidgetItemMonitor.swift @@ -34,7 +34,9 @@ class WidgetItemMonitor { isMonitoring = true Logger.widgets.info("Starting widget item monitoring") - // Start network monitoring for SSE connection + // Start network monitoring for SSE connection. + // startMonitoringNetwork returns immediately; the monitoring task + // lives inside the ItemEventStream actor for its lifetime. monitoringTask = Task { await ItemEventStream.startMonitoringNetwork() } @@ -124,9 +126,11 @@ class WidgetItemMonitor { } private func reloadWidgets(for itemName: String) { - // Reload all switch and sensor widgets - // Note: This reloads all widgets of these types, not just the specific item - WidgetCenter.shared.reloadTimelines(ofKind: "OpenHABSwitchWidget") + // Reload all switch and sensor widgets that may display this item. + // Each size is a separate widget kind since iOS 17+. + WidgetCenter.shared.reloadTimelines(ofKind: "OpenHABSwitchWidgetSmall") + WidgetCenter.shared.reloadTimelines(ofKind: "OpenHABSwitchWidgetMedium") + WidgetCenter.shared.reloadTimelines(ofKind: "OpenHABSwitchWidgetLarge") WidgetCenter.shared.reloadTimelines(ofKind: "OpenHABSensorWidget") } } From 365ddb0c2adf83f0462eeeb56129d7e2692fc250 Mon Sep 17 00:00:00 2001 From: Tim Bert <5411131+timbms@users.noreply.github.com> Date: Thu, 28 May 2026 16:46:33 +0200 Subject: [PATCH 03/91] =?UTF-8?q?Upgrade=20SwiftFormatPlugin=200.58.7?= =?UTF-8?q?=E2=86=920.61.1,=20SwiftLintPlugin=200.62.2=E2=86=920.63.3=20(#?= =?UTF-8?q?1225)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Bump exact versions in BuildTools/Package.swift and update Package.resolved - Fix line_length violations newly flagged by SwiftLint 0.63.3: add disable comments in OpenHABSitemapWidgetEvent, OpenHABWidget, OpenHABImageProcessorTests, SitemapDiagnostics, and JSONParserTests - Remove now-superfluous closure_end_indentation disables in TextInputRowView and DatePickerInputRowView - SnapshotRowInputBuildResult: add explicit Sendable conformance (required by Task in Swift 6) and guard it with // swiftformat:disable:next redundantSendable so the formatter leaves it intact Signed-off-by: Tim Mueller-Seydlitz --- AppIntents/Intents/ContactStateIntent.swift | 8 +- AppIntents/Intents/GetItemStateIntent.swift | 4 +- AppIntents/Intents/SetActiveHomeIntent.swift | 4 +- AppIntents/Intents/SetColorValueIntent.swift | 9 +- .../Intents/SetDateTimeValueIntent.swift | 9 +- .../Intents/SetDimmerRollerValueIntent.swift | 9 +- .../Intents/SetLocationValueIntent.swift | 9 +- AppIntents/Intents/SetNumberValueIntent.swift | 9 +- AppIntents/Intents/SetPlayerValueIntent.swift | 8 +- AppIntents/Intents/SetStringValueIntent.swift | 9 +- AppIntents/Intents/SetSwitchItemIntent.swift | 8 +- AppIntents/ItemEntity.swift | 35 ++++++-- AppIntents/ItemIdentifier.swift | 10 +-- BuildTools/Package.resolved | 10 +-- BuildTools/Package.swift | 4 +- .../Sources/CommonUI/ColorExtension.swift | 3 +- CommonUI/Sources/CommonUI/LogsViewer.swift | 9 +- .../Sources/CommonUI/PreviewConstants.swift | 3 +- NotificationService/NotificationService.swift | 2 +- .../OpenHABCore/Model/NumberState.swift | 2 +- .../OpenHABCore/Model/OpenHABEvent.swift | 2 +- .../OpenHABCore/Model/OpenHABLink.swift | 2 +- .../Model/OpenHABNotification.swift | 8 +- .../OpenHABCore/Model/OpenHABPage.swift | 3 +- .../Model/OpenHABServerProperties.swift | 5 -- .../OpenHABCore/Model/OpenHABSitemap.swift | 4 +- .../Model/OpenHABSitemapWidgetEvent.swift | 1 + .../OpenHABCore/Model/OpenHABWidget.swift | 14 ++- .../Model/SetpointDisplayFormatter.swift | 4 +- .../OpenHABCore/Util/BonjourService.swift | 10 ++- .../Util/ClientCertificateManager.swift | 9 +- .../Util/Collection+SafeAccess.swift | 2 +- .../Sources/OpenHABCore/Util/Comparable.swift | 2 +- .../Util/ConnectionConfiguration.swift | 16 ++-- .../Util/DateFormatterExtension.swift | 4 +- .../OpenHABCore/Util/ETagChecker.swift | 2 +- .../Sources/OpenHABCore/Util/Endpoint.swift | 7 +- .../Sources/OpenHABCore/Util/HTTPClient.swift | 4 +- .../OpenHABCore/Util/HTTPClientDelegate.swift | 9 +- .../OpenHABCore/Util/ItemEventStream.swift | 12 +-- .../OpenHABCore/Util/LoggerExtension.swift | 1 - .../OpenHABCore/Util/NWPathMonitoring.swift | 3 +- .../OpenHABCore/Util/NetworkTracker.swift | 24 ++--- .../OpenHABCore/Util/OpenAPIService.swift | 42 ++++----- .../Util/OpenHABImageProcessor.swift | 2 +- .../OpenHABCore/Util/OpenHABItemCache.swift | 6 +- .../OpenHABCore/Util/Preferences.swift | 15 ++-- .../Util/ServerCertificateManager.swift | 16 ++-- .../OpenHABCore/Util/StringExtension.swift | 4 +- .../OpenHABCore/Util/UIColorExtension.swift | 4 +- .../CertificateStoreTests.swift | 22 ++--- .../ClientCertificateManagerTests.swift | 2 +- .../CredentialsStoreTests.swift | 6 +- .../DateFormattingTests.swift | 46 +++++----- .../DoubleExtensionTests.swift | 24 ++--- .../OpenHABCoreTests/ETagCheckerTests.swift | 16 ++-- .../OpenHABCoreTests/EndpointTests.swift | 8 +- .../HTTPResponseExtension.swift | 44 ++++++--- .../OpenHABCoreTests/JSONParserTests.swift | 7 +- .../NetworkTrackerTests.swift | 3 +- .../OpenHABCoreTests/NumberStateTests.swift | 5 +- .../OpenAPIServiceSendItemCommandTests.swift | 1 - .../OpenHABCoreGeneralTests.swift | 2 - .../OpenHABImageProcessorTests.swift | 54 +++++------ .../OpenHABJSONParserTests.swift | 2 + .../OpenHABWidgetIconStateTests.swift | 1 - .../ServerCertificateManagerTests.swift | 4 +- .../SessionTaskChallengeTests.swift | 3 +- .../StringExtensionTests.swift | 90 +++++++++---------- .../TestClientTransport.swift | 8 +- .../UIColorExtensionTests.swift | 34 +++---- .../Tests/OpenHABCoreTests/UIColorTests.swift | 1 - .../OpenHABCoreTests/UserDefaultsTests.swift | 24 ++--- .../WidgetCommandDispatcherTests.swift | 1 - .../WidgetDisplayStateTests.swift | 4 +- .../WidgetMediaImageDescriptorTests.swift | 1 - .../WidgetRenderingKindTests.swift | 1 - .../OsLogRewriterLib/OsLogRewriter.swift | 16 ++-- .../xcshareddata/swiftpm/Package.resolved | 10 +-- openHAB/AppDelegate.swift | 6 +- .../SitemapPageViewModel+SupportTypes.swift | 53 ++--------- openHAB/Models/SitemapPageViewModel.swift | 8 +- openHAB/Models/WidgetMappingSnapshot.swift | 9 +- openHAB/NotificationCenterDelegateImpl.swift | 14 +-- openHAB/UI/DrawerView.swift | 6 +- openHAB/UI/HostingSitemapViewController.swift | 4 +- openHAB/UI/NotificationsView.swift | 2 +- openHAB/UI/OpenHABNavigationController.swift | 8 +- openHAB/UI/OpenHABRootViewController.swift | 8 +- openHAB/UI/OpenHABViewController.swift | 8 +- openHAB/UI/OpenHABWebViewController.swift | 7 +- .../UI/ScreenSaver/ScreenSaverManager.swift | 9 +- .../UI/SettingsView/ItemSelectionView.swift | 1 - .../ScreenSaverSettingsView.swift | 7 -- .../UI/SettingsView/SitemapSettingsView.swift | 6 -- .../UI/SwiftUI/EmbeddingRowInputView.swift | 6 +- openHAB/UI/SwiftUI/ImageView.swift | 1 - .../Rows/ColorTemperaturePickerRowView.swift | 2 +- .../SwiftUI/Rows/DatePickerInputRowView.swift | 10 +-- .../UI/SwiftUI/Rows/SegmentedRowView.swift | 2 - .../UI/SwiftUI/Rows/SelectionRowView.swift | 1 - openHAB/UI/SwiftUI/Rows/SliderRowView.swift | 2 +- .../UI/SwiftUI/Rows/TextInputRowView.swift | 38 ++++---- openHAB/UI/SwiftUI/SitemapDiagnostics.swift | 14 +-- openHAB/UI/SwiftUI/SitemapRowInput.swift | 4 +- openHAB/UI/SwiftUI/WidgetRowInputs.swift | 6 +- openHAB/UI/Watch/WatchMessageService.swift | 8 +- .../ColorTemperatureRowMathTests.swift | 2 - .../IconReloadCoordinatorTests.swift | 1 - .../InputCommandFormatterTests.swift | 4 - openHABTestsSwift/LocalizationTests.swift | 3 +- openHABTestsSwift/OpenHABEndPoint.swift | 4 +- openHABTestsSwift/OpenHABSVGTests.swift | 7 +- openHABTestsSwift/RowLayoutPolicyTests.swift | 1 - .../ScreenSaverLayoutCalculatorTests.swift | 1 - .../SitemapPageViewModelForegroundTests.swift | 15 ++-- .../SitemapRowInputBuilderTests.swift | 2 - .../SitemapRowInputMapperTests.swift | 1 - .../SliderOverrideSyncTests.swift | 1 - openHABTestsSwift/URLWebViewPathTests.swift | 40 ++++----- .../WebRowViewConfigurationTests.swift | 1 - openHABWatch/Domain/UserData.swift | 6 +- .../Extension/OpenHABWatchAppDelegate.swift | 1 - openHABWatch/External/AppMessageService.swift | 8 +- openHABWatch/Model/LazyView.swift | 2 +- .../Model/OpenHABWidgetExtension.swift | 2 - openHABWatch/Model/UserDefaultsBacked.swift | 4 +- openHABWatch/Views/Rows/ImageRow.swift | 4 +- openHABWatch/Views/Rows/SegmentRow.swift | 5 -- .../Views/Rows/SegmentSelectionView.swift | 3 - openHABWatch/Views/SitemapPageView.swift | 1 - openHABWatch/Views/Utils/WatchIconView.swift | 6 +- .../OpenHABWatchLaunchTests.swift | 2 +- .../OpenHABWatchUITests.swift | 4 +- 134 files changed, 620 insertions(+), 602 deletions(-) diff --git a/AppIntents/Intents/ContactStateIntent.swift b/AppIntents/Intents/ContactStateIntent.swift index 3bd60e599..3d4c85f01 100644 --- a/AppIntents/Intents/ContactStateIntent.swift +++ b/AppIntents/Intents/ContactStateIntent.swift @@ -28,9 +28,13 @@ enum ContactStateError: Error, CustomLocalizedStringResourceConvertible { @available(iOS 17.0, macOS 14.0, *) struct ContactStateIntent: AppIntent { - static var openAppWhenRun: Bool { false } + static var openAppWhenRun: Bool { + false + } - static var allowedItemTypes: [OpenHABItem.ItemType] { [.contact] } + static var allowedItemTypes: [OpenHABItem.ItemType] { + [.contact] + } static var parameterSummary: some ParameterSummary { Summary("Set the state of \(\.$itemEntity) to \(\.$state)") { diff --git a/AppIntents/Intents/GetItemStateIntent.swift b/AppIntents/Intents/GetItemStateIntent.swift index a52b9e697..1cb729cb9 100644 --- a/AppIntents/Intents/GetItemStateIntent.swift +++ b/AppIntents/Intents/GetItemStateIntent.swift @@ -25,7 +25,9 @@ enum ItemStateError: Error, CustomLocalizedStringResourceConvertible { @available(iOS 17.0, macOS 14.0, *) struct GetItemStateIntent: AppIntent { - static var openAppWhenRun: Bool { false } + static var openAppWhenRun: Bool { + false + } static var parameterSummary: some ParameterSummary { Summary("Get \(\.$itemEntity) State") { diff --git a/AppIntents/Intents/SetActiveHomeIntent.swift b/AppIntents/Intents/SetActiveHomeIntent.swift index a0c5a56cd..c7f3fcbf6 100644 --- a/AppIntents/Intents/SetActiveHomeIntent.swift +++ b/AppIntents/Intents/SetActiveHomeIntent.swift @@ -14,7 +14,9 @@ import OpenHABCore @available(iOS 17.0, macOS 14.0, watchOS 10.0, *) struct SetActiveHomeIntent: AppIntent { - static var openAppWhenRun: Bool { false } + static var openAppWhenRun: Bool { + false + } static let title: LocalizedStringResource = "Set Active Home" static let description = IntentDescription("Switch the active home in the openHAB app") diff --git a/AppIntents/Intents/SetColorValueIntent.swift b/AppIntents/Intents/SetColorValueIntent.swift index 493ffad01..5fd0d394b 100644 --- a/AppIntents/Intents/SetColorValueIntent.swift +++ b/AppIntents/Intents/SetColorValueIntent.swift @@ -31,9 +31,14 @@ enum ColorValueError: Error, CustomLocalizedStringResourceConvertible { @available(iOS 17.0, macOS 14.0, *) struct SetColorValueIntent: AppIntent { - static var openAppWhenRun: Bool { false } + static var openAppWhenRun: Bool { + false + } + + static var allowedItemTypes: [OpenHABItem.ItemType] { + [.color] + } - static var allowedItemTypes: [OpenHABItem.ItemType] { [.color] } static var parameterSummary: some ParameterSummary { Summary("Set \(\.$itemEntity) to \(\.$value) (HSB)") { \.$home diff --git a/AppIntents/Intents/SetDateTimeValueIntent.swift b/AppIntents/Intents/SetDateTimeValueIntent.swift index e8273be7c..ef1260a41 100644 --- a/AppIntents/Intents/SetDateTimeValueIntent.swift +++ b/AppIntents/Intents/SetDateTimeValueIntent.swift @@ -28,9 +28,14 @@ enum DateTimeValueError: Error, CustomLocalizedStringResourceConvertible { @available(iOS 17.0, macOS 14.0, *) struct SetDateTimeValueIntent: AppIntent { - static var openAppWhenRun: Bool { false } + static var openAppWhenRun: Bool { + false + } + + static var allowedItemTypes: [OpenHABItem.ItemType] { + [.dateTime] + } - static var allowedItemTypes: [OpenHABItem.ItemType] { [.dateTime] } static var parameterSummary: some ParameterSummary { Summary("Set \(\.$itemEntity) to \(\.$value)") { \.$home diff --git a/AppIntents/Intents/SetDimmerRollerValueIntent.swift b/AppIntents/Intents/SetDimmerRollerValueIntent.swift index 203224408..6b1db8b02 100644 --- a/AppIntents/Intents/SetDimmerRollerValueIntent.swift +++ b/AppIntents/Intents/SetDimmerRollerValueIntent.swift @@ -31,9 +31,14 @@ enum DimmerRollerValueError: Error, CustomLocalizedStringResourceConvertible { @available(iOS 17.0, macOS 14.0, *) struct SetDimmerRollerValueIntent: AppIntent { - static var openAppWhenRun: Bool { false } + static var openAppWhenRun: Bool { + false + } + + static var allowedItemTypes: [OpenHABItem.ItemType] { + [.dimmer, .rollershutter] + } - static var allowedItemTypes: [OpenHABItem.ItemType] { [.dimmer, .rollershutter] } static var parameterSummary: some ParameterSummary { Summary("Set \(\.$itemEntity) to \(\.$value)") { \.$home diff --git a/AppIntents/Intents/SetLocationValueIntent.swift b/AppIntents/Intents/SetLocationValueIntent.swift index 846f42121..a172f1ac8 100644 --- a/AppIntents/Intents/SetLocationValueIntent.swift +++ b/AppIntents/Intents/SetLocationValueIntent.swift @@ -34,9 +34,14 @@ enum LocationValueError: Error, CustomLocalizedStringResourceConvertible { @available(iOS 17.0, macOS 14.0, *) struct SetLocationValueIntent: AppIntent { - static var openAppWhenRun: Bool { false } + static var openAppWhenRun: Bool { + false + } + + static var allowedItemTypes: [OpenHABItem.ItemType] { + [.location] + } - static var allowedItemTypes: [OpenHABItem.ItemType] { [.location] } static var parameterSummary: some ParameterSummary { Summary("Set \(\.$itemEntity) to \(\.$latitude), \(\.$longitude)") { \.$home diff --git a/AppIntents/Intents/SetNumberValueIntent.swift b/AppIntents/Intents/SetNumberValueIntent.swift index 8b2e0cffb..07c1a61e1 100644 --- a/AppIntents/Intents/SetNumberValueIntent.swift +++ b/AppIntents/Intents/SetNumberValueIntent.swift @@ -28,9 +28,14 @@ enum NumberValueError: Error, CustomLocalizedStringResourceConvertible { @available(iOS 17.0, macOS 14.0, *) struct SetNumberValueIntent: AppIntent { - static var openAppWhenRun: Bool { false } + static var openAppWhenRun: Bool { + false + } + + static var allowedItemTypes: [OpenHABItem.ItemType] { + [.number, .numberWithDimension] + } - static var allowedItemTypes: [OpenHABItem.ItemType] { [.number, .numberWithDimension] } static var parameterSummary: some ParameterSummary { Summary("Set \(\.$itemEntity) to \(\.$value)") { \.$home diff --git a/AppIntents/Intents/SetPlayerValueIntent.swift b/AppIntents/Intents/SetPlayerValueIntent.swift index 3307c9552..69552ac4b 100644 --- a/AppIntents/Intents/SetPlayerValueIntent.swift +++ b/AppIntents/Intents/SetPlayerValueIntent.swift @@ -28,9 +28,13 @@ enum PlayerValueError: Error, CustomLocalizedStringResourceConvertible { @available(iOS 17.0, macOS 14.0, *) struct SetPlayerValueIntent: AppIntent { - static var openAppWhenRun: Bool { false } + static var openAppWhenRun: Bool { + false + } - static var allowedItemTypes: [OpenHABItem.ItemType] { [.player] } + static var allowedItemTypes: [OpenHABItem.ItemType] { + [.player] + } static var parameterSummary: some ParameterSummary { Summary("Send \(\.$action) to \(\.$itemEntity)") { diff --git a/AppIntents/Intents/SetStringValueIntent.swift b/AppIntents/Intents/SetStringValueIntent.swift index a0d235f8d..591310f4a 100644 --- a/AppIntents/Intents/SetStringValueIntent.swift +++ b/AppIntents/Intents/SetStringValueIntent.swift @@ -28,9 +28,14 @@ enum StringValueError: Error, CustomLocalizedStringResourceConvertible { @available(iOS 17.0, macOS 14.0, *) struct SetStringValueIntent: AppIntent { - static var openAppWhenRun: Bool { false } + static var openAppWhenRun: Bool { + false + } + + static var allowedItemTypes: [OpenHABItem.ItemType] { + [.stringItem] + } - static var allowedItemTypes: [OpenHABItem.ItemType] { [.stringItem] } static var parameterSummary: some ParameterSummary { Summary("Set \(\.$itemEntity) to \(\.$value)") { \.$home diff --git a/AppIntents/Intents/SetSwitchItemIntent.swift b/AppIntents/Intents/SetSwitchItemIntent.swift index 44f7e0a0b..0f9d88a81 100644 --- a/AppIntents/Intents/SetSwitchItemIntent.swift +++ b/AppIntents/Intents/SetSwitchItemIntent.swift @@ -28,9 +28,13 @@ enum ControlItemError: Error, CustomLocalizedStringResourceConvertible { @available(iOS 17.0, macOS 14.0, *) struct SetSwitchItemIntent: AppIntent { - static var openAppWhenRun: Bool { false } + static var openAppWhenRun: Bool { + false + } - static var allowedItemTypes: [OpenHABItem.ItemType] { [.switchItem] } + static var allowedItemTypes: [OpenHABItem.ItemType] { + [.switchItem] + } static var parameterSummary: some ParameterSummary { Summary("Send \(\.$action) to \(\.$itemEntity)") { diff --git a/AppIntents/ItemEntity.swift b/AppIntents/ItemEntity.swift index 8d1371202..127c0f2cc 100644 --- a/AppIntents/ItemEntity.swift +++ b/AppIntents/ItemEntity.swift @@ -26,15 +26,34 @@ protocol ItemEntity: AppEntity where ID == ItemIdentifier { @available(iOS 17.0, macOS 14.0, *) extension ItemEntity { - var homeId: UUID? { id.homeId } - var itemName: String { id.itemName } + var homeId: UUID? { + id.homeId + } + + var itemName: String { + id.itemName + } + + /// Convenient access to common item properties + var label: String { + item.label + } + + var category: String { + item.category + } + + var type: OpenHABItem.ItemType? { + item.type + } - // Convenient access to common item properties - var label: String { item.label } - var category: String { item.category } - var type: OpenHABItem.ItemType? { item.type } - var state: String? { item.state } - var link: String { item.link } + var state: String? { + item.state + } + + var link: String { + item.link + } var displayRepresentation: DisplayRepresentation { if let homeName { diff --git a/AppIntents/ItemIdentifier.swift b/AppIntents/ItemIdentifier.swift index da7b186b8..2378a6854 100644 --- a/AppIntents/ItemIdentifier.swift +++ b/AppIntents/ItemIdentifier.swift @@ -33,11 +33,11 @@ struct ItemIdentifier: Codable { } } -// homeId is a device-local UUID used only for cache lookups and network-tracker resolution. -// For v2 identifiers (homeIdentifier != nil), entity identity is homeIdentifier + itemName so the -// App Intents framework can match shortcuts across devices even when the embedded UUID differs. -// For legacy identifiers (homeIdentifier == nil), homeId is the only home discriminator available, -// so it is included in equality — without it, same-named items from different homes would collide. +/// homeId is a device-local UUID used only for cache lookups and network-tracker resolution. +/// For v2 identifiers (homeIdentifier != nil), entity identity is homeIdentifier + itemName so the +/// App Intents framework can match shortcuts across devices even when the embedded UUID differs. +/// For legacy identifiers (homeIdentifier == nil), homeId is the only home discriminator available, +/// so it is included in equality — without it, same-named items from different homes would collide. extension ItemIdentifier: Equatable { static func == (lhs: ItemIdentifier, rhs: ItemIdentifier) -> Bool { guard lhs.homeIdentifier == nil, rhs.homeIdentifier == nil else { diff --git a/BuildTools/Package.resolved b/BuildTools/Package.resolved index 8d2e03f72..230387d30 100644 --- a/BuildTools/Package.resolved +++ b/BuildTools/Package.resolved @@ -1,13 +1,13 @@ { - "originHash" : "981c20eb876b3478e70e69ad39fa28a4a7b3c0300977d08d767317cc43c4c93a", + "originHash" : "2195e162e93a04d869e4fd38210b4e523359da94e9b77c369bfa9c1dfa3e06b4", "pins" : [ { "identity" : "swiftformatplugin", "kind" : "remoteSourceControl", "location" : "https://github.com/weakfl/SwiftFormatPlugin", "state" : { - "revision" : "c41997c642ffc937c6e83b23dadbd7e626485cfa", - "version" : "0.58.7" + "revision" : "c7c81920d715e051306d2172083c14aaec2c1b9d", + "version" : "0.61.1" } }, { @@ -15,8 +15,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/weakfl/SwiftLintPlugin.git", "state" : { - "revision" : "47d1da3a8e30e0ada5543899e8c47f54664073ac", - "version" : "0.62.2" + "revision" : "3741567a0252090897336f1f39b8490107b49ead", + "version" : "0.63.3" } } ], diff --git a/BuildTools/Package.swift b/BuildTools/Package.swift index 32f3894f7..a3a30bcc8 100644 --- a/BuildTools/Package.swift +++ b/BuildTools/Package.swift @@ -5,8 +5,8 @@ let package = Package( name: "BuildTools", platforms: [.macOS(.v10_13)], dependencies: [ - .package(url: "https://github.com/weakfl/SwiftFormatPlugin", exact: "0.58.7"), - .package(url: "https://github.com/weakfl/SwiftLintPlugin.git", exact: "0.62.2") + .package(url: "https://github.com/weakfl/SwiftFormatPlugin", exact: "0.61.1"), + .package(url: "https://github.com/weakfl/SwiftLintPlugin.git", exact: "0.63.3") ], targets: [ .target( diff --git a/CommonUI/Sources/CommonUI/ColorExtension.swift b/CommonUI/Sources/CommonUI/ColorExtension.swift index 41d93a213..ea46d3485 100644 --- a/CommonUI/Sources/CommonUI/ColorExtension.swift +++ b/CommonUI/Sources/CommonUI/ColorExtension.swift @@ -36,8 +36,7 @@ public extension Color { let g: CGFloat = components?[1] ?? 0.0 let b: CGFloat = components?[2] ?? 0.0 - let hexString = String(format: "#%02lX%02lX%02lX", lroundf(Float(r * 255)), lroundf(Float(g * 255)), lroundf(Float(b * 255))) - return hexString + return String(format: "#%02lX%02lX%02lX", lroundf(Float(r * 255)), lroundf(Float(g * 255)), lroundf(Float(b * 255))) } } diff --git a/CommonUI/Sources/CommonUI/LogsViewer.swift b/CommonUI/Sources/CommonUI/LogsViewer.swift index 8ed5c7040..5b5f369f9 100644 --- a/CommonUI/Sources/CommonUI/LogsViewer.swift +++ b/CommonUI/Sources/CommonUI/LogsViewer.swift @@ -17,8 +17,10 @@ import SwiftUI // Thanks to https://useyourloaf.com/blog/fetching-oslog-messages-in-swift/ public struct LogsViewer: View { - private static let template = NSPredicate(format: - "(subsystem BEGINSWITH $PREFIX)") + private static let template = NSPredicate( + format: + "(subsystem BEGINSWITH $PREFIX)" + ) @State private var text = String(localized: "Loading…") @State private var exportURL: URL? @@ -88,7 +90,8 @@ public struct LogsViewer: View { let predicate = Self.template.withSubstitutionVariables( [ "PREFIX": "org.openhab" - ]) + ] + ) let logs = try Logger.fetch( since: dayAgo, diff --git a/CommonUI/Sources/CommonUI/PreviewConstants.swift b/CommonUI/Sources/CommonUI/PreviewConstants.swift index bda1d6366..299ee058c 100644 --- a/CommonUI/Sources/CommonUI/PreviewConstants.swift +++ b/CommonUI/Sources/CommonUI/PreviewConstants.swift @@ -24,8 +24,7 @@ public enum PreviewConstants { let data = sitemapJson do { let sitemapPage = try data.decoded(as: Components.Schemas.PageDTO.self) - let openHABSitemapPage = OpenHABPage(sitemapPage) - return openHABSitemapPage + return OpenHABPage(sitemapPage) } catch { logger.error("Should not throw \(error.localizedDescription)") return nil diff --git a/NotificationService/NotificationService.swift b/NotificationService/NotificationService.swift index b85b37d57..861cb88b3 100644 --- a/NotificationService/NotificationService.swift +++ b/NotificationService/NotificationService.swift @@ -155,7 +155,7 @@ actor NotificationServiceHandler { return (localURL, urlResponse.mimeType) } - // Helper function to determine full URL + /// Helper function to determine full URL private func resolveFullURL(from url: String, baseURL: String) -> URL? { if let parsedURL = URL(string: url), parsedURL.scheme != nil { return parsedURL diff --git a/OpenHABCore/Sources/OpenHABCore/Model/NumberState.swift b/OpenHABCore/Sources/OpenHABCore/Model/NumberState.swift index 14366701f..441cc2f63 100644 --- a/OpenHABCore/Sources/OpenHABCore/Model/NumberState.swift +++ b/OpenHABCore/Sources/OpenHABCore/Model/NumberState.swift @@ -62,7 +62,7 @@ public struct NumberState: CustomStringConvertible, Equatable { return stringValue } - // Access to default memberwise initializer not permitted outside of package + /// Access to default memberwise initializer not permitted outside of package public init(value: Double, unit: String? = "", format: String? = "") { self.value = value self.unit = unit diff --git a/OpenHABCore/Sources/OpenHABCore/Model/OpenHABEvent.swift b/OpenHABCore/Sources/OpenHABCore/Model/OpenHABEvent.swift index e901a04e3..0fd504954 100644 --- a/OpenHABCore/Sources/OpenHABCore/Model/OpenHABEvent.swift +++ b/OpenHABCore/Sources/OpenHABCore/Model/OpenHABEvent.swift @@ -18,7 +18,7 @@ public struct OpenHABEvent: Decodable, Hashable, Sendable { case itemStateChangedEvent = "ItemStateChangedEvent" } - struct Payload: Decodable, Equatable, Hashable, Sendable { + struct Payload: Decodable, Equatable, Hashable { private enum CodingKeys: String, CodingKey { case type, value, oldType, oldValue, lastStateUpdate, lastStateChange } diff --git a/OpenHABCore/Sources/OpenHABCore/Model/OpenHABLink.swift b/OpenHABCore/Sources/OpenHABCore/Model/OpenHABLink.swift index 78d471215..d74f557c3 100644 --- a/OpenHABCore/Sources/OpenHABCore/Model/OpenHABLink.swift +++ b/OpenHABCore/Sources/OpenHABCore/Model/OpenHABLink.swift @@ -11,7 +11,7 @@ import Foundation -struct OpenHABLink: Decodable, Sendable { +struct OpenHABLink: Decodable { var type: String? var url: String? } diff --git a/OpenHABCore/Sources/OpenHABCore/Model/OpenHABNotification.swift b/OpenHABCore/Sources/OpenHABCore/Model/OpenHABNotification.swift index 9a4d81d45..8d87d10dc 100644 --- a/OpenHABCore/Sources/OpenHABCore/Model/OpenHABNotification.swift +++ b/OpenHABCore/Sources/OpenHABCore/Model/OpenHABNotification.swift @@ -27,9 +27,9 @@ public struct OpenHABNotification: Sendable { } } -// Decode an instance of OpenHABNotification.CodingData rather than decoding a OpenHABNotificaiton value directly, -// then convert that into a openHABNotification -// Inspired by https://www.swiftbysundell.com/basics/codable?rq=codingdata +/// Decode an instance of OpenHABNotification.CodingData rather than decoding a OpenHABNotificaiton value directly, +/// then convert that into a openHABNotification +/// Inspired by https://www.swiftbysundell.com/basics/codable?rq=codingdata public extension OpenHABNotification { struct CodingData: Decodable { private enum CodingKeys: String, CodingKey { @@ -46,7 +46,7 @@ public extension OpenHABNotification { } } -// Convenience method to convert a decoded value into a proper OpenHABNotification instance +/// Convenience method to convert a decoded value into a proper OpenHABNotification instance extension OpenHABNotification.CodingData { var openHABNotification: OpenHABNotification { OpenHABNotification(message: message, created: created, id: id) diff --git a/OpenHABCore/Sources/OpenHABCore/Model/OpenHABPage.swift b/OpenHABCore/Sources/OpenHABCore/Model/OpenHABPage.swift index 52e5a24da..bd2744b68 100644 --- a/OpenHABCore/Sources/OpenHABCore/Model/OpenHABPage.swift +++ b/OpenHABCore/Sources/OpenHABCore/Model/OpenHABPage.swift @@ -53,7 +53,7 @@ public class OpenHABPage: NSObject, @unchecked Sendable { public extension OpenHABPage { func filter(_ isIncluded: (OpenHABWidget) throws -> Bool) rethrows -> OpenHABPage { - let filteredOpenHABSitemapPage = try OpenHABPage( + try OpenHABPage( pageId: pageId, title: title, link: link, @@ -61,7 +61,6 @@ public extension OpenHABPage { widgets: widgets.filter(isIncluded), icon: icon ) - return filteredOpenHABSitemapPage } } diff --git a/OpenHABCore/Sources/OpenHABCore/Model/OpenHABServerProperties.swift b/OpenHABCore/Sources/OpenHABCore/Model/OpenHABServerProperties.swift index 04067a120..25e30b65a 100644 --- a/OpenHABCore/Sources/OpenHABCore/Model/OpenHABServerProperties.swift +++ b/OpenHABCore/Sources/OpenHABCore/Model/OpenHABServerProperties.swift @@ -19,11 +19,6 @@ public struct OpenHABServerProperties: Decodable, Sendable { linkUrl(byType: "habpanel") } - init(version: String?, links: [OpenHABLink]) { - self.version = version - self.links = links - } - public func linkUrl(byType type: String?) -> String? { if let index = links.firstIndex(where: { $0.type == type }) { links[index].url diff --git a/OpenHABCore/Sources/OpenHABCore/Model/OpenHABSitemap.swift b/OpenHABCore/Sources/OpenHABCore/Model/OpenHABSitemap.swift index 3e86efc7c..e88525657 100644 --- a/OpenHABCore/Sources/OpenHABCore/Model/OpenHABSitemap.swift +++ b/OpenHABCore/Sources/OpenHABCore/Model/OpenHABSitemap.swift @@ -11,8 +11,8 @@ import Foundation -// The OpenHAB REST API returns either a value (eg. String, Int, Double...) or false (not null). -// Inspired by https://stackoverflow.com/questions/52836448/decodable-value-string-or-bool +/// The OpenHAB REST API returns either a value (eg. String, Int, Double...) or false (not null). +/// Inspired by https://stackoverflow.com/questions/52836448/decodable-value-string-or-bool public struct ValueOrFalse: Decodable { let value: T? diff --git a/OpenHABCore/Sources/OpenHABCore/Model/OpenHABSitemapWidgetEvent.swift b/OpenHABCore/Sources/OpenHABCore/Model/OpenHABSitemapWidgetEvent.swift index 92a87c00c..52ff8f15e 100644 --- a/OpenHABCore/Sources/OpenHABCore/Model/OpenHABSitemapWidgetEvent.swift +++ b/OpenHABCore/Sources/OpenHABCore/Model/OpenHABSitemapWidgetEvent.swift @@ -46,6 +46,7 @@ public class OpenHABSitemapWidgetEvent { convenience init?(_ event: Components.Schemas.SitemapWidgetEvent?) { guard let event else { return nil } + // swiftlint:disable:next line_length self.init(sitemapName: event.sitemapName, pageId: event.pageId, widgetId: event.widgetId, label: event.label, labelSource: event.labelSource, icon: event.icon, reloadIcon: event.reloadIcon, labelcolor: event.labelcolor, valuecolor: event.valuecolor, iconcolor: event.iconcolor, visibility: event.visibility, state: event.state, enrichedItem: OpenHABItem(event.item), descriptionChanged: event.descriptionChanged) } } diff --git a/OpenHABCore/Sources/OpenHABCore/Model/OpenHABWidget.swift b/OpenHABCore/Sources/OpenHABCore/Model/OpenHABWidget.swift index f5cb824bd..55b0fb02e 100644 --- a/OpenHABCore/Sources/OpenHABCore/Model/OpenHABWidget.swift +++ b/OpenHABCore/Sources/OpenHABCore/Model/OpenHABWidget.swift @@ -109,8 +109,13 @@ public class OpenHABWidget: NSObject, MKAnnotation, Identifiable, ObservableObje public var stateless: Bool? public var yAxisDecimalPattern: String? - public var widgetType: WidgetType { type } - public var hasLinkedPage: Bool { linkedPage != nil } + public var widgetType: WidgetType { + type + } + + public var hasLinkedPage: Bool { + linkedPage != nil + } public var coordinate: CLLocationCoordinate2D { item?.stateAsLocation()?.coordinate ?? kCLLocationCoordinate2DInvalid @@ -118,7 +123,7 @@ public class OpenHABWidget: NSObject, MKAnnotation, Identifiable, ObservableObje } public extension OpenHABWidget { - // This is an ugly initializer + /// This is an ugly initializer convenience init(widgetId: String, label: String, icon: String, @@ -219,11 +224,12 @@ public extension OpenHABWidget { } convenience init(icon: String, iconColor: String? = nil) { + // swiftlint:disable:next line_length self.init(widgetId: "\(UUID())", label: "", icon: icon, type: .unknown, url: nil, period: nil, minValue: nil, maxValue: nil, step: nil, refresh: nil, height: nil, isLeaf: nil, iconColor: iconColor, labelColor: nil, valueColor: nil, service: nil, state: nil, text: nil, legend: nil, inputHint: nil, encoding: nil, item: nil, linkedPage: nil, mappings: [], widgets: [], visibility: nil, switchSupport: nil, forceAsItem: nil, labelSource: .unknown, releaseOnly: nil) } } -// Recursive parsing of nested widget structure +/// Recursive parsing of nested widget structure public extension [OpenHABWidget] { mutating func flatten(_ widgets: [Element]) { for widget in widgets { diff --git a/OpenHABCore/Sources/OpenHABCore/Model/SetpointDisplayFormatter.swift b/OpenHABCore/Sources/OpenHABCore/Model/SetpointDisplayFormatter.swift index 819e9ea81..7c7dc4db2 100644 --- a/OpenHABCore/Sources/OpenHABCore/Model/SetpointDisplayFormatter.swift +++ b/OpenHABCore/Sources/OpenHABCore/Model/SetpointDisplayFormatter.swift @@ -12,8 +12,8 @@ import Foundation public enum SetpointDisplayFormatter { - // Use a stable dot-decimal locale for platforms that intentionally avoid - // locale-aware formatting until all number controls are migrated together. + /// Use a stable dot-decimal locale for platforms that intentionally avoid + /// locale-aware formatting until all number controls are migrated together. public static let dotDecimalLocale = Locale(identifier: "en_US_POSIX") // swiftlint:disable:next function_parameter_count diff --git a/OpenHABCore/Sources/OpenHABCore/Util/BonjourService.swift b/OpenHABCore/Sources/OpenHABCore/Util/BonjourService.swift index b701f8c10..249a98d5d 100644 --- a/OpenHABCore/Sources/OpenHABCore/Util/BonjourService.swift +++ b/OpenHABCore/Sources/OpenHABCore/Util/BonjourService.swift @@ -31,7 +31,9 @@ public struct DiscoveredServer: Hashable, Sendable { public let address: String public let port: Int - public var url: String { "\(scheme)://\(address):\(port)" } + public var url: String { + "\(scheme)://\(address):\(port)" + } public init(scheme: String, address: String, port: Int) { self.scheme = scheme @@ -42,7 +44,7 @@ public struct DiscoveredServer: Hashable, Sendable { // MARK: - Address Utilities -enum BonjourAddressUtils: Sendable { +enum BonjourAddressUtils { /// Determines if an address should be filtered out (link-local, loopback, unique local) static func shouldFilterAddress(_ address: String) -> Bool { address.hasPrefix("fe80:") || @@ -101,12 +103,12 @@ public protocol BonjourServiceProtocol: AnyObject, Sendable { public final class BonjourService: NSObject, BonjourServiceProtocol, NetServiceBrowserDelegate, NetServiceDelegate, Sendable { // MARK: - Types - private struct SchemePort: Hashable, Sendable { + private struct SchemePort: Hashable { let scheme: String let port: Int } - private struct ServiceAddressKey: Hashable, Sendable { + private struct ServiceAddressKey: Hashable { let name: String let type: String } diff --git a/OpenHABCore/Sources/OpenHABCore/Util/ClientCertificateManager.swift b/OpenHABCore/Sources/OpenHABCore/Util/ClientCertificateManager.swift index 7eb5d9b4b..484c3cbaa 100644 --- a/OpenHABCore/Sources/OpenHABCore/Util/ClientCertificateManager.swift +++ b/OpenHABCore/Sources/OpenHABCore/Util/ClientCertificateManager.swift @@ -15,11 +15,11 @@ import Security @MainActor public protocol ClientCertificateManagerDelegate: AnyObject { - // delegate should ask user for a decision on whether to import the client certificate into the keychain + /// delegate should ask user for a decision on whether to import the client certificate into the keychain func askForClientCertificateImport(_ clientCertificateManager: ClientCertificateManager?) async -> Bool - // delegate should ask user for a decision on whether to import the client certificate into the keychain + /// delegate should ask user for a decision on whether to import the client certificate into the keychain func askForCertificatePassword(_ clientCertificateManager: ClientCertificateManager?) async -> String? - // delegate should alert the user that an error occured importing the certificate + /// delegate should alert the user that an error occured importing the certificate func alertClientCertificateError(_ clientCertificateManager: ClientCertificateManager?, errMsg: String) async } @@ -157,8 +157,7 @@ public class ClientCertificateManager { guard let delegate else { return false } - let shouldImport = await delegate.askForClientCertificateImport(self) - return shouldImport + return await delegate.askForClientCertificateImport(self) } catch { Logger.clientCert.error("Failed to read certificate from URL: \(error.localizedDescription)") return false diff --git a/OpenHABCore/Sources/OpenHABCore/Util/Collection+SafeAccess.swift b/OpenHABCore/Sources/OpenHABCore/Util/Collection+SafeAccess.swift index 25f71cb04..08d478bb7 100644 --- a/OpenHABCore/Sources/OpenHABCore/Util/Collection+SafeAccess.swift +++ b/OpenHABCore/Sources/OpenHABCore/Util/Collection+SafeAccess.swift @@ -12,7 +12,7 @@ import Foundation public extension Collection { - // Returns the element at the specified index if it is within bounds, otherwise nil. + /// Returns the element at the specified index if it is within bounds, otherwise nil. subscript(safe index: Index) -> Element? { indices.contains(index) ? self[index] : nil } diff --git a/OpenHABCore/Sources/OpenHABCore/Util/Comparable.swift b/OpenHABCore/Sources/OpenHABCore/Util/Comparable.swift index 1521ea733..f15ad0ca4 100644 --- a/OpenHABCore/Sources/OpenHABCore/Util/Comparable.swift +++ b/OpenHABCore/Sources/OpenHABCore/Util/Comparable.swift @@ -11,7 +11,7 @@ import Foundation -// Idea taken from https://twitter.com/alexiscreuzot/status/1635489793294454784?s=61&t=8ECwUy6QFS5UxjAFZzZ-hw +/// Idea taken from https://twitter.com/alexiscreuzot/status/1635489793294454784?s=61&t=8ECwUy6QFS5UxjAFZzZ-hw public extension Comparable { func clamped(to limits: ClosedRange) -> Self { min(max(self, limits.lowerBound), limits.upperBound) diff --git a/OpenHABCore/Sources/OpenHABCore/Util/ConnectionConfiguration.swift b/OpenHABCore/Sources/OpenHABCore/Util/ConnectionConfiguration.swift index cf3592b63..4aa1c38db 100644 --- a/OpenHABCore/Sources/OpenHABCore/Util/ConnectionConfiguration.swift +++ b/OpenHABCore/Sources/OpenHABCore/Util/ConnectionConfiguration.swift @@ -19,7 +19,7 @@ public struct ConnectionPayload: Codable { public struct ConnectionConfiguration: Hashable, Sendable, Codable, Equatable { private enum CodingKeys: String, CodingKey { case url, alwaysSendBasicAuth, ignoreSSL, supportsNotifications, priority, cloudUserId - // Legacy keys — decoded for migration from old JSON, never written + /// Legacy keys — decoded for migration from old JSON, never written case username, password } @@ -42,8 +42,8 @@ public struct ConnectionConfiguration: Hashable, Sendable, Codable, Equatable { self.supportsNotifications = supportsNotifications } - // decodeIfPresent is used for every field that has a default so that stored data from older - // versions (missing those fields) decodes without throwing. + /// decodeIfPresent is used for every field that has a default so that stored data from older + /// versions (missing those fields) decodes without throwing. public init(from decoder: any Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let rawURL = try container.decode(String.self, forKey: .url) @@ -58,7 +58,7 @@ public struct ConnectionConfiguration: Hashable, Sendable, Codable, Equatable { cloudUserId = try container.decodeIfPresent(String.self, forKey: .cloudUserId) } - // 🔹 Normalize a URL (removes trailing slashes, trims spaces, redirects openHAB cloud) + /// 🔹 Normalize a URL (removes trailing slashes, trims spaces, redirects openHAB cloud) private static func normalizeURL(_ url: String) -> String { var cleanedURL = url.trimmingCharacters(in: .whitespacesAndNewlines) // Trim whitespace cleanedURL = uriWithoutTrailingSlashes(cleanedURL) // Remove trailing slashes @@ -115,7 +115,9 @@ extension ConnectionConfiguration { public extension ConnectionConfiguration { /// The host component of the connection URL, if parseable. - var host: String? { URL(string: url)?.host } + var host: String? { + URL(string: url)?.host + } /// URL suitable for diagnostics; credentials embedded in the URL are stripped. var publicLogURL: String { @@ -134,7 +136,9 @@ public extension ConnectionConfiguration { /// Whether this connection is to an openHAB Cloud instance. /// Currently determined by the "openHAB Cloud Service" user preference (`supportsNotifications`). - var isCloudConnection: Bool { supportsNotifications } + var isCloudConnection: Bool { + supportsNotifications + } } extension ConnectionConfiguration: CustomStringConvertible { diff --git a/OpenHABCore/Sources/OpenHABCore/Util/DateFormatterExtension.swift b/OpenHABCore/Sources/OpenHABCore/Util/DateFormatterExtension.swift index df710006d..fe207a1a8 100644 --- a/OpenHABCore/Sources/OpenHABCore/Util/DateFormatterExtension.swift +++ b/OpenHABCore/Sources/OpenHABCore/Util/DateFormatterExtension.swift @@ -11,8 +11,8 @@ import Foundation -// Custom DateFormatter to handle fractional seconds -// Inspired by https://useyourloaf.com/blog/swift-codable-with-custom-dates/ +/// Custom DateFormatter to handle fractional seconds +/// Inspired by https://useyourloaf.com/blog/swift-codable-with-custom-dates/ public extension DateFormatter { static let iso8601Full: DateFormatter = { let formatter = DateFormatter() diff --git a/OpenHABCore/Sources/OpenHABCore/Util/ETagChecker.swift b/OpenHABCore/Sources/OpenHABCore/Util/ETagChecker.swift index ee34131fc..6b1a23d9b 100644 --- a/OpenHABCore/Sources/OpenHABCore/Util/ETagChecker.swift +++ b/OpenHABCore/Sources/OpenHABCore/Util/ETagChecker.swift @@ -29,7 +29,7 @@ public actor ETagChecker { cache = ETagCache.shared } - // Internal initializer for testing - allows cache injection + /// Internal initializer for testing - allows cache injection init(httpClient: HTTPClient, cache: ETagCache) { self.httpClient = httpClient self.cache = cache diff --git a/OpenHABCore/Sources/OpenHABCore/Util/Endpoint.swift b/OpenHABCore/Sources/OpenHABCore/Util/Endpoint.swift index 710d7d5c5..e46e58631 100644 --- a/OpenHABCore/Sources/OpenHABCore/Util/Endpoint.swift +++ b/OpenHABCore/Sources/OpenHABCore/Util/Endpoint.swift @@ -22,7 +22,9 @@ public enum IconType: Int, CaseIterable, Identifiable, CustomStringConvertible, case png case svg - public var id: Self { self } + public var id: Self { + self + } public var description: String { switch self { @@ -59,9 +61,8 @@ public extension Endpoint { var components = URLComponents(string: baseURL) components?.path = path components?.queryItems = queryItems - let url = components?.url + return components?.url // Logger.endpoint.debug("URL: \(url?.absoluteString ?? "", privacy: .private)") - return url } static func appleRegistration(prefsURL: String, diff --git a/OpenHABCore/Sources/OpenHABCore/Util/HTTPClient.swift b/OpenHABCore/Sources/OpenHABCore/Util/HTTPClient.swift index 86d5005b2..95e5defec 100644 --- a/OpenHABCore/Sources/OpenHABCore/Util/HTTPClient.swift +++ b/OpenHABCore/Sources/OpenHABCore/Util/HTTPClient.swift @@ -248,7 +248,7 @@ public final class HTTPClient: NSObject, Sendable { case mjpegStream } - // this can be changed if we detect another server + /// this can be changed if we detect another server public let baseURL: URL? private let connectionConfiguration: ConnectionConfiguration @@ -336,7 +336,7 @@ public final class HTTPClient: NSObject, Sendable { return codingDatas.map(\.openHABNotification) } - /** + /* Initiates a download request to a specified base URL for a specified path and returns the file URL via a completion handler. - Parameters: diff --git a/OpenHABCore/Sources/OpenHABCore/Util/HTTPClientDelegate.swift b/OpenHABCore/Sources/OpenHABCore/Util/HTTPClientDelegate.swift index a434b9602..d8bd81d1a 100644 --- a/OpenHABCore/Sources/OpenHABCore/Util/HTTPClientDelegate.swift +++ b/OpenHABCore/Sources/OpenHABCore/Util/HTTPClientDelegate.swift @@ -54,14 +54,11 @@ public final class HTTPClientDelegate: NSObject, URLSessionDelegate, URLSessionT } switch authenticationMethod { case NSURLAuthenticationMethodServerTrust: - let result = await handleServerTrust(challenge: challenge) - return result + return await handleServerTrust(challenge: challenge) case NSURLAuthenticationMethodDefault, NSURLAuthenticationMethodHTTPBasic: - let result = await handleBasicAuth(challenge: challenge) - return result + return await handleBasicAuth(challenge: challenge) case NSURLAuthenticationMethodClientCertificate: - let result = await handleClientCertificateAuth(challenge: challenge) - return result + return await handleClientCertificateAuth(challenge: challenge) default: return (.performDefaultHandling, nil) } diff --git a/OpenHABCore/Sources/OpenHABCore/Util/ItemEventStream.swift b/OpenHABCore/Sources/OpenHABCore/Util/ItemEventStream.swift index a7cf15b8f..79684bd70 100644 --- a/OpenHABCore/Sources/OpenHABCore/Util/ItemEventStream.swift +++ b/OpenHABCore/Sources/OpenHABCore/Util/ItemEventStream.swift @@ -15,7 +15,7 @@ import OpenAPIRuntime import OpenAPIURLSession import OSLog -/** +/* Example usage: ``` @@ -47,14 +47,16 @@ public enum StateStreamMessage: Sendable, Equatable { } public actor EventStream { - // Alive and Item State Chnage message structures + /// Alive and Item State Chnage message structures private struct Alive: Decodable { let type: String; let interval: Int } - // Multiple items can come in a single message which makes this a little more complicated + /// Multiple items can come in a single message which makes this a little more complicated private struct ItemStateChanges: Decodable { struct Value: Decodable { let state: String } let wrapped: [String: Value] - var first: (String, Value)? { wrapped.first } + var first: (String, Value)? { + wrapped.first + } init(from decoder: any Decoder) throws { let container = try decoder.singleValueContainer() @@ -110,7 +112,7 @@ public actor EventStream { continuations.removeValue(forKey: id) } - // NetworkManager callback + /// NetworkManager callback private func updateConnection(_ info: ConnectionInfo?) { let newConfig = info?.configuration diff --git a/OpenHABCore/Sources/OpenHABCore/Util/LoggerExtension.swift b/OpenHABCore/Sources/OpenHABCore/Util/LoggerExtension.swift index e4383376c..4db12a34b 100644 --- a/OpenHABCore/Sources/OpenHABCore/Util/LoggerExtension.swift +++ b/OpenHABCore/Sources/OpenHABCore/Util/LoggerExtension.swift @@ -12,7 +12,6 @@ // Inspired by https://www.avanderlee.com/debugging/oslog-unified-logging/ import Foundation - import os.log public extension Logger { diff --git a/OpenHABCore/Sources/OpenHABCore/Util/NWPathMonitoring.swift b/OpenHABCore/Sources/OpenHABCore/Util/NWPathMonitoring.swift index fd4e3fbc7..6f6513f95 100644 --- a/OpenHABCore/Sources/OpenHABCore/Util/NWPathMonitoring.swift +++ b/OpenHABCore/Sources/OpenHABCore/Util/NWPathMonitoring.swift @@ -13,7 +13,7 @@ import Foundation import Network import os.log -// Wrap real NWPathMonitor +/// Wrap real NWPathMonitor final class RealPathMonitor: NWPathMonitoring, Sendable { private let monitor: NWPathMonitor @@ -41,3 +41,4 @@ public protocol NWPathMonitoring: AnyObject, Sendable { func startMonitoring(handler: @escaping (Bool) async -> Void) async func cancel() } + diff --git a/OpenHABCore/Sources/OpenHABCore/Util/NetworkTracker.swift b/OpenHABCore/Sources/OpenHABCore/Util/NetworkTracker.swift index 1e0536bee..4463d6282 100644 --- a/OpenHABCore/Sources/OpenHABCore/Util/NetworkTracker.swift +++ b/OpenHABCore/Sources/OpenHABCore/Util/NetworkTracker.swift @@ -29,7 +29,7 @@ public struct ConnectionInfo: Equatable, Sendable { public let version: Int public let proxyURL: URL? - // Explicit public memberwise initializer + /// Explicit public memberwise initializer public init(configuration: ConnectionConfiguration, version: Int, proxyURL: URL? = nil) { self.configuration = configuration self.version = version @@ -51,14 +51,14 @@ public enum NetworkTrackerError: Error, CustomDebugStringConvertible, Sendable { } } -// Prevent race conditions. -// Ensure thread-safe dictionary access. -// Avoid memory corruption errors like unrecognized selector. +/// Prevent race conditions. +/// Ensure thread-safe dictionary access. +/// Avoid memory corruption errors like unrecognized selector. public actor ConnectionPool { private var services: [ConnectionConfiguration: any OpenAPIServiceProtocol] = [:] private let serviceFactory: @Sendable (ConnectionConfiguration) throws -> any OpenAPIServiceProtocol - // Initializer allowing the injection of mocked OpenAPIServiceProtocol + /// Initializer allowing the injection of mocked OpenAPIServiceProtocol init(serviceFactory: @escaping @Sendable (ConnectionConfiguration) throws -> any OpenAPIServiceProtocol = { try OpenAPIService(connectionConfiguration: $0, serviceConfiguration: .shortTerm) }) { @@ -77,7 +77,7 @@ public actor ConnectionPool { } } -// Ensures a thread safe access to failureCounts dictionary +/// Ensures a thread safe access to failureCounts dictionary public actor ConnectionFailureTracker { private var enabled = false private var failureCounts: [ConnectionConfiguration: Int] = [:] @@ -270,7 +270,7 @@ public actor NetworkTracker { } } - // like startTracking but with the already configured connections and a fresh approach + /// like startTracking but with the already configured connections and a fresh approach public func restartTracking() async { Logger.networkTracker.debug("Networktracker: restartTracking") await failureTracker.resetAll() // just to make sure a few more connection attempts happen if necessary @@ -280,7 +280,7 @@ public actor NetworkTracker { await startTracking(connectionConfigurations: connectionConfigurations) } - // This gets called periodically when we have an active connection to make sure it's still the best choice + /// This gets called periodically when we have an active connection to make sure it's still the best choice private func checkActiveConnection() async { guard status != .stopped else { return @@ -603,10 +603,10 @@ public extension NetworkTracker { return service } - // Retries once after revalidating the connection on two transient failure kinds: - // • ClientError — transport failure against a stale connection (network switch, suspension) - // • noActiveConnection — all connection tests timed out during a network handoff; the - // tracker recovers shortly after, so one revalidation + retry is enough. + /// Retries once after revalidating the connection on two transient failure kinds: + /// • ClientError — transport failure against a stale connection (network switch, suspension) + /// • noActiveConnection — all connection tests timed out during a network handoff; the + /// tracker recovers shortly after, so one revalidation + retry is enough. private func withClientErrorRetry(_ operation: () async throws -> T) async throws -> T { do { return try await operation() diff --git a/OpenHABCore/Sources/OpenHABCore/Util/OpenAPIService.swift b/OpenHABCore/Sources/OpenHABCore/Util/OpenAPIService.swift index 7e7f150e9..bdb9036ee 100644 --- a/OpenHABCore/Sources/OpenHABCore/Util/OpenAPIService.swift +++ b/OpenHABCore/Sources/OpenHABCore/Util/OpenAPIService.swift @@ -35,25 +35,23 @@ protocol OpenAPIServiceProtocol: AnyObject, Sendable { func sendItemCommand(itemname: String, command: String, sourcePrefix: String?, deviceId: String?) async throws func updateItemState(itemname: String, with: String, sourcePrefix: String?, deviceId: String?) async throws func getItems() async throws -> [OpenHABItem] - func getItems( - query: Operations.getItems.Input.Query - ) async throws -> [OpenHABItem] + func getItems(query: Operations.getItems.Input.Query) async throws -> [OpenHABItem] func getItemByName(id: String) async throws -> OpenHABItem? func pollDataForPage(sitemapname: String, pageId: String, longPolling: Bool) async throws -> OpenHABPage? func runNow(ruleUID: String, payload: [String: any Sendable]) async throws } -// The generated OpenAPI client is wrapped by this curated API. -// The library leaks the fact that it uses Swift OpenAPI Generator under the hood in 'openHABSitemapWidgetEvents'. -// It will require the migration to Swift 6.1 before this can be changed. +/// The generated OpenAPI client is wrapped by this curated API. +/// The library leaks the fact that it uses Swift OpenAPI Generator under the hood in 'openHABSitemapWidgetEvents'. +/// It will require the migration to Swift 6.1 before this can be changed. public actor OpenAPIService { private var client: any APIProtocol private var url: URL? private var longPolling = false private var connectionConfiguration: ConnectionConfiguration - // Retained for lifecycle control only — URLSessionTransport does not call invalidateAndCancel() - // when it deallocates, so without this reference the session's threads and sockets would leak. - // Do not remove: deinit relies on this property to tear down the session. + /// Retained for lifecycle control only — URLSessionTransport does not call invalidateAndCancel() + /// when it deallocates, so without this reference the session's threads and sockets would leak. + /// Do not remove: deinit relies on this property to tear down the session. private var urlSession: URLSession? /// Creates a new client for OpenAPIService. @@ -110,10 +108,9 @@ public actor OpenAPIService { } private func prepareURLSessionConfiguration(longPolling: Bool) -> URLSessionConfiguration { - let config = URLSessionConfiguration.default + URLSessionConfiguration.default // config.timeoutIntervalForRequest = if longPolling { 35.0 } else { 20.0 } // config.timeoutIntervalForResource = config.timeoutIntervalForRequest + 25 - return config } private func sourceComponent(deviceId: String?) -> String? { @@ -206,7 +203,8 @@ public extension OpenAPIService { func runNow(ruleUID: String, payload: [String: any Sendable]) async throws { let path = Operations.runRuleNow_1.Input.Path(ruleUID: ruleUID) let jsonPayload = try Operations.runRuleNow_1.Input.Body.jsonPayload( - additionalProperties: OpenAPIObjectContainer(unvalidatedValue: payload)) + additionalProperties: OpenAPIObjectContainer(unvalidatedValue: payload) + ) _ = try await client.runRuleNow_1( path: path, body: .json(jsonPayload) @@ -234,7 +232,9 @@ public extension OpenAPIService { _next = { try await it.next() } } - public mutating func next() async throws -> Element? { try await _next() } + public mutating func next() async throws -> Element? { + try await _next() + } } public let _make: () -> Iterator @@ -243,10 +243,12 @@ public extension OpenAPIService { _make = { Iterator(base) } } - public func makeAsyncIterator() -> Iterator { _make() } + public func makeAsyncIterator() -> Iterator { + _make() + } } - // Returns subscription id or nil + /// Returns subscription id or nil func openHABcreateSubscription() async throws -> String? { Logger.openAPIService.info("Creating subscription") let result = try await client.createSitemapEventSubscription() @@ -282,7 +284,7 @@ public extension OpenAPIService { } public extension OpenAPIService { - // Internal function for pollPage + /// Internal function for pollPage internal func pollDataForPage(path: Operations.pollDataForPage.Input.Path, query: Operations.pollDataForPage.Input.Query = .init(), headers: Operations.pollDataForPage.Input.Headers) async throws -> OpenHABPage? { @@ -320,7 +322,7 @@ public extension OpenAPIService { return try await pollDataForPage(path: path, headers: headers) } - // Internal function for pollSitemap + /// Internal function for pollSitemap internal func pollDataForSitemap(path: Operations.pollDataForSitemap.Input.Path, query: Operations.pollDataForSitemap.Input.Query = .init(), headers: Operations.pollDataForSitemap.Input.Headers) async throws -> OpenHABSitemap? { @@ -330,11 +332,9 @@ public extension OpenAPIService { } } -// Array of items +/// Array of items public extension OpenAPIService { - func getItems( - query: Operations.getItems.Input.Query - ) async throws -> [OpenHABItem] { + func getItems(query: Operations.getItems.Input.Query) async throws -> [OpenHABItem] { try await client.getItems(query: query) .ok.body.json .compactMap(OpenHABItem.init) diff --git a/OpenHABCore/Sources/OpenHABCore/Util/OpenHABImageProcessor.swift b/OpenHABCore/Sources/OpenHABCore/Util/OpenHABImageProcessor.swift index 9f82b9c50..0caffa8e0 100644 --- a/OpenHABCore/Sources/OpenHABCore/Util/OpenHABImageProcessor.swift +++ b/OpenHABCore/Sources/OpenHABCore/Util/OpenHABImageProcessor.swift @@ -231,7 +231,7 @@ public struct OpenHABImageProcessor: ImageProcessor { process(item: .data(data), options: KingfisherParsedOptionsInfo(KingfisherManager.shared.defaultOptions)) } - // Convert input data/image to target image and return it. + /// Convert input data/image to target image and return it. public func process(item: ImageProcessItem, options: KingfisherParsedOptionsInfo) -> KFCrossPlatformImage? { switch item { case let .image(image): diff --git a/OpenHABCore/Sources/OpenHABCore/Util/OpenHABItemCache.swift b/OpenHABCore/Sources/OpenHABCore/Util/OpenHABItemCache.swift index 21a404344..f0174f1cd 100644 --- a/OpenHABCore/Sources/OpenHABCore/Util/OpenHABItemCache.swift +++ b/OpenHABCore/Sources/OpenHABCore/Util/OpenHABItemCache.swift @@ -147,9 +147,9 @@ public actor OpenHABItemCache { public static let instance = OpenHABItemCache() - // Per-home trackers are created fresh each session (cold-start cost on first use). - // 10 s matches NetworkTracker.shared and is enough to survive a typical cellular - // handoff (~7 s in practice) without failing with noActiveConnection. + /// Per-home trackers are created fresh each session (cold-start cost on first use). + /// 10 s matches NetworkTracker.shared and is enough to survive a typical cellular + /// handoff (~7 s in practice) without failing with noActiveConnection. private static let networkTimeout: TimeInterval = 10 private static let stubsDefaultsKey = "openHABItemStubs" diff --git a/OpenHABCore/Sources/OpenHABCore/Util/Preferences.swift b/OpenHABCore/Sources/OpenHABCore/Util/Preferences.swift index 699d2e340..3579d3bd6 100644 --- a/OpenHABCore/Sources/OpenHABCore/Util/Preferences.swift +++ b/OpenHABCore/Sources/OpenHABCore/Util/Preferences.swift @@ -108,11 +108,11 @@ public struct HomePreferences: Codable, Equatable { self.id = id } - // Custom decoder so that stored data from older app versions that are missing - // fields added later (e.g. alwaysAllowWebRTC, defaultMainUIPath, siteMapForWatchLabel) - // still decodes successfully. Without this, synthesized Codable requires every field - // to be present and silently falls back to the struct default via `try?` in - // UserDefaultObject — resetting homeName to "Home#1" for those users. + /// Custom decoder so that stored data from older app versions that are missing + /// fields added later (e.g. alwaysAllowWebRTC, defaultMainUIPath, siteMapForWatchLabel) + /// still decodes successfully. Without this, synthesized Codable requires every field + /// to be present and silently falls back to the struct default via `try?` in + /// UserDefaultObject — resetting homeName to "Home#1" for those users. public nonisolated init(from decoder: any Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) id = try container.decode(UUID.self, forKey: .id) @@ -355,12 +355,11 @@ public extension Preferences { @MainActor public extension Preferences { func listStoredHomes() -> [UUID] { - let preferenceIds = storedHomes + storedHomes .sorted { e1, e2 in e1.value.homeName <= e2.value.homeName } .map(\.key) - return preferenceIds } func createAndLoadNewStoredSettings(homeName: String) { @@ -611,7 +610,7 @@ public extension Preferences { getNotificationConnection(of: [homeConfig.remoteConnectionConfig]) } - // this will support mutliple connection configs, right now we just pass in the remote config + /// this will support mutliple connection configs, right now we just pass in the remote config func getNotificationConnection(of connections: [ConnectionConfiguration?]) -> ConnectionConfiguration? { connections .compactMap(\.self) diff --git a/OpenHABCore/Sources/OpenHABCore/Util/ServerCertificateManager.swift b/OpenHABCore/Sources/OpenHABCore/Util/ServerCertificateManager.swift index c59b12fae..c6b11ae18 100644 --- a/OpenHABCore/Sources/OpenHABCore/Util/ServerCertificateManager.swift +++ b/OpenHABCore/Sources/OpenHABCore/Util/ServerCertificateManager.swift @@ -14,11 +14,11 @@ import os.log @MainActor public protocol ServerCertificateManagerDelegate: AnyObject, Sendable { - // delegate should ask user for a decision on what to do with invalid certificate + /// delegate should ask user for a decision on what to do with invalid certificate func evaluateServerTrust(summary certificateSummary: String?, forDomain domain: String?) async -> ServerCertificateManager.EvaluateResult - // certificate received from openHAB doesn't match our record, ask user for a decision + /// certificate received from openHAB doesn't match our record, ask user for a decision func evaluateCertificateMismatch(summary certificateSummary: String?, forDomain domain: String?) async -> ServerCertificateManager.EvaluateResult - // notify delegate that the certificagtes that a user is willing to trust has changed + /// notify delegate that the certificagtes that a user is willing to trust has changed func acceptedServerCertificatesChanged() async } @@ -28,7 +28,7 @@ enum ServerCertificateManagerError: Error { @MainActor public final class ServerCertificateManager { - // Handle the different responses of the user + /// Handle the different responses of the user public enum EvaluateResult: Sendable { case undecided case deny @@ -37,10 +37,10 @@ public final class ServerCertificateManager { } public weak var delegate: (any ServerCertificateManagerDelegate)? - // ignoreSSL is a synonym for allowInvalidCertificates, ignoreCertificates + /// ignoreSSL is a synonym for allowInvalidCertificates, ignoreCertificates public var ignoreSSL = false - // Init a ServerCertificateManager and set ignore certificates setting + /// Init a ServerCertificateManager and set ignore certificates setting public init(ignoreSSL: Bool = false) { self.ignoreSSL = ignoreSSL Logger.serverCert.info("Initializing cert manager, delegating to CertificateStore") @@ -78,8 +78,8 @@ public final class ServerCertificateManager { return result } - // Evaluates trust received during SSL negotiation and checks it against known ones, - // against policy setting to ignore certificate errors and so on. + /// Evaluates trust received during SSL negotiation and checks it against known ones, + /// against policy setting to ignore certificate errors and so on. public func evaluate(_ serverTrust: SecTrust, forHost domain: String) async throws { let evaluateResult = wrapperSecTrustEvaluate(serverTrust: serverTrust) diff --git a/OpenHABCore/Sources/OpenHABCore/Util/StringExtension.swift b/OpenHABCore/Sources/OpenHABCore/Util/StringExtension.swift index 283dcdaca..0501e9818 100644 --- a/OpenHABCore/Sources/OpenHABCore/Util/StringExtension.swift +++ b/OpenHABCore/Sources/OpenHABCore/Util/StringExtension.swift @@ -57,8 +57,8 @@ public extension String { URL(string: self) == URL(string: self)?.absoluteURL } - // Sub-view gape title optionally concatenated with one space and value if present - to be inline with Nsic UI - // e.g. "Living Room [21°C]" → "Living Room 21°C" + /// Sub-view gape title optionally concatenated with one space and value if present - to be inline with Nsic UI + /// e.g. "Living Room [21°C]" → "Living Room 21°C" var labelValueTitle: String { // Base text before the first “[” let base = components(separatedBy: "[")[0] diff --git a/OpenHABCore/Sources/OpenHABCore/Util/UIColorExtension.swift b/OpenHABCore/Sources/OpenHABCore/Util/UIColorExtension.swift index 61ae7109c..74809beb1 100644 --- a/OpenHABCore/Sources/OpenHABCore/Util/UIColorExtension.swift +++ b/OpenHABCore/Sources/OpenHABCore/Util/UIColorExtension.swift @@ -30,7 +30,7 @@ public enum OHInterfaceStyle: Int { } public extension UIColor { - // system colors + /// system colors class var ohLabel: UIColor { #if os(iOS) if #available(iOS 13.0, *) { @@ -82,7 +82,7 @@ public extension UIColor { return .white } - // standard colors + /// standard colors class var ohMaroon: UIColor { OHInterfaceStyle.current == .light ? UIColor(hex: "#800000") : UIColor(hex: "#800000") } diff --git a/OpenHABCore/Tests/OpenHABCoreTests/CertificateStoreTests.swift b/OpenHABCore/Tests/OpenHABCoreTests/CertificateStoreTests.swift index 021f76ff3..facfd740a 100644 --- a/OpenHABCore/Tests/OpenHABCoreTests/CertificateStoreTests.swift +++ b/OpenHABCore/Tests/OpenHABCoreTests/CertificateStoreTests.swift @@ -22,7 +22,7 @@ enum CertificateStoreTestError: Error { @Suite("CertificateStore Tests", .serialized) struct CertificateStoreTests { - // Helper to load the bundled test certificate data + /// Helper to load the bundled test certificate data func loadTestCertificateData() throws -> Data { guard let certURL = Bundle.module.url(forResource: "test-cert", withExtension: "cer") else { throw CertificateStoreTestError.resourceNotFound("test-cert.cer") @@ -30,12 +30,12 @@ struct CertificateStoreTests { return try Data(contentsOf: certURL) } - // Helper to create a fresh in-memory store for isolated testing + /// Helper to create a fresh in-memory store for isolated testing func makeInMemoryStore() -> CertificateStore { CertificateStore(persistencePath: nil) } - // Helper to create a unique temporary path for persistence tests + /// Helper to create a unique temporary path for persistence tests func makeTempPersistencePath() -> URL { let tempDir = FileManager.default.temporaryDirectory let uniqueID = UUID().uuidString @@ -57,7 +57,7 @@ struct CertificateStoreTests { let info = await store.getCertificateInfo(forDomain: domain) #expect(info != nil) #expect(info?.data == data) - #expect(info!.dateAccepted.timeIntervalSinceNow > -5) // stored just now + #expect(try #require(info?.dateAccepted.timeIntervalSinceNow) > -5) // stored just now } @Test("Overwrite existing certificate updates data and date", .timeLimit(.minutes(1))) @@ -69,7 +69,7 @@ struct CertificateStoreTests { await store.storeCertificateData(first, forDomain: domain) let firstInfo = await store.getCertificateInfo(forDomain: domain) #expect(firstInfo != nil) - let firstDate = firstInfo!.dateAccepted + let firstDate = try #require(firstInfo?.dateAccepted) // Overwrite with new data let second = Data([0x0A, 0x0B]) @@ -77,12 +77,12 @@ struct CertificateStoreTests { let info = await store.getCertificateInfo(forDomain: domain) #expect(info != nil) - #expect(info!.data == second) - #expect(info!.dateAccepted >= firstDate) + #expect(info?.data == second) + #expect(try #require(info?.dateAccepted) >= firstDate) } @Test("Remove certificate clears entry", .timeLimit(.minutes(1))) - func removeClears() async throws { + func removeClears() async { let domain = "remove-test.openhab.org" let store = makeInMemoryStore() @@ -98,7 +98,7 @@ struct CertificateStoreTests { } @Test("Store nil removes certificate", .timeLimit(.minutes(1))) - func storeNilRemoves() async throws { + func storeNilRemoves() async { let domain = "nil-remove-test.openhab.org" let store = makeInMemoryStore() @@ -111,7 +111,7 @@ struct CertificateStoreTests { } @Test("Get all certificates returns expected entries", .timeLimit(.minutes(1))) - func getAllCertificates() async throws { + func getAllCertificates() async { let domainA = "list-a-test.openhab.org" let domainB = "list-b-test.openhab.org" let store = makeInMemoryStore() @@ -129,7 +129,7 @@ struct CertificateStoreTests { } @Test("Persistence across instances", .timeLimit(.minutes(1))) - func persistenceAcrossInstances() async throws { + func persistenceAcrossInstances() async { let domain = "persistence-test.openhab.org" let tempPath = makeTempPersistencePath() diff --git a/OpenHABCore/Tests/OpenHABCoreTests/ClientCertificateManagerTests.swift b/OpenHABCore/Tests/OpenHABCoreTests/ClientCertificateManagerTests.swift index f8c9d5360..8b2925a85 100644 --- a/OpenHABCore/Tests/OpenHABCoreTests/ClientCertificateManagerTests.swift +++ b/OpenHABCore/Tests/OpenHABCoreTests/ClientCertificateManagerTests.swift @@ -56,7 +56,7 @@ struct ClientCertificateManagerTests { #expect(!result) } - @Test func startImportCertificateReturnsTrueIfDelegateApproves() async throws { + @Test func startImportCertificateReturnsTrueIfDelegateApproves() async { guard let url = Bundle.module.url(forResource: "test", withExtension: "p12") else { Issue.record("Test PKCS#12 file not found.") return diff --git a/OpenHABCore/Tests/OpenHABCoreTests/CredentialsStoreTests.swift b/OpenHABCore/Tests/OpenHABCoreTests/CredentialsStoreTests.swift index 45a4e5bf3..f6282aa99 100644 --- a/OpenHABCore/Tests/OpenHABCoreTests/CredentialsStoreTests.swift +++ b/OpenHABCore/Tests/OpenHABCoreTests/CredentialsStoreTests.swift @@ -12,9 +12,9 @@ @testable import OpenHABCore import Testing -// These tests require the openHAB app as the test host so the xctest process inherits -// the keychain-access-groups entitlement. Without it SecItemAdd returns errSecMissingEntitlement -// and all store/retrieve tests fail. Configure the test scheme to use openHAB.app as the host. +/// These tests require the openHAB app as the test host so the xctest process inherits +/// the keychain-access-groups entitlement. Without it SecItemAdd returns errSecMissingEntitlement +/// and all store/retrieve tests fail. Configure the test scheme to use openHAB.app as the host. @Suite("CredentialsStore Tests", .serialized, .disabled("Requires openHAB.app test host for Keychain entitlement")) struct CredentialsStoreTests { private let homeId = UUID() diff --git a/OpenHABCore/Tests/OpenHABCoreTests/DateFormattingTests.swift b/OpenHABCore/Tests/OpenHABCoreTests/DateFormattingTests.swift index b3cd193e0..b4da9e1e5 100644 --- a/OpenHABCore/Tests/OpenHABCoreTests/DateFormattingTests.swift +++ b/OpenHABCore/Tests/OpenHABCoreTests/DateFormattingTests.swift @@ -20,7 +20,7 @@ private struct TimestampModel: Decodable { struct DateFormattingTests { // MARK: - ISO8601 Date Parsing Tests - @Test func iSO8601DateParsingWithFractionalSeconds() throws { + @Test func iSO8601DateParsingWithFractionalSeconds() { let dateString = "2025-12-21T10:30:45.123+00:00" let date = try? Date(dateString, strategy: Date.ISO8601FormatStyle(includingFractionalSeconds: true)) #expect(date != nil) @@ -39,37 +39,37 @@ struct DateFormattingTests { #expect(model2.timestamp.timeIntervalSince1970 > 0) } - @Test func iSO8601DateParsingWithZuluTimeZone() throws { + @Test func iSO8601DateParsingWithZuluTimeZone() { let dateString = "2025-12-21T10:30:45.123Z" let date = try? Date(dateString, strategy: Date.ISO8601FormatStyle(includingFractionalSeconds: true)) #expect(date != nil) } - @Test func iSO8601DateParsingWithPositiveTimeZone() throws { + @Test func iSO8601DateParsingWithPositiveTimeZone() { let dateString = "2025-12-21T10:30:45.123+05:30" let date = try? Date(dateString, strategy: Date.ISO8601FormatStyle(includingFractionalSeconds: true)) #expect(date != nil) } - @Test func iSO8601DateParsingWithNegativeTimeZone() throws { + @Test func iSO8601DateParsingWithNegativeTimeZone() { let dateString = "2025-12-21T10:30:45.123-08:00" let date = try? Date(dateString, strategy: Date.ISO8601FormatStyle(includingFractionalSeconds: true)) #expect(date != nil) } - @Test func iSO8601DateParsingWithMilliseconds() throws { + @Test func iSO8601DateParsingWithMilliseconds() { let dateString = "2025-12-21T10:30:45.999+00:00" let date = try? Date(dateString, strategy: Date.ISO8601FormatStyle(includingFractionalSeconds: true)) #expect(date != nil) } - @Test func iSO8601DateParsingWithInvalidFormat() throws { + @Test func iSO8601DateParsingWithInvalidFormat() { let dateString = "not a date" let date = try? Date(dateString, strategy: Date.ISO8601FormatStyle(includingFractionalSeconds: true)) #expect(date == nil) } - @Test func iSO8601DateParsingWithEmptyString() throws { + @Test func iSO8601DateParsingWithEmptyString() { let dateString = "" let date = try? Date(dateString, strategy: Date.ISO8601FormatStyle(includingFractionalSeconds: true)) #expect(date == nil) @@ -77,7 +77,7 @@ struct DateFormattingTests { // MARK: - ISO8601 Date Formatting Tests - @Test func iSO8601DateFormattingWithFractionalSeconds() throws { + @Test func iSO8601DateFormattingWithFractionalSeconds() { // Create a date with a known value let calendar = Calendar(identifier: .gregorian) var components = DateComponents() @@ -103,7 +103,7 @@ struct DateFormattingTests { #expect(formatted.contains("10:30:45")) } - @Test func iSO8601DateRoundTrip() throws { + @Test func iSO8601DateRoundTrip() { // Test that formatting and parsing a date produces the same value (within millisecond precision) let calendar = Calendar(identifier: .gregorian) var components = DateComponents() @@ -171,7 +171,7 @@ struct DateFormattingTests { @Test func dateTimeFormattingWithHourMinuteSecond() throws { var calendar = Calendar(identifier: .gregorian) - let tz = TimeZone(secondsFromGMT: 0)! // deterministic on CI + let tz = try #require(TimeZone(secondsFromGMT: 0)) // deterministic on CI calendar.timeZone = tz var components = DateComponents() @@ -197,7 +197,7 @@ struct DateFormattingTests { #expect(formatted.contains("T14:30:45")) } - @Test func dateFormattingAbbreviated() throws { + @Test func dateFormattingAbbreviated() { let calendar = Calendar(identifier: .gregorian) var components = DateComponents() components.year = 2025 @@ -216,7 +216,7 @@ struct DateFormattingTests { #expect(!formatted.isEmpty) } - @Test func dateFormattingTimeOmitted() throws { + @Test func dateFormattingTimeOmitted() { let calendar = Calendar(identifier: .gregorian) var components = DateComponents() components.year = 2025 @@ -238,7 +238,7 @@ struct DateFormattingTests { // MARK: - Log Date Format Tests (en_US_POSIX) - @Test func logDateFormattingWithPOSIXLocale() throws { + @Test func logDateFormattingWithPOSIXLocale() { // Test date formatting with en_US_POSIX locale for consistent log output let date = Date() @@ -261,7 +261,7 @@ struct DateFormattingTests { #expect(formatted.count > 15) } - @Test func logDateFormattingWithSingleDigitsPOSIXLocale() throws { + @Test func logDateFormattingWithSingleDigitsPOSIXLocale() { // Test with single digit month/day to verify two-digit padding let calendar = Calendar(identifier: .gregorian) var dateComponents = DateComponents() @@ -294,7 +294,7 @@ struct DateFormattingTests { #expect(formatted.contains("2025")) } - @Test func logDateFormattingConsistencyWithPOSIXLocale() throws { + @Test func logDateFormattingConsistencyWithPOSIXLocale() { // Verify that en_US_POSIX locale produces consistent, non-localized output let date1 = Date() let date2 = Date() @@ -330,7 +330,7 @@ struct DateFormattingTests { // MARK: - Screen Saver Time Format Tests - @Test func screenSaverTime24HourFormatWithLeadingZeros() throws { + @Test func screenSaverTime24HourFormatWithLeadingZeros() { // Test 24-hour format with leading zeros for screen saver let calendar = Calendar(identifier: .gregorian) var dateComponents = DateComponents() @@ -356,7 +356,7 @@ struct DateFormattingTests { #expect(formatted.contains("03")) } - @Test func screenSaverTime12HourFormatNaturalStyle() throws { + @Test func screenSaverTime12HourFormatNaturalStyle() { // Test 12-hour format with natural style (no leading zero for hours) let calendar = Calendar(identifier: .gregorian) var dateComponents = DateComponents() @@ -379,7 +379,7 @@ struct DateFormattingTests { #expect(formatted.contains("05")) // Minutes should be padded } - @Test func screenSaverTimeMidnightFormat() throws { + @Test func screenSaverTimeMidnightFormat() { // Test midnight (00:00:00) with 24-hour format let calendar = Calendar(identifier: .gregorian) var dateComponents = DateComponents() @@ -403,7 +403,7 @@ struct DateFormattingTests { #expect(formatted.contains("00")) } - @Test func screenSaverTimeWithoutSeconds() throws { + @Test func screenSaverTimeWithoutSeconds() { // Test time format without seconds (HH:mm) - verify format structure let date = Date() @@ -420,7 +420,7 @@ struct DateFormattingTests { #expect(formatted.count >= 5) } - @Test func screenSaverTime24HourVs12HourFormatting() throws { + @Test func screenSaverTime24HourVs12HourFormatting() { // Test the difference between 24-hour and 12-hour formatting let calendar = Calendar(identifier: .gregorian) var dateComponents = DateComponents() @@ -458,7 +458,7 @@ struct DateFormattingTests { // MARK: - Notification Timestamp Format Tests - @Test func notificationTimestampWithLongDateStandardTime() throws { + @Test func notificationTimestampWithLongDateStandardTime() { // Test notification format with long date and standard time (closest to original DateFormatter medium) let calendar = Calendar(identifier: .gregorian) var dateComponents = DateComponents() @@ -482,7 +482,7 @@ struct DateFormattingTests { // Exact format depends on locale, but should contain all components } - @Test func notificationTimestampIncludesSeconds() throws { + @Test func notificationTimestampIncludesSeconds() { // Verify that standard time format includes seconds (unlike shortened) let calendar = Calendar(identifier: .gregorian) var dateComponents = DateComponents() @@ -503,7 +503,7 @@ struct DateFormattingTests { #expect(standardFormat.count >= shortenedFormat.count) } - @Test func notificationTimestampConsistentFormat() throws { + @Test func notificationTimestampConsistentFormat() { // Test that notification timestamps are formatted consistently let calendar = Calendar(identifier: .gregorian) var dateComponents = DateComponents() diff --git a/OpenHABCore/Tests/OpenHABCoreTests/DoubleExtensionTests.swift b/OpenHABCore/Tests/OpenHABCoreTests/DoubleExtensionTests.swift index c50f61c48..1bf3949d3 100644 --- a/OpenHABCore/Tests/OpenHABCoreTests/DoubleExtensionTests.swift +++ b/OpenHABCore/Tests/OpenHABCoreTests/DoubleExtensionTests.swift @@ -14,7 +14,7 @@ import Testing struct DoubleExtensionTests { @Test - func valueTextWithNoDecimalPlaces() throws { + func valueTextWithNoDecimalPlaces() { let value = 42.0 let step = 1.0 let result = value.valueText(step: step) @@ -22,7 +22,7 @@ struct DoubleExtensionTests { } @Test - func valueTextWithOneDecimalPlace() throws { + func valueTextWithOneDecimalPlace() { let value = 42.5 let step = 0.1 let result = value.valueText(step: step) @@ -30,7 +30,7 @@ struct DoubleExtensionTests { } @Test - func valueTextWithTwoDecimalPlaces() throws { + func valueTextWithTwoDecimalPlaces() { let value = 42.75 let step = 0.01 let result = value.valueText(step: step) @@ -38,7 +38,7 @@ struct DoubleExtensionTests { } @Test - func valueTextWithThreeDecimalPlaces() throws { + func valueTextWithThreeDecimalPlaces() { let value = 3.142 let step = 0.001 let result = value.valueText(step: step) @@ -46,7 +46,7 @@ struct DoubleExtensionTests { } @Test - func valueTextRoundsToStepPrecision() throws { + func valueTextRoundsToStepPrecision() { let value = 3.14159 let step = 0.01 let result = value.valueText(step: step) @@ -54,7 +54,7 @@ struct DoubleExtensionTests { } @Test - func valueTextWithZeroValue() throws { + func valueTextWithZeroValue() { let value = 0.0 let step = 0.1 let result = value.valueText(step: step) @@ -62,7 +62,7 @@ struct DoubleExtensionTests { } @Test - func valueTextWithNegativeValue() throws { + func valueTextWithNegativeValue() { let value = -42.5 let step = 0.1 let result = value.valueText(step: step) @@ -70,7 +70,7 @@ struct DoubleExtensionTests { } @Test - func valueTextWithVerySmallStep() throws { + func valueTextWithVerySmallStep() { let value = 1.23456789 let step = 0.00001 let result = value.valueText(step: step) @@ -78,7 +78,7 @@ struct DoubleExtensionTests { } @Test - func valueTextPadsTrailingZeros() throws { + func valueTextPadsTrailingZeros() { let value = 42.0 let step = 0.01 let result = value.valueText(step: step) @@ -86,7 +86,7 @@ struct DoubleExtensionTests { } @Test - func valueTextWithLargeValue() throws { + func valueTextWithLargeValue() { let value = 12345.67 let step = 0.1 let result = value.valueText(step: step) @@ -94,7 +94,7 @@ struct DoubleExtensionTests { } @Test - func valueTextUsesDecimalPoint() throws { + func valueTextUsesDecimalPoint() { let value = 1234.5 let step = 0.1 let result = value.valueText(step: step) @@ -103,7 +103,7 @@ struct DoubleExtensionTests { } @Test - func valueTextNoThousandsSeparator() throws { + func valueTextNoThousandsSeparator() { let value = 1_000_000.0 let step = 1.0 let result = value.valueText(step: step) diff --git a/OpenHABCore/Tests/OpenHABCoreTests/ETagCheckerTests.swift b/OpenHABCore/Tests/OpenHABCoreTests/ETagCheckerTests.swift index 4c0059f4e..2a27aaba3 100644 --- a/OpenHABCore/Tests/OpenHABCoreTests/ETagCheckerTests.swift +++ b/OpenHABCore/Tests/OpenHABCoreTests/ETagCheckerTests.swift @@ -83,7 +83,7 @@ struct ETagCheckerTests { func firstRunStoresETag() async throws { MockURLProtocol.reset() - let testURL = URL(string: "https://example.com/index.html")! + let testURL = try #require(URL(string: "https://example.com/index.html")) let etagValue = "\"abc123\"" // Setup mock @@ -111,7 +111,7 @@ struct ETagCheckerTests { func unchangedWhenETagMatches() async throws { MockURLProtocol.reset() - let testURL = URL(string: "https://example.com/data.json")! + let testURL = try #require(URL(string: "https://example.com/data.json")) let etagValue = "\"def456\"" // Pre-populate cache @@ -136,7 +136,7 @@ struct ETagCheckerTests { func changedWhenETagDiffers() async throws { MockURLProtocol.reset() - let testURL = URL(string: "https://example.com/api/v1/data")! + let testURL = try #require(URL(string: "https://example.com/api/v1/data")) let oldETag = "\"old123\"" let newETag = "\"new456\"" @@ -166,7 +166,7 @@ struct ETagCheckerTests { func failedOnNetworkError() async throws { MockURLProtocol.reset() - let testURL = URL(string: "https://example.com/fail")! + let testURL = try #require(URL(string: "https://example.com/fail")) // Configure mock to fail MockURLProtocol.shouldFail = true @@ -188,7 +188,7 @@ struct ETagCheckerTests { func changedWhenNoETagHeader() async throws { MockURLProtocol.reset() - let testURL = URL(string: "https://example.com/no-etag")! + let testURL = try #require(URL(string: "https://example.com/no-etag")) // Mock response without ETag header MockURLProtocol.mockResponses[testURL] = (200, [:]) @@ -209,7 +209,7 @@ struct ETagCheckerTests { func normalizesQuotedETags() async throws { MockURLProtocol.reset() - let testURL = URL(string: "https://example.com/normalize")! + let testURL = try #require(URL(string: "https://example.com/normalize")) let quotedETag = "\"abc123\"" let unquotedETag = "abc123" @@ -236,7 +236,7 @@ struct ETagCheckerTests { func caseInsensitiveETagHeader() async throws { MockURLProtocol.reset() - let testURL = URL(string: "https://example.com/case-test")! + let testURL = try #require(URL(string: "https://example.com/case-test")) let etagValue = "\"xyz789\"" // Mock with lowercase "etag" (not "ETag") @@ -262,7 +262,7 @@ struct ETagCheckerTests { func weakETagsSupported() async throws { MockURLProtocol.reset() - let testURL = URL(string: "https://example.com/weak-etag")! + let testURL = try #require(URL(string: "https://example.com/weak-etag")) let weakETag = "W/\"abc123\"" // Store weak ETag diff --git a/OpenHABCore/Tests/OpenHABCoreTests/EndpointTests.swift b/OpenHABCore/Tests/OpenHABCoreTests/EndpointTests.swift index 287c0a62f..81e1c9430 100644 --- a/OpenHABCore/Tests/OpenHABCoreTests/EndpointTests.swift +++ b/OpenHABCore/Tests/OpenHABCoreTests/EndpointTests.swift @@ -106,7 +106,7 @@ struct EndpointTests { } @Test - func emptyIconReturnsNilEndpoint() throws { + func emptyIconReturnsNilEndpoint() { let result = Endpoint.icon( rootUrl: "https://example.org", version: 3, @@ -233,7 +233,7 @@ struct EndpointTests { } @Test - func classicNumberIconReturnsNil() throws { + func classicNumberIconReturnsNil() { let result = Endpoint.icon( rootUrl: "https://example.org", version: 4, @@ -251,8 +251,8 @@ struct EndpointTests { // "oh:classic:light" to "icon/light?format=PNG&anyFormat=true&iconset=classic", // "oh:custom:light" to "icon/light?format=PNG&anyFormat=true&iconset=custom" - // Test no state is transmitted - // Test baseURL + /// Test no state is transmitted + /// Test baseURL @Test func materialIcon1() throws { let result = Endpoint.icon( diff --git a/OpenHABCore/Tests/OpenHABCoreTests/HTTPResponseExtension.swift b/OpenHABCore/Tests/OpenHABCoreTests/HTTPResponseExtension.swift index d59d86d1f..f790385e1 100644 --- a/OpenHABCore/Tests/OpenHABCoreTests/HTTPResponseExtension.swift +++ b/OpenHABCore/Tests/OpenHABCoreTests/HTTPResponseExtension.swift @@ -44,13 +44,19 @@ public enum TestError: Swift.Error, LocalizedError, CustomStringConvertible, Sen } /// A localized description of the error suitable for presenting to the user. - public var errorDescription: String? { description } + public var errorDescription: String? { + description + } } public extension Date { - static var test: Date { Date(timeIntervalSince1970: 1_674_036_251) } + static var test: Date { + Date(timeIntervalSince1970: 1_674_036_251) + } - static var testString: String { "2023-01-18T10:04:11Z" } + static var testString: String { + "2023-01-18T10:04:11Z" + } } public extension HTTPResponse { @@ -61,19 +67,31 @@ public extension HTTPResponse { } } - func withEncodedBody(_ encodedBody: String) throws -> (HTTPResponse, HTTPBody) { (self, .init(encodedBody)) } + func withEncodedBody(_ encodedBody: String) throws -> (HTTPResponse, HTTPBody) { + (self, .init(encodedBody)) + } } public extension Data { - static var abcdString: String { "abcd" } + static var abcdString: String { + "abcd" + } - static var abcd: Data { Data(abcdString.utf8) } + static var abcd: Data { + Data(abcdString.utf8) + } - static var efghString: String { "efgh" } + static var efghString: String { + "efgh" + } - static var quotedEfghString: String { #""efgh""# } + static var quotedEfghString: String { + #""efgh""# + } - static var efgh: Data { Data(efghString.utf8) } + static var efgh: Data { + Data(efghString.utf8) + } static let crlf: ArraySlice = [0xD, 0xA] @@ -107,7 +125,9 @@ public extension Data { return bytes } - static var multipartBody: Data { Data(multipartBodyAsSlice) } + static var multipartBody: Data { + Data(multipartBodyAsSlice) + } static var multipartTypedBodyAsSlice: [UInt8] { var bytes: [UInt8] = [] @@ -180,5 +200,7 @@ public extension Data { } public extension HTTPRequest { - func withEncodedBody(_ encodedBody: String) -> (HTTPRequest, HTTPBody) { (self, .init(encodedBody)) } + func withEncodedBody(_ encodedBody: String) -> (HTTPRequest, HTTPBody) { + (self, .init(encodedBody)) + } } diff --git a/OpenHABCore/Tests/OpenHABCoreTests/JSONParserTests.swift b/OpenHABCore/Tests/OpenHABCoreTests/JSONParserTests.swift index b2df69776..8aada29e9 100644 --- a/OpenHABCore/Tests/OpenHABCoreTests/JSONParserTests.swift +++ b/OpenHABCore/Tests/OpenHABCoreTests/JSONParserTests.swift @@ -10,7 +10,6 @@ // SPDX-License-Identifier: EPL-2.0 @testable import OpenHABCore - import os.signpost import Testing @@ -27,8 +26,8 @@ struct JSONParserTests { #expect(codingData[0].homepage?.link == "https://192.168.2.63:8444/rest/sitemaps/myHome/myHome") } - // Version 2.1 is without timeout - // Contributed by Tobi-1234 in #348 + /// Version 2.1 is without timeout + /// Contributed by Tobi-1234 in #348 @Test func jSONShortSitemapDecoder() throws { let json = """ [{"name":"Haus","label":"Hauptmenü","link":"http://192.xxxx:8080/rest/sitemaps/Haus","homepage":{"link":"http://192.xxx:8080/rest/sitemaps/Haus/Haus","leaf":false,"widgets":[]}},{"name":"_default","label":"Home","link":"http://192.Xxx:8080/rest/sitemaps/_default","homepage":{"link":"http://192.Xxxx:8080/rest/sitemaps/_default/_default","leaf":false,"widgets":[]}}] @@ -69,9 +68,11 @@ struct JSONParserTests { } @Test func watchSitemap() throws { + // swiftlint:disable line_length let json = Data(""" {"name":"watch","label":"watch","link":"https://192.168.2.15:8444/rest/sitemaps/watch","homepage":{"id":"watch","title":"watch","link":"https://192.168.2.15:8444/rest/sitemaps/watch/watch","leaf":false,"timeout":false,"widgets":[{"widgetId":"00","type":"Frame","label":"Ground floor","icon":"frame","mappings":[],"widgets":[{"widgetId":"0000","type":"Switch","label":"Licht Oberlicht","icon":"switch","mappings":[],"item":{"link":"https://192.168.2.15:8444/rest/items/lcnLightSwitch14_1","state":"OFF","editable":false,"type":"Switch","name":"lcnLightSwitch14_1","label":"Licht Oberlicht","tags":["Lighting"],"groupNames":["G_PresenceSimulation","gLcn"]},"widgets":[]},{"widgetId":"0001","type":"Switch","label":"Licht Keller WC Decke","icon":"colorpicker","mappings":[],"item":{"link":"https://192.168.2.15:8444/rest/items/lcnLightSwitch6_1","state":"OFF","editable":false,"type":"Switch","name":"lcnLightSwitch6_1","label":"Licht Keller WC Decke","category":"colorpicker","tags":["Lighting"],"groupNames":["gKellerLicht","gLcn"]},"widgets":[]}]}]}} """.utf8) + // swiftlint:enable line_length let codingData = try decoder.decode(Components.Schemas.SitemapDTO.self, from: json) #expect(codingData.homepage?.link == "https://192.168.2.15:8444/rest/sitemaps/watch/watch") } diff --git a/OpenHABCore/Tests/OpenHABCoreTests/NetworkTrackerTests.swift b/OpenHABCore/Tests/OpenHABCoreTests/NetworkTrackerTests.swift index 23d5fc33b..6c5ae6a8b 100644 --- a/OpenHABCore/Tests/OpenHABCoreTests/NetworkTrackerTests.swift +++ b/OpenHABCore/Tests/OpenHABCoreTests/NetworkTrackerTests.swift @@ -12,9 +12,8 @@ import Combine import Foundation import Network -import OSLog - @testable import OpenHABCore +import OSLog import Testing import XCTest diff --git a/OpenHABCore/Tests/OpenHABCoreTests/NumberStateTests.swift b/OpenHABCore/Tests/OpenHABCoreTests/NumberStateTests.swift index ff9766835..1dae1b767 100644 --- a/OpenHABCore/Tests/OpenHABCoreTests/NumberStateTests.swift +++ b/OpenHABCore/Tests/OpenHABCoreTests/NumberStateTests.swift @@ -10,7 +10,6 @@ // SPDX-License-Identifier: EPL-2.0 @testable import OpenHABCore - import Testing import UIKit @@ -179,7 +178,7 @@ struct NumberStateTests { } @Test("parseAs conversions") - func parseAs() { + func parseAs() throws { #expect("ON".parseAsBool() == true) #expect("4,3,1".parseAsBrightness() == 1) #expect("4,31".parseAsBrightness() == nil) @@ -196,7 +195,7 @@ struct NumberStateTests { let col1 = "Uninitialized".parseAsUIColor() let col2 = UIColor(hue: 0, saturation: 0, brightness: 0, alpha: 1.0) #expect(col1 != nil) - #expect(col1!.equals(col2)) + #expect(try #require(col1?.equals(col2))) #expect("360,100,100".parseAsUIColor() == UIColor( hue: CGFloat(state: "360", divisor: 360), saturation: CGFloat(state: "100", divisor: 100), diff --git a/OpenHABCore/Tests/OpenHABCoreTests/OpenAPIServiceSendItemCommandTests.swift b/OpenHABCore/Tests/OpenHABCoreTests/OpenAPIServiceSendItemCommandTests.swift index 6c56142c7..c820e98aa 100644 --- a/OpenHABCore/Tests/OpenHABCoreTests/OpenAPIServiceSendItemCommandTests.swift +++ b/OpenHABCore/Tests/OpenHABCoreTests/OpenAPIServiceSendItemCommandTests.swift @@ -15,7 +15,6 @@ import OpenAPIRuntime import OpenHABCore import Testing -@Suite struct OpenAPIServiceSendItemCommandTests { @Test("sendItemCommand uses text/plain for non-empty commands") func sendItemCommandUsesPlainTextForNonEmptyCommand() async throws { diff --git a/OpenHABCore/Tests/OpenHABCoreTests/OpenHABCoreGeneralTests.swift b/OpenHABCore/Tests/OpenHABCoreTests/OpenHABCoreGeneralTests.swift index a771a0839..aaf43d234 100644 --- a/OpenHABCore/Tests/OpenHABCoreTests/OpenHABCoreGeneralTests.swift +++ b/OpenHABCore/Tests/OpenHABCoreTests/OpenHABCoreGeneralTests.swift @@ -10,9 +10,7 @@ // SPDX-License-Identifier: EPL-2.0 import Foundation - @testable import OpenHABCore - import XCTest final class OpenHABCoreGeneralTests: XCTestCase { diff --git a/OpenHABCore/Tests/OpenHABCoreTests/OpenHABImageProcessorTests.swift b/OpenHABCore/Tests/OpenHABCoreTests/OpenHABImageProcessorTests.swift index ebaa562e3..caee40f52 100644 --- a/OpenHABCore/Tests/OpenHABCoreTests/OpenHABImageProcessorTests.swift +++ b/OpenHABCore/Tests/OpenHABCoreTests/OpenHABImageProcessorTests.swift @@ -19,15 +19,17 @@ struct OpenHABImageProcessorTests { @Test func preprocessSVG_withCurrentColor() throws { // SVG with currentColor fill attribute + // swiftlint:disable line_length let svgString = """ """ + // swiftlint:enable line_length let svgData = Data(svgString.utf8) // Process with red color let processor = OpenHABImageProcessor(iconColor: "red") let processedData = processor.preprocessSVG(svgData) - let processedString = String(bytes: processedData, encoding: .utf8)! + let processedString = try #require(String(bytes: processedData, encoding: .utf8)) // Verify that style attribute was added with both color and fill // 'color' is needed for currentColor references, 'fill' for elements without explicit fill @@ -47,7 +49,7 @@ struct OpenHABImageProcessorTests { // Process without icon color let processor = OpenHABImageProcessor(iconColor: nil) let processedData = processor.preprocessSVG(svgData) - let processedString = String(bytes: processedData, encoding: .utf8)! + let processedString = try #require(String(bytes: processedData, encoding: .utf8)) // Verify that SVG is unchanged #expect(processedString == svgString) @@ -62,7 +64,7 @@ struct OpenHABImageProcessorTests { // Process with empty icon color let processor = OpenHABImageProcessor(iconColor: "") let processedData = processor.preprocessSVG(svgData) - let processedString = String(bytes: processedData, encoding: .utf8)! + let processedString = try #require(String(bytes: processedData, encoding: .utf8)) // Verify that SVG is unchanged #expect(processedString == svgString) @@ -77,7 +79,7 @@ struct OpenHABImageProcessorTests { // Process with blue color let processor = OpenHABImageProcessor(iconColor: "blue") let processedData = processor.preprocessSVG(svgData) - let processedString = String(bytes: processedData, encoding: .utf8)! + let processedString = try #require(String(bytes: processedData, encoding: .utf8)) // Verify that color and fill were prepended to existing style #expect(processedString.contains("style=\"color:#")) @@ -94,7 +96,7 @@ struct OpenHABImageProcessorTests { // Process with green color let processor = OpenHABImageProcessor(iconColor: "green") let processedData = processor.preprocessSVG(svgData) - let processedString = String(bytes: processedData, encoding: .utf8)! + let processedString = try #require(String(bytes: processedData, encoding: .utf8)) // Verify that color and fill were prepended to existing style with single quotes preserved #expect(processedString.contains("style='color:#")) @@ -112,7 +114,7 @@ struct OpenHABImageProcessorTests { // Process with orange color let processor = OpenHABImageProcessor(iconColor: "orange") let processedData = processor.preprocessSVG(svgData) - let processedString = String(bytes: processedData, encoding: .utf8)! + let processedString = try #require(String(bytes: processedData, encoding: .utf8)) // Verify that style with whitespace is handled correctly #expect(processedString.contains("color:#")) @@ -129,7 +131,7 @@ struct OpenHABImageProcessorTests { // Process with purple color let processor = OpenHABImageProcessor(iconColor: "purple") let processedData = processor.preprocessSVG(svgData) - let processedString = String(bytes: processedData, encoding: .utf8)! + let processedString = try #require(String(bytes: processedData, encoding: .utf8)) // Verify that single-quoted style with whitespace is handled correctly #expect(processedString.contains("color:#")) @@ -147,7 +149,7 @@ struct OpenHABImageProcessorTests { // Process with hex color let processor = OpenHABImageProcessor(iconColor: "#FF5733") let processedData = processor.preprocessSVG(svgData) - let processedString = String(bytes: processedData, encoding: .utf8)! + let processedString = try #require(String(bytes: processedData, encoding: .utf8)) // Verify that style attribute was added #expect(processedString.contains("style=\"color:#")) @@ -163,7 +165,7 @@ struct OpenHABImageProcessorTests { // Process with RGB color let processor = OpenHABImageProcessor(iconColor: "rgb(255, 0, 0)") let processedData = processor.preprocessSVG(svgData) - let processedString = String(bytes: processedData, encoding: .utf8)! + let processedString = try #require(String(bytes: processedData, encoding: .utf8)) // Verify that style attribute was added #expect(processedString.contains("style=\"color:#")) @@ -179,7 +181,7 @@ struct OpenHABImageProcessorTests { // Process with color let processor = OpenHABImageProcessor(iconColor: "orange") let processedData = processor.preprocessSVG(svgData) - let processedString = String(bytes: processedData, encoding: .utf8)! + let processedString = try #require(String(bytes: processedData, encoding: .utf8)) // Verify that other attributes are preserved #expect(processedString.contains("width=\"64\"")) @@ -191,7 +193,7 @@ struct OpenHABImageProcessorTests { #expect(processedString.contains("fill:#")) } - @Test func preprocessSVG_withInvalidData() throws { + @Test func preprocessSVG_withInvalidData() { let invalidData = Data("Not an SVG".utf8) // Process with color @@ -216,14 +218,14 @@ struct OpenHABImageProcessorTests { // Process with color let processor = OpenHABImageProcessor(iconColor: "purple") let processedData = processor.preprocessSVG(svgData) - let processedString = String(bytes: processedData, encoding: .utf8)! + let processedString = try #require(String(bytes: processedData, encoding: .utf8)) // Verify that style attribute was added #expect(processedString.contains("style=\"color:#")) #expect(processedString.contains("fill:#")) } - @Test func emptySVG_selfClosingRoot_isDetectedAsEmpty() throws { + @Test func emptySVG_selfClosingRoot_isDetectedAsEmpty() { let svgString = """ @@ -234,7 +236,7 @@ struct OpenHABImageProcessorTests { #expect(processor.isEmptySVGDocument(data: svgData)) } - @Test func emptySVG_withOpenAndCloseTag_isDetectedAsEmpty() throws { + @Test func emptySVG_withOpenAndCloseTag_isDetectedAsEmpty() { let svgString = """ """ @@ -244,7 +246,7 @@ struct OpenHABImageProcessorTests { #expect(processor.isEmptySVGDocument(data: svgData)) } - @Test func nonEmptySVG_isNotDetectedAsEmpty() throws { + @Test func nonEmptySVG_isNotDetectedAsEmpty() { let svgString = """ """ @@ -254,7 +256,7 @@ struct OpenHABImageProcessorTests { #expect(!processor.isEmptySVGDocument(data: svgData)) } - @Test func process_emptySVG_returnsEmptyImageNotWarningSymbol() throws { + @Test func process_emptySVG_returnsEmptyImageNotWarningSymbol() { let svgString = """ @@ -278,7 +280,7 @@ struct OpenHABImageProcessorTests { let processor = OpenHABImageProcessor(iconColor: "red") let processedData = processor.preprocessSVG(svgData) - let processedString = String(bytes: processedData, encoding: .utf8)! + let processedString = try #require(String(bytes: processedData, encoding: .utf8)) // Red should convert to #FF0000 #expect(processedString.contains("color:#FF0000")) @@ -287,7 +289,7 @@ struct OpenHABImageProcessorTests { // MARK: - Cache Identifier Normalization Tests - @Test func cacheIdentifier_normalizesNamedColors() throws { + @Test func cacheIdentifier_normalizesNamedColors() { // Verify that named colors are normalized to hex for consistent cache identifiers let processor1 = OpenHABImageProcessor(iconColor: "red") let processor2 = OpenHABImageProcessor(iconColor: "#FF0000") @@ -301,7 +303,7 @@ struct OpenHABImageProcessorTests { #expect(processor1.identifier == "org.openhab.svgprocessor.FF0000") } - @Test func cacheIdentifier_handlesDifferentColors() throws { + @Test func cacheIdentifier_handlesDifferentColors() { // Verify that different colors produce different cache identifiers let redProcessor = OpenHABImageProcessor(iconColor: "red") let blueProcessor = OpenHABImageProcessor(iconColor: "blue") @@ -312,7 +314,7 @@ struct OpenHABImageProcessorTests { #expect(redProcessor.identifier != greenProcessor.identifier) } - @Test func cacheIdentifier_withoutColor() throws { + @Test func cacheIdentifier_withoutColor() { // Verify that processors without color have the default identifier let processor1 = OpenHABImageProcessor() let processor2 = OpenHABImageProcessor(iconColor: nil) @@ -323,7 +325,7 @@ struct OpenHABImageProcessorTests { #expect(processor3.identifier == "org.openhab.svgprocessor") } - @Test func cacheIdentifier_fullSizeMode() throws { + @Test func cacheIdentifier_fullSizeMode() { let processorWithoutColor = OpenHABImageProcessor(svgMaxSize: nil) let processorWithColor = OpenHABImageProcessor(iconColor: "red", svgMaxSize: nil) @@ -331,13 +333,13 @@ struct OpenHABImageProcessorTests { #expect(processorWithColor.identifier == "org.openhab.svgprocessor.FF0000.fullsize") } - @Test func cacheIdentifier_customSizeMode() throws { + @Test func cacheIdentifier_customSizeMode() { let processor = OpenHABImageProcessor(iconColor: "red", svgMaxSize: CGSize(width: 128, height: 128)) #expect(processor.identifier == "org.openhab.svgprocessor.FF0000.128x128") } - @Test func cacheIdentifier_normalizesOpenHABColors() throws { + @Test func cacheIdentifier_normalizesOpenHABColors() { // Verify that openHAB semantic colors are normalized let processor1 = OpenHABImageProcessor(iconColor: "maroon") let processor2 = OpenHABImageProcessor(iconColor: "#800000") @@ -346,7 +348,7 @@ struct OpenHABImageProcessorTests { #expect(processor1.identifier == "org.openhab.svgprocessor.800000") } - @Test func cacheIdentifier_handlesWhitespace() throws { + @Test func cacheIdentifier_handlesWhitespace() { // Verify that whitespace is handled correctly let processor1 = OpenHABImageProcessor(iconColor: " red ") let processor2 = OpenHABImageProcessor(iconColor: "red") @@ -356,7 +358,7 @@ struct OpenHABImageProcessorTests { #expect(processor2.identifier == processor3.identifier) } - @Test func cacheIdentifier_handlesCaseSensitivity() throws { + @Test func cacheIdentifier_handlesCaseSensitivity() { // Verify that color strings are case-insensitive let processor1 = OpenHABImageProcessor(iconColor: "RED") let processor2 = OpenHABImageProcessor(iconColor: "Red") @@ -366,7 +368,7 @@ struct OpenHABImageProcessorTests { #expect(processor2.identifier == processor3.identifier) } - @Test func cacheIdentifier_fallbackForInvalidColors() throws { + @Test func cacheIdentifier_fallbackForInvalidColors() { // Verify that invalid colors fall back to normalized string let processor = OpenHABImageProcessor(iconColor: "xyz") diff --git a/OpenHABCore/Tests/OpenHABCoreTests/OpenHABJSONParserTests.swift b/OpenHABCore/Tests/OpenHABCoreTests/OpenHABJSONParserTests.swift index 718ff5eaf..8a9224728 100644 --- a/OpenHABCore/Tests/OpenHABCoreTests/OpenHABJSONParserTests.swift +++ b/OpenHABCore/Tests/OpenHABCoreTests/OpenHABJSONParserTests.swift @@ -20,9 +20,11 @@ struct OpenHABJSONParserTests { } @Test func jSONNotificationDecoder() throws { + // swiftlint:disable line_length let json = Data(""" [{"_id":"5c82e14d2b48ecbf7a7223e0","message":"Light Küche was changed","__v":0,"created":"2019-03-08T21:40:29.412Z"},{"_id":"5c82c9c22b48ecbf7a70f44e","message":"Light Küche was changed","__v":0,"created":"2019-03-08T20:00:02.368Z"},{"_id":"5c82c9c02b48ecbf7a70f42c","message":"Light Küche was changed","__v":0,"created":"2019-03-08T20:00:00.982Z"},{"_id":"5c7fff782b48ecbf7a4dd8e4","message":"Light Küche was changed","__v":0,"created":"2019-03-06T17:12:24.093Z"},{"_id":"5c7ff5c12b48ecbf7a4d56fb","message":"Light Küche was changed","__v":0,"created":"2019-03-06T16:30:57.101Z"},{"_id":"5c7ed0852b48ecbf7a3d2151","message":"Light Küche was changed","__v":0,"created":"2019-03-05T19:39:49.373Z"},{"_id":"5c7d50ba2b48ecbf7a26948f","message":"Light Küche was changed","__v":0,"created":"2019-03-04T16:22:18.473Z"},{"_id":"5c7d50b62b48ecbf7a269455","message":"Light Küche was changed","__v":0,"created":"2019-03-04T16:22:14.321Z"},{"_id":"5c7d50b42b48ecbf7a269442","message":"Light Küche was changed","__v":0,"created":"2019-03-04T16:22:12.468Z"},{"_id":"5c7d507d2b48ecbf7a26916c","message":"Light Küche was changed","__v":0,"created":"2019-03-04T16:21:17.006Z"}] """.utf8) + // swiftlint:enable line_length let codingData = try decoder.decode([OpenHABNotification.CodingData].self, from: json) #expect(codingData[0].message == "Light Küche was changed") diff --git a/OpenHABCore/Tests/OpenHABCoreTests/OpenHABWidgetIconStateTests.swift b/OpenHABCore/Tests/OpenHABCoreTests/OpenHABWidgetIconStateTests.swift index c2129b3b8..303a566dc 100644 --- a/OpenHABCore/Tests/OpenHABCoreTests/OpenHABWidgetIconStateTests.swift +++ b/OpenHABCore/Tests/OpenHABCoreTests/OpenHABWidgetIconStateTests.swift @@ -13,7 +13,6 @@ import OpenHABCore import SwiftUI import Testing -@Suite struct OpenHABWidgetIconStateTests { // @Test // func returnsLowercasedState() { diff --git a/OpenHABCore/Tests/OpenHABCoreTests/ServerCertificateManagerTests.swift b/OpenHABCore/Tests/OpenHABCoreTests/ServerCertificateManagerTests.swift index 141d9f83a..964e4e36c 100644 --- a/OpenHABCore/Tests/OpenHABCoreTests/ServerCertificateManagerTests.swift +++ b/OpenHABCore/Tests/OpenHABCoreTests/ServerCertificateManagerTests.swift @@ -51,7 +51,7 @@ struct ServerCertificateManagerTests { return (manager, delegate) } - // Clear all certificates before each test to ensure isolation + /// Clear all certificates before each test to ensure isolation func clearCertificateStore() async { let store = CertificateManagers.certificateStore let allCerts = await store.getAllCertificates() @@ -78,7 +78,7 @@ struct ServerCertificateManagerTests { let (manager, _) = createTestContext() let trust = try dummyTrust() let domain = "accepted-test.openhab.org" // Unique domain - let cert = manager.getLeafCertificate(trust: trust)! + let cert = try #require(manager.getLeafCertificate(trust: trust)) let certData = SecCertificateCopyData(cert) as Data // Store certificate in the shared store diff --git a/OpenHABCore/Tests/OpenHABCoreTests/SessionTaskChallengeTests.swift b/OpenHABCore/Tests/OpenHABCoreTests/SessionTaskChallengeTests.swift index 589863f63..d339012ce 100644 --- a/OpenHABCore/Tests/OpenHABCoreTests/SessionTaskChallengeTests.swift +++ b/OpenHABCore/Tests/OpenHABCoreTests/SessionTaskChallengeTests.swift @@ -10,9 +10,8 @@ // SPDX-License-Identifier: EPL-2.0 import Foundation -import Testing - @testable import OpenHABCore +import Testing // MARK: - Test Helpers diff --git a/OpenHABCore/Tests/OpenHABCoreTests/StringExtensionTests.swift b/OpenHABCore/Tests/OpenHABCoreTests/StringExtensionTests.swift index cc62dc7a1..9cbab6ad7 100644 --- a/OpenHABCore/Tests/OpenHABCoreTests/StringExtensionTests.swift +++ b/OpenHABCore/Tests/OpenHABCoreTests/StringExtensionTests.swift @@ -13,7 +13,7 @@ import Testing struct StringExtensionTests { - @Test func testRemoveTrailingSlashes() throws { + @Test func testRemoveTrailingSlashes() { #expect("example/".removeTrailingSlashes() == "example") #expect("example//".removeTrailingSlashes() == "example") #expect("example/path//".removeTrailingSlashes() == "example/path") @@ -25,7 +25,7 @@ struct StringExtensionTests { } @Test - func testIsNoneIcon() throws { + func testIsNoneIcon() { let testCases: [String: Bool] = [ "none": true, "oh:none": true, @@ -41,7 +41,7 @@ struct StringExtensionTests { } @Test - func testDataImageBase64Payload() throws { + func testDataImageBase64Payload() { let svgPayload = "PHN2Zz48L3N2Zz4=" #expect("data:image/svg+xml;base64,\(svgPayload)".dataImageBase64Payload == svgPayload) @@ -55,7 +55,7 @@ struct StringExtensionTests { } @Test - func testDataImageBase64Data() throws { + func testDataImageBase64Data() { let validPayload = "PHN2Zz48L3N2Zz4=" #expect("data:image/svg+xml;base64,\(validPayload)".dataImageBase64Data == Data(base64Encoded: validPayload)) @@ -67,57 +67,57 @@ struct StringExtensionTests { // MARK: - doubleValue Tests - @Test func doubleValueWithSimpleInteger() throws { + @Test func doubleValueWithSimpleInteger() { let result = "42".doubleValue #expect(result == 42.0) } - @Test func doubleValueWithDecimal() throws { + @Test func doubleValueWithDecimal() { let result = "42.5".doubleValue #expect(result == 42.5) } - @Test func doubleValueWithNegative() throws { + @Test func doubleValueWithNegative() { let result = "-42.5".doubleValue #expect(result == -42.5) } - @Test func doubleValueWithZero() throws { + @Test func doubleValueWithZero() { let result = "0".doubleValue #expect(result == 0.0) } - @Test func doubleValueWithLeadingZeros() throws { + @Test func doubleValueWithLeadingZeros() { let result = "00042.5".doubleValue #expect(result == 42.5) } - @Test func doubleValueWithVerySmallNumber() throws { + @Test func doubleValueWithVerySmallNumber() { let result = "0.0001".doubleValue #expect(result == 0.0001) } - @Test func doubleValueWithVeryLargeNumber() throws { + @Test func doubleValueWithVeryLargeNumber() { let result = "123456789.123".doubleValue #expect(result == 123_456_789.123) } - @Test func doubleValueWithInvalidString() throws { + @Test func doubleValueWithInvalidString() { let result = "not a number".doubleValue #expect(result.isNaN) } - @Test func doubleValueWithEmptyString() throws { + @Test func doubleValueWithEmptyString() { let result = "".doubleValue #expect(result.isNaN) } - @Test func doubleValueUsesDecimalPoint() throws { + @Test func doubleValueUsesDecimalPoint() { let result = "42.5".doubleValue #expect(result == 42.5) } - @Test func doubleValueIgnoresCommaAsDecimalSeparator() throws { + @Test func doubleValueIgnoresCommaAsDecimalSeparator() { // Should parse up to the comma, not treat comma as decimal separator let result = "42,5".doubleValue // Parser stops at comma and returns 42.0, not 42.5 @@ -126,42 +126,42 @@ struct StringExtensionTests { // MARK: - intValue Tests - @Test func intValueWithSimpleInteger() throws { + @Test func intValueWithSimpleInteger() { let result = "42".intValue #expect(result == 42) } - @Test func intValueWithNegative() throws { + @Test func intValueWithNegative() { let result = "-42".intValue #expect(result == -42) } - @Test func intValueWithZero() throws { + @Test func intValueWithZero() { let result = "0".intValue #expect(result == 0) } - @Test func intValueWithLeadingZeros() throws { + @Test func intValueWithLeadingZeros() { let result = "00042".intValue #expect(result == 42) } - @Test func intValueWithLargeNumber() throws { + @Test func intValueWithLargeNumber() { let result = "123456789".intValue #expect(result == 123_456_789) } - @Test func intValueWithInvalidString() throws { + @Test func intValueWithInvalidString() { let result = "not a number".intValue #expect(result == Int.max) } - @Test func intValueWithEmptyString() throws { + @Test func intValueWithEmptyString() { let result = "".intValue #expect(result == Int.max) } - @Test func intValueWithDecimalString() throws { + @Test func intValueWithDecimalString() { // Parser stops at decimal point, returns integer part let result = "42.5".intValue #expect(result == 42) @@ -169,117 +169,117 @@ struct StringExtensionTests { // MARK: - numberValue Tests - @Test func numberValueWithSimpleInteger() throws { + @Test func numberValueWithSimpleInteger() { let result = "42".numberValue #expect(result?.doubleValue == 42.0) } - @Test func numberValueWithDecimal() throws { + @Test func numberValueWithDecimal() { let result = "42.5".numberValue #expect(result?.doubleValue == 42.5) } - @Test func numberValueWithNegative() throws { + @Test func numberValueWithNegative() { let result = "-42.5".numberValue #expect(result?.doubleValue == -42.5) } - @Test func numberValueWithScientificNotation() throws { + @Test func numberValueWithScientificNotation() { let result = "1.23E+2".numberValue #expect(result?.doubleValue == 123.0) } - @Test func numberValueWithNegativeExponent() throws { + @Test func numberValueWithNegativeExponent() { let result = "1.23E-2".numberValue #expect(result?.doubleValue == 0.0123) } - @Test func numberValueFiltersInvalidCharacters() throws { + @Test func numberValueFiltersInvalidCharacters() { let result = "42.5abc".numberValue #expect(result?.doubleValue == 42.5) } - @Test func numberValueWithExtraWhitespace() throws { + @Test func numberValueWithExtraWhitespace() { let result = " 42.5 ".numberValue #expect(result?.doubleValue == 42.5) } - @Test func numberValueWithInvalidString() throws { + @Test func numberValueWithInvalidString() { let result = "not a number".numberValue #expect(result == nil) } - @Test func numberValueWithEmptyString() throws { + @Test func numberValueWithEmptyString() { let result = "".numberValue #expect(result == nil) } - @Test func numberValueUsesDecimalPoint() throws { + @Test func numberValueUsesDecimalPoint() { let result = "42.5".numberValue #expect(result?.doubleValue == 42.5) } - @Test func numberValueWithZero() throws { + @Test func numberValueWithZero() { let result = "0".numberValue #expect(result?.doubleValue == 0.0) } - @Test func numberValueWithPositiveSign() throws { + @Test func numberValueWithPositiveSign() { let result = "+42.5".numberValue #expect(result?.doubleValue == 42.5) } - @Test func numberValueFiltersLetters() throws { + @Test func numberValueFiltersLetters() { // Should filter out letters but keep valid number characters // "1E2abc3" -> filters to "1E23" -> parses as 1 × 10^23 let result = "1E2abc3".numberValue #expect(result?.doubleValue == 1e+23) } - @Test func numberValueFiltersLettersSimple() throws { + @Test func numberValueFiltersLettersSimple() { // Simpler case: "42abc.5def" -> filters to "42.5" let result = "42abc.5def".numberValue #expect(result?.doubleValue == 42.5) } - @Test func numberValueWithUppercaseScientificNotation() throws { + @Test func numberValueWithUppercaseScientificNotation() { let result = "1.5E+10".numberValue #expect(result?.doubleValue == 15_000_000_000.0) } - @Test func numberValueWithScientificNotationNoSign() throws { + @Test func numberValueWithScientificNotationNoSign() { let result = "1.5E10".numberValue #expect(result?.doubleValue == 15_000_000_000.0) } - @Test func numberValueWithVerySmallScientificNumber() throws { + @Test func numberValueWithVerySmallScientificNumber() { let result = "1.0E-10".numberValue #expect(result?.doubleValue == 0.0000000001) } - @Test func numberValueWithZeroExponent() throws { + @Test func numberValueWithZeroExponent() { let result = "1.5E0".numberValue #expect(result?.doubleValue == 1.5) } // MARK: - asDouble Tests - @Test func asDoubleWithValidNumber() throws { + @Test func asDoubleWithValidNumber() { let result = "42.5".asDouble #expect(result == 42.5) } - @Test func asDoubleWithInvalidString() throws { + @Test func asDoubleWithInvalidString() { let result = "not a number".asDouble #expect(result == 0.0) } - @Test func asDoubleWithEmptyString() throws { + @Test func asDoubleWithEmptyString() { let result = "".asDouble #expect(result == 0.0) } - @Test func asDoubleWithScientificNotation() throws { + @Test func asDoubleWithScientificNotation() { let result = "1.23E+2".asDouble #expect(result == 123.0) } diff --git a/OpenHABCore/Tests/OpenHABCoreTests/TestClientTransport.swift b/OpenHABCore/Tests/OpenHABCoreTests/TestClientTransport.swift index b6370d2cc..f8a33bc01 100644 --- a/OpenHABCore/Tests/OpenHABCoreTests/TestClientTransport.swift +++ b/OpenHABCore/Tests/OpenHABCoreTests/TestClientTransport.swift @@ -54,7 +54,9 @@ public struct TestClientTransport: ClientTransport { /// Initializes a `TestClientTransport` instance with a custom call handler. /// /// - Parameter callHandler: The closure responsible for processing client requests. - public init(callHandler: @escaping CallHandler) { self.callHandler = callHandler } + public init(callHandler: @escaping CallHandler) { + self.callHandler = callHandler + } /// Sends a client request using the test transport. /// @@ -67,5 +69,7 @@ public struct TestClientTransport: ClientTransport { /// - Throws: An error if the call handler encounters an issue. public func send(_ request: HTTPRequest, body: HTTPBody?, baseURL: URL, operationID: String) async throws -> ( HTTPResponse, HTTPBody? - ) { try await callHandler(request, body, baseURL, operationID) } + ) { + try await callHandler(request, body, baseURL, operationID) + } } diff --git a/OpenHABCore/Tests/OpenHABCoreTests/UIColorExtensionTests.swift b/OpenHABCore/Tests/OpenHABCoreTests/UIColorExtensionTests.swift index bf5a2adb3..bb4f10487 100644 --- a/OpenHABCore/Tests/OpenHABCoreTests/UIColorExtensionTests.swift +++ b/OpenHABCore/Tests/OpenHABCoreTests/UIColorExtensionTests.swift @@ -16,7 +16,7 @@ import UIKit struct UIColorExtensionTests { // MARK: - semanticColorToHex() Tests - @Test func semanticColorToHex_RegularRGBColors() throws { + @Test func semanticColorToHex_RegularRGBColors() { // Test regular RGB colors created from hex values let redColor = UIColor(hex: "#FF0000") #expect(redColor.semanticColorToHex() == "#FF0000") @@ -31,7 +31,7 @@ struct UIColorExtensionTests { #expect(customColor.semanticColorToHex() == "#A1B2C3") } - @Test func semanticColorToHex_GrayscaleColors() throws { + @Test func semanticColorToHex_GrayscaleColors() { // Test grayscale colors (black, white, gray) let blackColor = UIColor.black #expect(blackColor.semanticColorToHex() == "#000000") @@ -45,7 +45,7 @@ struct UIColorExtensionTests { #expect(grayHex?.hasPrefix("#") == true) } - @Test func semanticColorToHex_SemanticOHColors() throws { + @Test func semanticColorToHex_SemanticOHColors() { // Test openHAB semantic colors let ohBlackColor = UIColor.ohBlack let ohBlackHex = ohBlackColor.semanticColorToHex() @@ -67,7 +67,7 @@ struct UIColorExtensionTests { #expect(ohBlueColor.semanticColorToHex() == "#0000FF") } - @Test func semanticColorToHex_StandardColors() throws { + @Test func semanticColorToHex_StandardColors() { // Test standard UIColor colors let redColor = UIColor.red let redHex = redColor.semanticColorToHex() @@ -90,7 +90,7 @@ struct UIColorExtensionTests { #expect(yellowHex?.hasPrefix("#") == true) } - @Test func semanticColorToHex_CustomRGBColors() throws { + @Test func semanticColorToHex_CustomRGBColors() { // Test colors created with custom RGB values let customColor1 = UIColor(red: 0.5, green: 0.5, blue: 0.5, alpha: 1.0) let hex1 = customColor1.semanticColorToHex() @@ -111,7 +111,7 @@ struct UIColorExtensionTests { #expect(customColor4.semanticColorToHex() == "#FFFFFF") } - @Test func semanticColorToHex_AllOHNamedColors() throws { + @Test func semanticColorToHex_AllOHNamedColors() { // Test all openHAB named colors to ensure they convert successfully let colors: [(UIColor, String)] = [ (.ohMaroon, "#800000"), @@ -137,7 +137,7 @@ struct UIColorExtensionTests { } } - @Test func semanticColorToHex_EdgeCases() throws { + @Test func semanticColorToHex_EdgeCases() { // Test edge case colors with transparency let transparentColor = UIColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 0.5) let transparentHex = transparentColor.semanticColorToHex() @@ -156,7 +156,7 @@ struct UIColorExtensionTests { #expect(clearHex != nil) } - @Test func semanticColorToHex_HexFormat() throws { + @Test func semanticColorToHex_HexFormat() { // Verify that all returned hex strings start with '#' let testColors = [ UIColor.black, @@ -177,7 +177,7 @@ struct UIColorExtensionTests { // MARK: - toHex() Tests - @Test func toHex_BasicColors() throws { + @Test func toHex_BasicColors() { let redColor = UIColor(hex: "#FF0000") #expect(redColor.toHex() == "FF0000") @@ -188,7 +188,7 @@ struct UIColorExtensionTests { #expect(blueColor.toHex() == "0000FF") } - @Test func toHex_WithAlpha() throws { + @Test func toHex_WithAlpha() { let color = UIColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 0.5) let hexWithAlpha = color.toHex(alpha: true) #expect(hexWithAlpha != nil) @@ -197,7 +197,7 @@ struct UIColorExtensionTests { // MARK: - init(hex:) Tests - @Test func initFromHex_ValidHexStrings() throws { + @Test func initFromHex_ValidHexStrings() { let color1 = UIColor(hex: "#FF0000") #expect(color1.toHex() == "FF0000") @@ -208,7 +208,7 @@ struct UIColorExtensionTests { #expect(color3.toHex() == "0000FF") } - @Test func initFromHex_InvalidHexStrings() throws { + @Test func initFromHex_InvalidHexStrings() { // Test that invalid hex strings fall back to gray let invalidColor1 = UIColor(hex: "ABC") #expect(invalidColor1.toHex() == UIColor.gray.toHex()) @@ -219,7 +219,7 @@ struct UIColorExtensionTests { // MARK: - init(fromString:) Tests - @Test func initFromString_NamedColors() throws { + @Test func initFromString_NamedColors() { let redColor = UIColor(fromString: "red") #expect(redColor.semanticColorToHex() == "#FF0000") @@ -230,7 +230,7 @@ struct UIColorExtensionTests { #expect(maroonColor.semanticColorToHex() == "#800000") } - @Test func initFromString_HexStrings() throws { + @Test func initFromString_HexStrings() { let color1 = UIColor(fromString: "#FF0000") #expect(color1.semanticColorToHex() == "#FF0000") @@ -238,7 +238,7 @@ struct UIColorExtensionTests { #expect(color2.semanticColorToHex() == "#00FF00") } - @Test func initFromString_CaseInsensitive() throws { + @Test func initFromString_CaseInsensitive() { let color1 = UIColor(fromString: "RED") let color2 = UIColor(fromString: "red") let color3 = UIColor(fromString: "Red") @@ -247,7 +247,7 @@ struct UIColorExtensionTests { #expect(color2.semanticColorToHex() == color3.semanticColorToHex()) } - @Test func initFromString_WhitespaceHandling() throws { + @Test func initFromString_WhitespaceHandling() { let color1 = UIColor(fromString: " red ") #expect(color1.semanticColorToHex() == "#FF0000") @@ -255,7 +255,7 @@ struct UIColorExtensionTests { #expect(color2.semanticColorToHex() == "#FF0000") } - @Test func initFromString_InvalidString() throws { + @Test func initFromString_InvalidString() { // Test that short invalid strings fall back to gray let shortInvalidColor = UIColor(fromString: "xyz") #expect(shortInvalidColor.toHex() == UIColor.gray.toHex()) diff --git a/OpenHABCore/Tests/OpenHABCoreTests/UIColorTests.swift b/OpenHABCore/Tests/OpenHABCoreTests/UIColorTests.swift index 4312bdf4e..38149fcf2 100644 --- a/OpenHABCore/Tests/OpenHABCoreTests/UIColorTests.swift +++ b/OpenHABCore/Tests/OpenHABCoreTests/UIColorTests.swift @@ -13,7 +13,6 @@ import OpenHABCore import Testing import UIKit -@Suite struct UIColorTests { @Test func resolvesNamedColor_exactMatch() { diff --git a/OpenHABCore/Tests/OpenHABCoreTests/UserDefaultsTests.swift b/OpenHABCore/Tests/OpenHABCoreTests/UserDefaultsTests.swift index 7a3d104cd..a7c1a7c4f 100644 --- a/OpenHABCore/Tests/OpenHABCoreTests/UserDefaultsTests.swift +++ b/OpenHABCore/Tests/OpenHABCoreTests/UserDefaultsTests.swift @@ -16,9 +16,9 @@ import Testing @MainActor struct HomePreferencesDecodingTests { - // Simulates stored data from an older version: only id + homeName + minimal connection - // configs (missing supportsNotifications, username, password, and several HomePreferences - // fields added later). Verifies that decode succeeds and the stored homeName is preserved. + /// Simulates stored data from an older version: only id + homeName + minimal connection + /// configs (missing supportsNotifications, username, password, and several HomePreferences + /// fields added later). Verifies that decode succeeds and the stored homeName is preserved. @Test func legacyPayloadPreservesHomeName() throws { let json = """ { @@ -53,9 +53,9 @@ struct HomePreferencesDecodingTests { #expect(prefs.remoteConnectionConfig.supportsNotifications == true) } - // Verifies that a payload with only the required `id` key decodes successfully and - // all other fields fall back to their struct defaults, including homeName == "Home#1" - // and remote supportsNotifications == true. + /// Verifies that a payload with only the required `id` key decodes successfully and + /// all other fields fall back to their struct defaults, including homeName == "Home#1" + /// and remote supportsNotifications == true. @Test func minimalPayloadUsesDefaults() throws { let json = #"{"id":"550E8400-E29B-41D4-A716-446655440000"}"# let prefs = try JSONDecoder().decode(HomePreferences.self, from: Data(json.utf8)) @@ -66,8 +66,8 @@ struct HomePreferencesDecodingTests { #expect(prefs.remoteConnectionConfig == .remoteDefault) } - // Verifies that an explicit supportsNotifications: false in the remote config JSON is - // respected (not silently promoted to true by the role-aware default). + /// Verifies that an explicit supportsNotifications: false in the remote config JSON is + /// respected (not silently promoted to true by the role-aware default). @Test func explicitFalseNotificationsRespected() throws { let json = """ { @@ -85,7 +85,7 @@ struct HomePreferencesDecodingTests { #expect(prefs.remoteConnectionConfig.supportsNotifications == false) } - // Full round-trip: encode a HomePreferences, decode it, verify nothing is lost. + /// Full round-trip: encode a HomePreferences, decode it, verify nothing is lost. @Test func fullRoundTripPreservesAllFields() throws { let encoded = """ { @@ -139,12 +139,12 @@ struct HomePreferencesDecodingTests { } } -// .serialized prevents parallel test clones from racing on the shared group.org.openhab.app UserDefaults suite. +/// .serialized prevents parallel test clones from racing on the shared group.org.openhab.app UserDefaults suite. @Suite(.serialized) @MainActor struct UserDefaultsTests { @Test func consistency() throws { - let data = UserDefaults(suiteName: "group.org.openhab.app")! + let data = try #require(UserDefaults(suiteName: "group.org.openhab.app")) let defaultsName = try #require(Bundle.main.bundleIdentifier) data.removePersistentDomain(forName: defaultsName) @@ -190,6 +190,6 @@ struct UserDefaultsTests { homeWithoutCredentials.localConnectionConfig.password = "" homeWithoutCredentials.remoteConnectionConfig.username = "" homeWithoutCredentials.remoteConnectionConfig.password = "" - #expect(homeWithoutCredentials == (try? JSONDecoder().decode(HomePreferences.self, from: data.data(forKey: "currentHomePreferences")!))) + #expect(homeWithoutCredentials == (try? JSONDecoder().decode(HomePreferences.self, from: try #require(data.data(forKey: "currentHomePreferences"))))) } } diff --git a/OpenHABCore/Tests/OpenHABCoreTests/WidgetCommandDispatcherTests.swift b/OpenHABCore/Tests/OpenHABCoreTests/WidgetCommandDispatcherTests.swift index de6b71178..ffbc32f69 100644 --- a/OpenHABCore/Tests/OpenHABCoreTests/WidgetCommandDispatcherTests.swift +++ b/OpenHABCore/Tests/OpenHABCoreTests/WidgetCommandDispatcherTests.swift @@ -12,7 +12,6 @@ import OpenHABCore import Testing -@Suite @MainActor struct WidgetCommandDispatcherTests { @Test("Empty string command is dispatched for item name") diff --git a/OpenHABCore/Tests/OpenHABCoreTests/WidgetDisplayStateTests.swift b/OpenHABCore/Tests/OpenHABCoreTests/WidgetDisplayStateTests.swift index b2a44694e..7a2c58010 100644 --- a/OpenHABCore/Tests/OpenHABCoreTests/WidgetDisplayStateTests.swift +++ b/OpenHABCore/Tests/OpenHABCoreTests/WidgetDisplayStateTests.swift @@ -12,7 +12,6 @@ import OpenHABCore import Testing -@Suite struct WidgetDisplayStateTests { @Test func usesItemStateWhenWidgetStateIsEmpty() { @@ -81,7 +80,7 @@ struct WidgetDisplayStateTests { category: nil, options: nil ) - let widget = OpenHABWidget( + return OpenHABWidget( widgetId: "widget-id", label: label, icon: "switch", @@ -112,6 +111,5 @@ struct WidgetDisplayStateTests { forceAsItem: nil, labelSource: .sitemapDefinition ) - return widget } } diff --git a/OpenHABCore/Tests/OpenHABCoreTests/WidgetMediaImageDescriptorTests.swift b/OpenHABCore/Tests/OpenHABCoreTests/WidgetMediaImageDescriptorTests.swift index d3fe9709a..6d6880ba4 100644 --- a/OpenHABCore/Tests/OpenHABCoreTests/WidgetMediaImageDescriptorTests.swift +++ b/OpenHABCore/Tests/OpenHABCoreTests/WidgetMediaImageDescriptorTests.swift @@ -13,7 +13,6 @@ import Foundation import OpenHABCore import Testing -@Suite struct WidgetMediaImageDescriptorTests { @Test func chartDescriptorResolvesToChartLink() { diff --git a/OpenHABCore/Tests/OpenHABCoreTests/WidgetRenderingKindTests.swift b/OpenHABCore/Tests/OpenHABCoreTests/WidgetRenderingKindTests.swift index 40f96e4d7..e4a6629d8 100644 --- a/OpenHABCore/Tests/OpenHABCoreTests/WidgetRenderingKindTests.swift +++ b/OpenHABCore/Tests/OpenHABCoreTests/WidgetRenderingKindTests.swift @@ -12,7 +12,6 @@ import OpenHABCore import Testing -@Suite struct WidgetRenderingKindTests { @Test func switchWithMappingsUsesSegmentedKind() { diff --git a/OsLogRewriter/Sources/OsLogRewriterLib/OsLogRewriter.swift b/OsLogRewriter/Sources/OsLogRewriterLib/OsLogRewriter.swift index 37a5c2b1c..257e283a7 100644 --- a/OsLogRewriter/Sources/OsLogRewriterLib/OsLogRewriter.swift +++ b/OsLogRewriter/Sources/OsLogRewriterLib/OsLogRewriter.swift @@ -14,7 +14,7 @@ import SwiftParser import SwiftSyntax import SwiftSyntaxBuilder -// Helper class to recursively remove trivia from all syntax nodes +/// Helper class to recursively remove trivia from all syntax nodes final class TriviaRemover: SyntaxRewriter { override func visit(_ token: TokenSyntax) -> TokenSyntax { token.with(\.leadingTrivia, []).with(\.trailingTrivia, []) @@ -105,13 +105,13 @@ final class OsLogRewriter: SyntaxRewriter { ) } - // Helper function to recursively clean all trivia from a syntax node + /// Helper function to recursively clean all trivia from a syntax node func cleanAllTrivia(from expr: ExprSyntax) -> ExprSyntax { let triviaRemover = TriviaRemover() return ExprSyntax(triviaRemover.visit(expr)) } - // Helper function to extract indentation from trivia + /// Helper function to extract indentation from trivia func extractIndentation(from trivia: Trivia) -> Trivia { // Look for the last newline and extract spaces/tabs after it var indentationPieces: [TriviaPiece] = [] @@ -144,7 +144,7 @@ final class OsLogRewriter: SyntaxRewriter { return Trivia(pieces: indentationPieces.reversed()) } - // Helper function to detect indentation from the context of the os_log call + /// Helper function to detect indentation from the context of the os_log call func detectIndentationLevel(_ node: FunctionCallExprSyntax) -> Trivia { // First try to extract from the node's own leading trivia let directIndentation = extractIndentation(from: node.leadingTrivia) @@ -157,7 +157,7 @@ final class OsLogRewriter: SyntaxRewriter { return findContextualIndentation(node) ?? Trivia.spaces(8) // fallback } - // Helper function to find indentation from parent context + /// Helper function to find indentation from parent context func findContextualIndentation(_ node: some SyntaxProtocol) -> Trivia? { var current: (any SyntaxProtocol)? = node @@ -174,13 +174,13 @@ final class OsLogRewriter: SyntaxRewriter { return nil } - // Helper function to extract meaningful indentation (non-empty) + /// Helper function to extract meaningful indentation (non-empty) func extractMeaningfulIndentation(from trivia: Trivia) -> Trivia? { let extracted = extractIndentation(from: trivia) return extracted.isEmpty ? nil : extracted } - // Helper function to simplify string interpolations like "\(value)" to just value + /// Helper function to simplify string interpolations like "\(value)" to just value func simplifyStringInterpolation(_ expr: ExprSyntax) -> ExprSyntax { // Check if this is a string literal guard let stringLiteral = expr.as(StringLiteralExprSyntax.self) else { @@ -289,7 +289,7 @@ final class OsLogRewriter: SyntaxRewriter { } } -// Public API for the library +/// Public API for the library public func rewriteOsLogCalls(in sourceCode: String) throws -> String { let syntaxTree = Parser.parse(source: sourceCode) let rewriter = OsLogRewriter() diff --git a/openHAB.xcworkspace/xcshareddata/swiftpm/Package.resolved b/openHAB.xcworkspace/xcshareddata/swiftpm/Package.resolved index 87269bbca..3144b70a6 100644 --- a/openHAB.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/openHAB.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "38e24fd8c6bebb97ff99908ff7a1ec5f95cb577960130e58c7cfb021b32ae037", + "originHash" : "ce87d13cdc46b65a26be6c5d8b5ee53a01cd0be7cf67b2a5fcbc83f121924b1f", "pins" : [ { "identity" : "abseil-cpp-binary", @@ -249,8 +249,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/weakfl/SwiftFormatPlugin", "state" : { - "revision" : "c41997c642ffc937c6e83b23dadbd7e626485cfa", - "version" : "0.58.7" + "revision" : "c7c81920d715e051306d2172083c14aaec2c1b9d", + "version" : "0.61.1" } }, { @@ -258,8 +258,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/weakfl/SwiftLintPlugin.git", "state" : { - "revision" : "47d1da3a8e30e0ada5543899e8c47f54664073ac", - "version" : "0.62.2" + "revision" : "3741567a0252090897336f1f39b8490107b49ead", + "version" : "0.63.3" } }, { diff --git a/openHAB/AppDelegate.swift b/openHAB/AppDelegate.swift index 67f6b298c..1f766b33f 100644 --- a/openHAB/AppDelegate.swift +++ b/openHAB/AppDelegate.swift @@ -32,7 +32,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { private let notificationDelegate = NotificationCenterDelegateImpl() - // Delegate Requests from the Watch to the WatchMessageService + /// Delegate Requests from the Watch to the WatchMessageService var session: WCSession? { didSet { if let session { @@ -99,7 +99,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { configureImageCoders() - /// load and start the screensaver + // load and start the screensaver if let keyWindow = UIApplication.shared.firstKeyWindow { var config = ScreenSaverConfiguration() config.isEnabled = Preferences.shared.screensaverEnabled @@ -194,7 +194,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { return true } - // This is only informational - on success - DID Register + /// This is only informational - on success - DID Register func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { // Do nothing now, we are using FCM } diff --git a/openHAB/Models/SitemapPageViewModel+SupportTypes.swift b/openHAB/Models/SitemapPageViewModel+SupportTypes.swift index 975ad19a8..a622a8241 100644 --- a/openHAB/Models/SitemapPageViewModel+SupportTypes.swift +++ b/openHAB/Models/SitemapPageViewModel+SupportTypes.swift @@ -68,7 +68,7 @@ struct QueuedCommand { } // swiftlint:disable:next file_types_order -struct WidgetRenderKey: Equatable, Sendable { +struct WidgetRenderKey: Equatable { let label: String let icon: String let state: String @@ -148,50 +148,9 @@ struct WidgetRenderKey: Equatable, Sendable { childWidgets: widget.widgets.map(WidgetRenderKey.from) ) } - - // Keep this manual to avoid slow compile times for a very large synthesized implementation. - static func == (lhs: WidgetRenderKey, rhs: WidgetRenderKey) -> Bool { - lhs.label == rhs.label && - lhs.icon == rhs.icon && - lhs.state == rhs.state && - lhs.iconColor == rhs.iconColor && - lhs.labelColor == rhs.labelColor && - lhs.valueColor == rhs.valueColor && - lhs.url == rhs.url && - lhs.period == rhs.period && - lhs.service == rhs.service && - lhs.legend == rhs.legend && - lhs.refresh == rhs.refresh && - lhs.height == rhs.height && - lhs.forceAsItem == rhs.forceAsItem && - lhs.visibility == rhs.visibility && - lhs.staticIcon == rhs.staticIcon && - lhs.switchSupport == rhs.switchSupport && - lhs.releaseOnly == rhs.releaseOnly && - lhs.minValue == rhs.minValue && - lhs.maxValue == rhs.maxValue && - lhs.step == rhs.step && - lhs.pattern == rhs.pattern && - lhs.unit == rhs.unit && - lhs.type == rhs.type && - lhs.inputHintRawValue == rhs.inputHintRawValue && - lhs.encoding == rhs.encoding && - lhs.labelSourceRawValue == rhs.labelSourceRawValue && - lhs.yAxisDecimalPattern == rhs.yAxisDecimalPattern && - lhs.row == rhs.row && - lhs.column == rhs.column && - lhs.releaseCommand == rhs.releaseCommand && - lhs.command == rhs.command && - lhs.stateless == rhs.stateless && - lhs.linkedPageLink == rhs.linkedPageLink && - lhs.linkedPageTitle == rhs.linkedPageTitle && - lhs.mappings == rhs.mappings && - lhs.item == rhs.item && - lhs.childWidgets == rhs.childWidgets - } } -struct WidgetMappingKey: Equatable, Sendable { +struct WidgetMappingKey: Equatable { let command: String let label: String let row: Int? @@ -209,7 +168,7 @@ struct WidgetMappingKey: Equatable, Sendable { } } -struct WidgetItemKey: Equatable, Sendable { +struct WidgetItemKey: Equatable { let name: String let state: String? let link: String @@ -234,7 +193,7 @@ struct WidgetItemKey: Equatable, Sendable { } } -struct WidgetStateDescriptionKey: Equatable, Sendable { +struct WidgetStateDescriptionKey: Equatable { let minimum: Double let maximum: Double let step: Double @@ -255,7 +214,7 @@ struct WidgetStateDescriptionKey: Equatable, Sendable { } } -struct WidgetOptionKey: Equatable, Sendable { +struct WidgetOptionKey: Equatable { let value: String let label: String @@ -265,7 +224,7 @@ struct WidgetOptionKey: Equatable, Sendable { } } -struct WidgetCommandOptionKey: Equatable, Sendable { +struct WidgetCommandOptionKey: Equatable { let command: String let label: String? diff --git a/openHAB/Models/SitemapPageViewModel.swift b/openHAB/Models/SitemapPageViewModel.swift index 5242198ca..de6ef4af1 100644 --- a/openHAB/Models/SitemapPageViewModel.swift +++ b/openHAB/Models/SitemapPageViewModel.swift @@ -364,11 +364,9 @@ extension SitemapPageViewModel { } } - private func runPageHandling( - runID: UUID, - recreateService: Bool, - pipelineStart: Date - ) async { + private func runPageHandling(runID: UUID, + recreateService: Bool, + pipelineStart: Date) async { defer { if activePageHandlingID == runID { pageHandlingTask = nil diff --git a/openHAB/Models/WidgetMappingSnapshot.swift b/openHAB/Models/WidgetMappingSnapshot.swift index 137a1b840..a1d694fb4 100644 --- a/openHAB/Models/WidgetMappingSnapshot.swift +++ b/openHAB/Models/WidgetMappingSnapshot.swift @@ -12,6 +12,7 @@ import Foundation import OpenHABCore +// swiftformat:disable:next redundantSendable struct SnapshotRowInputBuildResult: Sendable { let inputs: [SitemapRowInput] let rowIDs: [RowID] @@ -19,7 +20,7 @@ struct SnapshotRowInputBuildResult: Sendable { let reusedInputCount: Int } -struct WidgetLinkedPageSnapshot: Sendable { +struct WidgetLinkedPageSnapshot { let link: String let title: String } @@ -96,7 +97,7 @@ enum SitemapRowInputSnapshotBuilder { } } -struct WidgetMappingSnapshot: Sendable { +struct WidgetMappingSnapshot { let widgetId: String let label: String let icon: String @@ -255,7 +256,9 @@ extension WidgetMappingSnapshot { OpenHABWidget.LabelSource(rawValue: labelSourceRawValue) ?? .unknown } - var hasLinkedPage: Bool { linkedPage != nil } + var hasLinkedPage: Bool { + linkedPage != nil + } var mediaImageDescriptor: WidgetMediaImageDescriptor { let itemPayload: WidgetMediaItemPayloadSnapshot = if let item { diff --git a/openHAB/NotificationCenterDelegateImpl.swift b/openHAB/NotificationCenterDelegateImpl.swift index 113aa9a8d..af234afdf 100644 --- a/openHAB/NotificationCenterDelegateImpl.swift +++ b/openHAB/NotificationCenterDelegateImpl.swift @@ -22,11 +22,11 @@ import UIKit @preconcurrency import UserNotifications import WatchConnectivity -// AVAudioPlayer must be created, used, and deallocated on the main thread. -// Using a plain `actor` placed it on a background executor, causing a crash when -// AVFoundation delivered finishedPlaying: on the main thread while the old player -// was simultaneously being deallocated on the actor's background thread (mutex contention -// in AVAudioPlayerCpp::DoAction / disposeQueue). @MainActor pins the entire lifecycle. +/// AVAudioPlayer must be created, used, and deallocated on the main thread. +/// Using a plain `actor` placed it on a background executor, causing a crash when +/// AVFoundation delivered finishedPlaying: on the main thread while the old player +/// was simultaneously being deallocated on the actor's background thread (mutex contention +/// in AVAudioPlayerCpp::DoAction / disposeQueue). @MainActor pins the entire lifecycle. @MainActor final class AudioPlayerActor { private var player: AVAudioPlayer? @@ -55,7 +55,7 @@ final class AudioPlayerActor { final class NotificationCenterDelegateImpl: NSObject, UNUserNotificationCenterDelegate { let audioPlayer = AudioPlayerActor() - // this is called when a notification comes in while in the foreground + /// this is called when a notification comes in while in the foreground func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification) async -> UNNotificationPresentationOptions { let userInfo = notification.request.content.userInfo Logger.notificationCenterDelegateImpl.info("Notification received while app is in foreground: \(userInfo)") @@ -150,7 +150,7 @@ final class NotificationCenterDelegateImpl: NSObject, UNUserNotificationCenterDe SwiftMessages.hideAll() } - // ✅ Ensure this runs on the MainActor + /// ✅ Ensure this runs on the MainActor @MainActor func notifyNotificationListeners(action: String?, cloudUserId: String? = nil) { // Wake up screen saver immediately on incoming notification interaction diff --git a/openHAB/UI/DrawerView.swift b/openHAB/UI/DrawerView.swift index a5753c4dd..6f2ffdf9f 100644 --- a/openHAB/UI/DrawerView.swift +++ b/openHAB/UI/DrawerView.swift @@ -64,9 +64,7 @@ enum DrawerFetch { } } - static func uiTiles( - fetch: () async throws -> [OpenHABUiTile] - ) async -> DrawerFetchResult<[OpenHABUiTile]> { + static func uiTiles(fetch: () async throws -> [OpenHABUiTile]) async -> DrawerFetchResult<[OpenHABUiTile]> { do { let uiTiles = try await fetch() try Task.checkCancellation() @@ -79,7 +77,7 @@ enum DrawerFetch { } } -// Display the active home name and connection type (Local / Remote) +/// Display the active home name and connection type (Local / Remote) struct ConnectionView: View { @EnvironmentObject private var networkTracker: MainActorNetworkTracker @ScaledMetric private var iconWidth = 20.0 diff --git a/openHAB/UI/HostingSitemapViewController.swift b/openHAB/UI/HostingSitemapViewController.swift index 6145cc33f..95e7f509d 100644 --- a/openHAB/UI/HostingSitemapViewController.swift +++ b/openHAB/UI/HostingSitemapViewController.swift @@ -59,7 +59,9 @@ class HostingSitemapViewController: UIHostingController, viewModel.pageTitle } - func viewName() -> String { "sitemap" } + func viewName() -> String { + "sitemap" + } nonisolated func reloadView() { Task { @MainActor in diff --git a/openHAB/UI/NotificationsView.swift b/openHAB/UI/NotificationsView.swift index 14b8cc9cd..143401b07 100644 --- a/openHAB/UI/NotificationsView.swift +++ b/openHAB/UI/NotificationsView.swift @@ -111,7 +111,7 @@ protocol NetworkTracking { var activeConnection: ConnectionInfo? { get } } -struct NotificationsView: View where Tracker: ObservableObject { +struct NotificationsView: View { @ObservedObject var networkTracker: Tracker @State var notifications: [OpenHABNotification] = [] let loadNotifications: NotificationLoader diff --git a/openHAB/UI/OpenHABNavigationController.swift b/openHAB/UI/OpenHABNavigationController.swift index 0e07a83f4..0a7f25ea3 100644 --- a/openHAB/UI/OpenHABNavigationController.swift +++ b/openHAB/UI/OpenHABNavigationController.swift @@ -26,11 +26,15 @@ import UIKit /// This is a wrapper around UINavigationController that allows the status bar to be hidden or shown. /// It is used to control the status bar for the entire app and is loaded from the Main storyboard entry point. class OpenHABNavigationController: UINavigationController { - override var childForStatusBarHidden: UIViewController? { nil } + override var childForStatusBarHidden: UIViewController? { + nil + } override var prefersStatusBarHidden: Bool { Preferences.shared.hideStatusBar } - override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation { .fade } + override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation { + .fade + } } diff --git a/openHAB/UI/OpenHABRootViewController.swift b/openHAB/UI/OpenHABRootViewController.swift index e8bcbd055..328ebe687 100644 --- a/openHAB/UI/OpenHABRootViewController.swift +++ b/openHAB/UI/OpenHABRootViewController.swift @@ -94,8 +94,7 @@ class OpenHABRootViewController: UIViewController { private lazy var webViewController: OpenHABWebViewController = { let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main) - var viewController = storyboard.instantiateViewController(withIdentifier: "OpenHABWebViewController") as! OpenHABWebViewController - return viewController + return storyboard.instantiateViewController(withIdentifier: "OpenHABWebViewController") as! OpenHABWebViewController }() lazy var sitemapViewController: any (UIViewController & OpenHABViewable) = { @@ -632,7 +631,8 @@ class OpenHABRootViewController: UIViewController { Preferences.shared.switchActiveHome(to: targetHome.id) } // if the app was woken from a fully stopped state, network tracking might not be active yet - await NetworkTracker.shared.startTracking(connectionConfigurations: + await NetworkTracker.shared.startTracking( + connectionConfigurations: [ Preferences.shared.currentHomePreferences.localConnectionConfig, Preferences.shared.currentHomePreferences.remoteConnectionConfig @@ -668,7 +668,7 @@ class OpenHABRootViewController: UIViewController { } } - // Helper function to safely call the completion handler on the main thread + /// Helper function to safely call the completion handler on the main thread private func callCompletionHandler(_ completionHandler: (() -> Void)?) { if let completionHandler { DispatchQueue.main.async { diff --git a/openHAB/UI/OpenHABViewController.swift b/openHAB/UI/OpenHABViewController.swift index 1617a67c1..17a362c30 100644 --- a/openHAB/UI/OpenHABViewController.swift +++ b/openHAB/UI/OpenHABViewController.swift @@ -111,7 +111,7 @@ class OpenHABViewController: UIViewController, OpenHABViewable { // MARK: - ServerCertificateManagerDelegate extension OpenHABViewController: ServerCertificateManagerDelegate { - // delegate should ask user for a decision on what to do with invalid certificate + /// delegate should ask user for a decision on what to do with invalid certificate @MainActor func evaluateServerTrust(summary certificateSummary: String?, forDomain domain: String?) async -> ServerCertificateManager.EvaluateResult { await withCheckedContinuation { continuation in @@ -133,7 +133,7 @@ extension OpenHABViewController: ServerCertificateManagerDelegate { } } - // certificate received from openHAB doesn't match our record, ask user for a decision + /// certificate received from openHAB doesn't match our record, ask user for a decision @MainActor func evaluateCertificateMismatch(summary certificateSummary: String?, forDomain domain: String?) async -> OpenHABCore.ServerCertificateManager.EvaluateResult { await withCheckedContinuation { continuation in @@ -168,7 +168,7 @@ extension OpenHABViewController: ServerCertificateManagerDelegate { @MainActor extension OpenHABViewController: ClientCertificateManagerDelegate { - // Ask user whether to import the certificate + /// Ask user whether to import the certificate func askForClientCertificateImport(_ clientCertificateManager: ClientCertificateManager?) async -> Bool { let shouldImport = await withCheckedContinuation { continuation in let alertController = UIAlertController( @@ -198,7 +198,7 @@ extension OpenHABViewController: ClientCertificateManagerDelegate { return false } - // Ask user for password to decode PKCS#12 + /// Ask user for password to decode PKCS#12 func askForCertificatePassword(_ clientCertificateManager: ClientCertificateManager?) async -> String? { await withCheckedContinuation { continuation in let alertController = UIAlertController( diff --git a/openHAB/UI/OpenHABWebViewController.swift b/openHAB/UI/OpenHABWebViewController.swift index 12d592a76..dd47e4695 100644 --- a/openHAB/UI/OpenHABWebViewController.swift +++ b/openHAB/UI/OpenHABWebViewController.swift @@ -22,7 +22,10 @@ class OpenHABWebViewController: OpenHABViewController { private var currentTarget = "" private var openHABTrackedRootUrl = "" private var activeConnectionInfo: ConnectionInfo? - private var activeConfig: ConnectionConfiguration? { activeConnectionInfo?.configuration } + private var activeConfig: ConnectionConfiguration? { + activeConnectionInfo?.configuration + } + private var hideNavigationBar = false private var activityIndicator: UIActivityIndicatorView! private var sseTimer: Timer? @@ -344,7 +347,7 @@ class OpenHABWebViewController: OpenHABViewController { return url } - // swift really makes you work to construct simple URLs, uhg..... + /// swift really makes you work to construct simple URLs, uhg..... func appendPathToURL(baseURL: URL, path: String) -> URL? { guard var urlComponents = URLComponents(url: baseURL, resolvingAgainstBaseURL: false) else { return nil diff --git a/openHAB/UI/ScreenSaver/ScreenSaverManager.swift b/openHAB/UI/ScreenSaver/ScreenSaverManager.swift index 2705ffcd3..644e961de 100644 --- a/openHAB/UI/ScreenSaver/ScreenSaverManager.swift +++ b/openHAB/UI/ScreenSaver/ScreenSaverManager.swift @@ -15,8 +15,13 @@ import SwiftUI import UIKit private class ScreenSaverHostingViewController: UIViewController { - override var prefersStatusBarHidden: Bool { true } - override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation { .fade } + override var prefersStatusBarHidden: Bool { + true + } + + override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation { + .fade + } } @MainActor diff --git a/openHAB/UI/SettingsView/ItemSelectionView.swift b/openHAB/UI/SettingsView/ItemSelectionView.swift index 1928f09f6..3f97b3634 100644 --- a/openHAB/UI/SettingsView/ItemSelectionView.swift +++ b/openHAB/UI/SettingsView/ItemSelectionView.swift @@ -75,7 +75,6 @@ struct ItemSelectionView: View { } } - @ViewBuilder private func itemRow(_ item: OpenHABItem) -> some View { Button { selectedItemName = (selectedItemName == item.name) ? nil : item.name diff --git a/openHAB/UI/SettingsView/ScreenSaverSettingsView.swift b/openHAB/UI/SettingsView/ScreenSaverSettingsView.swift index 3e4bbe14a..f2dfbc1c0 100644 --- a/openHAB/UI/SettingsView/ScreenSaverSettingsView.swift +++ b/openHAB/UI/SettingsView/ScreenSaverSettingsView.swift @@ -117,14 +117,12 @@ struct ScreenSaverSettingsView: View { ) } - @ViewBuilder private var enableSection: some View { Section { Toggle("Enable Screen Saver", isOn: $config.isEnabled) } } - @ViewBuilder private var appearanceSection: some View { Section("Appearance") { Toggle("Show Time", isOn: $config.showsTime) @@ -140,7 +138,6 @@ struct ScreenSaverSettingsView: View { .disabled(!config.isEnabled) } - @ViewBuilder private var timingSection: some View { Section("Timing") { Stepper(value: idleIntervalBinding, in: 5 ... 600, step: 5) { @@ -154,7 +151,6 @@ struct ScreenSaverSettingsView: View { .disabled(!config.isEnabled) } - @ViewBuilder private var fontSection: some View { Section("Font Size") { VStack(alignment: .leading) { @@ -174,7 +170,6 @@ struct ScreenSaverSettingsView: View { .disabled(!config.isEnabled) } - @ViewBuilder private var animationSection: some View { Section("Animation") { VStack(alignment: .leading) { @@ -187,7 +182,6 @@ struct ScreenSaverSettingsView: View { .disabled(!config.isEnabled) } - @ViewBuilder private var brightnessSection: some View { Section("Brightness") { Toggle("Enable Dimming", isOn: $config.enablesAutoDimming) @@ -214,7 +208,6 @@ struct ScreenSaverSettingsView: View { .disabled(!config.isEnabled) } - @ViewBuilder private var testSection: some View { Section { Button("Test Screen Saver") { diff --git a/openHAB/UI/SettingsView/SitemapSettingsView.swift b/openHAB/UI/SettingsView/SitemapSettingsView.swift index fac8e1fd7..1c9ec96b7 100644 --- a/openHAB/UI/SettingsView/SitemapSettingsView.swift +++ b/openHAB/UI/SettingsView/SitemapSettingsView.swift @@ -36,21 +36,18 @@ struct SitemapSettingsView: View { } } - @ViewBuilder private var realtimeSliderToggle: some View { Toggle(isOn: $settingsRealTimeSliders) { Text("Real-time Sliders") } } - @ViewBuilder private var searchFieldToggle: some View { Toggle(isOn: $settingsShowSearchField) { Text("Show Search Field") } } - @ViewBuilder private var cacheButton: some View { Button { KingfisherManager.shared.cache.calculateDiskStorageSize { result in @@ -72,7 +69,6 @@ struct SitemapSettingsView: View { ) } - @ViewBuilder private var iconTypePicker: some View { Picker(selection: $settingsIconType) { ForEach(IconType.allCases, id: \.self) { icontype in @@ -83,7 +79,6 @@ struct SitemapSettingsView: View { } } - @ViewBuilder private var sortOrderPicker: some View { Picker(selection: $settingsSortSitemapsBy) { ForEach(SortSitemapsOrder.allCases, id: \.self) { sortsitemaporder in @@ -94,7 +89,6 @@ struct SitemapSettingsView: View { } } - @ViewBuilder private var watchSitemapPicker: some View { Picker("Sitemap for Apple Watch", selection: $settingsSitemapForWatch) { if sitemaps.isEmpty { diff --git a/openHAB/UI/SwiftUI/EmbeddingRowInputView.swift b/openHAB/UI/SwiftUI/EmbeddingRowInputView.swift index 9de0f0873..cc5cbf128 100644 --- a/openHAB/UI/SwiftUI/EmbeddingRowInputView.swift +++ b/openHAB/UI/SwiftUI/EmbeddingRowInputView.swift @@ -122,9 +122,9 @@ private struct LinkedPageRowContent: View { struct EmbeddingRowInputView: View, Equatable { let rowInput: SitemapRowInput - // Subscribes to the view model so SwiftUI re-evaluates this view when - // rowInputs change — without this, rows that scroll into view after a - // foreground refresh may render stale data from a cached render. + /// Subscribes to the view model so SwiftUI re-evaluates this view when + /// rowInputs change — without this, rows that scroll into view after a + /// foreground refresh may render stale data from a cached render. @EnvironmentObject private var viewModel: SitemapPageViewModel var body: some View { diff --git a/openHAB/UI/SwiftUI/ImageView.swift b/openHAB/UI/SwiftUI/ImageView.swift index ea454e117..8631ef896 100644 --- a/openHAB/UI/SwiftUI/ImageView.swift +++ b/openHAB/UI/SwiftUI/ImageView.swift @@ -23,7 +23,6 @@ struct ImageView: View { @EnvironmentObject var networkTracker: MainActorNetworkTracker - @ViewBuilder var body: some View { if !url.isEmpty { switch url { diff --git a/openHAB/UI/SwiftUI/Rows/ColorTemperaturePickerRowView.swift b/openHAB/UI/SwiftUI/Rows/ColorTemperaturePickerRowView.swift index 93ef1712f..618a8be36 100644 --- a/openHAB/UI/SwiftUI/Rows/ColorTemperaturePickerRowView.swift +++ b/openHAB/UI/SwiftUI/Rows/ColorTemperaturePickerRowView.swift @@ -276,7 +276,7 @@ private struct ColorTemperaturePickerRowContent: View { } } - // Generate gradient colors similar to Android implementation + /// Generate gradient colors similar to Android implementation private func colorTemperatureGradient(steps: Int = 20) -> [Color] { ColorTemperatureRowMath.gradientTemperatures(for: temperatureRange, steps: steps).map { Color(temperature: $0) } } diff --git a/openHAB/UI/SwiftUI/Rows/DatePickerInputRowView.swift b/openHAB/UI/SwiftUI/Rows/DatePickerInputRowView.swift index 0657e3f77..1cf7918bd 100644 --- a/openHAB/UI/SwiftUI/Rows/DatePickerInputRowView.swift +++ b/openHAB/UI/SwiftUI/Rows/DatePickerInputRowView.swift @@ -113,11 +113,11 @@ struct DatePickerInputRowView: View { var body: some View { makeDateInputRowContent( DateInputRowConfig( - input: input) { command in - guard let itemName = input.itemName else { return } - viewModel.sendCommand(command, for: itemName) - // swiftlint:disable:next closure_end_indentation - } + input: input + ) { command in + guard let itemName = input.itemName else { return } + viewModel.sendCommand(command, for: itemName) + } ) } } diff --git a/openHAB/UI/SwiftUI/Rows/SegmentedRowView.swift b/openHAB/UI/SwiftUI/Rows/SegmentedRowView.swift index 5e71e5e3b..1f37486da 100644 --- a/openHAB/UI/SwiftUI/Rows/SegmentedRowView.swift +++ b/openHAB/UI/SwiftUI/Rows/SegmentedRowView.swift @@ -111,7 +111,6 @@ private struct SegmentedRowContent: View { } /// Button-based segmented control with animated selection indicator - @ViewBuilder private func segmentedButtons(mappings: [OpenHABWidgetMapping], selectedIndex: Int?, displayState: WidgetDisplayState, @@ -154,7 +153,6 @@ private struct SegmentedRowContent: View { .fixedSize(horizontal: false, vertical: true) } - @ViewBuilder private func pressReleaseButtons(mappings: [OpenHABWidgetMapping]) -> some View { HStack(spacing: 8) { ForEach(mappings.indices, id: \.self) { index in diff --git a/openHAB/UI/SwiftUI/Rows/SelectionRowView.swift b/openHAB/UI/SwiftUI/Rows/SelectionRowView.swift index 47d1ff745..532ad9cc1 100644 --- a/openHAB/UI/SwiftUI/Rows/SelectionRowView.swift +++ b/openHAB/UI/SwiftUI/Rows/SelectionRowView.swift @@ -56,7 +56,6 @@ private struct SelectionRowContent: View { } } - @ViewBuilder private func rowContent(displayedCommand: String) -> some View { RowViewWithIcon(input: input) { if !input.displayState.labelText.isEmpty { diff --git a/openHAB/UI/SwiftUI/Rows/SliderRowView.swift b/openHAB/UI/SwiftUI/Rows/SliderRowView.swift index e538d8e6a..b39a6f238 100644 --- a/openHAB/UI/SwiftUI/Rows/SliderRowView.swift +++ b/openHAB/UI/SwiftUI/Rows/SliderRowView.swift @@ -122,7 +122,7 @@ private struct SliderRowContent: View { guard dragWidgetId == input.widgetId, let dragStartVersion else { isEditing = false dragValue = nil - self.dragStartVersion = nil + dragStartVersion = nil dragWidgetId = nil return } diff --git a/openHAB/UI/SwiftUI/Rows/TextInputRowView.swift b/openHAB/UI/SwiftUI/Rows/TextInputRowView.swift index c52cff84e..91524fe25 100644 --- a/openHAB/UI/SwiftUI/Rows/TextInputRowView.swift +++ b/openHAB/UI/SwiftUI/Rows/TextInputRowView.swift @@ -64,9 +64,9 @@ struct InputCommandFormatter { // MARK: initial draft from server state - // Formats a Double (always dot-decimal from the server) using the locale decimal separator. - // Whole numbers are rendered without a decimal point (220.0 → "220"). - // Uses NumberFormatter to avoid Swift's exponent notation for small values (e.g. 1e-05). + /// Formats a Double (always dot-decimal from the server) using the locale decimal separator. + /// Whole numbers are rendered without a decimal point (220.0 → "220"). + /// Uses NumberFormatter to avoid Swift's exponent notation for small values (e.g. 1e-05). func localeFormattedValue(_ value: Double) -> String { if value.truncatingRemainder(dividingBy: 1) == 0, value >= Double(Int.min), @@ -82,8 +82,8 @@ struct InputCommandFormatter { return formatter.string(from: NSNumber(value: value)) ?? String(value) } - // Extracts the numeric portion from a formatted state string, e.g. "220 °C" → "220". - // For non-number inputs the string is returned unchanged. + /// Extracts the numeric portion from a formatted state string, e.g. "220 °C" → "220". + /// For non-number inputs the string is returned unchanged. func numericDraftFromState(_ text: String) -> String { guard !text.isEmpty else { return text } var result = "" @@ -103,7 +103,7 @@ struct InputCommandFormatter { return result } - // Extracts the unit suffix from a formatted state string, e.g. "220 °C" → " °C", "220" → "". + /// Extracts the unit suffix from a formatted state string, e.g. "220 °C" → " °C", "220" → "". func unitSuffixFromState(_ text: String) -> String { guard !text.isEmpty else { return "" } var i = text.startIndex @@ -194,8 +194,8 @@ private struct TextInputRowContent: View { inputCommandFormatter.command(from: draftInputText, hint: inputHint, unitSuffix: draftUnitSuffix) } - // Display value for number inputs: apply the number pattern with the current locale, - // falling back to the server-formatted label value, then the raw state string. + /// Display value for number inputs: apply the number pattern with the current locale, + /// falling back to the server-formatted label value, then the raw state string. private var formattedDisplayText: String { guard inputHint == .number, !inputText.isEmpty else { return inputText } if let pattern = input.numberPattern, !pattern.isEmpty { @@ -234,11 +234,13 @@ private struct TextInputRowContent: View { prepareEditDraft() showInputAlert = true } label: { - Text(inputText.isEmpty - ? "Enter \(inputHint == .number ? "number" : "text")" - : formattedDisplayText) - .lineLimit(nil) - .foregroundStyle(inputText.isEmpty ? .secondary : (input.valueColor.isEmpty ? .secondary : Color(fromString: input.valueColor))) + Text( + inputText.isEmpty + ? "Enter \(inputHint == .number ? "number" : "text")" + : formattedDisplayText + ) + .lineLimit(nil) + .foregroundStyle(inputText.isEmpty ? .secondary : (input.valueColor.isEmpty ? .secondary : Color(fromString: input.valueColor))) } .buttonStyle(.plain) .disabled(input.readOnly) @@ -324,11 +326,11 @@ struct TextInputRowView: View { var body: some View { makeTextInputRowContent( TextInputRowConfig( - input: input) { command in - guard let itemName = input.itemName else { return } - viewModel.sendCommand(command, for: itemName) - // swiftlint:disable:next closure_end_indentation - } + input: input + ) { command in + guard let itemName = input.itemName else { return } + viewModel.sendCommand(command, for: itemName) + } ) } } diff --git a/openHAB/UI/SwiftUI/SitemapDiagnostics.swift b/openHAB/UI/SwiftUI/SitemapDiagnostics.swift index 327fa3730..f80acfb71 100644 --- a/openHAB/UI/SwiftUI/SitemapDiagnostics.swift +++ b/openHAB/UI/SwiftUI/SitemapDiagnostics.swift @@ -137,9 +137,8 @@ enum SitemapDiagnostics { applyStateMs: Int, totalUpdateMs: Int) { guard isEnabled else { return } - logger.info( - "update origin=\(origin.rawValue, privacy: .public) widgets=\(widgetCount, privacy: .public) rows=\(rowCount, privacy: .public) inputsChanged=\(inputsChanged, privacy: .public) titleChanged=\(titleChanged, privacy: .public) reusedInputs=\(reusedInputCount, privacy: .public) changedRows=\(changedRowCount, privacy: .public) changedKinds=\(changedRowKinds, privacy: .public) buildRowInputsMs=\(buildRowInputsMs, privacy: .public) applyStateMs=\(applyStateMs, privacy: .public) analysisMs=\(analysisMs, privacy: .public) totalUpdateMs=\(totalUpdateMs, privacy: .public)" - ) + // swiftlint:disable:next line_length + logger.info("update origin=\(origin.rawValue, privacy: .public) widgets=\(widgetCount, privacy: .public) rows=\(rowCount, privacy: .public) inputsChanged=\(inputsChanged, privacy: .public) titleChanged=\(titleChanged, privacy: .public) reusedInputs=\(reusedInputCount, privacy: .public) changedRows=\(changedRowCount, privacy: .public) changedKinds=\(changedRowKinds, privacy: .public) buildRowInputsMs=\(buildRowInputsMs, privacy: .public) applyStateMs=\(applyStateMs, privacy: .public) analysisMs=\(analysisMs, privacy: .public) totalUpdateMs=\(totalUpdateMs, privacy: .public)") } static func logLongPoll(requestMs: Int, @@ -158,16 +157,17 @@ enum SitemapDiagnostics { replacedPendingPage: Bool, coalescedUpdates: Int) { guard isEnabled else { return } + // swiftlint:disable line_length logger.info( "longPollDebounce action=\(action, privacy: .public) elapsedMs=\(elapsedMs, privacy: .public) remainingMs=\(remainingMs, privacy: .public) replacedPendingPage=\(replacedPendingPage, privacy: .public) coalescedUpdates=\(coalescedUpdates, privacy: .public)" ) + // swiftlint:enable line_length } static func logIconSummary(_ snapshot: IconLoadDiagnosticsSnapshot) { guard isEnabled, !snapshot.isEmpty else { return } - logger.info( - "icons resolutionFailures=\(snapshot.resolutionFailures, privacy: .public) starts=\(snapshot.starts, privacy: .public) successes=\(snapshot.successes, privacy: .public) memoryHits=\(snapshot.memoryHits, privacy: .public) diskHits=\(snapshot.diskHits, privacy: .public) networkLoads=\(snapshot.networkLoads, privacy: .public) cancellations=\(snapshot.cancellations, privacy: .public) failures=\(snapshot.failures, privacy: .public) distinctURLs=\(snapshot.distinctURLs, privacy: .public) distinctRows=\(snapshot.distinctRows, privacy: .public)" - ) + // swiftlint:disable:next line_length + logger.info("icons resolutionFailures=\(snapshot.resolutionFailures, privacy: .public) starts=\(snapshot.starts, privacy: .public) successes=\(snapshot.successes, privacy: .public) memoryHits=\(snapshot.memoryHits, privacy: .public) diskHits=\(snapshot.diskHits, privacy: .public) networkLoads=\(snapshot.networkLoads, privacy: .public) cancellations=\(snapshot.cancellations, privacy: .public) failures=\(snapshot.failures, privacy: .public) distinctURLs=\(snapshot.distinctURLs, privacy: .public) distinctRows=\(snapshot.distinctRows, privacy: .public)") } static func logIconResolutionFailure(rowIdentity: String, @@ -176,9 +176,11 @@ enum SitemapDiagnostics { trackerStatus: String, connectionDescription: String) { guard isEnabled else { return } + // swiftlint:disable line_length logger.info( "iconResolutionFailure row=\(rowIdentity, privacy: .private(mask: .hash)) icon=\(icon, privacy: .public) reason=\(reason, privacy: .public) trackerStatus=\(trackerStatus, privacy: .public) connection=\(connectionDescription, privacy: .public)" ) + // swiftlint:enable line_length } static func logIconStart(rowIdentity: String, icon: String, url: URL, cacheKey: String) { diff --git a/openHAB/UI/SwiftUI/SitemapRowInput.swift b/openHAB/UI/SwiftUI/SitemapRowInput.swift index adbf1f9c3..5e0a3b41a 100644 --- a/openHAB/UI/SwiftUI/SitemapRowInput.swift +++ b/openHAB/UI/SwiftUI/SitemapRowInput.swift @@ -14,7 +14,7 @@ import OpenHABCore /// Stable identity for a row snapshot. /// Occurrence disambiguates repeated widget IDs in one page payload. -struct RowID: Hashable, Sendable { +struct RowID: Hashable { let pageKey: String let widgetId: String let occurrence: Int @@ -34,7 +34,7 @@ struct LinkedPageNavigation: Hashable { /// Draft immutable row union for a list-driven SwiftUI pipeline. /// Each row case carries a dedicated typed input. -enum SitemapRowInput: Identifiable, Equatable, Sendable { +enum SitemapRowInput: Identifiable, Equatable { case frame(RowID, FrameRowInput) case linked(RowID, LinkedPageRowInput) case text(RowID, TextRowInput) diff --git a/openHAB/UI/SwiftUI/WidgetRowInputs.swift b/openHAB/UI/SwiftUI/WidgetRowInputs.swift index 4a4d692fe..8d14963cf 100644 --- a/openHAB/UI/SwiftUI/WidgetRowInputs.swift +++ b/openHAB/UI/SwiftUI/WidgetRowInputs.swift @@ -17,7 +17,7 @@ protocol RowWithIconInput { var icon: RowIconInput { get } } -struct RowIconInput: Equatable, Sendable { +struct RowIconInput: Equatable { let icon: String let iconColor: String let staticIcon: Bool @@ -220,7 +220,7 @@ struct RollershutterRowInput: Equatable, RowWithIconInput { } } -struct InputRowInput: Sendable, Equatable, RowWithIconInput { +struct InputRowInput: Equatable, RowWithIconInput { let widgetId: String let renderingKind: WidgetRenderingKind let displayState: WidgetDisplayState @@ -258,7 +258,7 @@ struct InputRowInput: Sendable, Equatable, RowWithIconInput { } struct ButtonGridRowInput: Equatable, RowWithIconInput { - struct ButtonInput: Equatable, Sendable, Identifiable { + struct ButtonInput: Equatable, Identifiable { let id: String let label: String let icon: RowIconInput diff --git a/openHAB/UI/Watch/WatchMessageService.swift b/openHAB/UI/Watch/WatchMessageService.swift index 49a5b0325..bec9c6414 100644 --- a/openHAB/UI/Watch/WatchMessageService.swift +++ b/openHAB/UI/Watch/WatchMessageService.swift @@ -15,8 +15,8 @@ import OpenHABCore import os.log import WatchConnectivity -// This class receives Watch Request for the configuration data like localUrl. -// The functionality is activated in the AppDelegate. +/// This class receives Watch Request for the configuration data like localUrl. +/// The functionality is activated in the AppDelegate. class WatchMessageService: NSObject, WCSessionDelegate { @MainActor static let singleton = WatchMessageService() @@ -26,8 +26,8 @@ class WatchMessageService: NSObject, WCSessionDelegate { private var preferencesSubscription: AnyCancellable? - // This method gets called when the watch requests the data - // ⚠️ This is called off the main thread. Do NOT touch @MainActor stuff. + /// This method gets called when the watch requests the data + /// ⚠️ This is called off the main thread. Do NOT touch @MainActor stuff. func session(_ session: WCSession, didReceiveMessage message: [String: Any], replyHandler: @escaping ([String: Any]) -> Void) { guard message["request"] != nil else { return } diff --git a/openHABTestsSwift/ColorTemperatureRowMathTests.swift b/openHABTestsSwift/ColorTemperatureRowMathTests.swift index 1bbdb9163..099ceaabc 100644 --- a/openHABTestsSwift/ColorTemperatureRowMathTests.swift +++ b/openHABTestsSwift/ColorTemperatureRowMathTests.swift @@ -10,7 +10,6 @@ // SPDX-License-Identifier: EPL-2.0 @testable import openHAB - import Testing private enum TestValues { @@ -35,7 +34,6 @@ private enum TestValues { static let zero = 0.0 } -@Suite struct ColorTemperatureRowMathTests { @Test func normalizedRangeUsesFallbackForZeroSpan() { diff --git a/openHABTestsSwift/IconReloadCoordinatorTests.swift b/openHABTestsSwift/IconReloadCoordinatorTests.swift index ca72f6062..9c074b52e 100644 --- a/openHABTestsSwift/IconReloadCoordinatorTests.swift +++ b/openHABTestsSwift/IconReloadCoordinatorTests.swift @@ -14,7 +14,6 @@ import Foundation import Testing @MainActor -@Suite struct IconReloadCoordinatorTests { // Each test creates an isolated coordinator instance rather than using the shared // singleton, so tests do not interfere with each other. diff --git a/openHABTestsSwift/InputCommandFormatterTests.swift b/openHABTestsSwift/InputCommandFormatterTests.swift index 98ab4f574..c2405247f 100644 --- a/openHABTestsSwift/InputCommandFormatterTests.swift +++ b/openHABTestsSwift/InputCommandFormatterTests.swift @@ -16,7 +16,6 @@ import Testing // MARK: - isValidNumberDraft with comma separator -@Suite struct InputCommandFormatterCommaTests { let formatter = InputCommandFormatter(decimalSeparator: ",") @@ -68,7 +67,6 @@ struct InputCommandFormatterCommaTests { // MARK: - UIKit oracle (reference implementation) -@Suite struct InputCommandFormatterOracleTests { /// Port of the UIKit `textField(_:shouldChangeCharactersIn:replacementString:)` logic. /// Given the old text, the NSRange being replaced, and the replacement string, returns @@ -358,7 +356,6 @@ struct InputCommandFormatterOracleTests { // MARK: - localeFormattedValue with comma separator -@Suite struct InputCommandFormattedValueCommaTests { let formatter = InputCommandFormatter(decimalSeparator: ",") @@ -393,7 +390,6 @@ struct InputCommandFormattedValueCommaTests { } } -@Suite struct InputCommandFormatterTests { let formatter = InputCommandFormatter(decimalSeparator: ".") diff --git a/openHABTestsSwift/LocalizationTests.swift b/openHABTestsSwift/LocalizationTests.swift index e01cb1185..b49fa7514 100644 --- a/openHABTestsSwift/LocalizationTests.swift +++ b/openHABTestsSwift/LocalizationTests.swift @@ -9,9 +9,8 @@ // // SPDX-License-Identifier: EPL-2.0 -@testable import openHAB - import Foundation +@testable import openHAB import Testing private final class BundleLocator: AnyObject {} diff --git a/openHABTestsSwift/OpenHABEndPoint.swift b/openHABTestsSwift/OpenHABEndPoint.swift index 79f21ffbb..5203c7423 100644 --- a/openHABTestsSwift/OpenHABEndPoint.swift +++ b/openHABTestsSwift/OpenHABEndPoint.swift @@ -20,7 +20,7 @@ class OpenHABEndPoint: XCTestCase { // Put teardown code here. This method is called after the invocation of each test method in the class. } - func testExample() throws { + func testExample() { // This is an example of a functional test case. // Use XCTAssert and related functions to verify your tests produce the correct results. // Any test you write for XCTest can be annotated as throws and async. @@ -28,7 +28,7 @@ class OpenHABEndPoint: XCTestCase { // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. } - func testPerformanceExample() throws { + func testPerformanceExample() { // This is an example of a performance test case. measure { // Put the code you want to measure the time of here. diff --git a/openHABTestsSwift/OpenHABSVGTests.swift b/openHABTestsSwift/OpenHABSVGTests.swift index cc7e28c0c..e79c839ca 100644 --- a/openHABTestsSwift/OpenHABSVGTests.swift +++ b/openHABTestsSwift/OpenHABSVGTests.swift @@ -15,7 +15,6 @@ import UIKit private final class OpenHABSVGTestsBundleToken {} -@Suite struct OpenHABSVGTests { private func renderSVG(named name: String) throws -> UIImage? { let bundle = Bundle(for: OpenHABSVGTestsBundleToken.self) @@ -30,9 +29,9 @@ struct OpenHABSVGTests { return OpenHABImageProcessor().process(data: data) } - // The app-level contract is graceful SVG handling via OpenHABImageProcessor. - // Some fixtures are not directly decodable by Apple's built-in SVG renderer, - // but the processor must still return a fallback image instead of nil. + /// The app-level contract is graceful SVG handling via OpenHABImageProcessor. + /// Some fixtures are not directly decodable by Apple's built-in SVG renderer, + /// but the processor must still return a fallback image instead of nil. @Test func validSVGWithEmbeddedPNG() throws { let image = try renderSVG(named: "embeddedpng_valid") diff --git a/openHABTestsSwift/RowLayoutPolicyTests.swift b/openHABTestsSwift/RowLayoutPolicyTests.swift index b68508ed8..8a252684a 100644 --- a/openHABTestsSwift/RowLayoutPolicyTests.swift +++ b/openHABTestsSwift/RowLayoutPolicyTests.swift @@ -14,7 +14,6 @@ import OpenHABCore import SwiftUI import Testing -@Suite struct RowLayoutPolicyTests { @Test func linkedFrameUsesFrameInsetsAndBackground() { diff --git a/openHABTestsSwift/ScreenSaverLayoutCalculatorTests.swift b/openHABTestsSwift/ScreenSaverLayoutCalculatorTests.swift index 637fe5646..16bd225e5 100644 --- a/openHABTestsSwift/ScreenSaverLayoutCalculatorTests.swift +++ b/openHABTestsSwift/ScreenSaverLayoutCalculatorTests.swift @@ -13,7 +13,6 @@ import CoreGraphics @testable import openHAB import Testing -@Suite struct ScreenSaverLayoutCalculatorTests { @Test func oversizedTimeTextScalesDownToFitHorizontalMargins() { diff --git a/openHABTestsSwift/SitemapPageViewModelForegroundTests.swift b/openHABTestsSwift/SitemapPageViewModelForegroundTests.swift index 718ba37ce..1642ea73d 100644 --- a/openHABTestsSwift/SitemapPageViewModelForegroundTests.swift +++ b/openHABTestsSwift/SitemapPageViewModelForegroundTests.swift @@ -12,15 +12,14 @@ @testable import openHAB import Testing -// Regression test for the "foreground thundering herd" bug: -// All retained SitemapPageViewModels receiving UIApplication.didBecomeActiveNotification -// and simultaneously restarting page load/polling on app foreground. -// -// Only a visible page (between markAppeared and stopPageHandling) must react to -// refreshOnForeground. Pages that were stopped because they scrolled off-screen -// or were navigated away from must stay silent. +/// Regression test for the "foreground thundering herd" bug: +/// All retained SitemapPageViewModels receiving UIApplication.didBecomeActiveNotification +/// and simultaneously restarting page load/polling on app foreground. +/// +/// Only a visible page (between markAppeared and stopPageHandling) must react to +/// refreshOnForeground. Pages that were stopped because they scrolled off-screen +/// or were navigated away from must stay silent. @MainActor -@Suite struct SitemapPageViewModelForegroundTests { // lastForegroundRefreshAt advances only when the visibility guard passes, making // it the right synchronous observable for these tests — no Task.yield needed. diff --git a/openHABTestsSwift/SitemapRowInputBuilderTests.swift b/openHABTestsSwift/SitemapRowInputBuilderTests.swift index 345593c07..ce5dd2efc 100644 --- a/openHABTestsSwift/SitemapRowInputBuilderTests.swift +++ b/openHABTestsSwift/SitemapRowInputBuilderTests.swift @@ -13,7 +13,6 @@ import OpenHABCore import Testing -@Suite struct WidgetMappingSnapshotDisplayStateTests { @Test func labelOnlyInBracketsDoesNotLeakIntoLabelText() { @@ -51,7 +50,6 @@ struct WidgetMappingSnapshotDisplayStateTests { } } -@Suite struct SitemapRowInputBuilderTests { @Test func incrementalRebuildReusesUnchangedRows() { diff --git a/openHABTestsSwift/SitemapRowInputMapperTests.swift b/openHABTestsSwift/SitemapRowInputMapperTests.swift index c8359a0a3..4fb0a4e7e 100644 --- a/openHABTestsSwift/SitemapRowInputMapperTests.swift +++ b/openHABTestsSwift/SitemapRowInputMapperTests.swift @@ -13,7 +13,6 @@ import OpenHABCore import Testing -@Suite struct SitemapRowInputMapperTests { @Test func linkedPageWidgetsAlwaysMapToLinked() { diff --git a/openHABTestsSwift/SliderOverrideSyncTests.swift b/openHABTestsSwift/SliderOverrideSyncTests.swift index 3aed8ba5d..f23e73379 100644 --- a/openHABTestsSwift/SliderOverrideSyncTests.swift +++ b/openHABTestsSwift/SliderOverrideSyncTests.swift @@ -14,7 +14,6 @@ import OpenHABCore import Testing @MainActor -@Suite struct SliderOverrideSyncTests { @Test func clearsSliderOverrideWhenServerCatchesUp() { diff --git a/openHABTestsSwift/URLWebViewPathTests.swift b/openHABTestsSwift/URLWebViewPathTests.swift index 1c86e119b..77bb56038 100644 --- a/openHABTestsSwift/URLWebViewPathTests.swift +++ b/openHABTestsSwift/URLWebViewPathTests.swift @@ -15,14 +15,14 @@ import Testing @Suite("relativeWebViewPath(proxyURL:rootURLString:)") struct RelativeWebViewPathFunctionTests { - @Test func prefersProxyURLWhenPresent() { - let proxy = URL(string: "https://host/proxy")! + @Test func prefersProxyURLWhenPresent() throws { + let proxy = try #require(URL(string: "https://host/proxy")) let result = relativeWebViewPath("/proxy/ui", proxyURL: proxy, rootURLString: "https://host") #expect(result == "/ui") } - @Test func proxyDoesNotMatchSiblingPath() { - let proxy = URL(string: "https://host/proxy")! + @Test func proxyDoesNotMatchSiblingPath() throws { + let proxy = try #require(URL(string: "https://host/proxy")) let result = relativeWebViewPath("/proxy2/ui", proxyURL: proxy, rootURLString: "https://host") #expect(result == "/proxy2/ui") } @@ -42,50 +42,50 @@ struct RelativeWebViewPathFunctionTests { struct URLWebViewPathTests { // MARK: - Exact match - @Test func exactBasePath() { - let url = URL(string: "https://host/openhab")! + @Test func exactBasePath() throws { + let url = try #require(URL(string: "https://host/openhab")) #expect(url.relativeWebViewPath(for: "/openhab") == "/") } // MARK: - Normal sub-path stripping - @Test func stripsBasePath() { - let url = URL(string: "https://host/openhab")! + @Test func stripsBasePath() throws { + let url = try #require(URL(string: "https://host/openhab")) #expect(url.relativeWebViewPath(for: "/openhab/ui") == "/ui") } - @Test func stripsBasePathWithTrailingSlash() { - let url = URL(string: "https://host/openhab")! + @Test func stripsBasePathWithTrailingSlash() throws { + let url = try #require(URL(string: "https://host/openhab")) #expect(url.relativeWebViewPath(for: "/openhab/ui/") == "/ui/") } // MARK: - Sibling-path false-match guard (the bug this fixes) - @Test func doesNotMatchSiblingWithSamePrefix() { - let url = URL(string: "https://host/openhab")! + @Test func doesNotMatchSiblingWithSamePrefix() throws { + let url = try #require(URL(string: "https://host/openhab")) // /openhab2/ui must NOT be treated as a sub-path of /openhab #expect(url.relativeWebViewPath(for: "/openhab2/ui") == "/openhab2/ui") } - @Test func doesNotMatchSiblingExactPrefix() { - let url = URL(string: "https://host/openhab")! + @Test func doesNotMatchSiblingExactPrefix() throws { + let url = try #require(URL(string: "https://host/openhab")) #expect(url.relativeWebViewPath(for: "/openhab2") == "/openhab2") } // MARK: - Edge cases - @Test func emptyBasePath() { - let url = URL(string: "https://host")! + @Test func emptyBasePath() throws { + let url = try #require(URL(string: "https://host")) #expect(url.relativeWebViewPath(for: "/ui") == "/ui") } - @Test func rootBasePath() { - let url = URL(string: "https://host/")! + @Test func rootBasePath() throws { + let url = try #require(URL(string: "https://host/")) #expect(url.relativeWebViewPath(for: "/ui") == "/ui") } - @Test func unrelatedPath() { - let url = URL(string: "https://host/openhab")! + @Test func unrelatedPath() throws { + let url = try #require(URL(string: "https://host/openhab")) #expect(url.relativeWebViewPath(for: "/other/path") == "/other/path") } } diff --git a/openHABTestsSwift/WebRowViewConfigurationTests.swift b/openHABTestsSwift/WebRowViewConfigurationTests.swift index afddb03e0..1d3378047 100644 --- a/openHABTestsSwift/WebRowViewConfigurationTests.swift +++ b/openHABTestsSwift/WebRowViewConfigurationTests.swift @@ -13,7 +13,6 @@ import Testing import WebKit -@Suite struct WebRowViewConfigurationTests { @MainActor @Test diff --git a/openHABWatch/Domain/UserData.swift b/openHABWatch/Domain/UserData.swift index 1a486c911..198e9b43b 100644 --- a/openHABWatch/Domain/UserData.swift +++ b/openHABWatch/Domain/UserData.swift @@ -31,9 +31,9 @@ final class UserData: ObservableObject { private var cachedWidgets: [OpenHABWidget] = [] private var currentlyLoadingSitemap: String? private var lastObservedConnectionURL: String? - // Incremented each time a new pageHandlingTask is created. The task captures its own - // generation at creation time and only clears shared state when the generation still matches, - // preventing a completing old task from wiping out a newly started task for the same sitemap. + /// Incremented each time a new pageHandlingTask is created. The task captures its own + /// generation at creation time and only clears shared state when the generation still matches, + /// preventing a completing old task from wiping out a newly started task for the same sitemap. private var taskGeneration = 0 private var pageHandlingTask: Task? diff --git a/openHABWatch/Extension/OpenHABWatchAppDelegate.swift b/openHABWatch/Extension/OpenHABWatchAppDelegate.swift index 8aeb63fbb..352121f84 100644 --- a/openHABWatch/Extension/OpenHABWatchAppDelegate.swift +++ b/openHABWatch/Extension/OpenHABWatchAppDelegate.swift @@ -12,7 +12,6 @@ import Kingfisher import OpenHABCore import os.log - import WatchConnectivity import WatchKit diff --git a/openHABWatch/External/AppMessageService.swift b/openHABWatch/External/AppMessageService.swift index 6ba113e05..9103a6fe2 100644 --- a/openHABWatch/External/AppMessageService.swift +++ b/openHABWatch/External/AppMessageService.swift @@ -15,15 +15,15 @@ import os.log import WatchConnectivity import WatchKit -// This class handles values that are passed from the ios app. -// @unchecked Sendable: all mutable state (@MainActor contextRequestInFlight) is actor-protected; -// WCSession delegate callbacks are non-isolated and only schedule Tasks, never mutate directly. +/// This class handles values that are passed from the ios app. +/// @unchecked Sendable: all mutable state (@MainActor contextRequestInFlight) is actor-protected; +/// WCSession delegate callbacks are non-isolated and only schedule Tasks, never mutate directly. class AppMessageService: NSObject, WCSessionDelegate, @unchecked Sendable { @MainActor static let singleton = AppMessageService() private static let preferencesKey = "watchPreferences" - // Accessed only from @MainActor context (requestApplicationContext and its Task completions). + /// Accessed only from @MainActor context (requestApplicationContext and its Task completions). @MainActor private var contextRequestInFlight = false @MainActor diff --git a/openHABWatch/Model/LazyView.swift b/openHABWatch/Model/LazyView.swift index 746e98a28..2e73efc80 100644 --- a/openHABWatch/Model/LazyView.swift +++ b/openHABWatch/Model/LazyView.swift @@ -12,7 +12,7 @@ import Foundation import SwiftUI -// https://medium.com/better-programming/swiftui-navigation-links-and-the-common-pitfalls-faced-505cbfd8029b +/// https://medium.com/better-programming/swiftui-navigation-links-and-the-common-pitfalls-faced-505cbfd8029b struct LazyView: View { let build: () -> Content diff --git a/openHABWatch/Model/OpenHABWidgetExtension.swift b/openHABWatch/Model/OpenHABWidgetExtension.swift index b8e3ee0e1..ad90c8c7a 100644 --- a/openHABWatch/Model/OpenHABWidgetExtension.swift +++ b/openHABWatch/Model/OpenHABWidgetExtension.swift @@ -35,8 +35,6 @@ extension OpenHABWidget { if linkedPage != nil { Image(systemSymbol: .chevronRight) .foregroundStyle(.secondary) - } else { - EmptyView() } } } diff --git a/openHABWatch/Model/UserDefaultsBacked.swift b/openHABWatch/Model/UserDefaultsBacked.swift index 37ceb922d..d7f2b30f7 100644 --- a/openHABWatch/Model/UserDefaultsBacked.swift +++ b/openHABWatch/Model/UserDefaultsBacked.swift @@ -15,7 +15,7 @@ import Foundation struct UserDefaultsBacked { let key: String let defaultValue: T - // https://www.swiftbysundell.com/articles/property-wrappers-in-swift/ + /// https://www.swiftbysundell.com/articles/property-wrappers-in-swift/ var storage: UserDefaults = .standard var wrappedValue: T { @@ -28,7 +28,7 @@ struct UserDefaultsBacked { } } -// Convenience initializer when UserDefaults is optional. +/// Convenience initializer when UserDefaults is optional. extension UserDefaultsBacked where T: ExpressibleByNilLiteral { init(key: String, storage: UserDefaults = .standard) { self.init(key: key, defaultValue: nil, storage: storage) diff --git a/openHABWatch/Views/Rows/ImageRow.swift b/openHABWatch/Views/Rows/ImageRow.swift index 78e4d9286..8e77ed6e9 100644 --- a/openHABWatch/Views/Rows/ImageRow.swift +++ b/openHABWatch/Views/Rows/ImageRow.swift @@ -16,7 +16,7 @@ import OpenHABCore import os.log import SwiftUI -// Timer manager that persists across view updates +/// Timer manager that persists across view updates private class ImageRefreshTimer: ObservableObject { @Published var refreshCount = 0 private var timer: AnyCancellable? @@ -64,7 +64,7 @@ struct ImageRow: View, Equatable { @StateObject private var refreshTimer = ImageRefreshTimer() @ObservedObject private var networkTracker = MainActorNetworkTracker.shared - // For refreshing images, append a query parameter to bust the cache + /// For refreshing images, append a query parameter to bust the cache private var displayUrl: URL? { guard let url else { return nil } guard refresh > 0 else { return url } diff --git a/openHABWatch/Views/Rows/SegmentRow.swift b/openHABWatch/Views/Rows/SegmentRow.swift index 95d3d77b0..9bc39187e 100644 --- a/openHABWatch/Views/Rows/SegmentRow.swift +++ b/openHABWatch/Views/Rows/SegmentRow.swift @@ -66,7 +66,6 @@ struct SegmentRow: View { } } - @ViewBuilder private var pressReleaseButtons: some View { HStack(spacing: 8) { ForEach(viewModel.mappings.indices, id: \.self) { index in @@ -101,7 +100,6 @@ struct SegmentRow: View { // MARK: - Shared Components - @ViewBuilder private var iconTitleRow: some View { HStack { WatchIconView(model: widget.iconRenderModel(), settings: settings) @@ -119,7 +117,6 @@ struct SegmentRow: View { // MARK: - Multi-Segment (existing NavigationLink) - @ViewBuilder private var multiSegmentContent: some View { HStack { HStack { @@ -178,7 +175,6 @@ struct SegmentRow: View { // MARK: - Single Mapping func - @ViewBuilder private func singleButton(for mapping: OpenHABWidgetMapping) -> some View { inlineButton(label: mapping.label, isPressed: singlePressed) .overlay { @@ -205,7 +201,6 @@ struct SegmentRow: View { // MARK: - Shared Components - @ViewBuilder private func inlineButton(label: String, isPressed: Bool) -> some View { Text(label) .watchTextStyle(.control) diff --git a/openHABWatch/Views/Rows/SegmentSelectionView.swift b/openHABWatch/Views/Rows/SegmentSelectionView.swift index e96ea59fa..14efc46bd 100644 --- a/openHABWatch/Views/Rows/SegmentSelectionView.swift +++ b/openHABWatch/Views/Rows/SegmentSelectionView.swift @@ -57,7 +57,6 @@ struct SegmentSelectionView: View { _viewModel = State(wrappedValue: viewModel) } - @ViewBuilder private func standardButton(for mapping: OpenHABWidgetMapping, at index: Int) -> some View { Button { selectOption(at: index) @@ -67,7 +66,6 @@ struct SegmentSelectionView: View { .buttonStyle(PlainButtonStyle()) } - @ViewBuilder private func pressReleaseButton(for mapping: OpenHABWidgetMapping, at index: Int) -> some View { optionLabel(for: mapping, at: index, isPressed: pressedIndex == index) .gesture( @@ -85,7 +83,6 @@ struct SegmentSelectionView: View { ) } - @ViewBuilder private func optionLabel(for mapping: OpenHABWidgetMapping, at index: Int, isPressed: Bool) -> some View { HStack { Text(mapping.label) diff --git a/openHABWatch/Views/SitemapPageView.swift b/openHABWatch/Views/SitemapPageView.swift index 70557cbf5..b4efd90f4 100644 --- a/openHABWatch/Views/SitemapPageView.swift +++ b/openHABWatch/Views/SitemapPageView.swift @@ -61,7 +61,6 @@ struct SitemapPageView: View { } } - @ViewBuilder private var pageContent: some View { Group { if viewModel.isLoadingSitemap, viewModel.widgets.isEmpty { diff --git a/openHABWatch/Views/Utils/WatchIconView.swift b/openHABWatch/Views/Utils/WatchIconView.swift index 30d8626f0..97821396a 100644 --- a/openHABWatch/Views/Utils/WatchIconView.swift +++ b/openHABWatch/Views/Utils/WatchIconView.swift @@ -50,9 +50,9 @@ struct WatchIconView: View { )?.url } - // During a transient connection handover activeConnection can briefly be nil. - // Hold the last successfully resolved URL and connection for a short grace period - // so icons don't flash blank between reconnects, and retained auth carries over. + /// During a transient connection handover activeConnection can briefly be nil. + /// Hold the last successfully resolved URL and connection for a short grace period + /// so icons don't flash blank between reconnects, and retained auth carries over. private var displayIconURL: URL? { if let url = iconURL { return url } guard Date() <= keepLastResolvedIconURLUntil else { return nil } diff --git a/openHABWatchSwiftUI Watch AppUITests/OpenHABWatchLaunchTests.swift b/openHABWatchSwiftUI Watch AppUITests/OpenHABWatchLaunchTests.swift index 46b358882..cc429c90b 100644 --- a/openHABWatchSwiftUI Watch AppUITests/OpenHABWatchLaunchTests.swift +++ b/openHABWatchSwiftUI Watch AppUITests/OpenHABWatchLaunchTests.swift @@ -31,7 +31,7 @@ final class OpenHABWatchLaunchTests: XCTestCase { continueAfterFailure = false } - func testLaunch() throws { + func testLaunch() { let app = XCUIApplication() app.launch() diff --git a/openHABWatchSwiftUI Watch AppUITests/OpenHABWatchUITests.swift b/openHABWatchSwiftUI Watch AppUITests/OpenHABWatchUITests.swift index a387f82ad..dc9c46144 100644 --- a/openHABWatchSwiftUI Watch AppUITests/OpenHABWatchUITests.swift +++ b/openHABWatchSwiftUI Watch AppUITests/OpenHABWatchUITests.swift @@ -36,7 +36,7 @@ final class OpenHABWatchUITests: XCTestCase { // Put teardown code here. This method is called after the invocation of each test method in the class. } - func testExample() throws { + func testExample() { // UI tests must launch the application that they test. let app = XCUIApplication() app.launch() @@ -44,7 +44,7 @@ final class OpenHABWatchUITests: XCTestCase { // Use XCTAssert and related functions to verify your tests produce the correct results. } - func testLaunchPerformance() throws { + func testLaunchPerformance() { if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { // This measures how long it takes to launch your application. measure(metrics: [XCTApplicationLaunchMetric()]) { From aa6faf5552623d885cef799ea1e82bd515463952 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Tue, 19 May 2026 08:48:47 +0200 Subject: [PATCH 04/91] Add AppShortcutsProvider and fix intent dialog gaps - Add OpenHABShortcuts: AppShortcutsProvider registering all 10 intents with Siri/Shortcuts phrases for iOS 17+ - Add AppShortcuts.strings for all 9 supported locales (en/de/es/fi/fr/it/nb/nl/ru) using the .strings format required by the iOS 16 deployment target - Add resultValueName to GetItemStateIntent so its output is labeled in Shortcuts - Add ProvidesDialog to SetSwitchItemIntent and SetPlayerValueIntent - Simplify ItemEntity.displayRepresentation by removing the nonLocalizedDisplayString helper in favour of direct LocalizedStringResource string interpolation Signed-off-by: Tim Mueller-Seydlitz --- AppIntents/Intents/GetItemStateIntent.swift | 2 +- AppIntents/Intents/SetPlayerValueIntent.swift | 2 +- AppIntents/Intents/SetSwitchItemIntent.swift | 2 +- AppIntents/ItemEntity.swift | 12 +-- AppIntents/OpenHABShortcuts.swift | 84 +++++++++++++++++++ openHAB.xcodeproj/project.pbxproj | 67 +++++++++++++++ .../de.lproj/AppShortcuts.strings | 12 +++ .../en.lproj/AppShortcuts.strings | 12 +++ .../es.lproj/AppShortcuts.strings | 12 +++ .../fi.lproj/AppShortcuts.strings | 12 +++ .../fr.lproj/AppShortcuts.strings | 12 +++ .../it.lproj/AppShortcuts.strings | 12 +++ .../nb.lproj/AppShortcuts.strings | 12 +++ .../nl.lproj/AppShortcuts.strings | 12 +++ .../ru.lproj/AppShortcuts.strings | 12 +++ 15 files changed, 266 insertions(+), 11 deletions(-) create mode 100644 AppIntents/OpenHABShortcuts.swift create mode 100644 openHAB/Supporting Files/de.lproj/AppShortcuts.strings create mode 100644 openHAB/Supporting Files/en.lproj/AppShortcuts.strings create mode 100644 openHAB/Supporting Files/es.lproj/AppShortcuts.strings create mode 100644 openHAB/Supporting Files/fi.lproj/AppShortcuts.strings create mode 100644 openHAB/Supporting Files/fr.lproj/AppShortcuts.strings create mode 100644 openHAB/Supporting Files/it.lproj/AppShortcuts.strings create mode 100644 openHAB/Supporting Files/nb.lproj/AppShortcuts.strings create mode 100644 openHAB/Supporting Files/nl.lproj/AppShortcuts.strings create mode 100644 openHAB/Supporting Files/ru.lproj/AppShortcuts.strings diff --git a/AppIntents/Intents/GetItemStateIntent.swift b/AppIntents/Intents/GetItemStateIntent.swift index 1cb729cb9..34c3b44fd 100644 --- a/AppIntents/Intents/GetItemStateIntent.swift +++ b/AppIntents/Intents/GetItemStateIntent.swift @@ -36,7 +36,7 @@ struct GetItemStateIntent: AppIntent { } static let title: LocalizedStringResource = "Get Item State" - static let description = IntentDescription("Retrieve the current state of an item") + static let description = IntentDescription("Retrieve the current state of an item", resultValueName: "State") @Parameter(title: "Home") var home: Home? diff --git a/AppIntents/Intents/SetPlayerValueIntent.swift b/AppIntents/Intents/SetPlayerValueIntent.swift index 69552ac4b..17528ce78 100644 --- a/AppIntents/Intents/SetPlayerValueIntent.swift +++ b/AppIntents/Intents/SetPlayerValueIntent.swift @@ -77,6 +77,6 @@ struct SetPlayerValueIntent: AppIntent { throw PlayerValueError.commandFailed(error.localizedDescription) } - return .result(dialog: "Sent \(action) to \(itemEntity.label)") + return .result(dialog: "Sent \(action.rawValue) to \(itemEntity.label)") } } diff --git a/AppIntents/Intents/SetSwitchItemIntent.swift b/AppIntents/Intents/SetSwitchItemIntent.swift index 0f9d88a81..12aa00a35 100644 --- a/AppIntents/Intents/SetSwitchItemIntent.swift +++ b/AppIntents/Intents/SetSwitchItemIntent.swift @@ -84,6 +84,6 @@ struct SetSwitchItemIntent: AppIntent { // Toggle's optimistic visual flip and showing the wrong colour/text. try? await Task.sleep(for: .milliseconds(600)) - return .result(dialog: "Sent \(action) to \(itemEntity.label)") + return .result(dialog: "Sent \(action.rawValue) to \(itemEntity.label)") } } diff --git a/AppIntents/ItemEntity.swift b/AppIntents/ItemEntity.swift index 127c0f2cc..5ab6585ad 100644 --- a/AppIntents/ItemEntity.swift +++ b/AppIntents/ItemEntity.swift @@ -58,13 +58,13 @@ extension ItemEntity { var displayRepresentation: DisplayRepresentation { if let homeName { DisplayRepresentation( - title: nonLocalizedDisplayString(label, key: "__app_intents_item_label__"), - subtitle: nonLocalizedDisplayString("\(item.name) • \(homeName)", key: "__app_intents_item_subtitle__") + title: "\(label)", + subtitle: "\(item.name) • \(homeName)" ) } else { DisplayRepresentation( - title: nonLocalizedDisplayString(label, key: "__app_intents_item_label__"), - subtitle: nonLocalizedDisplayString(item.name, key: "__app_intents_item_name__") + title: "\(label)", + subtitle: "\(item.name)" ) } } @@ -76,8 +76,4 @@ extension ItemEntity { homeName: homeName ) } - - func nonLocalizedDisplayString(_ value: String, key: StaticString) -> LocalizedStringResource { - LocalizedStringResource(key, defaultValue: "\(value)") - } } diff --git a/AppIntents/OpenHABShortcuts.swift b/AppIntents/OpenHABShortcuts.swift new file mode 100644 index 000000000..c3f0ae82c --- /dev/null +++ b/AppIntents/OpenHABShortcuts.swift @@ -0,0 +1,84 @@ +// Copyright (c) 2010-2026 Contributors to the openHAB project +// +// See the NOTICE file(s) distributed with this work for additional +// information. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0 +// +// SPDX-License-Identifier: EPL-2.0 + +import AppIntents + +@available(iOS 17.0, macOS 14.0, watchOS 10.0, *) +struct OpenHABShortcuts: AppShortcutsProvider { + static var appShortcuts: [AppShortcut] { + AppShortcut( + intent: SetSwitchItemIntent(), + phrases: [ + "Set a switch in \(.applicationName)", + "Toggle a switch in \(.applicationName)" + ], + shortTitle: "Set Switch", + systemImageName: "switch.2" + ) + AppShortcut( + intent: GetItemStateIntent(), + phrases: ["Get item state in \(.applicationName)"], + shortTitle: "Get Item State", + systemImageName: "info.circle" + ) + AppShortcut( + intent: SetDimmerRollerValueIntent(), + phrases: [ + "Set dimmer in \(.applicationName)", + "Set roller shutter in \(.applicationName)" + ], + shortTitle: "Set Dimmer / Roller", + systemImageName: "slider.horizontal.3" + ) + AppShortcut( + intent: SetColorValueIntent(), + phrases: ["Set color light in \(.applicationName)"], + shortTitle: "Set Color", + systemImageName: "paintpalette" + ) + AppShortcut( + intent: SetNumberValueIntent(), + phrases: ["Set number value in \(.applicationName)"], + shortTitle: "Set Number", + systemImageName: "number" + ) + AppShortcut( + intent: SetPlayerValueIntent(), + phrases: ["Control player in \(.applicationName)"], + shortTitle: "Control Player", + systemImageName: "play.circle" + ) + AppShortcut( + intent: SetStringValueIntent(), + phrases: ["Set text value in \(.applicationName)"], + shortTitle: "Set Text", + systemImageName: "character.cursor.ibeam" + ) + AppShortcut( + intent: SetDateTimeValueIntent(), + phrases: ["Set date and time in \(.applicationName)"], + shortTitle: "Set Date & Time", + systemImageName: "calendar.badge.clock" + ) + AppShortcut( + intent: SetLocationValueIntent(), + phrases: ["Set location in \(.applicationName)"], + shortTitle: "Set Location", + systemImageName: "location" + ) + AppShortcut( + intent: ContactStateIntent(), + phrases: ["Set contact state in \(.applicationName)"], + shortTitle: "Set Contact State", + systemImageName: "sensor" + ) + } +} diff --git a/openHAB.xcodeproj/project.pbxproj b/openHAB.xcodeproj/project.pbxproj index 0b9673c79..37d8234f2 100644 --- a/openHAB.xcodeproj/project.pbxproj +++ b/openHAB.xcodeproj/project.pbxproj @@ -37,6 +37,7 @@ DA28C362225241DE00AB409C /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA28C361225241DE00AB409C /* WebKit.framework */; settings = {ATTRIBUTES = (Required, ); }; }; DA2C4FD52B4F573300D1C533 /* SDWebImageSVGCoder in Frameworks */ = {isa = PBXBuildFile; productRef = DA2C4FD42B4F573300D1C533 /* SDWebImageSVGCoder */; }; DA4D4DB5233F9ACB00B37E37 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = DA4D4DB4233F9ACB00B37E37 /* README.md */; }; + DA78C9202FBBA8A700A92C6E /* OpenHABShortcuts.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA78C91F2FBBA8A700A92C6E /* OpenHABShortcuts.swift */; }; DA817E7A234BF39B00C91824 /* CHANGELOG.md in Resources */ = {isa = PBXBuildFile; fileRef = DA817E79234BF39B00C91824 /* CHANGELOG.md */; }; DA9A7EFD2D668D5900824156 /* SFSafeSymbols in Frameworks */ = {isa = PBXBuildFile; productRef = DA9A7EFC2D668D5900824156 /* SFSafeSymbols */; }; DA9A7EFF2D66915900824156 /* SFSafeSymbols in Frameworks */ = {isa = PBXBuildFile; productRef = DA9A7EFE2D66915900824156 /* SFSafeSymbols */; }; @@ -210,6 +211,7 @@ DA4D4E0E2340A00200B37E37 /* Changes.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = Changes.md; sourceTree = ""; }; DA65871E236F83CD007E2E7F /* UserDefaultsExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsExtension.swift; sourceTree = ""; }; DA72E1B5236DEA0900B8EF3A /* AppMessageService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppMessageService.swift; sourceTree = ""; }; + DA78C91F2FBBA8A700A92C6E /* OpenHABShortcuts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenHABShortcuts.swift; sourceTree = ""; }; DA817E79234BF39B00C91824 /* CHANGELOG.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = ""; }; DA83C81D2F48AF7600CDACED /* Version.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Version.xcconfig; sourceTree = ""; }; DA8B14B52F3A0DFF007753FD /* WidgetRowFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetRowFactory.swift; sourceTree = ""; }; @@ -574,6 +576,42 @@ path = openHABTestsSwift; sourceTree = ""; }; + DA448E7E2EF435B400F0893C /* AppIntents */ = { + isa = PBXGroup; + children = ( + DA4BF3652F0419F30082479C /* Intents */, + DA4BF3532F0307D30082479C /* iOS16 */, + DAD5E85D2F02C3BC003215C0 /* ItemIdentifier.swift */, + DA4BF4042F06F5CA0082479C /* ContactState.swift */, + DAF58C7A2EF6E99500483AFD /* SwitchItemEntity.swift */, + DA4BF4352F07056F0082479C /* ItemEntityQuery.swift */, + DA4BF4332F0705580082479C /* ItemEntity.swift */, + DA4BF4022F06F5950082479C /* SwitchAction.swift */, + DA4BF4062F0800010082479C /* PlayerAction.swift */, + DA448E772EF435B400F0893C /* Home.swift */, + DA78C91F2FBBA8A700A92C6E /* OpenHABShortcuts.swift */, + ); + path = AppIntents; + sourceTree = ""; + }; + DA4BF3652F0419F30082479C /* Intents */ = { + isa = PBXGroup; + children = ( + DA4BF3542F03095D0082479C /* SetColorValueIntent.swift */, + DA4BF3552F03095D0082479C /* ContactStateIntent.swift */, + DA4BF3582F03098B0082479C /* SetDimmerRollerValueIntent.swift */, + DA4BF3512F0307A40082479C /* GetItemStateIntent.swift */, + DAD5E85B2F02C272003215C0 /* SetSwitchItemIntent.swift */, + DA4BF35C2F0309B10082479C /* SetStringValueIntent.swift */, + 1F77A157AAF1DD5AB45D6BA2 /* SetActiveHomeIntent.swift */, + DA4BF35A2F0309A60082479C /* SetNumberValueIntent.swift */, + DA4BF4082F0800020082479C /* SetPlayerValueIntent.swift */, + DA4BF40A2F0800030082479C /* SetDateTimeValueIntent.swift */, + DA4BF40C2F0800040082479C /* SetLocationValueIntent.swift */, + ); + path = Intents; + sourceTree = ""; + }; DA658720236F841F007E2E7F /* Views */ = { isa = PBXGroup; children = ( @@ -1009,6 +1047,15 @@ nl, ru, nb, + "en 2", + "de 2", + "es 2", + "fi 2", + "fr 2", + "it 2", + "nb 2", + "nl 2", + "ru 2", ); mainGroup = DFB2621E18830A3600D3244D; packageReferences = ( @@ -1194,6 +1241,26 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + DA448E852EF435B400F0893C /* Home.swift in Sources */, + DA4BF3522F0307A40082479C /* GetItemStateIntent.swift in Sources */, + DA4BF3562F03095D0082479C /* SetColorValueIntent.swift in Sources */, + DA4BF3572F03095D0082479C /* ContactStateIntent.swift in Sources */, + DA4BF3592F03098B0082479C /* SetDimmerRollerValueIntent.swift in Sources */, + DA4BF35B2F0309A60082479C /* SetNumberValueIntent.swift in Sources */, + DA4BF35D2F0309B10082479C /* SetStringValueIntent.swift in Sources */, + 772F724744CC8D15A673C246 /* SetActiveHomeIntent.swift in Sources */, + DA4BF4032F06F5950082479C /* SwitchAction.swift in Sources */, + DA4BF4052F06F5CA0082479C /* ContactState.swift in Sources */, + DA78C9202FBBA8A700A92C6E /* OpenHABShortcuts.swift in Sources */, + DA4BF4072F0800010082479C /* PlayerAction.swift in Sources */, + DA4BF4092F0800020082479C /* SetPlayerValueIntent.swift in Sources */, + DA4BF40B2F0800030082479C /* SetDateTimeValueIntent.swift in Sources */, + DA4BF40D2F0800040082479C /* SetLocationValueIntent.swift in Sources */, + DA4BF4342F0705580082479C /* ItemEntity.swift in Sources */, + DA4BF4362F07056F0082479C /* ItemEntityQuery.swift in Sources */, + DAD5E85C2F02C272003215C0 /* SetSwitchItemIntent.swift in Sources */, + DAD5E85E2F02C3C6003215C0 /* ItemIdentifier.swift in Sources */, + DAF58C7B2EF6E99500483AFD /* SwitchItemEntity.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/openHAB/Supporting Files/de.lproj/AppShortcuts.strings b/openHAB/Supporting Files/de.lproj/AppShortcuts.strings new file mode 100644 index 000000000..16935fec7 --- /dev/null +++ b/openHAB/Supporting Files/de.lproj/AppShortcuts.strings @@ -0,0 +1,12 @@ +"Set a switch in ${applicationName}" = "Schalter in ${applicationName} setzen"; +"Toggle a switch in ${applicationName}" = "Schalter in ${applicationName} umschalten"; +"Get item state in ${applicationName}" = "Elementstatus in ${applicationName} abrufen"; +"Set dimmer in ${applicationName}" = "Dimmer in ${applicationName} einstellen"; +"Set roller shutter in ${applicationName}" = "Rolladen in ${applicationName} einstellen"; +"Set color light in ${applicationName}" = "Farblampe in ${applicationName} einstellen"; +"Set number value in ${applicationName}" = "Zahlenwert in ${applicationName} setzen"; +"Control player in ${applicationName}" = "Player in ${applicationName} steuern"; +"Set text value in ${applicationName}" = "Textwert in ${applicationName} setzen"; +"Set date and time in ${applicationName}" = "Datum und Uhrzeit in ${applicationName} setzen"; +"Set location in ${applicationName}" = "Ort in ${applicationName} setzen"; +"Set contact state in ${applicationName}" = "Kontaktstatus in ${applicationName} setzen"; diff --git a/openHAB/Supporting Files/en.lproj/AppShortcuts.strings b/openHAB/Supporting Files/en.lproj/AppShortcuts.strings new file mode 100644 index 000000000..a1d61aa45 --- /dev/null +++ b/openHAB/Supporting Files/en.lproj/AppShortcuts.strings @@ -0,0 +1,12 @@ +"Set a switch in ${applicationName}" = "Set a switch in ${applicationName}"; +"Toggle a switch in ${applicationName}" = "Toggle a switch in ${applicationName}"; +"Get item state in ${applicationName}" = "Get item state in ${applicationName}"; +"Set dimmer in ${applicationName}" = "Set dimmer in ${applicationName}"; +"Set roller shutter in ${applicationName}" = "Set roller shutter in ${applicationName}"; +"Set color light in ${applicationName}" = "Set color light in ${applicationName}"; +"Set number value in ${applicationName}" = "Set number value in ${applicationName}"; +"Control player in ${applicationName}" = "Control player in ${applicationName}"; +"Set text value in ${applicationName}" = "Set text value in ${applicationName}"; +"Set date and time in ${applicationName}" = "Set date and time in ${applicationName}"; +"Set location in ${applicationName}" = "Set location in ${applicationName}"; +"Set contact state in ${applicationName}" = "Set contact state in ${applicationName}"; diff --git a/openHAB/Supporting Files/es.lproj/AppShortcuts.strings b/openHAB/Supporting Files/es.lproj/AppShortcuts.strings new file mode 100644 index 000000000..dd48f58b9 --- /dev/null +++ b/openHAB/Supporting Files/es.lproj/AppShortcuts.strings @@ -0,0 +1,12 @@ +"Set a switch in ${applicationName}" = "Configurar un interruptor en ${applicationName}"; +"Toggle a switch in ${applicationName}" = "Alternar un interruptor en ${applicationName}"; +"Get item state in ${applicationName}" = "Obtener el estado del elemento en ${applicationName}"; +"Set dimmer in ${applicationName}" = "Configurar el regulador en ${applicationName}"; +"Set roller shutter in ${applicationName}" = "Configurar la persiana en ${applicationName}"; +"Set color light in ${applicationName}" = "Configurar la luz de color en ${applicationName}"; +"Set number value in ${applicationName}" = "Establecer un valor numérico en ${applicationName}"; +"Control player in ${applicationName}" = "Controlar el reproductor en ${applicationName}"; +"Set text value in ${applicationName}" = "Establecer un valor de texto en ${applicationName}"; +"Set date and time in ${applicationName}" = "Establecer fecha y hora en ${applicationName}"; +"Set location in ${applicationName}" = "Establecer ubicación en ${applicationName}"; +"Set contact state in ${applicationName}" = "Establecer estado de contacto en ${applicationName}"; diff --git a/openHAB/Supporting Files/fi.lproj/AppShortcuts.strings b/openHAB/Supporting Files/fi.lproj/AppShortcuts.strings new file mode 100644 index 000000000..35b9fcd75 --- /dev/null +++ b/openHAB/Supporting Files/fi.lproj/AppShortcuts.strings @@ -0,0 +1,12 @@ +"Set a switch in ${applicationName}" = "Aseta kytkin ${applicationName}-sovelluksessa"; +"Toggle a switch in ${applicationName}" = "Vaihda kytkimen tila ${applicationName}-sovelluksessa"; +"Get item state in ${applicationName}" = "Hae kohteen tila ${applicationName}-sovelluksessa"; +"Set dimmer in ${applicationName}" = "Aseta himmentimen arvo ${applicationName}-sovelluksessa"; +"Set roller shutter in ${applicationName}" = "Aseta rullakaihdin ${applicationName}-sovelluksessa"; +"Set color light in ${applicationName}" = "Aseta värivalaisin ${applicationName}-sovelluksessa"; +"Set number value in ${applicationName}" = "Aseta lukuarvo ${applicationName}-sovelluksessa"; +"Control player in ${applicationName}" = "Ohjaa soitinta ${applicationName}-sovelluksessa"; +"Set text value in ${applicationName}" = "Aseta tekstiarvo ${applicationName}-sovelluksessa"; +"Set date and time in ${applicationName}" = "Aseta päivämäärä ja aika ${applicationName}-sovelluksessa"; +"Set location in ${applicationName}" = "Aseta sijainti ${applicationName}-sovelluksessa"; +"Set contact state in ${applicationName}" = "Aseta kontaktin tila ${applicationName}-sovelluksessa"; diff --git a/openHAB/Supporting Files/fr.lproj/AppShortcuts.strings b/openHAB/Supporting Files/fr.lproj/AppShortcuts.strings new file mode 100644 index 000000000..c1d66a9b2 --- /dev/null +++ b/openHAB/Supporting Files/fr.lproj/AppShortcuts.strings @@ -0,0 +1,12 @@ +"Set a switch in ${applicationName}" = "Définir un interrupteur dans ${applicationName}"; +"Toggle a switch in ${applicationName}" = "Basculer un interrupteur dans ${applicationName}"; +"Get item state in ${applicationName}" = "Obtenir l'état d'un élément dans ${applicationName}"; +"Set dimmer in ${applicationName}" = "Régler le variateur dans ${applicationName}"; +"Set roller shutter in ${applicationName}" = "Régler le volet roulant dans ${applicationName}"; +"Set color light in ${applicationName}" = "Régler la lumière de couleur dans ${applicationName}"; +"Set number value in ${applicationName}" = "Définir une valeur numérique dans ${applicationName}"; +"Control player in ${applicationName}" = "Contrôler le lecteur dans ${applicationName}"; +"Set text value in ${applicationName}" = "Définir une valeur texte dans ${applicationName}"; +"Set date and time in ${applicationName}" = "Définir la date et l'heure dans ${applicationName}"; +"Set location in ${applicationName}" = "Définir l'emplacement dans ${applicationName}"; +"Set contact state in ${applicationName}" = "Définir l'état du contact dans ${applicationName}"; diff --git a/openHAB/Supporting Files/it.lproj/AppShortcuts.strings b/openHAB/Supporting Files/it.lproj/AppShortcuts.strings new file mode 100644 index 000000000..e345785e2 --- /dev/null +++ b/openHAB/Supporting Files/it.lproj/AppShortcuts.strings @@ -0,0 +1,12 @@ +"Set a switch in ${applicationName}" = "Imposta un interruttore in ${applicationName}"; +"Toggle a switch in ${applicationName}" = "Attiva o disattiva un interruttore in ${applicationName}"; +"Get item state in ${applicationName}" = "Ottieni lo stato di un elemento in ${applicationName}"; +"Set dimmer in ${applicationName}" = "Imposta il dimmer in ${applicationName}"; +"Set roller shutter in ${applicationName}" = "Imposta la tapparella in ${applicationName}"; +"Set color light in ${applicationName}" = "Imposta la luce a colori in ${applicationName}"; +"Set number value in ${applicationName}" = "Imposta un valore numerico in ${applicationName}"; +"Control player in ${applicationName}" = "Controlla il lettore in ${applicationName}"; +"Set text value in ${applicationName}" = "Imposta un valore di testo in ${applicationName}"; +"Set date and time in ${applicationName}" = "Imposta data e ora in ${applicationName}"; +"Set location in ${applicationName}" = "Imposta la posizione in ${applicationName}"; +"Set contact state in ${applicationName}" = "Imposta lo stato del contatto in ${applicationName}"; diff --git a/openHAB/Supporting Files/nb.lproj/AppShortcuts.strings b/openHAB/Supporting Files/nb.lproj/AppShortcuts.strings new file mode 100644 index 000000000..e1131cc02 --- /dev/null +++ b/openHAB/Supporting Files/nb.lproj/AppShortcuts.strings @@ -0,0 +1,12 @@ +"Set a switch in ${applicationName}" = "Angi en bryter i ${applicationName}"; +"Toggle a switch in ${applicationName}" = "Veksle en bryter i ${applicationName}"; +"Get item state in ${applicationName}" = "Hent elementstatus i ${applicationName}"; +"Set dimmer in ${applicationName}" = "Angi dimmer i ${applicationName}"; +"Set roller shutter in ${applicationName}" = "Angi rullegardin i ${applicationName}"; +"Set color light in ${applicationName}" = "Angi fargelys i ${applicationName}"; +"Set number value in ${applicationName}" = "Angi tallverdi i ${applicationName}"; +"Control player in ${applicationName}" = "Kontroller avspiller i ${applicationName}"; +"Set text value in ${applicationName}" = "Angi tekstverdi i ${applicationName}"; +"Set date and time in ${applicationName}" = "Angi dato og klokkeslett i ${applicationName}"; +"Set location in ${applicationName}" = "Angi sted i ${applicationName}"; +"Set contact state in ${applicationName}" = "Angi kontaktstatus i ${applicationName}"; diff --git a/openHAB/Supporting Files/nl.lproj/AppShortcuts.strings b/openHAB/Supporting Files/nl.lproj/AppShortcuts.strings new file mode 100644 index 000000000..46e33804d --- /dev/null +++ b/openHAB/Supporting Files/nl.lproj/AppShortcuts.strings @@ -0,0 +1,12 @@ +"Set a switch in ${applicationName}" = "Schakelaar instellen in ${applicationName}"; +"Toggle a switch in ${applicationName}" = "Schakelaar omzetten in ${applicationName}"; +"Get item state in ${applicationName}" = "Elementstatus ophalen in ${applicationName}"; +"Set dimmer in ${applicationName}" = "Dimmer instellen in ${applicationName}"; +"Set roller shutter in ${applicationName}" = "Rolluik instellen in ${applicationName}"; +"Set color light in ${applicationName}" = "Kleurlamp instellen in ${applicationName}"; +"Set number value in ${applicationName}" = "Getal instellen in ${applicationName}"; +"Control player in ${applicationName}" = "Speler bedienen in ${applicationName}"; +"Set text value in ${applicationName}" = "Tekstwaarde instellen in ${applicationName}"; +"Set date and time in ${applicationName}" = "Datum en tijd instellen in ${applicationName}"; +"Set location in ${applicationName}" = "Locatie instellen in ${applicationName}"; +"Set contact state in ${applicationName}" = "Contactstatus instellen in ${applicationName}"; diff --git a/openHAB/Supporting Files/ru.lproj/AppShortcuts.strings b/openHAB/Supporting Files/ru.lproj/AppShortcuts.strings new file mode 100644 index 000000000..ca36157b6 --- /dev/null +++ b/openHAB/Supporting Files/ru.lproj/AppShortcuts.strings @@ -0,0 +1,12 @@ +"Set a switch in ${applicationName}" = "Установить переключатель в ${applicationName}"; +"Toggle a switch in ${applicationName}" = "Переключить в ${applicationName}"; +"Get item state in ${applicationName}" = "Получить состояние элемента в ${applicationName}"; +"Set dimmer in ${applicationName}" = "Установить диммер в ${applicationName}"; +"Set roller shutter in ${applicationName}" = "Установить рольставни в ${applicationName}"; +"Set color light in ${applicationName}" = "Установить цветовой свет в ${applicationName}"; +"Set number value in ${applicationName}" = "Установить числовое значение в ${applicationName}"; +"Control player in ${applicationName}" = "Управлять плеером в ${applicationName}"; +"Set text value in ${applicationName}" = "Установить текстовое значение в ${applicationName}"; +"Set date and time in ${applicationName}" = "Установить дату и время в ${applicationName}"; +"Set location in ${applicationName}" = "Установить местоположение в ${applicationName}"; +"Set contact state in ${applicationName}" = "Установить состояние контакта в ${applicationName}"; From 94a59e56478cf6e28fb1fbfd9c46e8d5d3944295 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Wed, 27 May 2026 15:41:56 +0200 Subject: [PATCH 05/91] Fix AppIntentsMetadataProcessor synonym warnings in AppEnum cases AppIntentsMetadataProcessor builds an NLP vocabulary model for Siri using each AppEnum case's DisplayRepresentation(title:synonyms:). Without synonyms the processor allocates synonym slots in its model but finds them empty, emitting 'unresolvable: empty template: Failed # variables.N.definitions.M.synomyms.K' warnings for every empty slot across all locales. Add 3 synonyms per case to SwitchAction, ContactState, and PlayerAction. This both fills the NLP model slots and improves Siri's recognition of different ways a user might name a switch action, contact state, or player command. Signed-off-by: Tim Mueller-Seydlitz --- AppIntents/ContactState.swift | 4 ++-- AppIntents/PlayerAction.swift | 12 ++++++------ AppIntents/SwitchAction.swift | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/AppIntents/ContactState.swift b/AppIntents/ContactState.swift index e6ff99c7c..4da4b6850 100644 --- a/AppIntents/ContactState.swift +++ b/AppIntents/ContactState.swift @@ -20,8 +20,8 @@ enum ContactState: String, AppEnum { static let typeDisplayRepresentation = TypeDisplayRepresentation(name: "Contact State") static let caseDisplayRepresentations: [Self: DisplayRepresentation] = [ - .open: "Open", - .closed: "Closed" + .open: DisplayRepresentation(title: "Open", synonyms: ["Opened", "Triggered", "Active"]), + .closed: DisplayRepresentation(title: "Closed", synonyms: ["Shut", "Inactive", "Reset"]) ] } diff --git a/AppIntents/PlayerAction.swift b/AppIntents/PlayerAction.swift index 55eab750e..31ea6e22d 100644 --- a/AppIntents/PlayerAction.swift +++ b/AppIntents/PlayerAction.swift @@ -24,12 +24,12 @@ enum PlayerAction: String, AppEnum { static let typeDisplayRepresentation = TypeDisplayRepresentation(name: "Player Action") static let caseDisplayRepresentations: [Self: DisplayRepresentation] = [ - .play: "Play", - .pause: "Pause", - .next: "Next", - .previous: "Previous", - .rewind: "Rewind", - .fastforward: "Fast Forward" + .play: DisplayRepresentation(title: "Play", synonyms: ["Start", "Resume", "Begin"]), + .pause: DisplayRepresentation(title: "Pause", synonyms: ["Stop", "Hold", "Suspend"]), + .next: DisplayRepresentation(title: "Next", synonyms: ["Skip", "Skip forward", "Next track"]), + .previous: DisplayRepresentation(title: "Previous", synonyms: ["Back", "Skip back", "Previous track"]), + .rewind: DisplayRepresentation(title: "Rewind", synonyms: ["Rewind back", "Go backward", "Replay"]), + .fastforward: DisplayRepresentation(title: "Fast Forward", synonyms: ["Speed up", "Skip ahead", "FF"]) ] } diff --git a/AppIntents/SwitchAction.swift b/AppIntents/SwitchAction.swift index fe5cf4026..655e26e22 100644 --- a/AppIntents/SwitchAction.swift +++ b/AppIntents/SwitchAction.swift @@ -21,9 +21,9 @@ enum SwitchAction: String, AppEnum { static let typeDisplayRepresentation = TypeDisplayRepresentation(name: "Switch Action") static let caseDisplayRepresentations: [Self: DisplayRepresentation] = [ - .on: "On", - .off: "Off", - .toggle: "Toggle" + .on: DisplayRepresentation(title: "On", synonyms: ["Turn on", "Switch on", "Enable"]), + .off: DisplayRepresentation(title: "Off", synonyms: ["Turn off", "Switch off", "Disable"]), + .toggle: DisplayRepresentation(title: "Toggle", synonyms: ["Flip", "Switch", "Change"]) ] } From 317a4b48c75c167ab1cb050e14d036d1a2c8a250 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Wed, 27 May 2026 15:52:27 +0200 Subject: [PATCH 06/91] Convert AppShortcuts.strings to AppShortcuts.xcstrings String Catalog The AppIntentsMetadataProcessor in Xcode 15+ expects App Shortcuts phrase localizations in String Catalog format (AppShortcuts.xcstrings). Using individual lproj/AppShortcuts.strings files causes the processor to fail to generate NLP synonym data for most locales, producing repeated 'unresolvable: empty template: Failed # variables.N.definitions.M.synomyms.K' build warnings. Consolidate all 9 locale translations (de, en, es, fi, fr, it, nb, nl, ru) into a single AppShortcuts.xcstrings String Catalog and remove the individual .strings files. Signed-off-by: Tim Mueller-Seydlitz --- .../Supporting Files/AppShortcuts.xcstrings | 702 ++++++++++++++++++ .../de.lproj/AppShortcuts.strings | 12 - .../en.lproj/AppShortcuts.strings | 12 - .../es.lproj/AppShortcuts.strings | 12 - .../fi.lproj/AppShortcuts.strings | 12 - .../fr.lproj/AppShortcuts.strings | 12 - .../it.lproj/AppShortcuts.strings | 12 - .../nb.lproj/AppShortcuts.strings | 12 - .../nl.lproj/AppShortcuts.strings | 12 - .../ru.lproj/AppShortcuts.strings | 12 - 10 files changed, 702 insertions(+), 108 deletions(-) create mode 100644 openHAB/Supporting Files/AppShortcuts.xcstrings delete mode 100644 openHAB/Supporting Files/de.lproj/AppShortcuts.strings delete mode 100644 openHAB/Supporting Files/en.lproj/AppShortcuts.strings delete mode 100644 openHAB/Supporting Files/es.lproj/AppShortcuts.strings delete mode 100644 openHAB/Supporting Files/fi.lproj/AppShortcuts.strings delete mode 100644 openHAB/Supporting Files/fr.lproj/AppShortcuts.strings delete mode 100644 openHAB/Supporting Files/it.lproj/AppShortcuts.strings delete mode 100644 openHAB/Supporting Files/nb.lproj/AppShortcuts.strings delete mode 100644 openHAB/Supporting Files/nl.lproj/AppShortcuts.strings delete mode 100644 openHAB/Supporting Files/ru.lproj/AppShortcuts.strings diff --git a/openHAB/Supporting Files/AppShortcuts.xcstrings b/openHAB/Supporting Files/AppShortcuts.xcstrings new file mode 100644 index 000000000..faffb150d --- /dev/null +++ b/openHAB/Supporting Files/AppShortcuts.xcstrings @@ -0,0 +1,702 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "Control player in ${applicationName}" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Player in ${applicationName} steuern" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Control player in ${applicationName}" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Controlar el reproductor en ${applicationName}" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ohjaa soitinta ${applicationName}-sovelluksessa" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contrôler le lecteur dans ${applicationName}" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Controlla il lettore in ${applicationName}" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontroller avspiller i ${applicationName}" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Speler bedienen in ${applicationName}" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Управлять плеером в ${applicationName}" + } + } + } + }, + "Get item state in ${applicationName}" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elementstatus in ${applicationName} abrufen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Get item state in ${applicationName}" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Obtener el estado del elemento en ${applicationName}" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hae kohteen tila ${applicationName}-sovelluksessa" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Obtenir l'état d'un élément dans ${applicationName}" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ottieni lo stato di un elemento in ${applicationName}" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hent elementstatus i ${applicationName}" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elementstatus ophalen in ${applicationName}" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Получить состояние элемента в ${applicationName}" + } + } + } + }, + "Set a switch in ${applicationName}" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schalter in ${applicationName} setzen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set a switch in ${applicationName}" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configurar un interruptor en ${applicationName}" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta kytkin ${applicationName}-sovelluksessa" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir un interrupteur dans ${applicationName}" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta un interruttore in ${applicationName}" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi en bryter i ${applicationName}" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schakelaar instellen in ${applicationName}" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить переключатель в ${applicationName}" + } + } + } + }, + "Set color light in ${applicationName}" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Farblampe in ${applicationName} einstellen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set color light in ${applicationName}" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configurar la luz de color en ${applicationName}" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta värivalaisin ${applicationName}-sovelluksessa" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Régler la lumière de couleur dans ${applicationName}" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta la luce a colori in ${applicationName}" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi fargelys i ${applicationName}" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kleurlamp instellen in ${applicationName}" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить цветовой свет в ${applicationName}" + } + } + } + }, + "Set contact state in ${applicationName}" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontaktstatus in ${applicationName} setzen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set contact state in ${applicationName}" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer estado de contacto en ${applicationName}" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta kontaktin tila ${applicationName}-sovelluksessa" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir l'état du contact dans ${applicationName}" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta lo stato del contatto in ${applicationName}" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi kontaktstatus i ${applicationName}" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contactstatus instellen in ${applicationName}" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить состояние контакта в ${applicationName}" + } + } + } + }, + "Set date and time in ${applicationName}" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datum und Uhrzeit in ${applicationName} setzen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set date and time in ${applicationName}" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer fecha y hora en ${applicationName}" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta päivämäärä ja aika ${applicationName}-sovelluksessa" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la date et l'heure dans ${applicationName}" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta data e ora in ${applicationName}" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi dato og klokkeslett i ${applicationName}" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datum en tijd instellen in ${applicationName}" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить дату и время в ${applicationName}" + } + } + } + }, + "Set dimmer in ${applicationName}" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dimmer in ${applicationName} einstellen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set dimmer in ${applicationName}" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configurar el regulador en ${applicationName}" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta himmentimen arvo ${applicationName}-sovelluksessa" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Régler le variateur dans ${applicationName}" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta il dimmer in ${applicationName}" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi dimmer i ${applicationName}" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dimmer instellen in ${applicationName}" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить диммер в ${applicationName}" + } + } + } + }, + "Set location in ${applicationName}" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ort in ${applicationName} setzen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set location in ${applicationName}" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer ubicación en ${applicationName}" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta sijainti ${applicationName}-sovelluksessa" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir l'emplacement dans ${applicationName}" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta la posizione in ${applicationName}" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi sted i ${applicationName}" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Locatie instellen in ${applicationName}" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить местоположение в ${applicationName}" + } + } + } + }, + "Set number value in ${applicationName}" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zahlenwert in ${applicationName} setzen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set number value in ${applicationName}" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer un valor numérico en ${applicationName}" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta lukuarvo ${applicationName}-sovelluksessa" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir une valeur numérique dans ${applicationName}" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta un valore numerico in ${applicationName}" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi tallverdi i ${applicationName}" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Getal instellen in ${applicationName}" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить числовое значение в ${applicationName}" + } + } + } + }, + "Set roller shutter in ${applicationName}" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rolladen in ${applicationName} einstellen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set roller shutter in ${applicationName}" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configurar la persiana en ${applicationName}" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta rullakaihdin ${applicationName}-sovelluksessa" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Régler le volet roulant dans ${applicationName}" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta la tapparella in ${applicationName}" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi rullegardin i ${applicationName}" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rolluik instellen in ${applicationName}" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить рольставни в ${applicationName}" + } + } + } + }, + "Set text value in ${applicationName}" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Textwert in ${applicationName} setzen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set text value in ${applicationName}" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer un valor de texto en ${applicationName}" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta tekstiarvo ${applicationName}-sovelluksessa" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir une valeur texte dans ${applicationName}" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta un valore di testo in ${applicationName}" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi tekstverdi i ${applicationName}" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tekstwaarde instellen in ${applicationName}" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить текстовое значение в ${applicationName}" + } + } + } + }, + "Toggle a switch in ${applicationName}" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schalter in ${applicationName} umschalten" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toggle a switch in ${applicationName}" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alternar un interruptor en ${applicationName}" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vaihda kytkimen tila ${applicationName}-sovelluksessa" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basculer un interrupteur dans ${applicationName}" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Attiva o disattiva un interruttore in ${applicationName}" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Veksle en bryter i ${applicationName}" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schakelaar omzetten in ${applicationName}" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Переключить в ${applicationName}" + } + } + } + } + }, + "version" : "1.0" +} diff --git a/openHAB/Supporting Files/de.lproj/AppShortcuts.strings b/openHAB/Supporting Files/de.lproj/AppShortcuts.strings deleted file mode 100644 index 16935fec7..000000000 --- a/openHAB/Supporting Files/de.lproj/AppShortcuts.strings +++ /dev/null @@ -1,12 +0,0 @@ -"Set a switch in ${applicationName}" = "Schalter in ${applicationName} setzen"; -"Toggle a switch in ${applicationName}" = "Schalter in ${applicationName} umschalten"; -"Get item state in ${applicationName}" = "Elementstatus in ${applicationName} abrufen"; -"Set dimmer in ${applicationName}" = "Dimmer in ${applicationName} einstellen"; -"Set roller shutter in ${applicationName}" = "Rolladen in ${applicationName} einstellen"; -"Set color light in ${applicationName}" = "Farblampe in ${applicationName} einstellen"; -"Set number value in ${applicationName}" = "Zahlenwert in ${applicationName} setzen"; -"Control player in ${applicationName}" = "Player in ${applicationName} steuern"; -"Set text value in ${applicationName}" = "Textwert in ${applicationName} setzen"; -"Set date and time in ${applicationName}" = "Datum und Uhrzeit in ${applicationName} setzen"; -"Set location in ${applicationName}" = "Ort in ${applicationName} setzen"; -"Set contact state in ${applicationName}" = "Kontaktstatus in ${applicationName} setzen"; diff --git a/openHAB/Supporting Files/en.lproj/AppShortcuts.strings b/openHAB/Supporting Files/en.lproj/AppShortcuts.strings deleted file mode 100644 index a1d61aa45..000000000 --- a/openHAB/Supporting Files/en.lproj/AppShortcuts.strings +++ /dev/null @@ -1,12 +0,0 @@ -"Set a switch in ${applicationName}" = "Set a switch in ${applicationName}"; -"Toggle a switch in ${applicationName}" = "Toggle a switch in ${applicationName}"; -"Get item state in ${applicationName}" = "Get item state in ${applicationName}"; -"Set dimmer in ${applicationName}" = "Set dimmer in ${applicationName}"; -"Set roller shutter in ${applicationName}" = "Set roller shutter in ${applicationName}"; -"Set color light in ${applicationName}" = "Set color light in ${applicationName}"; -"Set number value in ${applicationName}" = "Set number value in ${applicationName}"; -"Control player in ${applicationName}" = "Control player in ${applicationName}"; -"Set text value in ${applicationName}" = "Set text value in ${applicationName}"; -"Set date and time in ${applicationName}" = "Set date and time in ${applicationName}"; -"Set location in ${applicationName}" = "Set location in ${applicationName}"; -"Set contact state in ${applicationName}" = "Set contact state in ${applicationName}"; diff --git a/openHAB/Supporting Files/es.lproj/AppShortcuts.strings b/openHAB/Supporting Files/es.lproj/AppShortcuts.strings deleted file mode 100644 index dd48f58b9..000000000 --- a/openHAB/Supporting Files/es.lproj/AppShortcuts.strings +++ /dev/null @@ -1,12 +0,0 @@ -"Set a switch in ${applicationName}" = "Configurar un interruptor en ${applicationName}"; -"Toggle a switch in ${applicationName}" = "Alternar un interruptor en ${applicationName}"; -"Get item state in ${applicationName}" = "Obtener el estado del elemento en ${applicationName}"; -"Set dimmer in ${applicationName}" = "Configurar el regulador en ${applicationName}"; -"Set roller shutter in ${applicationName}" = "Configurar la persiana en ${applicationName}"; -"Set color light in ${applicationName}" = "Configurar la luz de color en ${applicationName}"; -"Set number value in ${applicationName}" = "Establecer un valor numérico en ${applicationName}"; -"Control player in ${applicationName}" = "Controlar el reproductor en ${applicationName}"; -"Set text value in ${applicationName}" = "Establecer un valor de texto en ${applicationName}"; -"Set date and time in ${applicationName}" = "Establecer fecha y hora en ${applicationName}"; -"Set location in ${applicationName}" = "Establecer ubicación en ${applicationName}"; -"Set contact state in ${applicationName}" = "Establecer estado de contacto en ${applicationName}"; diff --git a/openHAB/Supporting Files/fi.lproj/AppShortcuts.strings b/openHAB/Supporting Files/fi.lproj/AppShortcuts.strings deleted file mode 100644 index 35b9fcd75..000000000 --- a/openHAB/Supporting Files/fi.lproj/AppShortcuts.strings +++ /dev/null @@ -1,12 +0,0 @@ -"Set a switch in ${applicationName}" = "Aseta kytkin ${applicationName}-sovelluksessa"; -"Toggle a switch in ${applicationName}" = "Vaihda kytkimen tila ${applicationName}-sovelluksessa"; -"Get item state in ${applicationName}" = "Hae kohteen tila ${applicationName}-sovelluksessa"; -"Set dimmer in ${applicationName}" = "Aseta himmentimen arvo ${applicationName}-sovelluksessa"; -"Set roller shutter in ${applicationName}" = "Aseta rullakaihdin ${applicationName}-sovelluksessa"; -"Set color light in ${applicationName}" = "Aseta värivalaisin ${applicationName}-sovelluksessa"; -"Set number value in ${applicationName}" = "Aseta lukuarvo ${applicationName}-sovelluksessa"; -"Control player in ${applicationName}" = "Ohjaa soitinta ${applicationName}-sovelluksessa"; -"Set text value in ${applicationName}" = "Aseta tekstiarvo ${applicationName}-sovelluksessa"; -"Set date and time in ${applicationName}" = "Aseta päivämäärä ja aika ${applicationName}-sovelluksessa"; -"Set location in ${applicationName}" = "Aseta sijainti ${applicationName}-sovelluksessa"; -"Set contact state in ${applicationName}" = "Aseta kontaktin tila ${applicationName}-sovelluksessa"; diff --git a/openHAB/Supporting Files/fr.lproj/AppShortcuts.strings b/openHAB/Supporting Files/fr.lproj/AppShortcuts.strings deleted file mode 100644 index c1d66a9b2..000000000 --- a/openHAB/Supporting Files/fr.lproj/AppShortcuts.strings +++ /dev/null @@ -1,12 +0,0 @@ -"Set a switch in ${applicationName}" = "Définir un interrupteur dans ${applicationName}"; -"Toggle a switch in ${applicationName}" = "Basculer un interrupteur dans ${applicationName}"; -"Get item state in ${applicationName}" = "Obtenir l'état d'un élément dans ${applicationName}"; -"Set dimmer in ${applicationName}" = "Régler le variateur dans ${applicationName}"; -"Set roller shutter in ${applicationName}" = "Régler le volet roulant dans ${applicationName}"; -"Set color light in ${applicationName}" = "Régler la lumière de couleur dans ${applicationName}"; -"Set number value in ${applicationName}" = "Définir une valeur numérique dans ${applicationName}"; -"Control player in ${applicationName}" = "Contrôler le lecteur dans ${applicationName}"; -"Set text value in ${applicationName}" = "Définir une valeur texte dans ${applicationName}"; -"Set date and time in ${applicationName}" = "Définir la date et l'heure dans ${applicationName}"; -"Set location in ${applicationName}" = "Définir l'emplacement dans ${applicationName}"; -"Set contact state in ${applicationName}" = "Définir l'état du contact dans ${applicationName}"; diff --git a/openHAB/Supporting Files/it.lproj/AppShortcuts.strings b/openHAB/Supporting Files/it.lproj/AppShortcuts.strings deleted file mode 100644 index e345785e2..000000000 --- a/openHAB/Supporting Files/it.lproj/AppShortcuts.strings +++ /dev/null @@ -1,12 +0,0 @@ -"Set a switch in ${applicationName}" = "Imposta un interruttore in ${applicationName}"; -"Toggle a switch in ${applicationName}" = "Attiva o disattiva un interruttore in ${applicationName}"; -"Get item state in ${applicationName}" = "Ottieni lo stato di un elemento in ${applicationName}"; -"Set dimmer in ${applicationName}" = "Imposta il dimmer in ${applicationName}"; -"Set roller shutter in ${applicationName}" = "Imposta la tapparella in ${applicationName}"; -"Set color light in ${applicationName}" = "Imposta la luce a colori in ${applicationName}"; -"Set number value in ${applicationName}" = "Imposta un valore numerico in ${applicationName}"; -"Control player in ${applicationName}" = "Controlla il lettore in ${applicationName}"; -"Set text value in ${applicationName}" = "Imposta un valore di testo in ${applicationName}"; -"Set date and time in ${applicationName}" = "Imposta data e ora in ${applicationName}"; -"Set location in ${applicationName}" = "Imposta la posizione in ${applicationName}"; -"Set contact state in ${applicationName}" = "Imposta lo stato del contatto in ${applicationName}"; diff --git a/openHAB/Supporting Files/nb.lproj/AppShortcuts.strings b/openHAB/Supporting Files/nb.lproj/AppShortcuts.strings deleted file mode 100644 index e1131cc02..000000000 --- a/openHAB/Supporting Files/nb.lproj/AppShortcuts.strings +++ /dev/null @@ -1,12 +0,0 @@ -"Set a switch in ${applicationName}" = "Angi en bryter i ${applicationName}"; -"Toggle a switch in ${applicationName}" = "Veksle en bryter i ${applicationName}"; -"Get item state in ${applicationName}" = "Hent elementstatus i ${applicationName}"; -"Set dimmer in ${applicationName}" = "Angi dimmer i ${applicationName}"; -"Set roller shutter in ${applicationName}" = "Angi rullegardin i ${applicationName}"; -"Set color light in ${applicationName}" = "Angi fargelys i ${applicationName}"; -"Set number value in ${applicationName}" = "Angi tallverdi i ${applicationName}"; -"Control player in ${applicationName}" = "Kontroller avspiller i ${applicationName}"; -"Set text value in ${applicationName}" = "Angi tekstverdi i ${applicationName}"; -"Set date and time in ${applicationName}" = "Angi dato og klokkeslett i ${applicationName}"; -"Set location in ${applicationName}" = "Angi sted i ${applicationName}"; -"Set contact state in ${applicationName}" = "Angi kontaktstatus i ${applicationName}"; diff --git a/openHAB/Supporting Files/nl.lproj/AppShortcuts.strings b/openHAB/Supporting Files/nl.lproj/AppShortcuts.strings deleted file mode 100644 index 46e33804d..000000000 --- a/openHAB/Supporting Files/nl.lproj/AppShortcuts.strings +++ /dev/null @@ -1,12 +0,0 @@ -"Set a switch in ${applicationName}" = "Schakelaar instellen in ${applicationName}"; -"Toggle a switch in ${applicationName}" = "Schakelaar omzetten in ${applicationName}"; -"Get item state in ${applicationName}" = "Elementstatus ophalen in ${applicationName}"; -"Set dimmer in ${applicationName}" = "Dimmer instellen in ${applicationName}"; -"Set roller shutter in ${applicationName}" = "Rolluik instellen in ${applicationName}"; -"Set color light in ${applicationName}" = "Kleurlamp instellen in ${applicationName}"; -"Set number value in ${applicationName}" = "Getal instellen in ${applicationName}"; -"Control player in ${applicationName}" = "Speler bedienen in ${applicationName}"; -"Set text value in ${applicationName}" = "Tekstwaarde instellen in ${applicationName}"; -"Set date and time in ${applicationName}" = "Datum en tijd instellen in ${applicationName}"; -"Set location in ${applicationName}" = "Locatie instellen in ${applicationName}"; -"Set contact state in ${applicationName}" = "Contactstatus instellen in ${applicationName}"; diff --git a/openHAB/Supporting Files/ru.lproj/AppShortcuts.strings b/openHAB/Supporting Files/ru.lproj/AppShortcuts.strings deleted file mode 100644 index ca36157b6..000000000 --- a/openHAB/Supporting Files/ru.lproj/AppShortcuts.strings +++ /dev/null @@ -1,12 +0,0 @@ -"Set a switch in ${applicationName}" = "Установить переключатель в ${applicationName}"; -"Toggle a switch in ${applicationName}" = "Переключить в ${applicationName}"; -"Get item state in ${applicationName}" = "Получить состояние элемента в ${applicationName}"; -"Set dimmer in ${applicationName}" = "Установить диммер в ${applicationName}"; -"Set roller shutter in ${applicationName}" = "Установить рольставни в ${applicationName}"; -"Set color light in ${applicationName}" = "Установить цветовой свет в ${applicationName}"; -"Set number value in ${applicationName}" = "Установить числовое значение в ${applicationName}"; -"Control player in ${applicationName}" = "Управлять плеером в ${applicationName}"; -"Set text value in ${applicationName}" = "Установить текстовое значение в ${applicationName}"; -"Set date and time in ${applicationName}" = "Установить дату и время в ${applicationName}"; -"Set location in ${applicationName}" = "Установить местоположение в ${applicationName}"; -"Set contact state in ${applicationName}" = "Установить состояние контакта в ${applicationName}"; From 77d579e62d4550a8ccd1611dc4edc84099203dd9 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Wed, 27 May 2026 16:16:08 +0200 Subject: [PATCH 07/91] Bump deployment targets to iOS 17.0 / watchOS 10.0 AppShortcuts.xcstrings requires iOS 17.0+. Xcode set the minimum to 17.6 (latest iOS 17.x in the SDK) and watchOS to 10.6 accordingly. Also includes Xcode-generated Localizable.xcstrings additions from AppEnum caseDisplayRepresentations synonyms (extracted automatically from d917708b), and extractionState markers on secondary phrases in multi-phrase App Shortcuts (Xcode extraction limitation, runtime behaviour unchanged). Signed-off-by: Tim Mueller-Seydlitz --- openHAB.xcodeproj/project.pbxproj | 37 ++++ .../Supporting Files/AppShortcuts.xcstrings | 4 +- .../Supporting Files/Localizable.xcstrings | 173 ++++++++++++++++++ 3 files changed, 213 insertions(+), 1 deletion(-) diff --git a/openHAB.xcodeproj/project.pbxproj b/openHAB.xcodeproj/project.pbxproj index 37d8234f2..0be02d593 100644 --- a/openHAB.xcodeproj/project.pbxproj +++ b/openHAB.xcodeproj/project.pbxproj @@ -10,11 +10,22 @@ 2AE557B26AF10D16E65EB541 /* openHABWidgetExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 396B3EC10279D6D55CD888E8 /* openHABWidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 2F399AC92F54599400F72A30 /* Flow in Frameworks */ = {isa = PBXBuildFile; productRef = 2F399AC82F54599400F72A30 /* Flow */; }; 5111C4CC62E66D2FFA5C92EB /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 06A8490213945021806A13D1 /* SwiftUI.framework */; }; + 0648F50178784F149905E7CF /* PlayerAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4062F0800010082479C /* PlayerAction.swift */; }; + 1C1CA8C8C8424BAF8018F9BE /* SetStringValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF35C2F0309B10082479C /* SetStringValueIntent.swift */; }; + 1E4E7DB36BAC4804AD5E58E5 /* SetColorValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3542F03095D0082479C /* SetColorValueIntent.swift */; }; + 2F399AC92F54599400F72A30 /* Flow in Frameworks */ = {isa = PBXBuildFile; productRef = 2F399AC82F54599400F72A30 /* Flow */; }; + 3D3B2898C51F48BAA31210E4 /* ContactStateIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3552F03095D0082479C /* ContactStateIntent.swift */; }; + 3F087E4025E845FFBCBE45E3 /* ItemEntityQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4352F07056F0082479C /* ItemEntityQuery.swift */; }; + 598208789AC44F1D91DD5B17 /* SetPlayerValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4082F0800020082479C /* SetPlayerValueIntent.swift */; }; + 5B292637B6E141DB98BCAA26 /* Home.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA448E772EF435B400F0893C /* Home.swift */; }; + 61A606E5E6AE404584B0B1A0 /* SetNumberValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF35A2F0309A60082479C /* SetNumberValueIntent.swift */; }; 6557AF8F2C0241C10094D0C8 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 6557AF8E2C0241C10094D0C8 /* PrivacyInfo.xcprivacy */; }; 6557AF902C0241C10094D0C8 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 6557AF8E2C0241C10094D0C8 /* PrivacyInfo.xcprivacy */; }; 6557AF922C039D140094D0C8 /* FirebaseMessaging in Frameworks */ = {isa = PBXBuildFile; productRef = 6557AF912C039D140094D0C8 /* FirebaseMessaging */; }; 657144552C1E438700C8A1F3 /* NotificationService.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 6571444E2C1E438700C8A1F3 /* NotificationService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 657144962C30A16700C8A1F3 /* OpenHABCore in Frameworks */ = {isa = PBXBuildFile; productRef = 657144952C30A16700C8A1F3 /* OpenHABCore */; }; + 73F094A3C17641738D6215CD /* SetSwitchItemIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD5E85B2F02C272003215C0 /* SetSwitchItemIntent.swift */; }; + 791AFA47B71D4B6995369F3A /* GetItemStateIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3512F0307A40082479C /* GetItemStateIntent.swift */; }; 934E592528F16EBA00162004 /* OpenHABCore in Frameworks */ = {isa = PBXBuildFile; productRef = 934E592428F16EBA00162004 /* OpenHABCore */; }; 934E592728F16EBA00162004 /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 934E592628F16EBA00162004 /* Kingfisher */; }; 934E592928F16EBA00162004 /* DeviceKit in Frameworks */ = {isa = PBXBuildFile; productRef = 934E592828F16EBA00162004 /* DeviceKit */; }; @@ -33,6 +44,13 @@ B76C3BEEBF1F4730710AAB6E /* SDWebImageSVGCoder in Frameworks */ = {isa = PBXBuildFile; productRef = E92D1BEE08AD7DCB6F993340 /* SDWebImageSVGCoder */; }; C314F9C9CB55D0AA25E4BB0A /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0F298FB19C2A149258FE7CC6 /* WidgetKit.framework */; }; C380A8DFA36E43D49400A87E /* SFSafeSymbols in Frameworks */ = {isa = PBXBuildFile; productRef = 1F9D3602163D4BEBB384DDEE /* SFSafeSymbols */; }; + 970584BAA2884C8287297BF6 /* SwitchItemEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAF58C7A2EF6E99500483AFD /* SwitchItemEntity.swift */; }; + 9F54768418A14674AD9E2FCA /* ContactState.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4042F06F5CA0082479C /* ContactState.swift */; }; + B2FC662F25B2461887BB67DA /* ItemEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4332F0705580082479C /* ItemEntity.swift */; }; + BF0EB6DE70934966B1DC5479 /* SetDateTimeValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF40A2F0800030082479C /* SetDateTimeValueIntent.swift */; }; + C9DE79C32AF24FFEA0280115 /* ItemIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD5E85D2F02C3BC003215C0 /* ItemIdentifier.swift */; }; + CE7A58C0ED3A4A1ABCB20337 /* SetLocationValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF40C2F0800040082479C /* SetLocationValueIntent.swift */; }; + CF650A22059A4A00823EE656 /* SwitchAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4022F06F5950082479C /* SwitchAction.swift */; }; DA10161B2DC7BAE500552D14 /* SFSafeSymbols in Frameworks */ = {isa = PBXBuildFile; productRef = DA10161A2DC7BAE500552D14 /* SFSafeSymbols */; }; DA28C362225241DE00AB409C /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA28C361225241DE00AB409C /* WebKit.framework */; settings = {ATTRIBUTES = (Required, ); }; }; DA2C4FD52B4F573300D1C533 /* SDWebImageSVGCoder in Frameworks */ = {isa = PBXBuildFile; productRef = DA2C4FD42B4F573300D1C533 /* SDWebImageSVGCoder */; }; @@ -52,6 +70,7 @@ DFB2622F18830A3600D3244D /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DFB2622E18830A3600D3244D /* UIKit.framework */; }; DFE10414197415F900D94943 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DFE10413197415F900D94943 /* Security.framework */; }; EE6B566C271031A1EB5780D2 /* OpenHABCore in Frameworks */ = {isa = PBXBuildFile; productRef = 2247FFEA7206BFFC71AB02C4 /* OpenHABCore */; }; + E120F4A14B9942449F25E788 /* SetDimmerRollerValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3582F03098B0082479C /* SetDimmerRollerValueIntent.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -1199,6 +1218,24 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9F54768418A14674AD9E2FCA /* ContactState.swift in Sources */, + 3D3B2898C51F48BAA31210E4 /* ContactStateIntent.swift in Sources */, + 791AFA47B71D4B6995369F3A /* GetItemStateIntent.swift in Sources */, + 5B292637B6E141DB98BCAA26 /* Home.swift in Sources */, + B2FC662F25B2461887BB67DA /* ItemEntity.swift in Sources */, + 3F087E4025E845FFBCBE45E3 /* ItemEntityQuery.swift in Sources */, + C9DE79C32AF24FFEA0280115 /* ItemIdentifier.swift in Sources */, + 0648F50178784F149905E7CF /* PlayerAction.swift in Sources */, + 1E4E7DB36BAC4804AD5E58E5 /* SetColorValueIntent.swift in Sources */, + BF0EB6DE70934966B1DC5479 /* SetDateTimeValueIntent.swift in Sources */, + E120F4A14B9942449F25E788 /* SetDimmerRollerValueIntent.swift in Sources */, + CE7A58C0ED3A4A1ABCB20337 /* SetLocationValueIntent.swift in Sources */, + 61A606E5E6AE404584B0B1A0 /* SetNumberValueIntent.swift in Sources */, + 598208789AC44F1D91DD5B17 /* SetPlayerValueIntent.swift in Sources */, + 1C1CA8C8C8424BAF8018F9BE /* SetStringValueIntent.swift in Sources */, + 73F094A3C17641738D6215CD /* SetSwitchItemIntent.swift in Sources */, + CF650A22059A4A00823EE656 /* SwitchAction.swift in Sources */, + 970584BAA2884C8287297BF6 /* SwitchItemEntity.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/openHAB/Supporting Files/AppShortcuts.xcstrings b/openHAB/Supporting Files/AppShortcuts.xcstrings index faffb150d..8457e4881 100644 --- a/openHAB/Supporting Files/AppShortcuts.xcstrings +++ b/openHAB/Supporting Files/AppShortcuts.xcstrings @@ -524,6 +524,7 @@ } }, "Set roller shutter in ${applicationName}" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -640,6 +641,7 @@ } }, "Toggle a switch in ${applicationName}" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -699,4 +701,4 @@ } }, "version" : "1.0" -} +} \ No newline at end of file diff --git a/openHAB/Supporting Files/Localizable.xcstrings b/openHAB/Supporting Files/Localizable.xcstrings index c254781c4..2a67c98cb 100644 --- a/openHAB/Supporting Files/Localizable.xcstrings +++ b/openHAB/Supporting Files/Localizable.xcstrings @@ -190,6 +190,18 @@ } } }, + "%@ • %@" : { + "comment" : "The item name and the name of the home the item belongs to.", + "isCommentAutoGenerated" : true, + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "%1$@ • %2$@" + } + } + } + }, "%@ Settings" : { "comment" : "The title of the settings view. The placeholder is replaced with the user's home name.", "localizations" : { @@ -723,6 +735,10 @@ } } }, + "Active" : { + "comment" : "Display name for the contact state \"Open\".", + "isCommentAutoGenerated" : true + }, "active_url" : { "extractionState" : "manual", "localizations" : { @@ -1489,6 +1505,10 @@ } } }, + "Begin" : { + "comment" : "The action to play music.", + "isCommentAutoGenerated" : true + }, "bonjour_discovery_disclaimer" : { "localizations" : { "de" : { @@ -2309,6 +2329,10 @@ } } }, + "Change" : { + "comment" : "Display representation for the toggle action.", + "isCommentAutoGenerated" : true + }, "Check & Clear Image Cache" : { "comment" : "A button that triggers a check and clear action for the image cache.", "localizations" : { @@ -3523,6 +3547,10 @@ } } }, + "Control Player" : { + "comment" : "Text displayed in the shortcut picker for controlling a player.", + "isCommentAutoGenerated" : true + }, "Cool Daylight" : { "comment" : "A description for a color temperature between 6500 and 8000 Kelvin.", "localizations" : { @@ -4788,6 +4816,10 @@ } } }, + "Disable" : { + "comment" : "Displayed when the user disables a setting.", + "isCommentAutoGenerated" : true + }, "Disable Idle Timeout" : { "comment" : "A toggle that allows the user to disable the idle timeout feature.", "localizations" : { @@ -5469,6 +5501,10 @@ } } }, + "Enable" : { + "comment" : "Display name for the \"Enable\" action.", + "isCommentAutoGenerated" : true + }, "Enable Dimming" : { "comment" : "A toggle that enables or disables automatic brightness dimming.", "localizations" : { @@ -6177,6 +6213,14 @@ } } }, + "FF" : { + "comment" : "Synonyms for the \"Fast Forward\" player action.", + "isCommentAutoGenerated" : true + }, + "Flip" : { + "comment" : "Display name for the switch action \"Toggle\".", + "isCommentAutoGenerated" : true + }, "Font" : { "comment" : "A label for the font picker in the screen saver settings view.", "localizations" : { @@ -6481,6 +6525,9 @@ } } } + }, + "Go backward" : { + }, "habpanel" : { "extractionState" : "manual", @@ -6599,6 +6646,9 @@ } } } + }, + "Hold" : { + }, "Home" : { "comment" : "The name of the \"Home\" menu item.", @@ -7132,6 +7182,10 @@ } } }, + "Inactive" : { + "comment" : "Displayed when the contact is closed.", + "isCommentAutoGenerated" : true + }, "Increase %@" : { "comment" : "A button that increases the value of a setpoint. The argument is the name of the setpoint.", "localizations" : { @@ -9087,6 +9141,10 @@ } } }, + "Next track" : { + "comment" : "Synonyms for the \"Next\" player action.", + "isCommentAutoGenerated" : true + }, "No accepted server certificates" : { "comment" : "A message displayed when there are no accepted server certificates.", "localizations" : { @@ -10400,6 +10458,10 @@ "comment" : "Displayed name for the 'Open' contact state.", "isCommentAutoGenerated" : true }, + "Opened" : { + "comment" : "A synonym for the \"Open\" contact state.", + "isCommentAutoGenerated" : true + }, "openHAB Cloud Service" : { "comment" : "A toggle that allows the user to enable or disable notifications from the openHAB Cloud Service.", "localizations" : { @@ -11120,6 +11182,10 @@ } } }, + "Previous track" : { + "comment" : "Display name for the previous action.", + "isCommentAutoGenerated" : true + }, "privacy_policy" : { "localizations" : { "de" : { @@ -11709,6 +11775,14 @@ } } }, + "Replay" : { + "comment" : "A synonym for the rewind action.", + "isCommentAutoGenerated" : true + }, + "Reset" : { + "comment" : "Synonyms for the \"Reset\" state of a contact.", + "isCommentAutoGenerated" : true + }, "Restore Brightness: %lld %%" : { "comment" : "A label under the slider that shows the current brightness level and allows the user to adjust it. The value shown is the brightness level as a percentage.", "localizations" : { @@ -11827,6 +11901,10 @@ } } }, + "Resume" : { + "comment" : "Action to resume playback.", + "isCommentAutoGenerated" : true + }, "Retrieve the current state of an item" : { "comment" : "Description of the Get Item State app intent.", "localizations" : { @@ -12004,6 +12082,10 @@ } } }, + "Rewind back" : { + "comment" : "A synonym for the rewind action.", + "isCommentAutoGenerated" : true + }, "running_demo_mode" : { "extractionState" : "manual", "localizations" : { @@ -13127,6 +13209,10 @@ }, "Set active home to ${home}" : { + }, + "Set Color" : { + "comment" : "Short title for the shortcut to set a color value.", + "isCommentAutoGenerated" : true }, "Set Color Control Value" : { "comment" : "Title of the Set Color Control Value app intent.", @@ -13187,6 +13273,10 @@ } } }, + "Set Contact State" : { + "comment" : "Short title for the shortcut to set the contact state.", + "isCommentAutoGenerated" : true + }, "Set Contact State Value" : { "comment" : "Title of the Set Contact State Value app intent.", "localizations" : { @@ -13246,6 +13336,10 @@ } } }, + "Set Date & Time" : { + "comment" : "Short title for a shortcut that allows the user to set a date and time.", + "isCommentAutoGenerated" : true + }, "Set DateTime Control Value" : { "comment" : "Title of the Set DateTime Control Value app intent.", "localizations" : { @@ -13305,6 +13399,10 @@ } } }, + "Set Dimmer / Roller" : { + "comment" : "Short title for a shortcut that allows the user to set the value of a dimmer or roller shutter.", + "isCommentAutoGenerated" : true + }, "Set Dimmer or Roller Shutter Value" : { "comment" : "Title of the Set Dimmer or Roller Shutter Value app intent.", "localizations" : { @@ -13364,6 +13462,10 @@ } } }, + "Set Location" : { + "comment" : "Short title for the shortcut to set a location.", + "isCommentAutoGenerated" : true + }, "Set Location Control Value" : { "comment" : "Title of the Set Location Control Value app intent.", "localizations" : { @@ -13423,6 +13525,10 @@ } } }, + "Set Number" : { + "comment" : "Short title for the shortcut to set a number value.", + "isCommentAutoGenerated" : true + }, "Set Number Control Value" : { "comment" : "Title of the Set Number Control Value app intent.", "localizations" : { @@ -13600,6 +13706,10 @@ } } }, + "Set Switch" : { + "comment" : "Short title for the shortcut to set a switch.", + "isCommentAutoGenerated" : true + }, "Set Switch State" : { "comment" : "Title of the Set Switch State app intent.", "localizations" : { @@ -13659,6 +13769,10 @@ } } }, + "Set Text" : { + "comment" : "Short title for the shortcut to set a string value.", + "isCommentAutoGenerated" : true + }, "Set the color of a color control item" : { "comment" : "Description of the Set Color Control Value app intent.", "localizations" : { @@ -14612,6 +14726,10 @@ } } }, + "Shut" : { + "comment" : "Displayed title for the \"Shut\" state of a contact.", + "isCommentAutoGenerated" : true + }, "sitemap" : { "localizations" : { "de" : { @@ -14968,6 +15086,22 @@ } } }, + "Skip" : { + "comment" : "A synonym for the action \"Skip\".", + "isCommentAutoGenerated" : true + }, + "Skip ahead" : { + "comment" : "Synonyms for the \"Skip ahead\" action.", + "isCommentAutoGenerated" : true + }, + "Skip back" : { + "comment" : "Synonyms for the previous action.", + "isCommentAutoGenerated" : true + }, + "Skip forward" : { + "comment" : "A synonym for the \"Next\" action.", + "isCommentAutoGenerated" : true + }, "Soft White" : { "comment" : "A color temperature description.", "localizations" : { @@ -15145,6 +15279,10 @@ } } }, + "Speed up" : { + "comment" : "Synonyms for the \"Fast Forward\" action.", + "isCommentAutoGenerated" : true + }, "SSL error. The connection couldn’t be established securely." : { "comment" : "Error message displayed when a connection to the OpenHAB server fails due to a SSL error.", "localizations" : { @@ -15437,6 +15575,10 @@ } } }, + "Start" : { + "comment" : "A synonym for the \"Play\" action.", + "isCommentAutoGenerated" : true + }, "State" : { "comment" : "A label for a picker that lets the user select a state.", "localizations" : { @@ -15495,6 +15637,9 @@ } } } + }, + "Stop" : { + }, "String Item" : { "comment" : "Display name for a string item type.", @@ -15508,6 +15653,10 @@ } } }, + "Suspend" : { + "comment" : "Display name for the pause action.", + "isCommentAutoGenerated" : true + }, "SVG" : { "extractionState" : "manual", "localizations" : { @@ -15567,6 +15716,10 @@ } } }, + "Switch" : { + "comment" : "The type of action to perform on a switch.", + "isCommentAutoGenerated" : true + }, "Switch Action" : { "comment" : "Type display name for the SwitchAction enum used in app intents.", "localizations" : { @@ -15638,6 +15791,14 @@ } } }, + "Switch off" : { + "comment" : "Displayed when the user disables a device.", + "isCommentAutoGenerated" : true + }, + "Switch on" : { + "comment" : "Switch action to turn a device on.", + "isCommentAutoGenerated" : true + }, "Switch the active home in the openHAB app" : { "comment" : "Description of the intent to set the active home.", "isCommentAutoGenerated" : true @@ -16313,6 +16474,18 @@ "comment" : "Short title for a toggle switch shortcut.", "isCommentAutoGenerated" : true }, + "Triggered" : { + "comment" : "Display name for the \"Triggered\" state of a contact.", + "isCommentAutoGenerated" : true + }, + "Turn off" : { + "comment" : "Turn off action.", + "isCommentAutoGenerated" : true + }, + "Turn on" : { + "comment" : "A synonym for \"Turn on\".", + "isCommentAutoGenerated" : true + }, "unable_to_add_certificate" : { "extractionState" : "manual", "localizations" : { From c0ed23143f0594471b45b262967329d5c0d36875 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Thu, 28 May 2026 14:12:48 +0200 Subject: [PATCH 08/91] Linting and AppIntents SSE warnings fixes - OpenHABItemCache: add blank line after #endif (SwiftLint) - Localizable.xcstrings: fill in missing translations for AppIntents string keys that were in 'new' state (SSE/AppIntents warnings) Signed-off-by: Tim Mueller-Seydlitz --- .../OpenHABCore/Util/OpenHABItemCache.swift | 1 + .../Supporting Files/Localizable.xcstrings | 176 +++++++++--------- 2 files changed, 89 insertions(+), 88 deletions(-) diff --git a/OpenHABCore/Sources/OpenHABCore/Util/OpenHABItemCache.swift b/OpenHABCore/Sources/OpenHABCore/Util/OpenHABItemCache.swift index f0174f1cd..98c0e420d 100644 --- a/OpenHABCore/Sources/OpenHABCore/Util/OpenHABItemCache.swift +++ b/OpenHABCore/Sources/OpenHABCore/Util/OpenHABItemCache.swift @@ -152,6 +152,7 @@ public actor OpenHABItemCache { /// handoff (~7 s in practice) without failing with noActiveConnection. private static let networkTimeout: TimeInterval = 10 + private static let stubsDefaultsKey = "openHABItemStubs" private static let sharedDefaultsSuiteName = "group.org.openhab.app" diff --git a/openHAB/Supporting Files/Localizable.xcstrings b/openHAB/Supporting Files/Localizable.xcstrings index 2a67c98cb..ccef06c6a 100644 --- a/openHAB/Supporting Files/Localizable.xcstrings +++ b/openHAB/Supporting Files/Localizable.xcstrings @@ -6171,44 +6171,44 @@ }, "es" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Avance rápido" } }, "fi" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Kelaa eteenpäin" } }, "fr" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Avance rapide" } }, "it" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Avanti veloce" } }, "nb" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Spol fremover" } }, "nl" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Vooruitspoelen" } }, "ru" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Перемотать вперед" } } } @@ -7504,8 +7504,8 @@ }, "fi" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Kohde" } }, "fr" : { @@ -7534,8 +7534,8 @@ }, "ru" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Элемент" } } } @@ -9099,44 +9099,44 @@ }, "es" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Siguiente" } }, "fi" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Seuraava" } }, "fr" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Suivant" } }, "it" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Successivo" } }, "nb" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Neste" } }, "nl" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Volgende" } }, "ru" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Следующий" } } } @@ -10714,44 +10714,44 @@ }, "es" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Pausa" } }, "fi" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Tauko" } }, "fr" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Pause" } }, "it" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Pausa" } }, "nb" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Pause" } }, "nl" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Pauzeren" } }, "ru" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Пауза" } } } @@ -10833,44 +10833,44 @@ }, "es" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Reproducir" } }, "fi" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Toista" } }, "fr" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Lire" } }, "it" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Riproduci" } }, "nb" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Spill av" } }, "nl" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Afspelen" } }, "ru" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Воспроизвести" } } } @@ -11140,44 +11140,44 @@ }, "es" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Anterior" } }, "fi" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Edellinen" } }, "fr" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Précédent" } }, "it" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Precedente" } }, "nb" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Forrige" } }, "nl" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Vorige" } }, "ru" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Предыдущий" } } } @@ -12040,44 +12040,44 @@ }, "es" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Rebobinar" } }, "fi" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Kelaa taaksepäin" } }, "fr" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Rembobiner" } }, "it" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Riavvolgi" } }, "nb" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Spol tilbake" } }, "nl" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Terugspoelen" } }, "ru" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Перемотать назад" } } } From cf9145b0b68b79e3ffaa254d0ca13117c5f421be Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Thu, 28 May 2026 17:57:29 +0200 Subject: [PATCH 09/91] Register SetActiveHomeIntent with AppShortcutsProvider MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add "Switch Home" as an AppShortcut so the intent is discoverable via Siri, Shortcuts suggestions, and Spotlight — not just manually added shortcuts. - OpenHABShortcuts: add AppShortcut for SetActiveHomeIntent with two phrases ("Switch home in ", "Set active home in ") and systemImageName house.and.flag - AppShortcuts.xcstrings: add both phrases with translations for all 9 supported locales (de/en/es/fi/fr/it/nb/nl/ru) - Localizable.xcstrings: add "Switch Home" short-title entry Signed-off-by: Tim Mueller-Seydlitz --- AppIntents/OpenHABShortcuts.swift | 9 ++ .../Supporting Files/AppShortcuts.xcstrings | 116 ++++++++++++++++++ .../Supporting Files/Localizable.xcstrings | 4 + 3 files changed, 129 insertions(+) diff --git a/AppIntents/OpenHABShortcuts.swift b/AppIntents/OpenHABShortcuts.swift index c3f0ae82c..0525e0521 100644 --- a/AppIntents/OpenHABShortcuts.swift +++ b/AppIntents/OpenHABShortcuts.swift @@ -80,5 +80,14 @@ struct OpenHABShortcuts: AppShortcutsProvider { shortTitle: "Set Contact State", systemImageName: "sensor" ) + AppShortcut( + intent: SetActiveHomeIntent(), + phrases: [ + "Switch home in \(.applicationName)", + "Set active home in \(.applicationName)" + ], + shortTitle: "Switch Home", + systemImageName: "house.and.flag" + ) } } diff --git a/openHAB/Supporting Files/AppShortcuts.xcstrings b/openHAB/Supporting Files/AppShortcuts.xcstrings index 8457e4881..1ba1659f2 100644 --- a/openHAB/Supporting Files/AppShortcuts.xcstrings +++ b/openHAB/Supporting Files/AppShortcuts.xcstrings @@ -175,6 +175,64 @@ } } }, + "Set active home in ${applicationName}" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktives Zuhause in ${applicationName} setzen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set active home in ${applicationName}" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer el hogar activo en ${applicationName}" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta aktiivinen koti ${applicationName}-sovelluksessa" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la maison active dans ${applicationName}" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta la casa attiva in ${applicationName}" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi aktivt hjem i ${applicationName}" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actief thuis instellen in ${applicationName}" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить активный дом в ${applicationName}" + } + } + } + }, "Set color light in ${applicationName}" : { "localizations" : { "de" : { @@ -640,6 +698,64 @@ } } }, + "Switch home in ${applicationName}" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zuhause in ${applicationName} wechseln" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Switch home in ${applicationName}" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cambiar el hogar en ${applicationName}" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vaihda koti ${applicationName}-sovelluksessa" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Changer de maison dans ${applicationName}" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cambia casa in ${applicationName}" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bytt hjem i ${applicationName}" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Thuis wisselen in ${applicationName}" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Переключить дом в ${applicationName}" + } + } + } + }, "Toggle a switch in ${applicationName}" : { "extractionState" : "stale", "localizations" : { diff --git a/openHAB/Supporting Files/Localizable.xcstrings b/openHAB/Supporting Files/Localizable.xcstrings index ccef06c6a..18e2885d9 100644 --- a/openHAB/Supporting Files/Localizable.xcstrings +++ b/openHAB/Supporting Files/Localizable.xcstrings @@ -15779,6 +15779,10 @@ } } }, + "Switch Home" : { + "comment" : "Short title for the shortcut to switch the active home.", + "isCommentAutoGenerated" : true + }, "Switch Item" : { "comment" : "Display name for a switch item type.", "isCommentAutoGenerated" : true, From 4824a8fae4cbe915fcd95a0c9c4e59ba90d168a2 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Thu, 28 May 2026 18:02:01 +0200 Subject: [PATCH 10/91] Replace SetLocationValueIntent with SetActiveHomeIntent in AppShortcutsProvider AppShortcutsProvider is capped at 10 entries. SetLocationValueIntent is a rarely used edge-case; replacing it with the new SetActiveHomeIntent gives Siri / Shortcuts discovery to home-switching, which is a more common user action. - Remove SetLocationValueIntent AppShortcut and its phrase from AppShortcuts.xcstrings ("Set location in ${applicationName}") - SetLocationValueIntent itself is unchanged and still works when added manually in the Shortcuts app Signed-off-by: Tim Mueller-Seydlitz --- AppIntents/OpenHABShortcuts.swift | 6 -- .../Supporting Files/AppShortcuts.xcstrings | 58 ------------------- 2 files changed, 64 deletions(-) diff --git a/AppIntents/OpenHABShortcuts.swift b/AppIntents/OpenHABShortcuts.swift index 0525e0521..476ecbdbb 100644 --- a/AppIntents/OpenHABShortcuts.swift +++ b/AppIntents/OpenHABShortcuts.swift @@ -68,12 +68,6 @@ struct OpenHABShortcuts: AppShortcutsProvider { shortTitle: "Set Date & Time", systemImageName: "calendar.badge.clock" ) - AppShortcut( - intent: SetLocationValueIntent(), - phrases: ["Set location in \(.applicationName)"], - shortTitle: "Set Location", - systemImageName: "location" - ) AppShortcut( intent: ContactStateIntent(), phrases: ["Set contact state in \(.applicationName)"], diff --git a/openHAB/Supporting Files/AppShortcuts.xcstrings b/openHAB/Supporting Files/AppShortcuts.xcstrings index 1ba1659f2..89617bc90 100644 --- a/openHAB/Supporting Files/AppShortcuts.xcstrings +++ b/openHAB/Supporting Files/AppShortcuts.xcstrings @@ -465,64 +465,6 @@ } } }, - "Set location in ${applicationName}" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ort in ${applicationName} setzen" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Set location in ${applicationName}" - } - }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Establecer ubicación en ${applicationName}" - } - }, - "fi" : { - "stringUnit" : { - "state" : "translated", - "value" : "Aseta sijainti ${applicationName}-sovelluksessa" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Définir l'emplacement dans ${applicationName}" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Imposta la posizione in ${applicationName}" - } - }, - "nb" : { - "stringUnit" : { - "state" : "translated", - "value" : "Angi sted i ${applicationName}" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Locatie instellen in ${applicationName}" - } - }, - "ru" : { - "stringUnit" : { - "state" : "translated", - "value" : "Установить местоположение в ${applicationName}" - } - } - } - }, "Set number value in ${applicationName}" : { "localizations" : { "de" : { From 6a709ef0cddfbf54ebf1c86c75a8fe00f5e815b5 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Sun, 31 May 2026 22:14:49 +0200 Subject: [PATCH 11/91] Use iOS 17 animation and scroll APIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace chained withAnimation+Task.sleep in ScreenSaverView with withAnimation(_:completionCriteria:_:completion:) guarded by an animationGeneration counter — eliminates unstructured Tasks and the async concurrency overhead they carried. - Add ScrollPosition binding to SitemapPageView so the list scrolls to the top when the page changes (e.g. navigating back to root). - Add phaseAnimator to the failed-command indicator in SitemapNavigationView for a repeating opacity pulse that makes persistent failures visually distinct from transient ones. Signed-off-by: Tim Mueller-Seydlitz --- openHAB/UI/ScreenSaver/ScreenSaverView.swift | 20 ++++++++----------- .../UI/SwiftUI/SitemapNavigationView.swift | 5 +++++ openHAB/UI/SwiftUI/SitemapPageView.swift | 5 +++++ 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/openHAB/UI/ScreenSaver/ScreenSaverView.swift b/openHAB/UI/ScreenSaver/ScreenSaverView.swift index 1319743e6..e8b412d17 100644 --- a/openHAB/UI/ScreenSaver/ScreenSaverView.swift +++ b/openHAB/UI/ScreenSaver/ScreenSaverView.swift @@ -21,7 +21,7 @@ struct ScreenSaverView: View { @State private var fadeOpacity = 1.0 @State private var movementTimer = Timer.publish(every: 1, on: .main, in: .common).autoconnect() @State private var isTimerActive = false - @State private var animationTask: Task? + @State private var animationGeneration = 0 var body: some View { GeometryReader { geometry in @@ -64,15 +64,12 @@ struct ScreenSaverView: View { let half = max(configuration.fadeDuration / 2.0, 0.01) - animationTask?.cancel() - animationTask = Task { - withAnimation(.easeInOut(duration: half)) { - fadeOpacity = 0.0 - } - - try? await Task.sleep(nanoseconds: UInt64(half * 1_000_000_000)) - guard !Task.isCancelled else { return } - + animationGeneration += 1 + let generation = animationGeneration + withAnimation(.easeInOut(duration: half), completionCriteria: .logicallyComplete) { + fadeOpacity = 0.0 + } completion: { + guard animationGeneration == generation else { return } currentAnchor = randomAnchor() withAnimation(.easeInOut(duration: half)) { fadeOpacity = 1.0 @@ -145,8 +142,7 @@ struct ScreenSaverView: View { private func stopMovementTimer() { isTimerActive = false - animationTask?.cancel() - animationTask = nil + animationGeneration += 1 movementTimer.upstream.connect().cancel() } diff --git a/openHAB/UI/SwiftUI/SitemapNavigationView.swift b/openHAB/UI/SwiftUI/SitemapNavigationView.swift index 47b86b22a..015e3846d 100644 --- a/openHAB/UI/SwiftUI/SitemapNavigationView.swift +++ b/openHAB/UI/SwiftUI/SitemapNavigationView.swift @@ -142,6 +142,11 @@ struct SitemapNavigationView: View { } .foregroundStyle(.red) .ohTextToken(.secondary) + .phaseAnimator([1.0, 0.4]) { content, opacity in + content.opacity(opacity) + } animation: { _ in + .easeInOut(duration: 0.8) + } .accessibilityLabel("Command failures: \(count)") } } diff --git a/openHAB/UI/SwiftUI/SitemapPageView.swift b/openHAB/UI/SwiftUI/SitemapPageView.swift index 49f144479..c57cc45a8 100644 --- a/openHAB/UI/SwiftUI/SitemapPageView.swift +++ b/openHAB/UI/SwiftUI/SitemapPageView.swift @@ -17,6 +17,7 @@ import UIKit struct SitemapPageView: View { @StateObject var viewModel = SitemapPageViewModel() @State private var idleTimerDisabled = false + @State private var scrollPosition = ScrollPosition() private var isLinkedPage: Bool { viewModel.isLinked @@ -47,6 +48,10 @@ struct SitemapPageView: View { .listRowInsets(RowLayoutPolicy.rowInsets(for: rowInput)) .listRowBackground(RowLayoutPolicy.rowBackground(for: rowInput)) } + .scrollPosition($scrollPosition) + .onChange(of: viewModel.pageId) { + scrollPosition.scrollTo(edge: .top) + } } } .environmentObject(viewModel) From 088b1066a89d38545eca7e5ea3f5c97eac9d7d31 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Sun, 31 May 2026 22:39:28 +0200 Subject: [PATCH 12/91] Use iOS 18 onScrollGeometryChange in SitemapPageView Add a scroll-to-top FAB that appears when the user has scrolled more than 150 pt into the sitemap list, using the iOS 18 onScrollGeometryChange API instead of a GeometryReader. textInputSuggestions was considered for ItemSelectionView but is not available on iOS (only macOS/visionOS/watchOS). Signed-off-by: Tim Mueller-Seydlitz --- openHAB/UI/SwiftUI/SitemapPageView.swift | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/openHAB/UI/SwiftUI/SitemapPageView.swift b/openHAB/UI/SwiftUI/SitemapPageView.swift index c57cc45a8..55ebd359b 100644 --- a/openHAB/UI/SwiftUI/SitemapPageView.swift +++ b/openHAB/UI/SwiftUI/SitemapPageView.swift @@ -18,6 +18,7 @@ struct SitemapPageView: View { @StateObject var viewModel = SitemapPageViewModel() @State private var idleTimerDisabled = false @State private var scrollPosition = ScrollPosition() + @State private var showScrollToTop = false private var isLinkedPage: Bool { viewModel.isLinked @@ -49,9 +50,32 @@ struct SitemapPageView: View { .listRowBackground(RowLayoutPolicy.rowBackground(for: rowInput)) } .scrollPosition($scrollPosition) + .onScrollGeometryChange(for: Bool.self) { geo in + geo.contentOffset.y > 150 + } action: { _, isScrolledDown in + withAnimation(.easeInOut(duration: 0.2)) { + showScrollToTop = isScrolledDown + } + } .onChange(of: viewModel.pageId) { scrollPosition.scrollTo(edge: .top) } + .overlay(alignment: .bottomTrailing) { + if showScrollToTop { + Button { + withAnimation { + scrollPosition.scrollTo(edge: .top) + } + } label: { + Image(systemName: "arrow.up") + .font(.system(size: 16, weight: .semibold)) + .padding(12) + .background(.ultraThinMaterial, in: Circle()) + } + .padding([.trailing, .bottom], 16) + .transition(.scale(scale: 0.8).combined(with: .opacity)) + } + } } } .environmentObject(viewModel) From 3cf2d072c62a4097fa65fac8356f06cd9a97117b Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Mon, 1 Jun 2026 11:51:43 +0200 Subject: [PATCH 13/91] Bump minimum deployment targets to iOS 18 / watchOS 11 Raises IPHONEOS_DEPLOYMENT_TARGET to 18.0 and WATCHOS_DEPLOYMENT_TARGET to 11.0 across all targets and SPM packages, then removes the now-dead @available(iOS 17) / @available(watchOS 10) guards from all AppIntent types, entities, queries and enums, and the if #available(iOS 13) checks from UIColorExtension. Modernises previews to use @Previewable and adopts the iOS 18 SF Symbols rename for the spinning connection indicator. Signed-off-by: Tim Mueller-Seydlitz --- AGENTS.md | 4 +- AppIntents/ContactState.swift | 2 - AppIntents/Home.swift | 2 +- AppIntents/Intents/ContactStateIntent.swift | 1 - AppIntents/Intents/GetItemStateIntent.swift | 1 - AppIntents/Intents/SetActiveHomeIntent.swift | 1 - AppIntents/Intents/SetColorValueIntent.swift | 1 - .../Intents/SetDateTimeValueIntent.swift | 1 - .../Intents/SetDimmerRollerValueIntent.swift | 1 - .../Intents/SetLocationValueIntent.swift | 1 - AppIntents/Intents/SetNumberValueIntent.swift | 1 - AppIntents/Intents/SetPlayerValueIntent.swift | 1 - AppIntents/Intents/SetStringValueIntent.swift | 1 - AppIntents/Intents/SetSwitchItemIntent.swift | 1 - AppIntents/ItemEntity.swift | 2 - AppIntents/ItemEntityQuery.swift | 2 - AppIntents/OpenHABAppShortcutsProvider.swift | 24 --------- AppIntents/PlayerAction.swift | 2 - AppIntents/SwitchAction.swift | 2 - AppIntents/SwitchItemEntity.swift | 10 ---- CommonUI/Package.swift | 2 +- OpenHABCore/Package.swift | 2 +- .../OpenHABCore/Util/UIColorExtension.swift | 35 +++++-------- README.md | 6 +-- openHAB.xcodeproj/project.pbxproj | 50 +++++++++---------- .../SettingsView/BonjourDiscoverySheet.swift | 25 ++++------ .../SettingsView/ConnectionSettingsView.swift | 48 +++++++----------- .../SingleConnectionSettingsView.swift | 26 ++++------ openHAB/UI/SpinnerViewController.swift | 6 +-- openHAB/UI/SwiftUI/SitemapPageView.swift | 3 +- openHABTestsSwift/AppIntentsTests.swift | 24 --------- 31 files changed, 86 insertions(+), 202 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 7c356f16f..f669ca56e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -9,9 +9,9 @@ - UI tests: `xcodebuild test -workspace openHAB.xcworkspace -scheme openHABUITests` ## Architecture -- **Main app**: openHAB/ - UIKit + SwiftUI hybrid iOS app targeting iOS 16+ +- **Main app**: openHAB/ - UIKit + SwiftUI hybrid iOS app targeting iOS 18+ - **Core library**: OpenHABCore/ - Swift Package with shared business logic, models, API clients -- **Watch app**: openHABWatch/ - watchOS companion app (watchOS 10+) +- **Watch app**: openHABWatch/ - watchOS companion app (watchOS 11+) - **Extensions**: openHABIntents/ (Siri shortcuts), NotificationService/ (rich notifications) - **Tests**: openHABTestsSwift/ (Swift Testing), openHABUITests/ (UI automation). For targeted bug fixes, run only focused tests by default. - **Dependencies**: Kingfisher (image loading), SwiftUI, Firebase, OpenAPI runtime, SFSafeSymbols diff --git a/AppIntents/ContactState.swift b/AppIntents/ContactState.swift index e6ff99c7c..a87415eba 100644 --- a/AppIntents/ContactState.swift +++ b/AppIntents/ContactState.swift @@ -12,7 +12,6 @@ import AppIntents import Foundation -@available(iOS 17.0, macOS 14.0, *) enum ContactState: String, AppEnum { case open = "OPEN" case closed = "CLOSED" @@ -25,7 +24,6 @@ enum ContactState: String, AppEnum { ] } -@available(iOS 17.0, macOS 14.0, *) extension ContactState: CustomLocalizedStringResourceConvertible { var localizedStringResource: LocalizedStringResource { switch self { diff --git a/AppIntents/Home.swift b/AppIntents/Home.swift index 51cc9f34f..2751020df 100644 --- a/AppIntents/Home.swift +++ b/AppIntents/Home.swift @@ -105,7 +105,7 @@ enum HomeResolver { ) } - /// Production overload for item-by-name resolution (iOS 16 compat intents). + /// Production overload for item-by-name resolution. /// Builds a stable-identifier-aware `findHomeId` closure and delegates to the testable overload. static func resolveHomeId(selectedHome: Home?, itemName: String, diff --git a/AppIntents/Intents/ContactStateIntent.swift b/AppIntents/Intents/ContactStateIntent.swift index 3bd60e599..85837a9b4 100644 --- a/AppIntents/Intents/ContactStateIntent.swift +++ b/AppIntents/Intents/ContactStateIntent.swift @@ -26,7 +26,6 @@ enum ContactStateError: Error, CustomLocalizedStringResourceConvertible { } } -@available(iOS 17.0, macOS 14.0, *) struct ContactStateIntent: AppIntent { static var openAppWhenRun: Bool { false } diff --git a/AppIntents/Intents/GetItemStateIntent.swift b/AppIntents/Intents/GetItemStateIntent.swift index a52b9e697..fbd9c04b5 100644 --- a/AppIntents/Intents/GetItemStateIntent.swift +++ b/AppIntents/Intents/GetItemStateIntent.swift @@ -23,7 +23,6 @@ enum ItemStateError: Error, CustomLocalizedStringResourceConvertible { } } -@available(iOS 17.0, macOS 14.0, *) struct GetItemStateIntent: AppIntent { static var openAppWhenRun: Bool { false } diff --git a/AppIntents/Intents/SetActiveHomeIntent.swift b/AppIntents/Intents/SetActiveHomeIntent.swift index a0c5a56cd..f35e12a19 100644 --- a/AppIntents/Intents/SetActiveHomeIntent.swift +++ b/AppIntents/Intents/SetActiveHomeIntent.swift @@ -12,7 +12,6 @@ import AppIntents import OpenHABCore -@available(iOS 17.0, macOS 14.0, watchOS 10.0, *) struct SetActiveHomeIntent: AppIntent { static var openAppWhenRun: Bool { false } diff --git a/AppIntents/Intents/SetColorValueIntent.swift b/AppIntents/Intents/SetColorValueIntent.swift index 493ffad01..039341cb5 100644 --- a/AppIntents/Intents/SetColorValueIntent.swift +++ b/AppIntents/Intents/SetColorValueIntent.swift @@ -29,7 +29,6 @@ enum ColorValueError: Error, CustomLocalizedStringResourceConvertible { } } -@available(iOS 17.0, macOS 14.0, *) struct SetColorValueIntent: AppIntent { static var openAppWhenRun: Bool { false } diff --git a/AppIntents/Intents/SetDateTimeValueIntent.swift b/AppIntents/Intents/SetDateTimeValueIntent.swift index e8273be7c..58d35c17e 100644 --- a/AppIntents/Intents/SetDateTimeValueIntent.swift +++ b/AppIntents/Intents/SetDateTimeValueIntent.swift @@ -26,7 +26,6 @@ enum DateTimeValueError: Error, CustomLocalizedStringResourceConvertible { } } -@available(iOS 17.0, macOS 14.0, *) struct SetDateTimeValueIntent: AppIntent { static var openAppWhenRun: Bool { false } diff --git a/AppIntents/Intents/SetDimmerRollerValueIntent.swift b/AppIntents/Intents/SetDimmerRollerValueIntent.swift index 203224408..e2028b095 100644 --- a/AppIntents/Intents/SetDimmerRollerValueIntent.swift +++ b/AppIntents/Intents/SetDimmerRollerValueIntent.swift @@ -29,7 +29,6 @@ enum DimmerRollerValueError: Error, CustomLocalizedStringResourceConvertible { } } -@available(iOS 17.0, macOS 14.0, *) struct SetDimmerRollerValueIntent: AppIntent { static var openAppWhenRun: Bool { false } diff --git a/AppIntents/Intents/SetLocationValueIntent.swift b/AppIntents/Intents/SetLocationValueIntent.swift index 846f42121..89b873fbf 100644 --- a/AppIntents/Intents/SetLocationValueIntent.swift +++ b/AppIntents/Intents/SetLocationValueIntent.swift @@ -32,7 +32,6 @@ enum LocationValueError: Error, CustomLocalizedStringResourceConvertible { } } -@available(iOS 17.0, macOS 14.0, *) struct SetLocationValueIntent: AppIntent { static var openAppWhenRun: Bool { false } diff --git a/AppIntents/Intents/SetNumberValueIntent.swift b/AppIntents/Intents/SetNumberValueIntent.swift index 8b2e0cffb..af4acfb57 100644 --- a/AppIntents/Intents/SetNumberValueIntent.swift +++ b/AppIntents/Intents/SetNumberValueIntent.swift @@ -26,7 +26,6 @@ enum NumberValueError: Error, CustomLocalizedStringResourceConvertible { } } -@available(iOS 17.0, macOS 14.0, *) struct SetNumberValueIntent: AppIntent { static var openAppWhenRun: Bool { false } diff --git a/AppIntents/Intents/SetPlayerValueIntent.swift b/AppIntents/Intents/SetPlayerValueIntent.swift index 3307c9552..3f1f66e09 100644 --- a/AppIntents/Intents/SetPlayerValueIntent.swift +++ b/AppIntents/Intents/SetPlayerValueIntent.swift @@ -26,7 +26,6 @@ enum PlayerValueError: Error, CustomLocalizedStringResourceConvertible { } } -@available(iOS 17.0, macOS 14.0, *) struct SetPlayerValueIntent: AppIntent { static var openAppWhenRun: Bool { false } diff --git a/AppIntents/Intents/SetStringValueIntent.swift b/AppIntents/Intents/SetStringValueIntent.swift index a0d235f8d..c1dc9becf 100644 --- a/AppIntents/Intents/SetStringValueIntent.swift +++ b/AppIntents/Intents/SetStringValueIntent.swift @@ -26,7 +26,6 @@ enum StringValueError: Error, CustomLocalizedStringResourceConvertible { } } -@available(iOS 17.0, macOS 14.0, *) struct SetStringValueIntent: AppIntent { static var openAppWhenRun: Bool { false } diff --git a/AppIntents/Intents/SetSwitchItemIntent.swift b/AppIntents/Intents/SetSwitchItemIntent.swift index 44f7e0a0b..15cc5804b 100644 --- a/AppIntents/Intents/SetSwitchItemIntent.swift +++ b/AppIntents/Intents/SetSwitchItemIntent.swift @@ -26,7 +26,6 @@ enum ControlItemError: Error, CustomLocalizedStringResourceConvertible { } } -@available(iOS 17.0, macOS 14.0, *) struct SetSwitchItemIntent: AppIntent { static var openAppWhenRun: Bool { false } diff --git a/AppIntents/ItemEntity.swift b/AppIntents/ItemEntity.swift index 8d1371202..9488b3dc2 100644 --- a/AppIntents/ItemEntity.swift +++ b/AppIntents/ItemEntity.swift @@ -14,7 +14,6 @@ import OpenHABCore // MARK: - Shared Protocol for All Item Entities -@available(iOS 17.0, macOS 14.0, *) protocol ItemEntity: AppEntity where ID == ItemIdentifier { var id: ItemIdentifier { get set } var item: OpenHABItem { get set } @@ -24,7 +23,6 @@ protocol ItemEntity: AppEntity where ID == ItemIdentifier { init(_ openHABItem: OpenHABItem, homeId: UUID, homeName: String?) } -@available(iOS 17.0, macOS 14.0, *) extension ItemEntity { var homeId: UUID? { id.homeId } var itemName: String { id.itemName } diff --git a/AppIntents/ItemEntityQuery.swift b/AppIntents/ItemEntityQuery.swift index 1457cd92c..261b5069b 100644 --- a/AppIntents/ItemEntityQuery.swift +++ b/AppIntents/ItemEntityQuery.swift @@ -14,7 +14,6 @@ import OpenHABCore // MARK: - Shared Query Protocol -@available(iOS 17.0, macOS 14.0, *) protocol ItemEntityQuery: EntityStringQuery { associatedtype EntityType: ItemEntity @@ -22,7 +21,6 @@ protocol ItemEntityQuery: EntityStringQuery { var selectedHome: Home? { get } } -@available(iOS 17.0, macOS 14.0, *) extension ItemEntityQuery { @MainActor func getHomeName(for homeId: UUID) -> String? { diff --git a/AppIntents/OpenHABAppShortcutsProvider.swift b/AppIntents/OpenHABAppShortcutsProvider.swift index f5fb5edf1..88ea64af4 100644 --- a/AppIntents/OpenHABAppShortcutsProvider.swift +++ b/AppIntents/OpenHABAppShortcutsProvider.swift @@ -49,7 +49,6 @@ internal import SFSafeSymbols /// - Apple Documentation: https://developer.apple.com/documentation/appintents/app-shortcuts /// - PR #742: Original implementation /// - PR #1028: Enhanced intent implementations -@available(iOS 17.0, macOS 13.0, watchOS 9.0, tvOS 16.0, *) struct OpenHABAppShortcutsProvider: AppShortcutsProvider { static var appShortcuts: [AppShortcut] { // MARK: - Switch Control - Toggle Action @@ -77,26 +76,3 @@ struct OpenHABAppShortcutsProvider: AppShortcutsProvider { /// Orange matches the openHAB brand color static let shortcutTileColor: ShortcutTileColor = .orange } - -//// MARK: - AppShortcut + SFSymbol Extension -// -// @available(iOS 16.0, macOS 13.0, watchOS 9.0, tvOS 16.0, *) -// extension AppShortcut { -// /// Creates an app shortcut with a type-safe SF Symbol. -// /// -// /// - Parameters: -// /// - intent: The intent to run when the shortcut is invoked. -// /// - phrases: The phrases that trigger this shortcut. -// /// - shortTitle: A short title displayed in the Shortcuts app. -// /// - systemImage: The SF Symbol to display for this shortcut. -// /// -// init(intent: Intent, phrases: [AppShortcutPhrase], shortTitle: LocalizedStringResource, systemImage: SFSymbol) where Intent : AppIntent { -// -// self.init( -// intent: intent, -// phrases: phrases, -// shortTitle: shortTitle, -// systemImageName: systemImage.rawValue -// ) -// } -// } diff --git a/AppIntents/PlayerAction.swift b/AppIntents/PlayerAction.swift index 55eab750e..be74b8040 100644 --- a/AppIntents/PlayerAction.swift +++ b/AppIntents/PlayerAction.swift @@ -12,7 +12,6 @@ import AppIntents import Foundation -@available(iOS 17.0, macOS 14.0, *) enum PlayerAction: String, AppEnum { case play = "PLAY" case pause = "PAUSE" @@ -33,7 +32,6 @@ enum PlayerAction: String, AppEnum { ] } -@available(iOS 17.0, macOS 14.0, *) extension PlayerAction: CustomLocalizedStringResourceConvertible { var localizedStringResource: LocalizedStringResource { switch self { diff --git a/AppIntents/SwitchAction.swift b/AppIntents/SwitchAction.swift index fe5cf4026..0f99ae358 100644 --- a/AppIntents/SwitchAction.swift +++ b/AppIntents/SwitchAction.swift @@ -12,7 +12,6 @@ import AppIntents import Foundation -@available(iOS 17.0, macOS 14.0, *) enum SwitchAction: String, AppEnum { case on = "ON" case off = "OFF" @@ -27,7 +26,6 @@ enum SwitchAction: String, AppEnum { ] } -@available(iOS 17.0, macOS 14.0, *) extension SwitchAction: CustomLocalizedStringResourceConvertible { var localizedStringResource: LocalizedStringResource { switch self { diff --git a/AppIntents/SwitchItemEntity.swift b/AppIntents/SwitchItemEntity.swift index 9ce7e5033..7e3a71da5 100644 --- a/AppIntents/SwitchItemEntity.swift +++ b/AppIntents/SwitchItemEntity.swift @@ -14,7 +14,6 @@ import OpenHABCore // MARK: - DimmerItemEntity -@available(iOS 17.0, macOS 14.0, watchOS 10.0, *) struct DimmerItemEntity: ItemEntity { struct DimmerItemQuery: ItemEntityQuery { typealias EntityType = DimmerItemEntity @@ -47,7 +46,6 @@ struct DimmerItemEntity: ItemEntity { // MARK: - ColorItemEntity -@available(iOS 17.0, macOS 14.0, watchOS 10.0, *) struct ColorItemEntity: ItemEntity { struct ColorItemQuery: ItemEntityQuery { typealias EntityType = ColorItemEntity @@ -80,7 +78,6 @@ struct ColorItemEntity: ItemEntity { // MARK: - NumberItemEntity -@available(iOS 17.0, macOS 14.0, watchOS 10.0, *) struct NumberItemEntity: ItemEntity { struct NumberItemQuery: ItemEntityQuery { typealias EntityType = NumberItemEntity @@ -113,7 +110,6 @@ struct NumberItemEntity: ItemEntity { // MARK: - StringItemEntity -@available(iOS 17.0, macOS 14.0, watchOS 10.0, *) struct StringItemEntity: ItemEntity { struct StringItemQuery: ItemEntityQuery { typealias EntityType = StringItemEntity @@ -146,7 +142,6 @@ struct StringItemEntity: ItemEntity { // MARK: - ContactItemEntity -@available(iOS 17.0, macOS 14.0, watchOS 10.0, *) struct ContactItemEntity: ItemEntity { struct ContactItemQuery: ItemEntityQuery { typealias EntityType = ContactItemEntity @@ -179,7 +174,6 @@ struct ContactItemEntity: ItemEntity { // MARK: - GenericItemEntity (for ItemStateIntent - all types) -@available(iOS 17.0, macOS 14.0, watchOS 10.0, *) struct GenericItemEntity: ItemEntity { struct GenericItemQuery: ItemEntityQuery { typealias EntityType = GenericItemEntity @@ -212,7 +206,6 @@ struct GenericItemEntity: ItemEntity { // MARK: - PlayerItemEntity -@available(iOS 17.0, macOS 14.0, watchOS 10.0, *) struct PlayerItemEntity: ItemEntity { struct PlayerItemQuery: ItemEntityQuery { typealias EntityType = PlayerItemEntity @@ -245,7 +238,6 @@ struct PlayerItemEntity: ItemEntity { // MARK: - DateTimeItemEntity -@available(iOS 17.0, macOS 14.0, watchOS 10.0, *) struct DateTimeItemEntity: ItemEntity { struct DateTimeItemQuery: ItemEntityQuery { typealias EntityType = DateTimeItemEntity @@ -278,7 +270,6 @@ struct DateTimeItemEntity: ItemEntity { // MARK: - LocationItemEntity -@available(iOS 17.0, macOS 14.0, watchOS 10.0, *) struct LocationItemEntity: ItemEntity { struct LocationItemQuery: ItemEntityQuery { typealias EntityType = LocationItemEntity @@ -311,7 +302,6 @@ struct LocationItemEntity: ItemEntity { // MARK: - SwitchItemEntity -@available(iOS 17.0, macOS 14.0, watchOS 10.0, *) struct SwitchItemEntity: ItemEntity { struct SwitchItemQuery: ItemEntityQuery { typealias EntityType = SwitchItemEntity diff --git a/CommonUI/Package.swift b/CommonUI/Package.swift index 4018f6eb2..9f38f9352 100644 --- a/CommonUI/Package.swift +++ b/CommonUI/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "CommonUI", - platforms: [.iOS(.v17), .watchOS(.v10), .macOS(.v14)], + platforms: [.iOS(.v18), .watchOS(.v11), .macOS(.v14)], products: [ // Products define the executables and libraries a package produces, making them visible to other packages. diff --git a/OpenHABCore/Package.swift b/OpenHABCore/Package.swift index fe4f3f64f..b541b5638 100644 --- a/OpenHABCore/Package.swift +++ b/OpenHABCore/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "OpenHABCore", - platforms: [.iOS(.v17), .watchOS(.v10), .macOS(.v14)], + platforms: [.iOS(.v18), .watchOS(.v11), .macOS(.v14)], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( diff --git a/OpenHABCore/Sources/OpenHABCore/Util/UIColorExtension.swift b/OpenHABCore/Sources/OpenHABCore/Util/UIColorExtension.swift index 61ae7109c..c2373db98 100644 --- a/OpenHABCore/Sources/OpenHABCore/Util/UIColorExtension.swift +++ b/OpenHABCore/Sources/OpenHABCore/Util/UIColorExtension.swift @@ -33,38 +33,31 @@ public extension UIColor { // system colors class var ohLabel: UIColor { #if os(iOS) - if #available(iOS 13.0, *) { - return .label - } - #endif + return .label + #else return .black + #endif } class var ohSecondaryLabel: UIColor { #if os(iOS) - if #available(iOS 13.0, *) { - return .secondaryLabel - } - #endif + return .secondaryLabel + #else return .lightGray + #endif } class var ohSystemBackground: UIColor { #if os(iOS) - if #available(iOS 13.0, *) { - return .systemBackground - } - #endif + return .systemBackground + #else return .white + #endif } class var ohSystemGroupedBackground: UIColor { #if os(iOS) - if #available(iOS 13.0, *) { - return .systemGroupedBackground - } else { - return .groupTableViewBackground - } + return .systemGroupedBackground #elseif os(watchOS) return .black #else @@ -74,12 +67,10 @@ public extension UIColor { class var ohSecondarySystemGroupedBackground: UIColor { #if os(iOS) - if #available(iOS 13.0, *) { - return .secondarySystemGroupedBackground - } - #endif - + return .secondarySystemGroupedBackground + #else return .white + #endif } // standard colors diff --git a/README.md b/README.md index 21d7db38c..c4b9c0363 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ This is the IOS native client for openHAB. ### openHAB (Current) This is the primary openHAB app which contains the latest features and is updated regularly. This includes Apple Watch support, enhanced notifications, shortcuts and more. -Requires at least iOS 16 and openHAB 2.x and later. +Requires at least iOS 18 and openHAB 2.x and later. Download on the App Store @@ -29,7 +29,7 @@ Beta releases are available on [TestFlight](https://testflight.apple.com/join/0u ### openHAB V1 (Legacy) -This is the legacy app for users on iOS 15 or earlier as well as openHAB system 1.x and later (tested to at least openHAB 4). +This is the legacy app for users on iOS 17 or earlier as well as openHAB system 1.x and later (tested to at least openHAB 4). This app only receives security updates and minor fixes and is not intended for most users. Download on the App Store @@ -287,7 +287,7 @@ The app supports exposes several actions that let you control your openHAB insta If you want to contribute to the iOS application we are here to help you to set up development environment. openHAB iOS app is developed using Xcode and the standard iOS SDK from Apple. -The iOS application is based on the iOS 16 and watchOS 8 SDK and makes uses of several Swift packages. +The iOS application targets iOS 18 and watchOS 11 and makes uses of several Swift packages. To start developing you need an [Apple Developer](https://developer.apple.com/devcenter/ios/index.action) account. diff --git a/openHAB.xcodeproj/project.pbxproj b/openHAB.xcodeproj/project.pbxproj index 0b9673c79..5f3f914f4 100644 --- a/openHAB.xcodeproj/project.pbxproj +++ b/openHAB.xcodeproj/project.pbxproj @@ -1255,7 +1255,7 @@ INFOPLIST_FILE = openHABWidget/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = openHABWidget; INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 openHAB e.V. All rights reserved."; - IPHONEOS_DEPLOYMENT_TARGET = 17.6; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1290,7 +1290,7 @@ INFOPLIST_FILE = NotificationService/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = NotificationService; INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 openHAB e.V. All rights reserved."; - IPHONEOS_DEPLOYMENT_TARGET = 17.6; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1328,7 +1328,7 @@ INFOPLIST_FILE = NotificationService/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = NotificationService; INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 openHAB e.V. All rights reserved."; - IPHONEOS_DEPLOYMENT_TARGET = 17.6; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1357,7 +1357,7 @@ INFOPLIST_FILE = openHABWidget/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = openHABWidget; INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 openHAB e.V. All rights reserved."; - IPHONEOS_DEPLOYMENT_TARGET = 17.6; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1387,7 +1387,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = openHABUITests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 17.6; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1405,7 +1405,7 @@ SWIFT_PRECOMPILE_BRIDGING_HEADER = NO; TARGETED_DEVICE_FAMILY = "1,2"; TEST_TARGET_NAME = openHAB; - WATCHOS_DEPLOYMENT_TARGET = 8.0; + WATCHOS_DEPLOYMENT_TARGET = 11.0; }; name = Debug; }; @@ -1425,7 +1425,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = openHABUITests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 17.6; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1469,7 +1469,7 @@ INFOPLIST_KEY_CLKComplicationPrincipalClass = openHABWatch.ComplicationController; INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; INFOPLIST_KEY_WKCompanionAppBundleIdentifier = org.openhab.app; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; LD_RUNPATH_SEARCH_PATHS = ( "@executable_path/Frameworks", "@executable_path/../../Frameworks", @@ -1484,7 +1484,7 @@ SWIFT_EMIT_LOC_STRINGS = YES; TARGETED_DEVICE_FAMILY = 4; VERSIONING_SYSTEM = "apple-generic"; - WATCHOS_DEPLOYMENT_TARGET = 10.6; + WATCHOS_DEPLOYMENT_TARGET = 11.0; }; name = Debug; }; @@ -1513,7 +1513,7 @@ INFOPLIST_KEY_CLKComplicationPrincipalClass = openHABWatch.ComplicationController; INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; INFOPLIST_KEY_WKCompanionAppBundleIdentifier = org.openhab.app; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; LD_RUNPATH_SEARCH_PATHS = ( "@executable_path/Frameworks", "@executable_path/../../Frameworks", @@ -1529,7 +1529,7 @@ SWIFT_EMIT_LOC_STRINGS = YES; TARGETED_DEVICE_FAMILY = 4; VERSIONING_SYSTEM = "apple-generic"; - WATCHOS_DEPLOYMENT_TARGET = 10.6; + WATCHOS_DEPLOYMENT_TARGET = 11.0; }; name = Release; }; @@ -1547,7 +1547,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = openHABTestsSwift/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 17.6; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1582,7 +1582,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = openHABTestsSwift/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 17.6; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1633,7 +1633,7 @@ SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = 4; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/openHABWatch.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/openHABWatch"; - WATCHOS_DEPLOYMENT_TARGET = 10.6; + WATCHOS_DEPLOYMENT_TARGET = 11.0; }; name = Debug; }; @@ -1669,7 +1669,7 @@ SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = 4; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/openHABWatch.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/openHABWatch"; - WATCHOS_DEPLOYMENT_TARGET = 10.6; + WATCHOS_DEPLOYMENT_TARGET = 11.0; }; name = Release; }; @@ -1698,7 +1698,7 @@ SWIFT_EMIT_LOC_STRINGS = NO; TARGETED_DEVICE_FAMILY = 4; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/openHABWatch.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/openHABWatch"; - WATCHOS_DEPLOYMENT_TARGET = 10.6; + WATCHOS_DEPLOYMENT_TARGET = 11.0; }; name = Debug; }; @@ -1729,7 +1729,7 @@ SWIFT_EMIT_LOC_STRINGS = NO; TARGETED_DEVICE_FAMILY = 4; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/openHABWatch.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/openHABWatch"; - WATCHOS_DEPLOYMENT_TARGET = 10.6; + WATCHOS_DEPLOYMENT_TARGET = 11.0; }; name = Release; }; @@ -1757,7 +1757,7 @@ SWIFT_EMIT_LOC_STRINGS = NO; TARGETED_DEVICE_FAMILY = 4; TEST_TARGET_NAME = openHABWatch; - WATCHOS_DEPLOYMENT_TARGET = 10.6; + WATCHOS_DEPLOYMENT_TARGET = 11.0; }; name = Debug; }; @@ -1787,7 +1787,7 @@ SWIFT_EMIT_LOC_STRINGS = NO; TARGETED_DEVICE_FAMILY = 4; TEST_TARGET_NAME = openHABWatch; - WATCHOS_DEPLOYMENT_TARGET = 10.6; + WATCHOS_DEPLOYMENT_TARGET = 11.0; }; name = Release; }; @@ -1846,7 +1846,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; STRING_CATALOG_GENERATE_SYMBOLS = YES; @@ -1869,7 +1869,7 @@ SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; - WATCHOS_DEPLOYMENT_TARGET = 9.0; + WATCHOS_DEPLOYMENT_TARGET = 11.0; }; name = Debug; }; @@ -1922,7 +1922,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; ONLY_ACTIVE_ARCH = NO; PROVISIONING_PROFILE_SPECIFIER = "match AppStore org.openhab.app"; SDKROOT = iphoneos; @@ -1947,7 +1947,7 @@ TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; - WATCHOS_DEPLOYMENT_TARGET = 9.0; + WATCHOS_DEPLOYMENT_TARGET = 11.0; }; name = Release; }; @@ -1966,7 +1966,7 @@ INFOPLIST_FILE = "openHAB/Supporting Files/openHAB-Info.plist"; INFOPLIST_KEY_CFBundleDisplayName = openHAB; INFOPLIST_KEY_WKCompanionAppBundleIdentifier = ""; - IPHONEOS_DEPLOYMENT_TARGET = 17.6; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2012,7 +2012,7 @@ INFOPLIST_FILE = "openHAB/Supporting Files/openHAB-Info.plist"; INFOPLIST_KEY_CFBundleDisplayName = openHAB; INFOPLIST_KEY_WKCompanionAppBundleIdentifier = ""; - IPHONEOS_DEPLOYMENT_TARGET = 17.6; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/openHAB/UI/SettingsView/BonjourDiscoverySheet.swift b/openHAB/UI/SettingsView/BonjourDiscoverySheet.swift index 1cfbc262f..6e8e93ad0 100644 --- a/openHAB/UI/SettingsView/BonjourDiscoverySheet.swift +++ b/openHAB/UI/SettingsView/BonjourDiscoverySheet.swift @@ -96,24 +96,17 @@ struct BonjourDiscoverySheet: View { } } -// **TODO Migrate to @Previewable on iOS 17 #Preview { - struct PreviewWrapper: View { - @State private var isPresented = true - @State var connectionConfig = ConnectionConfiguration( - url: "https://openhab.local:8443", - username: "user", - password: "password123" - ) + @Previewable @State var isPresented = true + @Previewable @State var connectionConfig = ConnectionConfiguration( + url: "https://openhab.local:8443", + username: "user", + password: "password123" + ) - var body: some View { - NavigationStack { - Form { - BonjourDiscoverySheet(isPresented: $isPresented, connectionConfig: $connectionConfig) - } - } + NavigationStack { + Form { + BonjourDiscoverySheet(isPresented: $isPresented, connectionConfig: $connectionConfig) } } - - return PreviewWrapper() } diff --git a/openHAB/UI/SettingsView/ConnectionSettingsView.swift b/openHAB/UI/SettingsView/ConnectionSettingsView.swift index 098c3289d..03892f077 100644 --- a/openHAB/UI/SettingsView/ConnectionSettingsView.swift +++ b/openHAB/UI/SettingsView/ConnectionSettingsView.swift @@ -31,38 +31,26 @@ struct ConnectionSettingsView: View { } } -// **TODO Migrate to @Previewable on iOS 17 #Preview { - struct PreviewWrapper: View { - @State var demoMode = false - @State var localUrl = "https://openhab.local:8443" - @State var remoteUrl = "https://myopenhab.org" - @State var username = "user" - @State var password = "password123" - @State var alwaysSendCreds = true + @Previewable @State var demoMode = false + @Previewable @State var connectionConfig1 = ConnectionConfiguration( + url: "https://openhab.local:8443", + username: "user", + password: "password123" + ) + @Previewable @State var connectionConfig2 = ConnectionConfiguration( + url: "http://192.168.2.1", + username: "user", + password: "password123" + ) - @State var connectionConfig1 = ConnectionConfiguration( - url: "https://openhab.local:8443", - username: "user", - password: "password123" - ) - @State var connectionConfig2 = ConnectionConfiguration( - url: "http://192.168.2.1", - username: "user", - password: "password123" - ) - - var body: some View { - NavigationStack { - Form { - ConnectionSettingsView( - settingsDemomode: $demoMode, - localConnectionConfiguration: $connectionConfig1, - remoteConnectionConfiguration: $connectionConfig2 - ) - } - } + NavigationStack { + Form { + ConnectionSettingsView( + settingsDemomode: $demoMode, + localConnectionConfiguration: $connectionConfig1, + remoteConnectionConfiguration: $connectionConfig2 + ) } } - return PreviewWrapper() } diff --git a/openHAB/UI/SettingsView/SingleConnectionSettingsView.swift b/openHAB/UI/SettingsView/SingleConnectionSettingsView.swift index fddbf7509..32bd5e857 100644 --- a/openHAB/UI/SettingsView/SingleConnectionSettingsView.swift +++ b/openHAB/UI/SettingsView/SingleConnectionSettingsView.swift @@ -17,7 +17,7 @@ struct SpinningSymbol: View { @State private var isAnimating = false var body: some View { - Image(systemSymbol: .arrowTriangle2Circlepath) + Image(systemSymbol: .arrowTrianglehead2ClockwiseRotate90) .rotationEffect(.degrees(isAnimating ? 360 : 0)) .animation( Animation.linear(duration: 1.0) @@ -211,22 +211,16 @@ struct SingleConnectionSettingsView: View { } } -// **TODO Migrate to @Previewable on iOS 17 #Preview { - struct PreviewWrapper: View { - @State var connectionConfig = ConnectionConfiguration( - url: "https://openhab.local:8443", - username: "user", - password: "password123" - ) - - var body: some View { - NavigationStack { - Form { - SingleConnectionSettingsView(headerText: String(localized: "Connection Settings for local server"), connectionConfig: $connectionConfig, showNotificationToggle: false) - } - } + @Previewable @State var connectionConfig = ConnectionConfiguration( + url: "https://openhab.local:8443", + username: "user", + password: "password123" + ) + + NavigationStack { + Form { + SingleConnectionSettingsView(headerText: String(localized: "Connection Settings for local server"), connectionConfig: $connectionConfig, showNotificationToggle: false) } } - return PreviewWrapper() } diff --git a/openHAB/UI/SpinnerViewController.swift b/openHAB/UI/SpinnerViewController.swift index db86de35e..64adb647d 100644 --- a/openHAB/UI/SpinnerViewController.swift +++ b/openHAB/UI/SpinnerViewController.swift @@ -12,11 +12,7 @@ import UIKit class SpinnerViewController: UIViewController { - private var spinner: UIActivityIndicatorView = if #available(iOS 13.0, *) { - .init(style: .large) - } else { - .init(style: .gray) - } + private var spinner = UIActivityIndicatorView(style: .large) override func loadView() { view = UIView() diff --git a/openHAB/UI/SwiftUI/SitemapPageView.swift b/openHAB/UI/SwiftUI/SitemapPageView.swift index 55ebd359b..6649f3eff 100644 --- a/openHAB/UI/SwiftUI/SitemapPageView.swift +++ b/openHAB/UI/SwiftUI/SitemapPageView.swift @@ -11,6 +11,7 @@ import CommonUI import OpenHABCore +import SFSafeSymbols import SwiftUI import UIKit @@ -67,7 +68,7 @@ struct SitemapPageView: View { scrollPosition.scrollTo(edge: .top) } } label: { - Image(systemName: "arrow.up") + Image(systemSymbol: .arrowUp) .font(.system(size: 16, weight: .semibold)) .padding(12) .background(.ultraThinMaterial, in: Circle()) diff --git a/openHABTestsSwift/AppIntentsTests.swift b/openHABTestsSwift/AppIntentsTests.swift index 6c849c49b..ef0e42db8 100644 --- a/openHABTestsSwift/AppIntentsTests.swift +++ b/openHABTestsSwift/AppIntentsTests.swift @@ -39,7 +39,6 @@ private func makeItem(name: String = "TestItem", type: String, label: String = " struct SetColorValueIntentTests { let homeId = UUID() - @available(iOS 17.0, macOS 14.0, watchOS 10.0, *) private func makeIntent(home: Home? = nil, value: String = "240,100,100") -> SetColorValueIntent { let defaultHome = Home(id: homeId.uuidString, displayString: "Test Home") let entity = ColorItemEntity(makeItem(type: "Color", label: "Color Light"), homeId: homeId) @@ -50,7 +49,6 @@ struct SetColorValueIntentTests { return i } - @available(iOS 17.0, macOS 14.0, watchOS 10.0, *) @Test func homeMismatchThrowsItemNotInHome() async { let wrongHome = Home(id: UUID().uuidString, displayString: "Wrong Home") await #expect(throws: ColorValueError.self) { @@ -58,42 +56,36 @@ struct SetColorValueIntentTests { } } - @available(iOS 17.0, macOS 14.0, watchOS 10.0, *) @Test func twoComponentsThrowsInvalidValue() async { await #expect(throws: ColorValueError.self) { try await makeIntent(value: "240,100").perform() } } - @available(iOS 17.0, macOS 14.0, watchOS 10.0, *) @Test func hueAbove360ThrowsInvalidValue() async { await #expect(throws: ColorValueError.self) { try await makeIntent(value: "361,100,100").perform() } } - @available(iOS 17.0, macOS 14.0, watchOS 10.0, *) @Test func negativeHueThrowsInvalidValue() async { await #expect(throws: ColorValueError.self) { try await makeIntent(value: "-1,100,100").perform() } } - @available(iOS 17.0, macOS 14.0, watchOS 10.0, *) @Test func saturationAbove100ThrowsInvalidValue() async { await #expect(throws: ColorValueError.self) { try await makeIntent(value: "240,101,100").perform() } } - @available(iOS 17.0, macOS 14.0, watchOS 10.0, *) @Test func brightnessAbove100ThrowsInvalidValue() async { await #expect(throws: ColorValueError.self) { try await makeIntent(value: "240,100,101").perform() } } - @available(iOS 17.0, macOS 14.0, watchOS 10.0, *) @Test func nonNumericComponentThrowsInvalidValue() async { await #expect(throws: ColorValueError.self) { try await makeIntent(value: "blue,100,100").perform() @@ -107,7 +99,6 @@ struct SetColorValueIntentTests { struct SetDimmerRollerValueIntentTests { let homeId = UUID() - @available(iOS 17.0, macOS 14.0, watchOS 10.0, *) private func makeIntent(home: Home? = nil, value: Int = 50) -> SetDimmerRollerValueIntent { let defaultHome = Home(id: homeId.uuidString, displayString: "Test Home") let entity = DimmerItemEntity(makeItem(type: "Dimmer", label: "Dimmer Light"), homeId: homeId) @@ -118,7 +109,6 @@ struct SetDimmerRollerValueIntentTests { return i } - @available(iOS 17.0, macOS 14.0, watchOS 10.0, *) @Test func homeMismatchThrowsItemNotInHome() async { let wrongHome = Home(id: UUID().uuidString, displayString: "Wrong Home") await #expect(throws: DimmerRollerValueError.self) { @@ -126,14 +116,12 @@ struct SetDimmerRollerValueIntentTests { } } - @available(iOS 17.0, macOS 14.0, watchOS 10.0, *) @Test func valueAbove100ThrowsInvalidValue() async { await #expect(throws: DimmerRollerValueError.self) { try await makeIntent(value: 101).perform() } } - @available(iOS 17.0, macOS 14.0, watchOS 10.0, *) @Test func valueBelow0ThrowsInvalidValue() async { await #expect(throws: DimmerRollerValueError.self) { try await makeIntent(value: -1).perform() @@ -147,7 +135,6 @@ struct SetDimmerRollerValueIntentTests { struct SetLocationValueIntentTests { let homeId = UUID() - @available(iOS 17.0, macOS 14.0, watchOS 10.0, *) private func makeIntent(home: Home? = nil, latitude: Double = 48.0, longitude: Double = 11.0) -> SetLocationValueIntent { let defaultHome = Home(id: homeId.uuidString, displayString: "Test Home") let entity = LocationItemEntity(makeItem(type: "Location", label: "My Location"), homeId: homeId) @@ -159,7 +146,6 @@ struct SetLocationValueIntentTests { return intent } - @available(iOS 17.0, macOS 14.0, watchOS 10.0, *) @Test func homeMismatchThrowsItemNotInHome() async { let wrongHome = Home(id: UUID().uuidString, displayString: "Wrong Home") await #expect(throws: LocationValueError.self) { @@ -167,28 +153,24 @@ struct SetLocationValueIntentTests { } } - @available(iOS 17.0, macOS 14.0, watchOS 10.0, *) @Test func latitudeAbove90ThrowsInvalidLatitude() async { await #expect(throws: LocationValueError.self) { try await makeIntent(latitude: 90.001).perform() } } - @available(iOS 17.0, macOS 14.0, watchOS 10.0, *) @Test func latitudeBelow90ThrowsInvalidLatitude() async { await #expect(throws: LocationValueError.self) { try await makeIntent(latitude: -90.001).perform() } } - @available(iOS 17.0, macOS 14.0, watchOS 10.0, *) @Test func longitudeAbove180ThrowsInvalidLongitude() async { await #expect(throws: LocationValueError.self) { try await makeIntent(latitude: 0, longitude: 180.001).perform() } } - @available(iOS 17.0, macOS 14.0, watchOS 10.0, *) @Test func longitudeBelow180ThrowsInvalidLongitude() async { await #expect(throws: LocationValueError.self) { try await makeIntent(latitude: 0, longitude: -180.001).perform() @@ -202,7 +184,6 @@ struct SetLocationValueIntentTests { struct SetSwitchItemIntentTests { let homeId = UUID() - @available(iOS 17.0, macOS 14.0, watchOS 10.0, *) @Test func homeMismatchThrowsItemNotInHome() async { let wrongHome = Home(id: UUID().uuidString, displayString: "Wrong Home") let entity = SwitchItemEntity(makeItem(type: "Switch", label: "My Switch"), homeId: homeId) @@ -235,7 +216,6 @@ struct ItemSearchRankingTests { @Suite("Intent error descriptions") struct IntentErrorDescriptionTests { - @available(iOS 17.0, macOS 14.0, watchOS 10.0, *) @Test func colorItemNotInHomeContainsItemAndHomeName() { let error = ColorValueError.itemNotInHome("Color Light", "My Home") let str = String(localized: error.localizedStringResource) @@ -243,7 +223,6 @@ struct IntentErrorDescriptionTests { #expect(str.contains("My Home")) } - @available(iOS 17.0, macOS 14.0, watchOS 10.0, *) @Test func colorInvalidValueContainsValueAndItemName() { let error = ColorValueError.invalidValue("blue,100,100", "Color Light") let str = String(localized: error.localizedStringResource) @@ -251,7 +230,6 @@ struct IntentErrorDescriptionTests { #expect(str.contains("Color Light")) } - @available(iOS 17.0, macOS 14.0, watchOS 10.0, *) @Test func dimmerInvalidValueContainsValueAndItemName() { let error = DimmerRollerValueError.invalidValue(150, "My Dimmer") let str = String(localized: error.localizedStringResource) @@ -259,14 +237,12 @@ struct IntentErrorDescriptionTests { #expect(str.contains("My Dimmer")) } - @available(iOS 17.0, macOS 14.0, watchOS 10.0, *) @Test func locationInvalidLatitudeDescriptionContains90() { let error = LocationValueError.invalidLatitude let str = String(localized: error.localizedStringResource) #expect(str.contains("90")) } - @available(iOS 17.0, macOS 14.0, watchOS 10.0, *) @Test func locationInvalidLongitudeDescriptionContains180() { let error = LocationValueError.invalidLongitude let str = String(localized: error.localizedStringResource) From c73f2ec960e6d4dc5d0071952dac1da572be4b7d Mon Sep 17 00:00:00 2001 From: DigiH <17110652+DigiH@users.noreply.github.com> Date: Mon, 1 Jun 2026 19:54:46 +0200 Subject: [PATCH 14/91] Make ScrollToTop overlay button conditional (#1231) Signed-off-by: DigiH <17110652+DigiH@users.noreply.github.com> --- openHAB/UI/SwiftUI/SitemapPageView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openHAB/UI/SwiftUI/SitemapPageView.swift b/openHAB/UI/SwiftUI/SitemapPageView.swift index 6649f3eff..7504a24a5 100644 --- a/openHAB/UI/SwiftUI/SitemapPageView.swift +++ b/openHAB/UI/SwiftUI/SitemapPageView.swift @@ -55,7 +55,7 @@ struct SitemapPageView: View { geo.contentOffset.y > 150 } action: { _, isScrolledDown in withAnimation(.easeInOut(duration: 0.2)) { - showScrollToTop = isScrolledDown + showScrollToTop = Preferences.shared.hideStatusBar && isScrolledDown } } .onChange(of: viewModel.pageId) { From c7ab95e9cbfda96aa0ccad6b6aec3baffc535622 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Mon, 1 Jun 2026 22:13:56 +0200 Subject: [PATCH 15/91] Sensor widget: support 1/2/4 items per small/medium/large size Mirrors the Switch widget architecture. Small shows one sensor value prominently; medium and large show rows of label+state pairs with dividers. Accessory variants remain on the small intent/provider. New SensorMediumWidgetItemEntity and SensorLargeWidgetItemEntity carry per-size @IntentParameterDependency so the item picker is scoped to the selected home. OpenHABIconOverlay moved to shared helpers so both Switch and Sensor widgets reference it from one place. Also fixes two bugs that prevented real-time widget updates: - WidgetItemMonitor was reloading the old "OpenHABSensorWidget" kind; update to the three new split kinds (Small/Medium/Large). - ItemEventStream.trackItems() replaced the entire tracked-items set, so OpenHABRootViewController's sseCommandItem call overwrote widget items registered by WidgetItemMonitor. Server stopped sending SSE events for widget items, causing widgets to never receive state-change triggers even with the app in the foreground. Fix: add setItems(_:for:) with per-namespace sets whose union is sent to the server. Signed-off-by: Tim Mueller-Seydlitz --- .../OpenHABCore/Util/ItemEventStream.swift | 20 +- openHAB/UI/OpenHABRootViewController.swift | 2 +- openHAB/WidgetItemMonitor.swift | 8 +- openHABWidget/OpenHABWidgetBundle.swift | 4 +- openHABWidget/OpenHABWidgetHelpers.swift | 18 + .../SensorConfigurationAppIntent.swift | 44 +- .../SensorLargeWidgetItemEntity.swift | 40 ++ .../SensorMediumWidgetItemEntity.swift | 40 ++ openHABWidget/SensorWidgetEntryView.swift | 514 +++++++++--------- openHABWidget/SensorWidgetItemEntity.swift | 2 +- openHABWidget/SensorWidgetView.swift | 56 +- openHABWidget/SwitchWidgetEntryView.swift | 18 - 12 files changed, 468 insertions(+), 298 deletions(-) create mode 100644 openHABWidget/SensorLargeWidgetItemEntity.swift create mode 100644 openHABWidget/SensorMediumWidgetItemEntity.swift diff --git a/OpenHABCore/Sources/OpenHABCore/Util/ItemEventStream.swift b/OpenHABCore/Sources/OpenHABCore/Util/ItemEventStream.swift index 84ddc1309..9d501c724 100644 --- a/OpenHABCore/Sources/OpenHABCore/Util/ItemEventStream.swift +++ b/OpenHABCore/Sources/OpenHABCore/Util/ItemEventStream.swift @@ -64,7 +64,11 @@ public actor EventStream { } } - private var trackedItems: Set = [] + private var trackedItemsByNamespace: [String: Set] = [:] + private var trackedItems: Set { + trackedItemsByNamespace.values.reduce(into: Set()) { $0.formUnion($1) } + } + private var continuations = [UUID: AsyncStream>.Continuation]() private var listenTask: Task? private var networkMonitoringTask: Task? @@ -87,11 +91,17 @@ public actor EventStream { } } - public func trackItems(_ items: [String]) async { - trackedItems = Set(items) + public func setItems(_ items: [String], for namespace: String) async { + trackedItemsByNamespace[namespace] = Set(items) await sendTrackedItemsIfPossible() } + /// Sets items for the ``"default"`` namespace. Use ``setItems(_:for:)`` when multiple + /// callers need to track independent sets simultaneously. + public func trackItems(_ items: [String]) async { + await setItems(items, for: "default") + } + public func startMonitoringNetworkIfNeeded(initialConnection: ConnectionInfo?) { updateConnection(initialConnection) @@ -261,6 +271,10 @@ public extension ItemEventStream { await shared.trackItems(items) } + nonisolated static func setItems(_ items: [String], for namespace: String) async { + await shared.setItems(items, for: namespace) + } + static func startMonitoringNetwork(initialConnection: ConnectionInfo? = nil) async { await shared.startMonitoringNetworkIfNeeded(initialConnection: initialConnection) } diff --git a/openHAB/UI/OpenHABRootViewController.swift b/openHAB/UI/OpenHABRootViewController.swift index 328ebe687..bec438a84 100644 --- a/openHAB/UI/OpenHABRootViewController.swift +++ b/openHAB/UI/OpenHABRootViewController.swift @@ -404,7 +404,7 @@ class OpenHABRootViewController: UIViewController { localConnectionConfig, remoteConnectionConfig ]) - await ItemEventStream.trackItems(sseCommandItem.isEmpty ? [] : [sseCommandItem]) + await ItemEventStream.setItems(sseCommandItem.isEmpty ? [] : [sseCommandItem], for: "notification") } } } diff --git a/openHAB/WidgetItemMonitor.swift b/openHAB/WidgetItemMonitor.swift index 4e945784b..c7bd34a86 100644 --- a/openHAB/WidgetItemMonitor.swift +++ b/openHAB/WidgetItemMonitor.swift @@ -89,12 +89,12 @@ class WidgetItemMonitor { let itemNames = await WidgetItemRegistry.shared.getAllItemNames() guard !itemNames.isEmpty else { - await ItemEventStream.trackItems([]) + await ItemEventStream.setItems([], for: "widget") return } Logger.widgets.info("Tracking \(itemNames.count) widget items: \(itemNames.joined(separator: ", "))") - await ItemEventStream.trackItems(itemNames) + await ItemEventStream.setItems(itemNames, for: "widget") } private func handle(_ message: StreamOutput) async { @@ -131,6 +131,8 @@ class WidgetItemMonitor { WidgetCenter.shared.reloadTimelines(ofKind: "OpenHABSwitchWidgetSmall") WidgetCenter.shared.reloadTimelines(ofKind: "OpenHABSwitchWidgetMedium") WidgetCenter.shared.reloadTimelines(ofKind: "OpenHABSwitchWidgetLarge") - WidgetCenter.shared.reloadTimelines(ofKind: "OpenHABSensorWidget") + WidgetCenter.shared.reloadTimelines(ofKind: "OpenHABSensorWidgetSmall") + WidgetCenter.shared.reloadTimelines(ofKind: "OpenHABSensorWidgetMedium") + WidgetCenter.shared.reloadTimelines(ofKind: "OpenHABSensorWidgetLarge") } } diff --git a/openHABWidget/OpenHABWidgetBundle.swift b/openHABWidget/OpenHABWidgetBundle.swift index e534518b5..48060c61c 100644 --- a/openHABWidget/OpenHABWidgetBundle.swift +++ b/openHABWidget/OpenHABWidgetBundle.swift @@ -18,6 +18,8 @@ struct OpenHABWidgetBundle: WidgetBundle { SwitchSmallWidget() SwitchMediumWidget() SwitchLargeWidget() - SensorWidgetView() + SensorSmallWidget() + SensorMediumWidget() + SensorLargeWidget() } } diff --git a/openHABWidget/OpenHABWidgetHelpers.swift b/openHABWidget/OpenHABWidgetHelpers.swift index 00618d5ed..b6cc6f32e 100644 --- a/openHABWidget/OpenHABWidgetHelpers.swift +++ b/openHABWidget/OpenHABWidgetHelpers.swift @@ -13,6 +13,24 @@ import OpenHABCore internal import SFSafeSymbols import SwiftUI +// MARK: - Shared Icon Overlay + +struct OpenHABIconOverlay: View { + let size: CGFloat + var leadingPadding: CGFloat = 14 + var topPadding: CGFloat = 14 + + var body: some View { + Image("openHABIcon") + .resizable() + .scaledToFit() + .frame(width: size, height: size) + .opacity(0.5) + .padding(.leading, leadingPadding) + .padding(.top, topPadding) + } +} + // MARK: - Item Type Icons /// Returns an SF Symbol icon for the given openHAB item type diff --git a/openHABWidget/SensorConfigurationAppIntent.swift b/openHABWidget/SensorConfigurationAppIntent.swift index 7cbffcfb4..0813689a2 100644 --- a/openHABWidget/SensorConfigurationAppIntent.swift +++ b/openHABWidget/SensorConfigurationAppIntent.swift @@ -12,7 +12,9 @@ import AppIntents import Foundation -struct SensorConfigurationAppIntent: WidgetConfigurationIntent { +// MARK: - Small / accessory (1 item) + +struct SensorSmallConfigurationAppIntent: WidgetConfigurationIntent { static let title: LocalizedStringResource = "Sensor Widget Configuration" static let description = IntentDescription("Configure which sensor item to display in the widget.") @@ -20,5 +22,43 @@ struct SensorConfigurationAppIntent: WidgetConfigurationIntent { var home: Home? @Parameter(title: "Sensor Item") - var itemEntity: SensorWidgetItemEntity? + var item1: SensorWidgetItemEntity? +} + +// MARK: - Medium (2 items) + +struct SensorMediumConfigurationAppIntent: WidgetConfigurationIntent { + static let title: LocalizedStringResource = "Sensor Widget Configuration" + static let description = IntentDescription("Configure which sensor items to display in the widget.") + + @Parameter(title: "Home") + var home: Home? + + @Parameter(title: "Sensor Item 1") + var item1: SensorMediumWidgetItemEntity? + + @Parameter(title: "Sensor Item 2") + var item2: SensorMediumWidgetItemEntity? +} + +// MARK: - Large (4 items) + +struct SensorLargeConfigurationAppIntent: WidgetConfigurationIntent { + static let title: LocalizedStringResource = "Sensor Widget Configuration" + static let description = IntentDescription("Configure which sensor items to display in the widget.") + + @Parameter(title: "Home") + var home: Home? + + @Parameter(title: "Sensor Item 1") + var item1: SensorLargeWidgetItemEntity? + + @Parameter(title: "Sensor Item 2") + var item2: SensorLargeWidgetItemEntity? + + @Parameter(title: "Sensor Item 3") + var item3: SensorLargeWidgetItemEntity? + + @Parameter(title: "Sensor Item 4") + var item4: SensorLargeWidgetItemEntity? } diff --git a/openHABWidget/SensorLargeWidgetItemEntity.swift b/openHABWidget/SensorLargeWidgetItemEntity.swift new file mode 100644 index 000000000..d8dde0064 --- /dev/null +++ b/openHABWidget/SensorLargeWidgetItemEntity.swift @@ -0,0 +1,40 @@ +// Copyright (c) 2010-2026 Contributors to the openHAB project +// +// See the NOTICE file(s) distributed with this work for additional +// information. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0 +// +// SPDX-License-Identifier: EPL-2.0 + +import AppIntents +import OpenHABCore + +struct SensorLargeWidgetItemEntity: ItemEntity { + struct SensorLargeWidgetItemQuery: ItemEntityQuery { + typealias EntityType = SensorLargeWidgetItemEntity + + @IntentParameterDependency(\.$home) + var intent + + var allowedTypes: [OpenHABItem.ItemType] = [.number, .numberWithDimension, .stringItem] + var selectedHome: Home? { + intent?.home + } + } + + static let typeDisplayRepresentation = TypeDisplayRepresentation(name: "Sensor Item") + static let defaultQuery = SensorLargeWidgetItemQuery() + + var id: ItemIdentifier + var item: OpenHABItem + var homeName: String? + + init(id: ItemIdentifier, item: OpenHABItem, homeName: String? = nil) { + self.id = id + self.item = item + self.homeName = homeName + } +} diff --git a/openHABWidget/SensorMediumWidgetItemEntity.swift b/openHABWidget/SensorMediumWidgetItemEntity.swift new file mode 100644 index 000000000..df4ac0b14 --- /dev/null +++ b/openHABWidget/SensorMediumWidgetItemEntity.swift @@ -0,0 +1,40 @@ +// Copyright (c) 2010-2026 Contributors to the openHAB project +// +// See the NOTICE file(s) distributed with this work for additional +// information. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0 +// +// SPDX-License-Identifier: EPL-2.0 + +import AppIntents +import OpenHABCore + +struct SensorMediumWidgetItemEntity: ItemEntity { + struct SensorMediumWidgetItemQuery: ItemEntityQuery { + typealias EntityType = SensorMediumWidgetItemEntity + + @IntentParameterDependency(\.$home) + var intent + + var allowedTypes: [OpenHABItem.ItemType] = [.number, .numberWithDimension, .stringItem] + var selectedHome: Home? { + intent?.home + } + } + + static let typeDisplayRepresentation = TypeDisplayRepresentation(name: "Sensor Item") + static let defaultQuery = SensorMediumWidgetItemQuery() + + var id: ItemIdentifier + var item: OpenHABItem + var homeName: String? + + init(id: ItemIdentifier, item: OpenHABItem, homeName: String? = nil) { + self.id = id + self.item = item + self.homeName = homeName + } +} diff --git a/openHABWidget/SensorWidgetEntryView.swift b/openHABWidget/SensorWidgetEntryView.swift index e2755e7e7..894c7662e 100644 --- a/openHABWidget/SensorWidgetEntryView.swift +++ b/openHABWidget/SensorWidgetEntryView.swift @@ -20,102 +20,184 @@ import WidgetKit // MARK: - Timeline Entry struct SensorEntry: TimelineEntry { + struct Slot { + let item: OpenHABItem + let homeUUID: UUID + } + let date: Date - let configuration: SensorConfigurationAppIntent - let item: OpenHABItem? - let homeUUID: UUID? + let home: Home? + /// Always contains exactly as many elements as the widget size supports (1, 2, or 4). + /// nil means the slot is not configured. + let slots: [Slot?] +} + +// MARK: - Shared provider logic + +private protocol SensorSlotResolvable { + var homeId: UUID? { get } + var item: OpenHABItem { get } +} + +private func resolveSlot(entity: (any SensorSlotResolvable)?) async -> SensorEntry.Slot? { + guard let entity, let homeUUID = entity.homeId else { return nil } + await WidgetItemRegistry.shared.registerItem(name: entity.item.name, homeId: homeUUID) + let refreshed = await OpenHABItemCache.instance.getItemUncached(name: entity.item.name, home: homeUUID) + return SensorEntry.Slot(item: refreshed ?? entity.item, homeUUID: homeUUID) +} + +// MARK: - Sample data + +private func sampleItem(name: String, label: String, state: String) -> OpenHABItem { + OpenHABItem( + name: name, type: "Number:Temperature", state: state, link: "", + label: label, groupType: nil, stateDescription: nil, + commandDescription: nil, members: [], category: "temperature", options: nil + ) } -// MARK: - Timeline Provider +private let sampleSlots: [SensorEntry.Slot?] = { + let uuid = UUID() + return [ + SensorEntry.Slot(item: sampleItem(name: "LivingRoomTemp", label: "Living Room", state: "22.5 °C"), homeUUID: uuid), + SensorEntry.Slot(item: sampleItem(name: "BedroomHumidity", label: "Bedroom Humidity", state: "65 %"), homeUUID: uuid), + SensorEntry.Slot(item: sampleItem(name: "OutdoorTemp", label: "Outdoors", state: "18.3 °C"), homeUUID: uuid), + SensorEntry.Slot(item: sampleItem(name: "PowerUsage", label: "Power Usage", state: "1.2 kW"), homeUUID: uuid) + ] +}() + +// MARK: - Small provider (1 slot) + +struct SensorSmallProvider: AppIntentTimelineProvider { + typealias Intent = SensorSmallConfigurationAppIntent -struct SensorProvider: AppIntentTimelineProvider { func placeholder(in context: Context) -> SensorEntry { - // Create a sample sensor item for placeholder - let sampleItem = OpenHABItem( - name: "LivingRoomTemperature", - type: "Number:Temperature", - state: "22.5 °C", - link: "", - label: "Living Room Temperature", - groupType: nil, - stateDescription: nil, - commandDescription: nil, - members: [], - category: "temperature", - options: nil - ) - - return SensorEntry( - date: Date(), - configuration: SensorConfigurationAppIntent(), - item: sampleItem, - homeUUID: nil - ) + SensorEntry(date: Date(), home: nil, slots: [sampleSlots[0]]) } - func snapshot(for configuration: SensorConfigurationAppIntent, in context: Context) async -> SensorEntry { - // Show placeholder data in widget gallery - if context.isPreview { - return placeholder(in: context) - } - return await createEntry(for: configuration) + func snapshot(for configuration: Intent, in context: Context) async -> SensorEntry { + context.isPreview ? placeholder(in: context) : await createEntry(for: configuration) } - func timeline(for configuration: SensorConfigurationAppIntent, in context: Context) async -> Timeline { + func timeline(for configuration: Intent, in context: Context) async -> Timeline { let entry = await createEntry(for: configuration) + let next = Calendar.current.date(byAdding: .minute, value: 5, to: Date())! + return Timeline(entries: [entry], policy: .after(next)) + } - // Refresh every 5 minutes - let nextUpdate = Calendar.current.date(byAdding: .minute, value: 5, to: Date())! - return Timeline(entries: [entry], policy: .after(nextUpdate)) + private func createEntry(for configuration: Intent) async -> SensorEntry { + let slot = await resolveSlot(entity: configuration.item1 as (any SensorSlotResolvable)?) + return SensorEntry(date: Date(), home: configuration.home, slots: [slot]) } +} - private func createEntry(for configuration: SensorConfigurationAppIntent) async -> SensorEntry { - guard let itemEntity = configuration.itemEntity else { - return SensorEntry( - date: Date(), - configuration: configuration, - item: nil, - homeUUID: nil - ) - } +// MARK: - Medium provider (2 slots) + +struct SensorMediumProvider: AppIntentTimelineProvider { + typealias Intent = SensorMediumConfigurationAppIntent + + func placeholder(in context: Context) -> SensorEntry { + SensorEntry(date: Date(), home: nil, slots: Array(sampleSlots.prefix(2))) + } + + func snapshot(for configuration: Intent, in context: Context) async -> SensorEntry { + context.isPreview ? placeholder(in: context) : await createEntry(for: configuration) + } + + func timeline(for configuration: Intent, in context: Context) async -> Timeline { + let entry = await createEntry(for: configuration) + let next = Calendar.current.date(byAdding: .minute, value: 5, to: Date())! + return Timeline(entries: [entry], policy: .after(next)) + } + + private func createEntry(for configuration: Intent) async -> SensorEntry { + async let s1 = resolveSlot(entity: configuration.item1 as (any SensorSlotResolvable)?) + async let s2 = resolveSlot(entity: configuration.item2 as (any SensorSlotResolvable)?) + return await SensorEntry(date: Date(), home: configuration.home, slots: [s1, s2]) + } +} + +// MARK: - Large provider (4 slots) + +struct SensorLargeProvider: AppIntentTimelineProvider { + typealias Intent = SensorLargeConfigurationAppIntent + + func placeholder(in context: Context) -> SensorEntry { + SensorEntry(date: Date(), home: nil, slots: sampleSlots) + } + + func snapshot(for configuration: Intent, in context: Context) async -> SensorEntry { + context.isPreview ? placeholder(in: context) : await createEntry(for: configuration) + } - // Get item from entity - let item = itemEntity.item - guard let homeUUID = itemEntity.homeId else { - return SensorEntry( - date: Date(), - configuration: configuration, - item: item, - homeUUID: nil - ) + func timeline(for configuration: Intent, in context: Context) async -> Timeline { + let entry = await createEntry(for: configuration) + let next = Calendar.current.date(byAdding: .minute, value: 5, to: Date())! + return Timeline(entries: [entry], policy: .after(next)) + } + + private func createEntry(for configuration: Intent) async -> SensorEntry { + async let s1 = resolveSlot(entity: configuration.item1 as (any SensorSlotResolvable)?) + async let s2 = resolveSlot(entity: configuration.item2 as (any SensorSlotResolvable)?) + async let s3 = resolveSlot(entity: configuration.item3 as (any SensorSlotResolvable)?) + async let s4 = resolveSlot(entity: configuration.item4 as (any SensorSlotResolvable)?) + return await SensorEntry(date: Date(), home: configuration.home, slots: [s1, s2, s3, s4]) + } +} + +// MARK: - Unconfigured placeholder + +private struct UnconfiguredPlaceholder: View { + var body: some View { + VStack { + Image(systemSymbol: .gear) + .font(.largeTitle) + .foregroundColor(.secondary) + Text("Configure Widget") + .font(.caption) + .foregroundColor(.secondary) + .multilineTextAlignment(.center) } + } +} - // Register this item for monitoring in the main app - await WidgetItemRegistry.shared.registerItem(name: item.name, homeId: homeUUID) +// MARK: - Item Row (used in multi-item layouts) - // Refresh the item state from cache - let refreshedItem = await OpenHABItemCache.instance.getItemUncached(name: item.name, home: homeUUID) +private struct SensorItemRow: View { + let slot: SensorEntry.Slot - return SensorEntry( - date: Date(), - configuration: configuration, - item: refreshedItem ?? item, - homeUUID: homeUUID - ) + var body: some View { + let item = slot.item + let label = item.label.isEmpty ? item.name : item.label + HStack { + Text(label) + .font(.subheadline) + .fontWeight(.semibold) + .lineLimit(1) + .minimumScaleFactor(0.8) + .frame(maxWidth: .infinity, alignment: .leading) + Text(item.state ?? "—") + .font(.subheadline) + .fontWeight(.semibold) + .lineLimit(1) + .minimumScaleFactor(0.8) + .foregroundStyle(.secondary) + } } } -// MARK: - Small Widget +// MARK: - Small Widget (1 item) struct SensorSmallWidgetView: View { let entry: SensorEntry var body: some View { ZStack(alignment: .topLeading) { - VStack(spacing: 8) { - if let item = entry.item { - let itemLabel = item.label.isEmpty ? item.name : item.label - Text(itemLabel) + if let slot = entry.slots.compactMap(\.self).first { + let item = slot.item + let label = item.label.isEmpty ? item.name : item.label + VStack(spacing: 8) { + Text(label) .font(.headline) .lineLimit(1) .minimumScaleFactor(0.7) @@ -123,216 +205,124 @@ struct SensorSmallWidgetView: View { Spacer() - if let itemState = item.state { - Text(itemState) - .font(.title2) - .fontWeight(.bold) - .lineLimit(2) - .minimumScaleFactor(0.5) - .multilineTextAlignment(.center) - } else { - Text("—") - .font(.title2) - .foregroundColor(.secondary) - } + Text(item.state ?? "—") + .font(.title2) + .fontWeight(.bold) + .lineLimit(2) + .minimumScaleFactor(0.5) + .multilineTextAlignment(.center) Spacer() - } else { - VStack { - Image(systemSymbol: .gear) - .font(.largeTitle) - .foregroundColor(.secondary) - Text("Configure Widget") - .font(.caption) - .foregroundColor(.secondary) - .multilineTextAlignment(.center) - } } + .frame(maxHeight: .infinity) + .padding() + } else { + UnconfiguredPlaceholder() + .frame(maxWidth: .infinity, maxHeight: .infinity) + .padding() } - .frame(maxHeight: .infinity) - .padding() - - // Widget indicator icon - Image("openHABIcon") - .resizable() - .scaledToFit() - .frame(width: 16, height: 16) - .opacity(0.5) - .padding(8) + + OpenHABIconOverlay(size: 16) } } } -// MARK: - Medium Widget +// MARK: - Medium Widget (2 items) struct SensorMediumWidgetView: View { let entry: SensorEntry var body: some View { ZStack(alignment: .topLeading) { - HStack(spacing: 16) { - VStack(alignment: .leading, spacing: 8) { - if let item = entry.item { - let itemLabel = item.label.isEmpty ? item.name : item.label - Text(itemLabel) - .font(.title3) - .fontWeight(.semibold) - .lineLimit(2) - .padding(.top, 20) - - Spacer() - - if let itemState = item.state { - Text(itemState) - .font(.system(size: 36, weight: .bold)) - .foregroundColor(.primary) - .lineLimit(2) - } else { - Text("No Data") - .font(.title) - .foregroundColor(.secondary) - } - - Text(item.type?.rawValue ?? "Sensor") - .font(.caption) - .foregroundColor(.secondary) - } else { - VStack(alignment: .leading) { - Image(systemSymbol: .gear) - .font(.largeTitle) - .foregroundColor(.secondary) - Text("Configure Widget") - .font(.subheadline) - .foregroundColor(.secondary) + let filledSlots = entry.slots.compactMap(\.self) + if filledSlots.isEmpty { + UnconfiguredPlaceholder() + .frame(maxWidth: .infinity, maxHeight: .infinity) + .padding() + } else { + VStack(alignment: .leading, spacing: 0) { + ForEach(filledSlots.indices, id: \.self) { index in + SensorItemRow(slot: filledSlots[index]) + .padding(.horizontal) + .padding(.vertical, 10) + if index < filledSlots.count - 1 { + Divider() + .padding(.leading) } } } - - Spacer() + .frame(maxHeight: .infinity) + .padding(.top, 22) } - .frame(maxHeight: .infinity) - .padding() - - // Widget indicator icon - Image("openHABIcon") - .resizable() - .scaledToFit() - .frame(width: 18, height: 18) - .opacity(0.5) - .padding(8) + + OpenHABIconOverlay(size: 18) } } } -// MARK: - Large Widget +// MARK: - Large Widget (4 items) struct SensorLargeWidgetView: View { let entry: SensorEntry var body: some View { ZStack(alignment: .topLeading) { - VStack(alignment: .leading, spacing: 16) { - if let item = entry.item { - let itemLabel = item.label.isEmpty ? item.name : item.label - Text(itemLabel) - .font(.title) - .fontWeight(.bold) - .padding(.top, 20) - - HStack { - VStack(alignment: .leading, spacing: 8) { - Text("Current Value") - .font(.caption) - .foregroundColor(.secondary) - - if let itemState = item.state { - Text(itemState) - .font(.system(size: 48, weight: .bold)) - .foregroundColor(.primary) - .lineLimit(2) - } else { - Text("—") - .font(.system(size: 48)) - .foregroundColor(.secondary) - } + let filledSlots = entry.slots.compactMap(\.self) + if filledSlots.isEmpty { + VStack(alignment: .center, spacing: 16) { + Image(systemSymbol: .gear) + .font(.system(size: 60)) + .foregroundColor(.secondary) + Text("Configure Widget") + .font(.title2) + .foregroundColor(.secondary) + Text("Long press the widget to configure which sensors to display") + .font(.caption) + .foregroundColor(.secondary) + .multilineTextAlignment(.center) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + .padding() + } else { + VStack(alignment: .leading, spacing: 0) { + ForEach(filledSlots.indices, id: \.self) { index in + SensorItemRow(slot: filledSlots[index]) + .padding(.horizontal) + .padding(.vertical, 12) + if index < filledSlots.count - 1 { + Divider() + .padding(.leading) } - - Spacer() - } - - HStack { - Image(systemSymbol: sensorIcon(for: item.type)) - .font(.caption) - Text(item.type?.rawValue ?? "Sensor") - .font(.caption) - } - .foregroundColor(.secondary) - - Spacer() - } else { - VStack(alignment: .center, spacing: 16) { - Image(systemSymbol: .gear) - .font(.system(size: 60)) - .foregroundColor(.secondary) - Text("Configure Widget") - .font(.title2) - .foregroundColor(.secondary) - Text("Long press the widget to configure which sensor to display") - .font(.caption) - .foregroundColor(.secondary) - .multilineTextAlignment(.center) } - .frame(maxWidth: .infinity, maxHeight: .infinity) } + .frame(maxHeight: .infinity) + .padding(.top, 22) } - .frame(maxHeight: .infinity) - .padding() - - // Widget indicator icon - Image("openHABIcon") - .resizable() - .scaledToFit() - .frame(width: 20, height: 20) - .opacity(0.5) - .padding(12) - } - } - private func sensorIcon(for type: OpenHABItem.ItemType?) -> SFSymbol { - guard let type else { return .gaugeWithDotsNeedleBottom50percent } - switch type { - case .number, .numberWithDimension: - return .gaugeWithDotsNeedleBottom50percent - case .stringItem: - return .textQuote - default: - return .gaugeWithDotsNeedleBottom50percent + OpenHABIconOverlay(size: 20) } } } -// MARK: - Accessory Views +// MARK: - Accessory Views (share small provider / entry) struct SensorAccessoryCircularView: View { let entry: SensorEntry var body: some View { - if let item = entry.item, let itemState = item.state { - ZStack { - AccessoryWidgetBackground() + ZStack { + AccessoryWidgetBackground() + if let slot = entry.slots.compactMap(\.self).first, let state = slot.item.state { VStack(spacing: 2) { Image(systemSymbol: .gaugeWithDotsNeedleBottom50percent) .font(.caption) - Text(itemState) + Text(state) .font(.caption2) .fontWeight(.bold) .lineLimit(1) .minimumScaleFactor(0.5) } - } - } else { - ZStack { - AccessoryWidgetBackground() + } else { Image(systemSymbol: .gear) .font(.title3) } @@ -344,16 +334,16 @@ struct SensorAccessoryRectangularView: View { let entry: SensorEntry var body: some View { - if let item = entry.item { - let itemLabel = item.label.isEmpty ? item.name : item.label + if let slot = entry.slots.compactMap(\.self).first { + let item = slot.item + let label = item.label.isEmpty ? item.name : item.label VStack(alignment: .leading, spacing: 2) { - Text(itemLabel) + Text(label) .font(.headline) .lineLimit(1) .minimumScaleFactor(0.7) - - if let itemState = item.state { - Text(itemState) + if let state = item.state { + Text(state) .font(.body) .fontWeight(.semibold) .lineLimit(1) @@ -374,12 +364,13 @@ struct SensorAccessoryInlineView: View { let entry: SensorEntry var body: some View { - if let item = entry.item { - let itemLabel = item.label.isEmpty ? item.name : item.label - if let itemState = item.state { - Text("\(itemLabel): \(itemState)") + if let slot = entry.slots.compactMap(\.self).first { + let item = slot.item + let label = item.label.isEmpty ? item.name : item.label + if let state = item.state { + Text("\(label): \(state)") } else { - Text(itemLabel) + Text(label) } } else { Text("Configure Widget") @@ -387,7 +378,7 @@ struct SensorAccessoryInlineView: View { } } -// MARK: - Widget View +// MARK: - Unified Entry View (dispatches by family) struct SensorWidgetEntryView: View { var entry: SensorEntry @@ -413,27 +404,26 @@ struct SensorWidgetEntryView: View { } } -// MARK: - Preview +extension SensorWidgetItemEntity: SensorSlotResolvable {} +extension SensorMediumWidgetItemEntity: SensorSlotResolvable {} +extension SensorLargeWidgetItemEntity: SensorSlotResolvable {} + +// MARK: - Previews -#Preview(as: .systemSmall) { - SensorWidgetView() +#Preview("Small", as: .systemSmall) { + SensorSmallWidget() } timeline: { - SensorEntry( - date: .now, - configuration: SensorConfigurationAppIntent(), - item: OpenHABItem( - name: "LivingRoomTemperature", - type: "Number:Temperature", - state: "22.5 °C", - link: "", - label: "Living Room Temperature", - groupType: nil, - stateDescription: nil, - commandDescription: nil, - members: [], - category: "temperature", - options: nil - ), - homeUUID: nil - ) + SensorEntry(date: .now, home: nil, slots: [sampleSlots[0]]) +} + +#Preview("Medium", as: .systemMedium) { + SensorMediumWidget() +} timeline: { + SensorEntry(date: .now, home: nil, slots: Array(sampleSlots.prefix(2))) +} + +#Preview("Large", as: .systemLarge) { + SensorLargeWidget() +} timeline: { + SensorEntry(date: .now, home: nil, slots: sampleSlots) } diff --git a/openHABWidget/SensorWidgetItemEntity.swift b/openHABWidget/SensorWidgetItemEntity.swift index 2d3b97097..5a9410b85 100644 --- a/openHABWidget/SensorWidgetItemEntity.swift +++ b/openHABWidget/SensorWidgetItemEntity.swift @@ -16,7 +16,7 @@ struct SensorWidgetItemEntity: ItemEntity { struct SensorWidgetItemQuery: ItemEntityQuery { typealias EntityType = SensorWidgetItemEntity - @IntentParameterDependency(\.$home) + @IntentParameterDependency(\.$home) var intent var allowedTypes: [OpenHABItem.ItemType] = [.number, .numberWithDimension, .stringItem] diff --git a/openHABWidget/SensorWidgetView.swift b/openHABWidget/SensorWidgetView.swift index d4360de54..28debccd9 100644 --- a/openHABWidget/SensorWidgetView.swift +++ b/openHABWidget/SensorWidgetView.swift @@ -12,24 +12,24 @@ import SwiftUI import WidgetKit -struct SensorWidgetView: Widget { - let kind = "OpenHABSensorWidget" +// MARK: - Small + accessory widget (1 item) + +struct SensorSmallWidget: Widget { + let kind = "OpenHABSensorWidgetSmall" var body: some WidgetConfiguration { AppIntentConfiguration( kind: kind, - intent: SensorConfigurationAppIntent.self, - provider: SensorProvider() + intent: SensorSmallConfigurationAppIntent.self, + provider: SensorSmallProvider() ) { entry in SensorWidgetEntryView(entry: entry) .containerBackground(.fill.tertiary, for: .widget) } .configurationDisplayName("openHAB Sensor") - .description("Display your openHAB sensor values") + .description("Display an openHAB sensor value") .supportedFamilies([ .systemSmall, - .systemMedium, - .systemLarge, .accessoryCircular, .accessoryRectangular, .accessoryInline @@ -37,3 +37,45 @@ struct SensorWidgetView: Widget { .widgetContentMarginsDisabled() } } + +// MARK: - Medium widget (2 items) + +struct SensorMediumWidget: Widget { + let kind = "OpenHABSensorWidgetMedium" + + var body: some WidgetConfiguration { + AppIntentConfiguration( + kind: kind, + intent: SensorMediumConfigurationAppIntent.self, + provider: SensorMediumProvider() + ) { entry in + SensorWidgetEntryView(entry: entry) + .containerBackground(.fill.tertiary, for: .widget) + } + .configurationDisplayName("openHAB Sensor") + .description("Display two openHAB sensor values") + .supportedFamilies([.systemMedium]) + .widgetContentMarginsDisabled() + } +} + +// MARK: - Large widget (4 items) + +struct SensorLargeWidget: Widget { + let kind = "OpenHABSensorWidgetLarge" + + var body: some WidgetConfiguration { + AppIntentConfiguration( + kind: kind, + intent: SensorLargeConfigurationAppIntent.self, + provider: SensorLargeProvider() + ) { entry in + SensorWidgetEntryView(entry: entry) + .containerBackground(.fill.tertiary, for: .widget) + } + .configurationDisplayName("openHAB Sensor") + .description("Display up to four openHAB sensor values") + .supportedFamilies([.systemLarge]) + .widgetContentMarginsDisabled() + } +} diff --git a/openHABWidget/SwitchWidgetEntryView.swift b/openHABWidget/SwitchWidgetEntryView.swift index ed7967e3d..aa7bc2402 100644 --- a/openHABWidget/SwitchWidgetEntryView.swift +++ b/openHABWidget/SwitchWidgetEntryView.swift @@ -229,24 +229,6 @@ private struct UnconfiguredPlaceholder: View { } } -// MARK: - openHAB icon overlay - -private struct OpenHABIconOverlay: View { - let size: CGFloat - var leadingPadding: CGFloat = 14 - var topPadding: CGFloat = 14 - - var body: some View { - Image("openHABIcon") - .resizable() - .scaledToFit() - .frame(width: size, height: size) - .opacity(0.5) - .padding(.leading, leadingPadding) - .padding(.top, topPadding) - } -} - // MARK: - Small Widget (1 item) struct SwitchSmallWidgetView: View { From 984b82cc826a50704142259e1beaa6a57835d38a Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Mon, 1 Jun 2026 22:44:43 +0200 Subject: [PATCH 16/91] Sensor widget: format state values using stateDescription numberPattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apply the item's printf-style numberPattern (e.g. "%.1f °C") with the current locale when available, falling back to the raw state string. Covers all display sites: small, medium/large rows, and all three accessory variants. Signed-off-by: Tim Mueller-Seydlitz --- openHABWidget/SensorWidgetEntryView.swift | 25 +++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/openHABWidget/SensorWidgetEntryView.swift b/openHABWidget/SensorWidgetEntryView.swift index 894c7662e..f61a64f51 100644 --- a/openHABWidget/SensorWidgetEntryView.swift +++ b/openHABWidget/SensorWidgetEntryView.swift @@ -145,6 +145,15 @@ struct SensorLargeProvider: AppIntentTimelineProvider { } } +// MARK: - State formatting + +private func formattedState(for item: OpenHABItem) -> String { + guard let state = item.state else { return "—" } + guard let pattern = item.stateDescription?.numberPattern, !pattern.isEmpty else { return state } + let formatted = state.parseAsNumber(format: pattern).toString(locale: .current) + return formatted.isEmpty ? state : formatted +} + // MARK: - Unconfigured placeholder private struct UnconfiguredPlaceholder: View { @@ -176,7 +185,7 @@ private struct SensorItemRow: View { .lineLimit(1) .minimumScaleFactor(0.8) .frame(maxWidth: .infinity, alignment: .leading) - Text(item.state ?? "—") + Text(formattedState(for: item)) .font(.subheadline) .fontWeight(.semibold) .lineLimit(1) @@ -205,7 +214,7 @@ struct SensorSmallWidgetView: View { Spacer() - Text(item.state ?? "—") + Text(formattedState(for: item)) .font(.title2) .fontWeight(.bold) .lineLimit(2) @@ -312,11 +321,11 @@ struct SensorAccessoryCircularView: View { var body: some View { ZStack { AccessoryWidgetBackground() - if let slot = entry.slots.compactMap(\.self).first, let state = slot.item.state { + if let slot = entry.slots.compactMap(\.self).first, slot.item.state != nil { VStack(spacing: 2) { Image(systemSymbol: .gaugeWithDotsNeedleBottom50percent) .font(.caption) - Text(state) + Text(formattedState(for: slot.item)) .font(.caption2) .fontWeight(.bold) .lineLimit(1) @@ -342,8 +351,8 @@ struct SensorAccessoryRectangularView: View { .font(.headline) .lineLimit(1) .minimumScaleFactor(0.7) - if let state = item.state { - Text(state) + if item.state != nil { + Text(formattedState(for: item)) .font(.body) .fontWeight(.semibold) .lineLimit(1) @@ -367,8 +376,8 @@ struct SensorAccessoryInlineView: View { if let slot = entry.slots.compactMap(\.self).first { let item = slot.item let label = item.label.isEmpty ? item.name : item.label - if let state = item.state { - Text("\(label): \(state)") + if item.state != nil { + Text("\(label): \(formattedState(for: item))") } else { Text(label) } From 89c3ac924d85cba0ae5cac4293c086e8b2268284 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Fri, 12 Jun 2026 14:46:48 +0200 Subject: [PATCH 17/91] Merge develop + fix build warnings; add iOS home screen widgets with item display - Merge develop (watchOS complications, sitemap diagnostics, notification handling, sub-page navigation, foreground-refresh, WebRowView isolation, GetItemStateIntent improvements, and more) - Add iOS home screen sensor and switch widgets displaying item state - Remove AppIntents/iOS16 shims (deployment target is iOS 18) - Fix SwiftLint file_name/file_types_order violations in widget files - Fix deprecated onChange(of:perform:) to two-parameter closure form Signed-off-by: Tim Mueller-Seydlitz --- .gitignore | 2 + AGENTS.md | 1 + AppIntents/Intents/GetItemStateIntent.swift | 25 +- CHANGELOG.md | 38 ++ .../OpenHABCore/Model/OpenHABItem.swift | 7 +- .../Model/OpenHABNotification.swift | 129 ++++++- .../Model/PushNotificationPayload.swift | 76 ++++ .../Sources/OpenHABCore/Util/HTTPClient.swift | 2 +- .../OpenHABCore/Util/OpenHABItemCache.swift | 17 +- .../Assets.xcassets/Contents.json | 6 + .../Contents.json | 12 + .../OHComplicationCorner.png | Bin 0 -> 4767 bytes .../OHComplicationIcon.imageset/Contents.json | 12 + .../OHComplicationIcon.png | Bin 0 -> 5700 bytes OpenHABWatchComplications/Info.plist | 11 + .../OpenHABWatchComplications.swift | 94 +++++ .../OpenHABWatchComplicationsBundle.swift | 20 + Version.xcconfig | 4 +- fastlane/Fastfile | 6 + fastlane/Matchfile | 2 +- openHAB.xcodeproj/project.pbxproj | 358 +++++++++++++++++- .../xcshareddata/swiftpm/Package.resolved | 2 +- openHAB/AppDelegate.swift | 15 + openHAB/Models/SitemapPageViewModel.swift | 81 +++- openHAB/NotificationCenterDelegateImpl.swift | 76 +++- .../Supporting Files/Localizable.xcstrings | 10 +- openHAB/UI/HostingSitemapViewController.swift | 10 +- openHAB/UI/NotificationsView.swift | 50 ++- openHAB/UI/OpenHABRootViewController.swift | 45 ++- openHAB/UI/OpenHABWebViewController.swift | 106 +++++- openHAB/UI/SwiftUI/Rows/WebRowView.swift | 34 +- openHAB/UI/SwiftUI/SitemapDiagnostics.swift | 184 +++++++++ .../UI/SwiftUI/SitemapNavigationView.swift | 118 ++++-- openHAB/UI/SwiftUI/SitemapPageView.swift | 15 + .../SitemapDiagnosticsTests.swift | 81 ++++ .../WebRowViewConfigurationTests.swift | 20 +- .../Circular.imageset/Contents.json | 28 -- .../Contents.json | 53 --- .../Extra Large.imageset/Contents.json | 28 -- .../Graphic Bezel.imageset/Contents.json | 28 -- .../Graphic Circular.imageset/Contents.json | 28 -- .../Graphic Corner.imageset/Contents.json | 12 - .../Contents.json | 25 -- .../Contents.json | 12 - .../Modular.imageset/Contents.json | 28 -- .../Utilitarian.imageset/Contents.json | 28 -- .../OHIcon.imageset/Contents.json | 12 - .../Assets.xcassets/OHIcon.imageset/Icon.png | Bin 381270 -> 0 bytes .../OHTemplateIcon.imageset/Contents.json | 16 - .../oh_logo_template.pdf | Bin 6627 -> 0 bytes .../Extension/ComplicationController.swift | 131 ------- openHABWatch/Extension/Info.plist | 2 - ...Helpers.swift => OpenHABIconOverlay.swift} | 0 ...> SensorLargeConfigurationAppIntent.swift} | 0 ...dgetView.swift => SensorLargeWidget.swift} | 0 55 files changed, 1502 insertions(+), 598 deletions(-) create mode 100644 OpenHABCore/Sources/OpenHABCore/Model/PushNotificationPayload.swift create mode 100644 OpenHABWatchComplications/Assets.xcassets/Contents.json create mode 100644 OpenHABWatchComplications/Assets.xcassets/OHComplicationCorner.imageset/Contents.json create mode 100644 OpenHABWatchComplications/Assets.xcassets/OHComplicationCorner.imageset/OHComplicationCorner.png create mode 100644 OpenHABWatchComplications/Assets.xcassets/OHComplicationIcon.imageset/Contents.json create mode 100644 OpenHABWatchComplications/Assets.xcassets/OHComplicationIcon.imageset/OHComplicationIcon.png create mode 100644 OpenHABWatchComplications/Info.plist create mode 100644 OpenHABWatchComplications/OpenHABWatchComplications.swift create mode 100644 OpenHABWatchComplications/OpenHABWatchComplicationsBundle.swift create mode 100644 openHABTestsSwift/SitemapDiagnosticsTests.swift delete mode 100644 openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/Contents.json delete mode 100644 openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Contents.json delete mode 100644 openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Extra Large.imageset/Contents.json delete mode 100644 openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Graphic Bezel.imageset/Contents.json delete mode 100644 openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Graphic Circular.imageset/Contents.json delete mode 100644 openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Graphic Corner.imageset/Contents.json delete mode 100644 openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Graphic Extra Large.imageset/Contents.json delete mode 100644 openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Graphic Large Rectangular.imageset/Contents.json delete mode 100644 openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/Contents.json delete mode 100644 openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/Contents.json delete mode 100644 openHABWatch/Extension/Assets.xcassets/OHIcon.imageset/Contents.json delete mode 100644 openHABWatch/Extension/Assets.xcassets/OHIcon.imageset/Icon.png delete mode 100644 openHABWatch/Extension/Assets.xcassets/OHTemplateIcon.imageset/Contents.json delete mode 100644 openHABWatch/Extension/Assets.xcassets/OHTemplateIcon.imageset/oh_logo_template.pdf delete mode 100644 openHABWatch/Extension/ComplicationController.swift rename openHABWidget/{OpenHABWidgetHelpers.swift => OpenHABIconOverlay.swift} (100%) rename openHABWidget/{SensorConfigurationAppIntent.swift => SensorLargeConfigurationAppIntent.swift} (100%) rename openHABWidget/{SensorWidgetView.swift => SensorLargeWidget.swift} (100%) diff --git a/.gitignore b/.gitignore index ab38f1a05..967ff0538 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,5 @@ skills-lock.json BonjourDiscoveryTool/.build BonjourDiscoveryTool/.swiftpm BonjourDiscoveryTool/bonjour-discovery + +.xcodebuildmcp/ diff --git a/AGENTS.md b/AGENTS.md index f669ca56e..4992b39e4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -38,3 +38,4 @@ ## git - Always use git commit with -s -S +- If using XcodeBuildMCP, use the installed XcodeBuildMCP skill before calling XcodeBuildMCP tools. diff --git a/AppIntents/Intents/GetItemStateIntent.swift b/AppIntents/Intents/GetItemStateIntent.swift index 7d51b770e..b8c58b197 100644 --- a/AppIntents/Intents/GetItemStateIntent.swift +++ b/AppIntents/Intents/GetItemStateIntent.swift @@ -44,11 +44,14 @@ private struct LocalizedItemState: CustomLocalizedStringResourceConvertible { enum ItemStateError: Error, CustomLocalizedStringResourceConvertible { case itemNotInHome(String, String) + case itemNotFound(String) var localizedStringResource: LocalizedStringResource { switch self { case let .itemNotInHome(itemName, homeName): "Item '\(itemName)' is not in home '\(homeName)'" + case let .itemNotFound(itemName): + "Item '\(itemName)' not found" } } } @@ -87,12 +90,26 @@ struct GetItemStateIntent: AppIntent { mismatchError: ItemStateError.itemNotInHome ) - let freshItem = await OpenHABItemCache.instance.getItemUncached(name: itemEntity.itemName, home: homeId) - let state = freshItem?.state ?? itemEntity.item.state ?? "Unknown state" - let stateDisplay = LocalizedItemState(rawValue: state) + guard let item = await OpenHABItemCache.instance.getItemUncached(name: itemEntity.itemName, home: homeId) else { + throw ItemStateError.itemNotFound(itemEntity.itemName) + } + let rawState = item.state ?? "Unknown state" + + // Prefer the server-formatted state, then fall back to local number formatting, + // then fall back to the raw state string. + let displayState: String = if let transformed = item.transformedState, !transformed.isEmpty { + transformed + } else if let pattern = item.stateDescription?.numberPattern, + Double(rawState.components(separatedBy: " ").first ?? "") != nil { + rawState.parseAsNumber(format: pattern).toString(locale: .current) + } else { + rawState + } + + let stateDisplay = LocalizedItemState(rawValue: displayState) return .result( - value: state, + value: displayState, dialog: "The state of \(itemEntity.label) is \(stateDisplay)" ) } diff --git a/CHANGELOG.md b/CHANGELOG.md index 079b03d76..826b3d7d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,44 @@ ## [Unreleased] +## [Version 3.2.72, Build 281] - 2026-06-11Z + +- fix: isolate WebRowView data store from MainUI and fix linkActivated origin check (#1244) +- fix: throw on uncached fetch failure in GetItemStateIntent (#1243) + +## [Version 3.2.71, Build 280] - 2026-06-09Z + +- swiftlinted +- Add sitemap startup diagnostics (#1242) +- fix: defer sub-page navigation until root sitemap poll completes (#842) (#1241) +- fix: only hide navigation bar on explicit goFullscreen signal (#1017) (#1240) +- fix: open custom URL schemes from webview via system handler (#1239) +- fix: refresh sub-page items when app returns to foreground (#1238) +- Re-post notification on action failure so user can retry (#984) (#1233) +- fix: hide UIKit nav bar on sitemap when returning from pushed screens (#1237) + +## [Version 3.2.70, Build 279] - 2026-06-08Z + +- watchOS Complications size and legacy icon removal fix (#1236) + +## [Version 3.2.69, Build 278] - 2026-06-07Z + +- Expose displayState in Get Item State Shortcuts intent (#1235) +- Replace deprecated ClockKit complications with WidgetKit (#1030) (#1234) +- Include group items in Shortcuts intent pickers (#1232) + +- update code sign settings for OpenHABWatchComplications +- Replace deprecated ClockKit complications with WidgetKit (#1030) +- Include group items in Shortcuts intent pickers (#1232) + +## [Version 3.2.67, Build 276] - 2026-06-02Z + +- Notification handling (#1226) + +## [Version 3.2.66, Build 275] - 2026-06-01Z + +- Fix Webview widget URLs and auth for cloud connections (#1230) + ## [Version 3.2.64, Build 273] - 2026-05-31Z diff --git a/OpenHABCore/Sources/OpenHABCore/Model/OpenHABItem.swift b/OpenHABCore/Sources/OpenHABCore/Model/OpenHABItem.swift index 26b594d35..e69a57b34 100644 --- a/OpenHABCore/Sources/OpenHABCore/Model/OpenHABItem.swift +++ b/OpenHABCore/Sources/OpenHABCore/Model/OpenHABItem.swift @@ -35,6 +35,7 @@ public struct OpenHABItem: Sendable { public var groupType: ItemType? public var name = "" public var state: String? + public var transformedState: String? public var link = "" public var label = "" public var stateDescription: OpenHABStateDescription? @@ -53,7 +54,7 @@ public struct OpenHABItem: Sendable { isOfTypeOrGroupType(ItemType.player) } - public init(name: String, type: String, state: String?, link: String, label: String?, groupType: String?, stateDescription: OpenHABStateDescription?, commandDescription: OpenHABCommandDescription?, members: [OpenHABItem], category: String?, options: [OpenHABOptions]?) { + public init(name: String, type: String, state: String?, link: String, label: String?, groupType: String?, stateDescription: OpenHABStateDescription?, commandDescription: OpenHABCommandDescription?, members: [OpenHABItem], category: String?, options: [OpenHABOptions]?, transformedState: String? = nil) { self.name = name self.type = type.toItemType() if let state, state == "NULL" || state == "UNDEF" || state.caseInsensitiveCompare("undefined") == .orderedSame { @@ -61,6 +62,7 @@ public struct OpenHABItem: Sendable { } else { self.state = state } + self.transformedState = transformedState.flatMap { $0.isEmpty ? nil : $0 } self.link = link self.label = label.orEmpty self.groupType = groupType?.toItemType() @@ -152,7 +154,8 @@ extension OpenHABItem { commandDescription: OpenHABCommandDescription(item.commandDescription), members: [], category: item.category, - options: [] + options: [], + transformedState: item.transformedState ) } else { return nil diff --git a/OpenHABCore/Sources/OpenHABCore/Model/OpenHABNotification.swift b/OpenHABCore/Sources/OpenHABCore/Model/OpenHABNotification.swift index 8d87d10dc..e3b252fd3 100644 --- a/OpenHABCore/Sources/OpenHABCore/Model/OpenHABNotification.swift +++ b/OpenHABCore/Sources/OpenHABCore/Model/OpenHABNotification.swift @@ -11,19 +11,63 @@ import Foundation -public struct OpenHABNotification: Sendable { - public var message: String? - public var created: Date? - public var icon: String? - var severity: String? - public var id = "" - - public init(message: String? = nil, created: Date? = nil, icon: String? = nil, severity: String? = nil, id: String = "") { +public struct Payload: Hashable, Sendable { + public let onClick: String? + public let referenceId: String? + public let icon: String? + public let mediaAttachmentURL: URL? + public let tag: String? + public let type: String? + public let message: String? + public let title: String? + public let userId: String? + + public init(onClick: String? = nil, referenceId: String? = nil, icon: String? = nil, mediaAttachmentURL: URL? = nil, tag: String? = nil, type: String? = nil, message: String? = nil, title: String? = nil, userId: String? = nil) { + self.onClick = onClick + self.referenceId = referenceId + self.icon = icon + self.mediaAttachmentURL = mediaAttachmentURL + self.tag = tag + self.type = type + self.message = message + self.title = title + self.userId = userId + } +} + +public struct OpenHABNotification: Identifiable, Hashable, Sendable { + // MARK: - Public properties (domain model) + + public let id: String + public let message: String? + public let icon: String? + public let severity: String? + public let payload: Payload? + public let created: Date? + public let v: Int + + /// A convenient view-facing display string: payload title → root message → payload message + public var title: String { + if let payloadTitle = payload?.title, !payloadTitle.isEmpty { return payloadTitle } + if let msg = message, !msg.isEmpty { return msg } + return payload?.message ?? "" + } + + /// Prefer root message, fall back to payload.message + public init(id: String, + message: String?, + icon: String? = nil, + severity: String? = nil, + payload: Payload? = nil, + created: Date? = nil, + v: Int) { + self.id = id self.message = message - self.created = created self.icon = icon self.severity = severity - self.id = id + self.payload = payload + self.created = created + self.v = v } } @@ -35,20 +79,73 @@ public extension OpenHABNotification { private enum CodingKeys: String, CodingKey { case id = "_id" case message - case v = "__v" + case icon + case severity + case payload case created + case v = "__v" + } + + public struct PayloadInternal: Decodable { + // swiftlint:disable nesting + private enum CodingKeys: String, CodingKey { + case onClick = "on-click" + case referenceId = "reference-id" + case icon + case mediaAttachmentURL = "media-attachment-url" + case tag + case type + case message + case title + case userId + } + + // swiftlint:enable nesting + + public let onClick: String? + public let referenceId: String? + public let icon: String? + public let mediaAttachmentURL: URL? + public let tag: String? + public let type: String? + public let message: String? + public let title: String? + public let userId: String? } - let id: String - let message: String? - let v: Int - let created: Date? + public let id: String + public let message: String? + public let icon: String? + public let severity: String? + public let payload: PayloadInternal? + public let created: Date? + public let v: Int } } /// Convenience method to convert a decoded value into a proper OpenHABNotification instance extension OpenHABNotification.CodingData { var openHABNotification: OpenHABNotification { - OpenHABNotification(message: message, created: created, id: id) + OpenHABNotification( + id: id, + message: message, + icon: icon, + severity: severity, + payload: payload.map { + Payload( + onClick: $0.onClick, + referenceId: $0.referenceId, + icon: $0.icon, + mediaAttachmentURL: $0.mediaAttachmentURL, + tag: $0.tag, + type: $0.type, + message: $0.message, + title: $0.title, + userId: $0.userId + ) + }, + created: created, + v: v + ) } } diff --git a/OpenHABCore/Sources/OpenHABCore/Model/PushNotificationPayload.swift b/OpenHABCore/Sources/OpenHABCore/Model/PushNotificationPayload.swift new file mode 100644 index 000000000..0567c40f3 --- /dev/null +++ b/OpenHABCore/Sources/OpenHABCore/Model/PushNotificationPayload.swift @@ -0,0 +1,76 @@ +// Copyright (c) 2010-2026 Contributors to the openHAB project +// +// See the NOTICE file(s) distributed with this work for additional +// information. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0 +// +// SPDX-License-Identifier: EPL-2.0 + +import Foundation + +public struct NotificationAction: Codable, Sendable { + public let action: String + public let title: String +} + +public struct PushNotificationPayload: Sendable { + public let message: String? + public let title: String? + public let action: String? + public let cloudUserId: String? + public let tag: String? + public let referenceId: String? + public let type: String? + public let actions: [NotificationAction]? + public let mediaAttachmentUrl: URL? + public let category: String? + public let icon: String? + + public var displayMessage: String { + message ?? NSLocalizedString("message_not_decoded", comment: "") + } + + public var isHideNotification: Bool { + type == "hideNotification" + } + + public init(userInfo: [AnyHashable: Any]) { + message = userInfo["message"] as? String + title = userInfo["title"] as? String + + // Handle both actionIdentifier and on-click for backward compatibility + action = (userInfo["actionIdentifier"] as? String) ?? (userInfo["on-click"] as? String) + + icon = userInfo["icon"] as? String + + cloudUserId = userInfo["userId"] as? String + tag = userInfo["tag"] as? String + referenceId = userInfo["reference-id"] as? String + type = userInfo["type"] as? String + + // Parse actions array from JSON string + if let actionsJson = userInfo["actions"] as? String, + let actionsData = actionsJson.data(using: .utf8) { + actions = try? JSONDecoder().decode([NotificationAction].self, from: actionsData) + } else { + actions = nil + } + + // Parse media attachment URL + if let urlString = userInfo["media-attachment-url"] as? String { + mediaAttachmentUrl = URL(string: urlString) + } else { + mediaAttachmentUrl = nil + } + + // Extract category from aps + if let aps = userInfo["aps"] as? [String: Any] { + category = aps["category"] as? String + } else { + category = nil + } + } +} diff --git a/OpenHABCore/Sources/OpenHABCore/Util/HTTPClient.swift b/OpenHABCore/Sources/OpenHABCore/Util/HTTPClient.swift index 590826afb..37cfb4c5c 100644 --- a/OpenHABCore/Sources/OpenHABCore/Util/HTTPClient.swift +++ b/OpenHABCore/Sources/OpenHABCore/Util/HTTPClient.swift @@ -327,7 +327,7 @@ public final class HTTPClient: NSObject, Sendable { return data } - public func notification(urlString: String) async throws -> [OpenHABNotification] { + public func notifications(urlString: String) async throws -> [OpenHABNotification] { guard let url = Endpoint.notification(prefsURL: urlString).url else { throw HTTPClientError.couldNotLoadNotification } let data = try await notification(url: url) diff --git a/OpenHABCore/Sources/OpenHABCore/Util/OpenHABItemCache.swift b/OpenHABCore/Sources/OpenHABCore/Util/OpenHABItemCache.swift index 7cf0d9ade..b70553eb7 100644 --- a/OpenHABCore/Sources/OpenHABCore/Util/OpenHABItemCache.swift +++ b/OpenHABCore/Sources/OpenHABCore/Util/OpenHABItemCache.swift @@ -143,6 +143,7 @@ public actor OpenHABItemCache { let name: String let label: String let type: String? + let groupType: String? } public static let instance = OpenHABItemCache() @@ -235,7 +236,7 @@ public actor OpenHABItemCache { // fallback for intents that target a home the current process hasn't loaded. var allStubs = loadedStubs() for (homeId, homeItems) in items { - allStubs[homeId.uuidString] = homeItems.map { ItemStub(name: $0.name, label: $0.label, type: $0.type?.rawValue) } + allStubs[homeId.uuidString] = homeItems.map { ItemStub(name: $0.name, label: $0.label, type: $0.type?.rawValue, groupType: $0.groupType?.rawValue) } } guard let data = try? JSONEncoder().encode(allStubs) else { return } cachedStubs = allStubs @@ -244,7 +245,7 @@ public actor OpenHABItemCache { private func persistedItem(name: String, home: UUID) -> OpenHABItem? { guard let stub = loadedStubs()[home.uuidString]?.first(where: { $0.name == name }) else { return nil } - return OpenHABItem(name: stub.name, type: stub.type ?? "", state: nil, link: "", label: stub.label, groupType: nil, stateDescription: nil, commandDescription: nil, members: [], category: nil, options: nil) + return OpenHABItem(name: stub.name, type: stub.type ?? "", state: nil, link: "", label: stub.label, groupType: stub.groupType, stateDescription: nil, commandDescription: nil, members: [], category: nil, options: nil) } private func persistedItems(home: UUID) -> [OpenHABItem] { @@ -255,7 +256,7 @@ public actor OpenHABItemCache { state: nil, link: "", label: $0.label, - groupType: nil, + groupType: $0.groupType, stateDescription: nil, commandDescription: nil, members: [], @@ -275,7 +276,7 @@ public actor OpenHABItemCache { public func forceCacheReload(homes: [UUID]) async { Logger.itemCache.info("force cache reload for homes: \(homes)") do { - let loadedItems = try await loadNonGroupItemsForHomes(homes) + let loadedItems = try await loadItemsForHomes(homes) Logger.itemCache.info("Store loaded items in cache") let now = Date.now for (homeId, homeItems) in loadedItems { @@ -314,11 +315,6 @@ public actor OpenHABItemCache { .map(\.name) } - private func loadNonGroupItemsForHomes(_ homes: [UUID]) async throws -> [UUID: [OpenHABItem]] { - let allItemsArray = try await loadItemsForHomes(homes).map { (uuid, items) in (uuid, items.filter { $0.type != .group }) } - return Dictionary(uniqueKeysWithValues: allItemsArray) - } - private func loadItemsForHomes(_ homes: [UUID]) async throws -> [UUID: [OpenHABItem]] { await withThrowingTaskGroup { @Sendable group in for homeId in homes { @@ -486,7 +482,8 @@ public extension OpenHABItem { /// - Parameter types: Optional array of item types to match. If nil, all items match. /// - Returns: True if the item matches the filter, false otherwise func matches(types: [OpenHABItem.ItemType]?) -> Bool { - types == nil || (type.flatMap { types?.contains($0) } == true) + guard let types else { return true } + return types.contains { isOfTypeOrGroupType($0) } } func searchScore(for searchTerm: String, additionalCandidates: [String] = []) -> Int? { diff --git a/OpenHABWatchComplications/Assets.xcassets/Contents.json b/OpenHABWatchComplications/Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/OpenHABWatchComplications/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/OpenHABWatchComplications/Assets.xcassets/OHComplicationCorner.imageset/Contents.json b/OpenHABWatchComplications/Assets.xcassets/OHComplicationCorner.imageset/Contents.json new file mode 100644 index 000000000..9f3996d6a --- /dev/null +++ b/OpenHABWatchComplications/Assets.xcassets/OHComplicationCorner.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "OHComplicationCorner.png", + "idiom" : "watch" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/OpenHABWatchComplications/Assets.xcassets/OHComplicationCorner.imageset/OHComplicationCorner.png b/OpenHABWatchComplications/Assets.xcassets/OHComplicationCorner.imageset/OHComplicationCorner.png new file mode 100644 index 0000000000000000000000000000000000000000..712897db6a9c74b2379cf34124b9d09c805a69ca GIT binary patch literal 4767 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 literal 0 HcmV?d00001 diff --git a/OpenHABWatchComplications/Assets.xcassets/OHComplicationIcon.imageset/Contents.json b/OpenHABWatchComplications/Assets.xcassets/OHComplicationIcon.imageset/Contents.json new file mode 100644 index 000000000..568e8454d --- /dev/null +++ b/OpenHABWatchComplications/Assets.xcassets/OHComplicationIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "OHComplicationIcon.png", + "idiom" : "watch" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/OpenHABWatchComplications/Assets.xcassets/OHComplicationIcon.imageset/OHComplicationIcon.png b/OpenHABWatchComplications/Assets.xcassets/OHComplicationIcon.imageset/OHComplicationIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..a7a8cd4ab8c9635d710e0182dac19b56df25085a GIT binary patch literal 5700 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 R4yS*FYO3j}mMdEY{09m~znuU8 literal 0 HcmV?d00001 diff --git a/OpenHABWatchComplications/Info.plist b/OpenHABWatchComplications/Info.plist new file mode 100644 index 000000000..0f118fb75 --- /dev/null +++ b/OpenHABWatchComplications/Info.plist @@ -0,0 +1,11 @@ + + + + + NSExtension + + NSExtensionPointIdentifier + com.apple.widgetkit-extension + + + diff --git a/OpenHABWatchComplications/OpenHABWatchComplications.swift b/OpenHABWatchComplications/OpenHABWatchComplications.swift new file mode 100644 index 000000000..858a78cd4 --- /dev/null +++ b/OpenHABWatchComplications/OpenHABWatchComplications.swift @@ -0,0 +1,94 @@ +// Copyright (c) 2010-2026 Contributors to the openHAB project +// +// See the NOTICE file(s) distributed with this work for additional +// information. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0 +// +// SPDX-License-Identifier: EPL-2.0 + +import SwiftUI +import WidgetKit + +struct OpenHABWatchComplicationsProvider: TimelineProvider { + func placeholder(in context: Context) -> OpenHABWatchComplicationsEntry { + OpenHABWatchComplicationsEntry(date: Date()) + } + + func getSnapshot(in context: Context, completion: @escaping (OpenHABWatchComplicationsEntry) -> Void) { + let entry = OpenHABWatchComplicationsEntry(date: Date()) + completion(entry) + } + + func getTimeline(in context: Context, completion: @escaping (Timeline) -> Void) { + let entry = OpenHABWatchComplicationsEntry(date: Date()) + let timeline = Timeline(entries: [entry], policy: .never) + completion(timeline) + } +} + +struct OpenHABWatchComplicationsEntry: TimelineEntry { + let date: Date +} + +struct OpenHABComplicationEntryView: View { + var entry: OpenHABWatchComplicationsEntry + @Environment(\.widgetFamily) var family + + var body: some View { + complicationImage + .widgetAccentable() + .containerBackground(for: .widget) { + AccessoryWidgetBackground() + } + .accessibilityLabel("openHAB") + } + + @ViewBuilder + private var complicationImage: some View { + switch family { + case .accessoryCircular: + Image(.ohComplicationIcon) + .resizable() + .scaledToFit() + .padding(2) + case .accessoryCorner: + Image(.ohComplicationCorner) + .resizable() + .scaledToFit() + .padding(4) + default: + EmptyView() + } + } +} + +struct OpenHABWatchComplications: Widget { + let kind = "OpenHABWatchComplications" + + var body: some WidgetConfiguration { + StaticConfiguration(kind: kind, provider: OpenHABWatchComplicationsProvider()) { entry in + OpenHABComplicationEntryView(entry: entry) + } + .configurationDisplayName("openHAB") + .description("Open the openHAB app.") + .supportedFamilies([ + .accessoryCircular, + .accessoryCorner + ]) + } +} + +#Preview("Circular", as: .accessoryCircular) { + OpenHABWatchComplications() +} timeline: { + OpenHABWatchComplicationsEntry(date: Date()) +} + +#Preview("Corner", as: .accessoryCorner) { + OpenHABWatchComplications() +} timeline: { + OpenHABWatchComplicationsEntry(date: Date()) +} diff --git a/OpenHABWatchComplications/OpenHABWatchComplicationsBundle.swift b/OpenHABWatchComplications/OpenHABWatchComplicationsBundle.swift new file mode 100644 index 000000000..71a125fc4 --- /dev/null +++ b/OpenHABWatchComplications/OpenHABWatchComplicationsBundle.swift @@ -0,0 +1,20 @@ +// Copyright (c) 2010-2026 Contributors to the openHAB project +// +// See the NOTICE file(s) distributed with this work for additional +// information. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0 +// +// SPDX-License-Identifier: EPL-2.0 + +import SwiftUI +import WidgetKit + +@main +struct OpenHABWatchComplicationsBundle: WidgetBundle { + var body: some Widget { + OpenHABWatchComplications() + } +} diff --git a/Version.xcconfig b/Version.xcconfig index eb8cf8129..c4fdf9235 100644 --- a/Version.xcconfig +++ b/Version.xcconfig @@ -6,5 +6,5 @@ // https://developer.apple.com/documentation/xcode/adding-a-build-configuration-file-to-your-project -MARKETING_VERSION = 3.2.65 -CURRENT_PROJECT_VERSION = 274 +MARKETING_VERSION = 3.2.72 +CURRENT_PROJECT_VERSION = 281 diff --git a/fastlane/Fastfile b/fastlane/Fastfile index abe5c5191..c8e1531b3 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -149,6 +149,12 @@ platform :ios do build_configurations: ["Release"] ) + update_code_signing_settings( + targets: 'OpenHABWatchComplicationsExtension', + profile_name: 'match AppStore org.openhab.app.watchkitapp.OpenHABWatchComplications', + build_configurations: ["Release"] + ) + # setup code signing setup_keychain certificates diff --git a/fastlane/Matchfile b/fastlane/Matchfile index f0afd0034..6f37f651e 100644 --- a/fastlane/Matchfile +++ b/fastlane/Matchfile @@ -4,7 +4,7 @@ storage_mode("git") type("appstore") # The default type, can be: appstore, adhoc, enterprise or development -app_identifier(["org.openhab.app", "org.openhab.app.openHABIntents", "org.openhab.app.watchkitapp", "org.openhab.app.NotificationService"]) +app_identifier(["org.openhab.app", "org.openhab.app.openHABIntents", "org.openhab.app.watchkitapp", "org.openhab.app.watchkitapp.OpenHABWatchComplications", "org.openhab.app.NotificationService"]) # username("user@fastlane.tools") # Your Apple Developer Portal username # For all available options run `fastlane match --help` diff --git a/openHAB.xcodeproj/project.pbxproj b/openHAB.xcodeproj/project.pbxproj index 5f3f914f4..da0ab2249 100644 --- a/openHAB.xcodeproj/project.pbxproj +++ b/openHAB.xcodeproj/project.pbxproj @@ -7,14 +7,28 @@ objects = { /* Begin PBXBuildFile section */ + 0648F50178784F149905E7CF /* PlayerAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4062F0800010082479C /* PlayerAction.swift */; }; + 1C1CA8C8C8424BAF8018F9BE /* SetStringValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF35C2F0309B10082479C /* SetStringValueIntent.swift */; }; + 1E4E7DB36BAC4804AD5E58E5 /* SetColorValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3542F03095D0082479C /* SetColorValueIntent.swift */; }; 2AE557B26AF10D16E65EB541 /* openHABWidgetExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 396B3EC10279D6D55CD888E8 /* openHABWidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 2F399AC92F54599400F72A30 /* Flow in Frameworks */ = {isa = PBXBuildFile; productRef = 2F399AC82F54599400F72A30 /* Flow */; }; + 3A5DAEE42F1FD8D300CFA482 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3A0115E82F1E6A4400234D4B /* WidgetKit.framework */; }; + 3A5DAEE52F1FD8D300CFA482 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3A0115EA2F1E6A4400234D4B /* SwiftUI.framework */; }; + 3A5DAEF22F1FD8D300CFA482 /* OpenHABWatchComplicationsExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 3A5DAEE32F1FD8D300CFA482 /* OpenHABWatchComplicationsExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 3D3B2898C51F48BAA31210E4 /* ContactStateIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3552F03095D0082479C /* ContactStateIntent.swift */; }; + 3F087E4025E845FFBCBE45E3 /* ItemEntityQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4352F07056F0082479C /* ItemEntityQuery.swift */; }; 5111C4CC62E66D2FFA5C92EB /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 06A8490213945021806A13D1 /* SwiftUI.framework */; }; + 598208789AC44F1D91DD5B17 /* SetPlayerValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4082F0800020082479C /* SetPlayerValueIntent.swift */; }; + 5B292637B6E141DB98BCAA26 /* Home.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA448E772EF435B400F0893C /* Home.swift */; }; + 61A606E5E6AE404584B0B1A0 /* SetNumberValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF35A2F0309A60082479C /* SetNumberValueIntent.swift */; }; 6557AF8F2C0241C10094D0C8 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 6557AF8E2C0241C10094D0C8 /* PrivacyInfo.xcprivacy */; }; 6557AF902C0241C10094D0C8 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 6557AF8E2C0241C10094D0C8 /* PrivacyInfo.xcprivacy */; }; 6557AF922C039D140094D0C8 /* FirebaseMessaging in Frameworks */ = {isa = PBXBuildFile; productRef = 6557AF912C039D140094D0C8 /* FirebaseMessaging */; }; 657144552C1E438700C8A1F3 /* NotificationService.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 6571444E2C1E438700C8A1F3 /* NotificationService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 657144962C30A16700C8A1F3 /* OpenHABCore in Frameworks */ = {isa = PBXBuildFile; productRef = 657144952C30A16700C8A1F3 /* OpenHABCore */; }; + 73F094A3C17641738D6215CD /* SetSwitchItemIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD5E85B2F02C272003215C0 /* SetSwitchItemIntent.swift */; }; + 772F724744CC8D15A673C246 /* SetActiveHomeIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F77A157AAF1DD5AB45D6BA2 /* SetActiveHomeIntent.swift */; }; + 791AFA47B71D4B6995369F3A /* GetItemStateIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3512F0307A40082479C /* GetItemStateIntent.swift */; }; 934E592528F16EBA00162004 /* OpenHABCore in Frameworks */ = {isa = PBXBuildFile; productRef = 934E592428F16EBA00162004 /* OpenHABCore */; }; 934E592728F16EBA00162004 /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 934E592628F16EBA00162004 /* Kingfisher */; }; 934E592928F16EBA00162004 /* DeviceKit in Frameworks */ = {isa = PBXBuildFile; productRef = 934E592828F16EBA00162004 /* DeviceKit */; }; @@ -29,13 +43,35 @@ 93F8064727AE7A050035A6B0 /* SwiftMessages in Frameworks */ = {isa = PBXBuildFile; productRef = 93F8064627AE7A050035A6B0 /* SwiftMessages */; }; 93F8064A27AE7A2E0035A6B0 /* FlexColorPicker in Frameworks */ = {isa = PBXBuildFile; productRef = 93F8064927AE7A2E0035A6B0 /* FlexColorPicker */; }; 93F8065027AE7A830035A6B0 /* SideMenu in Frameworks */ = {isa = PBXBuildFile; productRef = 93F8064F27AE7A830035A6B0 /* SideMenu */; }; + 970584BAA2884C8287297BF6 /* SwitchItemEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAF58C7A2EF6E99500483AFD /* SwitchItemEntity.swift */; }; + 9F54768418A14674AD9E2FCA /* ContactState.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4042F06F5CA0082479C /* ContactState.swift */; }; AFE7BDC9A9901B49AF9D6635 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 16CFF4D57F6DF603FF1C1B9D /* Foundation.framework */; }; + B2FC662F25B2461887BB67DA /* ItemEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4332F0705580082479C /* ItemEntity.swift */; }; B76C3BEEBF1F4730710AAB6E /* SDWebImageSVGCoder in Frameworks */ = {isa = PBXBuildFile; productRef = E92D1BEE08AD7DCB6F993340 /* SDWebImageSVGCoder */; }; + BF0EB6DE70934966B1DC5479 /* SetDateTimeValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF40A2F0800030082479C /* SetDateTimeValueIntent.swift */; }; C314F9C9CB55D0AA25E4BB0A /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0F298FB19C2A149258FE7CC6 /* WidgetKit.framework */; }; C380A8DFA36E43D49400A87E /* SFSafeSymbols in Frameworks */ = {isa = PBXBuildFile; productRef = 1F9D3602163D4BEBB384DDEE /* SFSafeSymbols */; }; + C9DE79C32AF24FFEA0280115 /* ItemIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD5E85D2F02C3BC003215C0 /* ItemIdentifier.swift */; }; + CE7A58C0ED3A4A1ABCB20337 /* SetLocationValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF40C2F0800040082479C /* SetLocationValueIntent.swift */; }; + CF650A22059A4A00823EE656 /* SwitchAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4022F06F5950082479C /* SwitchAction.swift */; }; DA10161B2DC7BAE500552D14 /* SFSafeSymbols in Frameworks */ = {isa = PBXBuildFile; productRef = DA10161A2DC7BAE500552D14 /* SFSafeSymbols */; }; DA28C362225241DE00AB409C /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA28C361225241DE00AB409C /* WebKit.framework */; settings = {ATTRIBUTES = (Required, ); }; }; DA2C4FD52B4F573300D1C533 /* SDWebImageSVGCoder in Frameworks */ = {isa = PBXBuildFile; productRef = DA2C4FD42B4F573300D1C533 /* SDWebImageSVGCoder */; }; + DA448E852EF435B400F0893C /* Home.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA448E772EF435B400F0893C /* Home.swift */; }; + DA4BF3522F0307A40082479C /* GetItemStateIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3512F0307A40082479C /* GetItemStateIntent.swift */; }; + DA4BF3562F03095D0082479C /* SetColorValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3542F03095D0082479C /* SetColorValueIntent.swift */; }; + DA4BF3572F03095D0082479C /* ContactStateIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3552F03095D0082479C /* ContactStateIntent.swift */; }; + DA4BF3592F03098B0082479C /* SetDimmerRollerValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3582F03098B0082479C /* SetDimmerRollerValueIntent.swift */; }; + DA4BF35B2F0309A60082479C /* SetNumberValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF35A2F0309A60082479C /* SetNumberValueIntent.swift */; }; + DA4BF35D2F0309B10082479C /* SetStringValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF35C2F0309B10082479C /* SetStringValueIntent.swift */; }; + DA4BF4032F06F5950082479C /* SwitchAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4022F06F5950082479C /* SwitchAction.swift */; }; + DA4BF4052F06F5CA0082479C /* ContactState.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4042F06F5CA0082479C /* ContactState.swift */; }; + DA4BF4072F0800010082479C /* PlayerAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4062F0800010082479C /* PlayerAction.swift */; }; + DA4BF4092F0800020082479C /* SetPlayerValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4082F0800020082479C /* SetPlayerValueIntent.swift */; }; + DA4BF40B2F0800030082479C /* SetDateTimeValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF40A2F0800030082479C /* SetDateTimeValueIntent.swift */; }; + DA4BF40D2F0800040082479C /* SetLocationValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF40C2F0800040082479C /* SetLocationValueIntent.swift */; }; + DA4BF4342F0705580082479C /* ItemEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4332F0705580082479C /* ItemEntity.swift */; }; + DA4BF4362F07056F0082479C /* ItemEntityQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4352F07056F0082479C /* ItemEntityQuery.swift */; }; DA4D4DB5233F9ACB00B37E37 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = DA4D4DB4233F9ACB00B37E37 /* README.md */; }; DA817E7A234BF39B00C91824 /* CHANGELOG.md in Resources */ = {isa = PBXBuildFile; fileRef = DA817E79234BF39B00C91824 /* CHANGELOG.md */; }; DA9A7EFD2D668D5900824156 /* SFSafeSymbols in Frameworks */ = {isa = PBXBuildFile; productRef = DA9A7EFC2D668D5900824156 /* SFSafeSymbols */; }; @@ -43,13 +79,17 @@ DABB5E332D98972F009A4B8A /* SDWebImageSVGCoder in Frameworks */ = {isa = PBXBuildFile; productRef = DABB5E322D98972F009A4B8A /* SDWebImageSVGCoder */; }; DAC949FA2E219F0D007E67B7 /* CommonUI in Frameworks */ = {isa = PBXBuildFile; productRef = DAC949F92E219F0D007E67B7 /* CommonUI */; }; DAC949FC2E219F30007E67B7 /* CommonUI in Frameworks */ = {isa = PBXBuildFile; productRef = DAC949FB2E219F30007E67B7 /* CommonUI */; }; + DAD5E85C2F02C272003215C0 /* SetSwitchItemIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD5E85B2F02C272003215C0 /* SetSwitchItemIntent.swift */; }; + DAD5E85E2F02C3C6003215C0 /* ItemIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD5E85D2F02C3BC003215C0 /* ItemIdentifier.swift */; }; DAEFAA7F2F63536A00AC300B /* OpenHABCore in Frameworks */ = {isa = PBXBuildFile; productRef = DAEFAA7E2F63536A00AC300B /* OpenHABCore */; }; DAEFAA812F63537600AC300B /* SDWebImageSVGCoder in Frameworks */ = {isa = PBXBuildFile; productRef = DAEFAA802F63537600AC300B /* SDWebImageSVGCoder */; }; + DAF58C7B2EF6E99500483AFD /* SwitchItemEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAF58C7A2EF6E99500483AFD /* SwitchItemEntity.swift */; }; DAFF80982E4F47830084513E /* SDWebImage in Frameworks */ = {isa = PBXBuildFile; productRef = DAFF80972E4F47830084513E /* SDWebImage */; }; DFB2622B18830A3600D3244D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DFB2622A18830A3600D3244D /* Foundation.framework */; }; DFB2622D18830A3600D3244D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DFB2622C18830A3600D3244D /* CoreGraphics.framework */; }; DFB2622F18830A3600D3244D /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DFB2622E18830A3600D3244D /* UIKit.framework */; }; DFE10414197415F900D94943 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DFE10413197415F900D94943 /* Security.framework */; }; + E120F4A14B9942449F25E788 /* SetDimmerRollerValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3582F03098B0082479C /* SetDimmerRollerValueIntent.swift */; }; EE6B566C271031A1EB5780D2 /* OpenHABCore in Frameworks */ = {isa = PBXBuildFile; productRef = 2247FFEA7206BFFC71AB02C4 /* OpenHABCore */; }; /* End PBXBuildFile section */ @@ -61,6 +101,13 @@ remoteGlobalIDString = 6116C7DDD25EE245FE191A47; remoteInfo = openHABWidgetExtension; }; + 3A5DAEF02F1FD8D300CFA482 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DFB2621F18830A3600D3244D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 3A5DAEE22F1FD8D300CFA482; + remoteInfo = OpenHABWatchComplicationsExtension; + }; 657144532C1E438700C8A1F3 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = DFB2621F18830A3600D3244D /* Project object */; @@ -124,14 +171,25 @@ name = "Embed Watch Content"; runOnlyForDeploymentPostprocessing = 0; }; + 3A0115FD2F1E6A4500234D4B /* Embed Foundation Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + 3A5DAEF22F1FD8D300CFA482 /* OpenHABWatchComplicationsExtension.appex in Embed Foundation Extensions */, + ); + name = "Embed Foundation Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; 4D6470DE2561F935007B03FC /* Embed Foundation Extensions */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 12; dstPath = ""; dstSubfolderSpec = 13; files = ( - 657144552C1E438700C8A1F3 /* NotificationService.appex in Embed Foundation Extensions */, 2AE557B26AF10D16E65EB541 /* openHABWidgetExtension.appex in Embed Foundation Extensions */, + 657144552C1E438700C8A1F3 /* NotificationService.appex in Embed Foundation Extensions */, ); name = "Embed Foundation Extensions"; runOnlyForDeploymentPostprocessing = 0; @@ -163,9 +221,13 @@ 0F298FB19C2A149258FE7CC6 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS18.0.sdk/System/Library/Frameworks/WidgetKit.framework; sourceTree = DEVELOPER_DIR; }; 1224F78D228A89FC00750965 /* WatchMessageService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WatchMessageService.swift; sourceTree = ""; }; 16CFF4D57F6DF603FF1C1B9D /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS18.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; + 1F77A157AAF1DD5AB45D6BA2 /* SetActiveHomeIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetActiveHomeIntent.swift; sourceTree = ""; }; 2F399AB92F5357E900F72A30 /* InputCommandFormatterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputCommandFormatterTests.swift; sourceTree = ""; }; 396B3EC10279D6D55CD888E8 /* openHABWidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = openHABWidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 399449C421544C61AD83450C /* TextRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextRow.swift; sourceTree = ""; }; + 3A0115E82F1E6A4400234D4B /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; }; + 3A0115EA2F1E6A4400234D4B /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; }; + 3A5DAEE32F1FD8D300CFA482 /* OpenHABWatchComplicationsExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = OpenHABWatchComplicationsExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 6557AF8E2C0241C10094D0C8 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 6571444E2C1E438700C8A1F3 /* NotificationService.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NotificationService.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 657144502C1E438700C8A1F3 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; }; @@ -206,6 +268,21 @@ DA2E0B0D23DCC152009B0A99 /* MapView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MapView.swift; sourceTree = ""; }; DA2E0B0F23DCC439009B0A99 /* MapViewRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapViewRow.swift; sourceTree = ""; }; DA32D1B32C8C98C40018D974 /* IconWithAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconWithAction.swift; sourceTree = ""; }; + DA448E772EF435B400F0893C /* Home.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Home.swift; sourceTree = ""; }; + DA4BF3512F0307A40082479C /* GetItemStateIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetItemStateIntent.swift; sourceTree = ""; }; + DA4BF3542F03095D0082479C /* SetColorValueIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetColorValueIntent.swift; sourceTree = ""; }; + DA4BF3552F03095D0082479C /* ContactStateIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactStateIntent.swift; sourceTree = ""; }; + DA4BF3582F03098B0082479C /* SetDimmerRollerValueIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetDimmerRollerValueIntent.swift; sourceTree = ""; }; + DA4BF35A2F0309A60082479C /* SetNumberValueIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetNumberValueIntent.swift; sourceTree = ""; }; + DA4BF35C2F0309B10082479C /* SetStringValueIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetStringValueIntent.swift; sourceTree = ""; }; + DA4BF4022F06F5950082479C /* SwitchAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitchAction.swift; sourceTree = ""; }; + DA4BF4042F06F5CA0082479C /* ContactState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactState.swift; sourceTree = ""; }; + DA4BF4062F0800010082479C /* PlayerAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerAction.swift; sourceTree = ""; }; + DA4BF4082F0800020082479C /* SetPlayerValueIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetPlayerValueIntent.swift; sourceTree = ""; }; + DA4BF40A2F0800030082479C /* SetDateTimeValueIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetDateTimeValueIntent.swift; sourceTree = ""; }; + DA4BF40C2F0800040082479C /* SetLocationValueIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetLocationValueIntent.swift; sourceTree = ""; }; + DA4BF4332F0705580082479C /* ItemEntity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemEntity.swift; sourceTree = ""; }; + DA4BF4352F07056F0082479C /* ItemEntityQuery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemEntityQuery.swift; sourceTree = ""; }; DA4D4DB4233F9ACB00B37E37 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; DA4D4E0E2340A00200B37E37 /* Changes.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = Changes.md; sourceTree = ""; }; DA65871E236F83CD007E2E7F /* UserDefaultsExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsExtension.swift; sourceTree = ""; }; @@ -233,6 +310,8 @@ DAD085762AE4782E001D36BE /* openHABWatchSwiftUI Watch AppUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "openHABWatchSwiftUI Watch AppUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; DAD0857A2AE4782F001D36BE /* OpenHABWatchUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenHABWatchUITests.swift; sourceTree = ""; }; DAD0857C2AE4782F001D36BE /* OpenHABWatchLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenHABWatchLaunchTests.swift; sourceTree = ""; }; + DAD5E85B2F02C272003215C0 /* SetSwitchItemIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetSwitchItemIntent.swift; sourceTree = ""; }; + DAD5E85D2F02C3BC003215C0 /* ItemIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemIdentifier.swift; sourceTree = ""; }; DAF231D127BB6EEA00AB916C /* OpenHABSVGTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenHABSVGTests.swift; sourceTree = ""; }; DAF231D527BB702400AB916C /* valid_xmlns.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = valid_xmlns.svg; sourceTree = ""; }; DAF231D627BB702500AB916C /* invalid_xmlns.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = invalid_xmlns.svg; sourceTree = ""; }; @@ -247,6 +326,7 @@ DAF4581523DC483F0018B495 /* GenericRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenericRow.swift; sourceTree = ""; }; DAF4581723DC4A050018B495 /* ImageRow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageRow.swift; sourceTree = ""; }; DAF4581D23DC60020018B495 /* ImageRawRow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageRawRow.swift; sourceTree = ""; }; + DAF58C7A2EF6E99500483AFD /* SwitchItemEntity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitchItemEntity.swift; sourceTree = ""; }; DAFAF36E2F8892A3003E60EF /* AppIntentsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIntentsTests.swift; sourceTree = ""; }; DAFD2FE62E0D96700059A1EB /* OsLogRewriter */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = OsLogRewriter; sourceTree = ""; }; DFB2622718830A3600D3244D /* openHAB.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = openHAB.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -320,6 +400,13 @@ ); target = DA0775142346705D0086C685 /* openHABWatch */; }; + 3A5DAEF32F1FD8D300CFA482 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Info.plist, + ); + target = 3A5DAEE22F1FD8D300CFA482 /* OpenHABWatchComplicationsExtension */; + }; 4D140F69148E28C6B524B346 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( @@ -339,6 +426,7 @@ 2FD4E2E02F66F9FE00EBB340 /* openHABTestsSwift */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (2FD4E2F02F66F9FE00EBB340 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = openHABTestsSwift; sourceTree = ""; }; 2FD4E2F42F66FA2900EBB340 /* TestPlans */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (2FD4E2F62F66FA2900EBB340 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = TestPlans; sourceTree = ""; }; 2FD4E52B2F670BA500EBB340 /* openHAB */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (2FD4E58D2F670BA500EBB340 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, 2FD4E58E2F670BA600EBB340 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = openHAB; sourceTree = ""; }; + 3A5DAEE62F1FD8D300CFA482 /* OpenHABWatchComplications */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (3A5DAEF32F1FD8D300CFA482 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = OpenHABWatchComplications; sourceTree = ""; }; D41D0290EB2365F7E247EC0A /* openHABWidget */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (4D140F69148E28C6B524B346 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = openHABWidget; sourceTree = ""; }; DA8B15522F3BB74B007753FD /* openHABWatchTests */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = openHABWatchTests; sourceTree = ""; }; /* End PBXFileSystemSynchronizedRootGroup section */ @@ -373,6 +461,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 3A5DAEE02F1FD8D300CFA482 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 3A5DAEE52F1FD8D300CFA482 /* SwiftUI.framework in Frameworks */, + 3A5DAEE42F1FD8D300CFA482 /* WidgetKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 6571444B2C1E438700C8A1F3 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -545,9 +642,9 @@ DA07752A2346705F0086C685 /* OpenHABWatchAppDelegate.swift */, DA07752C2346705F0086C685 /* NotificationController.swift */, DA07752E2346705F0086C685 /* NotificationView.swift */, - DA0775302346705F0086C685 /* ComplicationController.swift */, DAC6608B236F6F4200F4501E /* Assets.xcassets */, DA0775372346705F0086C685 /* Info.plist */, + DA0775302346705F0086C685 /* ComplicationController.swift */, DA65871E236F83CD007E2E7F /* UserDefaultsExtension.swift */, DA0775382346705F0086C685 /* PushNotificationPayload.apns */, ); @@ -574,6 +671,40 @@ path = openHABTestsSwift; sourceTree = ""; }; + DA448E7E2EF435B400F0893C /* AppIntents */ = { + isa = PBXGroup; + children = ( + DA4BF3652F0419F30082479C /* Intents */, + DAD5E85D2F02C3BC003215C0 /* ItemIdentifier.swift */, + DA4BF4042F06F5CA0082479C /* ContactState.swift */, + DAF58C7A2EF6E99500483AFD /* SwitchItemEntity.swift */, + DA4BF4352F07056F0082479C /* ItemEntityQuery.swift */, + DA4BF4332F0705580082479C /* ItemEntity.swift */, + DA4BF4022F06F5950082479C /* SwitchAction.swift */, + DA4BF4062F0800010082479C /* PlayerAction.swift */, + DA448E772EF435B400F0893C /* Home.swift */, + ); + path = AppIntents; + sourceTree = ""; + }; + DA4BF3652F0419F30082479C /* Intents */ = { + isa = PBXGroup; + children = ( + DA4BF3542F03095D0082479C /* SetColorValueIntent.swift */, + DA4BF3552F03095D0082479C /* ContactStateIntent.swift */, + DA4BF3582F03098B0082479C /* SetDimmerRollerValueIntent.swift */, + DA4BF3512F0307A40082479C /* GetItemStateIntent.swift */, + DAD5E85B2F02C272003215C0 /* SetSwitchItemIntent.swift */, + DA4BF35C2F0309B10082479C /* SetStringValueIntent.swift */, + 1F77A157AAF1DD5AB45D6BA2 /* SetActiveHomeIntent.swift */, + DA4BF35A2F0309A60082479C /* SetNumberValueIntent.swift */, + DA4BF4082F0800020082479C /* SetPlayerValueIntent.swift */, + DA4BF40A2F0800030082479C /* SetDateTimeValueIntent.swift */, + DA4BF40C2F0800040082479C /* SetLocationValueIntent.swift */, + ); + path = Intents; + sourceTree = ""; + }; DA658720236F841F007E2E7F /* Views */ = { isa = PBXGroup; children = ( @@ -603,6 +734,14 @@ path = "Preview Content"; sourceTree = ""; }; + DADB3BDA2FDC0C140058522B /* Recovered References */ = { + isa = PBXGroup; + children = ( + 032C4C3A922FABCBEB20EAA3 /* AppIntents */, + ); + name = "Recovered References"; + sourceTree = ""; + }; DAF231D327BB6F5C00AB916C /* Ressources */ = { isa = PBXGroup; children = ( @@ -652,7 +791,8 @@ DFB2621E18830A3600D3244D = { isa = PBXGroup; children = ( - 032C4C3A922FABCBEB20EAA3 /* AppIntents */, + DA448E7E2EF435B400F0893C /* AppIntents */, + D41D0290EB2365F7E247EC0A /* openHABWidget */, DABB4CD22F45EFD100111AF3 /* openHABWatch.xctestplan */, 6557AF8E2C0241C10094D0C8 /* PrivacyInfo.xcprivacy */, DA4D4DB4233F9ACB00B37E37 /* README.md */, @@ -673,6 +813,7 @@ 2FD4E1EB2F66EF4F00EBB340 /* openHABWatchSwiftUI Watch AppUITests */, 2FD4E1E62F66EF4800EBB340 /* NotificationService */, DA8B15522F3BB74B007753FD /* openHABWatchTests */, + 3A5DAEE62F1FD8D300CFA482 /* OpenHABWatchComplications */, 2F2150892F75C2FC001BA057 /* Watch */, DFB2622818830A3600D3244D /* Products */, DFB2622918830A3600D3244D /* Frameworks */, @@ -680,7 +821,7 @@ DAFD2FE62E0D96700059A1EB /* OsLogRewriter */, DA0DA9E12E0C9B74000C5D0A /* BuildTools */, DA83C81D2F48AF7600CDACED /* Version.xcconfig */, - D41D0290EB2365F7E247EC0A /* openHABWidget */, + DADB3BDA2FDC0C140058522B /* Recovered References */, ); sourceTree = ""; }; @@ -695,6 +836,7 @@ DAD085762AE4782E001D36BE /* openHABWatchSwiftUI Watch AppUITests.xctest */, 6571444E2C1E438700C8A1F3 /* NotificationService.appex */, DA8B15512F3BB74B007753FD /* openHABWatchTests.xctest */, + 3A5DAEE32F1FD8D300CFA482 /* OpenHABWatchComplicationsExtension.appex */, 396B3EC10279D6D55CD888E8 /* openHABWidgetExtension.appex */, ); name = Products; @@ -709,6 +851,8 @@ DFE10413197415F900D94943 /* Security.framework */, DFB2622E18830A3600D3244D /* UIKit.framework */, DFB2624C18830A3600D3244D /* XCTest.framework */, + 3A0115E82F1E6A4400234D4B /* WidgetKit.framework */, + 3A0115EA2F1E6A4400234D4B /* SwiftUI.framework */, 964901D665A05573A8E2E7B2 /* iOS */, ); name = Frameworks; @@ -717,6 +861,26 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 3A5DAEE22F1FD8D300CFA482 /* OpenHABWatchComplicationsExtension */ = { + isa = PBXNativeTarget; + buildConfigurationList = 3A5DAEF42F1FD8D300CFA482 /* Build configuration list for PBXNativeTarget "OpenHABWatchComplicationsExtension" */; + buildPhases = ( + 3A5DAEDF2F1FD8D300CFA482 /* Sources */, + 3A5DAEE02F1FD8D300CFA482 /* Frameworks */, + 3A5DAEE12F1FD8D300CFA482 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 3A5DAEE62F1FD8D300CFA482 /* OpenHABWatchComplications */, + ); + name = OpenHABWatchComplicationsExtension; + productName = OpenHABWatchComplicationsExtension; + productReference = 3A5DAEE32F1FD8D300CFA482 /* OpenHABWatchComplicationsExtension.appex */; + productType = "com.apple.product-type.app-extension"; + }; 6116C7DDD25EE245FE191A47 /* openHABWidgetExtension */ = { isa = PBXNativeTarget; buildConfigurationList = 7E431775FC423F4117A62BF2 /* Build configuration list for PBXNativeTarget "openHABWidgetExtension" */; @@ -790,10 +954,12 @@ 39C91164B60A5677322E8DE2 /* Frameworks */, DA07751D2346705F0086C685 /* Sources */, 93F38C61238034AE001B1451 /* Embed Frameworks */, + 3A0115FD2F1E6A4500234D4B /* Embed Foundation Extensions */, ); buildRules = ( ); dependencies = ( + 3A5DAEF12F1FD8D300CFA482 /* PBXTargetDependency */, ); fileSystemSynchronizedGroups = ( 2FD4E29C2F66F9A500EBB340 /* openHABWatch */, @@ -912,7 +1078,6 @@ 50C8570F40A5885164DA68E6 /* PBXTargetDependency */, ); fileSystemSynchronizedGroups = ( - 032C4C3A922FABCBEB20EAA3 /* AppIntents */, 2FD4E52B2F670BA500EBB340 /* openHAB */, ); name = openHAB; @@ -1029,6 +1194,7 @@ projectRoot = ""; targets = ( DFB2622618830A3600D3244D /* openHAB */, + 6116C7DDD25EE245FE191A47 /* openHABWidgetExtension */, DA2DC22E21F2736C00830730 /* openHABTestsSwift */, 933D7F0322E7015000621A03 /* openHABUITests */, DA0775142346705D0086C685 /* openHABWatch */, @@ -1036,12 +1202,19 @@ DAD085752AE4782D001D36BE /* openHABWatchSwiftUI Watch AppUITests */, 6571444D2C1E438700C8A1F3 /* NotificationService */, DA8B15502F3BB74B007753FD /* openHABWatchTests */, - 6116C7DDD25EE245FE191A47 /* openHABWidgetExtension */, + 3A5DAEE22F1FD8D300CFA482 /* OpenHABWatchComplicationsExtension */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 3A5DAEE12F1FD8D300CFA482 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 6571444C2C1E438700C8A1F3 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -1134,6 +1307,13 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 3A5DAEDF2F1FD8D300CFA482 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 6571444A2C1E438700C8A1F3 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -1152,6 +1332,24 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9F54768418A14674AD9E2FCA /* ContactState.swift in Sources */, + 3D3B2898C51F48BAA31210E4 /* ContactStateIntent.swift in Sources */, + 791AFA47B71D4B6995369F3A /* GetItemStateIntent.swift in Sources */, + 5B292637B6E141DB98BCAA26 /* Home.swift in Sources */, + B2FC662F25B2461887BB67DA /* ItemEntity.swift in Sources */, + 3F087E4025E845FFBCBE45E3 /* ItemEntityQuery.swift in Sources */, + C9DE79C32AF24FFEA0280115 /* ItemIdentifier.swift in Sources */, + 0648F50178784F149905E7CF /* PlayerAction.swift in Sources */, + 1E4E7DB36BAC4804AD5E58E5 /* SetColorValueIntent.swift in Sources */, + BF0EB6DE70934966B1DC5479 /* SetDateTimeValueIntent.swift in Sources */, + E120F4A14B9942449F25E788 /* SetDimmerRollerValueIntent.swift in Sources */, + CE7A58C0ED3A4A1ABCB20337 /* SetLocationValueIntent.swift in Sources */, + 61A606E5E6AE404584B0B1A0 /* SetNumberValueIntent.swift in Sources */, + 598208789AC44F1D91DD5B17 /* SetPlayerValueIntent.swift in Sources */, + 1C1CA8C8C8424BAF8018F9BE /* SetStringValueIntent.swift in Sources */, + 73F094A3C17641738D6215CD /* SetSwitchItemIntent.swift in Sources */, + CF650A22059A4A00823EE656 /* SwitchAction.swift in Sources */, + 970584BAA2884C8287297BF6 /* SwitchItemEntity.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1187,6 +1385,25 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + DA448E852EF435B400F0893C /* Home.swift in Sources */, + DA4BF3522F0307A40082479C /* GetItemStateIntent.swift in Sources */, + DA4BF3562F03095D0082479C /* SetColorValueIntent.swift in Sources */, + DA4BF3572F03095D0082479C /* ContactStateIntent.swift in Sources */, + DA4BF3592F03098B0082479C /* SetDimmerRollerValueIntent.swift in Sources */, + DA4BF35B2F0309A60082479C /* SetNumberValueIntent.swift in Sources */, + DA4BF35D2F0309B10082479C /* SetStringValueIntent.swift in Sources */, + 772F724744CC8D15A673C246 /* SetActiveHomeIntent.swift in Sources */, + DA4BF4032F06F5950082479C /* SwitchAction.swift in Sources */, + DA4BF4052F06F5CA0082479C /* ContactState.swift in Sources */, + DA4BF4072F0800010082479C /* PlayerAction.swift in Sources */, + DA4BF4092F0800020082479C /* SetPlayerValueIntent.swift in Sources */, + DA4BF40B2F0800030082479C /* SetDateTimeValueIntent.swift in Sources */, + DA4BF40D2F0800040082479C /* SetLocationValueIntent.swift in Sources */, + DA4BF4342F0705580082479C /* ItemEntity.swift in Sources */, + DA4BF4362F07056F0082479C /* ItemEntityQuery.swift in Sources */, + DAD5E85C2F02C272003215C0 /* SetSwitchItemIntent.swift in Sources */, + DAD5E85E2F02C3C6003215C0 /* ItemIdentifier.swift in Sources */, + DAF58C7B2EF6E99500483AFD /* SwitchItemEntity.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1200,6 +1417,11 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 3A5DAEF12F1FD8D300CFA482 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 3A5DAEE22F1FD8D300CFA482 /* OpenHABWatchComplicationsExtension */; + targetProxy = 3A5DAEF02F1FD8D300CFA482 /* PBXContainerItemProxy */; + }; 50C8570F40A5885164DA68E6 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = openHABWidgetExtension; @@ -1271,6 +1493,99 @@ }; name = Debug; }; + 3A5DAEF52F1FD8D300CFA482 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = OpenHABWatchComplications/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = OpenHABWatchComplications; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2026 openHAB e.V. All rights reserved."; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + "@executable_path/../../../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.openhab.app.watchkitapp.OpenHABWatchComplications; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 6.0; + TARGETED_DEVICE_FAMILY = 4; + WATCHOS_DEPLOYMENT_TARGET = 11.0; + }; + name = Debug; + }; + 3A5DAEF62F1FD8D300CFA482 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "Apple Distribution"; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=watchos*]" = PBAPXHRAM9; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = OpenHABWatchComplications/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = OpenHABWatchComplications; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2026 openHAB e.V. All rights reserved."; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + "@executable_path/../../../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.openhab.app.watchkitapp.OpenHABWatchComplications; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=watchos*]" = "match AppStore org.openhab.app.watchkitapp.OpenHABWatchComplications"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 6.0; + TARGETED_DEVICE_FAMILY = 4; + WATCHOS_DEPLOYMENT_TARGET = 11.0; + }; + name = Release; + }; 657144562C1E438700C8A1F3 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -1405,7 +1720,7 @@ SWIFT_PRECOMPILE_BRIDGING_HEADER = NO; TARGETED_DEVICE_FAMILY = "1,2"; TEST_TARGET_NAME = openHAB; - WATCHOS_DEPLOYMENT_TARGET = 11.0; + WATCHOS_DEPLOYMENT_TARGET = 8.0; }; name = Debug; }; @@ -1451,7 +1766,6 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_COMPLICATION_NAME = Complication; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "$(inherited)"; @@ -1466,10 +1780,9 @@ GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = openHABWatch/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = openHAB; - INFOPLIST_KEY_CLKComplicationPrincipalClass = openHABWatch.ComplicationController; INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; INFOPLIST_KEY_WKCompanionAppBundleIdentifier = org.openhab.app; - IPHONEOS_DEPLOYMENT_TARGET = 18.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "@executable_path/Frameworks", "@executable_path/../../Frameworks", @@ -1492,7 +1805,6 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_COMPLICATION_NAME = Complication; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "$(inherited)"; @@ -1510,10 +1822,9 @@ GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = openHABWatch/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = openHAB; - INFOPLIST_KEY_CLKComplicationPrincipalClass = openHABWatch.ComplicationController; INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; INFOPLIST_KEY_WKCompanionAppBundleIdentifier = org.openhab.app; - IPHONEOS_DEPLOYMENT_TARGET = 18.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "@executable_path/Frameworks", "@executable_path/../../Frameworks", @@ -1547,7 +1858,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = openHABTestsSwift/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 18.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1582,7 +1893,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = openHABTestsSwift/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 18.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1846,7 +2157,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 18.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; STRING_CATALOG_GENERATE_SYMBOLS = YES; @@ -1869,7 +2180,7 @@ SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; - WATCHOS_DEPLOYMENT_TARGET = 11.0; + WATCHOS_DEPLOYMENT_TARGET = 9.0; }; name = Debug; }; @@ -1922,7 +2233,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 18.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; ONLY_ACTIVE_ARCH = NO; PROVISIONING_PROFILE_SPECIFIER = "match AppStore org.openhab.app"; SDKROOT = iphoneos; @@ -1947,7 +2258,7 @@ TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; - WATCHOS_DEPLOYMENT_TARGET = 11.0; + WATCHOS_DEPLOYMENT_TARGET = 9.0; }; name = Release; }; @@ -2045,6 +2356,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 3A5DAEF42F1FD8D300CFA482 /* Build configuration list for PBXNativeTarget "OpenHABWatchComplicationsExtension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3A5DAEF52F1FD8D300CFA482 /* Debug */, + 3A5DAEF62F1FD8D300CFA482 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 657144582C1E438700C8A1F3 /* Build configuration list for PBXNativeTarget "NotificationService" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/openHAB.xcworkspace/xcshareddata/swiftpm/Package.resolved b/openHAB.xcworkspace/xcshareddata/swiftpm/Package.resolved index 3144b70a6..016ba91be 100644 --- a/openHAB.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/openHAB.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "ce87d13cdc46b65a26be6c5d8b5ee53a01cd0be7cf67b2a5fcbc83f121924b1f", + "originHash" : "58e262edca0c1a44a3d746bb54ea29777166539a33f9466df8671cec55680599", "pins" : [ { "identity" : "abseil-cpp-binary", diff --git a/openHAB/AppDelegate.swift b/openHAB/AppDelegate.swift index 5ce08cc40..b47e81b3d 100644 --- a/openHAB/AppDelegate.swift +++ b/openHAB/AppDelegate.swift @@ -58,6 +58,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { UserDefaults.standard.register(defaults: appDefaults) Preferences.migratePreferences() + SitemapDiagnostics.markProcessLaunch(source: launchSource(launchOptions)) // Firebase must be configured before the storyboard loads its root view controller, // because OpenHABRootViewController.viewDidLoad calls Crashlytics before the deferred @@ -79,6 +80,20 @@ class AppDelegate: UIResponder, UIApplicationDelegate { return true } + private func launchSource(_ launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> String { + guard let launchOptions, !launchOptions.isEmpty else { return "user" } + if launchOptions[.remoteNotification] != nil { + return "remoteNotification" + } + if launchOptions[.url] != nil { + return "url" + } + if launchOptions[.location] != nil { + return "location" + } + return "other" + } + /// Setup that can be deferred until after the UI appears @MainActor private func performDeferredSetup() { diff --git a/openHAB/Models/SitemapPageViewModel.swift b/openHAB/Models/SitemapPageViewModel.swift index 601a4e2df..5b4263cb1 100644 --- a/openHAB/Models/SitemapPageViewModel.swift +++ b/openHAB/Models/SitemapPageViewModel.swift @@ -67,6 +67,7 @@ class SitemapPageViewModel: ObservableObject { private var lastUIUpdateAt: Date = .distantPast private var coalescedLongPollUpdateCount = 0 private var pendingLongPollPage: OpenHABPage? + private var pendingLinkedPageNavigation: LinkedPageNavigation? private var longPollDebounceTask: Task? private var foregroundObserverTask: Task? private var rowInputRebuildTask: Task? @@ -324,7 +325,6 @@ extension SitemapPageViewModel { return } - let pipelineStart = Date() let requestedKey = "\(defaultSitemap)|\(pageId)" if !forceRestart, let activeTask = pageHandlingTask, @@ -358,15 +358,24 @@ extension SitemapPageViewModel { pageHandlingTask = Task { await runPageHandling( runID: runID, - recreateService: recreateService, - pipelineStart: pipelineStart + reason: reason, + recreateService: recreateService ) } } private func runPageHandling(runID: UUID, - recreateService: Bool, - pipelineStart: Date) async { + reason: String, + recreateService: Bool) async { + let pipelineStartedAt = Date() + var diagnostics = SitemapInitialLoadMeasurement( + reason: reason, + isLinkedPage: isLinkedPage, + hasCurrentPage: currentPage != nil, + connectionWasReady: networkTracker.activeConnection != nil, + pipelineStartedAt: pipelineStartedAt + ) + defer { if activePageHandlingID == runID { pageHandlingTask = nil @@ -375,19 +384,47 @@ extension SitemapPageViewModel { } do { - guard await ensureSitemapAvailableForHandling() else { return } + guard await ensureSitemapAvailableForHandling() else { + diagnostics.log( + status: "failed", + page: currentPage, + rowCount: rowInputs.count, + errorDescription: "No sitemap available" + ) + return + } - guard let activeConnection = await waitForConnectionForHandling() else { return } + diagnostics.beginConnectionSelection() + guard let activeConnection = await waitForConnectionForHandling() else { + diagnostics.log( + status: "failed", + page: currentPage, + rowCount: rowInputs.count, + errorDescription: "No active connection" + ) + return + } + diagnostics.beginServiceSetup(connection: activeConnection.configuration) try setupServiceIfNeeded(activeConnection: activeConnection, forceRecreate: recreateService) if defaultSitemapLabel.isEmpty { + diagnostics.beginLabelFetch() await fetchSitemapLabel() } - guard let service = openAPIService else { return } + guard let service = openAPIService else { + diagnostics.log( + status: "failed", + page: currentPage, + rowCount: rowInputs.count, + errorDescription: "Sitemap service unavailable" + ) + return + } var lastResponseAt: Date? + diagnostics.beginInitialRequest() for try await event in SitemapPageLoader.stream( sitemapName: defaultSitemap, @@ -417,11 +454,16 @@ extension SitemapPageViewModel { case let .initialFetch(page): isLoading = false isUpdating = false - let totalDurationMs = Date().timeIntervalSince(pipelineStart) * 1000 - logger.info("Sitemap pipeline ready in \(Int(totalDurationMs.rounded()), privacy: .public)ms") + diagnostics.beginUIPreparation() if let page { await updateUI(with: page, origin: .initialPoll) } + diagnostics.log( + status: page == nil ? "empty" : "success", + page: page, + rowCount: rowInputs.count + ) + logger.info("Sitemap pipeline initial load completed") case let .longPoll(page, requestMs): let responseGapMs = lastResponseAt.map { Int((responseAt.timeIntervalSince($0) * 1000).rounded()) } SitemapDiagnostics.logLongPoll( @@ -436,6 +478,12 @@ extension SitemapPageViewModel { lastResponseAt = responseAt } } catch { + diagnostics.log( + status: error is CancellationError ? "cancelled" : "failed", + page: currentPage, + rowCount: rowInputs.count, + errorDescription: error.localizedDescription + ) handlePageHandlingError(error) } } @@ -553,6 +601,15 @@ extension SitemapPageViewModel { injectSendCommand(for: newWidgets) currentPage = page + // Apply a pending linked-page navigation on the first non-nil page update, regardless + // of origin. Checking only .initialPoll was insufficient because the loader emits + // .initialFetch(nil) when the server returns no page, skipping updateUI entirely; + // the next update arrives as .longPolling and the destination was never consumed. + if let nav = pendingLinkedPageNavigation { + pendingLinkedPageNavigation = nil + navigationPath.append(nav) + } + // Phase 2: slider override sync _ = clearSyncedSliderOverrides(using: newWidgets) let firstApplyMs = Int((Date().timeIntervalSince(applyStartedAt) * 1000).rounded()) @@ -758,6 +815,7 @@ extension SitemapPageViewModel { defaultSitemapLabel = "" self.pageId = pageId ?? "" navigationPath = [] + pendingLinkedPageNavigation = nil error = nil } @@ -768,8 +826,9 @@ extension SitemapPageViewModel { @MainActor // swiftlint:disable:next async_without_await - func pushSitemap(name: String, path: String?) async { + func pushSitemap(name: String, path: String?, pendingNavigation: LinkedPageNavigation? = nil) async { configureSitemap(name: name, pageId: path) + pendingLinkedPageNavigation = pendingNavigation startPageHandling(forceRestart: true, reason: "push-sitemap") } diff --git a/openHAB/NotificationCenterDelegateImpl.swift b/openHAB/NotificationCenterDelegateImpl.swift index 63abdc3a0..a81fcba93 100644 --- a/openHAB/NotificationCenterDelegateImpl.swift +++ b/openHAB/NotificationCenterDelegateImpl.swift @@ -11,17 +11,38 @@ import AVFoundation import Combine -import Firebase -import FirebaseMessaging import Kingfisher import OpenHABCore import os.log import SDWebImageSVGCoder +import SFSafeSymbols import SwiftMessages import UIKit @preconcurrency import UserNotifications import WatchConnectivity +@MainActor +struct OpenHABImageFetcher { + private let logger = Logger(subsystem: "org.openhab", category: "ImageFetcher") + + func image(from url: URL, + targetSize: CGSize? = nil, + extraOptions: KingfisherOptionsInfo = []) async throws -> UIImage { + let processor = OpenHABImageProcessor() + + var options: KingfisherOptionsInfo = [ + .processor(processor) + ] + + options.append(contentsOf: extraOptions) + + let result = try await KingfisherManager.shared.retrieveImage(with: url, options: options) + + logger.debug("Fetched image \(result.image.size.debugDescription, privacy: .public) from \(url.absoluteString, privacy: .public)") + return result.image + } +} + /// AVAudioPlayer must be created, used, and deallocated on the main thread. /// Using a plain `actor` placed it on a background executor, causing a crash when /// AVFoundation delivered finishedPlaying: on the main thread while the old player @@ -60,22 +81,25 @@ final class NotificationCenterDelegateImpl: NSObject, UNUserNotificationCenterDe let userInfo = notification.request.content.userInfo Logger.notificationCenterDelegateImpl.info("Notification received while app is in foreground: \(userInfo)") + let payload = PushNotificationPayload(userInfo: userInfo) + NotificationCenter.default.post( name: .openHABDidReceiveNotification, - object: nil, + object: payload, userInfo: userInfo ) + guard !payload.isHideNotification else { + return [] + } + // Use the system banner when there are media attachments so the image is visible. // The didReceive handler will process any on-click action when the user taps. if !notification.request.content.attachments.isEmpty { return [.banner, .sound] } - let message = userInfo["message"] as? String ?? String(localized: "message_not_decoded", comment: "") - let action = userInfo["actionIdentifier"] as? String ?? userInfo["on-click"] as? String - let cloudUserId = userInfo["userId"] as? String - await displayNotification(message: message, action: action, cloudUserId: cloudUserId) + await displayNotification(payload: payload) return [] } @@ -95,12 +119,14 @@ final class NotificationCenterDelegateImpl: NSObject, UNUserNotificationCenterDe let action = userInfo["actionIdentifier"] as? String ?? userInfo["on-click"] as? String let cloudUserId = userInfo["userId"] as? String - notifyNotificationListeners(action: action, cloudUserId: cloudUserId) + // Pass the original notification so action handlers can re-post it on failure, + // allowing the user to retry without waiting for a new notification. + notifyNotificationListeners(action: action, cloudUserId: cloudUserId, notification: response.notification) } } - private func displayNotification(message: String, action: String?, cloudUserId: String?) async { - Logger.notificationCenterDelegateImpl.info("displayNotification \(message)") + private func displayNotification(payload: PushNotificationPayload) async { + Logger.notificationCenterDelegateImpl.info("displayNotification \(payload.message ?? "")") audioPlayer.playSound() @@ -122,18 +148,38 @@ final class NotificationCenterDelegateImpl: NSObject, UNUserNotificationCenterDe } } + var iconImage = UIImage(systemSymbol: .exclamationmark) + if let activeConnection = MainActorNetworkTracker.shared.activeConnection, + let url = Endpoint.icon(rootUrl: activeConnection.configuration.url, version: activeConnection.version, icon: payload.icon, state: nil, iconType: .svg, iconColor: "", staticIcon: false)?.url { + do { + let fetcher = OpenHABImageFetcher() + iconImage = try await fetcher.image( + from: url, + targetSize: CGSize(width: 24, height: 24), + extraOptions: [.requestModifier(OpenHABAccessTokenAdapter(connectionConfiguration: activeConnection.configuration))] + ) + } catch { + Logger.notificationCenterDelegateImpl.error("Image load failed: \(error)") + } + } + await MainActor.run { SwiftMessages.show(config: config) { let view = MessageView.viewFromNib(layout: .cardView) view.configureTheme(.info) - view.configureContent(title: String(localized: "notification", comment: ""), body: message) + view.configureContent( + title: String(localized: "notification", comment: ""), + body: payload.displayMessage, + iconImage: iconImage + ) view.button?.setTitle(String(localized: "dismiss", comment: ""), for: .normal) + view.configureIcon(withSize: CGSize(width: 24, height: 24), contentMode: .scaleAspectFit) view.buttonTapHandler = { _ in SwiftMessages.hide() } - // Use closure-based tap gesture insteae of #selector + // Use closure-based tap gesture instead of #selector let tapGesture = MessageTapGestureRecognizer { Task { - await self.messageViewTapped(action: action, cloudUserId: cloudUserId) + await self.messageViewTapped(action: payload.action, cloudUserId: payload.cloudUserId) } } view.addGestureRecognizer(tapGesture) @@ -152,7 +198,7 @@ final class NotificationCenterDelegateImpl: NSObject, UNUserNotificationCenterDe /// ✅ Ensure this runs on the MainActor @MainActor - func notifyNotificationListeners(action: String?, cloudUserId: String? = nil) { + func notifyNotificationListeners(action: String?, cloudUserId: String? = nil, notification: UNNotification? = nil) { // Wake up screen saver immediately on incoming notification interaction NotificationCenter.default.post(name: .wakeScreenSaver, object: nil) @@ -164,7 +210,7 @@ final class NotificationCenterDelegateImpl: NSObject, UNUserNotificationCenterDe .compactMap { $0.rootViewController as? UINavigationController } .compactMap { $0.viewControllers.first as? OpenHABRootViewController } .first - rootViewController?.handleNotification(action: action, cloudUserId: cloudUserId) + rootViewController?.handleNotification(action: action, cloudUserId: cloudUserId, notification: notification) } } diff --git a/openHAB/Supporting Files/Localizable.xcstrings b/openHAB/Supporting Files/Localizable.xcstrings index b5ec5721c..e84673044 100644 --- a/openHAB/Supporting Files/Localizable.xcstrings +++ b/openHAB/Supporting Files/Localizable.xcstrings @@ -7608,6 +7608,10 @@ } } }, + "Item '%@' not found" : { + "comment" : "Error message when an item is not found.", + "isCommentAutoGenerated" : true + }, "Items" : { "comment" : "The title of the view that lists available items.", "localizations" : { @@ -16733,10 +16737,6 @@ } } }, - "Toggle Switch" : { - "comment" : "Short title for a toggle switch shortcut.", - "isCommentAutoGenerated" : true - }, "unable_to_add_certificate" : { "extractionState" : "manual", "localizations" : { @@ -17913,4 +17913,4 @@ } }, "version" : "1.1" -} +} \ No newline at end of file diff --git a/openHAB/UI/HostingSitemapViewController.swift b/openHAB/UI/HostingSitemapViewController.swift index 95e7f509d..07674ffda 100644 --- a/openHAB/UI/HostingSitemapViewController.swift +++ b/openHAB/UI/HostingSitemapViewController.swift @@ -91,10 +91,12 @@ class HostingSitemapViewController: UIHostingController, .appending(path: name) .appending(path: path) - // Load the root sitemap so the back button returns to it, then navigate to the sub-page - // within the existing SwiftUI NavigationStack so SwiftUI manages back-navigation. - await viewModel.pushSitemap(name: name, path: nil) - viewModel.navigateToLinkedPage(LinkedPageNavigation(pageLink: pageURL.absoluteString, pageTitle: name)) + // Load the root sitemap so the back button returns to it, then navigate to the sub-page. + // The navigation is deferred until the initial root page poll completes so that it fires + // after SwiftUI's NavigationStack is in a stable rendered state. This avoids the cold-start + // race where navigateToLinkedPage was called before the NavigationStack existed. + let nav = LinkedPageNavigation(pageLink: pageURL.absoluteString, pageTitle: name) + await viewModel.pushSitemap(name: name, path: nil, pendingNavigation: nav) } @MainActor diff --git a/openHAB/UI/NotificationsView.swift b/openHAB/UI/NotificationsView.swift index 143401b07..d0bd603d3 100644 --- a/openHAB/UI/NotificationsView.swift +++ b/openHAB/UI/NotificationsView.swift @@ -20,30 +20,40 @@ import SwiftUI typealias NotificationLoader = () async -> [OpenHABNotification] struct NotificationRow: View { - var notification: OpenHABNotification - var connection: ConnectionInfo + let notification: OpenHABNotification + let connection: ConnectionInfo var body: some View { - HStack { + HStack(alignment: .top, spacing: 12) { KFImage(iconUrl) .withOpenHABCredentials(for: connection) .placeholder { Image("openHABIcon").resizable() } + .setProcessor(OpenHABImageProcessor()) + .fade(duration: 0.25) .resizable() - .frame(width: 40, height: 40) - .clipShape(.rect(cornerRadius: 8)) + .aspectRatio(contentMode: .fit) + .frame(width: 24, height: 24) + .id(iconUrl?.absoluteString ?? "") VStack(alignment: .leading) { - Text(notification.message ?? "") - .font(.body) - if let timeStamp = notification.created { - Text(dateString(from: timeStamp)) + VStack(alignment: .leading, spacing: 2) { + Text(notification.title.isEmpty ? String(localized: "message_not_decoded", comment: "") : notification.title) + .font(.body) + if let timeStamp = notification.created { + Text(dateString(from: timeStamp)) + .font(.caption) + .foregroundStyle(.gray) + } + } + HStack { + Spacer() + Text(notification.payload?.tag ?? "") .font(.caption) - .foregroundStyle(.gray) + .foregroundStyle(.secondary) } } } - .padding(.vertical, 8) } @@ -88,16 +98,19 @@ struct NotificationsViewPreview: View { return NotificationsView(networkTracker: mockTracker, notifications: []) { [ OpenHABNotification( + id: UUID().uuidString, message: "Preview Notification 1", - created: .now, icon: "sun", - id: UUID().uuidString + payload: Payload(tag: "test1"), + created: .now, + v: 0 ), OpenHABNotification( + id: UUID().uuidString, message: "Preview Notification 2", - created: .now.addingTimeInterval(-3600), icon: "moon", - id: UUID().uuidString + created: .now.addingTimeInterval(-3600), + v: 1 ) ] } @@ -141,6 +154,11 @@ struct NotificationsView: View { .task { await notifications = loadNotifications() } + .onReceive(NotificationCenter.default.publisher(for: .openHABDidReceiveNotification)) { _ in + Task { + notifications = await loadNotifications() + } + } } } @@ -161,7 +179,7 @@ extension NotificationsView where Tracker == MainActorNetworkTracker { } let client = HTTPClient(connectionConfiguration: config) - return try await client.notification(urlString: config.url) + return try await client.notifications(urlString: config.url) } catch { Logger.notificationService.error("Failed to load notifications: \(error.localizedDescription, privacy: .public)") return [] diff --git a/openHAB/UI/OpenHABRootViewController.swift b/openHAB/UI/OpenHABRootViewController.swift index bec438a84..599dcef20 100644 --- a/openHAB/UI/OpenHABRootViewController.swift +++ b/openHAB/UI/OpenHABRootViewController.swift @@ -22,6 +22,7 @@ import SideMenu import SwiftMessages import SwiftUI import UIKit +import UserNotifications enum TargetController { case webview @@ -180,6 +181,13 @@ class OpenHABRootViewController: UIViewController { switchToSavedView() isDemoMode = Preferences.shared.currentHomePreferences.demomode } + // Restore correct nav bar state when returning from pushed screens (e.g. notifications, home + // selection). Those screens call setNavigationBarHidden(false) before pushing; popping back + // lands here but does not re-hide the bar, causing the UIKit hamburger and the SwiftUI one + // inside SitemapNavigationView to appear simultaneously. + if currentView === sitemapViewController { + navigationController?.setNavigationBarHidden(true, animated: animated) + } ImageDownloader.default.authenticationChallengeResponder = self } @@ -618,7 +626,7 @@ class OpenHABRootViewController: UIViewController { } } - func handleNotification(action: String?, cloudUserId: String?) { + func handleNotification(action: String?, cloudUserId: String?, notification: UNNotification? = nil) { guard let action else { return } Logger.viewController.info("handleNotification cloudUserId: \(cloudUserId ?? "")") @@ -639,11 +647,11 @@ class OpenHABRootViewController: UIViewController { ] ) _ = await NetworkTracker.shared.waitForActiveConnection() - handleNotificationInternal(action) + handleNotificationInternal(action, notification: notification) } } - private func handleNotificationInternal(_ action: String?) { + private func handleNotificationInternal(_ action: String?, notification: UNNotification? = nil) { Logger.viewController.info("handleNotificationInternal: \(action ?? "")") guard let action else { return } @@ -654,13 +662,13 @@ class OpenHABRootViewController: UIViewController { case "ui": uiCommandAction(cmd) case "command": - sendCommandAction(cmd) + sendCommandAction(cmd, notification: notification) case "http": httpCommandAction(action) case "app": appCommandAction(cmd) case "rule": - ruleCommandAction(cmd) + ruleCommandAction(cmd, notification: notification) case "device": deviceAction(cmd) default: @@ -724,7 +732,7 @@ class OpenHABRootViewController: UIViewController { } } - private func sendCommandAction(_ action: String) { + private func sendCommandAction(_ action: String, notification: UNNotification? = nil) { let components = action.split(separator: ":") guard components.count == 2 else { return @@ -739,8 +747,10 @@ class OpenHABRootViewController: UIViewController { try await NetworkTracker.shared.send(to: itemName, command: itemCommand, deviceId: deviceId) } catch NetworkTrackerError.noActiveConnection { displayErrorNotification("Could not find server") + repostNotification(notification) } catch { displayErrorNotification("Failed to establish a connection: \(error.localizedDescription)") + repostNotification(notification) Logger.viewController.error("Could not send data \(error.localizedDescription)") } } @@ -752,12 +762,23 @@ class OpenHABRootViewController: UIViewController { content.body = message content.sound = UNNotificationSound.default - // Create the request - let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil) - // Schedule the request with the notification center // no error handler because it only printed and tended to crash in swift6 - UNUserNotificationCenter.current().add(request) + UNUserNotificationCenter.current().add(UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)) + } + + private func repostNotification(_ notification: UNNotification?) { + guard let notification else { return } + let original = notification.request.content + let content = UNMutableNotificationContent() + content.title = original.title + content.subtitle = original.subtitle + content.body = original.body + content.sound = original.sound + content.categoryIdentifier = original.categoryIdentifier + content.userInfo = original.userInfo + content.badge = original.badge + UNUserNotificationCenter.current().add(UNNotificationRequest(identifier: notification.request.identifier, content: content, trigger: nil)) } private func httpCommandAction(_ command: String) { @@ -837,7 +858,7 @@ class OpenHABRootViewController: UIViewController { } } - private func ruleCommandAction(_ command: String) { + private func ruleCommandAction(_ command: String, notification: UNNotification? = nil) { let components = command.split(separator: ":", maxSplits: 2) guard !components.isEmpty else { @@ -866,9 +887,11 @@ class OpenHABRootViewController: UIViewController { Logger.viewController.info("Request succeeded") } catch let error as NetworkTrackerError { displayErrorNotification("\(error.localizedDescription)") + repostNotification(notification) } catch { Logger.viewController.error("Could not send data \(error.localizedDescription)") displayErrorNotification("Request to server failed: \(error.localizedDescription)") + repostNotification(notification) } } } diff --git a/openHAB/UI/OpenHABWebViewController.swift b/openHAB/UI/OpenHABWebViewController.swift index dd47e4695..3323510b4 100644 --- a/openHAB/UI/OpenHABWebViewController.swift +++ b/openHAB/UI/OpenHABWebViewController.swift @@ -35,6 +35,8 @@ class OpenHABWebViewController: OpenHABViewController { private var etagChecker: ETagChecker? private var etagCheckerConfigURL: String? // Track which config the checker was created for private var lastLoadedURL: String? // Track the last successfully loaded URL from didFinish + private var isConfirmingExternalURL = false + private var externalURLCooldownUntil: Date? var hasLoadedPage: Bool { !currentTarget.isEmpty @@ -80,6 +82,28 @@ class OpenHABWebViewController: OpenHABViewController { // Notify initial path on load notifyPathChange(); })(); + + // Intercept anchor clicks to custom URL schemes in capture phase so that + // event.preventDefault() fires before the browser initiates the navigation. + // event.isTrusted filters out programmatic .click() / dispatchEvent() calls. + // The message handler still shows a native confirmation because the bridge is + // accessible to all page scripts, not only this injected one. + (function() { + const nativeSchemes = ['http', 'https', 'about', 'blob', 'data', 'javascript', '']; + function isCustomScheme(url) { + const m = /^([a-z][a-z0-9+\\-.]*):/.exec((url || '').toLowerCase()); + return m != null && !nativeSchemes.includes(m[1]); + } + document.addEventListener('click', function(e) { + if (!e.isTrusted) return; + let el = e.target; + while (el && el.tagName !== 'A') el = el.parentElement; + if (el && el.href && isCustomScheme(el.href)) { + e.preventDefault(); + window.webkit.messageHandlers.externalURL.postMessage(el.href); + } + }, true); + })(); """ override open var shouldAutorotate: Bool { @@ -242,6 +266,9 @@ class OpenHABWebViewController: OpenHABViewController { webView = newWebview attachWebViewToLayout(newWebview) } + // Reset fullscreen state so a new page must explicitly re-request it. + // Without this, navigating away from a goFullscreen page keeps the bar hidden. + setHideNavigationBar(shouldHide: false) Logger.viewController.info("Loading URL: \(modifiedUrl)") webView.load(request) } @@ -450,6 +477,7 @@ class OpenHABWebViewController: OpenHABViewController { // adds: window.webkit.messageHandlers.xxxx.postMessage to JS env config.userContentController.add(self, name: "mainUi") config.userContentController.add(self, name: "pathChanged") + config.userContentController.add(self, name: "externalURL") config.userContentController.addUserScript(WKUserScript(source: js, injectionTime: .atDocumentStart, forMainFrameOnly: false)) config.websiteDataStore = WKWebsiteDataStore(forIdentifier: id) @@ -534,6 +562,16 @@ extension OpenHABWebViewController: WKScriptMessageHandler { default: break } } + if message.name == "externalURL", + let urlString = message.body as? String, + let url = URL(string: urlString), + !url.isNativeWebURL { + Task { @MainActor in + if await confirmOpenURL(url) { + await UIApplication.shared.open(url) + } + } + } } } @@ -542,13 +580,58 @@ extension OpenHABWebViewController: WKNavigationDelegate { guard let url = navigationAction.request.url else { return .allow } Logger.viewController.info("decidePolicyFor - url: \(url.absoluteString)") + if !url.isNativeWebURL { + // The injected JS anchor interceptor calls preventDefault() for real user taps, + // so those arrive via the externalURL message handler rather than this delegate. + // All custom-scheme navigations that reach here require an explicit native + // confirmation; navigationType is not a trustworthy user-gesture signal. + if await confirmOpenURL(url) { + await UIApplication.shared.open(url) + } + return .cancel + } if navigationAction.navigationType == .linkActivated { - await UIApplication.shared.open(url) - return .cancel // Stop in WebView + // Only leave the app for genuinely external links. Same-origin links + // (matching the active server's or proxy URL's full origin) are + // SPA-internal navigations that must stay in the WKWebView. + let urlOrigin = url.webOrigin + let activeOrigins: [String?] = [ + activeConfig.flatMap { URL(string: $0.url)?.webOrigin }, + activeConnectionInfo?.proxyURL?.webOrigin + ] + if !activeOrigins.contains(urlOrigin) { + await UIApplication.shared.open(url) + return .cancel + } } return .allow } + private func confirmOpenURL(_ url: URL) async -> Bool { + let now = Date() + guard !isConfirmingExternalURL, + presentedViewController == nil, + viewIfLoaded?.window != nil, + externalURLCooldownUntil.map({ now >= $0 }) ?? true else { + return false + } + isConfirmingExternalURL = true + defer { isConfirmingExternalURL = false } + + let confirmed = await withCheckedContinuation { continuation in + let alert = UIAlertController(title: url.absoluteString, message: nil, preferredStyle: .alert) + alert.addAction(UIAlertAction(title: String(localized: "Cancel"), style: .cancel) { _ in + continuation.resume(returning: false) + }) + alert.addAction(UIAlertAction(title: String(localized: "Open"), style: .default) { _ in + continuation.resume(returning: true) + }) + present(alert, animated: true) + } + externalURLCooldownUntil = Date(timeIntervalSinceNow: 3) + return confirmed + } + // swiftlint:disable:next async_without_await func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse) async -> WKNavigationResponsePolicy { if let response = navigationResponse.response as? HTTPURLResponse { @@ -564,6 +647,7 @@ extension OpenHABWebViewController: WKNavigationDelegate { func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation?) { Logger.viewController.info("didStartProvisionalNavigation - webView.url: \(String(describing: webView.url?.description))") + setHideNavigationBar(shouldHide: false) showActivityIndicator(show: true) } @@ -673,7 +757,6 @@ extension OpenHABWebViewController: WKNavigationDelegate { // Track the successfully loaded URL for ETag comparison lastLoadedURL = webView.url?.absoluteString - setHideNavigationBar(shouldHide: true) showActivityIndicator(show: false) UIView.animate(withDuration: 0.2) { self.loadingOverlay.alpha = 0 @@ -695,7 +778,7 @@ extension OpenHABWebViewController: WKNavigationDelegate { } func webView(_ webView: WKWebView, respondTo challenge: URLAuthenticationChallenge) async -> (URLSession.AuthChallengeDisposition, URLCredential?) { - Logger.viewController.info("Challenge.protectionSpace.authenticationMethod: \(String(describing: challenge.protectionSpace.authenticationMethod))") + Logger.viewController.info("respondTo challenge host=\(challenge.protectionSpace.host, privacy: .public) method=\(challenge.protectionSpace.authenticationMethod, privacy: .public)") if let url = modifyUrl(orig: URL(string: openHABTrackedRootUrl)), challenge.protectionSpace.host == url.host { if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust { @@ -728,6 +811,21 @@ extension OpenHABWebViewController: WKNavigationDelegate { } } +private extension URL { + var isNativeWebURL: Bool { + guard let scheme = scheme?.lowercased() else { return true } + return ["http", "https", "about", "blob", "data", "javascript"].contains(scheme) + } + + /// RFC 6454 origin: scheme + host + port, with default ports (80/443) omitted. + var webOrigin: String? { + guard let scheme = scheme?.lowercased(), let host else { return nil } + let defaultPort = scheme == "https" ? 443 : scheme == "http" ? 80 : nil + let portSuffix = (port != nil && port != defaultPort) ? ":\(port!)" : "" + return "\(scheme)://\(host)\(portSuffix)" + } +} + extension OpenHABWebViewController: WKUIDelegate { func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, diff --git a/openHAB/UI/SwiftUI/Rows/WebRowView.swift b/openHAB/UI/SwiftUI/Rows/WebRowView.swift index 05b23197e..00c4a090a 100644 --- a/openHAB/UI/SwiftUI/Rows/WebRowView.swift +++ b/openHAB/UI/SwiftUI/Rows/WebRowView.swift @@ -70,17 +70,37 @@ enum WebRowViewConfigurationFactory { let configuration = WKWebViewConfiguration() configuration.allowsInlineMediaPlayback = true configuration.mediaTypesRequiringUserActionForPlayback = [] - // iOS 17+: use the per-home sandboxed store shared with OpenHABWebViewController, - // so the widget inherits cloud session cookies already established by Basic UI. - // iOS 16: WKWebsiteDataStore(forIdentifier:) is unavailable; the WebKit default - // shared store is used instead. Cookie sharing with OpenHABWebViewController's - // nonpersistent cloud store is not possible on iOS 16 — Basic Auth challenge - // handling covers authentication for that case. + // iOS 17+: use a per-home sandboxed store derived from homeId but distinct from + // the store used by OpenHABWebViewController (which uses homeId directly). + // Separate identifiers give independent cookie and HTTP-cache storage for widget + // pages, preventing a widget from serving stale or mismatched assets to MainUI. + // WebContent process assignment remains at WebKit's discretion. + // Basic Auth challenge handling covers widget authentication. if #available(iOS 17, *) { - configuration.websiteDataStore = WKWebsiteDataStore(forIdentifier: homeId) + configuration.websiteDataStore = WKWebsiteDataStore(forIdentifier: widgetStoreID(for: homeId)) } return configuration } + + /// Derives a store ID that is per-home but distinct from every possible `homeId`. + /// XOR with a fixed non-zero mask is bijective (different inputs → different outputs) + /// and guarantees `widgetStoreID(x) ≠ x` for all x, regardless of UUID version. + static func widgetStoreID(for homeId: UUID) -> UUID { + // Mask bytes spell "WebRowViewStore!" in ASCII. + let mask: uuid_t = ( + 0x57, 0x65, 0x62, 0x52, 0x6F, 0x77, 0x56, 0x69, + 0x65, 0x77, 0x53, 0x74, 0x6F, 0x72, 0x65, 0x21 + ) + var bytes = homeId.uuid + withUnsafeMutableBytes(of: &bytes) { dst in + withUnsafeBytes(of: mask) { src in + for i in dst.indices { + dst[i] ^= src[i] + } + } + } + return UUID(uuid: bytes) + } } struct WidgetWebViewContainerView: View { diff --git a/openHAB/UI/SwiftUI/SitemapDiagnostics.swift b/openHAB/UI/SwiftUI/SitemapDiagnostics.swift index f80acfb71..b259a450e 100644 --- a/openHAB/UI/SwiftUI/SitemapDiagnostics.swift +++ b/openHAB/UI/SwiftUI/SitemapDiagnostics.swift @@ -115,14 +115,198 @@ actor IconLoadDiagnostics { } } +@MainActor +struct SitemapInitialLoadMeasurement { + private let pipelineStartedAt: Date + private let context: String + private let processAgeMs: Int + private let connectionWasReady: Bool + private var stage = "sitemapDiscovery" + private var stageStartedAt: Date + private var connection: ConnectionConfiguration? + private var discoveryMs = 0 + private var connectionWaitMs = 0 + private var serviceSetupMs = 0 + private var labelFetchMs = 0 + private var initialRequestStartedAt: Date? + private var initialRequestMs = 0 + private var uiPreparationMs = 0 + private var didLog = false + + private var currentInitialRequestMs: Int { + initialRequestStartedAt.map { elapsedMs(since: $0) } ?? initialRequestMs + } + + init(reason: String, + isLinkedPage: Bool, + hasCurrentPage: Bool, + connectionWasReady: Bool, + pipelineStartedAt: Date) { + context = SitemapDiagnostics.initialLoadContext( + reason: reason, + isLinkedPage: isLinkedPage, + hasCurrentPage: hasCurrentPage + ) + processAgeMs = SitemapDiagnostics.processAgeMs + self.connectionWasReady = connectionWasReady + self.pipelineStartedAt = pipelineStartedAt + stageStartedAt = pipelineStartedAt + } + + mutating func beginConnectionSelection() { + transition(to: "connectionSelection") + } + + mutating func beginServiceSetup(connection: ConnectionConfiguration) { + self.connection = connection + transition(to: "serviceSetup") + } + + mutating func beginLabelFetch() { + transition(to: "sitemapLabel") + } + + mutating func beginInitialRequest() { + transition(to: "initialRequest") + initialRequestStartedAt = stageStartedAt + } + + mutating func beginUIPreparation() { + transition(to: "uiPreparation") + } + + mutating func log(status: String, + page: OpenHABPage?, + rowCount: Int, + errorDescription: String = "") { + // The stream can later be cancelled after its initial fetch already logged success. + guard !didLog else { return } + didLog = true + finishCurrentStage() + SitemapDiagnostics.logInitialLoad( + context: context, + status: status, + stage: status == "success" || status == "empty" ? "complete" : stage, + processAgeMs: processAgeMs, + connectionWasReady: connectionWasReady, + connection: connection, + discoveryMs: discoveryMs, + connectionWaitMs: connectionWaitMs, + serviceSetupMs: serviceSetupMs, + labelFetchMs: labelFetchMs, + initialRequestMs: currentInitialRequestMs, + uiPreparationMs: uiPreparationMs, + totalMs: elapsedMs(since: pipelineStartedAt), + widgetCount: page?.widgets.count ?? 0, + rowCount: rowCount, + errorDescription: errorDescription + ) + } + + private mutating func transition(to nextStage: String) { + finishCurrentStage() + stage = nextStage + stageStartedAt = Date() + } + + private mutating func finishCurrentStage() { + let duration = elapsedMs(since: stageStartedAt) + switch stage { + case "sitemapDiscovery": + discoveryMs = duration + case "connectionSelection": + connectionWaitMs = duration + case "serviceSetup": + serviceSetupMs = duration + case "sitemapLabel": + labelFetchMs = duration + case "initialRequest": + initialRequestMs = duration + initialRequestStartedAt = nil + case "uiPreparation": + uiPreparationMs = duration + default: + break + } + } + + private func elapsedMs(since start: Date) -> Int { + Int((Date().timeIntervalSince(start) * 1000).rounded()) + } +} + @MainActor enum SitemapDiagnostics { private static let logger = Logger(subsystem: "org.openhab", category: "SitemapDiagnostics") + private static let processStartedAt = ProcessInfo.processInfo.systemUptime static var isEnabled: Bool { Preferences.shared.applicationPreferences.sitemapDiagnosticsLogging } + static var processAgeMs: Int { + Int(((ProcessInfo.processInfo.systemUptime - processStartedAt) * 1000).rounded()) + } + + static func markProcessLaunch(source: String) { + guard isEnabled else { return } + logger.info("processLaunch source=\(source, privacy: .public)") + } + + static func initialLoadContext(reason: String, + isLinkedPage: Bool, + hasCurrentPage: Bool) -> String { + if reason == "scene-became-active" { + return "foregroundResume" + } + if !isLinkedPage, + !hasCurrentPage, + reason == "manual" || reason == "connection-changed" { + return "coldStart" + } + if isLinkedPage { + return "linkedPage" + } + return reason + } + + // swiftlint:disable:next function_parameter_count + static func logInitialLoad(context: String, + status: String, + stage: String, + processAgeMs: Int, + connectionWasReady: Bool, + connection: ConnectionConfiguration?, + discoveryMs: Int, + connectionWaitMs: Int, + serviceSetupMs: Int, + labelFetchMs: Int, + initialRequestMs: Int, + uiPreparationMs: Int, + totalMs: Int, + widgetCount: Int, + rowCount: Int, + errorDescription: String = "") { + guard isEnabled else { return } + let connectionKind = connection.map(connectionKind(for:)) ?? "none" + let connectionDescription = connection?.publicLogDescription ?? "none" + // swiftlint:disable line_length + logger.info( + "initialLoad context=\(context, privacy: .public) status=\(status, privacy: .public) stage=\(stage, privacy: .public) processAgeMs=\(processAgeMs, privacy: .public) connectionWasReady=\(connectionWasReady, privacy: .public) connectionKind=\(connectionKind, privacy: .public) connection=\(connectionDescription, privacy: .public) discoveryMs=\(discoveryMs, privacy: .public) connectionWaitMs=\(connectionWaitMs, privacy: .public) serviceSetupMs=\(serviceSetupMs, privacy: .public) labelFetchMs=\(labelFetchMs, privacy: .public) initialRequestMs=\(initialRequestMs, privacy: .public) uiPreparationMs=\(uiPreparationMs, privacy: .public) totalMs=\(totalMs, privacy: .public) widgets=\(widgetCount, privacy: .public) rows=\(rowCount, privacy: .public) error=\(errorDescription, privacy: .public)" + ) + // swiftlint:enable line_length + } + + static func connectionKind(for configuration: ConnectionConfiguration) -> String { + if configuration.isCloudConnection { + return "cloud" + } + if configuration.priority == 0 { + return "local" + } + return "remote" + } + // swiftlint:disable:next function_parameter_count static func logUpdate(origin: PageUpdateOrigin, widgetCount: Int, diff --git a/openHAB/UI/SwiftUI/SitemapNavigationView.swift b/openHAB/UI/SwiftUI/SitemapNavigationView.swift index cb06f0ec2..6cb872b35 100644 --- a/openHAB/UI/SwiftUI/SitemapNavigationView.swift +++ b/openHAB/UI/SwiftUI/SitemapNavigationView.swift @@ -15,10 +15,9 @@ import SFSafeSymbols import SwiftUI struct SitemapNavigationView: View { - @Environment(\.scenePhase) private var scenePhase @StateObject var viewModel = SitemapPageViewModel() - @State private var hasSeenActivePhase = false @State private var isSearchPresented = false + @FocusState private var isLegacySearchFocused: Bool let onShowSideMenu: () -> Void var body: some View { @@ -28,19 +27,6 @@ struct SitemapNavigationView: View { SitemapPageView(viewModel: SitemapPageViewModel(pageUrl: nav.pageLink, title: nav.pageTitle)) } } - .onChange(of: scenePhase) { _, newPhase in - switch newPhase { - case .active: - // Skip only the first activation to avoid racing the initial .task startup. - guard hasSeenActivePhase else { - hasSeenActivePhase = true - return - } - viewModel.refreshOnForeground() - default: - break - } - } } @ViewBuilder @@ -58,13 +44,24 @@ struct SitemapNavigationView: View { } if viewModel.showSearchField { ToolbarItem(placement: .navigationBarTrailing) { - Button { - isSearchPresented = true - } label: { - Image(systemSymbol: .magnifyingglass) + if #available(iOS 17.0, *) { + Button { + isSearchPresented = true + } label: { + Image(systemSymbol: .magnifyingglass) + } + .ohMinimumHitTarget() + .accessibilityLabel("Search") + } else { + Button { + isSearchPresented = true + isLegacySearchFocused = true + } label: { + Image(systemSymbol: .magnifyingglass) + } + .ohMinimumHitTarget() + .accessibilityLabel("Search") } - .ohMinimumHitTarget() - .accessibilityLabel("Search") } } ToolbarItem(placement: .navigationBarTrailing) { @@ -78,21 +75,75 @@ struct SitemapNavigationView: View { } } - if viewModel.showSearchField, isSearchPresented { - page - .searchable( - text: $viewModel.searchText, - isPresented: $isSearchPresented, - placement: .navigationBarDrawer(displayMode: .always), - prompt: Text(String(localized: "search_items", comment: "")) - ) - .autocorrectionDisabled() - .textInputAutocapitalization(.never) + if viewModel.showSearchField { + if #available(iOS 17.0, *) { + if isSearchPresented { + page + .searchable( + text: $viewModel.searchText, + isPresented: $isSearchPresented, + placement: .navigationBarDrawer(displayMode: .always), + prompt: Text(String(localized: "search_items", comment: "")) + ) + .autocorrectionDisabled() + .textInputAutocapitalization(.never) + } else { + page + } + } else { + page + .safeAreaInset(edge: .bottom) { + if isSearchPresented { + legacySearchBar + } + } + } } else { page } } + private var legacySearchBar: some View { + HStack(spacing: 8) { + Image(systemSymbol: .magnifyingglass) + .foregroundStyle(.secondary) + .ohTextToken(.secondary) + + TextField(String(localized: "search_items", comment: ""), text: $viewModel.searchText) + .textInputAutocapitalization(.never) + .autocorrectionDisabled() + .focused($isLegacySearchFocused) + .ohTextToken(.secondary) + + if !viewModel.searchText.isEmpty { + Button { + viewModel.searchText = "" + } label: { + Image(systemSymbol: .xmarkCircleFill) + .foregroundStyle(.secondary) + } + .buttonStyle(.plain) + } + + Button { + isSearchPresented = false + isLegacySearchFocused = false + } label: { + Image(systemSymbol: .xmark) + .foregroundStyle(.secondary) + } + .buttonStyle(.plain) + } + .padding(.horizontal, 10) + .padding(.vertical, 8) + .background( + Color(.secondarySystemBackground).opacity(0.6), + in: RoundedRectangle(cornerRadius: 10) + ) + .padding(.horizontal, 12) + .padding(.bottom, 6) + } + @ViewBuilder private var interactionIndicator: some View { switch viewModel.sitemapInteractionSummary { @@ -142,11 +193,6 @@ struct SitemapNavigationView: View { } .foregroundStyle(.red) .ohTextToken(.secondary) - .phaseAnimator([1.0, 0.4]) { content, opacity in - content.opacity(opacity) - } animation: { _ in - .easeInOut(duration: 0.8) - } .accessibilityLabel("Command failures: \(count)") } } diff --git a/openHAB/UI/SwiftUI/SitemapPageView.swift b/openHAB/UI/SwiftUI/SitemapPageView.swift index 7504a24a5..38d30bdbb 100644 --- a/openHAB/UI/SwiftUI/SitemapPageView.swift +++ b/openHAB/UI/SwiftUI/SitemapPageView.swift @@ -17,9 +17,14 @@ import UIKit struct SitemapPageView: View { @StateObject var viewModel = SitemapPageViewModel() + @Environment(\.scenePhase) private var scenePhase @State private var idleTimerDisabled = false @State private var scrollPosition = ScrollPosition() @State private var showScrollToTop = false + /// Sub-pages are created while the app is already active, so the first .active transition they + /// observe is a genuine return from background — don't skip it. Root pages start false to skip + /// the launch-time .active that races the initial .task startup. + @State private var hasSeenActivePhase: Bool private var isLinkedPage: Bool { viewModel.isLinked @@ -105,6 +110,15 @@ struct SitemapPageView: View { UIApplication.shared.isIdleTimerDisabled = false } } + .onChange(of: scenePhase) { _, newPhase in + if newPhase == .active { + guard hasSeenActivePhase else { + hasSeenActivePhase = true + return + } + viewModel.refreshOnForeground() + } + } .navigationTitle(viewModel.pageTitle) .navigationBarTitleDisplayMode(.large) .alert("Error", isPresented: Binding( @@ -122,6 +136,7 @@ struct SitemapPageView: View { init(viewModel: SitemapPageViewModel) { _viewModel = StateObject(wrappedValue: viewModel) + _hasSeenActivePhase = State(initialValue: viewModel.isLinked) } } diff --git a/openHABTestsSwift/SitemapDiagnosticsTests.swift b/openHABTestsSwift/SitemapDiagnosticsTests.swift new file mode 100644 index 000000000..ea14fea2f --- /dev/null +++ b/openHABTestsSwift/SitemapDiagnosticsTests.swift @@ -0,0 +1,81 @@ +// Copyright (c) 2010-2026 Contributors to the openHAB project +// +// See the NOTICE file(s) distributed with this work for additional +// information. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0 +// +// SPDX-License-Identifier: EPL-2.0 + +@testable import openHAB +import OpenHABCore +import Testing + +@MainActor +struct SitemapDiagnosticsTests { + @Test(arguments: ["manual", "connection-changed"]) + func classifiesRootInitialLoadAsColdStart(reason: String) { + let context = SitemapDiagnostics.initialLoadContext( + reason: reason, + isLinkedPage: false, + hasCurrentPage: false + ) + + #expect(context == "coldStart") + } + + @Test + func classifiesForegroundRefreshSeparately() { + let context = SitemapDiagnostics.initialLoadContext( + reason: "scene-became-active", + isLinkedPage: false, + hasCurrentPage: true + ) + + #expect(context == "foregroundResume") + } + + @Test + func keepsWarmManualReloadContext() { + let context = SitemapDiagnostics.initialLoadContext( + reason: "manual", + isLinkedPage: false, + hasCurrentPage: true + ) + + #expect(context == "manual") + } + + @Test + func classifiesLinkedPageSeparately() { + let context = SitemapDiagnostics.initialLoadContext( + reason: "manual", + isLinkedPage: true, + hasCurrentPage: false + ) + + #expect(context == "linkedPage") + } + + @Test(arguments: [ + (supportsNotifications: true, priority: 1, expected: "cloud"), + (supportsNotifications: false, priority: 0, expected: "local"), + (supportsNotifications: false, priority: 1, expected: "remote") + ]) + func classifiesConnectionKind(supportsNotifications: Bool, + priority: Int, + expected: String) { + // isCloudConnection currently derives from supportsNotifications. + let configuration = ConnectionConfiguration( + url: "https://example.invalid", + username: "", + password: "", + supportsNotifications: supportsNotifications, + priority: priority + ) + + #expect(SitemapDiagnostics.connectionKind(for: configuration) == expected) + } +} diff --git a/openHABTestsSwift/WebRowViewConfigurationTests.swift b/openHABTestsSwift/WebRowViewConfigurationTests.swift index 3e765f29b..af6f456c5 100644 --- a/openHABTestsSwift/WebRowViewConfigurationTests.swift +++ b/openHABTestsSwift/WebRowViewConfigurationTests.swift @@ -67,11 +67,27 @@ struct WebRowViewConfigurationTests { @available(iOS 17, *) @MainActor @Test - func webRowConfigurationUsesPerHomeDataStore() { + func webRowConfigurationUsesPerHomeDerivedDataStore() { let homeId = UUID() let configuration = WebRowViewConfigurationFactory.make(homeId: homeId) + let expectedStoreID = WebRowViewConfigurationFactory.widgetStoreID(for: homeId) - #expect(configuration.websiteDataStore.identifier == homeId) + #expect(configuration.websiteDataStore.identifier == expectedStoreID) + } + + @available(iOS 17, *) + @MainActor + @Test + func widgetStoreIsIsolatedFromMainUIStore() { + // Verify for a range of UUIDs including one whose version nibble is already "5" + // to exercise the XOR path rather than a nibble-replacement assumption. + let v4Home = UUID() + let v5Home = UUID(uuidString: v4Home.uuidString.replacingCharacters( + in: v4Home.uuidString.index(v4Home.uuidString.startIndex, offsetBy: 14) ... v4Home.uuidString.index(v4Home.uuidString.startIndex, offsetBy: 14), + with: "5" + ))! + #expect(WebRowViewConfigurationFactory.widgetStoreID(for: v4Home) != v4Home) + #expect(WebRowViewConfigurationFactory.widgetStoreID(for: v5Home) != v5Home) } @available(iOS 17, *) diff --git a/openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/Contents.json b/openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/Contents.json deleted file mode 100644 index aefef2914..000000000 --- a/openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/Contents.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "images" : [ - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : "<=145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">161" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">183" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Contents.json b/openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Contents.json deleted file mode 100644 index e8b3252e3..000000000 --- a/openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Contents.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "assets" : [ - { - "filename" : "Circular.imageset", - "idiom" : "watch", - "role" : "circular" - }, - { - "filename" : "Extra Large.imageset", - "idiom" : "watch", - "role" : "extra-large" - }, - { - "filename" : "Graphic Bezel.imageset", - "idiom" : "watch", - "role" : "graphic-bezel" - }, - { - "filename" : "Graphic Circular.imageset", - "idiom" : "watch", - "role" : "graphic-circular" - }, - { - "filename" : "Graphic Corner.imageset", - "idiom" : "watch", - "role" : "graphic-corner" - }, - { - "filename" : "Graphic Extra Large.imageset", - "idiom" : "watch", - "role" : "graphic-extra-large" - }, - { - "filename" : "Graphic Large Rectangular.imageset", - "idiom" : "watch", - "role" : "graphic-large-rectangular" - }, - { - "filename" : "Modular.imageset", - "idiom" : "watch", - "role" : "modular" - }, - { - "filename" : "Utilitarian.imageset", - "idiom" : "watch", - "role" : "utilitarian" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Extra Large.imageset/Contents.json b/openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Extra Large.imageset/Contents.json deleted file mode 100644 index aefef2914..000000000 --- a/openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Extra Large.imageset/Contents.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "images" : [ - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : "<=145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">161" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">183" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Graphic Bezel.imageset/Contents.json b/openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Graphic Bezel.imageset/Contents.json deleted file mode 100644 index aefef2914..000000000 --- a/openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Graphic Bezel.imageset/Contents.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "images" : [ - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : "<=145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">161" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">183" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Graphic Circular.imageset/Contents.json b/openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Graphic Circular.imageset/Contents.json deleted file mode 100644 index aefef2914..000000000 --- a/openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Graphic Circular.imageset/Contents.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "images" : [ - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : "<=145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">161" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">183" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Graphic Corner.imageset/Contents.json b/openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Graphic Corner.imageset/Contents.json deleted file mode 100644 index e011e3271..000000000 --- a/openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Graphic Corner.imageset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "images" : [ - { - "idiom" : "watch", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Graphic Extra Large.imageset/Contents.json b/openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Graphic Extra Large.imageset/Contents.json deleted file mode 100644 index 26454cac8..000000000 --- a/openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Graphic Extra Large.imageset/Contents.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "images" : [ - { - "idiom" : "watch", - "scale" : "2x" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : "<=145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">183" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "auto-scaling" : "auto" - } -} diff --git a/openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Graphic Large Rectangular.imageset/Contents.json b/openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Graphic Large Rectangular.imageset/Contents.json deleted file mode 100644 index e011e3271..000000000 --- a/openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Graphic Large Rectangular.imageset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "images" : [ - { - "idiom" : "watch", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/Contents.json b/openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/Contents.json deleted file mode 100644 index aefef2914..000000000 --- a/openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/Contents.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "images" : [ - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : "<=145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">161" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">183" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/Contents.json b/openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/Contents.json deleted file mode 100644 index aefef2914..000000000 --- a/openHABWatch/Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/Contents.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "images" : [ - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : "<=145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">161" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">183" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/openHABWatch/Extension/Assets.xcassets/OHIcon.imageset/Contents.json b/openHABWatch/Extension/Assets.xcassets/OHIcon.imageset/Contents.json deleted file mode 100644 index b9688542d..000000000 --- a/openHABWatch/Extension/Assets.xcassets/OHIcon.imageset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "Icon.png" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/openHABWatch/Extension/Assets.xcassets/OHIcon.imageset/Icon.png b/openHABWatch/Extension/Assets.xcassets/OHIcon.imageset/Icon.png deleted file mode 100644 index bfcd842156e5a6f92abe30e36777a6c3a568191a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 381270 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 z
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

%*{H0++CliK7AY5 zM%l>mu(jybh_Q!~9I}UTJKi*BqF81qj)2tDQay!!C?_?Ja?T8sLv_5R+Hn7YeCsVv zm_0e9co$^Y#2T@7R#=6tl#Y}Pxy9_B=wDix9R{0l5}_R%OrbR_#JuXPiDOKM)**jk zalXln@uq}HW+_(R|L{ra2hX;uzAvBH5{{vSE`~);Bmh+uoH2F&YV%U_<+40vjE55)tvG0wEV+3+w8vcmI6{AL}Nj z9zt69*-SlBdR)K@BB?}26rK@GKF?i|zkab-J~Sx*rF6U7+-QTF`1!=nRD|UtGA}ve zq-=gIWdm)F5e_aYq zpUi6R86LQtFh8_3dP4K@D5L0p>cNPNu}_N-Yx4+igB^?4U=6)EGrT`4W)xNj{Net2 zB_D9n<2P)?Tf$+^tqmbuT4y$LWwU&ZAdqAlbZX6@2*Xjz+|I$IYc)-r>NW-mdx4`A zS)>-GI-Ke8bB%92XBeAzN0Xa4lVp3Uv7^ArSGgaoK=NsmCeCnnFTUuksQ)XU?Jn?y zdxt3({SZIIpAnQ!4@U(sfkEr$OG0O3M^RRL8a!dwsMgOr+Y+CRu{~I6wm%lkFyb>r zXPVP>rT$K3$P`+-&ROI}z0JUCMng`T|;U-bweJ(+i74 zME8UxBjt+~T1?=%DtNye1^I(40y__Hx-CVKOczNukVNFx64GvxKnt~6`U#*#UlzS} zoN|!{WgC%*@3OVf6X?iPym(v&U1XN^xxKVNIXMt=dmqH)MZ?Jam4VY%w~ynPh7uaW98cE_VGy1X+d(W7o+4(`(bS* zWr_qr>qHCdvSCd9faAa=oF+td`1hX7H3 zXQXARccHH{U6-_9m9g4^-y+c^Tu^>2NDD|q*5C0S0R-HI_U@}AR?qnSkmE?paiS@3 z&94IZrCj=mms@ziTFDa@H%u4d>E>N7S<|Y+%S-uW9xPPz<4lx5TT!tJJMKnpSZGPa z;73Q-E7Dm$XS7$IfDptG@Iz_^OdF(D+OA?s{9d!c0k_f^eB&PBkWaYG8G&q~%O-VQZj z2M=51L-5z|(1G#e*ztR^Qe5y=J zDG9;{c0}mX!`4#Pq7R~ZzWeP+oHEr&zF}t{`C~zqQONsua52qqd~)gHQ;&6n-p^9% zKYGcYfOX#YBZl{zT8e`x-Y1ppmvv9Z(F|kl)F~efMe_iYQ-ln`&IX|m3%&5co~C{+ zbJuKtU#L;S}XvYmz7Rtg4BHxj=!27 zp&%KzJb+*wd{(8~uU*_e`1cgOyzO?ux?e}B@Y&IOMj7GtJi}g6dsBa!sDW^l1XC960BygggbAqRH7#5K^5h?sLnbh5!=)Z0%>H<@cLfdjx+9aeHkz z%~e-&)cAs%h{#v|QkHjHIJ+e+C}JQskT`qwr5hvtms*R}wrkrCY=;+iR|%iyj@CYTZEM)=s-nTxEj`?n4DBuNT^SS+fx%Y&dUk~iAQ8zbV+mHG zh5G*3$aNDl-gM&^WgF-m3?f>Jn`(T+&Z&&m!JbIC;HDtN)Yo2GOfC}=@3XH6r7#Lj z)qZaDd`=#F;+QlE)=`v(aGSPa+CH{zaX@5B@$0im3%f_##_$}xsZVdAOgTZE5{<1@ zr5W?az5?$)s5Va!FC{@OPTF-PSf-7ZwCH1{Ja0n&w8thEVQ=r4D$JT%r&)Xbw8$$( z+PZHm{{h>?W4}!CB$W62hW{dYQj~w1cn*9gs&#E_P*!#!t`&PJ#Ja7b_e`fN0-@`$ z`7k=w1lTH4)qrdQ57gto=8leP$J9bIrX)gs(q+z<0?*Q%bx{Z0rr$hdUKoG^$ij7W z{UeA1B5dOV1?fm4USr2W_W9tvgv>1c$hq4G8|u8#=_xb;!=H^HZ_ zOg3^RqTCz*Q!2Z*bA0P5iTb6we0cn5Dsk zM0z%T+P?=lK!{P&WX}E+W1vH_q5rWAzV-8L%|%@hKAKw8pjQF{OOSZq3SJoVE@3Os zmn)!4SB&ye+{}Hl^iiUb4pILtm~Ru@g@) z=Re+sOW~|5AiqYq`F}zh}rfeHx&pqVM?H%})mJ65q4%g#6PnY9XBOrMs|IyO8q+4PWmM!c>`w_)Kz@ zRLHTD!XM9cxWc^i-MQor@>X*w#DS(*5S)6amBOuL!HF@i)aLd7=V-GtqoY;-3I-R( z`Pm#|RhadM{T@+HT?60E-z}nHCZm&rxYh$*DR~p|yFCyO*p_-;y`*z+r0qt5(|pHh zwCE{QTv1_Dd|Bqm;)Bz}mu2SDm8W$DMyi!^0CENa-A*hAuI(zq2BP26vf2z#`haupj9RjZEmw1aM?XHbjvVY`28x7RwEMr)E z3G*66D41d(ySyQ7yDM@etBJ|DLFx_&aB{ch!*gj3JfP)k^}hQ1Z2A9QRttFM`MCBzx-6gA6}2sQV6tcPAwZWj*|Jj3uXrLr8JZj^1+hm7%- z_PZqsCiW3@-RI4*v>GQ8H_hIWWQH+kg-|D8yw_2nK0bkDmXhqu%xk>CyWY{8nt5<* zq;z(oDRB=$iLWC1dNzv(CT9Q>s_3;*;j#byikIK4NvrR9d)eC%n`qrs$_rs-Z=<#K zsin7_4kJR`z2{z*-JO`^YD;_l6yRPgMa`c@G@QXFetFPlv11bZS$QW}u-Q8hLFBA~ z&RO0#&(xCyvZyA96td5^>}gZ-rp$(Ecm2}fbbg-DP5*=zjHkMcI@%wWEOv`b;I~u) z>2-1{6a1d8gVQNEq>^!8n!iYgT8>D+BdWa0vCof_&Zb<-%||(1L+GdB`L3q8hP2tL zkJkS>{T`waUYfsNtD%-`ry#Zvm4o=5(Ym2H1@F{oR@TQEKiKS~?Aqd>nPWdoQqJJn zljYieFXs%JW*;T25$^dxIbe)bw%i*$&XK?6;7nmi43Z;(rdfVjh7}jH<6a+ZLC&?tmy2HeM$})t}tMeZq=b< z9&9y!RAwV8P@(2R%bJ`b*E5v|`-(?yM3Rb`1@V!B!ja59bx*V#YOsc0^|!|^|2l!(mgqM(W6?8&)j=0GrjVsqux!Y)jGb)~~gHPx&=w*+?P)6r00=lD2M z-IRGf?YJwr=kjo}32w(oZ7-=xI}(j;yuT>MTmTg<x<}Kx7MoM)0)o z#__HZd*b8#G^rGJL4E5Z&U51>MSMzT>&r4$cYedKm?u-#PMCPoF=hE_h4A3F+xw3G7^0M0>|@ zQsV3{N&(CnWphb3M5G*_nlEitQJcMdqR{IaCNA727+U<| zO|~IqV2V-KC^T%VHI;2AjPf1m9o_X5bMWu*N6XT|{A3d^Or*4d>>r|?@T2(iY0UDWgagwltJ+Vgm27!4BI{swX#ZLVM~ zh0ghv+sgYD_S+>f`|NhpBV;=Egm|s1t6l$_(04Y3S1;OEQovs?pJJK>l?+8~>?7xF znvaQsWa&}FJ-gc5$96`<%^rN2D3Qx0#CU`O{oN~}5Hp}?G z8%#IaPFRDV>bWU!nCz$!e<%TF3u{6(^w(3$$_fpJM4Yk-ls0U29Qrq>HXHiQP25b| z>}UBY`AKMeJ~0gT=L4|Ql7H%-LKt}7-&~?a`SX`ovb%QCPnsU$w49X({>>|gqdcEW z@+#>+?%jBDL-0V?azpLcTQ5}hXo%bDk)H1!_8);0P6WbS-V@6eI6|cBU31v z%g`_66>O@wKXmo2Zaf&Ax2XHR@<0;`CtT>L{R1J=QLfW{ll5fWa)J+$&+ zDhgOWeruUo2CLZ9EhRGJ?r~cG!wwxl#O=WA7oRJ#LiQ8sJ`eXl%!RhDM=X-8qN(Ph zL!?|NEotpaE(8v~DM?Ly`SPTG?c@=uUD;Dub~r$i+qww>c$X7g>Nxi$td_?9g%!M1 zLu)wsn@3SW$tOE+P+kpU^x+@i6SBk$dfX+at_H!kU`=${NGw2H(jQBEm_hP#c$6#G zAtcJP!6e-t9Z`=h9m=31zPxr^P{8Y!b`>~VBb#F7c=HmmaH%%EKtJ8pS6!|SpZL7A z#tY$3-Ta7MjFg%f-r)J@Nt&GNeP2D>;mMs=sC$YIvftFHriH7Hlmn*Dt|KPAw!riL zz>K?Dbb&I-DEukIaCh_)7r5Vn#&K!RiFlSk8~?fwB%9jGXk_g*1+Rm~M{+4*)PnGH=`IZU zkKlJ$GQKm{jlqa8ubxk*CG7DI1IMxR4-JIAB5kk%%$fWUI76@x~jaCQn`M2vv>ZOte!O`u`K?m1t5(BCPK1_Onbvc zLEpX|^f<=W-9fyCOiC?rxf7N0=I)Bc8Hqke6fMCv0F4pwd&;(QV)xiQ62o?|lvl)v z$?2{x`Wf21TB5Hktfa3_U`bA;3w+Y%iOYBwaTGn4Ex4QyR9h*kTi!bH*hFfrI$$T*HxYpXwzBw5`1z zG)v>=#9J{;Oc0FK5>kij2Wn{aOy%7|3KF4d9|G|+yuk#D>5m#hdrTcO%m8r{oDSru$R(Pr;pG-}iJl zP5hq8mJ)Fk7V{N)#bk0}7}M zqX#6diFEdxUW=VPF!f3M%AWQf2H$ctfZ#c{Q+9Nc{DJ#n(aTnMV6J)>;nPTjEgF`5 zUgs4M$Lb&6MY4yrl2o9MAV51tRw%wuQ}tYzLCL<}?&?p_TZkpBQk4foB_ec_FL8bh zdT~xo&`n=xmF3O$1c@Dz$X6OWoeJIHx)ln6e~v#!=r4E12guKnx_ zYs5;-!w7sqB*@po77o=T3R#JTqG zTr?s$^8=!26Kk@v73lR6P&92aOnYCN_PCbX=@K6n=pS{13!J>5=ym7CNd$zQnN{Wl z@huV#`^OWk{L;)gU0aE89XkaeU6#jQ&qJDoNxwa_2d=6}c%U@c)eQi8NJbdsGkiBg3T$6w>d=TTwxUYmxFcU$2HRG>xD3t4dWfP#_D*{{4CPtG7(H?J zix<|mASv9Q2%oL>4!lWare{h(1i5q}x0=x`UVA zj3Za!4vS@xs|J+^*4!nzggi>qzqH}LL<~k>iQaAclgxE~q$iTiW)e~cwmX*>uzBG4 z2X=;>V!8xQ8h!=y*W^ZR8)GsVJ5?>t+Zd>rpG^AeP~Zs4Q_NAHpv*G^0C4r7o<|%A zm&*okbUMRlxHS9ANL8bTJ(yhMhuV0}sBA8-jAQzQH(BFNKUez?ak_D@|4$wtPf4;% z{|=w3owpT=x01Zsr7{z*s9Va{&!^A&*E~fEpK_W9U#6E=RitywCOc=RV9pn?v-(4~ zv1^{_GiUc3I78$X_*b}Eb~yMMyW}W4DmktX4TY+UbhVsFk~9r69svG9ywZ6cp{E&> z?i9AX8u8_VEtbiyW8#NpovACav^CoYy9T8V;@WNZI0JKCm;_1#WM35RO&*>I8@F$7 zXp7SesnZe7W)-n4>%i(n)heCMqcT4Tepkyp2f5q1eq#y1pD5BdS0M{=8M9&DziQNG z85?>6wqD`l4nSXi9Tc))d_N*@6`nQ<-rvT2Cc@^TgRd2E-bdGvyF+-hW|52QbbZV(`5Q!yY^pcM0N{*ro444k5ͫSj3^pt%In2 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

yv8;%%u<81<7?Fn^{Wyl@NZ=N(v1Sl0TTlpULm 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 z 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 diff --git a/openHABWatch/Extension/Assets.xcassets/OHTemplateIcon.imageset/Contents.json b/openHABWatch/Extension/Assets.xcassets/OHTemplateIcon.imageset/Contents.json deleted file mode 100644 index 0eca0f536..000000000 --- a/openHABWatch/Extension/Assets.xcassets/OHTemplateIcon.imageset/Contents.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "oh_logo_template.pdf" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - }, - "properties" : { - "template-rendering-intent" : "template", - "preserves-vector-representation" : true - } -} \ No newline at end of file diff --git a/openHABWatch/Extension/Assets.xcassets/OHTemplateIcon.imageset/oh_logo_template.pdf b/openHABWatch/Extension/Assets.xcassets/OHTemplateIcon.imageset/oh_logo_template.pdf deleted file mode 100644 index 4bcec77a4bc4a33571cd56a3497ba223b78fab96..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6627 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 diff --git a/openHABWatch/Extension/ComplicationController.swift b/openHABWatch/Extension/ComplicationController.swift deleted file mode 100644 index b17e74922..000000000 --- a/openHABWatch/Extension/ComplicationController.swift +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright (c) 2010-2026 Contributors to the openHAB project -// -// See the NOTICE file(s) distributed with this work for additional -// information. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0 -// -// SPDX-License-Identifier: EPL-2.0 - -import ClockKit -import DeviceKit -import WatchKit - -class ComplicationController: NSObject, CLKComplicationDataSource { - // MARK: - Complication Configuration - - func getComplicationDescriptors(handler: @escaping ([CLKComplicationDescriptor]) -> Void) { - // watchOS7: replacing deprecated CLKComplicationSupportedFamilies - let descriptors = [ - CLKComplicationDescriptor(identifier: "complication", displayName: Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as! String, supportedFamilies: CLKComplicationFamily.allCases) - // Multiple complication support can be added here with more descriptors - ] - handler(descriptors) - } - - func handleSharedComplicationDescriptors(_ complicationDescriptors: [CLKComplicationDescriptor]) { - // Do any necessary work to support these newly shared complication descriptors - } - - // MARK: - Timeline Configuration - - func getSupportedTimeTravelDirections(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTimeTravelDirections) -> Void) { - handler([]) - } - - func getTimelineStartDate(for complication: CLKComplication, withHandler handler: @escaping (Date?) -> Void) { - handler(nil) - } - - func getTimelineEndDate(for complication: CLKComplication, withHandler handler: @escaping (Date?) -> Void) { - handler(nil) - } - - func getPrivacyBehavior(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationPrivacyBehavior) -> Void) { - handler(.showOnLockScreen) - } - - // MARK: - Timeline Population - - func getCurrentTimelineEntry(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTimelineEntry?) -> Void) { - // Call the handler with the current timeline entry - let template = getTemplate(complication: complication) - if template != nil { - handler(CLKComplicationTimelineEntry(date: Date(), complicationTemplate: template!)) - } - } - - func getTimelineEntries(for complication: CLKComplication, before date: Date, limit: Int, withHandler handler: @escaping ([CLKComplicationTimelineEntry]?) -> Void) { - // Call the handler with the timeline entries prior to the given date - handler(nil) - } - - func getTimelineEntries(for complication: CLKComplication, after date: Date, limit: Int, withHandler handler: @escaping ([CLKComplicationTimelineEntry]?) -> Void) { - // Call the handler with the timeline entries after to the given date - handler(nil) - } - - // MARK: - Placeholder Templates - - func getLocalizableSampleTemplate(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTemplate?) -> Void) { - // This method will be called once per supported complication, and the results will be cached - handler(getTemplate(complication: complication)) - } - - func getPlaceholderTemplate(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTemplate?) -> Void) { - handler(getTemplate(complication: complication)) - } - - private func getTemplate(complication: CLKComplication) -> CLKComplicationTemplate? { - // default ist circularSmall - var template: CLKComplicationTemplate? - - switch complication.family { - case .modularSmall: - template = CLKComplicationTemplateModularSmallRingImage(imageProvider: CLKImageProvider(onePieceImage: UIImage(named: "OHTemplateIcon") ?? UIImage()), fillFraction: 1, ringStyle: CLKComplicationRingStyle.closed) - case .utilitarianSmall: - template = CLKComplicationTemplateUtilitarianSmallRingImage(imageProvider: CLKImageProvider(onePieceImage: UIImage(named: "OHTemplateIcon") ?? UIImage()), fillFraction: 1, ringStyle: CLKComplicationRingStyle.closed) - case .circularSmall: - template = CLKComplicationTemplateCircularSmallRingImage(imageProvider: CLKImageProvider(onePieceImage: UIImage(named: "OHTemplateIcon") ?? UIImage()), fillFraction: 1, ringStyle: CLKComplicationRingStyle.closed) - case .extraLarge: - template = CLKComplicationTemplateExtraLargeSimpleImage(imageProvider: CLKImageProvider(onePieceImage: UIImage(named: "OHTemplateIcon") ?? UIImage())) - case .graphicCorner: - template = CLKComplicationTemplateGraphicCornerTextImage(textProvider: CLKSimpleTextProvider(text: "openHAB"), imageProvider: CLKFullColorImageProvider(fullColorImage: getGraphicCornerImage())) - case .graphicBezel: - let modTemplate = CLKComplicationTemplateGraphicCircularImage(imageProvider: CLKFullColorImageProvider(fullColorImage: getGraphicCornerImage())) - template = CLKComplicationTemplateGraphicBezelCircularText(circularTemplate: modTemplate, textProvider: CLKSimpleTextProvider(text: "openHAB")) - case .graphicCircular: - template = CLKComplicationTemplateGraphicCircularImage(imageProvider: CLKFullColorImageProvider(fullColorImage: getGraphicCircularImage())) - default: break - } - - return template - } - - private func getGraphicCornerImage() -> UIImage { - let dimension: CGFloat = Device.current.diagonal < 2.0 ? 20 : 22 - return getGraphicImage(withSize: CGSize(width: dimension, height: dimension)) - } - - private func getGraphicCircularImage() -> UIImage { - let dimension: CGFloat = Device.current.diagonal < 2.0 ? 42 : 47 - return getGraphicImage(withSize: CGSize(width: dimension, height: dimension)) - } - - private func getGraphicImage(withSize size: CGSize) -> UIImage { - let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height) - - UIGraphicsBeginImageContextWithOptions(rect.size, false, WKInterfaceDevice.current().screenScale) - - if let image = UIImage(named: "OHIcon") { - image.draw(in: rect.insetBy(dx: -(rect.width / 10), dy: -(rect.height / 10))) - } - - let image = UIGraphicsGetImageFromCurrentImageContext() - UIGraphicsEndImageContext() - - return image ?? UIImage() - } -} diff --git a/openHABWatch/Extension/Info.plist b/openHABWatch/Extension/Info.plist index 7682d34a1..1056356cc 100644 --- a/openHABWatch/Extension/Info.plist +++ b/openHABWatch/Extension/Info.plist @@ -20,8 +20,6 @@ $(MARKETING_VERSION) CFBundleVersion $(CURRENT_PROJECT_VERSION) - CLKComplicationPrincipalClass - $(PRODUCT_MODULE_NAME).ComplicationController NSAppTransportSecurity NSAllowsArbitraryLoads diff --git a/openHABWidget/OpenHABWidgetHelpers.swift b/openHABWidget/OpenHABIconOverlay.swift similarity index 100% rename from openHABWidget/OpenHABWidgetHelpers.swift rename to openHABWidget/OpenHABIconOverlay.swift diff --git a/openHABWidget/SensorConfigurationAppIntent.swift b/openHABWidget/SensorLargeConfigurationAppIntent.swift similarity index 100% rename from openHABWidget/SensorConfigurationAppIntent.swift rename to openHABWidget/SensorLargeConfigurationAppIntent.swift diff --git a/openHABWidget/SensorWidgetView.swift b/openHABWidget/SensorLargeWidget.swift similarity index 100% rename from openHABWidget/SensorWidgetView.swift rename to openHABWidget/SensorLargeWidget.swift From 7b02c7f4ea73673668a30a4c40fe82f89afbb9f9 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Fri, 12 Jun 2026 19:59:32 +0200 Subject: [PATCH 18/91] chore: bump openHABTestsSwift deployment target to iOS 18.0 Signed-off-by: Tim Mueller-Seydlitz --- openHAB.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openHAB.xcodeproj/project.pbxproj b/openHAB.xcodeproj/project.pbxproj index da0ab2249..569c453a9 100644 --- a/openHAB.xcodeproj/project.pbxproj +++ b/openHAB.xcodeproj/project.pbxproj @@ -1858,7 +1858,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = openHABTestsSwift/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1893,7 +1893,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = openHABTestsSwift/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", From cb183ec8eb7c783c2ebf4764879caadb08cfb0a0 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Sun, 14 Jun 2026 13:30:11 +0200 Subject: [PATCH 19/91] build: raise macOS minimum to 15 for AsyncSequence.Failure support Signed-off-by: Tim Mueller-Seydlitz --- CommonUI/Package.swift | 2 +- OpenHABCore/Package.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CommonUI/Package.swift b/CommonUI/Package.swift index 9f38f9352..361fa6458 100644 --- a/CommonUI/Package.swift +++ b/CommonUI/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "CommonUI", - platforms: [.iOS(.v18), .watchOS(.v11), .macOS(.v14)], + platforms: [.iOS(.v18), .watchOS(.v11), .macOS(.v15)], products: [ // Products define the executables and libraries a package produces, making them visible to other packages. diff --git a/OpenHABCore/Package.swift b/OpenHABCore/Package.swift index b541b5638..dadc34781 100644 --- a/OpenHABCore/Package.swift +++ b/OpenHABCore/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "OpenHABCore", - platforms: [.iOS(.v18), .watchOS(.v11), .macOS(.v14)], + platforms: [.iOS(.v18), .watchOS(.v11), .macOS(.v15)], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( From 208e4b7dd3b2b65fa18b792f08431645c1756858 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Sun, 14 Jun 2026 16:48:20 +0200 Subject: [PATCH 20/91] chore: add shared schemes for openHABWidgetExtension and OpenHABWatchComplicationsExtension Signed-off-by: Tim Mueller-Seydlitz --- ...penHABWatchComplicationsExtension.xcscheme | 95 +++++++++++++++++++ .../xcschemes/openHABWidgetExtension.xcscheme | 95 +++++++++++++++++++ 2 files changed, 190 insertions(+) create mode 100644 openHAB.xcodeproj/xcshareddata/xcschemes/OpenHABWatchComplicationsExtension.xcscheme create mode 100644 openHAB.xcodeproj/xcshareddata/xcschemes/openHABWidgetExtension.xcscheme diff --git a/openHAB.xcodeproj/xcshareddata/xcschemes/OpenHABWatchComplicationsExtension.xcscheme b/openHAB.xcodeproj/xcshareddata/xcschemes/OpenHABWatchComplicationsExtension.xcscheme new file mode 100644 index 000000000..f1a8693e6 --- /dev/null +++ b/openHAB.xcodeproj/xcshareddata/xcschemes/OpenHABWatchComplicationsExtension.xcscheme @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openHAB.xcodeproj/xcshareddata/xcschemes/openHABWidgetExtension.xcscheme b/openHAB.xcodeproj/xcshareddata/xcschemes/openHABWidgetExtension.xcscheme new file mode 100644 index 000000000..77dab51ab --- /dev/null +++ b/openHAB.xcodeproj/xcshareddata/xcschemes/openHABWidgetExtension.xcscheme @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From c87f69d3f90219232c8ced03751b3dfba83a1a16 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Sun, 14 Jun 2026 16:58:04 +0200 Subject: [PATCH 21/91] fix: restore project.pbxproj corrupted by bad 3-way merge of pbxproj The previous merge of develop into this branch produced a corrupted pbxproj: the auto-merge stripped 16 PBXFileReference definitions and 29 XCSwiftPackageProductDependency entries while keeping the build phase entries that reference them, and introduced duplicate AppIntents file-system-synchronized groups (both branches added one independently with different GUIDs). Xcode could not parse the result and showed only the OpenHABCore/CommonUI package schemes. Restored the pre-merge pbxproj from this branch (cb183ec8), which already contained all develop's file-system-synchronized groups and the new widget/complications targets. Signed-off-by: Tim Mueller-Seydlitz --- openHAB.xcodeproj/project.pbxproj | 194 ++++++++++++++++++++++++------ 1 file changed, 156 insertions(+), 38 deletions(-) diff --git a/openHAB.xcodeproj/project.pbxproj b/openHAB.xcodeproj/project.pbxproj index 8286a98b1..569c453a9 100644 --- a/openHAB.xcodeproj/project.pbxproj +++ b/openHAB.xcodeproj/project.pbxproj @@ -26,6 +26,9 @@ 6557AF922C039D140094D0C8 /* FirebaseMessaging in Frameworks */ = {isa = PBXBuildFile; productRef = 6557AF912C039D140094D0C8 /* FirebaseMessaging */; }; 657144552C1E438700C8A1F3 /* NotificationService.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 6571444E2C1E438700C8A1F3 /* NotificationService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 657144962C30A16700C8A1F3 /* OpenHABCore in Frameworks */ = {isa = PBXBuildFile; productRef = 657144952C30A16700C8A1F3 /* OpenHABCore */; }; + 73F094A3C17641738D6215CD /* SetSwitchItemIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD5E85B2F02C272003215C0 /* SetSwitchItemIntent.swift */; }; + 772F724744CC8D15A673C246 /* SetActiveHomeIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F77A157AAF1DD5AB45D6BA2 /* SetActiveHomeIntent.swift */; }; + 791AFA47B71D4B6995369F3A /* GetItemStateIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3512F0307A40082479C /* GetItemStateIntent.swift */; }; 934E592528F16EBA00162004 /* OpenHABCore in Frameworks */ = {isa = PBXBuildFile; productRef = 934E592428F16EBA00162004 /* OpenHABCore */; }; 934E592728F16EBA00162004 /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 934E592628F16EBA00162004 /* Kingfisher */; }; 934E592928F16EBA00162004 /* DeviceKit in Frameworks */ = {isa = PBXBuildFile; productRef = 934E592828F16EBA00162004 /* DeviceKit */; }; @@ -54,6 +57,21 @@ DA10161B2DC7BAE500552D14 /* SFSafeSymbols in Frameworks */ = {isa = PBXBuildFile; productRef = DA10161A2DC7BAE500552D14 /* SFSafeSymbols */; }; DA28C362225241DE00AB409C /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA28C361225241DE00AB409C /* WebKit.framework */; settings = {ATTRIBUTES = (Required, ); }; }; DA2C4FD52B4F573300D1C533 /* SDWebImageSVGCoder in Frameworks */ = {isa = PBXBuildFile; productRef = DA2C4FD42B4F573300D1C533 /* SDWebImageSVGCoder */; }; + DA448E852EF435B400F0893C /* Home.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA448E772EF435B400F0893C /* Home.swift */; }; + DA4BF3522F0307A40082479C /* GetItemStateIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3512F0307A40082479C /* GetItemStateIntent.swift */; }; + DA4BF3562F03095D0082479C /* SetColorValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3542F03095D0082479C /* SetColorValueIntent.swift */; }; + DA4BF3572F03095D0082479C /* ContactStateIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3552F03095D0082479C /* ContactStateIntent.swift */; }; + DA4BF3592F03098B0082479C /* SetDimmerRollerValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3582F03098B0082479C /* SetDimmerRollerValueIntent.swift */; }; + DA4BF35B2F0309A60082479C /* SetNumberValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF35A2F0309A60082479C /* SetNumberValueIntent.swift */; }; + DA4BF35D2F0309B10082479C /* SetStringValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF35C2F0309B10082479C /* SetStringValueIntent.swift */; }; + DA4BF4032F06F5950082479C /* SwitchAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4022F06F5950082479C /* SwitchAction.swift */; }; + DA4BF4052F06F5CA0082479C /* ContactState.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4042F06F5CA0082479C /* ContactState.swift */; }; + DA4BF4072F0800010082479C /* PlayerAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4062F0800010082479C /* PlayerAction.swift */; }; + DA4BF4092F0800020082479C /* SetPlayerValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4082F0800020082479C /* SetPlayerValueIntent.swift */; }; + DA4BF40B2F0800030082479C /* SetDateTimeValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF40A2F0800030082479C /* SetDateTimeValueIntent.swift */; }; + DA4BF40D2F0800040082479C /* SetLocationValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF40C2F0800040082479C /* SetLocationValueIntent.swift */; }; + DA4BF4342F0705580082479C /* ItemEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4332F0705580082479C /* ItemEntity.swift */; }; + DA4BF4362F07056F0082479C /* ItemEntityQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4352F07056F0082479C /* ItemEntityQuery.swift */; }; DA4D4DB5233F9ACB00B37E37 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = DA4D4DB4233F9ACB00B37E37 /* README.md */; }; DA817E7A234BF39B00C91824 /* CHANGELOG.md in Resources */ = {isa = PBXBuildFile; fileRef = DA817E79234BF39B00C91824 /* CHANGELOG.md */; }; DA9A7EFD2D668D5900824156 /* SFSafeSymbols in Frameworks */ = {isa = PBXBuildFile; productRef = DA9A7EFC2D668D5900824156 /* SFSafeSymbols */; }; @@ -61,8 +79,11 @@ DABB5E332D98972F009A4B8A /* SDWebImageSVGCoder in Frameworks */ = {isa = PBXBuildFile; productRef = DABB5E322D98972F009A4B8A /* SDWebImageSVGCoder */; }; DAC949FA2E219F0D007E67B7 /* CommonUI in Frameworks */ = {isa = PBXBuildFile; productRef = DAC949F92E219F0D007E67B7 /* CommonUI */; }; DAC949FC2E219F30007E67B7 /* CommonUI in Frameworks */ = {isa = PBXBuildFile; productRef = DAC949FB2E219F30007E67B7 /* CommonUI */; }; + DAD5E85C2F02C272003215C0 /* SetSwitchItemIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD5E85B2F02C272003215C0 /* SetSwitchItemIntent.swift */; }; + DAD5E85E2F02C3C6003215C0 /* ItemIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD5E85D2F02C3BC003215C0 /* ItemIdentifier.swift */; }; DAEFAA7F2F63536A00AC300B /* OpenHABCore in Frameworks */ = {isa = PBXBuildFile; productRef = DAEFAA7E2F63536A00AC300B /* OpenHABCore */; }; DAEFAA812F63537600AC300B /* SDWebImageSVGCoder in Frameworks */ = {isa = PBXBuildFile; productRef = DAEFAA802F63537600AC300B /* SDWebImageSVGCoder */; }; + DAF58C7B2EF6E99500483AFD /* SwitchItemEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAF58C7A2EF6E99500483AFD /* SwitchItemEntity.swift */; }; DAFF80982E4F47830084513E /* SDWebImage in Frameworks */ = {isa = PBXBuildFile; productRef = DAFF80972E4F47830084513E /* SDWebImage */; }; DFB2622B18830A3600D3244D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DFB2622A18830A3600D3244D /* Foundation.framework */; }; DFB2622D18830A3600D3244D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DFB2622C18830A3600D3244D /* CoreGraphics.framework */; }; @@ -209,7 +230,19 @@ 3A5DAEE32F1FD8D300CFA482 /* OpenHABWatchComplicationsExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = OpenHABWatchComplicationsExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 6557AF8E2C0241C10094D0C8 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 6571444E2C1E438700C8A1F3 /* NotificationService.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NotificationService.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 657144502C1E438700C8A1F3 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; }; + 657144522C1E438700C8A1F3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 657144972C30A3E300C8A1F3 /* NotificationService.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NotificationService.entitlements; sourceTree = ""; }; 933D7F0422E7015000621A03 /* openHABUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = openHABUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 933D7F0622E7015000621A03 /* OpenHABUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenHABUITests.swift; sourceTree = ""; }; + 933D7F0822E7015100621A03 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 933D7F0E22E7030600621A03 /* SnapshotHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SnapshotHelper.swift; path = fastlane/SnapshotHelper.swift; sourceTree = SOURCE_ROOT; }; + 93685A792ADE755C0077A9A6 /* openHABTests.xctestplan */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = openHABTests.xctestplan; sourceTree = ""; }; + 93685A7B2ADE755C0077A9A6 /* openHABTestsSwift.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = openHABTestsSwift.xctestplan; sourceTree = ""; }; + 938BF89524EFBC5400E6B52F /* LocalizationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizationTests.swift; sourceTree = ""; }; + D86D8BF295C448039B2B85EB /* SelectionRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectionRow.swift; sourceTree = ""; }; + DA0749DD23E0B5950057FA83 /* ColorPickerRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorPickerRow.swift; sourceTree = ""; }; + DA0749DF23E0BF510057FA83 /* ColorSelection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorSelection.swift; sourceTree = ""; }; DA0775152346705D0086C685 /* openHABWatch.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = openHABWatch.app; sourceTree = BUILT_PRODUCTS_DIR; }; DA07751A2346705F0086C685 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; DA07751C2346705F0086C685 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -222,17 +255,79 @@ DA077649234683BC0086C685 /* SwitchRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitchRow.swift; sourceTree = ""; }; DA0776EF234788010086C685 /* UserData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserData.swift; sourceTree = ""; }; DA0DA9E12E0C9B74000C5D0A /* BuildTools */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = BuildTools; sourceTree = ""; }; + DA0F37CF23D4ACC7007EAB48 /* SliderRow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SliderRow.swift; sourceTree = ""; }; + DA15BFBC23C6726400BD8ADA /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = ""; }; + DA19E25A22FD801D002F8F2F /* OpenHABGeneralTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenHABGeneralTests.swift; sourceTree = ""; }; + DA2740FF2EA62F1F002FE576 /* SitemapPageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SitemapPageView.swift; sourceTree = ""; }; + DA2741012EA62FA3002FE576 /* SegmentSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SegmentSelectionView.swift; sourceTree = ""; }; DA28C361225241DE00AB409C /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; }; + DA2D2F892F3943A800EC605A /* WidgetRowViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetRowViewModel.swift; sourceTree = ""; }; DA2DC22F21F2736C00830730 /* openHABTestsSwift.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = openHABTestsSwift.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + DA2DC23321F2736C00830730 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + DA2E0AA323DC96E9009B0A99 /* ImageWithAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageWithAction.swift; sourceTree = ""; }; + DA2E0B0D23DCC152009B0A99 /* MapView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MapView.swift; sourceTree = ""; }; + DA2E0B0F23DCC439009B0A99 /* MapViewRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapViewRow.swift; sourceTree = ""; }; + DA32D1B32C8C98C40018D974 /* IconWithAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconWithAction.swift; sourceTree = ""; }; + DA448E772EF435B400F0893C /* Home.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Home.swift; sourceTree = ""; }; + DA4BF3512F0307A40082479C /* GetItemStateIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetItemStateIntent.swift; sourceTree = ""; }; + DA4BF3542F03095D0082479C /* SetColorValueIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetColorValueIntent.swift; sourceTree = ""; }; + DA4BF3552F03095D0082479C /* ContactStateIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactStateIntent.swift; sourceTree = ""; }; + DA4BF3582F03098B0082479C /* SetDimmerRollerValueIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetDimmerRollerValueIntent.swift; sourceTree = ""; }; + DA4BF35A2F0309A60082479C /* SetNumberValueIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetNumberValueIntent.swift; sourceTree = ""; }; + DA4BF35C2F0309B10082479C /* SetStringValueIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetStringValueIntent.swift; sourceTree = ""; }; + DA4BF4022F06F5950082479C /* SwitchAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitchAction.swift; sourceTree = ""; }; + DA4BF4042F06F5CA0082479C /* ContactState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactState.swift; sourceTree = ""; }; + DA4BF4062F0800010082479C /* PlayerAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerAction.swift; sourceTree = ""; }; + DA4BF4082F0800020082479C /* SetPlayerValueIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetPlayerValueIntent.swift; sourceTree = ""; }; + DA4BF40A2F0800030082479C /* SetDateTimeValueIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetDateTimeValueIntent.swift; sourceTree = ""; }; + DA4BF40C2F0800040082479C /* SetLocationValueIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetLocationValueIntent.swift; sourceTree = ""; }; + DA4BF4332F0705580082479C /* ItemEntity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemEntity.swift; sourceTree = ""; }; + DA4BF4352F07056F0082479C /* ItemEntityQuery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemEntityQuery.swift; sourceTree = ""; }; DA4D4DB4233F9ACB00B37E37 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; DA4D4E0E2340A00200B37E37 /* Changes.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = Changes.md; sourceTree = ""; }; + DA65871E236F83CD007E2E7F /* UserDefaultsExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsExtension.swift; sourceTree = ""; }; + DA72E1B5236DEA0900B8EF3A /* AppMessageService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppMessageService.swift; sourceTree = ""; }; DA817E79234BF39B00C91824 /* CHANGELOG.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = ""; }; DA83C81D2F48AF7600CDACED /* Version.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Version.xcconfig; sourceTree = ""; }; + DA8B14B52F3A0DFF007753FD /* WidgetRowFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetRowFactory.swift; sourceTree = ""; }; + DA8B14B72F3A11F0007753FD /* PreviewWidgetFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewWidgetFactory.swift; sourceTree = ""; }; + DA8B14B92F3A373A007753FD /* PreviewNavigationContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewNavigationContainer.swift; sourceTree = ""; }; + DA8B14BB2F3A3CB5007753FD /* WatchTypography.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchTypography.swift; sourceTree = ""; }; DA8B15512F3BB74B007753FD /* openHABWatchTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = openHABWatchTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + DA9641592F292EE200CEC181 /* BonjourDiscoveryViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BonjourDiscoveryViewModelTests.swift; sourceTree = ""; }; + DA96415B2F292F0600CEC181 /* OpenHABEndPoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenHABEndPoint.swift; sourceTree = ""; }; + DA9721C224E29A8F0092CCFD /* UserDefaultsBacked.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsBacked.swift; sourceTree = ""; }; DABB4CD22F45EFD100111AF3 /* openHABWatch.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; name = openHABWatch.xctestplan; path = TestPlans/openHABWatch.xctestplan; sourceTree = ""; }; + DABB4CDD2F4746B100111AF3 /* SitemapRowInputMapperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SitemapRowInputMapperTests.swift; sourceTree = ""; }; + DABB4CF12F4790AD00111AF3 /* RowLayoutPolicyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RowLayoutPolicyTests.swift; sourceTree = ""; }; + DABB4CF32F47BC5100111AF3 /* SliderOverrideSyncTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SliderOverrideSyncTests.swift; sourceTree = ""; }; + DAC6608B236F6F4200F4501E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + DAC6608C236F771600F4501E /* PreferencesSwiftUIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesSwiftUIView.swift; sourceTree = ""; }; + DAC9AF4624F9669F006DAE93 /* OpenHABWidgetExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenHABWidgetExtension.swift; sourceTree = ""; }; + DAC9AF4824F966FA006DAE93 /* LazyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LazyView.swift; sourceTree = ""; }; DACC0F7B2E883AC700B62043 /* AGENTS.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = AGENTS.md; sourceTree = ""; }; DAD0856C2AE4782B001D36BE /* openHABWatchSwiftUI Watch AppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "openHABWatchSwiftUI Watch AppTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; DAD085762AE4782E001D36BE /* openHABWatchSwiftUI Watch AppUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "openHABWatchSwiftUI Watch AppUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + DAD0857A2AE4782F001D36BE /* OpenHABWatchUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenHABWatchUITests.swift; sourceTree = ""; }; + DAD0857C2AE4782F001D36BE /* OpenHABWatchLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenHABWatchLaunchTests.swift; sourceTree = ""; }; + DAD5E85B2F02C272003215C0 /* SetSwitchItemIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetSwitchItemIntent.swift; sourceTree = ""; }; + DAD5E85D2F02C3BC003215C0 /* ItemIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemIdentifier.swift; sourceTree = ""; }; + DAF231D127BB6EEA00AB916C /* OpenHABSVGTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenHABSVGTests.swift; sourceTree = ""; }; + DAF231D527BB702400AB916C /* valid_xmlns.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = valid_xmlns.svg; sourceTree = ""; }; + DAF231D627BB702500AB916C /* invalid_xmlns.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = invalid_xmlns.svg; sourceTree = ""; }; + DAF231DA27BB828000AB916C /* pantryUseTagPoints2NonExistentElement.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = pantryUseTagPoints2NonExistentElement.svg; sourceTree = ""; }; + DAF231E227BBD1A000AB916C /* embeddedpng_valid.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = embeddedpng_valid.svg; sourceTree = ""; }; + DAF4578123D630C70018B495 /* WatchIconView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchIconView.swift; sourceTree = ""; }; + DAF4578823D79AA50018B495 /* DetailTextLabelView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailTextLabelView.swift; sourceTree = ""; }; + DAF4579F23DA3E1C0018B495 /* SegmentRow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SegmentRow.swift; sourceTree = ""; }; + DAF457A123DB6C640018B495 /* RollershutterRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RollershutterRow.swift; sourceTree = ""; }; + DAF457A523DB9CE00018B495 /* SetpointRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetpointRow.swift; sourceTree = ""; }; + DAF457A823DBA4990018B495 /* FrameRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FrameRow.swift; sourceTree = ""; }; + DAF4581523DC483F0018B495 /* GenericRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenericRow.swift; sourceTree = ""; }; + DAF4581723DC4A050018B495 /* ImageRow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageRow.swift; sourceTree = ""; }; + DAF4581D23DC60020018B495 /* ImageRawRow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageRawRow.swift; sourceTree = ""; }; + DAF58C7A2EF6E99500483AFD /* SwitchItemEntity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitchItemEntity.swift; sourceTree = ""; }; + DAFAF36E2F8892A3003E60EF /* AppIntentsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIntentsTests.swift; sourceTree = ""; }; DAFD2FE62E0D96700059A1EB /* OsLogRewriter */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = OsLogRewriter; sourceTree = ""; }; DFB2622718830A3600D3244D /* openHAB.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = openHAB.app; sourceTree = BUILT_PRODUCTS_DIR; }; DFB2622A18830A3600D3244D /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; @@ -240,6 +335,7 @@ DFB2622E18830A3600D3244D /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; DFB2624C18830A3600D3244D /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; DFE10413197415F900D94943 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; + E1AA00042F5D000100000001 /* ScreenSaverLayoutCalculatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenSaverLayoutCalculatorTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ @@ -315,40 +411,9 @@ isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( Info.plist, - DADB537C2FDECC790058522B /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { - isa = PBXFileSystemSynchronizedBuildFileExceptionSet; - membershipExceptions = ( - iOS16/GetItemState.swift, - iOS16/SetColorValue.swift, - iOS16/SetContactStateValue.swift, - iOS16/SetDateTimeValue.swift, - iOS16/SetDimmerRollerValue.swift, - iOS16/SetLocationValue.swift, - iOS16/SetNumberValue.swift, - iOS16/SetPlayerValue.swift, - iOS16/SetStringValue.swift, - iOS16/SetSwitchState.swift, ); target = 6116C7DDD25EE245FE191A47 /* openHABWidgetExtension */; }; - DADB537D2FDECC790058522B /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { - isa = PBXFileSystemSynchronizedBuildFileExceptionSet; - membershipExceptions = ( - Intents/SetActiveHomeIntent.swift, - iOS16/ActionMapper.swift, - iOS16/GetItemState.swift, - iOS16/SetColorValue.swift, - iOS16/SetContactStateValue.swift, - iOS16/SetDateTimeValue.swift, - iOS16/SetDimmerRollerValue.swift, - iOS16/SetLocationValue.swift, - iOS16/SetNumberValue.swift, - iOS16/SetPlayerValue.swift, - iOS16/SetStringValue.swift, - iOS16/SetSwitchState.swift, - ); - target = DA0775142346705D0086C685 /* openHABWatch */; - }; /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ @@ -364,7 +429,6 @@ 3A5DAEE62F1FD8D300CFA482 /* OpenHABWatchComplications */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (3A5DAEF32F1FD8D300CFA482 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = OpenHABWatchComplications; sourceTree = ""; }; D41D0290EB2365F7E247EC0A /* openHABWidget */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (4D140F69148E28C6B524B346 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = openHABWidget; sourceTree = ""; }; DA8B15522F3BB74B007753FD /* openHABWatchTests */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = openHABWatchTests; sourceTree = ""; }; - DADB533F2FDECC780058522B /* AppIntents */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (DADB537C2FDECC790058522B /* PBXFileSystemSynchronizedBuildFileExceptionSet */, DADB537D2FDECC790058522B /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = AppIntents; sourceTree = ""; }; /* End PBXFileSystemSynchronizedRootGroup section */ /* Begin PBXFrameworksBuildPhase section */ @@ -480,6 +544,23 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 1224F7C8228A8EC600750965 /* Domain */ = { + isa = PBXGroup; + children = ( + DA0776EF234788010086C685 /* UserData.swift */, + ); + path = Domain; + sourceTree = ""; + }; + 1224F7C9228A8ED100750965 /* External */ = { + isa = PBXGroup; + children = ( + DA72E1B5236DEA0900B8EF3A /* AppMessageService.swift */, + ); + name = External; + path = openHABWatch/External; + sourceTree = SOURCE_ROOT; + }; 2F2150892F75C2FC001BA057 /* Watch */ = { isa = PBXGroup; children = ( @@ -712,16 +793,18 @@ children = ( DA448E7E2EF435B400F0893C /* AppIntents */, D41D0290EB2365F7E247EC0A /* openHABWidget */, - DFB2621E18830A3600D3244D = { - isa = PBXGroup; - children = ( - DADB533F2FDECC780058522B /* AppIntents */, DABB4CD22F45EFD100111AF3 /* openHABWatch.xctestplan */, 6557AF8E2C0241C10094D0C8 /* PrivacyInfo.xcprivacy */, DA4D4DB4233F9ACB00B37E37 /* README.md */, DA4D4E0E2340A00200B37E37 /* Changes.md */, DA817E79234BF39B00C91824 /* CHANGELOG.md */, DACC0F7B2E883AC700B62043 /* AGENTS.md */, + 93685A782ADE755C0077A9A6 /* TestPlans */, + DA2DC23021F2736C00830730 /* openHABTestsSwift */, + 933D7F0522E7015000621A03 /* openHABUITests */, + DA0775162346705D0086C685 /* openHABWatch */, + DAD085792AE4782F001D36BE /* openHABWatchSwiftUI Watch AppUITests */, + 6571444F2C1E438700C8A1F3 /* NotificationService */, 2FD4E52B2F670BA500EBB340 /* openHAB */, 2FD4E2F42F66FA2900EBB340 /* TestPlans */, 2FD4E2E02F66F9FE00EBB340 /* openHABTestsSwift */, @@ -880,7 +963,6 @@ ); fileSystemSynchronizedGroups = ( 2FD4E29C2F66F9A500EBB340 /* openHABWatch */, - DADB533F2FDECC780058522B /* AppIntents */, ); name = openHABWatch; packageProductDependencies = ( @@ -997,7 +1079,6 @@ ); fileSystemSynchronizedGroups = ( 2FD4E52B2F670BA500EBB340 /* openHAB */, - DADB533F2FDECC780058522B /* AppIntents */, ); name = openHAB; packageProductDependencies = ( @@ -1251,6 +1332,24 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9F54768418A14674AD9E2FCA /* ContactState.swift in Sources */, + 3D3B2898C51F48BAA31210E4 /* ContactStateIntent.swift in Sources */, + 791AFA47B71D4B6995369F3A /* GetItemStateIntent.swift in Sources */, + 5B292637B6E141DB98BCAA26 /* Home.swift in Sources */, + B2FC662F25B2461887BB67DA /* ItemEntity.swift in Sources */, + 3F087E4025E845FFBCBE45E3 /* ItemEntityQuery.swift in Sources */, + C9DE79C32AF24FFEA0280115 /* ItemIdentifier.swift in Sources */, + 0648F50178784F149905E7CF /* PlayerAction.swift in Sources */, + 1E4E7DB36BAC4804AD5E58E5 /* SetColorValueIntent.swift in Sources */, + BF0EB6DE70934966B1DC5479 /* SetDateTimeValueIntent.swift in Sources */, + E120F4A14B9942449F25E788 /* SetDimmerRollerValueIntent.swift in Sources */, + CE7A58C0ED3A4A1ABCB20337 /* SetLocationValueIntent.swift in Sources */, + 61A606E5E6AE404584B0B1A0 /* SetNumberValueIntent.swift in Sources */, + 598208789AC44F1D91DD5B17 /* SetPlayerValueIntent.swift in Sources */, + 1C1CA8C8C8424BAF8018F9BE /* SetStringValueIntent.swift in Sources */, + 73F094A3C17641738D6215CD /* SetSwitchItemIntent.swift in Sources */, + CF650A22059A4A00823EE656 /* SwitchAction.swift in Sources */, + 970584BAA2884C8287297BF6 /* SwitchItemEntity.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1286,6 +1385,25 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + DA448E852EF435B400F0893C /* Home.swift in Sources */, + DA4BF3522F0307A40082479C /* GetItemStateIntent.swift in Sources */, + DA4BF3562F03095D0082479C /* SetColorValueIntent.swift in Sources */, + DA4BF3572F03095D0082479C /* ContactStateIntent.swift in Sources */, + DA4BF3592F03098B0082479C /* SetDimmerRollerValueIntent.swift in Sources */, + DA4BF35B2F0309A60082479C /* SetNumberValueIntent.swift in Sources */, + DA4BF35D2F0309B10082479C /* SetStringValueIntent.swift in Sources */, + 772F724744CC8D15A673C246 /* SetActiveHomeIntent.swift in Sources */, + DA4BF4032F06F5950082479C /* SwitchAction.swift in Sources */, + DA4BF4052F06F5CA0082479C /* ContactState.swift in Sources */, + DA4BF4072F0800010082479C /* PlayerAction.swift in Sources */, + DA4BF4092F0800020082479C /* SetPlayerValueIntent.swift in Sources */, + DA4BF40B2F0800030082479C /* SetDateTimeValueIntent.swift in Sources */, + DA4BF40D2F0800040082479C /* SetLocationValueIntent.swift in Sources */, + DA4BF4342F0705580082479C /* ItemEntity.swift in Sources */, + DA4BF4362F07056F0082479C /* ItemEntityQuery.swift in Sources */, + DAD5E85C2F02C272003215C0 /* SetSwitchItemIntent.swift in Sources */, + DAD5E85E2F02C3C6003215C0 /* ItemIdentifier.swift in Sources */, + DAF58C7B2EF6E99500483AFD /* SwitchItemEntity.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; From 07e414596a2a354e21fdc082bff6d376be485017 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Sun, 14 Jun 2026 17:10:42 +0200 Subject: [PATCH 22/91] chore: update openHABWidgetExtension scheme with askForAppToLaunch Signed-off-by: Tim Mueller-Seydlitz --- .../xcshareddata/xcschemes/openHABWidgetExtension.xcscheme | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openHAB.xcodeproj/xcshareddata/xcschemes/openHABWidgetExtension.xcscheme b/openHAB.xcodeproj/xcshareddata/xcschemes/openHABWidgetExtension.xcscheme index 77dab51ab..e06d83fe3 100644 --- a/openHAB.xcodeproj/xcshareddata/xcschemes/openHABWidgetExtension.xcscheme +++ b/openHAB.xcodeproj/xcshareddata/xcschemes/openHABWidgetExtension.xcscheme @@ -50,6 +50,7 @@ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" launchStyle = "0" + askForAppToLaunch = "Yes" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" @@ -73,6 +74,7 @@ savedToolIdentifier = "" useCustomWorkingDirectory = "NO" debugDocumentVersioning = "YES" + askForAppToLaunch = "Yes" launchAutomaticallySubstyle = "2"> From 62fcf0ce753955e2a4d7042f06b6a81359b62e88 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Wed, 17 Jun 2026 13:07:09 +0200 Subject: [PATCH 23/91] l10n: translate 2 new widget keys into all 9 languages Adds translations for 'Dimmer or Roller Item' and 'Toggle Switch' across de, en, es, fi, fr, it, nb, nl and ru. Signed-off-by: Tim Mueller-Seydlitz --- .../Supporting Files/Localizable.xcstrings | 116 +++++++++++++++++- 1 file changed, 114 insertions(+), 2 deletions(-) diff --git a/openHAB/Supporting Files/Localizable.xcstrings b/openHAB/Supporting Files/Localizable.xcstrings index c24b1e18a..4c776abe2 100644 --- a/openHAB/Supporting Files/Localizable.xcstrings +++ b/openHAB/Supporting Files/Localizable.xcstrings @@ -17850,11 +17850,123 @@ }, "Dimmer or Roller Item": { "comment": "Display name for a dimmer or roller item type.", - "isCommentAutoGenerated": true + "isCommentAutoGenerated": true, + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Dimmer or Roller Item" + } + }, + "de": { + "stringUnit": { + "state": "translated", + "value": "Dimmer- oder Rolladen-Item" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Elemento regulador o persiana" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Himmennin- tai rullakaihdinelementti" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Élément variateur ou volet" + } + }, + "it": { + "stringUnit": { + "state": "translated", + "value": "Elemento dimmer o tapparella" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Dimmer- eller rullelukkelement" + } + }, + "nl": { + "stringUnit": { + "state": "translated", + "value": "Dimmer- of rolluik-item" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Элемент диммера или ролеты" + } + } + } }, "Toggle Switch": { "comment": "Shortcut title for toggle switch action.", - "isCommentAutoGenerated": true + "isCommentAutoGenerated": true, + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Toggle Switch" + } + }, + "de": { + "stringUnit": { + "state": "translated", + "value": "Schalter umschalten" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Conmutar interruptor" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Vaihda kytkimen tila" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Basculer l'interrupteur" + } + }, + "it": { + "stringUnit": { + "state": "translated", + "value": "Attiva/Disattiva interruttore" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Bytt bryterposisjon" + } + }, + "nl": { + "stringUnit": { + "state": "translated", + "value": "Schakelaar omschakelen" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Переключить выключатель" + } + } + } } }, "version": "1.1" From c679848f49ff1d2c5fd245a94c55e5b2c8679752 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Wed, 17 Jun 2026 16:08:33 +0200 Subject: [PATCH 24/91] l10n: add Spanish, Finnish, Norwegian, and Russian translations Signed-off-by: Tim Mueller-Seydlitz --- .../Supporting Files/Localizable.xcstrings | 3462 ++++++++++------- 1 file changed, 2079 insertions(+), 1383 deletions(-) diff --git a/openHAB/Supporting Files/Localizable.xcstrings b/openHAB/Supporting Files/Localizable.xcstrings index 4c776abe2..03c927a2b 100644 --- a/openHAB/Supporting Files/Localizable.xcstrings +++ b/openHAB/Supporting Files/Localizable.xcstrings @@ -83,8 +83,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": " - " } }, "fr": { @@ -101,8 +101,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": " - " } }, "nl": { @@ -113,8 +113,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": " - " } } } @@ -153,6 +153,30 @@ "state": "translated", "value": "%@" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "%@" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "%@" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "%@" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "%@" + } } } }, @@ -173,14 +197,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "/overview/" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "/overview/" } }, "fr": { @@ -197,8 +221,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "/overview/" } }, "nl": { @@ -209,8 +233,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "/overview/" } } } @@ -240,6 +264,30 @@ "state": "translated", "value": "%@" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "%@" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "%@" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "%@" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "%@" + } } }, "comment": "A label that shows the percentage of brightness. The value is rounded to the nearest whole number." @@ -261,14 +309,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ajustes de %@" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "%@-asetukset" } }, "fr": { @@ -285,8 +333,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "%@-innstillinger" } }, "nl": { @@ -297,8 +345,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Настройки %@" } } } @@ -320,14 +368,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Valor de %@" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "%@-arvo" } }, "fr": { @@ -344,8 +392,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "%@-verdi" } }, "nl": { @@ -356,8 +404,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Значение %@" } } } @@ -379,14 +427,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "%lld" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "%lld" } }, "fr": { @@ -403,8 +451,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "%lld" } }, "nl": { @@ -415,8 +463,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "%lld" } } } @@ -438,14 +486,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "%lldK" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "%lldK" } }, "fr": { @@ -462,8 +510,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "%lldK" } }, "nl": { @@ -474,8 +522,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "%lldK" } } } @@ -497,14 +545,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Reloj de 24 horas" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "24 tunnin kello" } }, "fr": { @@ -521,8 +569,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "24-timers klokke" } }, "nl": { @@ -533,8 +581,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "24-часовые часы" } } } @@ -614,14 +662,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Certificados de servidor aceptados" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Hyväksytyt palvelinvarmenteet" } }, "fr": { @@ -638,8 +686,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Godkjente serversertifikater" } }, "nl": { @@ -650,8 +698,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Принятые сертификаты сервера" } } } @@ -679,8 +727,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Toiminto" } }, "fr": { @@ -709,8 +757,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Действие" } } } @@ -849,14 +897,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Añadido: %@" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Lisätty: %@" } }, "fr": { @@ -873,8 +921,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Lagt til: %@" } }, "nl": { @@ -885,8 +933,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Добавлено: %@" } } } @@ -914,8 +962,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aina" } }, "fr": { @@ -932,8 +980,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Alltid" } }, "nl": { @@ -944,8 +992,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Всегда" } } } @@ -967,14 +1015,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Permitir WebRTC siempre" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Salli WebRTC aina" } }, "fr": { @@ -991,8 +1039,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Tillat alltid WebRTC" } }, "nl": { @@ -1003,8 +1051,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Всегда разрешать WebRTC" } } } @@ -1026,14 +1074,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Enviar credenciales siempre" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Lähetä tunnistetiedot aina" } }, "fr": { @@ -1050,8 +1098,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Send alltid legitimasjon" } }, "nl": { @@ -1062,8 +1110,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Всегда отправлять учётные данные" } } } @@ -1203,14 +1251,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Animación" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Animaatio" } }, "fr": { @@ -1227,8 +1275,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Animasjon" } }, "nl": { @@ -1239,8 +1287,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Анимация" } } } @@ -1262,14 +1310,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Versión de la aplicación" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Sovelluksen versio" } }, "fr": { @@ -1286,8 +1334,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "App-versjon" } }, "nl": { @@ -1298,8 +1346,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Версия приложения" } } } @@ -1386,8 +1434,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ulkoasu" } }, "fr": { @@ -1404,8 +1452,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Utseende" } }, "nl": { @@ -1416,8 +1464,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Внешний вид" } } } @@ -1503,8 +1551,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Takaisin" } }, "fr": { @@ -1521,8 +1569,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Tilbake" } }, "nl": { @@ -1533,8 +1581,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Назад" } } } @@ -1620,8 +1668,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kirkkaus" } }, "fr": { @@ -1638,8 +1686,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Lysstyrke" } }, "nl": { @@ -1650,8 +1698,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Яркость" } } } @@ -1795,8 +1843,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Peruuta" } }, "fr": { @@ -1813,8 +1861,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Avbryt" } }, "nl": { @@ -1825,8 +1873,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Отмена" } } } @@ -1848,14 +1896,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Se produjo una cancelación" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Peruutus tapahtui" } }, "fr": { @@ -1872,8 +1920,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Avbrudd inntraff" } }, "nl": { @@ -1884,8 +1932,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Произошла отмена" } } } @@ -1906,14 +1954,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Luz de vela" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kynttilänvalo" } }, "fr": { @@ -1930,8 +1978,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Stearinlys" } }, "nl": { @@ -1942,8 +1990,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Свет свечи" } } } @@ -1965,14 +2013,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "No se puede conectar al servidor. ¿Está en línea?" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Yhteyden muodostaminen palvelimeen epäonnistui. Onko se verkossa?" } }, "fr": { @@ -1989,8 +2037,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kan ikke koble til serveren. Er den online?" } }, "nl": { @@ -2001,8 +2049,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Не удаётся подключиться к серверу. Он в сети?" } } } @@ -2024,14 +2072,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "No se puede encontrar el servidor. ¿Es correcta la URL?" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Palvelinta ei löydy. Onko URL oikein?" } }, "fr": { @@ -2048,8 +2096,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kan ikke finne serveren. Er URL-en riktig?" } }, "nl": { @@ -2060,8 +2108,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Не удаётся найти сервер. URL верный?" } } } @@ -2316,14 +2364,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Comprobar y limpiar caché de imágenes" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Tarkista ja tyhjennä kuvavälimuisti" } }, "fr": { @@ -2340,8 +2388,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Sjekk og tøm bildebuffer" } }, "nl": { @@ -2352,8 +2400,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Проверить и очистить кэш изображений" } } } @@ -2385,6 +2433,30 @@ "state": "translated", "value": "Scegli colore" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Elegir color" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Valitse väri" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Velg farge" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Выбрать цвет" + } } } }, @@ -2411,8 +2483,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Tyhjennä" } }, "fr": { @@ -2429,8 +2501,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Tøm" } }, "nl": { @@ -2441,8 +2513,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Очистить" } } } @@ -2464,14 +2536,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Limpiar caché web" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Tyhjennä verkkovälimuisti" } }, "fr": { @@ -2488,8 +2560,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Tøm nettbuffer" } }, "nl": { @@ -2500,8 +2572,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Очистить веб-кэш" } } } @@ -2641,14 +2713,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Certificados de cliente" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Asiakasvarmenteet" } }, "fr": { @@ -2665,8 +2737,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Klientsertifikater" } }, "nl": { @@ -2677,8 +2749,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Клиентские сертификаты" } } } @@ -2848,8 +2920,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Закрыто" } } } @@ -2877,8 +2949,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Väri" } }, "fr": { @@ -2895,8 +2967,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Farge" } }, "nl": { @@ -2907,8 +2979,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Цвет" } } } @@ -2940,6 +3012,30 @@ "state": "translated", "value": "Elemento colore" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Elemento de color" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Värielementti" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Fargeelement" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Элемент цвета" + } } } }, @@ -2970,6 +3066,30 @@ "state": "translated", "value": "Ruota dei colori" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Rueda de colores" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Väriympyrä" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Fargehjul" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Цветовой круг" + } } } }, @@ -2990,14 +3110,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Comando fallido: %@" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Komento epäonnistui: %@" } }, "fr": { @@ -3014,8 +3134,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kommando mislyktes: %@" } }, "nl": { @@ -3026,8 +3146,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Команда не выполнена: %@" } } } @@ -3049,14 +3169,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Errores de comando: %lld" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Komentoviat: %lld" } }, "fr": { @@ -3073,8 +3193,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kommandofeil: %lld" } }, "nl": { @@ -3085,8 +3205,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Сбоев команд: %lld" } } } @@ -3108,14 +3228,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Elemento de comando %@" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Komentoelementti %@" } }, "fr": { @@ -3132,8 +3252,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kommandoelement %@" } }, "nl": { @@ -3144,8 +3264,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Элемент команды %@" } } } @@ -3231,8 +3351,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Yhdistetään" } }, "fr": { @@ -3249,8 +3369,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kobler til" } }, "nl": { @@ -3261,8 +3381,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Подключение" } } } @@ -3461,14 +3581,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Conexión exitosa" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Yhteys muodostettu" } }, "fr": { @@ -3485,8 +3605,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Tilkobling vellykket" } }, "nl": { @@ -3497,8 +3617,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Соединение установлено" } } } @@ -3530,34 +3650,58 @@ "state": "translated", "value": "Elemento contatto" } - } - } - }, - "Contact State": { - "comment": "Type display name for the ContactState enum used in app intents.", - "localizations": { - "de": { + }, + "es": { "stringUnit": { "state": "translated", - "value": "Kontakt-Status" + "value": "Elemento de contacto" } }, - "en": { + "fi": { "stringUnit": { "state": "translated", - "value": "Contact State" + "value": "Kontaktielementti" } }, - "es": { + "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kontaktelement" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Элемент контакта" + } + } + } + }, + "Contact State": { + "comment": "Type display name for the ContactState enum used in app intents.", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Kontakt-Status" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Contact State" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Estado de contacto" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kontaktin tila" } }, "fr": { @@ -3574,8 +3718,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kontaktstatus" } }, "nl": { @@ -3586,8 +3730,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Состояние контакта" } } } @@ -3609,14 +3753,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Luz diurna fría" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Viileä päivänvalo" } }, "fr": { @@ -3633,8 +3777,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kjølig dagslys" } }, "nl": { @@ -3645,8 +3789,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Холодный дневной свет" } } } @@ -3668,14 +3812,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Blanco frío" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Viileä valkoinen" } }, "fr": { @@ -3692,8 +3836,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kjølig hvit" } }, "nl": { @@ -3704,8 +3848,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Холодный белый" } } } @@ -3733,8 +3877,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kopioi" } }, "fr": { @@ -3751,8 +3895,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kopier" } }, "nl": { @@ -3763,8 +3907,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Копировать" } } } @@ -3845,14 +3989,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Informe de fallos" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kaatumisraportointi" } }, "fr": { @@ -3869,8 +4013,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Krasjirapportering" } }, "nl": { @@ -3881,8 +4025,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Отчёты о сбоях" } } } @@ -4084,8 +4228,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Luo" } }, "fr": { @@ -4102,8 +4246,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Opprett" } }, "nl": { @@ -4114,8 +4258,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Создать" } } } @@ -4137,14 +4281,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Fecha y hora" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Päivämäärä ja aika" } }, "fr": { @@ -4161,8 +4305,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Dato og tid" } }, "nl": { @@ -4173,8 +4317,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Дата и время" } } } @@ -4206,6 +4350,30 @@ "state": "translated", "value": "Elemento DateTime" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Elemento DateTime" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "DateTime-elementti" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "DateTime-element" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Элемент DateTime" + } } } }, @@ -4226,14 +4394,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Luz diurna" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Päivänvalo" } }, "fr": { @@ -4250,8 +4418,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Dagslys" } }, "nl": { @@ -4262,8 +4430,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Дневной свет" } } } @@ -4403,14 +4571,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Disminuir %@" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Pienennä %@" } }, "fr": { @@ -4427,8 +4595,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Reduser %@" } }, "nl": { @@ -4439,8 +4607,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Уменьшить %@" } } } @@ -4462,14 +4630,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ruta predeterminada" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Oletuspolku" } }, "fr": { @@ -4486,8 +4654,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Standard sti" } }, "nl": { @@ -4498,8 +4666,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Путь по умолчанию" } } } @@ -4527,8 +4695,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Poista" } }, "fr": { @@ -4545,8 +4713,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Slett" } }, "nl": { @@ -4557,8 +4725,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Удалить" } } } @@ -4580,14 +4748,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "¿Eliminar hogar ‘%@’?" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Poistetaanko koti ‘%@’?" } }, "fr": { @@ -4604,8 +4772,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Slette hjem ‘%@’?" } }, "nl": { @@ -4616,8 +4784,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Удалить дом «%@»?" } } } @@ -4639,14 +4807,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Modo demo" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Esittelytila" } }, "fr": { @@ -4663,8 +4831,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Demomodus" } }, "nl": { @@ -4675,8 +4843,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Демо-режим" } } } @@ -4763,8 +4931,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Hylkää" } }, "fr": { @@ -4781,8 +4949,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Avslå" } }, "nl": { @@ -4793,8 +4961,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Отклонить" } } } @@ -4826,6 +4994,30 @@ "state": "translated", "value": "Elemento dimmer/tapparella" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Elemento atenuador/persianas" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Himmennin-/rullaverhoelementti" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Dimmer-/rullegardinelement" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Элемент диммера/рольставни" + } } } }, @@ -4846,14 +5038,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Desactivar tiempo de espera en reposo" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Poista käytöstä lepoajan aikakatkaisu" } }, "fr": { @@ -4870,8 +5062,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Deaktiver tidsavbrudd ved inaktivitet" } }, "nl": { @@ -4882,8 +5074,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Отключить тайм-аут простоя" } } } @@ -5095,6 +5287,30 @@ "state": "translated", "value": "Ricerca server openHAB in corso…" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Descubriendo servidores openHAB…" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Etsitään openHAB-palvelimia…" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Finner openHAB-servere…" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Обнаружение серверов openHAB…" + } } } }, @@ -5183,7 +5399,31 @@ "state": "translated", "value": "Fine" } - } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Hecho" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Valmis" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Ferdig" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Готово" + } + } } }, "Drag to change hue and saturation": { @@ -5213,6 +5453,30 @@ "state": "translated", "value": "Trascina per modificare tonalità e saturazione" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Arrastrar para cambiar el tono y la saturación" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Vedä muuttaaksesi värisävyä ja kylläisyyttä" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Dra for å endre fargetone og metning" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Перетащите для изменения оттенка и насыщенности" + } } } }, @@ -5239,8 +5503,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "tyhjä" } }, "fr": { @@ -5257,8 +5521,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "tom" } }, "nl": { @@ -5269,8 +5533,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "пусто" } } } @@ -5351,14 +5615,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Activar atenuación" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ota himmennys käyttöön" } }, "fr": { @@ -5375,8 +5639,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aktiver dimming" } }, "nl": { @@ -5387,8 +5651,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Включить затемнение" } } } @@ -5410,14 +5674,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Activar salvapantallas" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ota näytönsäästäjä käyttöön" } }, "fr": { @@ -5434,8 +5698,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aktiver skjermsparer" } }, "nl": { @@ -5446,8 +5710,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Включить заставку экрана" } } } @@ -5469,14 +5733,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Introducir un nombre para el nuevo hogar" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Syötä nimi uudelle kodille" } }, "fr": { @@ -5493,8 +5757,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Angi et navn for det nye hjemmet" } }, "nl": { @@ -5505,8 +5769,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Введите имя для нового дома" } } } @@ -5528,14 +5792,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Introducir un nuevo nombre para el hogar ‘%@’" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Syötä uusi nimi kodille ‘%@’" } }, "fr": { @@ -5552,8 +5816,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Angi et nytt navn for hjemmet ‘%@’" } }, "nl": { @@ -5564,8 +5828,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Введите новое имя для дома «%@»" } } } @@ -5587,14 +5851,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Introducir nuevo valor" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Syötä uusi arvo" } }, "fr": { @@ -5611,8 +5875,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Angi ny verdi" } }, "nl": { @@ -5623,8 +5887,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Введите новое значение" } } } @@ -5705,14 +5969,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Introducir texto" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Syötä teksti" } }, "fr": { @@ -5729,8 +5993,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Angi tekst" } }, "nl": { @@ -5741,8 +6005,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Введите текст" } } } @@ -5764,14 +6028,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Introducir URL del servidor remoto" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Syötä etäpalvelimen URL" } }, "fr": { @@ -5788,8 +6052,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Angi URL til fjernserver" } }, "nl": { @@ -5800,8 +6064,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Введите URL удалённого сервера" } } } @@ -5888,8 +6152,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Virhe" } }, "fr": { @@ -5906,8 +6170,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Feil" } }, "nl": { @@ -5918,8 +6182,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ошибка" } } } @@ -5941,14 +6205,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Duración del fundido: %@ s" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Häivytyksen kesto: %@ s" } }, "fr": { @@ -5965,8 +6229,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Falming varighet: %@ s" } }, "nl": { @@ -5977,8 +6241,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Длительность затухания: %@ с" } } } @@ -6000,14 +6264,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Avance rápido" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Pikakelaus eteenpäin" } }, "fr": { @@ -6024,8 +6288,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Spol frem" } }, "nl": { @@ -6036,8 +6300,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Перемотка вперёд" } } } @@ -6065,8 +6329,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Fontti" } }, "fr": { @@ -6083,8 +6347,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Skrift" } }, "nl": { @@ -6095,8 +6359,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Шрифт" } } } @@ -6124,8 +6388,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Fonttikoko" } }, "fr": { @@ -6142,8 +6406,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Skriftstørrelse" } }, "nl": { @@ -6154,8 +6418,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Размер шрифта" } } } @@ -6177,14 +6441,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Foo" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Foo" } }, "fr": { @@ -6201,8 +6465,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Foo" } }, "nl": { @@ -6213,8 +6477,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Foo" } } } @@ -6303,6 +6567,30 @@ "state": "translated", "value": "Ottieni lo stato di ${itemEntity}" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Obtener estado de ${itemEntity}" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Hae ${itemEntity}-tila" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Hent status for ${itemEntity}" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Получить состояние ${itemEntity}" + } } } }, @@ -6329,8 +6617,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Hae elementin tila" } }, "fr": { @@ -6359,8 +6647,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Получить состояние элемента" } } } @@ -6441,14 +6729,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ocultar barra de estado" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Piilota tilapalkki" } }, "fr": { @@ -6465,8 +6753,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Skjul statuslinje" } }, "nl": { @@ -6477,8 +6765,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Скрыть строку состояния" } } } @@ -6506,8 +6794,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Koti" } }, "fr": { @@ -6524,8 +6812,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Hjem" } }, "nl": { @@ -6536,8 +6824,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Дом" } } } @@ -6559,14 +6847,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Tipo de icono" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kuvaketyyppi" } }, "fr": { @@ -6583,8 +6871,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ikonstil" } }, "nl": { @@ -6595,8 +6883,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Тип значка" } } } @@ -6677,14 +6965,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Intervalo de reposo: %lld s" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Lepoväli: %lld s" } }, "fr": { @@ -6701,8 +6989,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Inaktivitetsintervall: %lld s" } }, "nl": { @@ -6713,8 +7001,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Интервал простоя: %lld с" } } } @@ -6736,14 +7024,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ignorar certificados SSL" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ohita SSL-varmenteet" } }, "fr": { @@ -6760,8 +7048,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ignorer SSL-sertifikater" } }, "nl": { @@ -6772,8 +7060,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Игнорировать SSL-сертификаты" } } } @@ -6854,14 +7142,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Caché de imágenes" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kuvavälimuisti" } }, "fr": { @@ -6878,8 +7166,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Bildebuffer" } }, "nl": { @@ -6890,8 +7178,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Кэш изображений" } } } @@ -6919,8 +7207,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Tuo" } }, "fr": { @@ -6937,8 +7225,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Importer" } }, "nl": { @@ -6949,8 +7237,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Импорт" } } } @@ -6972,14 +7260,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aumentar %@" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Suurenna %@" } }, "fr": { @@ -6996,8 +7284,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Øk %@" } }, "nl": { @@ -7008,8 +7296,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Увеличить %@" } } } @@ -7096,8 +7384,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Virheellinen arvo %lld kohteelle %@ (0–100)" } }, "fr": { @@ -7126,8 +7414,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Недопустимое значение %lld для %@ (0–100)" } } } @@ -7155,8 +7443,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Virheellinen arvo: %@ kohteelle %@ täytyy olla HSB (0–360, 0–100, 0–100)" } }, "fr": { @@ -7185,8 +7473,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Недопустимое значение: %@ для %@ должно быть HSB (0–360, 0–100, 0–100)" } } } @@ -7273,8 +7561,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Elementti" } }, "fr": { @@ -7303,8 +7591,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Элемент" } } } @@ -7326,14 +7614,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "El elemento ‘%@’ no está en el hogar ‘%@’" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Elementti ‘%@’ ei ole kodissa ‘%@’" } }, "fr": { @@ -7350,8 +7638,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Element ‘%@’ er ikke i hjemmet ‘%@’" } }, "nl": { @@ -7362,8 +7650,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Элемент «%@» не входит в дом «%@»" } } } @@ -7395,6 +7683,30 @@ "state": "translated", "value": "Elemento '%@' non trovato" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Elemento ‘%@’ no encontrado" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Elementtiä ‘%@’ ei löydy" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Element ‘%@’ ikke funnet" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Элемент «%@» не найден" + } } } }, @@ -7421,8 +7733,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Elementit" } }, "fr": { @@ -7439,8 +7751,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Elementer" } }, "nl": { @@ -7451,8 +7763,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Элементы" } } } @@ -7480,8 +7792,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Etiketti" } }, "fr": { @@ -7498,8 +7810,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Etikett" } }, "nl": { @@ -7510,8 +7822,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Метка" } } } @@ -7533,14 +7845,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Latitud" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Leveysaste" } }, "fr": { @@ -7557,8 +7869,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Breddegrad" } }, "nl": { @@ -7569,8 +7881,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Широта" } } } @@ -7592,14 +7904,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "La latitud debe estar entre -90 y 90" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Leveysasteen on oltava välillä −90 ja 90" } }, "fr": { @@ -7616,8 +7928,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Breddegraden må være mellom -90 og 90" } }, "nl": { @@ -7628,8 +7940,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Широта должна быть от -90 до 90" } } } @@ -7716,8 +8028,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Oikeudelliset tiedot" } }, "fr": { @@ -7734,8 +8046,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Juridisk" } }, "nl": { @@ -7746,8 +8058,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Юридическая информация" } } } @@ -7769,14 +8081,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Cargando elementos…" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ladataan elementtejä…" } }, "fr": { @@ -7793,8 +8105,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Laster elementer…" } }, "nl": { @@ -7805,8 +8117,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Загрузка элементов…" } } } @@ -7828,14 +8140,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Cargando mapa del sitio…" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ladataan sivukarttaa…" } }, "fr": { @@ -7852,8 +8164,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Laster nettstedskart…" } }, "nl": { @@ -7864,8 +8176,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Загрузка карты сайта…" } } } @@ -7956,6 +8268,30 @@ "state": "translated", "value": "Potrebbe essere necessario l'accesso alla rete locale." } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Es posible que se requiera acceso a la red local." + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Lähiverkon käyttöoikeus voi olla tarpeen." + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Tilgang til lokalt nettverk kan være nødvendig." + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Может потребоваться доступ к локальной сети." + } } } }, @@ -7986,6 +8322,30 @@ "state": "translated", "value": "Accesso alla rete locale richiesto" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Se requiere acceso a la red local" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Lähiverkon käyttöoikeus vaaditaan" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Tilgang til lokalt nettverk kreves" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Требуется доступ к локальной сети" + } } } }, @@ -8006,14 +8366,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Servidor local" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Paikallinen palvelin" } }, "fr": { @@ -8030,8 +8390,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Lokal server" } }, "nl": { @@ -8042,8 +8402,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Локальный сервер" } } } @@ -8129,8 +8489,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Sijainti" } }, "fr": { @@ -8147,8 +8507,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Plassering" } }, "nl": { @@ -8159,8 +8519,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Местоположение" } } } @@ -8192,6 +8552,30 @@ "state": "translated", "value": "Elemento posizione" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Elemento de ubicación" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Sijaintielementti" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Plasseringselement" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Элемент местоположения" + } } } }, @@ -8218,8 +8602,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Lokit" } }, "fr": { @@ -8236,8 +8620,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Logger" } }, "nl": { @@ -8248,8 +8632,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Журналы" } } } @@ -8271,14 +8655,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Longitud" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Pituusaste" } }, "fr": { @@ -8295,8 +8679,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Lengdegrad" } }, "nl": { @@ -8307,8 +8691,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Долгота" } } } @@ -8330,14 +8714,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "La longitud debe estar entre -180 y 180" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Pituusasteen on oltava välillä −180 ja 180" } }, "fr": { @@ -8354,8 +8738,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Lengdegraden må være mellom -180 og 180" } }, "nl": { @@ -8366,8 +8750,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Долгота должна быть от -180 до 180" } } } @@ -8389,14 +8773,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Baja %@" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Laskee %@" } }, "fr": { @@ -8413,8 +8797,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Senker med %@" } }, "nl": { @@ -8425,8 +8809,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Снижает на %@" } } } @@ -8454,8 +8838,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Pää" } }, "fr": { @@ -8472,8 +8856,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Hoved" } }, "nl": { @@ -8484,8 +8868,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Главная" } } } @@ -8565,14 +8949,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Gestionar hogares" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Hallitse koteja" } }, "fr": { @@ -8589,8 +8973,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Administrer hjem" } }, "nl": { @@ -8601,8 +8985,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Управление домами" } } } @@ -8682,14 +9066,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Intervalo de movimiento: %lld s" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Liikeväli: %lld s" } }, "fr": { @@ -8706,8 +9090,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Bevegelsesintervall: %lld s" } }, "nl": { @@ -8718,8 +9102,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Интервал движения: %lld с" } } } @@ -8747,8 +9131,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Nimi" } }, "fr": { @@ -8765,8 +9149,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Navn" } }, "nl": { @@ -8777,8 +9161,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Имя" } } } @@ -8800,14 +9184,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Nombre para el nuevo hogar" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Nimi uudelle kodille" } }, "fr": { @@ -8824,8 +9208,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Navn for nytt hjem" } }, "nl": { @@ -8836,8 +9220,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Имя для нового дома" } } } @@ -8917,14 +9301,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Nuevo nombre" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Uusi nimi" } }, "fr": { @@ -8941,8 +9325,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Nytt navn" } }, "nl": { @@ -8953,8 +9337,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Новое имя" } } } @@ -8976,14 +9360,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Siguiente" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Seuraava" } }, "fr": { @@ -9000,8 +9384,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Neste" } }, "nl": { @@ -9012,8 +9396,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Следующий" } } } @@ -9035,14 +9419,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "No hay certificados de servidor aceptados" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ei hyväksyttyjä palvelinvarmenteita" } }, "fr": { @@ -9059,8 +9443,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ingen godkjente serversertifikater" } }, "nl": { @@ -9071,8 +9455,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Нет принятых сертификатов сервера" } } } @@ -9093,14 +9477,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Sin URL de imagen" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ei kuvan URL-osoitetta" } }, "fr": { @@ -9117,8 +9501,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ingen bilde-URL" } }, "nl": { @@ -9129,8 +9513,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Нет URL изображения" } } } @@ -9152,14 +9536,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "No hay mapas del sitio disponibles" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ei sivukarttoja saatavilla" } }, "fr": { @@ -9176,8 +9560,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ingen nettstedskart tilgjengelig" } }, "nl": { @@ -9188,8 +9572,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Нет доступных карт сайта" } } } @@ -9211,14 +9595,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Sin URL de vídeo" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ei videon URL-osoitetta" } }, "fr": { @@ -9235,8 +9619,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ingen video-URL" } }, "nl": { @@ -9247,8 +9631,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Нет URL видео" } } } @@ -9270,14 +9654,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "No hay widgets disponibles." } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ei widgettejä saatavilla." } }, "fr": { @@ -9294,8 +9678,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ingen widgeter tilgjengelig." } }, "nl": { @@ -9306,8 +9690,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Нет доступных виджетов." } } } @@ -9388,14 +9772,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Sin conexión, reintentando…" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ei yhteyttä, yritetään uudelleen…" } }, "fr": { @@ -9412,8 +9796,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ingen tilkobling, kobler til igjen…" } }, "nl": { @@ -9424,8 +9808,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Нет подключения, повторное подключение…" } } } @@ -9628,8 +10012,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ilmoitukset" } }, "fr": { @@ -9646,8 +10030,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Varsler" } }, "nl": { @@ -9658,8 +10042,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Уведомления" } } } @@ -9691,6 +10075,30 @@ "state": "translated", "value": "Elemento numerico" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Elemento numérico" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Numeraalielementti" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Tallselement" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Числовой элемент" + } } } }, @@ -9717,8 +10125,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "pois" } }, "fr": { @@ -9735,8 +10143,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "av" } }, "nl": { @@ -9747,8 +10155,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "выкл." } } } @@ -9776,8 +10184,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Pois" } }, "fr": { @@ -9794,8 +10202,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Av" } }, "nl": { @@ -9806,8 +10214,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Выкл." } } } @@ -9835,8 +10243,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Offline" } }, "fr": { @@ -9853,8 +10261,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Frakoblet" } }, "nl": { @@ -9865,8 +10273,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Не в сети" } } } @@ -10071,8 +10479,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "OK" } }, "fr": { @@ -10089,8 +10497,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "OK" } }, "nl": { @@ -10101,8 +10509,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "ОК" } } } @@ -10130,8 +10538,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "päällä" } }, "fr": { @@ -10148,8 +10556,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "på" } }, "nl": { @@ -10160,8 +10568,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "вкл." } } } @@ -10183,14 +10591,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Encendido" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Päällä" } }, "fr": { @@ -10207,8 +10615,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "På" } }, "nl": { @@ -10219,8 +10627,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Вкл." } } } @@ -10242,14 +10650,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Una vez" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kerran" } }, "fr": { @@ -10266,8 +10674,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Én gang" } }, "nl": { @@ -10278,8 +10686,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Один раз" } } } @@ -10391,8 +10799,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Открыто" } } } @@ -10424,6 +10832,30 @@ "state": "translated", "value": "Apri Impostazioni" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Abrir ajustes" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Avaa asetukset" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Åpne innstillinger" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Открыть настройки" + } } } }, @@ -10444,14 +10876,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Servicio en la nube openHAB" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "openHAB-pilvipalvelu" } }, "fr": { @@ -10468,8 +10900,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "openHAB-skytjeneste" } }, "nl": { @@ -10480,8 +10912,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Облачный сервис openHAB" } } } @@ -10679,14 +11111,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Pausa" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Tauko" } }, "fr": { @@ -10703,8 +11135,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Pause" } }, "nl": { @@ -10715,8 +11147,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Пауза" } } } @@ -10738,14 +11170,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Reproducir" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Toista" } }, "fr": { @@ -10762,8 +11194,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Spill av" } }, "nl": { @@ -10774,8 +11206,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Воспроизвести" } } } @@ -10797,14 +11229,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Acción del reproductor" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Soittimen toiminto" } }, "fr": { @@ -10821,8 +11253,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Spillerhandling" } }, "nl": { @@ -10833,8 +11265,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Действие плеера" } } } @@ -10864,7 +11296,31 @@ "it": { "stringUnit": { "state": "translated", - "value": "Elemento player" + "value": "Elemento player" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Elemento reproductor" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Soitinelementti" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Spillerelement" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Элемент плеера" } } } @@ -10951,8 +11407,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Asetukset" } }, "fr": { @@ -10969,8 +11425,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Innstillinger" } }, "nl": { @@ -10981,8 +11437,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Настройки" } } } @@ -11004,14 +11460,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Estado de vista previa" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Esikatselutila" } }, "fr": { @@ -11028,8 +11484,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Forhåndsvisningstilstand" } }, "nl": { @@ -11040,8 +11496,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Состояние предварительного просмотра" } } } @@ -11063,14 +11519,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Anterior" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Edellinen" } }, "fr": { @@ -11087,8 +11543,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Forrige" } }, "nl": { @@ -11099,8 +11555,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Предыдущий" } } } @@ -11180,14 +11636,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Comandos en cola: %lld" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Jonossa olevat komennot: %lld" } }, "fr": { @@ -11204,8 +11660,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kommandoer i kø: %lld" } }, "nl": { @@ -11216,8 +11672,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Команды в очереди: %lld" } } } @@ -11239,14 +11695,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Sube %@" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Nostaa %@" } }, "fr": { @@ -11263,8 +11719,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Øker med %@" } }, "nl": { @@ -11275,8 +11731,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Повышает на %@" } } } @@ -11357,14 +11813,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Controles deslizantes en tiempo real" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Reaaliaikaiset liukusäätimet" } }, "fr": { @@ -11381,8 +11837,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Sanntidsskyvere" } }, "nl": { @@ -11393,8 +11849,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Слайдеры реального времени" } } } @@ -11416,14 +11872,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Servidor remoto" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Etäpalvelin" } }, "fr": { @@ -11440,8 +11896,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Fjernserver" } }, "nl": { @@ -11452,8 +11908,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Удалённый сервер" } } } @@ -11599,8 +12055,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Nimeä uudelleen" } }, "fr": { @@ -11617,8 +12073,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Gi nytt navn" } }, "nl": { @@ -11629,8 +12085,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Переименовать" } } } @@ -11652,14 +12108,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Restaurar brillo anterior al despertar" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Palauta aiempi kirkkaus herätessä" } }, "fr": { @@ -11676,8 +12132,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Gjenopprett tidligere lysstyrke ved vekking" } }, "nl": { @@ -11688,8 +12144,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Восстановить предыдущую яркость при пробуждении" } } } @@ -11717,8 +12173,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Hae elementin nykyinen tila" } }, "fr": { @@ -11747,8 +12203,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Получить текущее состояние элемента" } } } @@ -11829,14 +12285,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Rebobinar" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kelaa taaksepäin" } }, "fr": { @@ -11853,8 +12309,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Spol tilbake" } }, "nl": { @@ -11865,8 +12321,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Перемотка назад" } } } @@ -11953,8 +12409,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Tallenna" } }, "fr": { @@ -11971,8 +12427,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Lagre" } }, "nl": { @@ -11983,8 +12439,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Сохранить" } } } @@ -12012,8 +12468,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Näytönsäästäjä" } }, "fr": { @@ -12030,8 +12486,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Skjermsparer" } }, "nl": { @@ -12042,8 +12498,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Заставка экрана" } } } @@ -12065,14 +12521,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ajustes del salvapantallas" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Näytönsäästäjän asetukset" } }, "fr": { @@ -12089,8 +12545,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Skjermsparer-innstillinger" } }, "nl": { @@ -12101,8 +12557,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Настройки заставки" } } } @@ -12130,8 +12586,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Haku" } }, "fr": { @@ -12148,8 +12604,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Søk" } }, "nl": { @@ -12160,8 +12616,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Поиск" } } } @@ -12183,14 +12639,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Buscar un elemento" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Etsi elementtiä" } }, "fr": { @@ -12207,8 +12663,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Søk etter et element" } }, "nl": { @@ -12219,8 +12675,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Найти элемент" } } } @@ -12354,14 +12810,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Seleccionar color" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Valitse väri" } }, "fr": { @@ -12378,8 +12834,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Velg farge" } }, "nl": { @@ -12390,8 +12846,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Выбрать цвет" } } } @@ -12480,6 +12936,30 @@ "state": "translated", "value": "Invia ${action} a ${itemEntity}" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Enviar ${action} a ${itemEntity}" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Lähetä ${action} kohteeseen ${itemEntity}" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Send ${action} til ${itemEntity}" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Отправить ${action} в ${itemEntity}" + } } } }, @@ -12500,14 +12980,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Enviar un comando al reproductor como reproducir, pausar, siguiente o anterior" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Lähetä soittimelle komento, kuten toista, tauko, seuraava tai edellinen" } }, "fr": { @@ -12524,8 +13004,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Send en spillerkommando som spill av, pause, neste eller forrige" } }, "nl": { @@ -12536,8 +13016,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Отправить команду плееру: воспроизвести, пауза, следующий или предыдущий" } } } @@ -12558,14 +13038,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Enviando comandos: %lld" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Lähetetään komentoja: %lld" } }, "fr": { @@ -12582,8 +13062,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Sender kommandoer: %lld" } }, "nl": { @@ -12594,8 +13074,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Отправка команд: %lld" } } } @@ -12624,8 +13104,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "%1$@ lähetetty kohteeseen %2$@" } }, "fr": { @@ -12654,8 +13134,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "%1$@ отправлено в %2$@" } } } @@ -12677,14 +13157,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ubicación %lf, %lf enviada a %@" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Sijainti %lf, %lf lähetetty kohteeseen %@" } }, "fr": { @@ -12701,8 +13181,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Plassering %lf, %lf sendt til %@" } }, "nl": { @@ -12713,8 +13193,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Местоположение %lf, %lf отправлено в %@" } } } @@ -12742,8 +13222,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Väriarvo %@ lähetetty kohteeseen %@" } }, "fr": { @@ -12772,8 +13252,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Значение цвета %@ отправлено в %@" } } } @@ -12795,14 +13275,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Fecha %@ enviada a %@" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Päivämäärä %@ lähetetty kohteeseen %@" } }, "fr": { @@ -12819,8 +13299,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Dato %@ sendt til %@" } }, "nl": { @@ -12831,8 +13311,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Дата %@ отправлена в %@" } } } @@ -12860,8 +13340,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Luku %lf lähetetty kohteeseen %@" } }, "fr": { @@ -12890,8 +13370,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Число %lf отправлено в %@" } } } @@ -12919,8 +13399,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Merkkijono %@ lähetetty kohteeseen %@" } }, "fr": { @@ -12949,8 +13429,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Строка %@ отправлена в %@" } } } @@ -12978,8 +13458,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Arvo %lld lähetetty kohteeseen %@" } }, "fr": { @@ -13008,8 +13488,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Значение %lld отправлено в %@" } } } @@ -13039,6 +13519,30 @@ "state": "translated", "value": "Imposta ${itemEntity} su ${latitude}, ${longitude}" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Establecer ${itemEntity} en ${latitude}, ${longitude}" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Aseta ${itemEntity} arvoon ${latitude}, ${longitude}" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Sett ${itemEntity} til ${latitude}, ${longitude}" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Установить ${itemEntity} в ${latitude}, ${longitude}" + } } } }, @@ -13067,6 +13571,30 @@ "state": "translated", "value": "Imposta ${itemEntity} su ${value}" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Establecer ${itemEntity} en ${value}" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Aseta ${itemEntity} arvoon ${value}" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Sett ${itemEntity} til ${value}" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Установить ${itemEntity} в ${value}" + } } } }, @@ -13095,6 +13623,30 @@ "state": "translated", "value": "Imposta ${itemEntity} su ${value} (HSB)" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Establecer ${itemEntity} en ${value} (HSB)" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Aseta ${itemEntity} arvoon ${value} (HSB)" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Sett ${itemEntity} til ${value} (HSB)" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Установить ${itemEntity} в ${value} (HSB)" + } } } }, @@ -13227,8 +13779,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aseta värisäätimen arvo" } }, "fr": { @@ -13257,8 +13809,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Установить значение элемента управления цветом" } } } @@ -13286,8 +13838,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aseta kontaktin tilanarvo" } }, "fr": { @@ -13316,8 +13868,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Установить значение состояния контакта" } } } @@ -13339,14 +13891,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Establecer valor del control DateTime" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aseta DateTime-säätimen arvo" } }, "fr": { @@ -13363,8 +13915,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Angi DateTime-kontrollverdi" } }, "nl": { @@ -13375,8 +13927,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Установить значение элемента управления DateTime" } } } @@ -13404,8 +13956,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aseta himmentimen tai rullaverkon arvo" } }, "fr": { @@ -13434,8 +13986,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Установить значение диммера или рольставни" } } } @@ -13457,14 +14009,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Establecer valor del control de ubicación" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aseta sijaintisäätimen arvo" } }, "fr": { @@ -13481,8 +14033,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Angi plasseringskontrollverdi" } }, "nl": { @@ -13493,8 +14045,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Установить значение элемента управления местоположением" } } } @@ -13522,8 +14074,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aseta numerosäätimen arvo" } }, "fr": { @@ -13552,8 +14104,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Установить числовое значение элемента управления" } } } @@ -13575,14 +14127,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Establecer valor del control del reproductor" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aseta soittimen säätimen arvo" } }, "fr": { @@ -13599,8 +14151,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Angi spillerkontrollverdi" } }, "nl": { @@ -13611,8 +14163,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Установить значение элемента управления плеером" } } } @@ -13640,8 +14192,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aseta merkkijonosäätimen arvo" } }, "fr": { @@ -13670,8 +14222,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Установить строковое значение элемента управления" } } } @@ -13699,8 +14251,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aseta kytkimen tila" } }, "fr": { @@ -13729,8 +14281,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Установить состояние переключателя" } } } @@ -13758,8 +14310,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aseta värielementin väri" } }, "fr": { @@ -13788,8 +14340,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Установить цвет элемента управления цветом" } } } @@ -13811,14 +14363,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Establecer la fecha y hora de un elemento de control DateTime" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aseta DateTime-elementin päivämäärä ja aika" } }, "fr": { @@ -13835,8 +14387,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Angi dato og tid for et DateTime-kontrollelement" } }, "nl": { @@ -13847,8 +14399,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Установить дату и время элемента DateTime" } } } @@ -13876,8 +14428,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aseta numeroelementin desimaaliarvo" } }, "fr": { @@ -13906,8 +14458,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Установить десятичное значение числового элемента" } } } @@ -13935,8 +14487,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aseta himmentimen tai rullaverkon kokonaislukuarvo" } }, "fr": { @@ -13965,8 +14517,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Установить целочисленное значение диммера или рольставни" } } } @@ -13988,14 +14540,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Establecer la latitud y longitud de un elemento de control de ubicación" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aseta sijaintielementin leveys- ja pituusaste" } }, "fr": { @@ -14012,8 +14564,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Angi bredde- og lengdegrad for et plasseringskontrollelement" } }, "nl": { @@ -14024,8 +14576,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Установить широту и долготу элемента местоположения" } } } @@ -14055,6 +14607,30 @@ "state": "translated", "value": "Imposta lo stato di ${itemEntity} su ${state}" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Establecer el estado de ${itemEntity} en ${state}" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Aseta ${itemEntity}-tila arvoon ${state}" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Angi status for ${itemEntity} til ${state}" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Установить состояние ${itemEntity} в ${state}" + } } } }, @@ -14081,8 +14657,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aseta kontaktin tilaksi auki tai kiinni" } }, "fr": { @@ -14111,8 +14687,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Установить состояние контакта: открыт или закрыт" } } } @@ -14134,14 +14710,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Establecer el estado de un interruptor en encendido o apagado, o alternar su estado" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aseta kytkimen tilaksi päällä tai pois tai vaihda sen tilaa" } }, "fr": { @@ -14158,8 +14734,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Angi brytertilstanden til på eller av, eller bytt tilstand" } }, "nl": { @@ -14170,8 +14746,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Установить состояние переключателя вкл. или выкл., или переключить его состояние" } } } @@ -14199,8 +14775,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aseta merkkijonoelementin merkkijono" } }, "fr": { @@ -14229,8 +14805,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Установить строку строкового элемента" } } } @@ -14252,14 +14828,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Establecer valor" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aseta arvo" } }, "fr": { @@ -14276,8 +14852,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Angi verdi" } }, "nl": { @@ -14288,8 +14864,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Установить значение" } } } @@ -14375,8 +14951,8 @@ }, "fi": { "stringUnit": { - "state": "needs_review", - "value": "Settings not yet received from phone." + "state": "translated", + "value": "Asetuksia ei ole vielä vastaanotettu iPhonelta." } }, "fr": { @@ -14405,8 +14981,8 @@ }, "ru": { "stringUnit": { - "state": "needs_review", - "value": "Settings not yet received from phone." + "state": "translated", + "value": "Настройки ещё не получены с iPhone." } } } @@ -14428,14 +15004,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Mostrar fecha" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Näytä päivämäärä" } }, "fr": { @@ -14452,8 +15028,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Vis dato" } }, "nl": { @@ -14464,8 +15040,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Показать дату" } } } @@ -14487,14 +15063,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Mostrar campo de búsqueda" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Näytä hakukenttä" } }, "fr": { @@ -14511,8 +15087,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Vis søkefelt" } }, "nl": { @@ -14523,8 +15099,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Показать поле поиска" } } } @@ -14546,14 +15122,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Mostrar segundos" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Näytä sekunnit" } }, "fr": { @@ -14570,8 +15146,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Vis sekunder" } }, "nl": { @@ -14582,8 +15158,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Показать секунды" } } } @@ -14605,14 +15181,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Mostrar hora" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Näytä aika" } }, "fr": { @@ -14629,8 +15205,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Vis tid" } }, "nl": { @@ -14641,8 +15217,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Показать время" } } } @@ -14781,14 +15357,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Mapa del sitio" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Sivukartta" } }, "fr": { @@ -14805,8 +15381,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Nettstedskart" } }, "nl": { @@ -14817,8 +15393,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Карта сайта" } } } @@ -14850,6 +15426,30 @@ "state": "translated", "value": "Registrazione diagnostica sitemap" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Registro de diagnóstico del mapa del sitio" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Sivukartan diagnostiikkaloki" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Diagnoslogging for nettstedskart" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Журналирование диагностики карты сайта" + } } } }, @@ -14870,14 +15470,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Mapa del sitio para Apple Watch" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Apple Watch -sivukartta" } }, "fr": { @@ -14894,8 +15494,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Nettstedskart for Apple Watch" } }, "nl": { @@ -14906,8 +15506,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Карта сайта для Apple Watch" } } } @@ -14987,14 +15587,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Mapas del sitio" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Sivukartat" } }, "fr": { @@ -15011,8 +15611,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Nettstedskart" } }, "nl": { @@ -15023,8 +15623,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Карты сайта" } } } @@ -15046,14 +15646,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Tamaño: %llu MB" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Koko: %llu Mt" } }, "fr": { @@ -15070,8 +15670,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Størrelse: %llu MB" } }, "nl": { @@ -15082,8 +15682,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Размер: %llu МБ" } } } @@ -15105,14 +15705,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Blanco suave" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Pehmeä valkoinen" } }, "fr": { @@ -15129,8 +15729,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Myk hvit" } }, "nl": { @@ -15141,8 +15741,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Мягкий белый" } } } @@ -15164,14 +15764,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ordenar mapas del sitio por" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Lajittele sivukartat" } }, "fr": { @@ -15188,8 +15788,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Sorter nettstedskart etter" } }, "nl": { @@ -15200,8 +15800,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Сортировать карты сайта по" } } } @@ -15282,14 +15882,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Error SSL. No se pudo establecer la conexión de forma segura." } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "SSL-virhe. Yhteyttä ei voitu muodostaa turvallisesti." } }, "fr": { @@ -15306,8 +15906,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "SSL-feil. Tilkoblingen kunne ikke opprettes sikkert." } }, "nl": { @@ -15318,8 +15918,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ошибка SSL. Не удалось установить безопасное соединение." } } } @@ -15580,8 +16180,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Tila" } }, "fr": { @@ -15598,8 +16198,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Tilstand" } }, "nl": { @@ -15610,8 +16210,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Состояние" } } } @@ -15643,6 +16243,30 @@ "state": "translated", "value": "Elemento stringa" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Elemento de texto" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Merkkijonoelementti" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Tekststrengselement" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Строковый элемент" + } } } }, @@ -15722,14 +16346,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Acción del interruptor" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kytkimen toiminto" } }, "fr": { @@ -15746,8 +16370,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Bryterhandling" } }, "nl": { @@ -15758,8 +16382,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Действие переключателя" } } } @@ -15791,6 +16415,30 @@ "state": "translated", "value": "Elemento interruttore" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Elemento interruptor" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Kytkinelementti" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Bryterelement" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Элемент переключателя" + } } } }, @@ -15930,8 +16578,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Järjestelmä" } }, "fr": { @@ -15948,8 +16596,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "System" } }, "nl": { @@ -15960,8 +16608,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Система" } } } @@ -15983,14 +16631,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Probar conexión" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Testaa yhteys" } }, "fr": { @@ -16007,8 +16655,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Test tilkobling" } }, "nl": { @@ -16019,8 +16667,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Проверить соединение" } } } @@ -16042,14 +16690,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Probar salvapantallas" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Testaa näytönsäästäjä" } }, "fr": { @@ -16066,8 +16714,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Test skjermsparer" } }, "nl": { @@ -16078,8 +16726,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Проверить заставку" } } } @@ -16101,14 +16749,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "La conexión ha agotado el tiempo de espera. Inténtelo de nuevo más tarde." } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Yhteys aikakatkaistiin. Yritä myöhemmin uudelleen." } }, "fr": { @@ -16125,8 +16773,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Tilkoblingen ble tidsavbrutt. Prøv igjen senere." } }, "nl": { @@ -16137,8 +16785,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Время соединения истекло. Повторите попытку позже." } } } @@ -16166,8 +16814,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kohteen %@ tila on %@" } }, "fr": { @@ -16196,8 +16844,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Состояние %@ равно %@" } } } @@ -16225,8 +16873,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kohteen %@ tilaksi asetettiin %@" } }, "fr": { @@ -16255,8 +16903,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Состояние %@ установлено в %@" } } } @@ -16288,6 +16936,30 @@ "state": "translated", "value": "L'URL non è valido. Controlla il formato (es. http://192.168.2.1:8080 o http://[::1]:8080 per IPv6)." } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "La URL no es válida. Compruebe el formato (p. ej., http://192.168.2.1:8080 o http://[::1]:8080 para IPv6)." + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "URL on virheellinen. Tarkista muoto (esim. http://192.168.2.1:8080 tai http://[::1]:8080 IPv6:lle)." + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "URL-en er ugyldig. Sjekk formatet (f.eks. http://192.168.2.1:8080 eller http://[::1]:8080 for IPv6)." + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "URL недействителен. Проверьте формат (например, http://192.168.2.1:8080 или http://[::1]:8080 для IPv6)." + } } } }, @@ -16308,14 +16980,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Mosaicos" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ruudut" } }, "fr": { @@ -16332,8 +17004,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Fliser" } }, "nl": { @@ -16344,8 +17016,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Плитки" } } } @@ -16367,14 +17039,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Temporización" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ajoitus" } }, "fr": { @@ -16391,8 +17063,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Timing" } }, "nl": { @@ -16403,8 +17075,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Синхронизация" } } } @@ -16436,6 +17108,30 @@ "state": "translated", "value": "Per connetterti al tuo server openHAB locale, consenti l'accesso alla rete locale quando richiesto. Se in precedenza hai negato l'accesso, abilitalo in Impostazioni → Privacy e sicurezza → Rete locale." } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Para conectarse a su servidor openHAB local, permita el acceso a la red local cuando se le solicite. Si lo denegó anteriormente, actívelo en Ajustes → Privacidad y seguridad → Red local." + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Muodosta yhteys paikalliseen openHAB-palvelimeen sallimalla lähiverkkoyhteys pyydettäessä. Jos olet aiemmin kieltänyt sen, ota se käyttöön kohdassa Asetukset → Tietosuoja ja turvallisuus → Lähiverkko." + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "For å koble til din lokale openHAB-server, vennligst tillat tilgang til lokalt nettverk når du blir bedt om det. Hvis du tidligere nektet det, aktiver det i Innstillinger → Personvern og sikkerhet → Lokalt nettverk." + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Для подключения к локальному серверу openHAB разрешите доступ к локальной сети при появлении запроса. Если вы ранее отказали, включите его в Настройки → Конфиденциальность и безопасность → Локальная сеть." + } } } }, @@ -16493,8 +17189,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "переключить" } } } @@ -16516,14 +17212,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Alternar" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Vaihda" } }, "fr": { @@ -16540,8 +17236,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Bytt" } }, "nl": { @@ -16552,8 +17248,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Переключить" } } } @@ -16693,14 +17389,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Error inesperado: %@" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Odottamaton virhe: %@" } }, "fr": { @@ -16717,8 +17413,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Uventet feil: %@" } }, "nl": { @@ -16729,8 +17425,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Непредвиденная ошибка: %@" } } } @@ -16871,8 +17567,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Päivitetään…" } }, "fr": { @@ -16889,8 +17585,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Oppdaterer…" } }, "nl": { @@ -16901,8 +17597,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Обновление…" } } } @@ -16930,8 +17626,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "URL" } }, "fr": { @@ -16948,8 +17644,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "URL" } }, "nl": { @@ -16960,8 +17656,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "URL" } } } @@ -17106,8 +17802,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Käyttäjänimi" } }, "fr": { @@ -17124,8 +17820,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Brukernavn" } }, "nl": { @@ -17136,8 +17832,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Имя пользователя" } } } @@ -17165,8 +17861,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Arvo" } }, "fr": { @@ -17195,8 +17891,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Значение" } } } @@ -17276,14 +17972,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Muy frío" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Hyvin viileä" } }, "fr": { @@ -17300,8 +17996,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Svært kjølig" } }, "nl": { @@ -17312,8 +18008,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Очень холодный" } } } @@ -17335,14 +18031,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Muy cálido" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Hyvin lämmin" } }, "fr": { @@ -17359,8 +18055,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Svært varm" } }, "nl": { @@ -17371,8 +18067,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Очень тёплый" } } } @@ -17394,14 +18090,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Blanco cálido" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Lämmin valkoinen" } }, "fr": { @@ -17418,8 +18114,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Varm hvit" } }, "nl": { @@ -17430,8 +18126,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Тёплый белый" } } } @@ -17511,14 +18207,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Advertencia: Cambiar el nombre del hogar puede hacer que las integraciones externas, como los atajos, fallen hasta que se reconfiguren." } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Varoitus: Kodin uudelleennimeäminen voi aiheuttaa ulkoisten integraatioiden, kuten pikakomentojen, epäonnistumisen, kunnes ne on uudelleenmääritetty." } }, "fr": { @@ -17535,8 +18231,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Advarsel: Å gi hjemmet nytt navn kan føre til at eksterne integrasjoner som snarveier mislykkes inntil de er rekonfigurert." } }, "nl": { @@ -17547,8 +18243,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Предупреждение: переименование дома может привести к сбою внешних интеграций, таких как ярлыки, до их перенастройки." } } } @@ -17570,14 +18266,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Parece que estás sin conexión. Comprueba tu conexión a internet." } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Näyttää siltä, että olet offline. Tarkista internet-yhteytesi." } }, "fr": { @@ -17594,8 +18290,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Du ser ut til å være frakoblet. Sjekk internettilkoblingen din." } }, "nl": { @@ -17606,8 +18302,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Похоже, вы не в сети. Проверьте подключение к интернету." } } } @@ -17628,14 +18324,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Tamaño del reloj: %@" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kellon koko: %@" } }, "fr": { @@ -17652,8 +18348,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Klokkestørrelse: %@" } }, "nl": { @@ -17664,8 +18360,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Размер часов: %@" } } }, @@ -17687,14 +18383,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Nivel de atenuación: %@" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Himmennystaso: %@" } }, "fr": { @@ -17711,8 +18407,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Dimmenivå: %@" } }, "nl": { @@ -17723,8 +18419,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Уровень затемнения: %@" } } }, @@ -17746,14 +18442,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Fecha relativa: %@" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Suhteellinen päivämäärä: %@" } }, "fr": { @@ -17770,8 +18466,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Relativ dato: %@" } }, "nl": { @@ -17782,8 +18478,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Относительная дата: %@" } } }, @@ -17805,14 +18501,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Restaurar brillo: %@" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Palauta kirkkaus: %@" } }, "fr": { @@ -17829,8 +18525,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Gjenopprett lysstyrke: %@" } }, "nl": { @@ -17841,8 +18537,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Восстановить яркость: %@" } } }, From d8111793f9f0337d65ebdb48e44ce1084946a21f Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Wed, 17 Jun 2026 17:11:49 +0200 Subject: [PATCH 25/91] chore: remove 5 stale localization keys on feature branch Signed-off-by: Tim Mueller-Seydlitz --- .../Supporting Files/Localizable.xcstrings | 1508 +++++++---------- 1 file changed, 608 insertions(+), 900 deletions(-) diff --git a/openHAB/Supporting Files/Localizable.xcstrings b/openHAB/Supporting Files/Localizable.xcstrings index 03c927a2b..3f80d015d 100644 --- a/openHAB/Supporting Files/Localizable.xcstrings +++ b/openHAB/Supporting Files/Localizable.xcstrings @@ -124,49 +124,49 @@ "extractionState": "extracted_with_value", "isCommentAutoGenerated": true, "localizations": { - "en": { + "de": { "stringUnit": { - "state": "new", + "state": "translated", "value": "%@" } }, - "de": { + "en": { "stringUnit": { - "state": "translated", + "state": "new", "value": "%@" } }, - "fr": { + "es": { "stringUnit": { "state": "translated", "value": "%@" } }, - "nl": { + "fi": { "stringUnit": { "state": "translated", "value": "%@" } }, - "it": { + "fr": { "stringUnit": { "state": "translated", "value": "%@" } }, - "es": { + "it": { "stringUnit": { "state": "translated", "value": "%@" } }, - "fi": { + "nb": { "stringUnit": { "state": "translated", "value": "%@" } }, - "nb": { + "nl": { "stringUnit": { "state": "translated", "value": "%@" @@ -240,6 +240,7 @@ } }, "%@": { + "comment": "A label that shows the percentage of brightness. The value is rounded to the nearest whole number.", "localizations": { "de": { "stringUnit": { @@ -247,37 +248,37 @@ "value": "%@" } }, - "fr": { + "es": { "stringUnit": { "state": "translated", "value": "%@" } }, - "nl": { + "fi": { "stringUnit": { "state": "translated", "value": "%@" } }, - "it": { + "fr": { "stringUnit": { "state": "translated", "value": "%@" } }, - "es": { + "it": { "stringUnit": { "state": "translated", "value": "%@" } }, - "fi": { + "nb": { "stringUnit": { "state": "translated", "value": "%@" } }, - "nb": { + "nl": { "stringUnit": { "state": "translated", "value": "%@" @@ -289,8 +290,7 @@ "value": "%@" } } - }, - "comment": "A label that shows the percentage of brightness. The value is rounded to the nearest whole number." + } }, "%@ Settings": { "comment": "The title of the settings view. The placeholder is replaced with the user's home name.", @@ -2416,40 +2416,40 @@ "value": "Farbe wählen" } }, - "fr": { + "es": { "stringUnit": { "state": "translated", - "value": "Choisir une couleur" + "value": "Elegir color" } }, - "nl": { + "fi": { "stringUnit": { "state": "translated", - "value": "Kleur kiezen" + "value": "Valitse väri" } }, - "it": { + "fr": { "stringUnit": { "state": "translated", - "value": "Scegli colore" + "value": "Choisir une couleur" } }, - "es": { + "it": { "stringUnit": { "state": "translated", - "value": "Elegir color" + "value": "Scegli colore" } }, - "fi": { + "nb": { "stringUnit": { "state": "translated", - "value": "Valitse väri" + "value": "Velg farge" } }, - "nb": { + "nl": { "stringUnit": { "state": "translated", - "value": "Velg farge" + "value": "Kleur kiezen" } }, "ru": { @@ -2813,6 +2813,65 @@ } } }, + "Clock Size: %@": { + "comment": "A label describing the percentage of the screen that is used for the clock face.", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Uhrgröße: %@" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Clock Size: %@" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Tamaño del reloj: %@" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Kellon koko: %@" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Taille de l'horloge : %@" + } + }, + "it": { + "stringUnit": { + "state": "translated", + "value": "Dimensione orologio: %@" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Klokkestørrelse: %@" + } + }, + "nl": { + "stringUnit": { + "state": "translated", + "value": "Klokgrootte: %@" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Размер часов: %@" + } + } + } + }, "closed": { "extractionState": "manual", "localizations": { @@ -2995,40 +3054,40 @@ "value": "Farb-Item" } }, - "fr": { + "es": { "stringUnit": { "state": "translated", - "value": "Élément couleur" + "value": "Elemento de color" } }, - "nl": { + "fi": { "stringUnit": { "state": "translated", - "value": "Kleur-item" + "value": "Värielementti" } }, - "it": { + "fr": { "stringUnit": { "state": "translated", - "value": "Elemento colore" + "value": "Élément couleur" } }, - "es": { + "it": { "stringUnit": { "state": "translated", - "value": "Elemento de color" + "value": "Elemento colore" } }, - "fi": { + "nb": { "stringUnit": { "state": "translated", - "value": "Värielementti" + "value": "Fargeelement" } }, - "nb": { + "nl": { "stringUnit": { "state": "translated", - "value": "Fargeelement" + "value": "Kleur-item" } }, "ru": { @@ -3049,40 +3108,40 @@ "value": "Farbrad" } }, - "fr": { + "es": { "stringUnit": { "state": "translated", - "value": "Roue des couleurs" + "value": "Rueda de colores" } }, - "nl": { + "fi": { "stringUnit": { "state": "translated", - "value": "Kleurwiel" + "value": "Väriympyrä" } }, - "it": { + "fr": { "stringUnit": { "state": "translated", - "value": "Ruota dei colori" + "value": "Roue des couleurs" } }, - "es": { + "it": { "stringUnit": { "state": "translated", - "value": "Rueda de colores" + "value": "Ruota dei colori" } }, - "fi": { + "nb": { "stringUnit": { "state": "translated", - "value": "Väriympyrä" + "value": "Fargehjul" } }, - "nb": { + "nl": { "stringUnit": { "state": "translated", - "value": "Fargehjul" + "value": "Kleurwiel" } }, "ru": { @@ -3633,40 +3692,40 @@ "value": "Kontakt-Item" } }, - "fr": { + "es": { "stringUnit": { "state": "translated", - "value": "Élément de contact" + "value": "Elemento de contacto" } }, - "nl": { + "fi": { "stringUnit": { "state": "translated", - "value": "Contact-item" + "value": "Kontaktielementti" } }, - "it": { + "fr": { "stringUnit": { "state": "translated", - "value": "Elemento contatto" + "value": "Élément de contact" } }, - "es": { + "it": { "stringUnit": { "state": "translated", - "value": "Elemento de contacto" + "value": "Elemento contatto" } }, - "fi": { + "nb": { "stringUnit": { "state": "translated", - "value": "Kontaktielementti" + "value": "Kontaktelement" } }, - "nb": { + "nl": { "stringUnit": { "state": "translated", - "value": "Kontaktelement" + "value": "Contact-item" } }, "ru": { @@ -4333,40 +4392,40 @@ "value": "DateTime-Item" } }, - "fr": { + "es": { "stringUnit": { "state": "translated", - "value": "Élément DateHeure" + "value": "Elemento DateTime" } }, - "nl": { + "fi": { "stringUnit": { "state": "translated", - "value": "DateTime-item" + "value": "DateTime-elementti" } }, - "it": { + "fr": { "stringUnit": { "state": "translated", - "value": "Elemento DateTime" + "value": "Élément DateHeure" } }, - "es": { + "it": { "stringUnit": { "state": "translated", "value": "Elemento DateTime" } }, - "fi": { + "nb": { "stringUnit": { "state": "translated", - "value": "DateTime-elementti" + "value": "DateTime-element" } }, - "nb": { + "nl": { "stringUnit": { "state": "translated", - "value": "DateTime-element" + "value": "DateTime-item" } }, "ru": { @@ -4967,103 +5026,168 @@ } } }, - "Dimmer/Roller Item": { - "comment": "Display name for a dimmer or roller item type.", - "isCommentAutoGenerated": true, + "Dim Level: %@": { + "comment": "A label displaying the current dim level of the screen saver. The value is shown as a percentage.", "localizations": { "de": { "stringUnit": { "state": "translated", - "value": "Dimmer/Roller-Item" + "value": "Dimmwert: %@" } }, - "fr": { + "en": { "stringUnit": { "state": "translated", - "value": "Élément gradateur/volet" + "value": "Dim Level: %@" } }, - "nl": { + "es": { "stringUnit": { "state": "translated", - "value": "Dimmer/Roller-item" + "value": "Nivel de atenuación: %@" } }, - "it": { + "fi": { "stringUnit": { "state": "translated", - "value": "Elemento dimmer/tapparella" + "value": "Himmennystaso: %@" } }, - "es": { + "fr": { "stringUnit": { "state": "translated", - "value": "Elemento atenuador/persianas" + "value": "Niveau de gradation : %@" } }, - "fi": { + "it": { "stringUnit": { "state": "translated", - "value": "Himmennin-/rullaverhoelementti" + "value": "Livello dimmeraggio: %@" } }, "nb": { "stringUnit": { "state": "translated", - "value": "Dimmer-/rullegardinelement" + "value": "Dimmenivå: %@" + } + }, + "nl": { + "stringUnit": { + "state": "translated", + "value": "Dimniveau: %@" } }, "ru": { "stringUnit": { "state": "translated", - "value": "Элемент диммера/рольставни" + "value": "Уровень затемнения: %@" } } } }, - "Disable Idle Timeout": { - "comment": "A toggle that allows the user to disable the idle timeout feature.", + "Dimmer or Roller Item": { + "comment": "Display name for a dimmer or roller item type.", + "isCommentAutoGenerated": true, "localizations": { "de": { "stringUnit": { "state": "translated", - "value": "Ruhezustand-Timeout deaktivieren" + "value": "Dimmer- oder Rolladen-Item" } }, "en": { "stringUnit": { "state": "translated", - "value": "Disable Idle Timeout" + "value": "Dimmer or Roller Item" } }, "es": { "stringUnit": { "state": "translated", - "value": "Desactivar tiempo de espera en reposo" + "value": "Elemento regulador o persiana" } }, "fi": { "stringUnit": { "state": "translated", - "value": "Poista käytöstä lepoajan aikakatkaisu" + "value": "Himmennin- tai rullakaihdinelementti" } }, "fr": { "stringUnit": { "state": "translated", - "value": "Désactiver le délai d'inactivité" + "value": "Élément variateur ou volet" } }, "it": { "stringUnit": { "state": "translated", - "value": "Disabilita timeout inattività" + "value": "Elemento dimmer o tapparella" } }, "nb": { "stringUnit": { "state": "translated", - "value": "Deaktiver tidsavbrudd ved inaktivitet" + "value": "Dimmer- eller rullelukkelement" + } + }, + "nl": { + "stringUnit": { + "state": "translated", + "value": "Dimmer- of rolluik-item" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Элемент диммера или ролеты" + } + } + } + }, + "Disable Idle Timeout": { + "comment": "A toggle that allows the user to disable the idle timeout feature.", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Ruhezustand-Timeout deaktivieren" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Disable Idle Timeout" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Desactivar tiempo de espera en reposo" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Poista käytöstä lepoajan aikakatkaisu" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Désactiver le délai d'inactivité" + } + }, + "it": { + "stringUnit": { + "state": "translated", + "value": "Disabilita timeout inattività" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Deaktiver tidsavbrudd ved inaktivitet" } }, "nl": { @@ -5270,40 +5394,40 @@ "value": "Discovering openHAB servers…" } }, - "fr": { + "es": { "stringUnit": { "state": "translated", - "value": "Recherche des serveurs openHAB…" + "value": "Descubriendo servidores openHAB…" } }, - "nl": { + "fi": { "stringUnit": { "state": "translated", - "value": "openHAB-servers worden ontdekt…" + "value": "Etsitään openHAB-palvelimia…" } }, - "it": { + "fr": { "stringUnit": { "state": "translated", - "value": "Ricerca server openHAB in corso…" + "value": "Recherche des serveurs openHAB…" } }, - "es": { + "it": { "stringUnit": { "state": "translated", - "value": "Descubriendo servidores openHAB…" + "value": "Ricerca server openHAB in corso…" } }, - "fi": { + "nb": { "stringUnit": { "state": "translated", - "value": "Etsitään openHAB-palvelimia…" + "value": "Finner openHAB-servere…" } }, - "nb": { + "nl": { "stringUnit": { "state": "translated", - "value": "Finner openHAB-servere…" + "value": "openHAB-servers worden ontdekt…" } }, "ru": { @@ -5382,40 +5506,40 @@ "value": "Fertig" } }, - "fr": { + "es": { "stringUnit": { "state": "translated", - "value": "Terminé" + "value": "Hecho" } }, - "nl": { + "fi": { "stringUnit": { "state": "translated", - "value": "Gereed" + "value": "Valmis" } }, - "it": { + "fr": { "stringUnit": { "state": "translated", - "value": "Fine" + "value": "Terminé" } }, - "es": { + "it": { "stringUnit": { "state": "translated", - "value": "Hecho" + "value": "Fine" } }, - "fi": { + "nb": { "stringUnit": { "state": "translated", - "value": "Valmis" + "value": "Ferdig" } }, - "nb": { + "nl": { "stringUnit": { "state": "translated", - "value": "Ferdig" + "value": "Gereed" } }, "ru": { @@ -5436,40 +5560,40 @@ "value": "Ziehen, um Farbton und Sättigung zu ändern" } }, - "fr": { + "es": { "stringUnit": { "state": "translated", - "value": "Faire glisser pour modifier la teinte et la saturation" + "value": "Arrastrar para cambiar el tono y la saturación" } }, - "nl": { + "fi": { "stringUnit": { "state": "translated", - "value": "Sleep om tint en verzadiging te wijzigen" + "value": "Vedä muuttaaksesi värisävyä ja kylläisyyttä" } }, - "it": { + "fr": { "stringUnit": { "state": "translated", - "value": "Trascina per modificare tonalità e saturazione" + "value": "Faire glisser pour modifier la teinte et la saturation" } }, - "es": { + "it": { "stringUnit": { "state": "translated", - "value": "Arrastrar para cambiar el tono y la saturación" + "value": "Trascina per modificare tonalità e saturazione" } }, - "fi": { + "nb": { "stringUnit": { "state": "translated", - "value": "Vedä muuttaaksesi värisävyä ja kylläisyyttä" + "value": "Dra for å endre fargetone og metning" } }, - "nb": { + "nl": { "stringUnit": { "state": "translated", - "value": "Dra for å endre fargetone og metning" + "value": "Sleep om tint en verzadiging te wijzigen" } }, "ru": { @@ -6550,40 +6674,40 @@ "value": "${itemEntity}-Status erhalten" } }, - "fr": { + "es": { "stringUnit": { "state": "translated", - "value": "Obtenir l'état de ${itemEntity}" + "value": "Obtener estado de ${itemEntity}" } }, - "nl": { + "fi": { "stringUnit": { "state": "translated", - "value": "Status van ${itemEntity} ophalen" + "value": "Hae ${itemEntity}-tila" } }, - "it": { + "fr": { "stringUnit": { "state": "translated", - "value": "Ottieni lo stato di ${itemEntity}" + "value": "Obtenir l'état de ${itemEntity}" } }, - "es": { + "it": { "stringUnit": { "state": "translated", - "value": "Obtener estado de ${itemEntity}" + "value": "Ottieni lo stato di ${itemEntity}" } }, - "fi": { + "nb": { "stringUnit": { "state": "translated", - "value": "Hae ${itemEntity}-tila" + "value": "Hent status for ${itemEntity}" } }, - "nb": { + "nl": { "stringUnit": { "state": "translated", - "value": "Hent status for ${itemEntity}" + "value": "Status van ${itemEntity} ophalen" } }, "ru": { @@ -7666,40 +7790,40 @@ "value": "Element '%@' nicht gefunden" } }, - "fr": { + "es": { "stringUnit": { "state": "translated", - "value": "Élément '%@' introuvable" + "value": "Elemento ‘%@’ no encontrado" } }, - "nl": { + "fi": { "stringUnit": { "state": "translated", - "value": "Item '%@' niet gevonden" + "value": "Elementtiä ‘%@’ ei löydy" } }, - "it": { + "fr": { "stringUnit": { "state": "translated", - "value": "Elemento '%@' non trovato" + "value": "Élément '%@' introuvable" } }, - "es": { + "it": { "stringUnit": { "state": "translated", - "value": "Elemento ‘%@’ no encontrado" + "value": "Elemento '%@' non trovato" } }, - "fi": { + "nb": { "stringUnit": { "state": "translated", - "value": "Elementtiä ‘%@’ ei löydy" + "value": "Element ‘%@’ ikke funnet" } }, - "nb": { + "nl": { "stringUnit": { "state": "translated", - "value": "Element ‘%@’ ikke funnet" + "value": "Item '%@' niet gevonden" } }, "ru": { @@ -8251,40 +8375,40 @@ "value": "Möglicherweise ist der Zugriff auf das lokale Netzwerk erforderlich." } }, - "fr": { + "es": { "stringUnit": { "state": "translated", - "value": "L'accès au réseau local peut être requis." + "value": "Es posible que se requiera acceso a la red local." } }, - "nl": { + "fi": { "stringUnit": { "state": "translated", - "value": "Toegang tot lokaal netwerk kan vereist zijn." + "value": "Lähiverkon käyttöoikeus voi olla tarpeen." } }, - "it": { + "fr": { "stringUnit": { "state": "translated", - "value": "Potrebbe essere necessario l'accesso alla rete locale." + "value": "L'accès au réseau local peut être requis." } }, - "es": { + "it": { "stringUnit": { "state": "translated", - "value": "Es posible que se requiera acceso a la red local." + "value": "Potrebbe essere necessario l'accesso alla rete locale." } }, - "fi": { + "nb": { "stringUnit": { "state": "translated", - "value": "Lähiverkon käyttöoikeus voi olla tarpeen." + "value": "Tilgang til lokalt nettverk kan være nødvendig." } }, - "nb": { + "nl": { "stringUnit": { "state": "translated", - "value": "Tilgang til lokalt nettverk kan være nødvendig." + "value": "Toegang tot lokaal netwerk kan vereist zijn." } }, "ru": { @@ -8305,40 +8429,40 @@ "value": "Zugriff auf lokales Netzwerk erforderlich" } }, - "fr": { + "es": { "stringUnit": { "state": "translated", - "value": "Accès au réseau local requis" + "value": "Se requiere acceso a la red local" } }, - "nl": { + "fi": { "stringUnit": { "state": "translated", - "value": "Toegang tot lokaal netwerk vereist" + "value": "Lähiverkon käyttöoikeus vaaditaan" } }, - "it": { + "fr": { "stringUnit": { "state": "translated", - "value": "Accesso alla rete locale richiesto" + "value": "Accès au réseau local requis" } }, - "es": { + "it": { "stringUnit": { "state": "translated", - "value": "Se requiere acceso a la red local" + "value": "Accesso alla rete locale richiesto" } }, - "fi": { + "nb": { "stringUnit": { "state": "translated", - "value": "Lähiverkon käyttöoikeus vaaditaan" + "value": "Tilgang til lokalt nettverk kreves" } }, - "nb": { + "nl": { "stringUnit": { "state": "translated", - "value": "Tilgang til lokalt nettverk kreves" + "value": "Toegang tot lokaal netwerk vereist" } }, "ru": { @@ -8535,40 +8659,40 @@ "value": "Standort-Item" } }, - "fr": { + "es": { "stringUnit": { "state": "translated", - "value": "Élément de localisation" + "value": "Elemento de ubicación" } }, - "nl": { + "fi": { "stringUnit": { "state": "translated", - "value": "Locatie-item" + "value": "Sijaintielementti" } }, - "it": { + "fr": { "stringUnit": { "state": "translated", - "value": "Elemento posizione" + "value": "Élément de localisation" } }, - "es": { + "it": { "stringUnit": { "state": "translated", - "value": "Elemento de ubicación" + "value": "Elemento posizione" } }, - "fi": { + "nb": { "stringUnit": { "state": "translated", - "value": "Sijaintielementti" + "value": "Plasseringselement" } }, - "nb": { + "nl": { "stringUnit": { "state": "translated", - "value": "Plasseringselement" + "value": "Locatie-item" } }, "ru": { @@ -10058,28 +10182,10 @@ "value": "Numerisches-Item" } }, - "fr": { + "es": { "stringUnit": { "state": "translated", - "value": "Élément numérique" - } - }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Numeriek item" - } - }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elemento numerico" - } - }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento numérico" + "value": "Elemento numérico" } }, "fi": { @@ -10088,75 +10194,34 @@ "value": "Numeraalielementti" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Tallselement" - } - }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Числовой элемент" - } - } - } - }, - "off": { - "comment": "Label for the \"off\" state of a contact.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "aus" - } - }, - "en": { - "stringUnit": { - "state": "translated", - "value": "off" - } - }, - "es": { - "stringUnit": { - "state": "translated", - "value": "desactivada" - } - }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "pois" - } - }, "fr": { "stringUnit": { "state": "translated", - "value": "désactivé" + "value": "Élément numérique" } }, "it": { "stringUnit": { "state": "translated", - "value": "non attivo" + "value": "Elemento numerico" } }, "nb": { "stringUnit": { "state": "translated", - "value": "av" + "value": "Tallselement" } }, "nl": { "stringUnit": { "state": "translated", - "value": "uit" + "value": "Numeriek item" } }, "ru": { "stringUnit": { "state": "translated", - "value": "выкл." + "value": "Числовой элемент" } } } @@ -10515,65 +10580,6 @@ } } }, - "on": { - "comment": "\"on\" in capitalized form.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "an" - } - }, - "en": { - "stringUnit": { - "state": "translated", - "value": "on" - } - }, - "es": { - "stringUnit": { - "state": "translated", - "value": "activada" - } - }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "päällä" - } - }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "activé" - } - }, - "it": { - "stringUnit": { - "state": "translated", - "value": "attivo" - } - }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "på" - } - }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "aan" - } - }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "вкл." - } - } - } - }, "On": { "comment": "A label indicating that a switch is on.", "localizations": { @@ -10815,40 +10821,40 @@ "value": "Einstellungen öffnen" } }, - "fr": { + "es": { "stringUnit": { "state": "translated", - "value": "Ouvrir les réglages" + "value": "Abrir ajustes" } }, - "nl": { + "fi": { "stringUnit": { "state": "translated", - "value": "Instellingen openen" + "value": "Avaa asetukset" } }, - "it": { + "fr": { "stringUnit": { "state": "translated", - "value": "Apri Impostazioni" + "value": "Ouvrir les réglages" } }, - "es": { + "it": { "stringUnit": { "state": "translated", - "value": "Abrir ajustes" + "value": "Apri Impostazioni" } }, - "fi": { + "nb": { "stringUnit": { "state": "translated", - "value": "Avaa asetukset" + "value": "Åpne innstillinger" } }, - "nb": { + "nl": { "stringUnit": { "state": "translated", - "value": "Åpne innstillinger" + "value": "Instellingen openen" } }, "ru": { @@ -11281,40 +11287,40 @@ "value": "Player-Item" } }, - "fr": { + "es": { "stringUnit": { "state": "translated", - "value": "Élément lecteur" + "value": "Elemento reproductor" } }, - "nl": { + "fi": { "stringUnit": { "state": "translated", - "value": "Speler-item" + "value": "Soitinelementti" } }, - "it": { + "fr": { "stringUnit": { "state": "translated", - "value": "Elemento player" + "value": "Élément lecteur" } }, - "es": { + "it": { "stringUnit": { "state": "translated", - "value": "Elemento reproductor" + "value": "Elemento player" } }, - "fi": { + "nb": { "stringUnit": { "state": "translated", - "value": "Soitinelementti" + "value": "Spillerelement" } }, - "nb": { + "nl": { "stringUnit": { "state": "translated", - "value": "Spillerelement" + "value": "Speler-item" } }, "ru": { @@ -11855,6 +11861,65 @@ } } }, + "Relative Date: %@": { + "comment": "A label describing the relative size of the date text compared to the clock text.", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Relatives Datum: %@" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Relative Date: %@" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Fecha relativa: %@" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Suhteellinen päivämäärä: %@" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Date relative : %@" + } + }, + "it": { + "stringUnit": { + "state": "translated", + "value": "Data relativa: %@" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Relativ dato: %@" + } + }, + "nl": { + "stringUnit": { + "state": "translated", + "value": "Relatieve datum: %@" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Относительная дата: %@" + } + } + } + }, "Remote server": { "comment": "Header text for the remote server section in the connection settings view.", "localizations": { @@ -12091,126 +12156,185 @@ } } }, - "Restore Previous Brightness on Wake": { - "comment": "A toggle that allows the user to restore the brightness level to its previous value when the device wakes up.", + "Restore Brightness: %@": { + "comment": "A label under the slider that shows the current brightness level and allows the user to adjust it. The value shown is the brightness level as a percentage.", "localizations": { "de": { "stringUnit": { "state": "translated", - "value": "Vorherige Helligkeit beim Aufwecken wiederherstellen" + "value": "Helligkeit wiederherstellen: %@" } }, "en": { "stringUnit": { "state": "translated", - "value": "Restore Previous Brightness on Wake" + "value": "Restore Brightness: %@" } }, "es": { "stringUnit": { "state": "translated", - "value": "Restaurar brillo anterior al despertar" + "value": "Restaurar brillo: %@" } }, "fi": { "stringUnit": { "state": "translated", - "value": "Palauta aiempi kirkkaus herätessä" + "value": "Palauta kirkkaus: %@" } }, "fr": { "stringUnit": { "state": "translated", - "value": "Restaurer la luminosité précédente au réveil" + "value": "Restaurer la luminosité : %@" } }, "it": { "stringUnit": { "state": "translated", - "value": "Ripristina luminosità precedente alla riattivazione" + "value": "Ripristina luminosità: %@" } }, "nb": { "stringUnit": { "state": "translated", - "value": "Gjenopprett tidligere lysstyrke ved vekking" + "value": "Gjenopprett lysstyrke: %@" } }, "nl": { "stringUnit": { "state": "translated", - "value": "Vorige helderheid herstellen bij activering" + "value": "Helderheid herstellen: %@" } }, "ru": { "stringUnit": { "state": "translated", - "value": "Восстановить предыдущую яркость при пробуждении" + "value": "Восстановить яркость: %@" } } } }, - "Retrieve the current state of an item": { - "comment": "Description of the Get Item State app intent.", + "Restore Previous Brightness on Wake": { + "comment": "A toggle that allows the user to restore the brightness level to its previous value when the device wakes up.", "localizations": { "de": { "stringUnit": { "state": "translated", - "value": "Aktuellen Status eines Items abrufen" + "value": "Vorherige Helligkeit beim Aufwecken wiederherstellen" } }, "en": { "stringUnit": { "state": "translated", - "value": "Retrieve the current state of an item" + "value": "Restore Previous Brightness on Wake" } }, "es": { "stringUnit": { "state": "translated", - "value": "Recuperar el estado actual de un elemento" + "value": "Restaurar brillo anterior al despertar" } }, "fi": { "stringUnit": { "state": "translated", - "value": "Hae elementin nykyinen tila" + "value": "Palauta aiempi kirkkaus herätessä" } }, "fr": { "stringUnit": { "state": "translated", - "value": "Récupérer l'état actuel d'un item" + "value": "Restaurer la luminosité précédente au réveil" } }, "it": { "stringUnit": { "state": "translated", - "value": "Recupera lo stato attuale di un Item" + "value": "Ripristina luminosità precedente alla riattivazione" } }, "nb": { "stringUnit": { "state": "translated", - "value": "Hent nåværende tilstand for et Item" + "value": "Gjenopprett tidligere lysstyrke ved vekking" } }, "nl": { "stringUnit": { "state": "translated", - "value": "Haal de huidige status van een item op" + "value": "Vorige helderheid herstellen bij activering" } }, "ru": { "stringUnit": { "state": "translated", - "value": "Получить текущее состояние элемента" + "value": "Восстановить предыдущую яркость при пробуждении" } } } }, - "retry": { - "comment": "retry connection", + "Retrieve the current state of an item": { + "comment": "Description of the Get Item State app intent.", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Aktuellen Status eines Items abrufen" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Retrieve the current state of an item" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Recuperar el estado actual de un elemento" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Hae elementin nykyinen tila" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Récupérer l'état actuel d'un item" + } + }, + "it": { + "stringUnit": { + "state": "translated", + "value": "Recupera lo stato attuale di un Item" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Hent nåværende tilstand for et Item" + } + }, + "nl": { + "stringUnit": { + "state": "translated", + "value": "Haal de huidige status van een item op" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Получить текущее состояние элемента" + } + } + } + }, + "retry": { + "comment": "retry connection", "localizations": { "de": { "stringUnit": { @@ -12919,40 +13043,40 @@ "value": "${action} an ${itemEntity} senden" } }, - "fr": { + "es": { "stringUnit": { "state": "translated", - "value": "Envoyer ${action} à ${itemEntity}" + "value": "Enviar ${action} a ${itemEntity}" } }, - "nl": { + "fi": { "stringUnit": { "state": "translated", - "value": "${action} verzenden naar ${itemEntity}" + "value": "Lähetä ${action} kohteeseen ${itemEntity}" } }, - "it": { + "fr": { "stringUnit": { "state": "translated", - "value": "Invia ${action} a ${itemEntity}" + "value": "Envoyer ${action} à ${itemEntity}" } }, - "es": { + "it": { "stringUnit": { "state": "translated", - "value": "Enviar ${action} a ${itemEntity}" + "value": "Invia ${action} a ${itemEntity}" } }, - "fi": { + "nb": { "stringUnit": { "state": "translated", - "value": "Lähetä ${action} kohteeseen ${itemEntity}" + "value": "Send ${action} til ${itemEntity}" } }, - "nb": { + "nl": { "stringUnit": { "state": "translated", - "value": "Send ${action} til ${itemEntity}" + "value": "${action} verzenden naar ${itemEntity}" } }, "ru": { @@ -13502,40 +13626,40 @@ "value": "Standort ${itemEntity} auf ${latitude}, ${longitude} setzen" } }, - "fr": { + "es": { "stringUnit": { "state": "translated", - "value": "Définir ${itemEntity} à ${latitude}, ${longitude}" + "value": "Establecer ${itemEntity} en ${latitude}, ${longitude}" } }, - "nl": { + "fi": { "stringUnit": { "state": "translated", - "value": "${itemEntity} instellen op ${latitude}, ${longitude}" + "value": "Aseta ${itemEntity} arvoon ${latitude}, ${longitude}" } }, - "it": { + "fr": { "stringUnit": { "state": "translated", - "value": "Imposta ${itemEntity} su ${latitude}, ${longitude}" + "value": "Définir ${itemEntity} à ${latitude}, ${longitude}" } }, - "es": { + "it": { "stringUnit": { "state": "translated", - "value": "Establecer ${itemEntity} en ${latitude}, ${longitude}" + "value": "Imposta ${itemEntity} su ${latitude}, ${longitude}" } }, - "fi": { + "nb": { "stringUnit": { "state": "translated", - "value": "Aseta ${itemEntity} arvoon ${latitude}, ${longitude}" + "value": "Sett ${itemEntity} til ${latitude}, ${longitude}" } }, - "nb": { + "nl": { "stringUnit": { "state": "translated", - "value": "Sett ${itemEntity} til ${latitude}, ${longitude}" + "value": "${itemEntity} instellen op ${latitude}, ${longitude}" } }, "ru": { @@ -13554,40 +13678,40 @@ "value": "${itemEntity} auf ${value} setzen" } }, - "fr": { + "es": { "stringUnit": { "state": "translated", - "value": "Définir ${itemEntity} sur ${value}" + "value": "Establecer ${itemEntity} en ${value}" } }, - "nl": { + "fi": { "stringUnit": { "state": "translated", - "value": "${itemEntity} instellen op ${value}" + "value": "Aseta ${itemEntity} arvoon ${value}" } }, - "it": { + "fr": { "stringUnit": { "state": "translated", - "value": "Imposta ${itemEntity} su ${value}" + "value": "Définir ${itemEntity} sur ${value}" } }, - "es": { + "it": { "stringUnit": { "state": "translated", - "value": "Establecer ${itemEntity} en ${value}" + "value": "Imposta ${itemEntity} su ${value}" } }, - "fi": { + "nb": { "stringUnit": { "state": "translated", - "value": "Aseta ${itemEntity} arvoon ${value}" + "value": "Sett ${itemEntity} til ${value}" } }, - "nb": { + "nl": { "stringUnit": { "state": "translated", - "value": "Sett ${itemEntity} til ${value}" + "value": "${itemEntity} instellen op ${value}" } }, "ru": { @@ -13606,40 +13730,40 @@ "value": "${itemEntity} auf ${value} (HSB) setzen" } }, - "fr": { + "es": { "stringUnit": { "state": "translated", - "value": "Définir ${itemEntity} sur ${value} (TSL)" + "value": "Establecer ${itemEntity} en ${value} (HSB)" } }, - "nl": { + "fi": { "stringUnit": { "state": "translated", - "value": "${itemEntity} instellen op ${value} (HSB)" + "value": "Aseta ${itemEntity} arvoon ${value} (HSB)" } }, - "it": { + "fr": { "stringUnit": { "state": "translated", - "value": "Imposta ${itemEntity} su ${value} (HSB)" + "value": "Définir ${itemEntity} sur ${value} (TSL)" } }, - "es": { + "it": { "stringUnit": { "state": "translated", - "value": "Establecer ${itemEntity} en ${value} (HSB)" + "value": "Imposta ${itemEntity} su ${value} (HSB)" } }, - "fi": { + "nb": { "stringUnit": { "state": "translated", - "value": "Aseta ${itemEntity} arvoon ${value} (HSB)" + "value": "Sett ${itemEntity} til ${value} (HSB)" } }, - "nb": { + "nl": { "stringUnit": { "state": "translated", - "value": "Sett ${itemEntity} til ${value} (HSB)" + "value": "${itemEntity} instellen op ${value} (HSB)" } }, "ru": { @@ -14590,40 +14714,40 @@ "value": "Den Status von ${itemEntity} auf ${state} setzen" } }, - "fr": { + "es": { "stringUnit": { "state": "translated", - "value": "Définir l'état de ${itemEntity} sur ${state}" + "value": "Establecer el estado de ${itemEntity} en ${state}" } }, - "nl": { + "fi": { "stringUnit": { "state": "translated", - "value": "De status van ${itemEntity} instellen op ${state}" + "value": "Aseta ${itemEntity}-tila arvoon ${state}" } }, - "it": { + "fr": { "stringUnit": { "state": "translated", - "value": "Imposta lo stato di ${itemEntity} su ${state}" + "value": "Définir l'état de ${itemEntity} sur ${state}" } }, - "es": { + "it": { "stringUnit": { "state": "translated", - "value": "Establecer el estado de ${itemEntity} en ${state}" + "value": "Imposta lo stato di ${itemEntity} su ${state}" } }, - "fi": { + "nb": { "stringUnit": { "state": "translated", - "value": "Aseta ${itemEntity}-tila arvoon ${state}" + "value": "Angi status for ${itemEntity} til ${state}" } }, - "nb": { + "nl": { "stringUnit": { "state": "translated", - "value": "Angi status for ${itemEntity} til ${state}" + "value": "De status van ${itemEntity} instellen op ${state}" } }, "ru": { @@ -15409,40 +15533,40 @@ "value": "Sitemap-Diagnoseprotokollierung" } }, - "fr": { + "es": { "stringUnit": { "state": "translated", - "value": "Journalisation des diagnostics du plan du site" + "value": "Registro de diagnóstico del mapa del sitio" } }, - "nl": { + "fi": { "stringUnit": { "state": "translated", - "value": "Sitemap-diagnoselogboek" + "value": "Sivukartan diagnostiikkaloki" } }, - "it": { + "fr": { "stringUnit": { "state": "translated", - "value": "Registrazione diagnostica sitemap" + "value": "Journalisation des diagnostics du plan du site" } }, - "es": { + "it": { "stringUnit": { "state": "translated", - "value": "Registro de diagnóstico del mapa del sitio" + "value": "Registrazione diagnostica sitemap" } }, - "fi": { + "nb": { "stringUnit": { "state": "translated", - "value": "Sivukartan diagnostiikkaloki" + "value": "Diagnoslogging for nettstedskart" } }, - "nb": { + "nl": { "stringUnit": { "state": "translated", - "value": "Diagnoslogging for nettstedskart" + "value": "Sitemap-diagnoselogboek" } }, "ru": { @@ -16226,40 +16350,40 @@ "value": "String-Item" } }, - "fr": { + "es": { "stringUnit": { "state": "translated", - "value": "Élément texte" + "value": "Elemento de texto" } }, - "nl": { + "fi": { "stringUnit": { "state": "translated", - "value": "Tekst-item" + "value": "Merkkijonoelementti" } }, - "it": { + "fr": { "stringUnit": { "state": "translated", - "value": "Elemento stringa" + "value": "Élément texte" } }, - "es": { + "it": { "stringUnit": { "state": "translated", - "value": "Elemento de texto" + "value": "Elemento stringa" } }, - "fi": { + "nb": { "stringUnit": { "state": "translated", - "value": "Merkkijonoelementti" + "value": "Tekststrengselement" } }, - "nb": { + "nl": { "stringUnit": { "state": "translated", - "value": "Tekststrengselement" + "value": "Tekst-item" } }, "ru": { @@ -16398,40 +16522,40 @@ "value": "Switch-Item" } }, - "fr": { + "es": { "stringUnit": { "state": "translated", - "value": "Élément interrupteur" + "value": "Elemento interruptor" } }, - "nl": { + "fi": { "stringUnit": { "state": "translated", - "value": "Schakelaar-item" + "value": "Kytkinelementti" } }, - "it": { + "fr": { "stringUnit": { "state": "translated", - "value": "Elemento interruttore" + "value": "Élément interrupteur" } }, - "es": { + "it": { "stringUnit": { "state": "translated", - "value": "Elemento interruptor" + "value": "Elemento interruttore" } }, - "fi": { + "nb": { "stringUnit": { "state": "translated", - "value": "Kytkinelementti" + "value": "Bryterelement" } }, - "nb": { + "nl": { "stringUnit": { "state": "translated", - "value": "Bryterelement" + "value": "Schakelaar-item" } }, "ru": { @@ -16919,40 +17043,40 @@ "value": "Die URL ist ungültig. Bitte überprüfen Sie das Format (z.B. http://192.168.2.1:8080 oder http://[::1]:8080 für IPv6)." } }, - "fr": { + "es": { "stringUnit": { "state": "translated", - "value": "L'URL est invalide. Veuillez vérifier le format (p. ex., http://192.168.2.1:8080 ou http://[::1]:8080 pour IPv6)." + "value": "La URL no es válida. Compruebe el formato (p. ej., http://192.168.2.1:8080 o http://[::1]:8080 para IPv6)." } }, - "nl": { + "fi": { "stringUnit": { "state": "translated", - "value": "De URL is ongeldig. Controleer het formaat (bijv. http://192.168.2.1:8080 of http://[::1]:8080 voor IPv6)." + "value": "URL on virheellinen. Tarkista muoto (esim. http://192.168.2.1:8080 tai http://[::1]:8080 IPv6:lle)." } }, - "it": { + "fr": { "stringUnit": { "state": "translated", - "value": "L'URL non è valido. Controlla il formato (es. http://192.168.2.1:8080 o http://[::1]:8080 per IPv6)." + "value": "L'URL est invalide. Veuillez vérifier le format (p. ex., http://192.168.2.1:8080 ou http://[::1]:8080 pour IPv6)." } }, - "es": { + "it": { "stringUnit": { "state": "translated", - "value": "La URL no es válida. Compruebe el formato (p. ej., http://192.168.2.1:8080 o http://[::1]:8080 para IPv6)." + "value": "L'URL non è valido. Controlla il formato (es. http://192.168.2.1:8080 o http://[::1]:8080 per IPv6)." } }, - "fi": { + "nb": { "stringUnit": { "state": "translated", - "value": "URL on virheellinen. Tarkista muoto (esim. http://192.168.2.1:8080 tai http://[::1]:8080 IPv6:lle)." + "value": "URL-en er ugyldig. Sjekk formatet (f.eks. http://192.168.2.1:8080 eller http://[::1]:8080 for IPv6)." } }, - "nb": { + "nl": { "stringUnit": { "state": "translated", - "value": "URL-en er ugyldig. Sjekk formatet (f.eks. http://192.168.2.1:8080 eller http://[::1]:8080 for IPv6)." + "value": "De URL is ongeldig. Controleer het formaat (bijv. http://192.168.2.1:8080 of http://[::1]:8080 voor IPv6)." } }, "ru": { @@ -17091,24 +17215,6 @@ "value": "Um eine Verbindung zu Ihrem lokalen openHAB-Server herzustellen, erlauben Sie bitte den Zugriff auf das lokale Netzwerk, wenn Sie dazu aufgefordert werden. Wenn Sie es zuvor abgelehnt haben, aktivieren Sie es unter Einstellungen → Datenschutz & Sicherheit → Lokales Netzwerk." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Pour vous connecter à votre serveur openHAB local, veuillez autoriser l'accès au réseau local lorsque vous y êtes invité. Si vous l'avez précédemment refusé, activez-le dans Réglages → Confidentialité et sécurité → Réseau local." - } - }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Om verbinding te maken met uw lokale openHAB-server, sta toegang tot het lokale netwerk toe wanneer hierom wordt gevraagd. Als u dit eerder heeft geweigerd, schakel het in via Instellingen → Privacy en beveiliging → Lokaal netwerk." - } - }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Per connetterti al tuo server openHAB locale, consenti l'accesso alla rete locale quando richiesto. Se in precedenza hai negato l'accesso, abilitalo in Impostazioni → Privacy e sicurezza → Rete locale." - } - }, "es": { "stringUnit": { "state": "translated", @@ -17121,76 +17227,34 @@ "value": "Muodosta yhteys paikalliseen openHAB-palvelimeen sallimalla lähiverkkoyhteys pyydettäessä. Jos olet aiemmin kieltänyt sen, ota se käyttöön kohdassa Asetukset → Tietosuoja ja turvallisuus → Lähiverkko." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "For å koble til din lokale openHAB-server, vennligst tillat tilgang til lokalt nettverk når du blir bedt om det. Hvis du tidligere nektet det, aktiver det i Innstillinger → Personvern og sikkerhet → Lokalt nettverk." - } - }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Для подключения к локальному серверу openHAB разрешите доступ к локальной сети при появлении запроса. Если вы ранее отказали, включите его в Настройки → Конфиденциальность и безопасность → Локальная сеть." - } - } - } - }, - "toggle": { - "comment": "Action to toggle a switch between on and off states", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "umschalten" - } - }, - "en": { - "stringUnit": { - "state": "translated", - "value": "toggle" - } - }, - "es": { - "stringUnit": { - "state": "translated", - "value": "alternar" - } - }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "vaihda" - } - }, "fr": { "stringUnit": { "state": "translated", - "value": "basculer" + "value": "Pour vous connecter à votre serveur openHAB local, veuillez autoriser l'accès au réseau local lorsque vous y êtes invité. Si vous l'avez précédemment refusé, activez-le dans Réglages → Confidentialité et sécurité → Réseau local." } }, "it": { "stringUnit": { "state": "translated", - "value": "alternare" + "value": "Per connetterti al tuo server openHAB locale, consenti l'accesso alla rete locale quando richiesto. Se in precedenza hai negato l'accesso, abilitalo in Impostazioni → Privacy e sicurezza → Rete locale." } }, "nb": { "stringUnit": { "state": "translated", - "value": "veksle" + "value": "For å koble til din lokale openHAB-server, vennligst tillat tilgang til lokalt nettverk når du blir bedt om det. Hvis du tidligere nektet det, aktiver det i Innstillinger → Personvern og sikkerhet → Lokalt nettverk." } }, "nl": { "stringUnit": { "state": "translated", - "value": "omschakelen" + "value": "Om verbinding te maken met uw lokale openHAB-server, sta toegang tot het lokale netwerk toe wanneer hierom wordt gevraagd. Als u dit eerder heeft geweigerd, schakel het in via Instellingen → Privacy en beveiliging → Lokaal netwerk." } }, "ru": { "stringUnit": { "state": "translated", - "value": "переключить" + "value": "Для подключения к локальному серверу openHAB разрешите доступ к локальной сети при появлении запроса. Если вы ранее отказали, включите его в Настройки → Конфиденциальность и безопасность → Локальная сеть." } } } @@ -18307,362 +18371,6 @@ } } } - }, - "Clock Size: %@": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Uhrgröße: %@" - } - }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Clock Size: %@" - } - }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Tamaño del reloj: %@" - } - }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kellon koko: %@" - } - }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Taille de l'horloge : %@" - } - }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Dimensione orologio: %@" - } - }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Klokkestørrelse: %@" - } - }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Klokgrootte: %@" - } - }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Размер часов: %@" - } - } - }, - "comment": "A label describing the percentage of the screen that is used for the clock face." - }, - "Dim Level: %@": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Dimmwert: %@" - } - }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Dim Level: %@" - } - }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Nivel de atenuación: %@" - } - }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Himmennystaso: %@" - } - }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Niveau de gradation : %@" - } - }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Livello dimmeraggio: %@" - } - }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Dimmenivå: %@" - } - }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Dimniveau: %@" - } - }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Уровень затемнения: %@" - } - } - }, - "comment": "A label displaying the current dim level of the screen saver. The value is shown as a percentage." - }, - "Relative Date: %@": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Relatives Datum: %@" - } - }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Relative Date: %@" - } - }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Fecha relativa: %@" - } - }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Suhteellinen päivämäärä: %@" - } - }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Date relative : %@" - } - }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Data relativa: %@" - } - }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Relativ dato: %@" - } - }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Relatieve datum: %@" - } - }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Относительная дата: %@" - } - } - }, - "comment": "A label describing the relative size of the date text compared to the clock text." - }, - "Restore Brightness: %@": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Helligkeit wiederherstellen: %@" - } - }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Restore Brightness: %@" - } - }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Restaurar brillo: %@" - } - }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Palauta kirkkaus: %@" - } - }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Restaurer la luminosité : %@" - } - }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Ripristina luminosità: %@" - } - }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Gjenopprett lysstyrke: %@" - } - }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Helderheid herstellen: %@" - } - }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Восстановить яркость: %@" - } - } - }, - "comment": "A label under the slider that shows the current brightness level and allows the user to adjust it. The value shown is the brightness level as a percentage." - }, - "Dimmer or Roller Item": { - "comment": "Display name for a dimmer or roller item type.", - "isCommentAutoGenerated": true, - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Dimmer or Roller Item" - } - }, - "de": { - "stringUnit": { - "state": "translated", - "value": "Dimmer- oder Rolladen-Item" - } - }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento regulador o persiana" - } - }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Himmennin- tai rullakaihdinelementti" - } - }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Élément variateur ou volet" - } - }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elemento dimmer o tapparella" - } - }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Dimmer- eller rullelukkelement" - } - }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Dimmer- of rolluik-item" - } - }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент диммера или ролеты" - } - } - } - }, - "Toggle Switch": { - "comment": "Shortcut title for toggle switch action.", - "isCommentAutoGenerated": true, - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Toggle Switch" - } - }, - "de": { - "stringUnit": { - "state": "translated", - "value": "Schalter umschalten" - } - }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Conmutar interruptor" - } - }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Vaihda kytkimen tila" - } - }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Basculer l'interrupteur" - } - }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Attiva/Disattiva interruttore" - } - }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Bytt bryterposisjon" - } - }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Schakelaar omschakelen" - } - }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Переключить выключатель" - } - } - } } }, "version": "1.1" From b64402b68335ea128ff36ccaad481c788de5eb85 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Wed, 17 Jun 2026 17:23:42 +0200 Subject: [PATCH 26/91] l10n: switch German and French translations to informal address (du/tu) Signed-off-by: Tim Mueller-Seydlitz --- .../Supporting Files/Localizable.xcstrings | 24282 ++++++++-------- 1 file changed, 12141 insertions(+), 12141 deletions(-) diff --git a/openHAB/Supporting Files/Localizable.xcstrings b/openHAB/Supporting Files/Localizable.xcstrings index 3f80d015d..7d3434909 100644 --- a/openHAB/Supporting Files/Localizable.xcstrings +++ b/openHAB/Supporting Files/Localizable.xcstrings @@ -1,18377 +1,18377 @@ { - "sourceLanguage": "en", - "strings": { - "": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "" + "sourceLanguage" : "en", + "strings" : { + "" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "" } }, - "es": { - "stringUnit": { - "state": "new", - "value": "" + "es" : { + "stringUnit" : { + "state" : "new", + "value" : "" } }, - "fi": { - "stringUnit": { - "state": "new", - "value": "" + "fi" : { + "stringUnit" : { + "state" : "new", + "value" : "" } }, - "fr": { - "stringUnit": { - "state": "new", - "value": "" + "fr" : { + "stringUnit" : { + "state" : "new", + "value" : "" } }, - "it": { - "stringUnit": { - "state": "new", - "value": "" + "it" : { + "stringUnit" : { + "state" : "new", + "value" : "" } }, - "nb": { - "stringUnit": { - "state": "new", - "value": "" + "nb" : { + "stringUnit" : { + "state" : "new", + "value" : "" } }, - "nl": { - "stringUnit": { - "state": "new", - "value": "" + "nl" : { + "stringUnit" : { + "state" : "new", + "value" : "" } }, - "ru": { - "stringUnit": { - "state": "new", - "value": "" + "ru" : { + "stringUnit" : { + "state" : "new", + "value" : "" } } }, - "shouldTranslate": false + "shouldTranslate" : false }, - " - ": { - "comment": "A separator text displayed between the temperature value and its description in the color temperature picker row.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": " - " + " - " : { + "comment" : "A separator text displayed between the temperature value and its description in the color temperature picker row.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : " - " } }, - "en": { - "stringUnit": { - "state": "translated", - "value": " - " + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : " - " } }, - "es": { - "stringUnit": { - "state": "translated", - "value": " - " + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : " - " } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": " - " + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : " - " } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": " - " + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : " - " } }, - "it": { - "stringUnit": { - "state": "translated", - "value": " - " + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : " - " } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": " - " + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : " - " } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": " - " + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : " - " } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": " - " + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : " - " } } } }, - "__item_state_passthrough__": { - "comment": "Placeholder for a numeric or custom item state that should be displayed as-is.", - "extractionState": "extracted_with_value", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "%@" + "__item_state_passthrough__" : { + "comment" : "Placeholder for a numeric or custom item state that should be displayed as-is.", + "extractionState" : "extracted_with_value", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } }, - "en": { - "stringUnit": { - "state": "new", - "value": "%@" + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "%@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "%@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "%@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "%@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "%@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "%@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "%@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "%@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } } } }, - "/overview/": { - "comment": "A placeholder text in the text field of the default path setting.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "/overview/" + "/overview/" : { + "comment" : "A placeholder text in the text field of the default path setting.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "/overview/" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "/overview/" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "/overview/" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "/overview/" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "/overview/" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "/overview/" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "/overview/" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "/overview/" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "/overview/" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "/overview/" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "/overview/" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "/overview/" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "/overview/" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "/overview/" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "/overview/" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "/overview/" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "/overview/" } } } }, - "%@": { - "comment": "A label that shows the percentage of brightness. The value is rounded to the nearest whole number.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "%@" + "%@" : { + "comment" : "A label that shows the percentage of brightness. The value is rounded to the nearest whole number.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "%@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "%@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "%@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "%@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "%@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "%@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "%@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } } } }, - "%@ Settings": { - "comment": "The title of the settings view. The placeholder is replaced with the user's home name.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "%@-Einstellungen" + "%@ Settings" : { + "comment" : "The title of the settings view. The placeholder is replaced with the user's home name.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@-Einstellungen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "%@ Settings" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ Settings" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Ajustes de %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajustes de %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "%@-asetukset" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@-asetukset" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Paramètres %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Paramètres %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Impostazioni %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impostazioni %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "%@-innstillinger" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@-innstillinger" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "%@ Instellingen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ Instellingen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Настройки %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Настройки %@" } } } }, - "%@ value": { - "comment": "A text field displaying the current value of a setpoint.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "%@ Wert" + "%@ value" : { + "comment" : "A text field displaying the current value of a setpoint.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ Wert" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "%@ value" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ value" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Valor de %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valor de %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "%@-arvo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@-arvo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Valeur %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valeur %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Valore %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valore %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "%@-verdi" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@-verdi" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "%@ waarde" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ waarde" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Значение %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Значение %@" } } } }, - "%lld": { - "comment": "A label indicating that there are multiple commands currently being sent to the device. The number in the parentheses is the count of these commands.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "%lld" + "%lld" : { + "comment" : "A label indicating that there are multiple commands currently being sent to the device. The number in the parentheses is the count of these commands.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "%lld" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "%lld" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "%lld" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "%lld" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "%lld" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "%lld" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "%lld" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "%lld" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld" } } } }, - "%lldK": { - "comment": "A text view displaying the selected color temperature in Kelvin. The value is shown in a smaller font next to a separator text (\" - \").", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "%lldK" + "%lldK" : { + "comment" : "A text view displaying the selected color temperature in Kelvin. The value is shown in a smaller font next to a separator text (\" - \").", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lldK" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "%lldK" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lldK" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "%lldK" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lldK" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "%lldK" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lldK" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "%lldK" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lldK" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "%lldK" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lldK" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "%lldK" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lldK" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "%lldK" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lldK" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "%lldK" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lldK" } } } }, - "24-Hour Clock": { - "comment": "A toggle that allows the user to switch between 12-hour and 24-hour time formats.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "24-Stunden Uhr" + "24-Hour Clock" : { + "comment" : "A toggle that allows the user to switch between 12-hour and 24-hour time formats.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "24-Stunden Uhr" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "24-Hour Clock" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "24-Hour Clock" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Reloj de 24 horas" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reloj de 24 horas" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "24 tunnin kello" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "24 tunnin kello" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Horloge 24 heures" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Horloge 24 heures" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Orologio 24 ore" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Orologio 24 ore" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "24-timers klokke" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "24-timers klokke" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "24-uurs klok" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "24-uurs klok" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "24-часовые часы" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "24-часовые часы" } } } }, - "about_settings": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Über" + "about_settings" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Über" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "About" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "About" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Acerca de" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Acerca de" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "About" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "About" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "A propos" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "A propos" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Info" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Info" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Om" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Om" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Over" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Over" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "About" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "About" } } } }, - "Accepted Server Certificates": { - "comment": "A link to view and manage the user's accepted server certificates.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Akzeptierte Server-Zertifikate" + "Accepted Server Certificates" : { + "comment" : "A link to view and manage the user's accepted server certificates.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Akzeptierte Server-Zertifikate" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Accepted Server Certificates" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Accepted Server Certificates" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Certificados de servidor aceptados" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Certificados de servidor aceptados" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Hyväksytyt palvelinvarmenteet" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hyväksytyt palvelinvarmenteet" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Certificats serveur acceptés" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Certificats serveur acceptés" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Certificati server accettati" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Certificati server accettati" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Godkjente serversertifikater" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Godkjente serversertifikater" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Geaccepteerde servercertificaten" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geaccepteerde servercertificaten" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Принятые сертификаты сервера" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Принятые сертификаты сервера" } } } }, - "Action": { - "comment": "Parameter title for an action in app intents.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Aktion" + "Action" : { + "comment" : "Parameter title for an action in app intents.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktion" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Action" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Action" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Acción" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Acción" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Toiminto" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toiminto" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Action" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Action" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Azione" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Azione" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Handling" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Handling" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Actie" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actie" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Действие" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Действие" } } } }, - "activate": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Aktivieren" + "activate" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktivieren" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Activate" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activate" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Activar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Activate" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activate" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Activer" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activer" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Attiva" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Attiva" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Aktiver" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktiver" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Activeren" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activeren" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Activate" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activate" } } } }, - "active_url": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Aktive URL" + "active_url" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktive URL" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Active URL" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Active URL" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "URL activa" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL activa" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Active URL" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Active URL" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "URL active" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL active" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "URL Attivo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL Attivo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Aktiv URL" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktiv URL" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Actieve URL" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actieve URL" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Активный URL-адрес" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Активный URL-адрес" } } } }, - "Added: %@": { - "comment": "A label displaying the date and time when a server certificate was added to the app.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Hinzugefügt am: %@" + "Added: %@" : { + "comment" : "A label displaying the date and time when a server certificate was added to the app.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hinzugefügt am: %@" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Added: %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Added: %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Añadido: %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Añadido: %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Lisätty: %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lisätty: %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Ajouté : %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajouté : %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Aggiunto: %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aggiunto: %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Lagt til: %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lagt til: %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Toegevoegd: %@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toegevoegd: %@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Добавлено: %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Добавлено: %@" } } } }, - "Always": { - "comment": "Button title for allowing the app to use the invalid server certificate, even if it is self-signed.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Immer" + "Always" : { + "comment" : "Button title for allowing the app to use the invalid server certificate, even if it is self-signed.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Immer" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Always" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Always" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Siempre" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Siempre" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aina" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aina" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Toujours" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toujours" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Sempre" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sempre" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Alltid" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alltid" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Altijd" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Altijd" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Всегда" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Всегда" } } } }, - "Always allow WebRTC": { - "comment": "A toggle that allows the user to enable or disable WebRTC.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "WebRTC immer erlauben" + "Always allow WebRTC" : { + "comment" : "A toggle that allows the user to enable or disable WebRTC.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "WebRTC immer erlauben" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Always allow WebRTC" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Always allow WebRTC" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Permitir WebRTC siempre" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Permitir WebRTC siempre" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Salli WebRTC aina" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Salli WebRTC aina" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Toujours autoriser WebRTC" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toujours autoriser WebRTC" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Consenti sempre WebRTC" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Consenti sempre WebRTC" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Tillat alltid WebRTC" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tillat alltid WebRTC" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "WebRTC altijd toestaan" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "WebRTC altijd toestaan" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Всегда разрешать WebRTC" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Всегда разрешать WebRTC" } } } }, - "Always send credentials": { - "comment": "A toggle that lets the user specify whether to always send basic authentication credentials with requests.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Anmeldedaten immer senden" + "Always send credentials" : { + "comment" : "A toggle that lets the user specify whether to always send basic authentication credentials with requests.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anmeldedaten immer senden" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Always send credentials" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Always send credentials" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Enviar credenciales siempre" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviar credenciales siempre" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Lähetä tunnistetiedot aina" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lähetä tunnistetiedot aina" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Toujours envoyer les identifiants" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toujours envoyer les identifiants" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Invia sempre le credenziali" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Invia sempre le credenziali" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Send alltid legitimasjon" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Send alltid legitimasjon" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Inloggegevens altijd verzenden" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inloggegevens altijd verzenden" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Всегда отправлять учётные данные" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Всегда отправлять учётные данные" } } } }, - "always_allow_webrtc": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "WebRTC immer erlauben" + "always_allow_webrtc" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "WebRTC immer erlauben" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Always Allow WebRTC" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Always Allow WebRTC" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Consenti sempre WebRTC" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Consenti sempre WebRTC" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Always Allow WebRTC" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Always Allow WebRTC" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Toujours autoriser WebRTC" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toujours autoriser WebRTC" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Consenti sempre WebRTC" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Consenti sempre WebRTC" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Always Allow WebRTC" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Always Allow WebRTC" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "WebRTC altijd toestaan" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "WebRTC altijd toestaan" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Always Allow WebRTC" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Always Allow WebRTC" } } } }, - "always_send_credentials": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Anmeldedaten immer senden" + "always_send_credentials" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anmeldedaten immer senden" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Always send credentials" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Always send credentials" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Siempre enviar credenciales" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Siempre enviar credenciales" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Always send credentials" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Always send credentials" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Toujours envoyer les identifiants" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toujours envoyer les identifiants" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Invia sempre le credenziali" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Invia sempre le credenziali" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Alltid send innloggingsinformasjon" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alltid send innloggingsinformasjon" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Altijd inloggegevens versturen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Altijd inloggegevens versturen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Always send credentials" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Always send credentials" } } } }, - "Animation": { - "comment": "A section header for the animation settings in the screen saver view.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Animation" + "Animation" : { + "comment" : "A section header for the animation settings in the screen saver view.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Animation" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Animation" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Animation" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Animación" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Animación" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Animaatio" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Animaatio" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Animation" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Animation" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Animazione" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Animazione" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Animasjon" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Animasjon" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Animatie" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Animatie" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Анимация" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Анимация" } } } }, - "App Version": { - "comment": "The label and value for the application version in the \"About\" settings view.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "App-Version" + "App Version" : { + "comment" : "The label and value for the application version in the \"About\" settings view.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "App-Version" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "App Version" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "App Version" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Versión de la aplicación" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Versión de la aplicación" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Sovelluksen versio" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sovelluksen versio" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Version de l'application" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Version de l'application" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Versione app" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Versione app" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "App-versjon" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "App-versjon" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "App-versie" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "App-versie" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Версия приложения" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Версия приложения" } } } }, - "app_version": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "App-Version" + "app_version" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "App-Version" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "App Version" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "App Version" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Versión de la aplicación" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Versión de la aplicación" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "App Version" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "App Version" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Version de l'application" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Version de l'application" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Versione App" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Versione App" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Programversjon" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Programversjon" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "App Versie" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "App Versie" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "App Version" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "App Version" } } } }, - "Appearance": { - "comment": "The header for the \"Appearance\" section in the screen saver settings view.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Anzeige" + "Appearance" : { + "comment" : "The header for the \"Appearance\" section in the screen saver settings view.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anzeige" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Appearance" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Appearance" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Aspecto" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aspecto" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ulkoasu" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ulkoasu" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Apparence" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apparence" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Aspetto" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aspetto" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Utseende" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Utseende" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Weergave" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Weergave" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Внешний вид" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Внешний вид" } } } }, - "application_settings": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Anwendungseinstellungen" + "application_settings" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anwendungseinstellungen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Application Settings" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Application Settings" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Configuración de la aplicación" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración de la aplicación" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Application Settings" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Application Settings" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Paramètres de l'application" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Paramètres de l'application" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Impostazioni Applicazione" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impostazioni Applicazione" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Programinnstillinger" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Programinnstillinger" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Applicatie Instellingen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Applicatie Instellingen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Application Settings" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Application Settings" } } } }, - "Back": { - "comment": "A button label that goes back to the previous screen.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zurück" + "Back" : { + "comment" : "A button label that goes back to the previous screen.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zurück" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Back" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Back" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Atrás" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Atrás" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Takaisin" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Takaisin" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Retour" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Retour" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Indietro" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Indietro" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Tilbake" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tilbake" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Vorige" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vorige" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Назад" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Назад" } } } }, - "bonjour_discovery_disclaimer": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Bonjour-Entdeckung kann vielleicht nicht alle Server finden. Zusätzliche Server könnten im Netzwerk verfügbar sein." + "bonjour_discovery_disclaimer" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bonjour-Entdeckung kann vielleicht nicht alle Server finden. Zusätzliche Server könnten im Netzwerk verfügbar sein." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Bonjour discovery may not find all servers. Additional servers may be available on your network." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bonjour discovery may not find all servers. Additional servers may be available on your network." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Bonjour discovery may not find all servers. Additional servers may be available on your network." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bonjour discovery may not find all servers. Additional servers may be available on your network." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Bonjour discovery may not find all servers. Additional servers may be available on your network." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bonjour discovery may not find all servers. Additional servers may be available on your network." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Bonjour discovery may not find all servers. Additional servers may be available on your network." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bonjour discovery may not find all servers. Additional servers may be available on your network." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Bonjour discovery may not find all servers. Additional servers may be available on your network." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bonjour discovery may not find all servers. Additional servers may be available on your network." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Bonjour discovery may not find all servers. Additional servers may be available on your network." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bonjour discovery may not find all servers. Additional servers may be available on your network." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Bonjour discovery may not find all servers. Additional servers may be available on your network." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bonjour discovery may not find all servers. Additional servers may be available on your network." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Bonjour discovery may not find all servers. Additional servers may be available on your network." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bonjour discovery may not find all servers. Additional servers may be available on your network." } } } }, - "Brightness": { - "comment": "A section header for brightness settings.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Helligkeit" + "Brightness" : { + "comment" : "A section header for brightness settings.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Helligkeit" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Brightness" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brightness" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Brillo" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brillo" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kirkkaus" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kirkkaus" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Luminosité" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Luminosité" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Luminosità" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Luminosità" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Lysstyrke" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lysstyrke" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Helderheid" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Helderheid" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Яркость" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Яркость" } } } }, - "cache_cleared": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Cache gelöscht" + "cache_cleared" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cache gelöscht" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Cache Cleared" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cache Cleared" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Caché limpiada" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Caché limpiada" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Cache Cleared" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cache Cleared" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Cache effacé" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cache effacé" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Cache svuotata" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cache svuotata" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Cache Cleared" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cache Cleared" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Cache gewist" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cache gewist" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Cache Cleared" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cache Cleared" } } } }, - "cancel": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Abbrechen" + "cancel" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abbrechen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Cancel" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cancel" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Cancelar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cancelar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Cancel" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cancel" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Annuler" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Annuler" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Annulla" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Annulla" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Avbryt" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Avbryt" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Annuleer" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Annuleer" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Cancel" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cancel" } } } }, - "Cancel": { - "comment": "A button to cancel renaming the home.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Abbrechen" + "Cancel" : { + "comment" : "A button to cancel renaming the home.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abbrechen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Cancel" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cancel" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Cancelar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cancelar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Peruuta" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Peruuta" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Annuler" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Annuler" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Annulla" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Annulla" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Avbryt" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Avbryt" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Annuleer" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Annuleer" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Отмена" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Отмена" } } } }, - "Cancellation occurred": { - "comment": "Message displayed when a connection test is cancelled.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Abbruch aufgetreten" + "Cancellation occurred" : { + "comment" : "Message displayed when a connection test is cancelled.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abbruch aufgetreten" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Cancellation occurred" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cancellation occurred" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Se produjo una cancelación" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Se produjo una cancelación" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Peruutus tapahtui" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Peruutus tapahtui" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Annulation survenue" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Annulation survenue" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Annullamento eseguito" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Annullamento eseguito" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Avbrudd inntraff" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Avbrudd inntraff" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Annulering opgetreden" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Annulering opgetreden" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Произошла отмена" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Произошла отмена" } } } }, - "Candlelight": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Kerzenlicht" + "Candlelight" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kerzenlicht" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Candlelight" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Candlelight" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Luz de vela" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Luz de vela" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kynttilänvalo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kynttilänvalo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Lumière de bougie" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lumière de bougie" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Luce di candela" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Luce di candela" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Stearinlys" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stearinlys" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Kaarslicht" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kaarslicht" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Свет свечи" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Свет свечи" } } } }, - "Cannot connect to the server. Is it online?": { - "comment": "Error message indicating that the device cannot connect to the server.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Verbindung zum Server kann nicht hergestellt werden. Ist er online?" + "Cannot connect to the server. Is it online?" : { + "comment" : "Error message indicating that the device cannot connect to the server.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbindung zum Server kann nicht hergestellt werden. Ist er online?" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Cannot connect to the server. Is it online?" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cannot connect to the server. Is it online?" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "No se puede conectar al servidor. ¿Está en línea?" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se puede conectar al servidor. ¿Está en línea?" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Yhteyden muodostaminen palvelimeen epäonnistui. Onko se verkossa?" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Yhteyden muodostaminen palvelimeen epäonnistui. Onko se verkossa?" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Impossible de se connecter au serveur. Est-il en ligne ?" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossible de se connecter au serveur. Est-il en ligne ?" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Impossibile connettersi al server. È online?" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossibile connettersi al server. È online?" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kan ikke koble til serveren. Er den online?" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kan ikke koble til serveren. Er den online?" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Kan geen verbinding maken met de server. Is deze online?" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kan geen verbinding maken met de server. Is deze online?" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Не удаётся подключиться к серверу. Он в сети?" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Не удаётся подключиться к серверу. Он в сети?" } } } }, - "Cannot find the server. Is the URL correct?": { - "comment": "Error message when the URL cannot be resolved to a host.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Der Server kann nicht gefunden werden. Ist die URL korrekt?" + "Cannot find the server. Is the URL correct?" : { + "comment" : "Error message when the URL cannot be resolved to a host.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Der Server kann nicht gefunden werden. Ist die URL korrekt?" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Cannot find the server. Is the URL correct?" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cannot find the server. Is the URL correct?" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "No se puede encontrar el servidor. ¿Es correcta la URL?" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se puede encontrar el servidor. ¿Es correcta la URL?" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Palvelinta ei löydy. Onko URL oikein?" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Palvelinta ei löydy. Onko URL oikein?" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Impossible de trouver le serveur. L'URL est-elle correcte ?" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossible de trouver le serveur. L'URL est-elle correcte ?" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Impossibile trovare il server. L'URL è corretto?" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossibile trovare il server. L'URL è corretto?" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kan ikke finne serveren. Er URL-en riktig?" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kan ikke finne serveren. Er URL-en riktig?" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Kan de server niet vinden. Is de URL correct?" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kan de server niet vinden. Is de URL correct?" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Не удаётся найти сервер. URL верный?" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Не удаётся найти сервер. URL верный?" } } } }, - "certficate_exists": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zertifikat existiert bereits im Schlüsselbund." + "certficate_exists" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zertifikat existiert bereits im Schlüsselbund." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Certificate already exists in the keychain." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Certificate already exists in the keychain." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "El certificado ya existe en el llavero." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "El certificado ya existe en el llavero." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Certificate already exists in the keychain." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Certificate already exists in the keychain." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Le certificat existe déjà dans le trousseau." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Le certificat existe déjà dans le trousseau." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Il certificato esiste già nel portachiavi." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Il certificato esiste già nel portachiavi." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sertifikatet finnes allerede i nøkkelkjeden." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sertifikatet finnes allerede i nøkkelkjeden." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Certificaat bestaat al in de sleutelhanger." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Certificaat bestaat al in de sleutelhanger." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Certificate already exists in the keychain." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Certificate already exists in the keychain." } } } }, - "certificate_import_password": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Passwort wird für den Import benötigt." + "certificate_import_password" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Passwort wird für den Import benötigt." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Password required for import." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Password required for import." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Se requiere contraseña para importar." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Se requiere contraseña para importar." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Password required for import." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Password required for import." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Mot de passe requis pour l'importation." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mot de passe requis pour l'importation." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Password richiesta per l'importazione." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Password richiesta per l'importazione." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Passord kreves for import." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Passord kreves for import." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Wachtwoord vereist voor import." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wachtwoord vereist voor import." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Password required for import." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Password required for import." } } } }, - "certificate_import_text": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Clientzertifikat in den Schlüsselbund importieren?" + "certificate_import_text" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clientzertifikat in den Schlüsselbund importieren?" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Import client certificate into the keychain?" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Import client certificate into the keychain?" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "¿Importar certificado de cliente al llavero?" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Importar certificado de cliente al llavero?" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Import client certificate into the keychain?" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Import client certificate into the keychain?" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Importer le certificat client dans la trousseau ?" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Importer le certificat client dans la trousseau ?" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Importare il certificato client nel portachiavi?" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Importare il certificato client nel portachiavi?" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Importer klientsertifikat til nøkkelringen?" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Importer klientsertifikat til nøkkelringen?" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Client-certificaat in de sleutelhanger importeren?" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client-certificaat in de sleutelhanger importeren?" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Import client certificate into the keychain?" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Import client certificate into the keychain?" } } } }, - "certificate_import_title": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Client-Zertifikatimport" + "certificate_import_title" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client-Zertifikatimport" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Client Certificate Import" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client Certificate Import" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Importar certificado de cliente" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Importar certificado de cliente" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Client Certificate Import" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client Certificate Import" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Importation du certificat client" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Importation du certificat client" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Importazione Certificato Client" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Importazione Certificato Client" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Import av Klientsertifikat" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Import av Klientsertifikat" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Cliëntcertificaat Importeren" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cliëntcertificaat Importeren" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Client Certificate Import" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client Certificate Import" } } } }, - "Check & Clear Image Cache": { - "comment": "A button that triggers a check and clear action for the image cache.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Bilder-Cache checken & löschen" + "Check & Clear Image Cache" : { + "comment" : "A button that triggers a check and clear action for the image cache.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bilder-Cache checken & löschen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Check & Clear Image Cache" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Check & Clear Image Cache" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Comprobar y limpiar caché de imágenes" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Comprobar y limpiar caché de imágenes" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Tarkista ja tyhjennä kuvavälimuisti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tarkista ja tyhjennä kuvavälimuisti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Vérifier et vider le cache d'images" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vérifier et vider le cache d'images" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Controlla e svuota la cache immagini" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Controlla e svuota la cache immagini" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sjekk og tøm bildebuffer" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sjekk og tøm bildebuffer" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Afbeeldingscache controleren & wissen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Afbeeldingscache controleren & wissen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Проверить и очистить кэш изображений" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Проверить и очистить кэш изображений" } } } }, - "Choose color": { - "comment": "A label for the color picker button.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Farbe wählen" + "Choose color" : { + "comment" : "A label for the color picker button.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Farbe wählen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elegir color" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elegir color" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Valitse väri" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valitse väri" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Choisir une couleur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Choisir une couleur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Scegli colore" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Scegli colore" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Velg farge" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Velg farge" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Kleur kiezen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kleur kiezen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Выбрать цвет" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Выбрать цвет" } } } }, - "Clear": { - "comment": "The title of a button that clears the website cache.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Löschen" + "Clear" : { + "comment" : "The title of a button that clears the website cache.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Löschen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Clear" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clear" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Borrar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Borrar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Tyhjennä" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tyhjennä" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Effacer" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Effacer" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Cancella" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cancella" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Tøm" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tøm" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Wis" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wis" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Очистить" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Очистить" } } } }, - "Clear Web Cache": { - "comment": "A button that clears the web cache.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Web-Cache löschen" + "Clear Web Cache" : { + "comment" : "A button that clears the web cache.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Web-Cache löschen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Clear Web Cache" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clear Web Cache" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Limpiar caché web" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Limpiar caché web" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Tyhjennä verkkovälimuisti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tyhjennä verkkovälimuisti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Vider le cache web" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vider le cache web" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Svuota cache web" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Svuota cache web" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Tøm nettbuffer" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tøm nettbuffer" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Webcache wissen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Webcache wissen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Очистить веб-кэш" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Очистить веб-кэш" } } } }, - "clear_image_cache": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Bilder-Cache löschen" + "clear_image_cache" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bilder-Cache löschen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Clear Image Cache" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clear Image Cache" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Limpiar caché de imágenes" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Limpiar caché de imágenes" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Clear Image Cache" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clear Image Cache" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Vider le cache des images" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vider le cache des images" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Pulisci Cache Immagini" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pulisci Cache Immagini" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Tøm Hurtigbuffer for Bilder" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tøm Hurtigbuffer for Bilder" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Leeg de afbeeldingencache" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Leeg de afbeeldingencache" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Clear Image Cache" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clear Image Cache" } } } }, - "clear_web_cache": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Web-Cache löschen" + "clear_web_cache" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Web-Cache löschen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Clear Web Cache" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clear Web Cache" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Limpiar caché web" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Limpiar caché web" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Clear Web Cache" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clear Web Cache" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Vider le cache Web" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vider le cache Web" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Svuota memoria cache Web" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Svuota memoria cache Web" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Clear Web Cache" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clear Web Cache" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Wis webcache" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wis webcache" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Clear Web Cache" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clear Web Cache" } } } }, - "Client Certificates": { - "comment": "A link to manage client certificates.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Client-Zertifikate" + "Client Certificates" : { + "comment" : "A link to manage client certificates.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client-Zertifikate" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Client Certificates" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client Certificates" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Certificados de cliente" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Certificados de cliente" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Asiakasvarmenteet" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Asiakasvarmenteet" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Certificats client" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Certificats client" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Certificati client" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Certificati client" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Klientsertifikater" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Klientsertifikater" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Clientcertificaten" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clientcertificaten" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Клиентские сертификаты" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Клиентские сертификаты" } } } }, - "client_certificates": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Client-Zertifikate" + "client_certificates" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client-Zertifikate" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Client Certificates" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client Certificates" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Certificados de cliente" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Certificados de cliente" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Client Certificates" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client Certificates" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Certificats du client" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Certificats du client" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Certificati Client" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Certificati Client" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Klientsertifikater" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Klientsertifikater" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Cliëntcertificaat" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cliëntcertificaat" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Client Certificates" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client Certificates" } } } }, - "Clock Size: %@": { - "comment": "A label describing the percentage of the screen that is used for the clock face.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Uhrgröße: %@" + "Clock Size: %@" : { + "comment" : "A label describing the percentage of the screen that is used for the clock face.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uhrgröße: %@" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Clock Size: %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clock Size: %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Tamaño del reloj: %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tamaño del reloj: %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kellon koko: %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kellon koko: %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Taille de l'horloge : %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Taille de l'horloge : %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Dimensione orologio: %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dimensione orologio: %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Klokkestørrelse: %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Klokkestørrelse: %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Klokgrootte: %@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Klokgrootte: %@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Размер часов: %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Размер часов: %@" } } } }, - "closed": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "geschlossen" + "closed" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "geschlossen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "closed" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "closed" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "cerrado" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "cerrado" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "closed" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "closed" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "fermé" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "fermé" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "chiuso" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "chiuso" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "lukket" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "lukket" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "gesloten" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "gesloten" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "closed" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "closed" } } } }, - "Closed": { - "comment": "Displayed name for the closed contact state.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Geschlossen" + "Closed" : { + "comment" : "Displayed name for the closed contact state.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geschlossen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Cerrado" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cerrado" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Suljettu" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suljettu" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Fermé" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fermé" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Chiuso" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chiuso" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Lukket" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lukket" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Gesloten" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gesloten" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Закрыто" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Закрыто" } } } }, - "Color": { - "comment": "The title of the color picker in the widget.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Farbe" + "Color" : { + "comment" : "The title of the color picker in the widget.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Farbe" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Color" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Color" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Color" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Color" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Väri" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Väri" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Couleur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Couleur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Colore" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Colore" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Farge" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Farge" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Kleur" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kleur" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Цвет" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Цвет" } } } }, - "Color Item": { - "comment": "Display name for a color item type.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Farb-Item" + "Color Item" : { + "comment" : "Display name for a color item type.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Farb-Item" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento de color" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento de color" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Värielementti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Värielementti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Élément couleur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément couleur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elemento colore" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento colore" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Fargeelement" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fargeelement" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Kleur-item" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kleur-item" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент цвета" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент цвета" } } } }, - "Color wheel": { - "comment": "A description of a color wheel.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Farbrad" + "Color wheel" : { + "comment" : "A description of a color wheel.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Farbrad" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Rueda de colores" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rueda de colores" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Väriympyrä" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Väriympyrä" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Roue des couleurs" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Roue des couleurs" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Ruota dei colori" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ruota dei colori" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Fargehjul" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fargehjul" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Kleurwiel" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kleurwiel" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Цветовой круг" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Цветовой круг" } } } }, - "Command failed: %@": { - "comment": "Error message when sending a command to an item fails. Placeholder is the error description.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Befehlsfehler: %@" + "Command failed: %@" : { + "comment" : "Error message when sending a command to an item fails. Placeholder is the error description.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Befehlsfehler: %@" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Command failed: %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Command failed: %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Comando fallido: %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Comando fallido: %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Komento epäonnistui: %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Komento epäonnistui: %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Commande échouée : %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Commande échouée : %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Comando non riuscito: %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Comando non riuscito: %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kommando mislyktes: %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kommando mislyktes: %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Opdracht mislukt: %@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opdracht mislukt: %@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Команда не выполнена: %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Команда не выполнена: %@" } } } }, - "Command failures: %lld": { - "comment": "A label describing the number of failed commands. The argument is the number of failed commands.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Befehlsfehler: %lld" + "Command failures: %lld" : { + "comment" : "A label describing the number of failed commands. The argument is the number of failed commands.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Befehlsfehler: %lld" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Command failures: %lld" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Command failures: %lld" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Errores de comando: %lld" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Errores de comando: %lld" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Komentoviat: %lld" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Komentoviat: %lld" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Échecs de commande : %lld" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Échecs de commande : %lld" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Errori di comando: %lld" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Errori di comando: %lld" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kommandofeil: %lld" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kommandofeil: %lld" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Opdrachtfouten: %lld" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opdrachtfouten: %lld" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Сбоев команд: %lld" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Сбоев команд: %lld" } } } }, - "Command Item %@": { - "comment": "A label for the command item setting in the application settings view.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Befehl-Item %@" + "Command Item %@" : { + "comment" : "A label for the command item setting in the application settings view.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Befehl-Item %@" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Command Item %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Command Item %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento de comando %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento de comando %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Komentoelementti %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Komentoelementti %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Élément de commande %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément de commande %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elemento comando %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento comando %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kommandoelement %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kommandoelement %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Opdracht-item %@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opdracht-item %@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент команды %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент команды %@" } } } }, - "connecting": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Verbinden" + "connecting" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbinden" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Connecting" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecting" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Conectando" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectando" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Connecting" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecting" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Connexion en cours" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connexion en cours" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Connessione" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connessione" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kobler til" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kobler til" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Verbinden" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbinden" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Connecting" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecting" } } } }, - "Connecting": { - "comment": "A label displayed while waiting for a connection to the OpenHAB server.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Verbinden" + "Connecting" : { + "comment" : "A label displayed while waiting for a connection to the OpenHAB server.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbinden" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Connecting" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecting" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Conectando" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectando" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Yhdistetään" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Yhdistetään" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Connexion" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connexion" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Connetto" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connetto" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kobler til" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kobler til" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Verbinden" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbinden" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Подключение" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Подключение" } } } }, - "connecting_discovered": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Mit entdeckter URL verbinden" + "connecting_discovered" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mit entdeckter URL verbinden" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Connecting to discovered URL" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecting to discovered URL" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Conectando a URL descubierta" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectando a URL descubierta" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Connecting to discovered URL" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecting to discovered URL" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Connexion à l'URL découverte" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connexion à l'URL découverte" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Connessione all'URL trovato" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connessione all'URL trovato" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kobler til oppdaget URL" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kobler til oppdaget URL" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Verbinden naar gevonden URL" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbinden naar gevonden URL" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Connecting to discovered URL" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecting to discovered URL" } } } }, - "connecting_local": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Mit lokaler URL verbinden" + "connecting_local" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mit lokaler URL verbinden" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Connecting to local URL" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecting to local URL" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Conectando a la URL local" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectando a la URL local" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Connecting to local URL" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecting to local URL" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Connexion à l'adresse locale" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connexion à l'adresse locale" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Connessione a URL locale" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connessione a URL locale" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kobler til lokal URL" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kobler til lokal URL" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Verbinden naar lokale URL" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbinden naar lokale URL" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Connecting to local URL" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecting to local URL" } } } }, - "connecting_remote": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Mit Remote-URL verbinden" + "connecting_remote" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mit Remote-URL verbinden" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Connecting to remote URL" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecting to remote URL" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Conectando a la URL remota" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectando a la URL remota" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Connecting to remote URL" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecting to remote URL" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Connexion à l'URL distante" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connexion à l'URL distante" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Connessione a URL remoto" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connessione a URL remoto" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Connecting to remote URL" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecting to remote URL" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Verbinden met externe URL" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbinden met externe URL" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Connecting to remote URL" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecting to remote URL" } } } }, - "Connection successful": { - "comment": "Message displayed when a network connection test is successful.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Verbindung erfolgreich" + "Connection successful" : { + "comment" : "Message displayed when a network connection test is successful.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbindung erfolgreich" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Connection successful" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connection successful" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Conexión exitosa" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conexión exitosa" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Yhteys muodostettu" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Yhteys muodostettu" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Connexion réussie" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connexion réussie" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Connessione riuscita" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connessione riuscita" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Tilkobling vellykket" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tilkobling vellykket" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Verbinding geslaagd" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbinding geslaagd" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Соединение установлено" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Соединение установлено" } } } }, - "Contact Item": { - "comment": "Display name for a contact item type.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Kontakt-Item" + "Contact Item" : { + "comment" : "Display name for a contact item type.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontakt-Item" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento de contacto" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento de contacto" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kontaktielementti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontaktielementti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Élément de contact" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément de contact" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elemento contatto" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento contatto" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kontaktelement" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontaktelement" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Contact-item" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contact-item" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент контакта" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент контакта" } } } }, - "Contact State": { - "comment": "Type display name for the ContactState enum used in app intents.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Kontakt-Status" + "Contact State" : { + "comment" : "Type display name for the ContactState enum used in app intents.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontakt-Status" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Contact State" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contact State" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Estado de contacto" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Estado de contacto" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kontaktin tila" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontaktin tila" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "État du contact" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "État du contact" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Stato contatto" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stato contatto" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kontaktstatus" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontaktstatus" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Contactstatus" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contactstatus" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Состояние контакта" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Состояние контакта" } } } }, - "Cool Daylight": { - "comment": "A description for a color temperature between 6500 and 8000 Kelvin.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Kaltes Tageslicht" + "Cool Daylight" : { + "comment" : "A description for a color temperature between 6500 and 8000 Kelvin.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kaltes Tageslicht" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Cool Daylight" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cool Daylight" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Luz diurna fría" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Luz diurna fría" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Viileä päivänvalo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Viileä päivänvalo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Lumière du jour froide" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lumière du jour froide" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Luce del giorno fredda" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Luce del giorno fredda" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kjølig dagslys" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kjølig dagslys" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Koel daglicht" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Koel daglicht" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Холодный дневной свет" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Холодный дневной свет" } } } }, - "Cool White": { - "comment": "A color temperature range that maps to \"Cool White\".", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Kaltweiß" + "Cool White" : { + "comment" : "A color temperature range that maps to \"Cool White\".", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kaltweiß" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Cool White" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cool White" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Blanco frío" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blanco frío" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Viileä valkoinen" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Viileä valkoinen" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Blanc froid" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blanc froid" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Bianco freddo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bianco freddo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kjølig hvit" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kjølig hvit" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Koel wit" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Koel wit" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Холодный белый" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Холодный белый" } } } }, - "Copy": { - "comment": "Label for a button that copies the text of a text row to the clipboard.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Kopieren" + "Copy" : { + "comment" : "Label for a button that copies the text of a text row to the clipboard.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kopieren" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Copy" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copy" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Copiar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copiar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kopioi" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kopioi" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Copier" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copier" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Copia" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copia" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kopier" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kopier" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Kopieer" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kopieer" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Копировать" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Копировать" } } } }, - "copy_label": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Item-Bezeichnung kopieren" + "copy_label" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Item-Bezeichnung kopieren" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Copy item label" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copy item label" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Copiar etiqueta del ítem" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copiar etiqueta del ítem" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Copy item label" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copy item label" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Copier le label de l'item" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copier le label de l'item" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Copia etichetta Item" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copia etichetta Item" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kopier Item-etikett" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kopier Item-etikett" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Item label kopiëren" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Item label kopiëren" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Copy item label" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copy item label" } } } }, - "Crash Reporting": { - "comment": "A toggle that allows the user to enable or disable crash reporting.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Absturzberichterstattung" + "Crash Reporting" : { + "comment" : "A toggle that allows the user to enable or disable crash reporting.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Absturzberichterstattung" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Crash Reporting" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crash Reporting" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Informe de fallos" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Informe de fallos" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kaatumisraportointi" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kaatumisraportointi" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Rapport de plantage" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rapport de plantage" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Segnalazione arresti anomali" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Segnalazione arresti anomali" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Krasjirapportering" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Krasjirapportering" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Crashrapportage" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crashrapportage" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Отчёты о сбоях" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Отчёты о сбоях" } } } }, - "crash_detected": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Absturz festgestellt" + "crash_detected" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Absturz festgestellt" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Crash detected" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crash detected" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Error detectado" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error detectado" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Crash detected" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crash detected" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Crash détecté" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crash détecté" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Crash rilevato" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crash rilevato" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Krasj oppdaget" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Krasj oppdaget" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Crash gedetecteerd" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crash gedetecteerd" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Crash detected" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crash detected" } } } }, - "crash_reporting": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Absturzberichterstattung" + "crash_reporting" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Absturzberichterstattung" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Crash Reporting" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crash Reporting" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Informe de errores" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Informe de errores" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Crash Reporting" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crash Reporting" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Rapports d'erreurs" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rapports d'erreurs" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Segnalazioni di crash" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Segnalazioni di crash" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Krasjrapportering" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Krasjrapportering" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Foutenrapportering" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Foutenrapportering" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Crash Reporting" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crash Reporting" } } } }, - "crash_reporting_info": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Durch die Aktivierung der Absturzberichterstattung stimmst du zu, dass Informationen zu deinem Gerät und der Nutzung der App erfasst und mit Crashlytics (einem Unternehmen von Google) geteilt werden. Weitere Informationen findest du in unserer Datenschutzerklärung." + "crash_reporting_info" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Durch die Aktivierung der Absturzberichterstattung stimmst du zu, dass Informationen zu deinem Gerät und der Nutzung der App erfasst und mit Crashlytics (einem Unternehmen von Google) geteilt werden. Weitere Informationen findest du in unserer Datenschutzerklärung." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "By activating crash reporting you agree that device and usage information will be collected and shared with Crashlytics (a Google company). For further information view our privacy policy." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "By activating crash reporting you agree that device and usage information will be collected and shared with Crashlytics (a Google company). For further information view our privacy policy." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Al activar los informes de fallos, usted acepta que la información de uso y del dispositivo se recopilará y compartirá con Crashlytics (una empresa de Google). Para obtener más información, consulte nuestra política de privacidad." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Al activar los informes de fallos, usted acepta que la información de uso y del dispositivo se recopilará y compartirá con Crashlytics (una empresa de Google). Para obtener más información, consulte nuestra política de privacidad." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "By activating crash reporting you agree that device and usage information will be collected and shared with Crashlytics (a Google company). For further information view our privacy policy." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "By activating crash reporting you agree that device and usage information will be collected and shared with Crashlytics (a Google company). For further information view our privacy policy." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "En activant le rapport de d'erreurs, vous acceptez que les informations relatives à l'appareil et à son utilisation soient collectées et partagées avec Crashlytics (une entreprise Google). Pour plus d'informations, consultez notre politique de confidentialité." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "En activant le rapport de d'erreurs, tu acceptes que les informations relatives à l'appareil et à son utilisation soient collectées et partagées avec Crashlytics (une entreprise Google). Pour plus d'informations, consulte notre politique de confidentialité." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Attivando la segnalazione di crash si accetta che le informazioni sul dispositivo e sull'utilizzo saranno raccolte e condivise con Crashlytics (un'azienda di Google). Per ulteriori informazioni consultare la nostra informativa sulla privacy." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Attivando la segnalazione di crash si accetta che le informazioni sul dispositivo e sull'utilizzo saranno raccolte e condivise con Crashlytics (un'azienda di Google). Per ulteriori informazioni consultare la nostra informativa sulla privacy." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ved å aktivere krasjrapportering samtykker du i at enhet og bruksinformasjon samles inn og deles med Crashlytics (et Google-firma). For mer informasjon se våre retningslinjer for personvern." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ved å aktivere krasjrapportering samtykker du i at enhet og bruksinformasjon samles inn og deles med Crashlytics (et Google-firma). For mer informasjon se våre retningslinjer for personvern." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Door het activeren van crash-rapportage gaat u akkoord dat apparaat- en gebruiksinformatie zal worden verzameld en gedeeld met Crashlytics (een Google-bedrijf). Voor meer informatie bekijk ons privacybeleid." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Door het activeren van crash-rapportage gaat u akkoord dat apparaat- en gebruiksinformatie zal worden verzameld en gedeeld met Crashlytics (een Google-bedrijf). Voor meer informatie bekijk ons privacybeleid." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "By activating crash reporting you agree that device and usage information will be collected and shared with Crashlytics (a Google company). For further information view our privacy policy." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "By activating crash reporting you agree that device and usage information will be collected and shared with Crashlytics (a Google company). For further information view our privacy policy." } } } }, - "Create": { - "comment": "The text on a button that creates a new home.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Erstellen" + "Create" : { + "comment" : "The text on a button that creates a new home.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erstellen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Create" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Create" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Crear" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crear" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Luo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Luo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Créer" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Créer" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Crea" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crea" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Opprett" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opprett" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Maak aan" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Maak aan" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Создать" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Создать" } } } }, - "Date and Time": { - "comment": "Parameter title for date and time in the Set DateTime Control Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Datum und Uhrzeit" + "Date and Time" : { + "comment" : "Parameter title for date and time in the Set DateTime Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datum und Uhrzeit" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Date and Time" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Date and Time" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Fecha y hora" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fecha y hora" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Päivämäärä ja aika" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Päivämäärä ja aika" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Date et heure" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Date et heure" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Data e ora" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Data e ora" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Dato og tid" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dato og tid" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Datum en tijd" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datum en tijd" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Дата и время" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Дата и время" } } } }, - "DateTime Item": { - "comment": "Display name for a DateTimeItemEntity.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "DateTime-Item" + "DateTime Item" : { + "comment" : "Display name for a DateTimeItemEntity.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "DateTime-Item" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento DateTime" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento DateTime" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "DateTime-elementti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "DateTime-elementti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Élément DateHeure" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément DateHeure" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elemento DateTime" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento DateTime" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "DateTime-element" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "DateTime-element" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "DateTime-item" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "DateTime-item" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент DateTime" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент DateTime" } } } }, - "Daylight": { - "comment": "A descriptive label for a color temperature range.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Tageslicht" + "Daylight" : { + "comment" : "A descriptive label for a color temperature range.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tageslicht" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Daylight" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Daylight" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Luz diurna" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Luz diurna" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Päivänvalo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Päivänvalo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Lumière du jour" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lumière du jour" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Luce del giorno" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Luce del giorno" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Dagslys" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dagslys" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Daglicht" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Daglicht" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Дневной свет" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Дневной свет" } } } }, - "debug": { - "comment": "Section header text in the Debug Settings view.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Debug" + "debug" : { + "comment" : "Section header text in the Debug Settings view.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Debug" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Debug" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Debug" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Debug" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Debug" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Debug" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Debug" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Debug" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" } } } }, - "Debug": { - "comment": "Tab label for the Debug tab in the watch app.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Debug" + "Debug" : { + "comment" : "Tab label for the Debug tab in the watch app.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Debug" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Debug" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Debug" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Debug" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Debug" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Debug" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Debug" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Debug" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" } } } }, - "Decrease %@": { - "comment": "Accessibility labels and hints for the increase and decrease buttons.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "%@ verringern" + "Decrease %@" : { + "comment" : "Accessibility labels and hints for the increase and decrease buttons.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ verringern" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Decrease %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Decrease %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Disminuir %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Disminuir %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Pienennä %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pienennä %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Diminuer %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Diminuer %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Diminuisci %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Diminuisci %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Reduser %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reduser %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "%@ verlagen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ verlagen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Уменьшить %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Уменьшить %@" } } } }, - "Default Path": { - "comment": "A label displayed above the text field for the default path of the main UI.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Standardpfad" + "Default Path" : { + "comment" : "A label displayed above the text field for the default path of the main UI.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Standardpfad" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Default Path" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Default Path" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Ruta predeterminada" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ruta predeterminada" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Oletuspolku" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Oletuspolku" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Chemin par défaut" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chemin par défaut" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Percorso predefinito" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Percorso predefinito" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Standard sti" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Standard sti" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Standaardpad" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Standaardpad" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Путь по умолчанию" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Путь по умолчанию" } } } }, - "Delete": { - "comment": "The text on the \"Delete\" button in the alert that appears when you long-press a home in the list.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Löschen" + "Delete" : { + "comment" : "The text on the \"Delete\" button in the alert that appears when you long-press a home in the list.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Löschen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Delete" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delete" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Eliminar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eliminar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Poista" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Poista" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Supprimer" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Supprimer" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elimina" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elimina" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Slett" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Slett" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Verwijder" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verwijder" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Удалить" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Удалить" } } } }, - "Delete home '%@'?": { - "comment": "An alert that appears when the user swipes to delete a home. The argument is the name of the home that is about to be deleted.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zuhause „%@“ löschen?" + "Delete home '%@'?" : { + "comment" : "An alert that appears when the user swipes to delete a home. The argument is the name of the home that is about to be deleted.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zuhause „%@“ löschen?" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Delete home '%@'?" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delete home '%@'?" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "¿Eliminar hogar ‘%@’?" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Eliminar hogar ‘%@’?" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Poistetaanko koti ‘%@’?" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Poistetaanko koti ‘%@’?" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Supprimer la maison '%@' ?" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Supprimer la maison '%@' ?" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Eliminare la casa '%@'?" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eliminare la casa '%@'?" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Slette hjem ‘%@’?" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Slette hjem ‘%@’?" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Huis '%@' verwijderen?" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Huis '%@' verwijderen?" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Удалить дом «%@»?" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Удалить дом «%@»?" } } } }, - "Demo Mode": { - "comment": "A toggle that enables or disables demo mode.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Demomodus" + "Demo Mode" : { + "comment" : "A toggle that enables or disables demo mode.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Demomodus" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Demo Mode" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Demo Mode" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Modo demo" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modo demo" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Esittelytila" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Esittelytila" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Mode démo" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mode démo" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Modalità demo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modalità demo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Demomodus" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Demomodus" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Demomodus" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Demomodus" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Демо-режим" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Демо-режим" } } } }, - "demo_mode": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Demomodus" + "demo_mode" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Demomodus" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Demo mode" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Demo mode" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Modo de demostración" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modo de demostración" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Demo mode" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Demo mode" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Mode démo" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mode démo" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Modalità demo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modalità demo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Demomodus" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Demomodus" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Demomodus" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Demomodus" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Demo mode" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Demo mode" } } } }, - "Deny": { - "comment": "A button label that denies access to a website.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Ablehnen" + "Deny" : { + "comment" : "A button label that denies access to a website.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ablehnen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Deny" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Deny" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Denegar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Denegar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Hylkää" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hylkää" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Refuser" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Refuser" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Non consentire" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Non consentire" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Avslå" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Avslå" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Weiger" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Weiger" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Отклонить" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Отклонить" } } } }, - "Dim Level: %@": { - "comment": "A label displaying the current dim level of the screen saver. The value is shown as a percentage.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Dimmwert: %@" + "Dim Level: %@" : { + "comment" : "A label displaying the current dim level of the screen saver. The value is shown as a percentage.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dimmwert: %@" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Dim Level: %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dim Level: %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Nivel de atenuación: %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nivel de atenuación: %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Himmennystaso: %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Himmennystaso: %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Niveau de gradation : %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Niveau de gradation : %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Livello dimmeraggio: %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Livello dimmeraggio: %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Dimmenivå: %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dimmenivå: %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Dimniveau: %@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dimniveau: %@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Уровень затемнения: %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Уровень затемнения: %@" } } } }, - "Dimmer or Roller Item": { - "comment": "Display name for a dimmer or roller item type.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Dimmer- oder Rolladen-Item" + "Dimmer or Roller Item" : { + "comment" : "Display name for a dimmer or roller item type.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dimmer- oder Rolladen-Item" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Dimmer or Roller Item" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dimmer or Roller Item" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento regulador o persiana" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento regulador o persiana" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Himmennin- tai rullakaihdinelementti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Himmennin- tai rullakaihdinelementti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Élément variateur ou volet" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément variateur ou volet" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elemento dimmer o tapparella" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento dimmer o tapparella" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Dimmer- eller rullelukkelement" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dimmer- eller rullelukkelement" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Dimmer- of rolluik-item" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dimmer- of rolluik-item" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент диммера или ролеты" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент диммера или ролеты" } } } }, - "Disable Idle Timeout": { - "comment": "A toggle that allows the user to disable the idle timeout feature.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Ruhezustand-Timeout deaktivieren" + "Disable Idle Timeout" : { + "comment" : "A toggle that allows the user to disable the idle timeout feature.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ruhezustand-Timeout deaktivieren" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Disable Idle Timeout" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Disable Idle Timeout" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Desactivar tiempo de espera en reposo" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Desactivar tiempo de espera en reposo" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Poista käytöstä lepoajan aikakatkaisu" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Poista käytöstä lepoajan aikakatkaisu" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Désactiver le délai d'inactivité" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Désactiver le délai d'inactivité" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Disabilita timeout inattività" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Disabilita timeout inattività" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Deaktiver tidsavbrudd ved inaktivitet" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Deaktiver tidsavbrudd ved inaktivitet" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Time-out bij inactiviteit uitschakelen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Time-out bij inactiviteit uitschakelen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Отключить тайм-аут простоя" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Отключить тайм-аут простоя" } } } }, - "disable_idle_timeout": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Bildschirm-Timeout deaktivieren" + "disable_idle_timeout" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bildschirm-Timeout deaktivieren" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Disable Idle Timeout" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Disable Idle Timeout" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Desactivar tiempo de espera de inactividad" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Desactivar tiempo de espera de inactividad" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Disable Idle Timeout" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Disable Idle Timeout" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Désactiver le délai d'inactivité" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Désactiver le délai d'inactivité" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Disabilita Timeout Inattività" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Disabilita Timeout Inattività" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Deaktiver Tidsavbrudd for Inaktivitet" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Deaktiver Tidsavbrudd for Inaktivitet" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Schermtimeout uitschakelen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schermtimeout uitschakelen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Disable Idle Timeout" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Disable Idle Timeout" } } } }, - "discovered_servers": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Gefundene Server" + "discovered_servers" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gefundene Server" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Discovered Servers" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Discovered Servers" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Discovered Servers" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Discovered Servers" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Discovered Servers" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Discovered Servers" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Discovered Servers" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Discovered Servers" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Discovered Servers" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Discovered Servers" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Discovered Servers" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Discovered Servers" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Discovered Servers" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Discovered Servers" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Discovered Servers" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Discovered Servers" } } } }, - "discovering_oh": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "OpenHAB entdecken" + "discovering_oh" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "OpenHAB entdecken" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Discovering openHAB" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Discovering openHAB" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Descubriendo openHAB" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Descubriendo openHAB" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Discovering openHAB" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Discovering openHAB" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Découvrir openHAB" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Découvrir openHAB" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Cercando openHAB" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cercando openHAB" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Oppdager openHAB" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Oppdager openHAB" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Bezig met zoeken naar openHAB" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bezig met zoeken naar openHAB" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Discovering openHAB" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Discovering openHAB" } } } }, - "discovering_servers": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "openHAB Server werden entdeckt …" + "discovering_servers" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Server werden entdeckt …" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Discovering openHAB servers…" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Discovering openHAB servers…" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Descubriendo servidores openHAB…" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Descubriendo servidores openHAB…" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Etsitään openHAB-palvelimia…" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Etsitään openHAB-palvelimia…" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Recherche des serveurs openHAB…" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Recherche des serveurs openHAB…" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Ricerca server openHAB in corso…" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ricerca server openHAB in corso…" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Finner openHAB-servere…" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Finner openHAB-servere…" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "openHAB-servers worden ontdekt…" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB-servers worden ontdekt…" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Обнаружение серверов openHAB…" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Обнаружение серверов openHAB…" } } } }, - "dismiss": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Verwerfen" + "dismiss" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verwerfen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Dismiss" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dismiss" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Descartar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Descartar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Dismiss" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dismiss" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Ignorer" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ignorer" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Ignora" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ignora" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Avvis" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Avvis" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Afwijzen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Afwijzen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Dismiss" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dismiss" } } } }, - "Done": { - "comment": "A button that dismisses a sheet.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Fertig" + "Done" : { + "comment" : "A button that dismisses a sheet.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fertig" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Hecho" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hecho" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Valmis" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valmis" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Terminé" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terminé" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Fine" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fine" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ferdig" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ferdig" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Gereed" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gereed" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Готово" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Готово" } } } }, - "Drag to change hue and saturation": { - "comment": "A hint that describes how to interact with a color wheel.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Ziehen, um Farbton und Sättigung zu ändern" + "Drag to change hue and saturation" : { + "comment" : "A hint that describes how to interact with a color wheel.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ziehen, um Farbton und Sättigung zu ändern" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Arrastrar para cambiar el tono y la saturación" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Arrastrar para cambiar el tono y la saturación" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Vedä muuttaaksesi värisävyä ja kylläisyyttä" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vedä muuttaaksesi värisävyä ja kylläisyyttä" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Faire glisser pour modifier la teinte et la saturation" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Faire glisser pour modifier la teinte et la saturation" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Trascina per modificare tonalità e saturazione" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trascina per modificare tonalità e saturazione" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Dra for å endre fargetone og metning" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dra for å endre fargetone og metning" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Sleep om tint en verzadiging te wijzigen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sleep om tint en verzadiging te wijzigen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Перетащите для изменения оттенка и насыщенности" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Перетащите для изменения оттенка и насыщенности" } } } }, - "empty": { - "comment": "local or remote URL: empty", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "leer" + "empty" : { + "comment" : "local or remote URL: empty", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "leer" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "empty" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "empty" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "vacío" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "vacío" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "tyhjä" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "tyhjä" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "vide" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "vide" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "vuoto" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "vuoto" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "tom" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "tom" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "leeg" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "leeg" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "пусто" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "пусто" } } } }, - "empty_sitemap": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "openHAB lieferte eine leere Sitemap-Liste" + "empty_sitemap" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB lieferte eine leere Sitemap-Liste" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "openHAB returned empty sitemap list" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB returned empty sitemap list" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "lista de mapas web de openHAB vacía" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "lista de mapas web de openHAB vacía" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "openHAB returned empty sitemap list" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB returned empty sitemap list" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "openHAB a renvoyé une liste vide de sitemaps" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB a renvoyé une liste vide de sitemaps" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "openHAB ha ritornato una lista vuota di sitemap" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB ha ritornato una lista vuota di sitemap" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "openHAB returnerte en tom Sitemap-liste" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB returnerte en tom Sitemap-liste" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "openHAB heeft een lege Sitemap geladen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB heeft een lege Sitemap geladen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "openHAB returned empty sitemap list" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB returned empty sitemap list" } } } }, - "Enable Dimming": { - "comment": "A toggle that enables or disables automatic brightness dimming.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Dimmen aktivieren" + "Enable Dimming" : { + "comment" : "A toggle that enables or disables automatic brightness dimming.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dimmen aktivieren" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Enable Dimming" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enable Dimming" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Activar atenuación" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activar atenuación" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ota himmennys käyttöön" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ota himmennys käyttöön" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Activer la gradation" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activer la gradation" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Abilita dimmeraggio" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abilita dimmeraggio" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Aktiver dimming" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktiver dimming" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Dimmen inschakelen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dimmen inschakelen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Включить затемнение" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Включить затемнение" } } } }, - "Enable Screen Saver": { - "comment": "A toggle switch that enables or disables the screen saver feature.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Bildschirmschoner aktivieren" + "Enable Screen Saver" : { + "comment" : "A toggle switch that enables or disables the screen saver feature.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bildschirmschoner aktivieren" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Enable Screen Saver" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enable Screen Saver" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Activar salvapantallas" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activar salvapantallas" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ota näytönsäästäjä käyttöön" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ota näytönsäästäjä käyttöön" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Activer l'économiseur d'écran" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activer l'économiseur d'écran" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Abilita salvaschermo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abilita salvaschermo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Aktiver skjermsparer" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktiver skjermsparer" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Schermbeveiliging inschakelen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schermbeveiliging inschakelen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Включить заставку экрана" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Включить заставку экрана" } } } }, - "Enter a name for the new home": { - "comment": "A prompt that appears when creating a new home, asking for a name.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Name für das neue Zuhause eingeben" + "Enter a name for the new home" : { + "comment" : "A prompt that appears when creating a new home, asking for a name.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Name für das neue Zuhause eingeben" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Enter a name for the new home" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enter a name for the new home" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Introducir un nombre para el nuevo hogar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Introducir un nombre para el nuevo hogar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Syötä nimi uudelle kodille" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Syötä nimi uudelle kodille" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Saisissez un nom pour la nouvelle maison" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Saisissez un nom pour la nouvelle maison" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Inserisci un nome per la nuova casa" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inserisci un nome per la nuova casa" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi et navn for det nye hjemmet" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi et navn for det nye hjemmet" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Voer een naam in voor het nieuwe huis" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Voer een naam in voor het nieuwe huis" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Введите имя для нового дома" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Введите имя для нового дома" } } } }, - "Enter a new name for the home '%@'": { - "comment": "Alerts for renaming or deleting a home.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Neuer Name für das Zuhause „%@“ eingeben" + "Enter a new name for the home '%@'" : { + "comment" : "Alerts for renaming or deleting a home.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Neuer Name für das Zuhause „%@“ eingeben" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Enter a new name for the home '%@'" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enter a new name for the home '%@'" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Introducir un nuevo nombre para el hogar ‘%@’" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Introducir un nuevo nombre para el hogar ‘%@’" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Syötä uusi nimi kodille ‘%@’" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Syötä uusi nimi kodille ‘%@’" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Saisissez un nouveau nom pour la maison '%@'" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Saisissez un nouveau nom pour la maison '%@'" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Inserisci un nuovo nome per la casa '%@'" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inserisci un nuovo nome per la casa '%@'" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi et nytt navn for hjemmet ‘%@’" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi et nytt navn for hjemmet ‘%@’" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Voer een nieuwe naam in voor het huis '%@'" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Voer een nieuwe naam in voor het huis '%@'" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Введите новое имя для дома «%@»" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Введите новое имя для дома «%@»" } } } }, - "Enter new value": { - "comment": "The title of an alert that appears when the user taps the \"Add Time\" button in the example.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Neuen Wert eingeben" + "Enter new value" : { + "comment" : "The title of an alert that appears when the user taps the \"Add Time\" button in the example.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Neuen Wert eingeben" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Enter new value" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enter new value" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Introducir nuevo valor" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Introducir nuevo valor" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Syötä uusi arvo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Syötä uusi arvo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Saisir une nouvelle valeur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Saisir une nouvelle valeur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Inserisci nuovo valore" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inserisci nuovo valore" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi ny verdi" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi ny verdi" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Nieuwe waarde invoeren" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nieuwe waarde invoeren" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Введите новое значение" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Введите новое значение" } } } }, - "Enter password for server": { - "comment": "A prompt instructing the user to enter a password for the server. Try to keep shortish.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Server-Passwort eingeben" + "Enter password for server" : { + "comment" : "A prompt instructing the user to enter a password for the server. Try to keep shortish.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Server-Passwort eingeben" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Enter password for server" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enter password for server" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Introduce la contraseña del servidor" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Introduce la contraseña del servidor" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Anna palvelimen salasana" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anna palvelimen salasana" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Entrez le mot de passe du serveur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Entrez le mot de passe du serveur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Inserisci la password del server" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inserisci la password del server" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Skriv inn passord for server" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skriv inn passord for server" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Voer wachtwoord voor server in" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Voer wachtwoord voor server in" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Введите пароль для сервера" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Введите пароль для сервера" } } } }, - "Enter text": { - "comment": "A placeholder text for a text input field.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Text eingeben" + "Enter text" : { + "comment" : "A placeholder text for a text input field.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Text eingeben" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Enter text" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enter text" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Introducir texto" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Introducir texto" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Syötä teksti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Syötä teksti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Saisir du texte" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Saisir du texte" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Inserisci testo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inserisci testo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi tekst" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi tekst" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Tekst invoeren" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tekst invoeren" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Введите текст" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Введите текст" } } } }, - "Enter URL of remote server": { - "comment": "A message displayed when the URL field in the connection settings is empty.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "URL des entfernten Servers eingeben" + "Enter URL of remote server" : { + "comment" : "A message displayed when the URL field in the connection settings is empty.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL des entfernten Servers eingeben" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Enter URL of remote server" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enter URL of remote server" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Introducir URL del servidor remoto" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Introducir URL del servidor remoto" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Syötä etäpalvelimen URL" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Syötä etäpalvelimen URL" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Saisir l'URL du serveur distant" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Saisir l'URL du serveur distant" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Inserisci URL del server remoto" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inserisci URL del server remoto" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi URL til fjernserver" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi URL til fjernserver" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "URL van externe server invoeren" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL van externe server invoeren" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Введите URL удалённого сервера" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Введите URL удалённого сервера" } } } }, - "Enter username for server, if required": { - "comment": "A message displayed below the username field, instructing the user to enter a username if one is required by the server. Try to keep shortish.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Server-Benutzername eingeben, falls erforderlich" + "Enter username for server, if required" : { + "comment" : "A message displayed below the username field, instructing the user to enter a username if one is required by the server. Try to keep shortish.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Server-Benutzername eingeben, falls erforderlich" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Enter username for server, if required" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enter username for server, if required" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Introduce el nombre de usuario del servidor, si es necesario" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Introduce el nombre de usuario del servidor, si es necesario" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Anna palvelimen käyttäjänimi tarvittaessa" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anna palvelimen käyttäjänimi tarvittaessa" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Entrez le nom d'utilisateur du serveur, si requis" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Entrez le nom d'utilisateur du serveur, si requis" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Inserisci il nome utente del server, se richiesto" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inserisci il nome utente del server, se richiesto" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Skriv inn brukernavn for server, om nødvendig" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skriv inn brukernavn for server, om nødvendig" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Voer gebruikersnaam voor server in, indien vereist" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Voer gebruikersnaam voor server in, indien vereist" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Введите имя пользователя для сервера, если требуется" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Введите имя пользователя для сервера, если требуется" } } } }, - "Error": { - "comment": "The title of an alert that appears when an error occurs.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Fehler" + "Error" : { + "comment" : "The title of an alert that appears when an error occurs.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fehler" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Error" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Error" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Virhe" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Virhe" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Erreur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erreur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Errore" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Errore" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Feil" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Feil" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Fout" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fout" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Ошибка" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ошибка" } } } }, - "Fade Duration: %@ s": { - "comment": "A slider that lets the user adjust the duration of the fade-in animation when the screen saver is activated. The argument is the string “%.1f”.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Fade-Dauer: %@ s" + "Fade Duration: %@ s" : { + "comment" : "A slider that lets the user adjust the duration of the fade-in animation when the screen saver is activated. The argument is the string “%.1f”.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fade-Dauer: %@ s" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Fade Duration: %@ s" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fade Duration: %@ s" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Duración del fundido: %@ s" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Duración del fundido: %@ s" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Häivytyksen kesto: %@ s" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Häivytyksen kesto: %@ s" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Durée de fondu : %@ s" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Durée de fondu : %@ s" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Durata dissolvenza: %@ s" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Durata dissolvenza: %@ s" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Falming varighet: %@ s" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Falming varighet: %@ s" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Vervaagtijd: %@ s" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vervaagtijd: %@ s" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Длительность затухания: %@ с" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Длительность затухания: %@ с" } } } }, - "Fast Forward": { - "comment": "Display name for the fast forward action in the PlayerAction enum.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Schneller Vorlauf" + "Fast Forward" : { + "comment" : "Display name for the fast forward action in the PlayerAction enum.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schneller Vorlauf" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Fast Forward" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fast Forward" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Avance rápido" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Avance rápido" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Pikakelaus eteenpäin" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pikakelaus eteenpäin" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Avance rapide" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Avance rapide" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Avanzamento rapido" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Avanzamento rapido" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Spol frem" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Spol frem" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Vooruitspoelen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vooruitspoelen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Перемотка вперёд" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Перемотка вперёд" } } } }, - "Font": { - "comment": "A label for the font picker in the screen saver settings view.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Schrift" + "Font" : { + "comment" : "A label for the font picker in the screen saver settings view.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schrift" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Font" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Font" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Tipo de letra" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tipo de letra" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Fontti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fontti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Police" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Police" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Font" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Font" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Skrift" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skrift" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Lettertype" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lettertype" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Шрифт" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Шрифт" } } } }, - "Font Size": { - "comment": "A section header that indicates the font size settings.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Schriftgröße" + "Font Size" : { + "comment" : "A section header that indicates the font size settings.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schriftgröße" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Font Size" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Font Size" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Tamaño de letra" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tamaño de letra" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Fonttikoko" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fonttikoko" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Taille de la police" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Taille de la police" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Dimensioni font" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dimensioni font" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Skriftstørrelse" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skriftstørrelse" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Lettergrootte" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lettergrootte" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Размер шрифта" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Размер шрифта" } } } }, - "Foo": { - "comment": "A label for a text field where the user can input \"Foo\".", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Foo" + "Foo" : { + "comment" : "A label for a text field where the user can input \"Foo\".", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Foo" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Foo" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Foo" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Foo" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Foo" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Foo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Foo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Foo" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Foo" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Foo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Foo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Foo" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Foo" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Foo" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Foo" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Foo" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Foo" } } } }, - "For Shortcuts to work across multiple devices, each home must have the same name on every device.": { - "comment": "Instruction shown in Manage Homes explaining that home names must match across devices for Shortcuts to work.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Damit Kurzbefehle geräteübergreifend funktionieren, muss jedes Zuhause auf jedem Gerät denselben Namen haben." + "For Shortcuts to work across multiple devices, each home must have the same name on every device." : { + "comment" : "Instruction shown in Manage Homes explaining that home names must match across devices for Shortcuts to work.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Damit Kurzbefehle geräteübergreifend funktionieren, muss jedes Zuhause auf jedem Gerät denselben Namen haben." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "For Shortcuts to work across multiple devices, each home must have the same name on every device." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "For Shortcuts to work across multiple devices, each home must have the same name on every device." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Para que los atajos funcionen en varios dispositivos, cada hogar debe tener el mismo nombre en cada dispositivo." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Para que los atajos funcionen en varios dispositivos, cada hogar debe tener el mismo nombre en cada dispositivo." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Jotta pikakuvakkeet toimisivat useilla laitteilla, jokaisella kodilla täytyy olla sama nimi joka laitteella." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jotta pikakuvakkeet toimisivat useilla laitteilla, jokaisella kodilla täytyy olla sama nimi joka laitteella." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Pour que les raccourcis fonctionnent sur plusieurs appareils, chaque maison doit avoir le même nom sur chaque appareil." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pour que les raccourcis fonctionnent sur plusieurs appareils, chaque maison doit avoir le même nom sur chaque appareil." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Per far funzionare i comandi rapidi su più dispositivi, ogni casa deve avere lo stesso nome su ogni dispositivo." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Per far funzionare i comandi rapidi su più dispositivi, ogni casa deve avere lo stesso nome su ogni dispositivo." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "For at snarveier skal fungere på tvers av enheter, må hvert hjem ha samme navn på hver enhet." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "For at snarveier skal fungere på tvers av enheter, må hvert hjem ha samme navn på hver enhet." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Om snelkoppelingen op meerdere apparaten te laten werken, moet elk thuis dezelfde naam hebben op elk apparaat." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Om snelkoppelingen op meerdere apparaten te laten werken, moet elk thuis dezelfde naam hebben op elk apparaat." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Чтобы быстрые команды работали на нескольких устройствах, у каждого дома должно быть одинаковое название на каждом устройстве." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Чтобы быстрые команды работали на нескольких устройствах, у каждого дома должно быть одинаковое название на каждом устройстве." } } } }, - "Get ${itemEntity} State": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "${itemEntity}-Status erhalten" + "Get ${itemEntity} State" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "${itemEntity}-Status erhalten" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Obtener estado de ${itemEntity}" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Obtener estado de ${itemEntity}" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Hae ${itemEntity}-tila" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hae ${itemEntity}-tila" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Obtenir l'état de ${itemEntity}" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Obtenir l'état de ${itemEntity}" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Ottieni lo stato di ${itemEntity}" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ottieni lo stato di ${itemEntity}" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Hent status for ${itemEntity}" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hent status for ${itemEntity}" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Status van ${itemEntity} ophalen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Status van ${itemEntity} ophalen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Получить состояние ${itemEntity}" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Получить состояние ${itemEntity}" } } } }, - "Get Item State": { - "comment": "Title of the Get Item State app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Status eines Items erhalten" + "Get Item State" : { + "comment" : "Title of the Get Item State app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Status eines Items erhalten" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Get Item State" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Get Item State" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Obtener estado del ítem" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Obtener estado del ítem" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Hae elementin tila" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hae elementin tila" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Récupérer l'état de l'item" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Récupérer l'état de l'item" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Leggi lo stato dell'Item" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Leggi lo stato dell'Item" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Få Item-tilstand" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Få Item-tilstand" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Haal item state op" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Haal item state op" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Получить состояние элемента" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Получить состояние элемента" } } } }, - "habpanel": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "HABPanel" + "habpanel" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "HABPanel" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "HABPanel" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "HABPanel" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "HABPanel" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "HABPanel" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "HABPanel" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "HABPanel" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "HABPanel" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "HABPanel" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "HABPanel" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "HABPanel" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "HABPanel" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "HABPanel" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "HABPanel" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "HABPanel" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "HABPanel" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "HABPanel" } } } }, - "Hide Status Bar": { - "comment": "A toggle that allows the user to hide the status bar in the app.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Statusleiste ausblenden" + "Hide Status Bar" : { + "comment" : "A toggle that allows the user to hide the status bar in the app.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Statusleiste ausblenden" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Hide Status Bar" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hide Status Bar" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Ocultar barra de estado" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ocultar barra de estado" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Piilota tilapalkki" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Piilota tilapalkki" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Masquer la barre d'état" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Masquer la barre d'état" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Nascondi barra di stato" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nascondi barra di stato" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Skjul statuslinje" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skjul statuslinje" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Statusbalk verbergen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Statusbalk verbergen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Скрыть строку состояния" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Скрыть строку состояния" } } } }, - "Home": { - "comment": "The name of the \"Home\" menu item.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zuhause" + "Home" : { + "comment" : "The name of the \"Home\" menu item.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zuhause" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Home" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Home" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Casa" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Casa" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Koti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Koti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Domicile" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Domicile" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Casa" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Casa" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Hjem" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hjem" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Thuis" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Thuis" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Дом" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Дом" } } } }, - "Icon Type": { - "comment": "A label describing the option to change the icon type.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Icon-Typ" + "Icon Type" : { + "comment" : "A label describing the option to change the icon type.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Icon-Typ" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Icon Type" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Icon Type" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Tipo de icono" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tipo de icono" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kuvaketyyppi" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kuvaketyyppi" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Type d'icône" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Type d'icône" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Tipo icona" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tipo icona" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ikonstil" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ikonstil" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Pictogramtype" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pictogramtype" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Тип значка" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Тип значка" } } } }, - "icon_type": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Icon-Typ" + "icon_type" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Icon-Typ" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Icon Type" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Icon Type" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Tipo de icono" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tipo de icono" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Icon Type" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Icon Type" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Type d'icône" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Type d'icône" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Tipo di icona" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tipo di icona" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ikontype" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ikontype" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Icoontype" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Icoontype" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Icon Type" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Icon Type" } } } }, - "Idle Interval: %lld s": { - "comment": "A stepper that allows the user to set the number of seconds after which the screen saver will activate if no movement is detected. The value shown is the current idle interval in seconds.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Ruhezustand-Intervall: %lld s" + "Idle Interval: %lld s" : { + "comment" : "A stepper that allows the user to set the number of seconds after which the screen saver will activate if no movement is detected. The value shown is the current idle interval in seconds.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ruhezustand-Intervall: %lld s" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Idle Interval: %lld s" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Idle Interval: %lld s" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Intervalo de reposo: %lld s" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Intervalo de reposo: %lld s" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Lepoväli: %lld s" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lepoväli: %lld s" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Intervalle d'inactivité : %lld s" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Intervalle d'inactivité : %lld s" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Intervallo inattività: %lld s" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Intervallo inattività: %lld s" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Inaktivitetsintervall: %lld s" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inaktivitetsintervall: %lld s" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Inactiviteitsinterval: %lld s" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inactiviteitsinterval: %lld s" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Интервал простоя: %lld с" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Интервал простоя: %lld с" } } } }, - "Ignore SSL certificates": { - "comment": "A toggle that lets the user ignore SSL certificate warnings.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "SSL-Zertifikate ignorieren" + "Ignore SSL certificates" : { + "comment" : "A toggle that lets the user ignore SSL certificate warnings.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL-Zertifikate ignorieren" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Ignore SSL certificates" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ignore SSL certificates" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Ignorar certificados SSL" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ignorar certificados SSL" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ohita SSL-varmenteet" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ohita SSL-varmenteet" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Ignorer les certificats SSL" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ignorer les certificats SSL" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Ignora certificati SSL" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ignora certificati SSL" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ignorer SSL-sertifikater" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ignorer SSL-sertifikater" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "SSL-certificaten negeren" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL-certificaten negeren" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Игнорировать SSL-сертификаты" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Игнорировать SSL-сертификаты" } } } }, - "ignore_ssl_certificates": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "SSL-Zertifikate ignorieren" + "ignore_ssl_certificates" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL-Zertifikate ignorieren" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Ignore SSL Certificates" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ignore SSL Certificates" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Ignorar certificados SSL" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ignorar certificados SSL" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ignore SSL Certificates" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ignore SSL Certificates" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Ignorer les certificats SSL" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ignorer les certificats SSL" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Ignora certificati SSL" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ignora certificati SSL" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ignorer SSL-sertifikater" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ignorer SSL-sertifikater" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Negeer SSL-certificaten" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Negeer SSL-certificaten" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Игнорировать SSL-сертификаты" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Игнорировать SSL-сертификаты" } } } }, - "Image Cache": { - "comment": "The title of an alert that shows the result of checking and clearing the image cache.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Bilder-Cache" + "Image Cache" : { + "comment" : "The title of an alert that shows the result of checking and clearing the image cache.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bilder-Cache" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Image Cache" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Image Cache" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Caché de imágenes" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Caché de imágenes" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kuvavälimuisti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kuvavälimuisti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Cache d'images" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cache d'images" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Cache immagini" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cache immagini" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Bildebuffer" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bildebuffer" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Afbeeldingscache" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Afbeeldingscache" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Кэш изображений" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Кэш изображений" } } } }, - "Import": { - "comment": "\"Import\" button title.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Importieren" + "Import" : { + "comment" : "\"Import\" button title.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Importieren" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Import" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Import" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Importar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Importar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Tuo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tuo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Importer" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Importer" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Importa" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Importa" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Importer" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Importer" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Importeer" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Importeer" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Импорт" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Импорт" } } } }, - "Increase %@": { - "comment": "A button that increases the value of a setpoint. The argument is the name of the setpoint.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "%@ erhöhen" + "Increase %@" : { + "comment" : "A button that increases the value of a setpoint. The argument is the name of the setpoint.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ erhöhen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Increase %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Increase %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Aumentar %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aumentar %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Suurenna %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suurenna %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Augmenter %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Augmenter %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Aumenta %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aumenta %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Øk %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Øk %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "%@ verhogen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ verhogen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Увеличить %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Увеличить %@" } } } }, - "info": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Info" + "info" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Info" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Info" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Info" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Información" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Información" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Info" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Info" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Infos" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Infos" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Informazioni" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Informazioni" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Informasjon" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Informasjon" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Info" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Info" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Info" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Info" } } } }, - "Invalid value %lld for %@ (0-100)": { - "comment": "Error message when a dimmer or roller shutter value is out of range. First placeholder is the invalid value, second is the item name.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Ungültiger Wert %lld für %@ (0-100)" + "Invalid value %lld for %@ (0-100)" : { + "comment" : "Error message when a dimmer or roller shutter value is out of range. First placeholder is the invalid value, second is the item name.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ungültiger Wert %lld für %@ (0-100)" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Invalid value %lld for %@ (0-100)" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Invalid value %lld for %@ (0-100)" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Valor no válido %lld para %@ (0-100)" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valor no válido %lld para %@ (0-100)" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Virheellinen arvo %lld kohteelle %@ (0–100)" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Virheellinen arvo %lld kohteelle %@ (0–100)" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Valeur %lld invalide pour %@ (0-100)" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valeur %lld invalide pour %@ (0-100)" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Valore non valido %lld per %@ (0-100)" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valore non valido %lld per %@ (0-100)" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ugyldig verdi %lld for %@ (0-100)" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ugyldig verdi %lld for %@ (0-100)" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Ongeldige waarde %lld voor %@ (0-100)" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ongeldige waarde %lld voor %@ (0-100)" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Недопустимое значение %lld для %@ (0–100)" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Недопустимое значение %lld для %@ (0–100)" } } } }, - "Invalid value: %@ for %@ must be HSB (0-360,0-100,0-100)": { - "comment": "Error message when a color value is not valid HSB. First placeholder is the invalid value, second is the item name.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Ungültiger Wert: %@ für %@ muss HSB sein (0-360,0-100,0-100)" + "Invalid value: %@ for %@ must be HSB (0-360,0-100,0-100)" : { + "comment" : "Error message when a color value is not valid HSB. First placeholder is the invalid value, second is the item name.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ungültiger Wert: %@ für %@ muss HSB sein (0-360,0-100,0-100)" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Invalid value: %@ for %@ must be HSB (0-360,0-100,0-100)" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Invalid value: %@ for %@ must be HSB (0-360,0-100,0-100)" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Valor no válido: %@ para %@ debe ser HSB (0-360,0-100,0-100)" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valor no válido: %@ para %@ debe ser HSB (0-360,0-100,0-100)" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Virheellinen arvo: %@ kohteelle %@ täytyy olla HSB (0–360, 0–100, 0–100)" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Virheellinen arvo: %@ kohteelle %@ täytyy olla HSB (0–360, 0–100, 0–100)" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Valeur invalide : %@ pour %@ doit être HSB (0-360,0-100,0-100)" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valeur invalide : %@ pour %@ doit être HSB (0-360,0-100,0-100)" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Valore non valido: %@ per %@ deve essere HSB (0-360,0-100,0-100)" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valore non valido: %@ per %@ deve essere HSB (0-360,0-100,0-100)" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ugyldig verdi: %@ for %@ må være HSB (0-360,0-100,0-100)" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ugyldig verdi: %@ for %@ må være HSB (0-360,0-100,0-100)" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Ongeldige waarde: %@ voor %@ moet HSB zijn (0-360,0-100,0-100)" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ongeldige waarde: %@ voor %@ moet HSB zijn (0-360,0-100,0-100)" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Недопустимое значение: %@ для %@ должно быть HSB (0–360, 0–100, 0–100)" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Недопустимое значение: %@ для %@ должно быть HSB (0–360, 0–100, 0–100)" } } } }, - "invalid_connection_configuration": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Ungültige Verbindungskonfiguration." + "invalid_connection_configuration" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ungültige Verbindungskonfiguration." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Invalid connection configuration." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Invalid connection configuration." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Configuración de conexión no válida." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración de conexión no válida." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Virheellinen yhteysasetukset." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Virheellinen yhteysasetukset." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Configuration de connexion invalide." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuration de connexion invalide." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Configurazione della connessione non valida." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configurazione della connessione non valida." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ugyldig tilkoblingskonfigurasjon." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ugyldig tilkoblingskonfigurasjon." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Ongeldige verbindingsconfiguratie." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ongeldige verbindingsconfiguratie." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Недопустимая конфигурация подключения." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Недопустимая конфигурация подключения." } } } }, - "Item": { - "comment": "Parameter title for an openHAB item in app intents.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Item" + "Item" : { + "comment" : "Parameter title for an openHAB item in app intents.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Item" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Item" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Item" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Ítem" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ítem" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Elementti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elementti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Item" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Item" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Item" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Item" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Item" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Item" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Item" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Item" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент" } } } }, - "Item '%@' is not in home '%@'": { - "comment": "Error message when the selected item does not belong to the selected home. First placeholder is the item name, second is the home name.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Das Item „%@“ befindet sich nicht im Zuhause „%@“" + "Item '%@' is not in home '%@'" : { + "comment" : "Error message when the selected item does not belong to the selected home. First placeholder is the item name, second is the home name.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Das Item „%@“ befindet sich nicht im Zuhause „%@“" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Item '%@' is not in home '%@'" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Item '%@' is not in home '%@'" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "El elemento ‘%@’ no está en el hogar ‘%@’" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "El elemento ‘%@’ no está en el hogar ‘%@’" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Elementti ‘%@’ ei ole kodissa ‘%@’" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elementti ‘%@’ ei ole kodissa ‘%@’" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "L'élément '%@' n'est pas dans la maison '%@'" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "L'élément '%@' n'est pas dans la maison '%@'" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "L'elemento '%@' non si trova nella casa '%@'" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "L'elemento '%@' non si trova nella casa '%@'" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Element ‘%@’ er ikke i hjemmet ‘%@’" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Element ‘%@’ er ikke i hjemmet ‘%@’" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Item '%@' staat niet in huis '%@'" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Item '%@' staat niet in huis '%@'" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент «%@» не входит в дом «%@»" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент «%@» не входит в дом «%@»" } } } }, - "Item '%@' not found": { - "comment": "Error message when an item is not found. The argument is the name of the item.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Element '%@' nicht gefunden" + "Item '%@' not found" : { + "comment" : "Error message when an item is not found. The argument is the name of the item.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Element '%@' nicht gefunden" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento ‘%@’ no encontrado" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento ‘%@’ no encontrado" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Elementtiä ‘%@’ ei löydy" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elementtiä ‘%@’ ei löydy" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Élément '%@' introuvable" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément '%@' introuvable" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elemento '%@' non trovato" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento '%@' non trovato" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Element ‘%@’ ikke funnet" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Element ‘%@’ ikke funnet" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Item '%@' niet gevonden" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Item '%@' niet gevonden" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент «%@» не найден" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент «%@» не найден" } } } }, - "Items": { - "comment": "The title of the view that lists available items.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Items" + "Items" : { + "comment" : "The title of the view that lists available items.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Items" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Items" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Items" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Ítems" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ítems" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Elementit" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elementit" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Éléments" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Éléments" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elementi" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elementi" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Elementer" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elementer" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Onderdelen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Onderdelen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элементы" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элементы" } } } }, - "Label": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Label" + "Label" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Label" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Label" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Label" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Etiqueta" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Etiqueta" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Etiketti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Etiketti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Étiquette" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Étiquette" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Etichetta" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Etichetta" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Etikett" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Etikett" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Label" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Label" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Метка" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Метка" } } } }, - "Latitude": { - "comment": "Parameter title for latitude in the Set Location Control Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Breitengrad" + "Latitude" : { + "comment" : "Parameter title for latitude in the Set Location Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Breitengrad" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Latitude" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Latitude" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Latitud" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Latitud" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Leveysaste" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Leveysaste" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Latitude" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Latitude" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Latitudine" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Latitudine" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Breddegrad" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Breddegrad" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Breedtegraad" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Breedtegraad" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Широта" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Широта" } } } }, - "Latitude must be between -90 and 90": { - "comment": "Error message when a latitude value is out of the valid range.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Breitengrad muss zwischen -90 and 90 sein" + "Latitude must be between -90 and 90" : { + "comment" : "Error message when a latitude value is out of the valid range.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Breitengrad muss zwischen -90 and 90 sein" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Latitude must be between -90 and 90" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Latitude must be between -90 and 90" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "La latitud debe estar entre -90 y 90" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "La latitud debe estar entre -90 y 90" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Leveysasteen on oltava välillä −90 ja 90" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Leveysasteen on oltava välillä −90 ja 90" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "La latitude doit être comprise entre -90 et 90" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "La latitude doit être comprise entre -90 et 90" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "La latitudine deve essere compresa tra -90 e 90" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "La latitudine deve essere compresa tra -90 e 90" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Breddegraden må være mellom -90 og 90" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Breddegraden må være mellom -90 og 90" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Breedtegraad moet tussen -90 en 90 liggen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Breedtegraad moet tussen -90 en 90 liggen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Широта должна быть от -90 до 90" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Широта должна быть от -90 до 90" } } } }, - "legal": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Rechtliches" + "legal" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rechtliches" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Legal" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Legal" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Legal" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Legal" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Legal" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Legal" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Légal" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Légal" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Legale" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Legale" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Juridisk" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Juridisk" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Juridisch" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Juridisch" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Legal" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Legal" } } } }, - "Legal": { - "comment": "A link to the app's legal information.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Rechtliches" + "Legal" : { + "comment" : "A link to the app's legal information.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rechtliches" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Legal" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Legal" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Aviso legal" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aviso legal" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Oikeudelliset tiedot" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Oikeudelliset tiedot" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Mentions légales" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mentions légales" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Note legali" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Note legali" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Juridisk" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Juridisk" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Juridische informatie" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Juridische informatie" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Юридическая информация" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Юридическая информация" } } } }, - "Loading Items…": { - "comment": "A message displayed while waiting to load items.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Items werden geladen …" + "Loading Items…" : { + "comment" : "A message displayed while waiting to load items.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Items werden geladen …" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Loading Items…" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loading Items…" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Cargando elementos…" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cargando elementos…" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ladataan elementtejä…" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ladataan elementtejä…" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Chargement des éléments…" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chargement des éléments…" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Caricamento elementi…" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Caricamento elementi…" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Laster elementer…" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Laster elementer…" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Items worden geladen…" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Items worden geladen…" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Загрузка элементов…" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Загрузка элементов…" } } } }, - "Loading sitemap…": { - "comment": "A placeholder text indicating that the sitemap is being loaded.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sitemap wird geladen …" + "Loading sitemap…" : { + "comment" : "A placeholder text indicating that the sitemap is being loaded.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap wird geladen …" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Loading sitemap…" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loading sitemap…" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Cargando mapa del sitio…" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cargando mapa del sitio…" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ladataan sivukarttaa…" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ladataan sivukarttaa…" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Chargement du plan du site…" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chargement du plan du site…" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Caricamento sitemap…" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Caricamento sitemap…" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Laster nettstedskart…" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Laster nettstedskart…" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Sitemap wordt geladen…" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap wordt geladen…" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Загрузка карты сайта…" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Загрузка карты сайта…" } } } }, - "Loading…": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Wird geladen …" + "Loading…" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wird geladen …" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Loading…" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loading…" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Cargando…" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cargando…" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ladataan …" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ladataan …" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Chargement…" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chargement…" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Caricamento …" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Caricamento …" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Laster …" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Laster …" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Laden…" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Laden…" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Загрузка …" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Загрузка …" } } } }, - "Local Network access may be required.": { - "comment": "A warning message that appears when the user's local network access is required to connect to a server.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Möglicherweise ist der Zugriff auf das lokale Netzwerk erforderlich." + "Local Network access may be required." : { + "comment" : "A warning message that appears when the user's local network access is required to connect to a server.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Möglicherweise ist der Zugriff auf das lokale Netzwerk erforderlich." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Es posible que se requiera acceso a la red local." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Es posible que se requiera acceso a la red local." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Lähiverkon käyttöoikeus voi olla tarpeen." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lähiverkon käyttöoikeus voi olla tarpeen." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "L'accès au réseau local peut être requis." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "L'accès au réseau local peut être requis." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Potrebbe essere necessario l'accesso alla rete locale." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Potrebbe essere necessario l'accesso alla rete locale." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Tilgang til lokalt nettverk kan være nødvendig." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tilgang til lokalt nettverk kan være nødvendig." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Toegang tot lokaal netwerk kan vereist zijn." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toegang tot lokaal netwerk kan vereist zijn." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Может потребоваться доступ к локальной сети." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Может потребоваться доступ к локальной сети." } } } }, - "Local Network Access Required": { - "comment": "A title for an alert that warns the user that local network access is required to connect to a local openHAB server.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zugriff auf lokales Netzwerk erforderlich" + "Local Network Access Required" : { + "comment" : "A title for an alert that warns the user that local network access is required to connect to a local openHAB server.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zugriff auf lokales Netzwerk erforderlich" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Se requiere acceso a la red local" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Se requiere acceso a la red local" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Lähiverkon käyttöoikeus vaaditaan" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lähiverkon käyttöoikeus vaaditaan" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Accès au réseau local requis" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Accès au réseau local requis" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Accesso alla rete locale richiesto" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Accesso alla rete locale richiesto" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Tilgang til lokalt nettverk kreves" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tilgang til lokalt nettverk kreves" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Toegang tot lokaal netwerk vereist" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toegang tot lokaal netwerk vereist" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Требуется доступ к локальной сети" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Требуется доступ к локальной сети" } } } }, - "Local server": { - "comment": "Title of the section in the connection settings view that pertains to the local OpenHAB server.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Lokaler Server" + "Local server" : { + "comment" : "Title of the section in the connection settings view that pertains to the local OpenHAB server.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lokaler Server" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Local server" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Local server" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Servidor local" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Servidor local" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Paikallinen palvelin" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Paikallinen palvelin" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Serveur local" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Serveur local" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Server locale" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Server locale" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Lokal server" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lokal server" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Lokale server" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lokale server" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Локальный сервер" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Локальный сервер" } } } }, - "local_url": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Lokale URL" + "local_url" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lokale URL" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Local URL" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Local URL" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "URL local" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL local" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Local URL" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Local URL" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "URL locale" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL locale" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "URL locale" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL locale" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Lokal URL" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lokal URL" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Lokale URL" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lokale URL" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Локальный URL-адрес" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Локальный URL-адрес" } } } }, - "Location": { - "comment": "Name of a marker displayed on a map view.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Standort" + "Location" : { + "comment" : "Name of a marker displayed on a map view.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Standort" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Location" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Location" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Ubicación" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ubicación" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Sijainti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sijainti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Emplacement" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Emplacement" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Posizione" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Posizione" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Plassering" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Plassering" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Locatie" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Locatie" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Местоположение" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Местоположение" } } } }, - "Location Item": { - "comment": "Display name for a location item type.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Standort-Item" + "Location Item" : { + "comment" : "Display name for a location item type.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Standort-Item" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento de ubicación" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento de ubicación" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Sijaintielementti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sijaintielementti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Élément de localisation" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément de localisation" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elemento posizione" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento posizione" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Plasseringselement" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Plasseringselement" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Locatie-item" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Locatie-item" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент местоположения" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент местоположения" } } } }, - "Logs": { - "comment": "A link in the debug settings that navigates to the logs viewer.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Protokolle" + "Logs" : { + "comment" : "A link in the debug settings that navigates to the logs viewer.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Protokolle" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Logs" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Logs" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Registros" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Registros" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Lokit" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lokit" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Historiques" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Historiques" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Log" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Log" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Logger" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Logger" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Logbestanden" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Logbestanden" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Журналы" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Журналы" } } } }, - "Longitude": { - "comment": "Parameter title for longitude in the Set Location Control Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Längengrad" + "Longitude" : { + "comment" : "Parameter title for longitude in the Set Location Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Längengrad" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Longitude" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Longitude" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Longitud" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Longitud" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Pituusaste" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pituusaste" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Longitude" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Longitude" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Longitudine" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Longitudine" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Lengdegrad" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lengdegrad" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Lengtegraad" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lengtegraad" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Долгота" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Долгота" } } } }, - "Longitude must be between -180 and 180": { - "comment": "Error message when a longitude value is out of the valid range.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Längengrad muss zwischen -180 and 180 sein" + "Longitude must be between -180 and 180" : { + "comment" : "Error message when a longitude value is out of the valid range.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Längengrad muss zwischen -180 and 180 sein" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Longitude must be between -180 and 180" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Longitude must be between -180 and 180" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "La longitud debe estar entre -180 y 180" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "La longitud debe estar entre -180 y 180" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Pituusasteen on oltava välillä −180 ja 180" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pituusasteen on oltava välillä −180 ja 180" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "La longitude doit être comprise entre -180 et 180" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "La longitude doit être comprise entre -180 et 180" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "La longitudine deve essere compresa tra -180 e 180" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "La longitudine deve essere compresa tra -180 e 180" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Lengdegraden må være mellom -180 og 180" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lengdegraden må være mellom -180 og 180" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Lengtegraad moet tussen -180 en 180 liggen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lengtegraad moet tussen -180 en 180 liggen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Долгота должна быть от -180 до 180" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Долгота должна быть от -180 до 180" } } } }, - "Lowers by %@": { - "comment": "A hint that describes how much the value can be decreased by.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Um %@ senken" + "Lowers by %@" : { + "comment" : "A hint that describes how much the value can be decreased by.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Um %@ senken" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Lowers by %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lowers by %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Baja %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Baja %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Laskee %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Laskee %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Abaisse de %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abaisse de %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Abbassa di %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abbassa di %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Senker med %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Senker med %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Verlaagt met %@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verlaagt met %@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Снижает на %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Снижает на %@" } } } }, - "Main": { - "comment": "The header text for the \"Main\" section in the drawer view.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Main" + "Main" : { + "comment" : "The header text for the \"Main\" section in the drawer view.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Main" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Main" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Main" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Principal" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Principal" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Pää" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pää" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Principal" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Principal" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Principale" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Principale" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Hoved" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hoved" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Hoofd" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hoofd" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Главная" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Главная" } } } }, - "mainui_settings": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Main UI-Einstellungen" + "mainui_settings" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Main UI-Einstellungen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Main UI Settings" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Main UI Settings" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Ajustes de la interfaz de usuario principal" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajustes de la interfaz de usuario principal" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Main UI Settings" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Main UI Settings" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Paramètres Main UI" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Paramètres Main UI" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Impostazioni Main UI" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impostazioni Main UI" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Innstillinger for Main UI" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Innstillinger for Main UI" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Main UI Instellingen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Main UI Instellingen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Main UI Settings" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Main UI Settings" } } } }, - "Manage Homes": { - "comment": "The title of a view that allows users to manage their homes.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zuhause verwalten" + "Manage Homes" : { + "comment" : "The title of a view that allows users to manage their homes.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zuhause verwalten" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Manage Homes" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manage Homes" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Gestionar hogares" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gestionar hogares" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Hallitse koteja" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hallitse koteja" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Gérer les maisons" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gérer les maisons" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Gestisci case" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gestisci case" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Administrer hjem" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Administrer hjem" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Huizen beheren" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Huizen beheren" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Управление домами" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Управление домами" } } } }, - "message_not_decoded": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Nachricht konnte nicht dekodiert werden" + "message_not_decoded" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nachricht konnte nicht dekodiert werden" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Message could not be decoded" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Message could not be decoded" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "No se ha podido decodificar el mensaje" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se ha podido decodificar el mensaje" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Message could not be decoded" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Message could not be decoded" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Le message n'a pas pu être décodé" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Le message n'a pas pu être décodé" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Il messaggio non può essere decodificato" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Il messaggio non può essere decodificato" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Melding kunne ikke dekodes" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Melding kunne ikke dekodes" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Bericht kon niet gedecodeerd worden" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bericht kon niet gedecodeerd worden" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Message could not be decoded" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Message could not be decoded" } } } }, - "Movement Interval: %lld s": { - "comment": "A stepper that allows the user to set the interval in seconds between when the screen saver will activate and when it will deactivate after a user is detected moving.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Bewegungsintervall: %lld s" + "Movement Interval: %lld s" : { + "comment" : "A stepper that allows the user to set the interval in seconds between when the screen saver will activate and when it will deactivate after a user is detected moving.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bewegungsintervall: %lld s" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Movement Interval: %lld s" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Movement Interval: %lld s" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Intervalo de movimiento: %lld s" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Intervalo de movimiento: %lld s" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Liikeväli: %lld s" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Liikeväli: %lld s" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Intervalle de déplacement : %lld s" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Intervalle de déplacement : %lld s" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Intervallo di movimento: %lld s" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Intervallo di movimento: %lld s" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Bevegelsesintervall: %lld s" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bevegelsesintervall: %lld s" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Bewegingsinterval: %lld s" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bewegingsinterval: %lld s" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Интервал движения: %lld с" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Интервал движения: %lld с" } } } }, - "Name": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Name" + "Name" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Name" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Name" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Name" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Nombre" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nombre" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Nimi" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nimi" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Nom" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nom" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Nome" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nome" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Navn" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Navn" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Naam" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Naam" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Имя" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Имя" } } } }, - "Name for new home": { - "comment": "A placeholder label for a text field in an alert used to enter the name of a new home.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Name für neues Zuhause" + "Name for new home" : { + "comment" : "A placeholder label for a text field in an alert used to enter the name of a new home.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Name für neues Zuhause" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Name for new home" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Name for new home" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Nombre para el nuevo hogar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nombre para el nuevo hogar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Nimi uudelle kodille" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nimi uudelle kodille" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Nom de la nouvelle maison" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nom de la nouvelle maison" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Nome per la nuova casa" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nome per la nuova casa" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Navn for nytt hjem" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Navn for nytt hjem" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Naam voor nieuw huis" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Naam voor nieuw huis" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Имя для нового дома" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Имя для нового дома" } } } }, - "network_not_available": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Netzwerk ist nicht verfügbar." + "network_not_available" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Netzwerk ist nicht verfügbar." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Network is not available." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Network is not available." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Red no disponible." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Red no disponible." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Network is not available." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Network is not available." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Aucune connexion réseau disponible." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucune connexion réseau disponible." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Rete non disponibile." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rete non disponibile." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Nettverk er ikke tilgjengelig." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nettverk er ikke tilgjengelig." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Netwerk is niet beschikbaar." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Netwerk is niet beschikbaar." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Network is not available." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Network is not available." } } } }, - "New name": { - "comment": "A label for a text field where the user can enter a new name for a home.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Neuer Name" + "New name" : { + "comment" : "A label for a text field where the user can enter a new name for a home.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Neuer Name" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "New name" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "New name" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Nuevo nombre" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nuevo nombre" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Uusi nimi" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uusi nimi" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Nouveau nom" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nouveau nom" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Nuovo nome" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nuovo nome" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Nytt navn" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nytt navn" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Nieuwe naam" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nieuwe naam" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Новое имя" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Новое имя" } } } }, - "Next": { - "comment": "Display name for the next action in the PlayerAction enum.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Nächstes" + "Next" : { + "comment" : "Display name for the next action in the PlayerAction enum.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nächstes" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Next" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Next" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Siguiente" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Siguiente" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Seuraava" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seuraava" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Suivant" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suivant" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Successivo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Successivo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Neste" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Neste" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Volgende" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Volgende" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Следующий" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Следующий" } } } }, - "No accepted server certificates": { - "comment": "A message displayed when there are no accepted server certificates.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Keine akzeptierten Server-Zertifikate" + "No accepted server certificates" : { + "comment" : "A message displayed when there are no accepted server certificates.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine akzeptierten Server-Zertifikate" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "No accepted server certificates" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No accepted server certificates" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "No hay certificados de servidor aceptados" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No hay certificados de servidor aceptados" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ei hyväksyttyjä palvelinvarmenteita" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ei hyväksyttyjä palvelinvarmenteita" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Aucun certificat serveur accepté" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucun certificat serveur accepté" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Nessun certificato server accettato" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nessun certificato server accettato" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ingen godkjente serversertifikater" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen godkjente serversertifikater" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Geen geaccepteerde servercertificaten" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geen geaccepteerde servercertificaten" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Нет принятых сертификатов сервера" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Нет принятых сертификатов сервера" } } } }, - "No Image URL": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Keine Bild-URL" + "No Image URL" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine Bild-URL" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "No Image URL" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No Image URL" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Sin URL de imagen" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sin URL de imagen" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ei kuvan URL-osoitetta" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ei kuvan URL-osoitetta" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Aucune URL d'image" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucune URL d'image" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Nessun URL immagine" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nessun URL immagine" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ingen bilde-URL" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen bilde-URL" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Geen afbeeldings-URL" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geen afbeeldings-URL" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Нет URL изображения" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Нет URL изображения" } } } }, - "No sitemaps available": { - "comment": "A message indicating that there are no sitemaps available to choose from in the \"Sitemap For Apple Watch\" picker.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Keine Sitemaps verfügbar" + "No sitemaps available" : { + "comment" : "A message indicating that there are no sitemaps available to choose from in the \"Sitemap For Apple Watch\" picker.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine Sitemaps verfügbar" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "No sitemaps available" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No sitemaps available" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "No hay mapas del sitio disponibles" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No hay mapas del sitio disponibles" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ei sivukarttoja saatavilla" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ei sivukarttoja saatavilla" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Aucun plan du site disponible" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucun plan du site disponible" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Nessuna sitemap disponibile" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nessuna sitemap disponibile" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ingen nettstedskart tilgjengelig" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen nettstedskart tilgjengelig" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Geen sitemaps beschikbaar" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geen sitemaps beschikbaar" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Нет доступных карт сайта" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Нет доступных карт сайта" } } } }, - "No Video URL": { - "comment": "A message displayed when a video URL is not provided for a video row.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Keine Video-URL" + "No Video URL" : { + "comment" : "A message displayed when a video URL is not provided for a video row.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine Video-URL" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "No Video URL" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No Video URL" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Sin URL de vídeo" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sin URL de vídeo" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ei videon URL-osoitetta" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ei videon URL-osoitetta" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Aucune URL vidéo" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucune URL vidéo" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Nessun URL video" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nessun URL video" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ingen video-URL" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen video-URL" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Geen video-URL" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geen video-URL" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Нет URL видео" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Нет URL видео" } } } }, - "No widgets available.": { - "comment": "A message displayed when a user has no widgets configured.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Keine Widgets verfügbar." + "No widgets available." : { + "comment" : "A message displayed when a user has no widgets configured.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine Widgets verfügbar." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "No widgets available." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No widgets available." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "No hay widgets disponibles." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No hay widgets disponibles." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ei widgettejä saatavilla." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ei widgettejä saatavilla." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Aucun widget disponible." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucun widget disponible." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Nessun widget disponibile." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nessun widget disponibile." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ingen widgeter tilgjengelig." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen widgeter tilgjengelig." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Geen widgets beschikbaar." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geen widgets beschikbaar." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Нет доступных виджетов." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Нет доступных виджетов." } } } }, - "no_active_connection": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Keine aktive Verbindung vorhanden. Bitte Einstellungen prüfen" + "no_active_connection" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine aktive Verbindung vorhanden. Bitte Einstellungen prüfen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "No active connection available. Please check your settings." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No active connection available. Please check your settings." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "No active connection available. Please check your settings." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No active connection available. Please check your settings." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "No active connection available. Please check your settings." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "No active connection available. Please check your settings." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "No active connection available. Please check your settings." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "No active connection available. Please check your settings." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "No active connection available. Please check your settings." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "No active connection available. Please check your settings." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "No active connection available. Please check your settings." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "No active connection available. Please check your settings." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "No active connection available. Please check your settings." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "No active connection available. Please check your settings." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "No active connection available. Please check your settings." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "No active connection available. Please check your settings." } } } }, - "no_connection_will_reconnect": { - "comment": "Title of a popup message displayed when the OpenHAB client is not connected to the server and will automatically try to reconnect.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Keine Verbindung, erneuter Verbindungsversuch" + "no_connection_will_reconnect" : { + "comment" : "Title of a popup message displayed when the OpenHAB client is not connected to the server and will automatically try to reconnect.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine Verbindung, erneuter Verbindungsversuch" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "no_connection_will_reconnect" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "no_connection_will_reconnect" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Sin conexión, reintentando…" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sin conexión, reintentando…" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ei yhteyttä, yritetään uudelleen…" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ei yhteyttä, yritetään uudelleen…" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Pas de connexion, reconnexion en cours" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pas de connexion, reconnexion en cours" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Nessuna connessione, riconnessione in corso" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nessuna connessione, riconnessione in corso" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ingen tilkobling, kobler til igjen…" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen tilkobling, kobler til igjen…" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Geen verbinding, opnieuw verbinden" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geen verbinding, opnieuw verbinden" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Нет подключения, повторное подключение…" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Нет подключения, повторное подключение…" } } } }, - "no_servers_found": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "No servers found" + "no_servers_found" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "No servers found" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "No servers found" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No servers found" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "No servers found" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No servers found" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "No servers found" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "No servers found" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Aucun serveur trouvé" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucun serveur trouvé" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "No servers found" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "No servers found" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "No servers found" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "No servers found" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "No servers found" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "No servers found" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "No servers found" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "No servers found" } } } }, - "notification": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Benachrichtigung" + "notification" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Benachrichtigung" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Notification" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notification" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Notificación" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notificación" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Notification" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notification" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Notification" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notification" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Notifica" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notifica" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Varsel" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Varsel" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Notificatie" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notificatie" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Notification" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notification" } } } }, - "notifications": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Benachrichtigungen" + "notifications" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Benachrichtigungen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Notifications" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notifications" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Notificaciones" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notificaciones" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Notifications" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notifications" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Notifications" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notifications" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Notifiche" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notifiche" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Varsler" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Varsler" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Notificaties" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notificaties" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Notifications" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notifications" } } } }, - "Notifications": { - "comment": "The title of the notifications view.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Benachrichtigungen" + "Notifications" : { + "comment" : "The title of the notifications view.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Benachrichtigungen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Notifications" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notifications" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Notificaciones" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notificaciones" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ilmoitukset" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ilmoitukset" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Notifications" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notifications" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Notifiche" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notifiche" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Varsler" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Varsler" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Notificaties" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notificaties" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Уведомления" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Уведомления" } } } }, - "Number Item": { - "comment": "Display name for a number item type.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Numerisches-Item" + "Number Item" : { + "comment" : "Display name for a number item type.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Numerisches-Item" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento numérico" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento numérico" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Numeraalielementti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Numeraalielementti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Élément numérique" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément numérique" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elemento numerico" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento numerico" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Tallselement" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tallselement" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Numeriek item" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Numeriek item" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Числовой элемент" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Числовой элемент" } } } }, - "Off": { - "comment": "The text \"Off\" displayed in the accessibility value of the toggle.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Aus" + "Off" : { + "comment" : "The text \"Off\" displayed in the accessibility value of the toggle.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aus" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Off" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Off" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Desactivado" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Desactivado" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Pois" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pois" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Désactivé" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Désactivé" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "No" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "No" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Av" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Av" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Uit" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uit" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Выкл." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Выкл." } } } }, - "Offline": { - "comment": "A label indicating that the device is currently offline.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Offline" + "Offline" : { + "comment" : "A label indicating that the device is currently offline.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Offline" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Offline" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Offline" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Sin conexión" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sin conexión" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Offline" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Offline" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Déconnecté" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Déconnecté" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Non in linea" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Non in linea" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Frakoblet" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Frakoblet" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Offline" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Offline" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Не в сети" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Не в сети" } } } }, - "oh_secret": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "openHAB-Secret" + "oh_secret" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB-Secret" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "openHAB Secret" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Secret" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "código secreto de openHAB" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "código secreto de openHAB" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "openHAB Secret" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Secret" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Phrase secrète openHAB" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Phrase secrète openHAB" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "openHAB Secret" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Secret" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "openHAB Hemmelighet" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Hemmelighet" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "openHAB geheim" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB geheim" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "openHAB Secret" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Secret" } } } }, - "oh_uuid": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "openHAB-UUID" + "oh_uuid" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB-UUID" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "openHAB UUID" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB UUID" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "openHAB UUID" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB UUID" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "openHAB UUID" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB UUID" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "openHAB UUID" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB UUID" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "openHAB UUID" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB UUID" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "openHAB UUID" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB UUID" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "openHAB UUID" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB UUID" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "openHAB UUID" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB UUID" } } } }, - "oh_version": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "openHAB-Version" + "oh_version" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB-Version" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "openHAB Version" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Version" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Versión de openHAB" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Versión de openHAB" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "openHAB Version" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Version" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "openHAB Version" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Version" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Versione openHAB" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Versione openHAB" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "openHAB versjon" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB versjon" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "openHAB Versie" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Versie" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "openHAB Version" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Version" } } } }, - "OK": { - "comment": "The text for an OK button.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "OK" + "OK" : { + "comment" : "The text for an OK button.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "OK" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "OK" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "OK" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Aceptar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aceptar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "OK" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "OK" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "OK" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "OK" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "OK" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "OK" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "OK" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "OK" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "OK" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "OK" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "ОК" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "ОК" } } } }, - "On": { - "comment": "A label indicating that a switch is on.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "An" + "On" : { + "comment" : "A label indicating that a switch is on.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "An" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "On" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "On" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Encendido" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Encendido" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Päällä" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Päällä" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Activé" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activé" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Attivo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Attivo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "På" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "På" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Aan" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aan" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Вкл." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Вкл." } } } }, - "Once": { - "comment": "Button to allow the app to connect to the server only once, even if the certificate is invalid.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Einmal" + "Once" : { + "comment" : "Button to allow the app to connect to the server only once, even if the certificate is invalid.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Einmal" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Once" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Once" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Una vez" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Una vez" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kerran" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kerran" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Une fois" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Une fois" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Una volta" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Una volta" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Én gang" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Én gang" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Eenmalig" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eenmalig" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Один раз" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Один раз" } } } }, - "open": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "offen" + "open" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "offen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "open" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "open" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "abierto" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "abierto" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "open" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "open" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "ouvert" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "ouvert" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "aperto" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "aperto" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "åpen" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "åpen" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "open" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "open" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "open" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "open" } } } }, - "Open": { - "comment": "Displayed name for the 'Open' contact state.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Geöffnet" + "Open" : { + "comment" : "Displayed name for the 'Open' contact state.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geöffnet" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Abierto" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abierto" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Auki" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Auki" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Ouvert" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ouvert" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Aperto" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aperto" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Åpen" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Åpen" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Open" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Open" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Открыто" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Открыто" } } } }, - "Open Settings": { - "comment": "A button that opens the user's system settings.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Einstellungen öffnen" + "Open Settings" : { + "comment" : "A button that opens the user's system settings.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Einstellungen öffnen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Abrir ajustes" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abrir ajustes" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Avaa asetukset" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Avaa asetukset" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Ouvrir les réglages" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ouvrir les réglages" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Apri Impostazioni" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apri Impostazioni" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Åpne innstillinger" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Åpne innstillinger" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Instellingen openen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Instellingen openen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Открыть настройки" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Открыть настройки" } } } }, - "openHAB Cloud Service": { - "comment": "A toggle that allows the user to enable or disable notifications from the openHAB Cloud Service.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "openHAB Cloud-Dienst" + "openHAB Cloud Service" : { + "comment" : "A toggle that allows the user to enable or disable notifications from the openHAB Cloud Service.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Cloud-Dienst" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "openHAB Cloud Service" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Cloud Service" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Servicio en la nube openHAB" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Servicio en la nube openHAB" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "openHAB-pilvipalvelu" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB-pilvipalvelu" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Service openHAB Cloud" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Service openHAB Cloud" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Servizio openHAB Cloud" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Servizio openHAB Cloud" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "openHAB-skytjeneste" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB-skytjeneste" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "openHAB Cloud-dienst" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Cloud-dienst" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Облачный сервис openHAB" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Облачный сервис openHAB" } } } }, - "openhab_connection": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "openHAB Verbindung" + "openhab_connection" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Verbindung" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "openHAB Connection" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Connection" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "conexión openHAB" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "conexión openHAB" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "openHAB Connection" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Connection" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Connexion openHAB" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connexion openHAB" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Connessione openHAB" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connessione openHAB" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "openHAB Tilkobling" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Tilkobling" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "openHAB Verbinding" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Verbinding" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "подключение к OpenHAB" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "подключение к OpenHAB" } } } }, - "password": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Passwort" + "password" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Passwort" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Password" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Password" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Contraseña" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contraseña" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Password" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Password" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Mot de passe" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mot de passe" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Password" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Password" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Passord" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Passord" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Wachtwoord" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wachtwoord" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Password" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Password" } } } }, - "Password": { - "comment": "A label displayed above a text field for entering a password.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Passwort" + "Password" : { + "comment" : "A label displayed above a text field for entering a password.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Passwort" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Password" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Password" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Contraseña" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contraseña" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Salasana" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Salasana" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Mot de passe" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mot de passe" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Password" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Password" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Passord" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Passord" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Wachtwoord" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wachtwoord" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Пароль" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Пароль" } } } }, - "Pause": { - "comment": "Display name for the pause action in the PlayerAction enum.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Pause" + "Pause" : { + "comment" : "Display name for the pause action in the PlayerAction enum.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pause" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Pause" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pause" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Pausa" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pausa" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Tauko" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tauko" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Pause" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pause" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Pausa" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pausa" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Pause" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pause" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Pauzeren" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pauzeren" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Пауза" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Пауза" } } } }, - "Play": { - "comment": "Display name for the play action in the PlayerAction enum.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Abspielen" + "Play" : { + "comment" : "Display name for the play action in the PlayerAction enum.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abspielen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Play" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Play" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Reproducir" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reproducir" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Toista" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toista" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Lire" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lire" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Riproduci" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Riproduci" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Spill av" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Spill av" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Afspelen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Afspelen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Воспроизвести" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Воспроизвести" } } } }, - "Player Action": { - "comment": "Type display name for the PlayerAction enum used in app intents.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Player-Aktion" + "Player Action" : { + "comment" : "Type display name for the PlayerAction enum used in app intents.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Player-Aktion" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Player Action" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Player Action" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Acción del reproductor" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Acción del reproductor" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Soittimen toiminto" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Soittimen toiminto" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Action du lecteur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Action du lecteur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Azione player" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Azione player" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Spillerhandling" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Spillerhandling" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Speleractie" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Speleractie" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Действие плеера" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Действие плеера" } } } }, - "Player Item": { - "comment": "Display name for a player item type.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Player-Item" + "Player Item" : { + "comment" : "Display name for a player item type.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Player-Item" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento reproductor" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento reproductor" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Soitinelementti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Soitinelementti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Élément lecteur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément lecteur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elemento player" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento player" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Spillerelement" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Spillerelement" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Speler-item" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Speler-item" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент плеера" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент плеера" } } } }, - "PNG": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "PNG" + "PNG" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "PNG" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "PNG" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "PNG" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "PNG" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "PNG" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "PNG" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "PNG" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "PNG" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "PNG" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "PNG" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "PNG" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "PNG" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "PNG" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "PNG" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "PNG" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "PNG" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "PNG" } } } }, - "Preferences": { - "comment": "Label for the preferences tab in the watch app.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Einstellungen" + "Preferences" : { + "comment" : "Label for the preferences tab in the watch app.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Einstellungen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Preferences" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Preferences" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Preferencias" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Preferencias" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Asetukset" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Asetukset" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Préférences" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Préférences" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Preferenze" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Preferenze" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Innstillinger" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Innstillinger" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Voorkeuren" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Voorkeuren" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Настройки" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Настройки" } } } }, - "Preview state": { - "comment": "The accessibility label for the picker that allows the user to select and preview a different state of an interactive state token.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Vorschauzustand" + "Preview state" : { + "comment" : "The accessibility label for the picker that allows the user to select and preview a different state of an interactive state token.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vorschauzustand" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Preview state" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Preview state" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Estado de vista previa" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Estado de vista previa" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Esikatselutila" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Esikatselutila" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "État d'aperçu" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "État d'aperçu" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Stato anteprima" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stato anteprima" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Forhåndsvisningstilstand" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Forhåndsvisningstilstand" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Voorbeeldstatus" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Voorbeeldstatus" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Состояние предварительного просмотра" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Состояние предварительного просмотра" } } } }, - "Previous": { - "comment": "Display name for the previous action in the PlayerAction enum.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Vorheriges" + "Previous" : { + "comment" : "Display name for the previous action in the PlayerAction enum.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vorheriges" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Previous" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Previous" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Anterior" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anterior" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Edellinen" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Edellinen" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Précédent" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Précédent" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Precedente" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Precedente" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Forrige" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Forrige" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Vorige" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vorige" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Предыдущий" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Предыдущий" } } } }, - "privacy_policy": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Datenschutzerklärung" + "privacy_policy" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datenschutzerklärung" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Privacy Policy" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Privacy Policy" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Política de privacidad" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Política de privacidad" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Privacy Policy" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Privacy Policy" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Politique de Confidentialité" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Politique de Confidentialité" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Politica sulla privacy" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Politica sulla privacy" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Personvernregler" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Personvernregler" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Privacybeleid" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Privacybeleid" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Privacy Policy" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Privacy Policy" } } } }, - "Queued commands: %lld": { - "comment": "A label indicating that there are one or more commands in the queue. The placeholder inside the label is replaced with the actual number of queued commands.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Wartende Befehle: %lld" + "Queued commands: %lld" : { + "comment" : "A label indicating that there are one or more commands in the queue. The placeholder inside the label is replaced with the actual number of queued commands.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wartende Befehle: %lld" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Queued commands: %lld" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Queued commands: %lld" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Comandos en cola: %lld" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Comandos en cola: %lld" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Jonossa olevat komennot: %lld" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jonossa olevat komennot: %lld" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Commandes en attente : %lld" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Commandes en attente : %lld" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Comandi in coda: %lld" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Comandi in coda: %lld" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kommandoer i kø: %lld" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kommandoer i kø: %lld" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Opdrachten in wachtrij: %lld" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opdrachten in wachtrij: %lld" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Команды в очереди: %lld" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Команды в очереди: %lld" } } } }, - "Raises by %@": { - "comment": "A hint that appears when a user hovers over the \"Increase\" button in the setpoint row. The argument is the step size for the current setpoint.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Um %@ erhöhen" + "Raises by %@" : { + "comment" : "A hint that appears when a user hovers over the \"Increase\" button in the setpoint row. The argument is the step size for the current setpoint.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Um %@ erhöhen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Raises by %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Raises by %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Sube %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sube %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Nostaa %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nostaa %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Monte de %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Monte de %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Aumenta di %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aumenta di %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Øker med %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Øker med %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Verhoogt met %@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verhoogt met %@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Повышает на %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Повышает на %@" } } } }, - "real_time_sliders": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Echtzeitschieberegler" + "real_time_sliders" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Echtzeitschieberegler" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Real-time Sliders" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Real-time Sliders" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Deslizadores en tiempo real" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Deslizadores en tiempo real" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Real-time Sliders" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Real-time Sliders" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Curseurs en temps réel" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Curseurs en temps réel" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Cursori In Tempo Reale" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cursori In Tempo Reale" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sanntids Skyveknapper" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sanntids Skyveknapper" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Realtime schuifregelaars" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Realtime schuifregelaars" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Real-time Sliders" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Real-time Sliders" } } } }, - "Real-time Sliders": { - "comment": "A toggle that enables or disables the real-time slider feature.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Echtzeitschieberegler" + "Real-time Sliders" : { + "comment" : "A toggle that enables or disables the real-time slider feature.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Echtzeitschieberegler" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Real-time Sliders" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Real-time Sliders" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Controles deslizantes en tiempo real" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Controles deslizantes en tiempo real" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Reaaliaikaiset liukusäätimet" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reaaliaikaiset liukusäätimet" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Curseurs en temps réel" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Curseurs en temps réel" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Cursori in tempo reale" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cursori in tempo reale" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sanntidsskyvere" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sanntidsskyvere" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Realtime schuifregelaars" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Realtime schuifregelaars" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Слайдеры реального времени" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Слайдеры реального времени" } } } }, - "Relative Date: %@": { - "comment": "A label describing the relative size of the date text compared to the clock text.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Relatives Datum: %@" + "Relative Date: %@" : { + "comment" : "A label describing the relative size of the date text compared to the clock text.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Relatives Datum: %@" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Relative Date: %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Relative Date: %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Fecha relativa: %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fecha relativa: %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Suhteellinen päivämäärä: %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suhteellinen päivämäärä: %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Date relative : %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Date relative : %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Data relativa: %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Data relativa: %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Relativ dato: %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Relativ dato: %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Relatieve datum: %@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Relatieve datum: %@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Относительная дата: %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Относительная дата: %@" } } } }, - "Remote server": { - "comment": "Header text for the remote server section in the connection settings view.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Fernzugriff-Server" + "Remote server" : { + "comment" : "Header text for the remote server section in the connection settings view.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fernzugriff-Server" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Remote server" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Remote server" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Servidor remoto" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Servidor remoto" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Etäpalvelin" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Etäpalvelin" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Serveur distant" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Serveur distant" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Server remoto" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Server remoto" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Fjernserver" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fjernserver" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Externe server" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Externe server" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Удалённый сервер" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Удалённый сервер" } } } }, - "remote_url": { - "comment": "Space constraint string which has only about half the screen width on Apple Watch. Best to try and keep it no longer than the English string.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Fern-URL" + "remote_url" : { + "comment" : "Space constraint string which has only about half the screen width on Apple Watch. Best to try and keep it no longer than the English string.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fern-URL" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Remote URL" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Remote URL" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "URL remota" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL remota" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Remote URL" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Remote URL" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "URL distante" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL distante" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "URL remoto" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL remoto" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ekstern URL" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ekstern URL" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Externe URL" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Externe URL" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Удаленный URL-адрес" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Удаленный URL-адрес" } } } }, - "remote_url_not_configured": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "URL für Fernzugriff ist nicht konfiguriert." + "remote_url_not_configured" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL für Fernzugriff ist nicht konfiguriert." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Remote URL is not configured." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Remote URL is not configured." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "La URL remota no está configurada." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "La URL remota no está configurada." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Remote URL is not configured." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Remote URL is not configured." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "L'URL distante n'est pas configurée." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "L'URL distante n'est pas configurée." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "L'URL remoto non è configurato." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "L'URL remoto non è configurato." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ekstern URL er ikke konfigurert." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ekstern URL er ikke konfigurert." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Externe URL is niet geconfigureerd." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Externe URL is niet geconfigureerd." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Remote URL is not configured." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Remote URL is not configured." } } } }, - "Rename": { - "comment": "A button that renames a home.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Umbenennen" + "Rename" : { + "comment" : "A button that renames a home.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Umbenennen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Rename" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rename" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Renombrar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Renombrar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Nimeä uudelleen" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nimeä uudelleen" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Renommer" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Renommer" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Rinomina" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rinomina" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Gi nytt navn" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gi nytt navn" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Wijzig naam" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wijzig naam" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Переименовать" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Переименовать" } } } }, - "Restore Brightness: %@": { - "comment": "A label under the slider that shows the current brightness level and allows the user to adjust it. The value shown is the brightness level as a percentage.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Helligkeit wiederherstellen: %@" + "Restore Brightness: %@" : { + "comment" : "A label under the slider that shows the current brightness level and allows the user to adjust it. The value shown is the brightness level as a percentage.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Helligkeit wiederherstellen: %@" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Restore Brightness: %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Restore Brightness: %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Restaurar brillo: %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Restaurar brillo: %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Palauta kirkkaus: %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Palauta kirkkaus: %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Restaurer la luminosité : %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Restaurer la luminosité : %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Ripristina luminosità: %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ripristina luminosità: %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Gjenopprett lysstyrke: %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gjenopprett lysstyrke: %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Helderheid herstellen: %@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Helderheid herstellen: %@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Восстановить яркость: %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Восстановить яркость: %@" } } } }, - "Restore Previous Brightness on Wake": { - "comment": "A toggle that allows the user to restore the brightness level to its previous value when the device wakes up.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Vorherige Helligkeit beim Aufwecken wiederherstellen" + "Restore Previous Brightness on Wake" : { + "comment" : "A toggle that allows the user to restore the brightness level to its previous value when the device wakes up.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vorherige Helligkeit beim Aufwecken wiederherstellen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Restore Previous Brightness on Wake" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Restore Previous Brightness on Wake" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Restaurar brillo anterior al despertar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Restaurar brillo anterior al despertar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Palauta aiempi kirkkaus herätessä" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Palauta aiempi kirkkaus herätessä" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Restaurer la luminosité précédente au réveil" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Restaurer la luminosité précédente au réveil" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Ripristina luminosità precedente alla riattivazione" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ripristina luminosità precedente alla riattivazione" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Gjenopprett tidligere lysstyrke ved vekking" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gjenopprett tidligere lysstyrke ved vekking" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Vorige helderheid herstellen bij activering" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vorige helderheid herstellen bij activering" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Восстановить предыдущую яркость при пробуждении" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Восстановить предыдущую яркость при пробуждении" } } } }, - "Retrieve the current state of an item": { - "comment": "Description of the Get Item State app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Aktuellen Status eines Items abrufen" + "Retrieve the current state of an item" : { + "comment" : "Description of the Get Item State app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktuellen Status eines Items abrufen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Retrieve the current state of an item" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Retrieve the current state of an item" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Recuperar el estado actual de un elemento" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Recuperar el estado actual de un elemento" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Hae elementin nykyinen tila" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hae elementin nykyinen tila" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Récupérer l'état actuel d'un item" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Récupérer l'état actuel d'un item" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Recupera lo stato attuale di un Item" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Recupera lo stato attuale di un Item" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Hent nåværende tilstand for et Item" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hent nåværende tilstand for et Item" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Haal de huidige status van een item op" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Haal de huidige status van een item op" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Получить текущее состояние элемента" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Получить текущее состояние элемента" } } } }, - "retry": { - "comment": "retry connection", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Erneut versuchen" + "retry" : { + "comment" : "retry connection", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erneut versuchen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Retry" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Retry" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Reintentar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reintentar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Retry" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Retry" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Réessayer" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Réessayer" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Riprova" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Riprova" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Prøv på nytt" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Prøv på nytt" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Opnieuw" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opnieuw" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Retry" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Retry" } } } }, - "Rewind": { - "comment": "Display name for the rewind action in the PlayerAction enum.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zurückspulen" + "Rewind" : { + "comment" : "Display name for the rewind action in the PlayerAction enum.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zurückspulen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Rewind" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rewind" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Rebobinar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rebobinar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kelaa taaksepäin" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kelaa taaksepäin" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Retour arrière" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Retour arrière" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Riavvolgi" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Riavvolgi" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Spol tilbake" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Spol tilbake" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Terugspoelen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terugspoelen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Перемотка назад" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Перемотка назад" } } } }, - "running_demo_mode": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Läuft im Demomodus. Überprüfe die Einstellungen, um den Demomodus zu deaktivieren." + "running_demo_mode" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Läuft im Demomodus. Überprüfe die Einstellungen, um den Demomodus zu deaktivieren." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Running in demo mode. Check settings to disable demo mode." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Running in demo mode. Check settings to disable demo mode." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Funccionando en modo demostración. Comprueba la configuración para desactivarlo." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Funccionando en modo demostración. Comprueba la configuración para desactivarlo." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Running in demo mode. Check settings to disable demo mode." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Running in demo mode. Check settings to disable demo mode." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Exécution en mode démo. Vérifiez les paramètres pour désactiver le mode démo." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Exécution en mode démo. Vérifiez les paramètres pour désactiver le mode démo." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Esecuzione in modalità demo. Controlla le impostazioni per disabilitare la modalità demo." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Esecuzione in modalità demo. Controlla le impostazioni per disabilitare la modalità demo." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kjører i demomodus. Sjekk innstillingene for å deaktivere demomodus." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kjører i demomodus. Sjekk innstillingene for å deaktivere demomodus." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Uitvoeren in demo-modus. Controleer instellingen om demomodus uit te schakelen." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uitvoeren in demo-modus. Controleer instellingen om demomodus uit te schakelen." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Running in demo mode. Check settings to disable demo mode." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Running in demo mode. Check settings to disable demo mode." } } } }, - "Save": { - "comment": "The text for a button that saves current settings.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sichern" + "Save" : { + "comment" : "The text for a button that saves current settings.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sichern" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Save" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Save" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Guardar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Guardar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Tallenna" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tallenna" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Enregistrer" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enregistrer" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Salva" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Salva" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Lagre" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lagre" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Bewaar" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bewaar" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Сохранить" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Сохранить" } } } }, - "Screen Saver": { - "comment": "The title of the screen.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Bildschirmschoner" + "Screen Saver" : { + "comment" : "The title of the screen.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bildschirmschoner" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Screen Saver" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Screen Saver" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Salvapantallas" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Salvapantallas" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Näytönsäästäjä" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Näytönsäästäjä" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Économiseur d’écran" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Économiseur d’écran" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Salvaschermo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Salvaschermo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Skjermsparer" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skjermsparer" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Schermbeveiliging" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schermbeveiliging" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Заставка экрана" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Заставка экрана" } } } }, - "Screen Saver Settings": { - "comment": "A link to the settings related to the screen saver.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Bildschirmschoner-Einstellungen" + "Screen Saver Settings" : { + "comment" : "A link to the settings related to the screen saver.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bildschirmschoner-Einstellungen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Screen Saver Settings" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Screen Saver Settings" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Ajustes del salvapantallas" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajustes del salvapantallas" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Näytönsäästäjän asetukset" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Näytönsäästäjän asetukset" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Paramètres de l'économiseur d'écran" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Paramètres de l'économiseur d'écran" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Impostazioni salvaschermo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impostazioni salvaschermo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Skjermsparer-innstillinger" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skjermsparer-innstillinger" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Schermbeveiligingsinstellingen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schermbeveiligingsinstellingen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Настройки заставки" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Настройки заставки" } } } }, - "Search": { - "comment": "A text field for searching through a list of items.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Suchen" + "Search" : { + "comment" : "A text field for searching through a list of items.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suchen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Search" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Search" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Buscar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Buscar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Haku" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Haku" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Rechercher" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rechercher" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Cerca" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cerca" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Søk" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Søk" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Zoek" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zoek" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Поиск" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Поиск" } } } }, - "Search for an item": { - "comment": "Dialog text prompting the user to search for an openHAB item in app intents.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Nach Item suchen" + "Search for an item" : { + "comment" : "Dialog text prompting the user to search for an openHAB item in app intents.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nach Item suchen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Search for an item" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Search for an item" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Buscar un elemento" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Buscar un elemento" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Etsi elementtiä" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Etsi elementtiä" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Rechercher un élément" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rechercher un élément" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Cerca un elemento" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cerca un elemento" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Søk etter et element" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Søk etter et element" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Zoeken naar een item" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zoeken naar een item" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Найти элемент" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Найти элемент" } } } }, - "search_items": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Suche openHAB Items" + "search_items" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suche openHAB Items" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Search openHAB items" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Search openHAB items" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Buscar ítems en openHAB" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Buscar ítems en openHAB" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Search openHAB items" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Search openHAB items" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Rechercher des items openHAB" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rechercher des items openHAB" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Cerca Item openHAB" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cerca Item openHAB" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Søk i openHAB Items" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Søk i openHAB Items" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Zoek openHAB items" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zoek openHAB items" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Search openHAB items" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Search openHAB items" } } } }, - "Select a home for '%@'": { - "comment": "A message to be displayed in a dialog box when the user needs to select a home. The argument is the name of the item that needs a home.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zuhause für '%@' auswählen" + "Select a home for '%@'" : { + "comment" : "A message to be displayed in a dialog box when the user needs to select a home. The argument is the name of the item that needs a home.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zuhause für '%@' auswählen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Seleccionar una casa para '%@'" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seleccionar una casa para '%@'" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Valitse koti kohteelle '%@'" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valitse koti kohteelle '%@'" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Sélectionner un domicile pour '%@'" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sélectionner un domicile pour '%@'" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Seleziona una casa per '%@'" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seleziona una casa per '%@'" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Velg et hjem for '%@'" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Velg et hjem for '%@'" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Selecteer een thuis voor '%@'" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Selecteer een thuis voor '%@'" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Выберите дом для '%@'" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Выберите дом для '%@'" } } } }, - "Select color": { - "comment": "A button that, when pressed, opens a view allowing the user to select a color.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Farbe wählen" + "Select color" : { + "comment" : "A button that, when pressed, opens a view allowing the user to select a color.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Farbe wählen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Select color" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Select color" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Seleccionar color" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seleccionar color" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Valitse väri" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valitse väri" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Sélectionner une couleur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sélectionner une couleur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Seleziona colore" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seleziona colore" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Velg farge" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Velg farge" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Kleur selecteren" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kleur selecteren" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Выбрать цвет" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Выбрать цвет" } } } }, - "select_sitemap": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sitemap auswählen" + "select_sitemap" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap auswählen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Select Sitemap" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Select Sitemap" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Seleccionar mapa web" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seleccionar mapa web" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Select Sitemap" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Select Sitemap" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Sélectionner le Sitemap" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sélectionner le Sitemap" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Seleziona Sitemap" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seleziona Sitemap" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Velg Sitemap" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Velg Sitemap" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Selecteer Sitemap" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Selecteer Sitemap" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Выбрать Sitemap" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Выбрать Sitemap" } } } }, - "Send ${action} to ${itemEntity}": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "${action} an ${itemEntity} senden" + "Send ${action} to ${itemEntity}" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "${action} an ${itemEntity} senden" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Enviar ${action} a ${itemEntity}" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviar ${action} a ${itemEntity}" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Lähetä ${action} kohteeseen ${itemEntity}" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lähetä ${action} kohteeseen ${itemEntity}" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Envoyer ${action} à ${itemEntity}" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Envoyer ${action} à ${itemEntity}" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Invia ${action} a ${itemEntity}" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Invia ${action} a ${itemEntity}" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Send ${action} til ${itemEntity}" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Send ${action} til ${itemEntity}" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "${action} verzenden naar ${itemEntity}" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "${action} verzenden naar ${itemEntity}" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Отправить ${action} в ${itemEntity}" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Отправить ${action} в ${itemEntity}" } } } }, - "Send a player command such as play, pause, next, or previous": { - "comment": "Description of the Set Player Control Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Einen Player-Befehl senden, z. B. Abspielen, Pause, Weiter oder Zurück" + "Send a player command such as play, pause, next, or previous" : { + "comment" : "Description of the Set Player Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Einen Player-Befehl senden, z. B. Abspielen, Pause, Weiter oder Zurück" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Send a player command such as play, pause, next, or previous" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Send a player command such as play, pause, next, or previous" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Enviar un comando al reproductor como reproducir, pausar, siguiente o anterior" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviar un comando al reproductor como reproducir, pausar, siguiente o anterior" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Lähetä soittimelle komento, kuten toista, tauko, seuraava tai edellinen" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lähetä soittimelle komento, kuten toista, tauko, seuraava tai edellinen" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Envoyer une commande au lecteur telle que lire, pause, suivant ou précédent" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Envoyer une commande au lecteur telle que lire, pause, suivant ou précédent" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Invia un comando al player come riproduci, pausa, successivo o precedente" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Invia un comando al player come riproduci, pausa, successivo o precedente" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Send en spillerkommando som spill av, pause, neste eller forrige" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Send en spillerkommando som spill av, pause, neste eller forrige" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Een spelersopdracht verzenden zoals afspelen, pauzeren, volgende of vorige" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Een spelersopdracht verzenden zoals afspelen, pauzeren, volgende of vorige" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Отправить команду плееру: воспроизвести, пауза, следующий или предыдущий" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Отправить команду плееру: воспроизвести, пауза, следующий или предыдущий" } } } }, - "Sending commands: %lld": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Befehle werden gesendet: %lld" + "Sending commands: %lld" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Befehle werden gesendet: %lld" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sending commands: %lld" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sending commands: %lld" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Enviando comandos: %lld" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviando comandos: %lld" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Lähetetään komentoja: %lld" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lähetetään komentoja: %lld" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Envoi de commandes : %lld" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Envoi de commandes : %lld" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Invio comandi: %lld" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Invio comandi: %lld" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sender kommandoer: %lld" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sender kommandoer: %lld" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Opdrachten verzenden: %lld" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opdrachten verzenden: %lld" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Отправка команд: %lld" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Отправка команд: %lld" } } } }, - "Sent %@ to %@": { - "comment": "The result text displayed in the dialog. The argument is the action, and the second argument is the item label.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "%1$@ an %2$@ gesendet" + "Sent %@ to %@" : { + "comment" : "The result text displayed in the dialog. The argument is the action, and the second argument is the item label.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ an %2$@ gesendet" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sent %1$@ to %2$@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sent %1$@ to %2$@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Enviado %1$@ a %2$@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviado %1$@ a %2$@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "%1$@ lähetetty kohteeseen %2$@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ lähetetty kohteeseen %2$@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "%1$@ envoyé à %2$@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ envoyé à %2$@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "%1$@ inviato a %2$@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ inviato a %2$@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "%1$@ sendt til %2$@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ sendt til %2$@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "%1$@ verzonden naar %2$@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ verzonden naar %2$@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "%1$@ отправлено в %2$@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ отправлено в %2$@" } } } }, - "Sent location %lf, %lf to %@": { - "comment": "Dialog shown after setting a location value. First two placeholders are latitude and longitude, third is the item name.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "%lf, %lf wurde an Standort %@ gesendet" + "Sent location %lf, %lf to %@" : { + "comment" : "Dialog shown after setting a location value. First two placeholders are latitude and longitude, third is the item name.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lf, %lf wurde an Standort %@ gesendet" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sent location %lf, %lf to %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sent location %lf, %lf to %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Ubicación %lf, %lf enviada a %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ubicación %lf, %lf enviada a %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Sijainti %lf, %lf lähetetty kohteeseen %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sijainti %lf, %lf lähetetty kohteeseen %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Emplacement %lf, %lf envoyé à %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Emplacement %lf, %lf envoyé à %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Posizione %lf, %lf inviata a %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Posizione %lf, %lf inviata a %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Plassering %lf, %lf sendt til %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Plassering %lf, %lf sendt til %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Locatie %lf, %lf verzonden naar %@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Locatie %lf, %lf verzonden naar %@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Местоположение %lf, %lf отправлено в %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Местоположение %lf, %lf отправлено в %@" } } } }, - "Sent the color value of %@ to %@": { - "comment": "Dialog shown after setting a color value. First placeholder is the color value, second is the item name.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Der Farbwert %@ wurde an %@ gesendet" + "Sent the color value of %@ to %@" : { + "comment" : "Dialog shown after setting a color value. First placeholder is the color value, second is the item name.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Der Farbwert %@ wurde an %@ gesendet" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sent the color value of %@ to %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sent the color value of %@ to %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Enviado el valor de color de %@ a %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviado el valor de color de %@ a %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Väriarvo %@ lähetetty kohteeseen %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Väriarvo %@ lähetetty kohteeseen %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Envoyé la valeur de couleur de %@ à %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Envoyé la valeur de couleur de %@ à %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Inviato il valore di colore di %@ a %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inviato il valore di colore di %@ a %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sendte fargeverdien %@ til %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sendte fargeverdien %@ til %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "De kleurwaarde van %@ is verzonden naar %@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De kleurwaarde van %@ is verzonden naar %@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Значение цвета %@ отправлено в %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Значение цвета %@ отправлено в %@" } } } }, - "Sent the date %@ to %@": { - "comment": "Dialog shown after setting a date/time value. First placeholder is the date string, second is the item name.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Datum %@ auf %@ setzen" + "Sent the date %@ to %@" : { + "comment" : "Dialog shown after setting a date/time value. First placeholder is the date string, second is the item name.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datum %@ auf %@ setzen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sent the date %@ to %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sent the date %@ to %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Fecha %@ enviada a %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fecha %@ enviada a %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Päivämäärä %@ lähetetty kohteeseen %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Päivämäärä %@ lähetetty kohteeseen %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Date %@ envoyée à %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Date %@ envoyée à %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Data %@ inviata a %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Data %@ inviata a %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Dato %@ sendt til %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dato %@ sendt til %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Datum %@ verzonden naar %@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datum %@ verzonden naar %@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Дата %@ отправлена в %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Дата %@ отправлена в %@" } } } }, - "Sent the number %lf to %@": { - "comment": "Dialog shown after setting a number control value. First placeholder is the decimal value, second is the item name.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Die Zahl %lf wurde an %@ gesendet" + "Sent the number %lf to %@" : { + "comment" : "Dialog shown after setting a number control value. First placeholder is the decimal value, second is the item name.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Die Zahl %lf wurde an %@ gesendet" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sent the number %lf to %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sent the number %lf to %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Definir el número %lf a %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Definir el número %lf a %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Luku %lf lähetetty kohteeseen %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Luku %lf lähetetty kohteeseen %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Envoyé le numéro %lf à %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Envoyé le numéro %lf à %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Inviato il numero %lf a %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inviato il numero %lf a %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Send den numeriske verdien %lf til %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Send den numeriske verdien %lf til %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "De waarde %lf is verzonden naar %@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De waarde %lf is verzonden naar %@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Число %lf отправлено в %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Число %lf отправлено в %@" } } } }, - "Sent the string %@ to %@": { - "comment": "Dialog shown after setting a string control value. First placeholder is the string value, second is the item name.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Der String %@ wurde an %@ gesendet" + "Sent the string %@ to %@" : { + "comment" : "Dialog shown after setting a string control value. First placeholder is the string value, second is the item name.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Der String %@ wurde an %@ gesendet" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sent the string %@ to %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sent the string %@ to %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Enviada la cadena %@ a %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviada la cadena %@ a %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Merkkijono %@ lähetetty kohteeseen %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Merkkijono %@ lähetetty kohteeseen %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Envoi de la chaîne %@ à %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Envoi de la chaîne %@ à %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Inviata la stringa %@ a %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inviata la stringa %@ a %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Send strengverdien %@ til %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Send strengverdien %@ til %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "De waarde %@ is verzonden naar %@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De waarde %@ is verzonden naar %@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Строка %@ отправлена в %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Строка %@ отправлена в %@" } } } }, - "Sent the value of %lld to %@": { - "comment": "Dialog shown after setting a dimmer or roller shutter value. First placeholder is the integer value, second is the item name.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Der Wert %lld wurde an %@ gesendet" + "Sent the value of %lld to %@" : { + "comment" : "Dialog shown after setting a dimmer or roller shutter value. First placeholder is the integer value, second is the item name.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Der Wert %lld wurde an %@ gesendet" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sent the value of %lld to %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sent the value of %lld to %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Enviado el valor de %lld a %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviado el valor de %lld a %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Arvo %lld lähetetty kohteeseen %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Arvo %lld lähetetty kohteeseen %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Envoyé la valeur %lld à %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Envoyé la valeur %lld à %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Inviato il valore %lld a %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inviato il valore %lld a %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sendte verdien %lld til %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sendte verdien %lld til %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "De waarde %lld is verzonden naar %@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De waarde %lld is verzonden naar %@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Значение %lld отправлено в %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Значение %lld отправлено в %@" } } } }, - "Set ${itemEntity} to ${latitude}, ${longitude}": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Standort ${itemEntity} auf ${latitude}, ${longitude} setzen" + "Set ${itemEntity} to ${latitude}, ${longitude}" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Standort ${itemEntity} auf ${latitude}, ${longitude} setzen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer ${itemEntity} en ${latitude}, ${longitude}" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer ${itemEntity} en ${latitude}, ${longitude}" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta ${itemEntity} arvoon ${latitude}, ${longitude}" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta ${itemEntity} arvoon ${latitude}, ${longitude}" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir ${itemEntity} à ${latitude}, ${longitude}" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir ${itemEntity} à ${latitude}, ${longitude}" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta ${itemEntity} su ${latitude}, ${longitude}" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta ${itemEntity} su ${latitude}, ${longitude}" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sett ${itemEntity} til ${latitude}, ${longitude}" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sett ${itemEntity} til ${latitude}, ${longitude}" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "${itemEntity} instellen op ${latitude}, ${longitude}" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "${itemEntity} instellen op ${latitude}, ${longitude}" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить ${itemEntity} в ${latitude}, ${longitude}" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить ${itemEntity} в ${latitude}, ${longitude}" } } } }, - "Set ${itemEntity} to ${value}": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "${itemEntity} auf ${value} setzen" + "Set ${itemEntity} to ${value}" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "${itemEntity} auf ${value} setzen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer ${itemEntity} en ${value}" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer ${itemEntity} en ${value}" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta ${itemEntity} arvoon ${value}" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta ${itemEntity} arvoon ${value}" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir ${itemEntity} sur ${value}" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir ${itemEntity} sur ${value}" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta ${itemEntity} su ${value}" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta ${itemEntity} su ${value}" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sett ${itemEntity} til ${value}" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sett ${itemEntity} til ${value}" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "${itemEntity} instellen op ${value}" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "${itemEntity} instellen op ${value}" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить ${itemEntity} в ${value}" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить ${itemEntity} в ${value}" } } } }, - "Set ${itemEntity} to ${value} (HSB)": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "${itemEntity} auf ${value} (HSB) setzen" + "Set ${itemEntity} to ${value} (HSB)" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "${itemEntity} auf ${value} (HSB) setzen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer ${itemEntity} en ${value} (HSB)" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer ${itemEntity} en ${value} (HSB)" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta ${itemEntity} arvoon ${value} (HSB)" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta ${itemEntity} arvoon ${value} (HSB)" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir ${itemEntity} sur ${value} (TSL)" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir ${itemEntity} sur ${value} (TSL)" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta ${itemEntity} su ${value} (HSB)" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta ${itemEntity} su ${value} (HSB)" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sett ${itemEntity} til ${value} (HSB)" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sett ${itemEntity} til ${value} (HSB)" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "${itemEntity} instellen op ${value} (HSB)" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "${itemEntity} instellen op ${value} (HSB)" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить ${itemEntity} в ${value} (HSB)" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить ${itemEntity} в ${value} (HSB)" } } } }, - "Set Active Home": { - "comment": "Title of the intent to set the active home.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Aktives Zuhause setzen" + "Set Active Home" : { + "comment" : "Title of the intent to set the active home.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktives Zuhause setzen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer casa activa" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer casa activa" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta aktiivinen koti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta aktiivinen koti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir le domicile actif" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir le domicile actif" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta casa attiva" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta casa attiva" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi aktivt hjem" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi aktivt hjem" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Actief thuis instellen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actief thuis instellen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить активный дом" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить активный дом" } } } }, - "Set active home to ${home}": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Aktives Zuhause auf ${home} setzen" + "Set active home to ${home}" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktives Zuhause auf ${home} setzen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer casa activa como ${home}" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer casa activa como ${home}" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta aktiiviseksi kodiksi ${home}" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta aktiiviseksi kodiksi ${home}" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir ${home} comme domicile actif" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir ${home} comme domicile actif" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta ${home} come casa attiva" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta ${home} come casa attiva" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi ${home} som aktivt hjem" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi ${home} som aktivt hjem" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "${home} instellen als actief thuis" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "${home} instellen als actief thuis" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить ${home} как активный дом" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить ${home} как активный дом" } } } }, - "Set Color Control Value": { - "comment": "Title of the Set Color Control Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Farbwert festlegen" + "Set Color Control Value" : { + "comment" : "Title of the Set Color Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Farbwert festlegen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set Color Control Value" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set Color Control Value" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Define el valor de control de color" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Define el valor de control de color" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta värisäätimen arvo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta värisäätimen arvo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir la valeur de contrôle de couleur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la valeur de contrôle de couleur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta Valore Controllo Colore" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta Valore Controllo Colore" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi Fargekontrollverdi" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi Fargekontrollverdi" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Kleurwaarde instellen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kleurwaarde instellen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить значение элемента управления цветом" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить значение элемента управления цветом" } } } }, - "Set Contact State Value": { - "comment": "Title of the Set Contact State Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Wert eines Kontakt=Status festlegen" + "Set Contact State Value" : { + "comment" : "Title of the Set Contact State Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wert eines Kontakt=Status festlegen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set Contact State Value" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set Contact State Value" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer el valor del estado del pulsador" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer el valor del estado del pulsador" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta kontaktin tilanarvo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta kontaktin tilanarvo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir la valeur de l'état du contacteur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la valeur de l'état du contacteur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta lo Stato del Contatto" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta lo Stato del Contatto" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sett Tilstand for Bryter" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sett Tilstand for Bryter" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Stel contact status waarde in" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stel contact status waarde in" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить значение состояния контакта" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить значение состояния контакта" } } } }, - "Set DateTime Control Value": { - "comment": "Title of the Set DateTime Control Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Wert eines DateTime-Kontrollelements setzen " + "Set DateTime Control Value" : { + "comment" : "Title of the Set DateTime Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wert eines DateTime-Kontrollelements setzen " } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set DateTime Control Value" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set DateTime Control Value" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer valor del control DateTime" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer valor del control DateTime" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta DateTime-säätimen arvo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta DateTime-säätimen arvo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir la valeur du contrôle DateHeure" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la valeur du contrôle DateHeure" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta valore controllo DateTime" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta valore controllo DateTime" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi DateTime-kontrollverdi" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi DateTime-kontrollverdi" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "DateTime-regelaarswaarde instellen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "DateTime-regelaarswaarde instellen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить значение элемента управления DateTime" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить значение элемента управления DateTime" } } } }, - "Set Dimmer or Roller Shutter Value": { - "comment": "Title of the Set Dimmer or Roller Shutter Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Wert eines Dimmers oder Rollladens festlegen" + "Set Dimmer or Roller Shutter Value" : { + "comment" : "Title of the Set Dimmer or Roller Shutter Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wert eines Dimmers oder Rollladens festlegen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set Dimmer or Roller Shutter Value" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set Dimmer or Roller Shutter Value" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Definir el valor del regulador o persiana" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Definir el valor del regulador o persiana" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta himmentimen tai rullaverkon arvo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta himmentimen tai rullaverkon arvo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Régler la valeur du Dimmer ou du Volet Roulant" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Régler la valeur du Dimmer ou du Volet Roulant" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta il valore di Dimmer o Tapparella" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta il valore di Dimmer o Tapparella" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi Verdi for Dimmer eller Rullegardin" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi Verdi for Dimmer eller Rullegardin" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Stel Dimmer of Rolluik waarde in" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stel Dimmer of Rolluik waarde in" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить значение диммера или рольставни" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить значение диммера или рольставни" } } } }, - "Set Location Control Value": { - "comment": "Title of the Set Location Control Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Standort-Kontrollwert setzen" + "Set Location Control Value" : { + "comment" : "Title of the Set Location Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Standort-Kontrollwert setzen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set Location Control Value" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set Location Control Value" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer valor del control de ubicación" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer valor del control de ubicación" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta sijaintisäätimen arvo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta sijaintisäätimen arvo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir la valeur du contrôle de localisation" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la valeur du contrôle de localisation" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta valore controllo posizione" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta valore controllo posizione" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi plasseringskontrollverdi" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi plasseringskontrollverdi" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Locatieregelaarswaarde instellen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Locatieregelaarswaarde instellen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить значение элемента управления местоположением" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить значение элемента управления местоположением" } } } }, - "Set Number Control Value": { - "comment": "Title of the Set Number Control Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Wert eines numerischen Kontrollelements setzen" + "Set Number Control Value" : { + "comment" : "Title of the Set Number Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wert eines numerischen Kontrollelements setzen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set Number Control Value" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set Number Control Value" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer valor de control de número" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer valor de control de número" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta numerosäätimen arvo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta numerosäätimen arvo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir la valeur de contrôle du numéro" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la valeur de contrôle du numéro" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta Valore del Numero di Controllo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta Valore del Numero di Controllo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sett Verdi for Numerisk Kontroll" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sett Verdi for Numerisk Kontroll" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Zet Nummer Control Waarde" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zet Nummer Control Waarde" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить числовое значение элемента управления" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить числовое значение элемента управления" } } } }, - "Set Player Control Value": { - "comment": "Title of the Set Player Control Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Player-Kontrollelement setzen" + "Set Player Control Value" : { + "comment" : "Title of the Set Player Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Player-Kontrollelement setzen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set Player Control Value" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set Player Control Value" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer valor del control del reproductor" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer valor del control del reproductor" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta soittimen säätimen arvo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta soittimen säätimen arvo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir la valeur du contrôle du lecteur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la valeur du contrôle du lecteur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta valore controllo player" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta valore controllo player" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi spillerkontrollverdi" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi spillerkontrollverdi" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Spelerregelaarswaarde instellen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Spelerregelaarswaarde instellen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить значение элемента управления плеером" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить значение элемента управления плеером" } } } }, - "Set String Control Value": { - "comment": "Title of the Set String Control Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "String-Kontrollwert setzen" + "Set String Control Value" : { + "comment" : "Title of the Set String Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "String-Kontrollwert setzen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set String Control Value" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set String Control Value" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer el valor de control de cadena" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer el valor de control de cadena" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta merkkijonosäätimen arvo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta merkkijonosäätimen arvo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir la valeur de contrôle de chaîne" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la valeur de contrôle de chaîne" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta Valore della Stringa di Controllo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta Valore della Stringa di Controllo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sett Strengkontroll-Verdi" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sett Strengkontroll-Verdi" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Zet String Control Waarde in" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zet String Control Waarde in" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить строковое значение элемента управления" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить строковое значение элемента управления" } } } }, - "Set Switch State": { - "comment": "Title of the Set Switch State app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Schalterzustand setzen" + "Set Switch State" : { + "comment" : "Title of the Set Switch State app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schalterzustand setzen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set Switch State" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set Switch State" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Definir el estado del interruptor" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Definir el estado del interruptor" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta kytkimen tila" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta kytkimen tila" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir l'état du Switch" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir l'état du Switch" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta stato Interruttore" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta stato Interruttore" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi Brytertilstand" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi Brytertilstand" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Stel schakelstatus in" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stel schakelstatus in" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить состояние переключателя" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить состояние переключателя" } } } }, - "Set the color of a color control item": { - "comment": "Description of the Set Color Control Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Farbe eines Kontrollelements festlegen" + "Set the color of a color control item" : { + "comment" : "Description of the Set Color Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Farbe eines Kontrollelements festlegen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set the color of a color control item" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set the color of a color control item" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Definir el color de un ítem de control de color" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Definir el color de un ítem de control de color" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta värielementin väri" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta värielementin väri" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir la couleur d'un élément de contrôle de couleur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la couleur d'un élément de contrôle de couleur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta il colore di un Item di controllo colore" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta il colore di un Item di controllo colore" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi fargen til et fargekontroll-Item" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi fargen til et fargekontroll-Item" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Stel de kleur van een kleurbesturingsitem in" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stel de kleur van een kleurbesturingsitem in" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить цвет элемента управления цветом" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить цвет элемента управления цветом" } } } }, - "Set the date and time of a DateTime control item": { - "comment": "Description of the Set DateTime Control Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Datum und Uhrzeit eines DateTime-Kontrollelements setzen" + "Set the date and time of a DateTime control item" : { + "comment" : "Description of the Set DateTime Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datum und Uhrzeit eines DateTime-Kontrollelements setzen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set the date and time of a DateTime control item" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set the date and time of a DateTime control item" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer la fecha y hora de un elemento de control DateTime" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer la fecha y hora de un elemento de control DateTime" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta DateTime-elementin päivämäärä ja aika" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta DateTime-elementin päivämäärä ja aika" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir la date et l'heure d'un élément de contrôle DateHeure" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la date et l'heure d'un élément de contrôle DateHeure" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta la data e l'ora di un elemento di controllo DateTime" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta la data e l'ora di un elemento di controllo DateTime" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi dato og tid for et DateTime-kontrollelement" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi dato og tid for et DateTime-kontrollelement" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "De datum en tijd instellen van een DateTime-regelaarselement" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De datum en tijd instellen van een DateTime-regelaarselement" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить дату и время элемента DateTime" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить дату и время элемента DateTime" } } } }, - "Set the decimal value of a number control item": { - "comment": "Description of the Set Number Control Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Dezimalwert eines numerischen Kontrollelements setzen" + "Set the decimal value of a number control item" : { + "comment" : "Description of the Set Number Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dezimalwert eines numerischen Kontrollelements setzen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set the decimal value of a number control item" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set the decimal value of a number control item" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer el valor decimal de un ítem de control numérico" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer el valor decimal de un ítem de control numérico" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta numeroelementin desimaaliarvo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta numeroelementin desimaaliarvo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir la valeur décimale d'un item de contrôle de nombre" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la valeur décimale d'un item de contrôle de nombre" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta il valore decimale di un Item di controllo numerico" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta il valore decimale di un Item di controllo numerico" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sett desimalverdien til en numerisk kontroll-Item" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sett desimalverdien til en numerisk kontroll-Item" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Stel de decimale waarde van een nummer control item in" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stel de decimale waarde van een nummer control item in" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить десятичное значение числового элемента" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить десятичное значение числового элемента" } } } }, - "Set the integer value of a dimmer or roller shutter": { - "comment": "Description of the Set Dimmer or Roller Shutter Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Festlegen des Wertes eines Dimmers oder Rollladens" + "Set the integer value of a dimmer or roller shutter" : { + "comment" : "Description of the Set Dimmer or Roller Shutter Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Festlegen des Wertes eines Dimmers oder Rollladens" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set the integer value of a dimmer or roller shutter" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set the integer value of a dimmer or roller shutter" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Define el valor entero de un regulador o persiana" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Define el valor entero de un regulador o persiana" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta himmentimen tai rullaverkon kokonaislukuarvo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta himmentimen tai rullaverkon kokonaislukuarvo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir la valeur entière d'un dimmer ou d'un volet roulant" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la valeur entière d'un dimmer ou d'un volet roulant" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta il valore intero di un dimmer o una tapparella" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta il valore intero di un dimmer o una tapparella" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sett heltallsverdien til en dimmer eller rullegardin" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sett heltallsverdien til en dimmer eller rullegardin" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Zet de integerwaarde van een dimmer of rolluik" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zet de integerwaarde van een dimmer of rolluik" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить целочисленное значение диммера или рольставни" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить целочисленное значение диммера или рольставни" } } } }, - "Set the latitude and longitude of a location control item": { - "comment": "Description of the Set Location Control Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Breitengrad und Längengrad eines Standort-Kontrollelements setzen" + "Set the latitude and longitude of a location control item" : { + "comment" : "Description of the Set Location Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Breitengrad und Längengrad eines Standort-Kontrollelements setzen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set the latitude and longitude of a location control item" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set the latitude and longitude of a location control item" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer la latitud y longitud de un elemento de control de ubicación" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer la latitud y longitud de un elemento de control de ubicación" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta sijaintielementin leveys- ja pituusaste" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta sijaintielementin leveys- ja pituusaste" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir la latitude et la longitude d'un élément de contrôle de localisation" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la latitude et la longitude d'un élément de contrôle de localisation" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta la latitudine e la longitudine di un elemento di controllo posizione" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta la latitudine e la longitudine di un elemento di controllo posizione" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi bredde- og lengdegrad for et plasseringskontrollelement" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi bredde- og lengdegrad for et plasseringskontrollelement" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "De breedte- en lengtegraad instellen van een locatieregelaarselement" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De breedte- en lengtegraad instellen van een locatieregelaarselement" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить широту и долготу элемента местоположения" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить широту и долготу элемента местоположения" } } } }, - "Set the state of ${itemEntity} to ${state}": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Den Status von ${itemEntity} auf ${state} setzen" + "Set the state of ${itemEntity} to ${state}" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Den Status von ${itemEntity} auf ${state} setzen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer el estado de ${itemEntity} en ${state}" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer el estado de ${itemEntity} en ${state}" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta ${itemEntity}-tila arvoon ${state}" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta ${itemEntity}-tila arvoon ${state}" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir l'état de ${itemEntity} sur ${state}" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir l'état de ${itemEntity} sur ${state}" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta lo stato di ${itemEntity} su ${state}" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta lo stato di ${itemEntity} su ${state}" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi status for ${itemEntity} til ${state}" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi status for ${itemEntity} til ${state}" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "De status van ${itemEntity} instellen op ${state}" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De status van ${itemEntity} instellen op ${state}" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить состояние ${itemEntity} в ${state}" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить состояние ${itemEntity} в ${state}" } } } }, - "Set the state of a contact open or closed": { - "comment": "Description of the Set Contact State Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Status eines Kontakts auf offen oder geschlossen setzen" + "Set the state of a contact open or closed" : { + "comment" : "Description of the Set Contact State Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Status eines Kontakts auf offen oder geschlossen setzen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set the state of a contact open or closed" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set the state of a contact open or closed" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer el estado de un interruptor encendido o apagado" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer el estado de un interruptor encendido o apagado" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta kontaktin tilaksi auki tai kiinni" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta kontaktin tilaksi auki tai kiinni" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir l'état d'un contacteur ouvert ou fermé" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir l'état d'un contacteur ouvert ou fermé" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta lo stato di un contatto aperto o chiuso" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta lo stato di un contatto aperto o chiuso" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sett tilstanden til en bryter til åpen eller lukket" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sett tilstanden til en bryter til åpen eller lukket" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Zet de status van een contact open of gesloten" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zet de status van een contact open of gesloten" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить состояние контакта: открыт или закрыт" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить состояние контакта: открыт или закрыт" } } } }, - "Set the state of a switch on or off, or toggle its state": { - "comment": "Description of the Set Switch State app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Switch-Status auf An, Aus oder Umschalten setzen" + "Set the state of a switch on or off, or toggle its state" : { + "comment" : "Description of the Set Switch State app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Switch-Status auf An, Aus oder Umschalten setzen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set the state of a switch on or off, or toggle its state" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set the state of a switch on or off, or toggle its state" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer el estado de un interruptor en encendido o apagado, o alternar su estado" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer el estado de un interruptor en encendido o apagado, o alternar su estado" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta kytkimen tilaksi päällä tai pois tai vaihda sen tilaa" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta kytkimen tilaksi päällä tai pois tai vaihda sen tilaa" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir l'état d'un interrupteur sur activé ou désactivé, ou basculer son état" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir l'état d'un interrupteur sur activé ou désactivé, ou basculer son état" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta lo stato di un interruttore su acceso o spento, o attiva/disattiva il suo stato" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta lo stato di un interruttore su acceso o spento, o attiva/disattiva il suo stato" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi brytertilstanden til på eller av, eller bytt tilstand" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi brytertilstanden til på eller av, eller bytt tilstand" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "De status van een schakelaar op aan of uit zetten, of de status omschakelen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De status van een schakelaar op aan of uit zetten, of de status omschakelen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить состояние переключателя вкл. или выкл., или переключить его состояние" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить состояние переключателя вкл. или выкл., или переключить его состояние" } } } }, - "Set the string of a string control item": { - "comment": "Description of the Set String Control Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zeichenkette eines String-Kontrollelements setzen" + "Set the string of a string control item" : { + "comment" : "Description of the Set String Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zeichenkette eines String-Kontrollelements setzen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set the string of a string control item" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set the string of a string control item" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer la cadena de un ítem controlador de cadena" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer la cadena de un ítem controlador de cadena" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta merkkijonoelementin merkkijono" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta merkkijonoelementin merkkijono" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir la chaîne d'un item de contrôle de chaîne" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la chaîne d'un item de contrôle de chaîne" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta il valore di una item di controllo di tipo stringa" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta il valore di una item di controllo di tipo stringa" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi teksten til et teksterkontroll-Item" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi teksten til et teksterkontroll-Item" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "De tekenreeks van een string controle item instellen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De tekenreeks van een string controle item instellen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить строку строкового элемента" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить строку строкового элемента" } } } }, - "Set value": { - "comment": "A button that sets the value of a text input.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Wert setzen" + "Set value" : { + "comment" : "A button that sets the value of a text input.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wert setzen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set value" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set value" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer valor" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer valor" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta arvo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta arvo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir la valeur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la valeur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta valore" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta valore" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi verdi" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi verdi" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Waarde instellen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Waarde instellen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить значение" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить значение" } } } }, - "settings": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Einstellungen" + "settings" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Einstellungen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Settings" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Settings" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Configuración" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Settings" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Settings" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Paramétrage" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Paramétrage" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Impostazioni" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impostazioni" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Innstillinger" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Innstillinger" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Instellingen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Instellingen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Settings" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Settings" } } } }, - "settings_not_received": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Einstellungen noch nicht vom iPhone empfangen." + "settings_not_received" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Einstellungen noch nicht vom iPhone empfangen." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Settings not yet received from iPhone." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Settings not yet received from iPhone." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Configuración aún no recibida del iPhone." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración aún no recibida del iPhone." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Asetuksia ei ole vielä vastaanotettu iPhonelta." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Asetuksia ei ole vielä vastaanotettu iPhonelta." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Paramètres pas encore reçus de l'iPhone." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Paramètres pas encore reçus de l'iPhone." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Impostazioni non ancora ricevute da iPhone." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impostazioni non ancora ricevute da iPhone." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Innstillinger som ennå ikke er mottatt fra iPhone." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Innstillinger som ennå ikke er mottatt fra iPhone." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Instellingen nog niet ontvangen van iPhone." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Instellingen nog niet ontvangen van iPhone." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Настройки ещё не получены с iPhone." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Настройки ещё не получены с iPhone." } } } }, - "Show Date": { - "comment": "A toggle that enables or disables the display of the date in the screen saver.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Datum anzeigen" + "Show Date" : { + "comment" : "A toggle that enables or disables the display of the date in the screen saver.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datum anzeigen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Show Date" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show Date" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Mostrar fecha" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostrar fecha" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Näytä päivämäärä" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Näytä päivämäärä" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Afficher la date" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Afficher la date" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Mostra data" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostra data" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Vis dato" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vis dato" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Datum weergeven" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datum weergeven" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Показать дату" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Показать дату" } } } }, - "Show Search Field": { - "comment": "A toggle that enables or disables the search field in the app.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Suchfeld anzeigen" + "Show Search Field" : { + "comment" : "A toggle that enables or disables the search field in the app.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suchfeld anzeigen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Show Search Field" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show Search Field" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Mostrar campo de búsqueda" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostrar campo de búsqueda" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Näytä hakukenttä" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Näytä hakukenttä" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Afficher le champ de recherche" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Afficher le champ de recherche" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Mostra campo di ricerca" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostra campo di ricerca" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Vis søkefelt" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vis søkefelt" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Zoekveld weergeven" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zoekveld weergeven" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Показать поле поиска" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Показать поле поиска" } } } }, - "Show Seconds": { - "comment": "A toggle that lets the user enable or disable the display of seconds in the screen saver.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sekunden anzeigen" + "Show Seconds" : { + "comment" : "A toggle that lets the user enable or disable the display of seconds in the screen saver.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sekunden anzeigen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Show Seconds" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show Seconds" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Mostrar segundos" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostrar segundos" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Näytä sekunnit" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Näytä sekunnit" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Afficher les secondes" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Afficher les secondes" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Mostra secondi" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostra secondi" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Vis sekunder" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vis sekunder" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Seconden weergeven" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seconden weergeven" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Показать секунды" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Показать секунды" } } } }, - "Show Time": { - "comment": "A toggle that enables or disables the display of the time.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zeit anzeigen" + "Show Time" : { + "comment" : "A toggle that enables or disables the display of the time.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zeit anzeigen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Show Time" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show Time" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Mostrar hora" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostrar hora" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Näytä aika" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Näytä aika" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Afficher l'heure" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Afficher l'heure" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Mostra ora" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostra ora" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Vis tid" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vis tid" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Tijd weergeven" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tijd weergeven" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Показать время" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Показать время" } } } }, - "show_search_field": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Suchfeld anzeigen" + "show_search_field" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suchfeld anzeigen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Show Search Field" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show Search Field" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Show Search Field" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show Search Field" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Show Search Field" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show Search Field" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Show Search Field" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show Search Field" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Show Search Field" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show Search Field" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Show Search Field" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show Search Field" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Show Search Field" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show Search Field" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Show Search Field" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show Search Field" } } } }, - "sitemap": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sitemap" + "sitemap" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sitemap" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Mapa web" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mapa web" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Sitemap" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Sitemap" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Sitemap" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sitemap" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Sitemap" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Sitemap" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap" } } } }, - "Sitemap": { - "comment": "Label for the tab item representing the sitemap view.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sitemap" + "Sitemap" : { + "comment" : "Label for the tab item representing the sitemap view.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sitemap" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Mapa del sitio" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mapa del sitio" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Sivukartta" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sivukartta" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Plan du site" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Plan du site" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Sitemap" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Nettstedskart" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nettstedskart" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Sitemap" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Карта сайта" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Карта сайта" } } } }, - "Sitemap Diagnostics Logging": { - "comment": "A toggle that enables or disables logging of sitemap diagnostics.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sitemap-Diagnoseprotokollierung" + "Sitemap Diagnostics Logging" : { + "comment" : "A toggle that enables or disables logging of sitemap diagnostics.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap-Diagnoseprotokollierung" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Registro de diagnóstico del mapa del sitio" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Registro de diagnóstico del mapa del sitio" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Sivukartan diagnostiikkaloki" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sivukartan diagnostiikkaloki" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Journalisation des diagnostics du plan du site" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Journalisation des diagnostics du plan du site" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Registrazione diagnostica sitemap" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Registrazione diagnostica sitemap" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Diagnoslogging for nettstedskart" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Diagnoslogging for nettstedskart" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Sitemap-diagnoselogboek" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap-diagnoselogboek" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Журналирование диагностики карты сайта" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Журналирование диагностики карты сайта" } } } }, - "Sitemap for Apple Watch": { - "comment": "A label for selecting a sitemap to be used with an Apple Watch app.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Apple Watch Sitemap" + "Sitemap for Apple Watch" : { + "comment" : "A label for selecting a sitemap to be used with an Apple Watch app.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple Watch Sitemap" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sitemap for Apple Watch" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap for Apple Watch" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Mapa del sitio para Apple Watch" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mapa del sitio para Apple Watch" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Apple Watch -sivukartta" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple Watch -sivukartta" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Plan du site pour Apple Watch" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Plan du site pour Apple Watch" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Sitemap per Apple Watch" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap per Apple Watch" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Nettstedskart for Apple Watch" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nettstedskart for Apple Watch" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Sitemap voor Apple Watch" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap voor Apple Watch" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Карта сайта для Apple Watch" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Карта сайта для Apple Watch" } } } }, - "sitemap_settings": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sitemap-Einstellungen" + "sitemap_settings" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap-Einstellungen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sitemap Settings" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap Settings" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Ajustes del esquema" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajustes del esquema" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Sitemap Settings" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap Settings" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Paramètres Sitemap" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Paramètres Sitemap" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Impostazioni Sitemap" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impostazioni Sitemap" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Innstillinger for Sitemap" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Innstillinger for Sitemap" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Sitemap instellingen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap instellingen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Sitemap Settings" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap Settings" } } } }, - "Sitemaps": { - "comment": "A section header that lists the user's available sitemaps.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sitemaps" + "Sitemaps" : { + "comment" : "A section header that lists the user's available sitemaps.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemaps" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sitemaps" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemaps" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Mapas del sitio" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mapas del sitio" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Sivukartat" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sivukartat" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Plans du site" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Plans du site" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Sitemap" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Nettstedskart" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nettstedskart" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Sitemaps" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemaps" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Карты сайта" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Карты сайта" } } } }, - "Size: %llu MB": { - "comment": "A message showing the size of the image cache. The argument is the size of the image cache in MB.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Größe: %llu MB" + "Size: %llu MB" : { + "comment" : "A message showing the size of the image cache. The argument is the size of the image cache in MB.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Größe: %llu MB" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Size: %llu MB" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Size: %llu MB" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Tamaño: %llu MB" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tamaño: %llu MB" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Koko: %llu Mt" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Koko: %llu Mt" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Taille : %llu Mo" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Taille : %llu Mo" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Dimensione: %llu MB" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dimensione: %llu MB" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Størrelse: %llu MB" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Størrelse: %llu MB" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Grootte: %llu MB" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Grootte: %llu MB" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Размер: %llu МБ" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Размер: %llu МБ" } } } }, - "Soft White": { - "comment": "A color temperature description.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Weiches Weiß" + "Soft White" : { + "comment" : "A color temperature description.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Weiches Weiß" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Soft White" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Soft White" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Blanco suave" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blanco suave" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Pehmeä valkoinen" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pehmeä valkoinen" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Blanc doux" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blanc doux" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Bianco morbido" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bianco morbido" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Myk hvit" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Myk hvit" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Zacht wit" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zacht wit" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Мягкий белый" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Мягкий белый" } } } }, - "Sort sitemaps by": { - "comment": "A label describing the option to sort sitemaps.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sitemaps sortieren nach" + "Sort sitemaps by" : { + "comment" : "A label describing the option to sort sitemaps.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemaps sortieren nach" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sort sitemaps by" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sort sitemaps by" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Ordenar mapas del sitio por" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ordenar mapas del sitio por" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Lajittele sivukartat" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lajittele sivukartat" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Trier les plans du site par" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trier les plans du site par" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Ordina sitemap per" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ordina sitemap per" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sorter nettstedskart etter" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sorter nettstedskart etter" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Sitemaps sorteren op" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemaps sorteren op" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Сортировать карты сайта по" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Сортировать карты сайта по" } } } }, - "sortSitemapsBy": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sitemaps sortieren nach" + "sortSitemapsBy" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemaps sortieren nach" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sort sitemaps by" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sort sitemaps by" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Ordenar mapas del sitio por" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ordenar mapas del sitio por" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Sitemapien järjestys" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemapien järjestys" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Trier les sitemaps par" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trier les sitemaps par" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Ordina le Sitemaps per" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ordina le Sitemaps per" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sorter SiteMaps etter" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sorter SiteMaps etter" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Sorteer sitemaps op" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sorteer sitemaps op" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Sort sitemaps by" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sort sitemaps by" } } } }, - "SSL error. The connection couldn’t be established securely.": { - "comment": "Error message displayed when a connection to the OpenHAB server fails due to a SSL error.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "SSL-Fehler. Die Verbindung konnte nicht sicher hergestellt werden." + "SSL error. The connection couldn’t be established securely." : { + "comment" : "Error message displayed when a connection to the OpenHAB server fails due to a SSL error.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL-Fehler. Die Verbindung konnte nicht sicher hergestellt werden." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "SSL error. The connection couldn’t be established securely." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL error. The connection couldn’t be established securely." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Error SSL. No se pudo establecer la conexión de forma segura." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error SSL. No se pudo establecer la conexión de forma segura." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "SSL-virhe. Yhteyttä ei voitu muodostaa turvallisesti." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL-virhe. Yhteyttä ei voitu muodostaa turvallisesti." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Erreur SSL. La connexion n'a pas pu être établie de manière sécurisée." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erreur SSL. La connexion n'a pas pu être établie de manière sécurisée." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Errore SSL. Impossibile stabilire la connessione in modo sicuro." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Errore SSL. Impossibile stabilire la connessione in modo sicuro." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "SSL-feil. Tilkoblingen kunne ikke opprettes sikkert." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL-feil. Tilkoblingen kunne ikke opprettes sikkert." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "SSL-fout. De verbinding kon niet veilig worden opgezet." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL-fout. De verbinding kon niet veilig worden opgezet." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Ошибка SSL. Не удалось установить безопасное соединение." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ошибка SSL. Не удалось установить безопасное соединение." } } } }, - "ssl_certificate_error": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "SSL-Zertifikatfehler" + "ssl_certificate_error" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL-Zertifikatfehler" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "SSL Certificate Error" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL Certificate Error" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Error de certificado SSL" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error de certificado SSL" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "SSL Certificate Error" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL Certificate Error" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Erreur de certificat SSL" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erreur de certificat SSL" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Errore certificato SSL" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Errore certificato SSL" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Feil med SSL sertifikat" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Feil med SSL sertifikat" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "SSL-certificaat fout" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL-certificaat fout" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "SSL Certificate Error" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL Certificate Error" } } } }, - "ssl_certificate_invalid": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Das von %1$@ für %2$@ vorgestellte SSL-Zertifikat ist ungültig. Möchtest du fortfahren?" + "ssl_certificate_invalid" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Das von %1$@ für %2$@ vorgestellte SSL-Zertifikat ist ungültig. Möchtest du fortfahren?" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "SSL Certificate presented by %1$@ for %2$@ is invalid. Do you want to proceed?" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL Certificate presented by %1$@ for %2$@ is invalid. Do you want to proceed?" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "El certificado SSL presentado por %1$@ para %2$@ no es válido. ¿Desea continuar?" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "El certificado SSL presentado por %1$@ para %2$@ no es válido. ¿Desea continuar?" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "SSL Certificate presented by %1$@ for %2$@ is invalid. Do you want to proceed?" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL Certificate presented by %1$@ for %2$@ is invalid. Do you want to proceed?" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Le certificat SSL présenté par %1$@ pour %2$@ n'est pas valide. Voulez-vous continuer ?" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Le certificat SSL présenté par %1$@ pour %2$@ n'est pas valide. Veux-tu continuer ?" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Il certificato SSL presentato da %1$@ per %2$@ non è valido. Vuoi procedere?" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Il certificato SSL presentato da %1$@ per %2$@ non è valido. Vuoi procedere?" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "SSL-sertifikat presentert av %1$@ for %2$@ er ugyldig. Vil du fortsette?" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL-sertifikat presentert av %1$@ for %2$@ er ugyldig. Vil du fortsette?" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "SSL-certificaat gepresenteerd door %1$@ voor %2$@ is ongeldig. Wilt u toch doorgaan?" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL-certificaat gepresenteerd door %1$@ voor %2$@ is ongeldig. Wilt u toch doorgaan?" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "SSL Certificate presented by %1$@ for %2$@ is invalid. Do you want to proceed?" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL Certificate presented by %1$@ for %2$@ is invalid. Do you want to proceed?" } } } }, - "ssl_certificate_no_match": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Das von %1$@ für %2$@ vorgestellte SSL-Zertifikat stimmt nicht mit dem Datensatz überein. Möchtest du fortfahren?" + "ssl_certificate_no_match" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Das von %1$@ für %2$@ vorgestellte SSL-Zertifikat stimmt nicht mit dem Datensatz überein. Möchtest du fortfahren?" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "SSL Certificate presented by %1$@ for %2$@ doesn't match the record. Do you want to proceed?" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL Certificate presented by %1$@ for %2$@ doesn't match the record. Do you want to proceed?" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "El certificado SSL presentado por %1$@ para %2$@ no coincide con el registro. ¿Desea continuar?" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "El certificado SSL presentado por %1$@ para %2$@ no coincide con el registro. ¿Desea continuar?" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "SSL Certificate presented by %1$@ for %2$@ doesn't match the record. Do you want to proceed?" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL Certificate presented by %1$@ for %2$@ doesn't match the record. Do you want to proceed?" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Le certificat SSL présenté par %1$@ pour %2$@ ne correspond pas à l'enregistrement. Voulez-vous continuer ?" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Le certificat SSL présenté par %1$@ pour %2$@ ne correspond pas à l'enregistrement. Veux-tu continuer ?" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Il certificato SSL presentato da %1$@ per %2$@ non corrisponde al record. Vuoi procedere?" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Il certificato SSL presentato da %1$@ per %2$@ non corrisponde al record. Vuoi procedere?" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "SSL-sertifikat presentert av %1$@ for %2$@ samsvarer ikke med det som er lagret fra før av. Vil du fortsette?" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL-sertifikat presentert av %1$@ for %2$@ samsvarer ikke med det som er lagret fra før av. Vil du fortsette?" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "SSL-certificaat gepresenteerd door %1$@ voor %2$@ komt niet overeen met het record. Wilt u toch doorgaan?" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL-certificaat gepresenteerd door %1$@ voor %2$@ komt niet overeen met het record. Wilt u toch doorgaan?" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "SSL Certificate presented by %1$@ for %2$@ doesn't match the record. Do you want to proceed?" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL Certificate presented by %1$@ for %2$@ doesn't match the record. Do you want to proceed?" } } } }, - "ssl_certificate_warning": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "SSL-Zertifikatwarnung" + "ssl_certificate_warning" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL-Zertifikatwarnung" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "SSL Certificate Warning" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL Certificate Warning" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Aviso de certificado SSL" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aviso de certificado SSL" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "SSL Certificate Warning" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL Certificate Warning" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Avertissement de certificat SSL" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Avertissement de certificat SSL" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Avviso Certificato SSL" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Avviso Certificato SSL" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Advarsel for SSL sertifikat" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Advarsel for SSL sertifikat" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "SSL-certificaat waarschuwing" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL-certificaat waarschuwing" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "SSL Certificate Warning" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL Certificate Warning" } } } }, - "State": { - "comment": "A label for a picker that lets the user select a state.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zustand" + "State" : { + "comment" : "A label for a picker that lets the user select a state.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zustand" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "State" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "State" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Provincia" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Provincia" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Tila" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tila" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "État" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "État" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Stato" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stato" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Tilstand" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tilstand" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Status" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Status" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Состояние" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Состояние" } } } }, - "String Item": { - "comment": "Display name for a string item type.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "String-Item" + "String Item" : { + "comment" : "Display name for a string item type.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "String-Item" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento de texto" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento de texto" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Merkkijonoelementti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Merkkijonoelementti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Élément texte" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément texte" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elemento stringa" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento stringa" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Tekststrengselement" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tekststrengselement" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Tekst-item" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tekst-item" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Строковый элемент" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Строковый элемент" } } } }, - "SVG": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "SVG" + "SVG" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "SVG" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "SVG" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "SVG" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "SVG" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "SVG" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "SVG" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "SVG" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "SVG" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "SVG" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "SVG" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "SVG" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "SVG" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "SVG" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "SVG" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "SVG" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "SVG" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "SVG" } } } }, - "Switch Action": { - "comment": "Type display name for the SwitchAction enum used in app intents.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Switch-Aktion" + "Switch Action" : { + "comment" : "Type display name for the SwitchAction enum used in app intents.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Switch-Aktion" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Switch Action" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Switch Action" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Acción del interruptor" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Acción del interruptor" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kytkimen toiminto" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kytkimen toiminto" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Action d'interrupteur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Action d'interrupteur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Azione interruttore" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Azione interruttore" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Bryterhandling" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bryterhandling" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Schakelaaractie" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schakelaaractie" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Действие переключателя" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Действие переключателя" } } } }, - "Switch Item": { - "comment": "Display name for a switch item type.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Switch-Item" + "Switch Item" : { + "comment" : "Display name for a switch item type.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Switch-Item" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento interruptor" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento interruptor" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kytkinelementti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kytkinelementti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Élément interrupteur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément interrupteur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elemento interruttore" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento interruttore" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Bryterelement" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bryterelement" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Schakelaar-item" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schakelaar-item" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент переключателя" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент переключателя" } } } }, - "Switch the active home in the openHAB app": { - "comment": "Description of the intent to set the active home.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Aktives Zuhause in der openHAB-App wechseln" + "Switch the active home in the openHAB app" : { + "comment" : "Description of the intent to set the active home.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktives Zuhause in der openHAB-App wechseln" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Cambiar la casa activa en la app openHAB" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cambiar la casa activa en la app openHAB" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Vaihda aktiivinen koti openHAB-sovelluksessa" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vaihda aktiivinen koti openHAB-sovelluksessa" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Changer le domicile actif dans l'application openHAB" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Changer le domicile actif dans l'application openHAB" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Cambia la casa attiva nell'app openHAB" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cambia la casa attiva nell'app openHAB" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Bytt aktivt hjem i openHAB-appen" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bytt aktivt hjem i openHAB-appen" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Actief thuis wijzigen in de openHAB-app" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actief thuis wijzigen in de openHAB-app" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Сменить активный дом в приложении openHAB" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Сменить активный дом в приложении openHAB" } } } }, - "sync_prefs": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Synchronisationseinstellung" + "sync_prefs" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Synchronisationseinstellung" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sync preferences" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sync preferences" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Preferencias de sincronización" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Preferencias de sincronización" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Sync preferences" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sync preferences" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Synchroniser les préférences" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Synchroniser les préférences" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Sincronizza preferenze" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sincronizza preferenze" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sync-innstillinger" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sync-innstillinger" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Synchronisatie voorkeuren" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Synchronisatie voorkeuren" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Sync preferences" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sync preferences" } } } }, - "System": { - "comment": "A section header in the drawer view that links to the system settings.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "System" + "System" : { + "comment" : "A section header in the drawer view that links to the system settings.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "System" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "System" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "System" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Sistema" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sistema" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Järjestelmä" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Järjestelmä" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Système" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Système" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Sistema" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sistema" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "System" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "System" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Systeem" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Systeem" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Система" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Система" } } } }, - "Test Connection": { - "comment": "A button label that triggers a test connection action.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Testverbindung" + "Test Connection" : { + "comment" : "A button label that triggers a test connection action.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Testverbindung" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Test Connection" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Test Connection" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Probar conexión" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Probar conexión" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Testaa yhteys" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Testaa yhteys" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Tester la connexion" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tester la connexion" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Testa connessione" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Testa connessione" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Test tilkobling" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Test tilkobling" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Verbinding testen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbinding testen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Проверить соединение" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Проверить соединение" } } } }, - "Test Screen Saver": { - "comment": "A button that can be used to test the functionality of the screen saver.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Bildschirmschoner testen" + "Test Screen Saver" : { + "comment" : "A button that can be used to test the functionality of the screen saver.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bildschirmschoner testen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Test Screen Saver" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Test Screen Saver" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Probar salvapantallas" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Probar salvapantallas" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Testaa näytönsäästäjä" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Testaa näytönsäästäjä" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Tester l'économiseur d'écran" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tester l'économiseur d'écran" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Testa salvaschermo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Testa salvaschermo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Test skjermsparer" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Test skjermsparer" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Schermbeveiliging testen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schermbeveiliging testen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Проверить заставку" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Проверить заставку" } } } }, - "The connection timed out. Try again later.": { - "comment": "Error message displayed when a network request times out.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Die Verbindung ist abgelaufen. Später noch einmal versuchen." + "The connection timed out. Try again later." : { + "comment" : "Error message displayed when a network request times out.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Die Verbindung ist abgelaufen. Später noch einmal versuchen." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "The connection timed out. Try again later." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "The connection timed out. Try again later." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "La conexión ha agotado el tiempo de espera. Inténtelo de nuevo más tarde." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "La conexión ha agotado el tiempo de espera. Inténtelo de nuevo más tarde." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Yhteys aikakatkaistiin. Yritä myöhemmin uudelleen." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Yhteys aikakatkaistiin. Yritä myöhemmin uudelleen." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "La connexion a expiré. Réessayez plus tard." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "La connexion a expiré. Réessayez plus tard." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "La connessione è scaduta. Riprova più tardi." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "La connessione è scaduta. Riprova più tardi." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Tilkoblingen ble tidsavbrutt. Prøv igjen senere." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tilkoblingen ble tidsavbrutt. Prøv igjen senere." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "De verbinding is verlopen. Probeer het later opnieuw." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De verbinding is verlopen. Probeer het later opnieuw." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Время соединения истекло. Повторите попытку позже." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Время соединения истекло. Повторите попытку позже." } } } }, - "The state of %@ is %@": { - "comment": "Dialog shown when retrieving an item state. First placeholder is the item name, second is its state.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Der Status von %@ ist %@" + "The state of %@ is %@" : { + "comment" : "Dialog shown when retrieving an item state. First placeholder is the item name, second is its state.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Der Status von %@ ist %@" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "The state of %@ is %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "The state of %@ is %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "El estado de %@ es %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "El estado de %@ es %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kohteen %@ tila on %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kohteen %@ tila on %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "L'état de %@ est %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "L'état de %@ est %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Lo stato di %@ è %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lo stato di %@ è %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Tilstanden til %@ er %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tilstanden til %@ er %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "De staat van %@ is %@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De staat van %@ is %@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Состояние %@ равно %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Состояние %@ равно %@" } } } }, - "The state of %@ was set to %@": { - "comment": "Dialog shown after setting a contact state. First placeholder is the item name, second is the new state.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Der Status von %@ wurde auf %@ gesetzt" + "The state of %@ was set to %@" : { + "comment" : "Dialog shown after setting a contact state. First placeholder is the item name, second is the new state.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Der Status von %@ wurde auf %@ gesetzt" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "The state of %@ was set to %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "The state of %@ was set to %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "El estado de %@ se ha establecido en %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "El estado de %@ se ha establecido en %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kohteen %@ tilaksi asetettiin %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kohteen %@ tilaksi asetettiin %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "L'état de %@ a été réglé sur %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "L'état de %@ a été réglé sur %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Lo stato di %@ è stato impostato su %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lo stato di %@ è stato impostato su %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Tilstanden til %@ ble satt til %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tilstanden til %@ ble satt til %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "De status van %@ is ingesteld op %@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De status van %@ is ingesteld op %@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Состояние %@ установлено в %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Состояние %@ установлено в %@" } } } }, - "The URL is invalid. Please check the format (e.g., http://192.168.2.1:8080 or http://[::1]:8080 for IPv6).": { - "comment": "Error message displayed when the URL is invalid.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Die URL ist ungültig. Bitte überprüfen Sie das Format (z.B. http://192.168.2.1:8080 oder http://[::1]:8080 für IPv6)." + "The URL is invalid. Please check the format (e.g., http://192.168.2.1:8080 or http://[::1]:8080 for IPv6)." : { + "comment" : "Error message displayed when the URL is invalid.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Die URL ist ungültig. Bitte überprüfe das Format (z.B. http://192.168.2.1:8080 oder http://[::1]:8080 für IPv6)." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "La URL no es válida. Compruebe el formato (p. ej., http://192.168.2.1:8080 o http://[::1]:8080 para IPv6)." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "La URL no es válida. Compruebe el formato (p. ej., http://192.168.2.1:8080 o http://[::1]:8080 para IPv6)." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "URL on virheellinen. Tarkista muoto (esim. http://192.168.2.1:8080 tai http://[::1]:8080 IPv6:lle)." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL on virheellinen. Tarkista muoto (esim. http://192.168.2.1:8080 tai http://[::1]:8080 IPv6:lle)." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "L'URL est invalide. Veuillez vérifier le format (p. ex., http://192.168.2.1:8080 ou http://[::1]:8080 pour IPv6)." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "L'URL est invalide. Veuillez vérifier le format (p. ex., http://192.168.2.1:8080 ou http://[::1]:8080 pour IPv6)." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "L'URL non è valido. Controlla il formato (es. http://192.168.2.1:8080 o http://[::1]:8080 per IPv6)." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "L'URL non è valido. Controlla il formato (es. http://192.168.2.1:8080 o http://[::1]:8080 per IPv6)." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "URL-en er ugyldig. Sjekk formatet (f.eks. http://192.168.2.1:8080 eller http://[::1]:8080 for IPv6)." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL-en er ugyldig. Sjekk formatet (f.eks. http://192.168.2.1:8080 eller http://[::1]:8080 for IPv6)." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "De URL is ongeldig. Controleer het formaat (bijv. http://192.168.2.1:8080 of http://[::1]:8080 voor IPv6)." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De URL is ongeldig. Controleer het formaat (bijv. http://192.168.2.1:8080 of http://[::1]:8080 voor IPv6)." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "URL недействителен. Проверьте формат (например, http://192.168.2.1:8080 или http://[::1]:8080 для IPv6)." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL недействителен. Проверьте формат (например, http://192.168.2.1:8080 или http://[::1]:8080 для IPv6)." } } } }, - "Tiles": { - "comment": "A section header that lists the user's tiles.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Kacheln" + "Tiles" : { + "comment" : "A section header that lists the user's tiles.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kacheln" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Tiles" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tiles" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Mosaicos" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mosaicos" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ruudut" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ruudut" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Tuiles" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tuiles" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Riquadri" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Riquadri" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Fliser" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fliser" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Tegels" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tegels" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Плитки" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Плитки" } } } }, - "Timing": { - "comment": "The header for the timing section in the screen saver settings view.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Timing" + "Timing" : { + "comment" : "The header for the timing section in the screen saver settings view.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Timing" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Timing" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Timing" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Temporización" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Temporización" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ajoitus" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajoitus" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Minutage" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Minutage" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Temporizzazione" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Temporizzazione" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Timing" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Timing" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Timing" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Timing" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Синхронизация" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Синхронизация" } } } }, - "To connect to your local openHAB server, please allow Local Network access when prompted. If you previously denied it, enable it in Settings → Privacy & Security → Local Network.": { - "comment": "A message displayed in the alert when the user tries to connect to a local openHAB server.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Um eine Verbindung zu Ihrem lokalen openHAB-Server herzustellen, erlauben Sie bitte den Zugriff auf das lokale Netzwerk, wenn Sie dazu aufgefordert werden. Wenn Sie es zuvor abgelehnt haben, aktivieren Sie es unter Einstellungen → Datenschutz & Sicherheit → Lokales Netzwerk." + "To connect to your local openHAB server, please allow Local Network access when prompted. If you previously denied it, enable it in Settings → Privacy & Security → Local Network." : { + "comment" : "A message displayed in the alert when the user tries to connect to a local openHAB server.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Um eine Verbindung zu deinem lokalen openHAB-Server herzustellen, erlauben bitte den Zugriff auf das lokale Netzwerk, wenn du dazu aufgefordert wirst. Wenn du es zuvor abgelehnt hast, aktiviere es unter Einstellungen → Datenschutz & Sicherheit → Lokales Netzwerk." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Para conectarse a su servidor openHAB local, permita el acceso a la red local cuando se le solicite. Si lo denegó anteriormente, actívelo en Ajustes → Privacidad y seguridad → Red local." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Para conectarse a su servidor openHAB local, permita el acceso a la red local cuando se le solicite. Si lo denegó anteriormente, actívelo en Ajustes → Privacidad y seguridad → Red local." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Muodosta yhteys paikalliseen openHAB-palvelimeen sallimalla lähiverkkoyhteys pyydettäessä. Jos olet aiemmin kieltänyt sen, ota se käyttöön kohdassa Asetukset → Tietosuoja ja turvallisuus → Lähiverkko." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Muodosta yhteys paikalliseen openHAB-palvelimeen sallimalla lähiverkkoyhteys pyydettäessä. Jos olet aiemmin kieltänyt sen, ota se käyttöön kohdassa Asetukset → Tietosuoja ja turvallisuus → Lähiverkko." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Pour vous connecter à votre serveur openHAB local, veuillez autoriser l'accès au réseau local lorsque vous y êtes invité. Si vous l'avez précédemment refusé, activez-le dans Réglages → Confidentialité et sécurité → Réseau local." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pour te connecter à ton serveur openHAB local, autorise l’accès au réseau local lorsque tu y es invité. Si tu l’as précédemment refusé, active-le dans Réglages → Confidentialité et sécurité → Réseau local." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Per connetterti al tuo server openHAB locale, consenti l'accesso alla rete locale quando richiesto. Se in precedenza hai negato l'accesso, abilitalo in Impostazioni → Privacy e sicurezza → Rete locale." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Per connetterti al tuo server openHAB locale, consenti l'accesso alla rete locale quando richiesto. Se in precedenza hai negato l'accesso, abilitalo in Impostazioni → Privacy e sicurezza → Rete locale." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "For å koble til din lokale openHAB-server, vennligst tillat tilgang til lokalt nettverk når du blir bedt om det. Hvis du tidligere nektet det, aktiver det i Innstillinger → Personvern og sikkerhet → Lokalt nettverk." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "For å koble til din lokale openHAB-server, vennligst tillat tilgang til lokalt nettverk når du blir bedt om det. Hvis du tidligere nektet det, aktiver det i Innstillinger → Personvern og sikkerhet → Lokalt nettverk." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Om verbinding te maken met uw lokale openHAB-server, sta toegang tot het lokale netwerk toe wanneer hierom wordt gevraagd. Als u dit eerder heeft geweigerd, schakel het in via Instellingen → Privacy en beveiliging → Lokaal netwerk." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Om verbinding te maken met uw lokale openHAB-server, sta toegang tot het lokale netwerk toe wanneer hierom wordt gevraagd. Als u dit eerder heeft geweigerd, schakel het in via Instellingen → Privacy en beveiliging → Lokaal netwerk." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Для подключения к локальному серверу openHAB разрешите доступ к локальной сети при появлении запроса. Если вы ранее отказали, включите его в Настройки → Конфиденциальность и безопасность → Локальная сеть." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Для подключения к локальному серверу openHAB разрешите доступ к локальной сети при появлении запроса. Если вы ранее отказали, включите его в Настройки → Конфиденциальность и безопасность → Локальная сеть." } } } }, - "Toggle": { - "comment": "Display name for the toggle action in the SwitchAction enum.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Umschalten" + "Toggle" : { + "comment" : "Display name for the toggle action in the SwitchAction enum.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Umschalten" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Toggle" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toggle" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Alternar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alternar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Vaihda" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vaihda" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Basculer" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basculer" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Attiva/Disattiva" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Attiva/Disattiva" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Bytt" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bytt" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Omschakelen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Omschakelen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Переключить" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Переключить" } } } }, - "unable_to_add_certificate": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zertifikat konnte nicht zum Schlüsselbund hinzugefügt werden: %@." + "unable_to_add_certificate" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zertifikat konnte nicht zum Schlüsselbund hinzugefügt werden: %@." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Unable to add certificate to the keychain: %@." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Unable to add certificate to the keychain: %@." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "No se puede agregar el certificado al llavero %@." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se puede agregar el certificado al llavero %@." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Unable to add certificate to the keychain: %@." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Unable to add certificate to the keychain: %@." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Impossible d'ajouter le certificat au trousseau : %@." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossible d'ajouter le certificat au trousseau : %@." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Impossibile aggiungere il certificato al portachiavi: %@." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossibile aggiungere il certificato al portachiavi: %@." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kan ikke legge sertifikatet til i nøkkelkjeden: %@." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kan ikke legge sertifikatet til i nøkkelkjeden: %@." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Kan certificaat niet toevoegen aan de sleutelhanger: %@." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kan certificaat niet toevoegen aan de sleutelhanger: %@." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Unable to add certificate to the keychain: %@." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Unable to add certificate to the keychain: %@." } } } }, - "unable_to_decode_certificate": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zertifikat kann nicht dekodiert werden: %@." + "unable_to_decode_certificate" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zertifikat kann nicht dekodiert werden: %@." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Unable to decode certificate: %@." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Unable to decode certificate: %@." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "No se puede descifrar el certificado: %@." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se puede descifrar el certificado: %@." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Unable to decode certificate: %@." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Unable to decode certificate: %@." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Impossible de décoder le certificat : %@." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossible de décoder le certificat : %@." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Impossibile decodificare il certificato: %@." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossibile decodificare il certificato: %@." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kan ikke dekode sertifikat: %@." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kan ikke dekode sertifikat: %@." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Kan certificaat niet decoderen: %@." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kan certificaat niet decoderen: %@." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Unable to decode certificate: %@." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Unable to decode certificate: %@." } } } }, - "Unexpected error: %@": { - "comment": "A user-visible description of a `DecodingError` that might occur when testing a network connection.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Unerwarteter Fehler: %@" + "Unexpected error: %@" : { + "comment" : "A user-visible description of a `DecodingError` that might occur when testing a network connection.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Unerwarteter Fehler: %@" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Unexpected error: %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Unexpected error: %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Error inesperado: %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error inesperado: %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Odottamaton virhe: %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Odottamaton virhe: %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Erreur inattendue : %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erreur inattendue : %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Errore imprevisto: %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Errore imprevisto: %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Uventet feil: %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uventet feil: %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Onverwachte fout: %@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Onverwachte fout: %@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Непредвиденная ошибка: %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Непредвиденная ошибка: %@" } } } }, - "unknown": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "unbekannt" + "unknown" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "unbekannt" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "unknown" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "unknown" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "desconocido" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "desconocido" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "unknown" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "unknown" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "inconnu" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "inconnu" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "sconosciuto" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "sconosciuto" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "ukjent" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "ukjent" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "onbekend" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "onbekend" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "unknown" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "unknown" } } } }, - "Unknown home": { - "comment": "Error message displayed when a home is selected but the home ID is not found in the list of stored homes.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Unbekanntes Zuhause" + "Unknown home" : { + "comment" : "Error message displayed when a home is selected but the home ID is not found in the list of stored homes.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Unbekanntes Zuhause" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Casa desconocida" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Casa desconocida" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Tuntematon koti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tuntematon koti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Domicile inconnu" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Domicile inconnu" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Casa sconosciuta" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Casa sconosciuta" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ukjent hjem" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ukjent hjem" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Onbekend thuis" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Onbekend thuis" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Неизвестный дом" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Неизвестный дом" } } } }, - "Updating…": { - "comment": "A text that appears while a user is waiting for the sitemap to update.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Aktualisierung …" + "Updating…" : { + "comment" : "A text that appears while a user is waiting for the sitemap to update.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktualisierung …" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Updating…" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Updating…" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Actualizando…" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actualizando…" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Päivitetään…" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Päivitetään…" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Mise à jour…" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mise à jour…" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Aggiorno …" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aggiorno …" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Oppdaterer…" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Oppdaterer…" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Bijwerken…" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bijwerken…" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Обновление…" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Обновление…" } } } }, - "URL": { - "comment": "A text field for entering the URL of the remote server.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "URL" + "URL" : { + "comment" : "A text field for entering the URL of the remote server.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "URL" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "URL" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "URL" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "URL" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "URL" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "URL" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "URL" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "URL" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL" } } } }, - "uselastpath_settings": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zuletzt besuchten Pfad verwenden?" + "uselastpath_settings" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zuletzt besuchten Pfad verwenden?" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Use last visited path?" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Use last visited path?" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "¿Usar la última ruta visitada?" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Usar la última ruta visitada?" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Käytä edellistä polkua?" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Käytä edellistä polkua?" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Utiliser le chemin de la dernière visite ?" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Utiliser le chemin de la dernière visite ?" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Usare L'Ultimo Percorso Visitato?" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Usare L'Ultimo Percorso Visitato?" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Bruk sist brukte bane?" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bruk sist brukte bane?" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Laatst bezochte pad gebruiken?" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Laatst bezochte pad gebruiken?" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Use last visited path?" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Use last visited path?" } } } }, - "username": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Benutzername" + "username" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Benutzername" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Username" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Username" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Nombre de usario" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nombre de usario" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Username" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Username" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Identifiant" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Identifiant" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Utente" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Utente" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Brukernavn" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brukernavn" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Gebruikersnaam" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gebruikersnaam" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Username" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Username" } } } }, - "Username": { - "comment": "A label displayed above the text field for the username.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Benutzername" + "Username" : { + "comment" : "A label displayed above the text field for the username.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Benutzername" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Username" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Username" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Nombre de usuario" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nombre de usuario" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Käyttäjänimi" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Käyttäjänimi" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Nom d’utilisateur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nom d’utilisateur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Nome utente" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nome utente" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Brukernavn" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brukernavn" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Gebruikersnaam" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gebruikersnaam" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Имя пользователя" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Имя пользователя" } } } }, - "Value": { - "comment": "Parameter title for a value in app intents.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Wert" + "Value" : { + "comment" : "Parameter title for a value in app intents.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wert" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Value" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Value" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Valor" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valor" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Arvo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Arvo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Valeur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valeur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Valore" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valore" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Verdi" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verdi" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Waarde" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Waarde" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Значение" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Значение" } } } }, - "version": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Version" + "version" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Version" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Version" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Version" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Versión" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Versión" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Version" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Version" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Version" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Version" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Versione" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Versione" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Versjon" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Versjon" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Versie" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Versie" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Version" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Version" } } } }, - "Very Cool": { - "comment": "A description for a very cool color temperature.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sehr kalt" + "Very Cool" : { + "comment" : "A description for a very cool color temperature.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sehr kalt" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Very Cool" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Very Cool" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Muy frío" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Muy frío" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Hyvin viileä" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hyvin viileä" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Très froid" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Très froid" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Molto freddo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Molto freddo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Svært kjølig" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Svært kjølig" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Zeer koel" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zeer koel" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Очень холодный" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Очень холодный" } } } }, - "Very Warm": { - "comment": "A color temperature range description.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sehr warm" + "Very Warm" : { + "comment" : "A color temperature range description.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sehr warm" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Very Warm" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Very Warm" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Muy cálido" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Muy cálido" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Hyvin lämmin" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hyvin lämmin" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Très chaud" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Très chaud" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Molto caldo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Molto caldo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Svært varm" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Svært varm" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Zeer warm" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zeer warm" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Очень тёплый" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Очень тёплый" } } } }, - "Warm White": { - "comment": "A color temperature range description.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Warmweiß" + "Warm White" : { + "comment" : "A color temperature range description.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Warmweiß" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Warm White" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Warm White" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Blanco cálido" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blanco cálido" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Lämmin valkoinen" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lämmin valkoinen" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Blanc chaud" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blanc chaud" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Bianco caldo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bianco caldo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Varm hvit" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Varm hvit" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Warm wit" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Warm wit" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Тёплый белый" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Тёплый белый" } } } }, - "warning": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Warnung" + "warning" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Warnung" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Warning" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Warning" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Aviso" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aviso" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Varoitus" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Varoitus" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Attention" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Attention" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Attenzione" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Attenzione" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Advarsel" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Advarsel" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Waarschuwing" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Waarschuwing" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Warning" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Warning" } } } }, - "Warning: Renaming the home might cause external integrations like shortcuts to fail until reconfigured.": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Warnung: Das Umbenennen des Zuhauses kann dazu führen, dass externe Integrationen wie Kurzbefehle bis zur Neukonfiguration fehlschlagen." + "Warning: Renaming the home might cause external integrations like shortcuts to fail until reconfigured." : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Warnung: Das Umbenennen des Zuhauses kann dazu führen, dass externe Integrationen wie Kurzbefehle bis zur Neukonfiguration fehlschlagen." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Warning: Renaming the home might cause external integrations like shortcuts to fail until reconfigured." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Warning: Renaming the home might cause external integrations like shortcuts to fail until reconfigured." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Advertencia: Cambiar el nombre del hogar puede hacer que las integraciones externas, como los atajos, fallen hasta que se reconfiguren." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Advertencia: Cambiar el nombre del hogar puede hacer que las integraciones externas, como los atajos, fallen hasta que se reconfiguren." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Varoitus: Kodin uudelleennimeäminen voi aiheuttaa ulkoisten integraatioiden, kuten pikakomentojen, epäonnistumisen, kunnes ne on uudelleenmääritetty." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Varoitus: Kodin uudelleennimeäminen voi aiheuttaa ulkoisten integraatioiden, kuten pikakomentojen, epäonnistumisen, kunnes ne on uudelleenmääritetty." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Attention : renommer la maison peut entraîner l'échec des intégrations externes telles que les raccourcis jusqu'à leur reconfiguration." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Attention : renommer la maison peut entraîner l'échec des intégrations externes telles que les raccourcis jusqu'à leur reconfiguration." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Attenzione: rinominare la casa potrebbe causare il malfunzionamento delle integrazioni esterne come i comandi rapidi fino alla loro riconfigurazione." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Attenzione: rinominare la casa potrebbe causare il malfunzionamento delle integrazioni esterne come i comandi rapidi fino alla loro riconfigurazione." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Advarsel: Å gi hjemmet nytt navn kan føre til at eksterne integrasjoner som snarveier mislykkes inntil de er rekonfigurert." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Advarsel: Å gi hjemmet nytt navn kan føre til at eksterne integrasjoner som snarveier mislykkes inntil de er rekonfigurert." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Waarschuwing: Het hernoemen van het huis kan ertoe leiden dat externe integraties zoals snelkoppelingen mislukken totdat ze opnieuw zijn geconfigureerd." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Waarschuwing: Het hernoemen van het huis kan ertoe leiden dat externe integraties zoals snelkoppelingen mislukken totdat ze opnieuw zijn geconfigureerd." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Предупреждение: переименование дома может привести к сбою внешних интеграций, таких как ярлыки, до их перенастройки." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Предупреждение: переименование дома может привести к сбою внешних интеграций, таких как ярлыки, до их перенастройки." } } } }, - "You appear to be offline. Check your internet connection.": { - "comment": "Error message displayed when the user is offline.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Du scheinst offline zu sein. Überprüfe deine Internetverbindung." + "You appear to be offline. Check your internet connection." : { + "comment" : "Error message displayed when the user is offline.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Du scheinst offline zu sein. Überprüfe deine Internetverbindung." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "You appear to be offline. Check your internet connection." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "You appear to be offline. Check your internet connection." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Parece que estás sin conexión. Comprueba tu conexión a internet." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Parece que estás sin conexión. Comprueba tu conexión a internet." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Näyttää siltä, että olet offline. Tarkista internet-yhteytesi." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Näyttää siltä, että olet offline. Tarkista internet-yhteytesi." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Vous semblez être hors ligne. Vérifiez votre connexion Internet." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tu sembles être hors ligne. Vérifie ta connexion Internet." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Sembra che tu sia offline. Controlla la connessione a Internet." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sembra che tu sia offline. Controlla la connessione a Internet." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Du ser ut til å være frakoblet. Sjekk internettilkoblingen din." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Du ser ut til å være frakoblet. Sjekk internettilkoblingen din." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "U lijkt offline te zijn. Controleer uw internetverbinding." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "U lijkt offline te zijn. Controleer uw internetverbinding." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Похоже, вы не в сети. Проверьте подключение к интернету." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Похоже, вы не в сети. Проверьте подключение к интернету." } } } } }, - "version": "1.1" -} + "version" : "1.1" +} \ No newline at end of file From 3fddb18af1abb9756441c2645b5a19424ee896b4 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Wed, 17 Jun 2026 17:31:32 +0200 Subject: [PATCH 27/91] l10n: add Spanish, Finnish, Norwegian, and Russian translations Also updates German and French informal address forms (du/tu). Signed-off-by: Tim Mueller-Seydlitz --- .../Supporting Files/Localizable.xcstrings | 3466 ++++++++++------- 1 file changed, 2081 insertions(+), 1385 deletions(-) diff --git a/openHAB/Supporting Files/Localizable.xcstrings b/openHAB/Supporting Files/Localizable.xcstrings index de173bc8f..1b361e08a 100644 --- a/openHAB/Supporting Files/Localizable.xcstrings +++ b/openHAB/Supporting Files/Localizable.xcstrings @@ -83,8 +83,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": " - " } }, "fr": { @@ -101,8 +101,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": " - " } }, "nl": { @@ -113,8 +113,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": " - " } } } @@ -153,6 +153,30 @@ "state": "translated", "value": "%@" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "%@" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "%@" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "%@" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "%@" + } } } }, @@ -173,14 +197,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "/overview/" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "/overview/" } }, "fr": { @@ -197,8 +221,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "/overview/" } }, "nl": { @@ -209,8 +233,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "/overview/" } } } @@ -240,6 +264,30 @@ "state": "translated", "value": "%@" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "%@" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "%@" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "%@" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "%@" + } } }, "comment": "A label that shows the percentage of brightness. The value is rounded to the nearest whole number." @@ -261,14 +309,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ajustes de %@" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "%@-asetukset" } }, "fr": { @@ -285,8 +333,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "%@-innstillinger" } }, "nl": { @@ -297,8 +345,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Настройки %@" } } } @@ -320,14 +368,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Valor de %@" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "%@-arvo" } }, "fr": { @@ -344,8 +392,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "%@-verdi" } }, "nl": { @@ -356,8 +404,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Значение %@" } } } @@ -379,14 +427,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "%lld" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "%lld" } }, "fr": { @@ -403,8 +451,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "%lld" } }, "nl": { @@ -415,8 +463,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "%lld" } } } @@ -438,14 +486,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "%lldK" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "%lldK" } }, "fr": { @@ -462,8 +510,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "%lldK" } }, "nl": { @@ -474,8 +522,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "%lldK" } } } @@ -497,14 +545,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Reloj de 24 horas" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "24 tunnin kello" } }, "fr": { @@ -521,8 +569,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "24-timers klokke" } }, "nl": { @@ -533,8 +581,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "24-часовые часы" } } } @@ -614,14 +662,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Certificados de servidor aceptados" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Hyväksytyt palvelinvarmenteet" } }, "fr": { @@ -638,8 +686,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Godkjente serversertifikater" } }, "nl": { @@ -650,8 +698,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Принятые сертификаты сервера" } } } @@ -679,8 +727,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Toiminto" } }, "fr": { @@ -709,8 +757,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Действие" } } } @@ -849,14 +897,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Añadido: %@" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Lisätty: %@" } }, "fr": { @@ -873,8 +921,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Lagt til: %@" } }, "nl": { @@ -885,8 +933,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Добавлено: %@" } } } @@ -914,8 +962,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aina" } }, "fr": { @@ -932,8 +980,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Alltid" } }, "nl": { @@ -944,8 +992,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Всегда" } } } @@ -967,14 +1015,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Permitir WebRTC siempre" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Salli WebRTC aina" } }, "fr": { @@ -991,8 +1039,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Tillat alltid WebRTC" } }, "nl": { @@ -1003,8 +1051,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Всегда разрешать WebRTC" } } } @@ -1026,14 +1074,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Enviar credenciales siempre" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Lähetä tunnistetiedot aina" } }, "fr": { @@ -1050,8 +1098,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Send alltid legitimasjon" } }, "nl": { @@ -1062,8 +1110,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Всегда отправлять учётные данные" } } } @@ -1203,14 +1251,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Animación" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Animaatio" } }, "fr": { @@ -1227,8 +1275,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Animasjon" } }, "nl": { @@ -1239,8 +1287,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Анимация" } } } @@ -1262,14 +1310,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Versión de la aplicación" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Sovelluksen versio" } }, "fr": { @@ -1286,8 +1334,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "App-versjon" } }, "nl": { @@ -1298,8 +1346,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Версия приложения" } } } @@ -1386,8 +1434,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ulkoasu" } }, "fr": { @@ -1404,8 +1452,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Utseende" } }, "nl": { @@ -1416,8 +1464,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Внешний вид" } } } @@ -1503,8 +1551,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Takaisin" } }, "fr": { @@ -1521,8 +1569,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Tilbake" } }, "nl": { @@ -1533,8 +1581,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Назад" } } } @@ -1620,8 +1668,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kirkkaus" } }, "fr": { @@ -1638,8 +1686,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Lysstyrke" } }, "nl": { @@ -1650,8 +1698,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Яркость" } } } @@ -1795,8 +1843,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Peruuta" } }, "fr": { @@ -1813,8 +1861,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Avbryt" } }, "nl": { @@ -1825,8 +1873,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Отмена" } } } @@ -1848,14 +1896,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Se produjo una cancelación" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Peruutus tapahtui" } }, "fr": { @@ -1872,8 +1920,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Avbrudd inntraff" } }, "nl": { @@ -1884,8 +1932,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Произошла отмена" } } } @@ -1906,14 +1954,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Luz de vela" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kynttilänvalo" } }, "fr": { @@ -1930,8 +1978,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Stearinlys" } }, "nl": { @@ -1942,8 +1990,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Свет свечи" } } } @@ -1965,14 +2013,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "No se puede conectar al servidor. ¿Está en línea?" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Yhteyden muodostaminen palvelimeen epäonnistui. Onko se verkossa?" } }, "fr": { @@ -1989,8 +2037,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kan ikke koble til serveren. Er den online?" } }, "nl": { @@ -2001,8 +2049,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Не удаётся подключиться к серверу. Он в сети?" } } } @@ -2024,14 +2072,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "No se puede encontrar el servidor. ¿Es correcta la URL?" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Palvelinta ei löydy. Onko URL oikein?" } }, "fr": { @@ -2048,8 +2096,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kan ikke finne serveren. Er URL-en riktig?" } }, "nl": { @@ -2060,8 +2108,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Не удаётся найти сервер. URL верный?" } } } @@ -2316,14 +2364,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Comprobar y limpiar caché de imágenes" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Tarkista ja tyhjennä kuvavälimuisti" } }, "fr": { @@ -2340,8 +2388,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Sjekk og tøm bildebuffer" } }, "nl": { @@ -2352,8 +2400,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Проверить и очистить кэш изображений" } } } @@ -2385,6 +2433,30 @@ "state": "translated", "value": "Scegli colore" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Elegir color" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Valitse väri" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Velg farge" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Выбрать цвет" + } } } }, @@ -2411,8 +2483,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Tyhjennä" } }, "fr": { @@ -2429,8 +2501,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Tøm" } }, "nl": { @@ -2441,8 +2513,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Очистить" } } } @@ -2464,14 +2536,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Limpiar caché web" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Tyhjennä verkkovälimuisti" } }, "fr": { @@ -2488,8 +2560,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Tøm nettbuffer" } }, "nl": { @@ -2500,8 +2572,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Очистить веб-кэш" } } } @@ -2641,14 +2713,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Certificados de cliente" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Asiakasvarmenteet" } }, "fr": { @@ -2665,8 +2737,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Klientsertifikater" } }, "nl": { @@ -2677,8 +2749,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Клиентские сертификаты" } } } @@ -2848,8 +2920,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Закрыто" } } } @@ -2877,8 +2949,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Väri" } }, "fr": { @@ -2895,8 +2967,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Farge" } }, "nl": { @@ -2907,8 +2979,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Цвет" } } } @@ -2940,6 +3012,30 @@ "state": "translated", "value": "Elemento colore" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Elemento de color" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Värielementti" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Fargeelement" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Элемент цвета" + } } } }, @@ -2970,6 +3066,30 @@ "state": "translated", "value": "Ruota dei colori" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Rueda de colores" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Väriympyrä" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Fargehjul" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Цветовой круг" + } } } }, @@ -2990,14 +3110,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Comando fallido: %@" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Komento epäonnistui: %@" } }, "fr": { @@ -3014,8 +3134,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kommando mislyktes: %@" } }, "nl": { @@ -3026,8 +3146,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Команда не выполнена: %@" } } } @@ -3049,14 +3169,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Errores de comando: %lld" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Komentoviat: %lld" } }, "fr": { @@ -3073,8 +3193,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kommandofeil: %lld" } }, "nl": { @@ -3085,8 +3205,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Сбоев команд: %lld" } } } @@ -3108,14 +3228,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Elemento de comando %@" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Komentoelementti %@" } }, "fr": { @@ -3132,8 +3252,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kommandoelement %@" } }, "nl": { @@ -3144,8 +3264,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Элемент команды %@" } } } @@ -3231,8 +3351,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Yhdistetään" } }, "fr": { @@ -3249,8 +3369,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kobler til" } }, "nl": { @@ -3261,8 +3381,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Подключение" } } } @@ -3461,14 +3581,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Conexión exitosa" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Yhteys muodostettu" } }, "fr": { @@ -3485,8 +3605,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Tilkobling vellykket" } }, "nl": { @@ -3497,8 +3617,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Соединение установлено" } } } @@ -3530,34 +3650,58 @@ "state": "translated", "value": "Elemento contatto" } - } - } - }, - "Contact State": { - "comment": "Type display name for the ContactState enum used in app intents.", - "localizations": { - "de": { + }, + "es": { "stringUnit": { "state": "translated", - "value": "Kontakt-Status" + "value": "Elemento de contacto" } }, - "en": { + "fi": { "stringUnit": { "state": "translated", - "value": "Contact State" + "value": "Kontaktielementti" } }, - "es": { + "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kontaktelement" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Элемент контакта" + } + } + } + }, + "Contact State": { + "comment": "Type display name for the ContactState enum used in app intents.", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Kontakt-Status" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Contact State" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Estado de contacto" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kontaktin tila" } }, "fr": { @@ -3574,8 +3718,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kontaktstatus" } }, "nl": { @@ -3586,8 +3730,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Состояние контакта" } } } @@ -3609,14 +3753,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Luz diurna fría" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Viileä päivänvalo" } }, "fr": { @@ -3633,8 +3777,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kjølig dagslys" } }, "nl": { @@ -3645,8 +3789,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Холодный дневной свет" } } } @@ -3668,14 +3812,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Blanco frío" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Viileä valkoinen" } }, "fr": { @@ -3692,8 +3836,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kjølig hvit" } }, "nl": { @@ -3704,8 +3848,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Холодный белый" } } } @@ -3733,8 +3877,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kopioi" } }, "fr": { @@ -3751,8 +3895,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kopier" } }, "nl": { @@ -3763,8 +3907,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Копировать" } } } @@ -3845,14 +3989,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Informe de fallos" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kaatumisraportointi" } }, "fr": { @@ -3869,8 +4013,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Krasjirapportering" } }, "nl": { @@ -3881,8 +4025,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Отчёты о сбоях" } } } @@ -4084,8 +4228,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Luo" } }, "fr": { @@ -4102,8 +4246,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Opprett" } }, "nl": { @@ -4114,8 +4258,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Создать" } } } @@ -4137,14 +4281,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Fecha y hora" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Päivämäärä ja aika" } }, "fr": { @@ -4161,8 +4305,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Dato og tid" } }, "nl": { @@ -4173,8 +4317,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Дата и время" } } } @@ -4206,6 +4350,30 @@ "state": "translated", "value": "Elemento DateTime" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Elemento DateTime" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "DateTime-elementti" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "DateTime-element" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Элемент DateTime" + } } } }, @@ -4226,14 +4394,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Luz diurna" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Päivänvalo" } }, "fr": { @@ -4250,8 +4418,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Dagslys" } }, "nl": { @@ -4262,8 +4430,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Дневной свет" } } } @@ -4403,14 +4571,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Disminuir %@" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Pienennä %@" } }, "fr": { @@ -4427,8 +4595,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Reduser %@" } }, "nl": { @@ -4439,8 +4607,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Уменьшить %@" } } } @@ -4462,14 +4630,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ruta predeterminada" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Oletuspolku" } }, "fr": { @@ -4486,8 +4654,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Standard sti" } }, "nl": { @@ -4498,8 +4666,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Путь по умолчанию" } } } @@ -4527,8 +4695,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Poista" } }, "fr": { @@ -4545,8 +4713,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Slett" } }, "nl": { @@ -4557,8 +4725,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Удалить" } } } @@ -4580,14 +4748,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "¿Eliminar hogar ‘%@’?" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Poistetaanko koti ‘%@’?" } }, "fr": { @@ -4604,8 +4772,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Slette hjem ‘%@’?" } }, "nl": { @@ -4616,8 +4784,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Удалить дом «%@»?" } } } @@ -4639,14 +4807,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Modo demo" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Esittelytila" } }, "fr": { @@ -4663,8 +4831,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Demomodus" } }, "nl": { @@ -4675,8 +4843,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Демо-режим" } } } @@ -4763,8 +4931,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Hylkää" } }, "fr": { @@ -4781,8 +4949,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Avslå" } }, "nl": { @@ -4793,8 +4961,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Отклонить" } } } @@ -4826,6 +4994,30 @@ "state": "translated", "value": "Elemento dimmer/tapparella" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Elemento atenuador/persianas" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Himmennin-/rullaverhoelementti" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Dimmer-/rullegardinelement" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Элемент диммера/рольставни" + } } } }, @@ -4846,14 +5038,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Desactivar tiempo de espera en reposo" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Poista käytöstä lepoajan aikakatkaisu" } }, "fr": { @@ -4870,8 +5062,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Deaktiver tidsavbrudd ved inaktivitet" } }, "nl": { @@ -4882,8 +5074,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Отключить тайм-аут простоя" } } } @@ -5095,6 +5287,30 @@ "state": "translated", "value": "Ricerca server openHAB in corso…" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Descubriendo servidores openHAB…" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Etsitään openHAB-palvelimia…" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Finner openHAB-servere…" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Обнаружение серверов openHAB…" + } } } }, @@ -5183,7 +5399,31 @@ "state": "translated", "value": "Fine" } - } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Hecho" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Valmis" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Ferdig" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Готово" + } + } } }, "Drag to change hue and saturation": { @@ -5213,6 +5453,30 @@ "state": "translated", "value": "Trascina per modificare tonalità e saturazione" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Arrastrar para cambiar el tono y la saturación" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Vedä muuttaaksesi värisävyä ja kylläisyyttä" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Dra for å endre fargetone og metning" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Перетащите для изменения оттенка и насыщенности" + } } } }, @@ -5239,8 +5503,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "tyhjä" } }, "fr": { @@ -5257,8 +5521,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "tom" } }, "nl": { @@ -5269,8 +5533,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "пусто" } } } @@ -5351,14 +5615,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Activar atenuación" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ota himmennys käyttöön" } }, "fr": { @@ -5375,8 +5639,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aktiver dimming" } }, "nl": { @@ -5387,8 +5651,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Включить затемнение" } } } @@ -5410,14 +5674,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Activar salvapantallas" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ota näytönsäästäjä käyttöön" } }, "fr": { @@ -5434,8 +5698,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aktiver skjermsparer" } }, "nl": { @@ -5446,8 +5710,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Включить заставку экрана" } } } @@ -5469,14 +5733,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Introducir un nombre para el nuevo hogar" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Syötä nimi uudelle kodille" } }, "fr": { @@ -5493,8 +5757,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Angi et navn for det nye hjemmet" } }, "nl": { @@ -5505,8 +5769,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Введите имя для нового дома" } } } @@ -5528,14 +5792,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Introducir un nuevo nombre para el hogar ‘%@’" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Syötä uusi nimi kodille ‘%@’" } }, "fr": { @@ -5552,8 +5816,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Angi et nytt navn for hjemmet ‘%@’" } }, "nl": { @@ -5564,8 +5828,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Введите новое имя для дома «%@»" } } } @@ -5587,14 +5851,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Introducir nuevo valor" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Syötä uusi arvo" } }, "fr": { @@ -5611,8 +5875,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Angi ny verdi" } }, "nl": { @@ -5623,8 +5887,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Введите новое значение" } } } @@ -5705,14 +5969,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Introducir texto" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Syötä teksti" } }, "fr": { @@ -5729,8 +5993,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Angi tekst" } }, "nl": { @@ -5741,8 +6005,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Введите текст" } } } @@ -5764,14 +6028,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Introducir URL del servidor remoto" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Syötä etäpalvelimen URL" } }, "fr": { @@ -5788,8 +6052,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Angi URL til fjernserver" } }, "nl": { @@ -5800,8 +6064,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Введите URL удалённого сервера" } } } @@ -5888,8 +6152,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Virhe" } }, "fr": { @@ -5906,8 +6170,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Feil" } }, "nl": { @@ -5918,8 +6182,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ошибка" } } } @@ -5941,14 +6205,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Duración del fundido: %@ s" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Häivytyksen kesto: %@ s" } }, "fr": { @@ -5965,8 +6229,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Falming varighet: %@ s" } }, "nl": { @@ -5977,8 +6241,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Длительность затухания: %@ с" } } } @@ -6000,14 +6264,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Avance rápido" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Pikakelaus eteenpäin" } }, "fr": { @@ -6024,8 +6288,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Spol frem" } }, "nl": { @@ -6036,8 +6300,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Перемотка вперёд" } } } @@ -6065,8 +6329,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Fontti" } }, "fr": { @@ -6083,8 +6347,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Skrift" } }, "nl": { @@ -6095,8 +6359,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Шрифт" } } } @@ -6124,8 +6388,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Fonttikoko" } }, "fr": { @@ -6142,8 +6406,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Skriftstørrelse" } }, "nl": { @@ -6154,8 +6418,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Размер шрифта" } } } @@ -6177,14 +6441,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Foo" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Foo" } }, "fr": { @@ -6201,8 +6465,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Foo" } }, "nl": { @@ -6213,8 +6477,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Foo" } } } @@ -6303,6 +6567,30 @@ "state": "translated", "value": "Ottieni lo stato di ${itemEntity}" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Obtener estado de ${itemEntity}" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Hae ${itemEntity}-tila" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Hent status for ${itemEntity}" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Получить состояние ${itemEntity}" + } } } }, @@ -6329,8 +6617,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Hae elementin tila" } }, "fr": { @@ -6359,8 +6647,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Получить состояние элемента" } } } @@ -6441,14 +6729,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ocultar barra de estado" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Piilota tilapalkki" } }, "fr": { @@ -6465,8 +6753,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Skjul statuslinje" } }, "nl": { @@ -6477,8 +6765,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Скрыть строку состояния" } } } @@ -6506,8 +6794,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Koti" } }, "fr": { @@ -6524,8 +6812,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Hjem" } }, "nl": { @@ -6536,8 +6824,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Дом" } } } @@ -6559,14 +6847,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Tipo de icono" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kuvaketyyppi" } }, "fr": { @@ -6583,8 +6871,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ikonstil" } }, "nl": { @@ -6595,8 +6883,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Тип значка" } } } @@ -6677,14 +6965,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Intervalo de reposo: %lld s" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Lepoväli: %lld s" } }, "fr": { @@ -6701,8 +6989,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Inaktivitetsintervall: %lld s" } }, "nl": { @@ -6713,8 +7001,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Интервал простоя: %lld с" } } } @@ -6736,14 +7024,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ignorar certificados SSL" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ohita SSL-varmenteet" } }, "fr": { @@ -6760,8 +7048,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ignorer SSL-sertifikater" } }, "nl": { @@ -6772,8 +7060,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Игнорировать SSL-сертификаты" } } } @@ -6854,14 +7142,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Caché de imágenes" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kuvavälimuisti" } }, "fr": { @@ -6878,8 +7166,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Bildebuffer" } }, "nl": { @@ -6890,8 +7178,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Кэш изображений" } } } @@ -6919,8 +7207,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Tuo" } }, "fr": { @@ -6937,8 +7225,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Importer" } }, "nl": { @@ -6949,8 +7237,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Импорт" } } } @@ -6972,14 +7260,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aumentar %@" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Suurenna %@" } }, "fr": { @@ -6996,8 +7284,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Øk %@" } }, "nl": { @@ -7008,8 +7296,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Увеличить %@" } } } @@ -7096,8 +7384,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Virheellinen arvo %lld kohteelle %@ (0–100)" } }, "fr": { @@ -7126,8 +7414,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Недопустимое значение %lld для %@ (0–100)" } } } @@ -7155,8 +7443,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Virheellinen arvo: %@ kohteelle %@ täytyy olla HSB (0–360, 0–100, 0–100)" } }, "fr": { @@ -7185,8 +7473,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Недопустимое значение: %@ для %@ должно быть HSB (0–360, 0–100, 0–100)" } } } @@ -7273,8 +7561,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Elementti" } }, "fr": { @@ -7303,8 +7591,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Элемент" } } } @@ -7326,14 +7614,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "El elemento ‘%@’ no está en el hogar ‘%@’" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Elementti ‘%@’ ei ole kodissa ‘%@’" } }, "fr": { @@ -7350,8 +7638,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Element ‘%@’ er ikke i hjemmet ‘%@’" } }, "nl": { @@ -7362,8 +7650,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Элемент «%@» не входит в дом «%@»" } } } @@ -7395,6 +7683,30 @@ "state": "translated", "value": "Elemento '%@' non trovato" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Elemento ‘%@’ no encontrado" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Elementtiä ‘%@’ ei löydy" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Element ‘%@’ ikke funnet" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Элемент «%@» не найден" + } } } }, @@ -7421,8 +7733,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Elementit" } }, "fr": { @@ -7439,8 +7751,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Elementer" } }, "nl": { @@ -7451,8 +7763,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Элементы" } } } @@ -7480,8 +7792,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Etiketti" } }, "fr": { @@ -7498,8 +7810,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Etikett" } }, "nl": { @@ -7510,8 +7822,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Метка" } } } @@ -7533,14 +7845,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Latitud" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Leveysaste" } }, "fr": { @@ -7557,8 +7869,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Breddegrad" } }, "nl": { @@ -7569,8 +7881,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Широта" } } } @@ -7592,14 +7904,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "La latitud debe estar entre -90 y 90" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Leveysasteen on oltava välillä −90 ja 90" } }, "fr": { @@ -7616,8 +7928,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Breddegraden må være mellom -90 og 90" } }, "nl": { @@ -7628,8 +7940,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Широта должна быть от -90 до 90" } } } @@ -7716,8 +8028,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Oikeudelliset tiedot" } }, "fr": { @@ -7734,8 +8046,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Juridisk" } }, "nl": { @@ -7746,8 +8058,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Юридическая информация" } } } @@ -7769,14 +8081,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Cargando elementos…" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ladataan elementtejä…" } }, "fr": { @@ -7793,8 +8105,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Laster elementer…" } }, "nl": { @@ -7805,8 +8117,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Загрузка элементов…" } } } @@ -7828,14 +8140,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Cargando mapa del sitio…" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ladataan sivukarttaa…" } }, "fr": { @@ -7852,8 +8164,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Laster nettstedskart…" } }, "nl": { @@ -7864,8 +8176,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Загрузка карты сайта…" } } } @@ -7956,6 +8268,30 @@ "state": "translated", "value": "Potrebbe essere necessario l'accesso alla rete locale." } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Es posible que se requiera acceso a la red local." + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Lähiverkon käyttöoikeus voi olla tarpeen." + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Tilgang til lokalt nettverk kan være nødvendig." + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Может потребоваться доступ к локальной сети." + } } } }, @@ -7986,6 +8322,30 @@ "state": "translated", "value": "Accesso alla rete locale richiesto" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Se requiere acceso a la red local" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Lähiverkon käyttöoikeus vaaditaan" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Tilgang til lokalt nettverk kreves" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Требуется доступ к локальной сети" + } } } }, @@ -8006,14 +8366,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Servidor local" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Paikallinen palvelin" } }, "fr": { @@ -8030,8 +8390,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Lokal server" } }, "nl": { @@ -8042,8 +8402,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Локальный сервер" } } } @@ -8129,8 +8489,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Sijainti" } }, "fr": { @@ -8147,8 +8507,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Plassering" } }, "nl": { @@ -8159,8 +8519,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Местоположение" } } } @@ -8192,6 +8552,30 @@ "state": "translated", "value": "Elemento posizione" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Elemento de ubicación" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Sijaintielementti" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Plasseringselement" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Элемент местоположения" + } } } }, @@ -8218,8 +8602,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Lokit" } }, "fr": { @@ -8236,8 +8620,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Logger" } }, "nl": { @@ -8248,8 +8632,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Журналы" } } } @@ -8271,14 +8655,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Longitud" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Pituusaste" } }, "fr": { @@ -8295,8 +8679,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Lengdegrad" } }, "nl": { @@ -8307,8 +8691,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Долгота" } } } @@ -8330,14 +8714,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "La longitud debe estar entre -180 y 180" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Pituusasteen on oltava välillä −180 ja 180" } }, "fr": { @@ -8354,8 +8738,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Lengdegraden må være mellom -180 og 180" } }, "nl": { @@ -8366,8 +8750,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Долгота должна быть от -180 до 180" } } } @@ -8389,14 +8773,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Baja %@" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Laskee %@" } }, "fr": { @@ -8413,8 +8797,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Senker med %@" } }, "nl": { @@ -8425,8 +8809,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Снижает на %@" } } } @@ -8454,8 +8838,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Pää" } }, "fr": { @@ -8472,8 +8856,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Hoved" } }, "nl": { @@ -8484,8 +8868,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Главная" } } } @@ -8565,14 +8949,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Gestionar hogares" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Hallitse koteja" } }, "fr": { @@ -8589,8 +8973,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Administrer hjem" } }, "nl": { @@ -8601,8 +8985,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Управление домами" } } } @@ -8682,14 +9066,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Intervalo de movimiento: %lld s" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Liikeväli: %lld s" } }, "fr": { @@ -8706,8 +9090,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Bevegelsesintervall: %lld s" } }, "nl": { @@ -8718,8 +9102,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Интервал движения: %lld с" } } } @@ -8747,8 +9131,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Nimi" } }, "fr": { @@ -8765,8 +9149,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Navn" } }, "nl": { @@ -8777,8 +9161,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Имя" } } } @@ -8800,14 +9184,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Nombre para el nuevo hogar" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Nimi uudelle kodille" } }, "fr": { @@ -8824,8 +9208,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Navn for nytt hjem" } }, "nl": { @@ -8836,8 +9220,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Имя для нового дома" } } } @@ -8917,14 +9301,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Nuevo nombre" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Uusi nimi" } }, "fr": { @@ -8941,8 +9325,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Nytt navn" } }, "nl": { @@ -8953,8 +9337,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Новое имя" } } } @@ -8976,14 +9360,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Siguiente" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Seuraava" } }, "fr": { @@ -9000,8 +9384,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Neste" } }, "nl": { @@ -9012,8 +9396,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Следующий" } } } @@ -9035,14 +9419,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "No hay certificados de servidor aceptados" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ei hyväksyttyjä palvelinvarmenteita" } }, "fr": { @@ -9059,8 +9443,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ingen godkjente serversertifikater" } }, "nl": { @@ -9071,8 +9455,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Нет принятых сертификатов сервера" } } } @@ -9093,14 +9477,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Sin URL de imagen" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ei kuvan URL-osoitetta" } }, "fr": { @@ -9117,8 +9501,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ingen bilde-URL" } }, "nl": { @@ -9129,8 +9513,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Нет URL изображения" } } } @@ -9152,14 +9536,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "No hay mapas del sitio disponibles" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ei sivukarttoja saatavilla" } }, "fr": { @@ -9176,8 +9560,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ingen nettstedskart tilgjengelig" } }, "nl": { @@ -9188,8 +9572,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Нет доступных карт сайта" } } } @@ -9211,14 +9595,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Sin URL de vídeo" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ei videon URL-osoitetta" } }, "fr": { @@ -9235,8 +9619,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ingen video-URL" } }, "nl": { @@ -9247,8 +9631,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Нет URL видео" } } } @@ -9270,14 +9654,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "No hay widgets disponibles." } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ei widgettejä saatavilla." } }, "fr": { @@ -9294,8 +9678,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ingen widgeter tilgjengelig." } }, "nl": { @@ -9306,8 +9690,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Нет доступных виджетов." } } } @@ -9388,14 +9772,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Sin conexión, reintentando…" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ei yhteyttä, yritetään uudelleen…" } }, "fr": { @@ -9412,8 +9796,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ingen tilkobling, kobler til igjen…" } }, "nl": { @@ -9424,8 +9808,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Нет подключения, повторное подключение…" } } } @@ -9628,8 +10012,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ilmoitukset" } }, "fr": { @@ -9646,8 +10030,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Varsler" } }, "nl": { @@ -9658,8 +10042,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Уведомления" } } } @@ -9691,6 +10075,30 @@ "state": "translated", "value": "Elemento numerico" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Elemento numérico" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Numeraalielementti" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Tallselement" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Числовой элемент" + } } } }, @@ -9717,8 +10125,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "pois" } }, "fr": { @@ -9735,8 +10143,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "av" } }, "nl": { @@ -9747,8 +10155,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "выкл." } } } @@ -9776,8 +10184,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Pois" } }, "fr": { @@ -9794,8 +10202,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Av" } }, "nl": { @@ -9806,8 +10214,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Выкл." } } } @@ -9835,8 +10243,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Offline" } }, "fr": { @@ -9853,8 +10261,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Frakoblet" } }, "nl": { @@ -9865,8 +10273,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Не в сети" } } } @@ -10071,8 +10479,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "OK" } }, "fr": { @@ -10089,8 +10497,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "OK" } }, "nl": { @@ -10101,8 +10509,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "ОК" } } } @@ -10130,8 +10538,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "päällä" } }, "fr": { @@ -10148,8 +10556,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "på" } }, "nl": { @@ -10160,8 +10568,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "вкл." } } } @@ -10183,14 +10591,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Encendido" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Päällä" } }, "fr": { @@ -10207,8 +10615,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "På" } }, "nl": { @@ -10219,8 +10627,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Вкл." } } } @@ -10242,14 +10650,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Una vez" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kerran" } }, "fr": { @@ -10266,8 +10674,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Én gang" } }, "nl": { @@ -10278,8 +10686,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Один раз" } } } @@ -10391,8 +10799,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Открыто" } } } @@ -10424,6 +10832,30 @@ "state": "translated", "value": "Apri Impostazioni" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Abrir ajustes" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Avaa asetukset" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Åpne innstillinger" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Открыть настройки" + } } } }, @@ -10444,14 +10876,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Servicio en la nube openHAB" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "openHAB-pilvipalvelu" } }, "fr": { @@ -10468,8 +10900,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "openHAB-skytjeneste" } }, "nl": { @@ -10480,8 +10912,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Облачный сервис openHAB" } } } @@ -10679,14 +11111,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Pausa" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Tauko" } }, "fr": { @@ -10703,8 +11135,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Pause" } }, "nl": { @@ -10715,8 +11147,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Пауза" } } } @@ -10738,14 +11170,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Reproducir" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Toista" } }, "fr": { @@ -10762,8 +11194,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Spill av" } }, "nl": { @@ -10774,8 +11206,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Воспроизвести" } } } @@ -10797,14 +11229,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Acción del reproductor" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Soittimen toiminto" } }, "fr": { @@ -10821,8 +11253,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Spillerhandling" } }, "nl": { @@ -10833,8 +11265,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Действие плеера" } } } @@ -10864,7 +11296,31 @@ "it": { "stringUnit": { "state": "translated", - "value": "Elemento player" + "value": "Elemento player" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Elemento reproductor" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Soitinelementti" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Spillerelement" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Элемент плеера" } } } @@ -10951,8 +11407,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Asetukset" } }, "fr": { @@ -10969,8 +11425,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Innstillinger" } }, "nl": { @@ -10981,8 +11437,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Настройки" } } } @@ -11004,14 +11460,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Estado de vista previa" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Esikatselutila" } }, "fr": { @@ -11028,8 +11484,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Forhåndsvisningstilstand" } }, "nl": { @@ -11040,8 +11496,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Состояние предварительного просмотра" } } } @@ -11063,14 +11519,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Anterior" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Edellinen" } }, "fr": { @@ -11087,8 +11543,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Forrige" } }, "nl": { @@ -11099,8 +11555,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Предыдущий" } } } @@ -11180,14 +11636,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Comandos en cola: %lld" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Jonossa olevat komennot: %lld" } }, "fr": { @@ -11204,8 +11660,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kommandoer i kø: %lld" } }, "nl": { @@ -11216,8 +11672,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Команды в очереди: %lld" } } } @@ -11239,14 +11695,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Sube %@" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Nostaa %@" } }, "fr": { @@ -11263,8 +11719,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Øker med %@" } }, "nl": { @@ -11275,8 +11731,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Повышает на %@" } } } @@ -11357,14 +11813,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Controles deslizantes en tiempo real" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Reaaliaikaiset liukusäätimet" } }, "fr": { @@ -11381,8 +11837,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Sanntidsskyvere" } }, "nl": { @@ -11393,8 +11849,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Слайдеры реального времени" } } } @@ -11416,14 +11872,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Servidor remoto" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Etäpalvelin" } }, "fr": { @@ -11440,8 +11896,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Fjernserver" } }, "nl": { @@ -11452,8 +11908,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Удалённый сервер" } } } @@ -11599,8 +12055,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Nimeä uudelleen" } }, "fr": { @@ -11617,8 +12073,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Gi nytt navn" } }, "nl": { @@ -11629,8 +12085,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Переименовать" } } } @@ -11652,14 +12108,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Restaurar brillo anterior al despertar" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Palauta aiempi kirkkaus herätessä" } }, "fr": { @@ -11676,8 +12132,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Gjenopprett tidligere lysstyrke ved vekking" } }, "nl": { @@ -11688,8 +12144,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Восстановить предыдущую яркость при пробуждении" } } } @@ -11717,8 +12173,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Hae elementin nykyinen tila" } }, "fr": { @@ -11747,8 +12203,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Получить текущее состояние элемента" } } } @@ -11829,14 +12285,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Rebobinar" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kelaa taaksepäin" } }, "fr": { @@ -11853,8 +12309,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Spol tilbake" } }, "nl": { @@ -11865,8 +12321,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Перемотка назад" } } } @@ -11953,8 +12409,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Tallenna" } }, "fr": { @@ -11971,8 +12427,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Lagre" } }, "nl": { @@ -11983,8 +12439,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Сохранить" } } } @@ -12012,8 +12468,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Näytönsäästäjä" } }, "fr": { @@ -12030,8 +12486,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Skjermsparer" } }, "nl": { @@ -12042,8 +12498,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Заставка экрана" } } } @@ -12065,14 +12521,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ajustes del salvapantallas" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Näytönsäästäjän asetukset" } }, "fr": { @@ -12089,8 +12545,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Skjermsparer-innstillinger" } }, "nl": { @@ -12101,8 +12557,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Настройки заставки" } } } @@ -12130,8 +12586,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Haku" } }, "fr": { @@ -12148,8 +12604,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Søk" } }, "nl": { @@ -12160,8 +12616,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Поиск" } } } @@ -12183,14 +12639,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Buscar un elemento" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Etsi elementtiä" } }, "fr": { @@ -12207,8 +12663,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Søk etter et element" } }, "nl": { @@ -12219,8 +12675,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Найти элемент" } } } @@ -12354,14 +12810,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Seleccionar color" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Valitse väri" } }, "fr": { @@ -12378,8 +12834,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Velg farge" } }, "nl": { @@ -12390,8 +12846,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Выбрать цвет" } } } @@ -12480,6 +12936,30 @@ "state": "translated", "value": "Invia ${action} a ${itemEntity}" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Enviar ${action} a ${itemEntity}" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Lähetä ${action} kohteeseen ${itemEntity}" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Send ${action} til ${itemEntity}" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Отправить ${action} в ${itemEntity}" + } } } }, @@ -12500,14 +12980,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Enviar un comando al reproductor como reproducir, pausar, siguiente o anterior" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Lähetä soittimelle komento, kuten toista, tauko, seuraava tai edellinen" } }, "fr": { @@ -12524,8 +13004,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Send en spillerkommando som spill av, pause, neste eller forrige" } }, "nl": { @@ -12536,8 +13016,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Отправить команду плееру: воспроизвести, пауза, следующий или предыдущий" } } } @@ -12558,14 +13038,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Enviando comandos: %lld" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Lähetetään komentoja: %lld" } }, "fr": { @@ -12582,8 +13062,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Sender kommandoer: %lld" } }, "nl": { @@ -12594,8 +13074,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Отправка команд: %lld" } } } @@ -12624,8 +13104,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "%1$@ lähetetty kohteeseen %2$@" } }, "fr": { @@ -12654,8 +13134,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "%1$@ отправлено в %2$@" } } } @@ -12677,14 +13157,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ubicación %lf, %lf enviada a %@" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Sijainti %lf, %lf lähetetty kohteeseen %@" } }, "fr": { @@ -12701,8 +13181,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Plassering %lf, %lf sendt til %@" } }, "nl": { @@ -12713,8 +13193,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Местоположение %lf, %lf отправлено в %@" } } } @@ -12742,8 +13222,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Väriarvo %@ lähetetty kohteeseen %@" } }, "fr": { @@ -12772,8 +13252,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Значение цвета %@ отправлено в %@" } } } @@ -12795,14 +13275,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Fecha %@ enviada a %@" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Päivämäärä %@ lähetetty kohteeseen %@" } }, "fr": { @@ -12819,8 +13299,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Dato %@ sendt til %@" } }, "nl": { @@ -12831,8 +13311,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Дата %@ отправлена в %@" } } } @@ -12860,8 +13340,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Luku %lf lähetetty kohteeseen %@" } }, "fr": { @@ -12890,8 +13370,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Число %lf отправлено в %@" } } } @@ -12919,8 +13399,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Merkkijono %@ lähetetty kohteeseen %@" } }, "fr": { @@ -12949,8 +13429,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Строка %@ отправлена в %@" } } } @@ -12978,8 +13458,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Arvo %lld lähetetty kohteeseen %@" } }, "fr": { @@ -13008,8 +13488,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Значение %lld отправлено в %@" } } } @@ -13039,6 +13519,30 @@ "state": "translated", "value": "Imposta ${itemEntity} su ${latitude}, ${longitude}" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Establecer ${itemEntity} en ${latitude}, ${longitude}" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Aseta ${itemEntity} arvoon ${latitude}, ${longitude}" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Sett ${itemEntity} til ${latitude}, ${longitude}" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Установить ${itemEntity} в ${latitude}, ${longitude}" + } } } }, @@ -13067,6 +13571,30 @@ "state": "translated", "value": "Imposta ${itemEntity} su ${value}" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Establecer ${itemEntity} en ${value}" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Aseta ${itemEntity} arvoon ${value}" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Sett ${itemEntity} til ${value}" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Установить ${itemEntity} в ${value}" + } } } }, @@ -13095,6 +13623,30 @@ "state": "translated", "value": "Imposta ${itemEntity} su ${value} (HSB)" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Establecer ${itemEntity} en ${value} (HSB)" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Aseta ${itemEntity} arvoon ${value} (HSB)" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Sett ${itemEntity} til ${value} (HSB)" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Установить ${itemEntity} в ${value} (HSB)" + } } } }, @@ -13227,8 +13779,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aseta värisäätimen arvo" } }, "fr": { @@ -13257,8 +13809,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Установить значение элемента управления цветом" } } } @@ -13286,8 +13838,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aseta kontaktin tilanarvo" } }, "fr": { @@ -13316,8 +13868,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Установить значение состояния контакта" } } } @@ -13339,14 +13891,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Establecer valor del control DateTime" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aseta DateTime-säätimen arvo" } }, "fr": { @@ -13363,8 +13915,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Angi DateTime-kontrollverdi" } }, "nl": { @@ -13375,8 +13927,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Установить значение элемента управления DateTime" } } } @@ -13404,8 +13956,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aseta himmentimen tai rullaverkon arvo" } }, "fr": { @@ -13434,8 +13986,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Установить значение диммера или рольставни" } } } @@ -13457,14 +14009,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Establecer valor del control de ubicación" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aseta sijaintisäätimen arvo" } }, "fr": { @@ -13481,8 +14033,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Angi plasseringskontrollverdi" } }, "nl": { @@ -13493,8 +14045,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Установить значение элемента управления местоположением" } } } @@ -13522,8 +14074,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aseta numerosäätimen arvo" } }, "fr": { @@ -13552,8 +14104,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Установить числовое значение элемента управления" } } } @@ -13575,14 +14127,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Establecer valor del control del reproductor" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aseta soittimen säätimen arvo" } }, "fr": { @@ -13599,8 +14151,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Angi spillerkontrollverdi" } }, "nl": { @@ -13611,8 +14163,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Установить значение элемента управления плеером" } } } @@ -13640,8 +14192,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aseta merkkijonosäätimen arvo" } }, "fr": { @@ -13670,8 +14222,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Установить строковое значение элемента управления" } } } @@ -13699,8 +14251,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aseta kytkimen tila" } }, "fr": { @@ -13729,8 +14281,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Установить состояние переключателя" } } } @@ -13758,8 +14310,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aseta värielementin väri" } }, "fr": { @@ -13788,8 +14340,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Установить цвет элемента управления цветом" } } } @@ -13811,14 +14363,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Establecer la fecha y hora de un elemento de control DateTime" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aseta DateTime-elementin päivämäärä ja aika" } }, "fr": { @@ -13835,8 +14387,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Angi dato og tid for et DateTime-kontrollelement" } }, "nl": { @@ -13847,8 +14399,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Установить дату и время элемента DateTime" } } } @@ -13876,8 +14428,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aseta numeroelementin desimaaliarvo" } }, "fr": { @@ -13906,8 +14458,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Установить десятичное значение числового элемента" } } } @@ -13935,8 +14487,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aseta himmentimen tai rullaverkon kokonaislukuarvo" } }, "fr": { @@ -13965,8 +14517,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Установить целочисленное значение диммера или рольставни" } } } @@ -13988,14 +14540,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Establecer la latitud y longitud de un elemento de control de ubicación" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aseta sijaintielementin leveys- ja pituusaste" } }, "fr": { @@ -14012,8 +14564,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Angi bredde- og lengdegrad for et plasseringskontrollelement" } }, "nl": { @@ -14024,8 +14576,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Установить широту и долготу элемента местоположения" } } } @@ -14055,6 +14607,30 @@ "state": "translated", "value": "Imposta lo stato di ${itemEntity} su ${state}" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Establecer el estado de ${itemEntity} en ${state}" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Aseta ${itemEntity}-tila arvoon ${state}" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Angi status for ${itemEntity} til ${state}" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Установить состояние ${itemEntity} в ${state}" + } } } }, @@ -14081,8 +14657,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aseta kontaktin tilaksi auki tai kiinni" } }, "fr": { @@ -14111,8 +14687,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Установить состояние контакта: открыт или закрыт" } } } @@ -14134,14 +14710,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Establecer el estado de un interruptor en encendido o apagado, o alternar su estado" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aseta kytkimen tilaksi päällä tai pois tai vaihda sen tilaa" } }, "fr": { @@ -14158,8 +14734,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Angi brytertilstanden til på eller av, eller bytt tilstand" } }, "nl": { @@ -14170,8 +14746,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Установить состояние переключателя вкл. или выкл., или переключить его состояние" } } } @@ -14199,8 +14775,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aseta merkkijonoelementin merkkijono" } }, "fr": { @@ -14229,8 +14805,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Установить строку строкового элемента" } } } @@ -14252,14 +14828,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Establecer valor" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Aseta arvo" } }, "fr": { @@ -14276,8 +14852,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Angi verdi" } }, "nl": { @@ -14288,8 +14864,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Установить значение" } } } @@ -14375,8 +14951,8 @@ }, "fi": { "stringUnit": { - "state": "needs_review", - "value": "Settings not yet received from phone." + "state": "translated", + "value": "Asetuksia ei ole vielä vastaanotettu iPhonelta." } }, "fr": { @@ -14405,8 +14981,8 @@ }, "ru": { "stringUnit": { - "state": "needs_review", - "value": "Settings not yet received from phone." + "state": "translated", + "value": "Настройки ещё не получены с iPhone." } } } @@ -14428,14 +15004,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Mostrar fecha" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Näytä päivämäärä" } }, "fr": { @@ -14452,8 +15028,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Vis dato" } }, "nl": { @@ -14464,8 +15040,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Показать дату" } } } @@ -14487,14 +15063,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Mostrar campo de búsqueda" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Näytä hakukenttä" } }, "fr": { @@ -14511,8 +15087,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Vis søkefelt" } }, "nl": { @@ -14523,8 +15099,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Показать поле поиска" } } } @@ -14546,14 +15122,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Mostrar segundos" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Näytä sekunnit" } }, "fr": { @@ -14570,8 +15146,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Vis sekunder" } }, "nl": { @@ -14582,8 +15158,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Показать секунды" } } } @@ -14605,14 +15181,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Mostrar hora" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Näytä aika" } }, "fr": { @@ -14629,8 +15205,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Vis tid" } }, "nl": { @@ -14641,8 +15217,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Показать время" } } } @@ -14781,14 +15357,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Mapa del sitio" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Sivukartta" } }, "fr": { @@ -14805,8 +15381,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Nettstedskart" } }, "nl": { @@ -14817,8 +15393,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Карта сайта" } } } @@ -14850,6 +15426,30 @@ "state": "translated", "value": "Registrazione diagnostica sitemap" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Registro de diagnóstico del mapa del sitio" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Sivukartan diagnostiikkaloki" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Diagnoslogging for nettstedskart" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Журналирование диагностики карты сайта" + } } } }, @@ -14870,14 +15470,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Mapa del sitio para Apple Watch" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Apple Watch -sivukartta" } }, "fr": { @@ -14894,8 +15494,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Nettstedskart for Apple Watch" } }, "nl": { @@ -14906,8 +15506,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Карта сайта для Apple Watch" } } } @@ -14987,14 +15587,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Mapas del sitio" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Sivukartat" } }, "fr": { @@ -15011,8 +15611,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Nettstedskart" } }, "nl": { @@ -15023,8 +15623,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Карты сайта" } } } @@ -15046,14 +15646,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Tamaño: %llu MB" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Koko: %llu Mt" } }, "fr": { @@ -15070,8 +15670,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Størrelse: %llu MB" } }, "nl": { @@ -15082,8 +15682,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Размер: %llu МБ" } } } @@ -15105,14 +15705,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Blanco suave" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Pehmeä valkoinen" } }, "fr": { @@ -15129,8 +15729,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Myk hvit" } }, "nl": { @@ -15141,8 +15741,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Мягкий белый" } } } @@ -15164,14 +15764,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ordenar mapas del sitio por" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Lajittele sivukartat" } }, "fr": { @@ -15188,8 +15788,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Sorter nettstedskart etter" } }, "nl": { @@ -15200,8 +15800,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Сортировать карты сайта по" } } } @@ -15282,14 +15882,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Error SSL. No se pudo establecer la conexión de forma segura." } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "SSL-virhe. Yhteyttä ei voitu muodostaa turvallisesti." } }, "fr": { @@ -15306,8 +15906,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "SSL-feil. Tilkoblingen kunne ikke opprettes sikkert." } }, "nl": { @@ -15318,8 +15918,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ошибка SSL. Не удалось установить безопасное соединение." } } } @@ -15412,7 +16012,7 @@ "fr": { "stringUnit": { "state": "translated", - "value": "Le certificat SSL présenté par %1$@ pour %2$@ n’est pas valide. Veux-tu continuer ?" + "value": "Le certificat SSL présenté par %1$@ pour %2$@ n’est pas valide. Veux-tu continuer ?" } }, "it": { @@ -15470,7 +16070,7 @@ "fr": { "stringUnit": { "state": "translated", - "value": "Le certificat SSL présenté par %1$@ pour %2$@ ne correspond pas à l’enregistrement. Veux-tu continuer ?" + "value": "Le certificat SSL présenté par %1$@ pour %2$@ ne correspond pas à l’enregistrement. Veux-tu continuer ?" } }, "it": { @@ -15580,8 +16180,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Tila" } }, "fr": { @@ -15598,8 +16198,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Tilstand" } }, "nl": { @@ -15610,8 +16210,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Состояние" } } } @@ -15643,6 +16243,30 @@ "state": "translated", "value": "Elemento stringa" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Elemento de texto" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Merkkijonoelementti" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Tekststrengselement" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Строковый элемент" + } } } }, @@ -15722,14 +16346,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Acción del interruptor" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kytkimen toiminto" } }, "fr": { @@ -15746,8 +16370,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Bryterhandling" } }, "nl": { @@ -15758,8 +16382,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Действие переключателя" } } } @@ -15791,6 +16415,30 @@ "state": "translated", "value": "Elemento interruttore" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Elemento interruptor" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Kytkinelementti" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Bryterelement" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Элемент переключателя" + } } } }, @@ -15930,8 +16578,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Järjestelmä" } }, "fr": { @@ -15948,8 +16596,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "System" } }, "nl": { @@ -15960,8 +16608,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Система" } } } @@ -15983,14 +16631,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Probar conexión" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Testaa yhteys" } }, "fr": { @@ -16007,8 +16655,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Test tilkobling" } }, "nl": { @@ -16019,8 +16667,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Проверить соединение" } } } @@ -16042,14 +16690,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Probar salvapantallas" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Testaa näytönsäästäjä" } }, "fr": { @@ -16066,8 +16714,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Test skjermsparer" } }, "nl": { @@ -16078,8 +16726,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Проверить заставку" } } } @@ -16101,14 +16749,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "La conexión ha agotado el tiempo de espera. Inténtelo de nuevo más tarde." } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Yhteys aikakatkaistiin. Yritä myöhemmin uudelleen." } }, "fr": { @@ -16125,8 +16773,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Tilkoblingen ble tidsavbrutt. Prøv igjen senere." } }, "nl": { @@ -16137,8 +16785,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Время соединения истекло. Повторите попытку позже." } } } @@ -16166,8 +16814,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kohteen %@ tila on %@" } }, "fr": { @@ -16196,8 +16844,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Состояние %@ равно %@" } } } @@ -16225,8 +16873,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kohteen %@ tilaksi asetettiin %@" } }, "fr": { @@ -16255,8 +16903,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Состояние %@ установлено в %@" } } } @@ -16288,6 +16936,30 @@ "state": "translated", "value": "L'URL non è valido. Controlla il formato (es. http://192.168.2.1:8080 o http://[::1]:8080 per IPv6)." } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "La URL no es válida. Compruebe el formato (p. ej., http://192.168.2.1:8080 o http://[::1]:8080 para IPv6)." + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "URL on virheellinen. Tarkista muoto (esim. http://192.168.2.1:8080 tai http://[::1]:8080 IPv6:lle)." + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "URL-en er ugyldig. Sjekk formatet (f.eks. http://192.168.2.1:8080 eller http://[::1]:8080 for IPv6)." + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "URL недействителен. Проверьте формат (например, http://192.168.2.1:8080 или http://[::1]:8080 для IPv6)." + } } } }, @@ -16308,14 +16980,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Mosaicos" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ruudut" } }, "fr": { @@ -16332,8 +17004,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Fliser" } }, "nl": { @@ -16344,8 +17016,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Плитки" } } } @@ -16367,14 +17039,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Temporización" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Ajoitus" } }, "fr": { @@ -16391,8 +17063,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Timing" } }, "nl": { @@ -16403,8 +17075,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Синхронизация" } } } @@ -16436,6 +17108,30 @@ "state": "translated", "value": "Per connetterti al tuo server openHAB locale, consenti l'accesso alla rete locale quando richiesto. Se in precedenza hai negato l'accesso, abilitalo in Impostazioni → Privacy e sicurezza → Rete locale." } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Para conectarse a su servidor openHAB local, permita el acceso a la red local cuando se le solicite. Si lo denegó anteriormente, actívelo en Ajustes → Privacidad y seguridad → Red local." + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Muodosta yhteys paikalliseen openHAB-palvelimeen sallimalla lähiverkkoyhteys pyydettäessä. Jos olet aiemmin kieltänyt sen, ota se käyttöön kohdassa Asetukset → Tietosuoja ja turvallisuus → Lähiverkko." + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "For å koble til din lokale openHAB-server, vennligst tillat tilgang til lokalt nettverk når du blir bedt om det. Hvis du tidligere nektet det, aktiver det i Innstillinger → Personvern og sikkerhet → Lokalt nettverk." + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Для подключения к локальному серверу openHAB разрешите доступ к локальной сети при появлении запроса. Если вы ранее отказали, включите его в Настройки → Конфиденциальность и безопасность → Локальная сеть." + } } } }, @@ -16493,8 +17189,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "переключить" } } } @@ -16516,14 +17212,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Alternar" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Vaihda" } }, "fr": { @@ -16540,8 +17236,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Bytt" } }, "nl": { @@ -16552,8 +17248,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Переключить" } } } @@ -16693,14 +17389,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Error inesperado: %@" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Odottamaton virhe: %@" } }, "fr": { @@ -16717,8 +17413,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Uventet feil: %@" } }, "nl": { @@ -16729,8 +17425,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Непредвиденная ошибка: %@" } } } @@ -16871,8 +17567,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Päivitetään…" } }, "fr": { @@ -16889,8 +17585,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Oppdaterer…" } }, "nl": { @@ -16901,8 +17597,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Обновление…" } } } @@ -16930,8 +17626,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "URL" } }, "fr": { @@ -16948,8 +17644,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "URL" } }, "nl": { @@ -16960,8 +17656,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "URL" } } } @@ -17106,8 +17802,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Käyttäjänimi" } }, "fr": { @@ -17124,8 +17820,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Brukernavn" } }, "nl": { @@ -17136,8 +17832,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Имя пользователя" } } } @@ -17165,8 +17861,8 @@ }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Arvo" } }, "fr": { @@ -17195,8 +17891,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Значение" } } } @@ -17276,14 +17972,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Muy frío" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Hyvin viileä" } }, "fr": { @@ -17300,8 +17996,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Svært kjølig" } }, "nl": { @@ -17312,8 +18008,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Очень холодный" } } } @@ -17335,14 +18031,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Muy cálido" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Hyvin lämmin" } }, "fr": { @@ -17359,8 +18055,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Svært varm" } }, "nl": { @@ -17371,8 +18067,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Очень тёплый" } } } @@ -17394,14 +18090,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Blanco cálido" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Lämmin valkoinen" } }, "fr": { @@ -17418,8 +18114,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Varm hvit" } }, "nl": { @@ -17430,8 +18126,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Тёплый белый" } } } @@ -17511,14 +18207,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Advertencia: Cambiar el nombre del hogar puede hacer que las integraciones externas, como los atajos, fallen hasta que se reconfiguren." } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Varoitus: Kodin uudelleennimeäminen voi aiheuttaa ulkoisten integraatioiden, kuten pikakomentojen, epäonnistumisen, kunnes ne on uudelleenmääritetty." } }, "fr": { @@ -17535,8 +18231,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Advarsel: Å gi hjemmet nytt navn kan føre til at eksterne integrasjoner som snarveier mislykkes inntil de er rekonfigurert." } }, "nl": { @@ -17547,8 +18243,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Предупреждение: переименование дома может привести к сбою внешних интеграций, таких как ярлыки, до их перенастройки." } } } @@ -17570,14 +18266,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Parece que estás sin conexión. Comprueba tu conexión a internet." } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Näyttää siltä, että olet offline. Tarkista internet-yhteytesi." } }, "fr": { @@ -17594,8 +18290,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Du ser ut til å være frakoblet. Sjekk internettilkoblingen din." } }, "nl": { @@ -17606,8 +18302,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Похоже, вы не в сети. Проверьте подключение к интернету." } } } @@ -17628,14 +18324,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Tamaño del reloj: %@" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Kellon koko: %@" } }, "fr": { @@ -17652,8 +18348,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Klokkestørrelse: %@" } }, "nl": { @@ -17664,8 +18360,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Размер часов: %@" } } }, @@ -17687,14 +18383,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Nivel de atenuación: %@" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Himmennystaso: %@" } }, "fr": { @@ -17711,8 +18407,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Dimmenivå: %@" } }, "nl": { @@ -17723,8 +18419,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Уровень затемнения: %@" } } }, @@ -17746,14 +18442,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Fecha relativa: %@" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Suhteellinen päivämäärä: %@" } }, "fr": { @@ -17770,8 +18466,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Relativ dato: %@" } }, "nl": { @@ -17782,8 +18478,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Относительная дата: %@" } } }, @@ -17805,14 +18501,14 @@ }, "es": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Restaurar brillo: %@" } }, "fi": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Palauta kirkkaus: %@" } }, "fr": { @@ -17829,8 +18525,8 @@ }, "nb": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Gjenopprett lysstyrke: %@" } }, "nl": { @@ -17841,8 +18537,8 @@ }, "ru": { "stringUnit": { - "state": "new", - "value": "" + "state": "translated", + "value": "Восстановить яркость: %@" } } }, From 3d384ccdfccaa1896cb43856390cddf20d47b62c Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Wed, 24 Jun 2026 18:12:59 +0200 Subject: [PATCH 28/91] fix: add provisioning profile for openHABWidgetExtension Add org.openhab.app.openHABWidget to Matchfile app identifiers and wire up update_code_signing_settings for the openHABWidgetExtension target in the beta lane to fix TestFlight archive failure. Signed-off-by: Tim Mueller-Seydlitz --- fastlane/Fastfile | 6 ++++++ fastlane/Matchfile | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/fastlane/Fastfile b/fastlane/Fastfile index c8e1531b3..6698bb6ad 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -149,6 +149,12 @@ platform :ios do build_configurations: ["Release"] ) + update_code_signing_settings( + targets: 'openHABWidgetExtension', + profile_name: 'match AppStore org.openhab.app.openHABWidget', + build_configurations: ["Release"] + ) + update_code_signing_settings( targets: 'OpenHABWatchComplicationsExtension', profile_name: 'match AppStore org.openhab.app.watchkitapp.OpenHABWatchComplications', diff --git a/fastlane/Matchfile b/fastlane/Matchfile index 6f37f651e..5f4fa3850 100644 --- a/fastlane/Matchfile +++ b/fastlane/Matchfile @@ -4,7 +4,7 @@ storage_mode("git") type("appstore") # The default type, can be: appstore, adhoc, enterprise or development -app_identifier(["org.openhab.app", "org.openhab.app.openHABIntents", "org.openhab.app.watchkitapp", "org.openhab.app.watchkitapp.OpenHABWatchComplications", "org.openhab.app.NotificationService"]) +app_identifier(["org.openhab.app", "org.openhab.app.openHABIntents", "org.openhab.app.watchkitapp", "org.openhab.app.watchkitapp.OpenHABWatchComplications", "org.openhab.app.NotificationService", "org.openhab.app.openHABWidget"]) # username("user@fastlane.tools") # Your Apple Developer Portal username # For all available options run `fastlane match --help` From 19c681031f1e6d84afdfb65cae7b483a84250236 Mon Sep 17 00:00:00 2001 From: openhab-bot Date: Wed, 24 Jun 2026 16:57:28 +0000 Subject: [PATCH 29/91] committed version bump: 3.4.1 (287) Signed-off-by: openhab-bot --- CHANGELOG.md | 26 ++++++++++++++++++++++++++ Version.xcconfig | 4 ++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd9b6b8bd..66a275091 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,32 @@ ## [Unreleased] +- fix: add provisioning profile for openHABWidgetExtension +- fix: replace UIScreen.main with scene-based screen access (iOS 27 crash) (#1269) +- Bump minor version number to 3.3.1 +- chore(deps): bump actions/checkout from 6 to 7 (#1268) +- chore: remove Crowdin workflow and clean up Package.resolved +- chore: remove unused DeviceKit dependency +- l10n: add Spanish, Finnish, Norwegian, and Russian translations +- l10n: switch German and French translations to informal address (du/tu) +- chore: remove 5 stale localization keys on feature branch +- l10n: add Spanish, Finnish, Norwegian, and Russian translations +- l10n: translate 2 new widget keys into all 9 languages +- chore: update openHABWidgetExtension scheme with askForAppToLaunch +- fix: restore project.pbxproj corrupted by bad 3-way merge of pbxproj +- chore: add shared schemes for openHABWidgetExtension and OpenHABWatchComplicationsExtension +- build: raise macOS minimum to 15 for AsyncSequence.Failure support +- chore: bump openHABTestsSwift deployment target to iOS 18.0 +- Merge develop + fix build warnings; add iOS home screen widgets with item display +- Sensor widget: format state values using stateDescription numberPattern +- Sensor widget: support 1/2/4 items per small/medium/large size +- Make ScrollToTop overlay button conditional (#1231) +- Bump minimum deployment targets to iOS 18 / watchOS 11 +- Use iOS 18 onScrollGeometryChange in SitemapPageView +- Use iOS 17 animation and scroll APIs +- ItemEventStream: backport SSE watchdog and self-contained network monitor +- openHABWidget: add interactive switch and sensor widgets + ## [Version 3.2.77, Build 286] - 2026-06-20Z - Fix Home re-tap reload and screensaver idle reset (#1266) diff --git a/Version.xcconfig b/Version.xcconfig index 9c8463cea..dd9301df6 100644 --- a/Version.xcconfig +++ b/Version.xcconfig @@ -6,5 +6,5 @@ // https://developer.apple.com/documentation/xcode/adding-a-build-configuration-file-to-your-project -MARKETING_VERSION = 3.4.0 -CURRENT_PROJECT_VERSION = 286 +MARKETING_VERSION = 3.4.1 +CURRENT_PROJECT_VERSION = 287 From 85c66980e49d812b0a787d2060207d9557a94f11 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Thu, 25 Jun 2026 09:13:36 +0200 Subject: [PATCH 30/91] fix: add OpenHABShortcuts.swift to app target to resolve AppShortcuts.xcstrings phrase warnings AppIntentsMetadataProcessor could not discover the AppShortcutsProvider because OpenHABShortcuts.swift was not compiled into the main app target, causing all phrases in AppShortcuts.xcstrings to be flagged as unused. Also removes redundant manual AppIntents source entries from the widget extension (already covered by the synchronized AppIntents folder) and excludes OpenHABShortcuts.swift from the widget synchronized group. Signed-off-by: Tim Mueller-Seydlitz --- .../Model/OpenHABNotification.swift | 1 - .../OpenHABCore/Util/NWPathMonitoring.swift | 1 - .../OpenHABCore/Util/OpenHABItemCache.swift | 1 - openHAB.xcodeproj/project.pbxproj | 101 ++- openHAB/AppDelegate.swift | 2 - openHAB/Models/SitemapPageViewModel.swift | 1 - openHAB/NotificationCenterDelegateImpl.swift | 1 - .../Supporting Files/AppShortcuts.xcstrings | 1 + .../Supporting Files/Localizable.xcstrings | 606 +----------------- 9 files changed, 63 insertions(+), 652 deletions(-) diff --git a/OpenHABCore/Sources/OpenHABCore/Model/OpenHABNotification.swift b/OpenHABCore/Sources/OpenHABCore/Model/OpenHABNotification.swift index c980af108..e3b252fd3 100644 --- a/OpenHABCore/Sources/OpenHABCore/Model/OpenHABNotification.swift +++ b/OpenHABCore/Sources/OpenHABCore/Model/OpenHABNotification.swift @@ -71,7 +71,6 @@ public struct OpenHABNotification: Identifiable, Hashable, Sendable { } } - /// Decode an instance of OpenHABNotification.CodingData rather than decoding a OpenHABNotificaiton value directly, /// then convert that into a openHABNotification /// Inspired by https://www.swiftbysundell.com/basics/codable?rq=codingdata diff --git a/OpenHABCore/Sources/OpenHABCore/Util/NWPathMonitoring.swift b/OpenHABCore/Sources/OpenHABCore/Util/NWPathMonitoring.swift index 6f6513f95..dc5bd50f9 100644 --- a/OpenHABCore/Sources/OpenHABCore/Util/NWPathMonitoring.swift +++ b/OpenHABCore/Sources/OpenHABCore/Util/NWPathMonitoring.swift @@ -41,4 +41,3 @@ public protocol NWPathMonitoring: AnyObject, Sendable { func startMonitoring(handler: @escaping (Bool) async -> Void) async func cancel() } - diff --git a/OpenHABCore/Sources/OpenHABCore/Util/OpenHABItemCache.swift b/OpenHABCore/Sources/OpenHABCore/Util/OpenHABItemCache.swift index 755c107df..b70553eb7 100644 --- a/OpenHABCore/Sources/OpenHABCore/Util/OpenHABItemCache.swift +++ b/OpenHABCore/Sources/OpenHABCore/Util/OpenHABItemCache.swift @@ -153,7 +153,6 @@ public actor OpenHABItemCache { /// handoff (~7 s in practice) without failing with noActiveConnection. private static let networkTimeout: TimeInterval = 10 - private static let stubsDefaultsKey = "openHABItemStubs" private static let sharedDefaultsSuiteName = "group.org.openhab.app" diff --git a/openHAB.xcodeproj/project.pbxproj b/openHAB.xcodeproj/project.pbxproj index 69e66ee8e..adcb96ce7 100644 --- a/openHAB.xcodeproj/project.pbxproj +++ b/openHAB.xcodeproj/project.pbxproj @@ -18,12 +18,6 @@ 3D3B2898C51F48BAA31210E4 /* ContactStateIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3552F03095D0082479C /* ContactStateIntent.swift */; }; 3F087E4025E845FFBCBE45E3 /* ItemEntityQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4352F07056F0082479C /* ItemEntityQuery.swift */; }; 5111C4CC62E66D2FFA5C92EB /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 06A8490213945021806A13D1 /* SwiftUI.framework */; }; - 0648F50178784F149905E7CF /* PlayerAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4062F0800010082479C /* PlayerAction.swift */; }; - 1C1CA8C8C8424BAF8018F9BE /* SetStringValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF35C2F0309B10082479C /* SetStringValueIntent.swift */; }; - 1E4E7DB36BAC4804AD5E58E5 /* SetColorValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3542F03095D0082479C /* SetColorValueIntent.swift */; }; - 2F399AC92F54599400F72A30 /* Flow in Frameworks */ = {isa = PBXBuildFile; productRef = 2F399AC82F54599400F72A30 /* Flow */; }; - 3D3B2898C51F48BAA31210E4 /* ContactStateIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3552F03095D0082479C /* ContactStateIntent.swift */; }; - 3F087E4025E845FFBCBE45E3 /* ItemEntityQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4352F07056F0082479C /* ItemEntityQuery.swift */; }; 598208789AC44F1D91DD5B17 /* SetPlayerValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4082F0800020082479C /* SetPlayerValueIntent.swift */; }; 5B292637B6E141DB98BCAA26 /* Home.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA448E772EF435B400F0893C /* Home.swift */; }; 61A606E5E6AE404584B0B1A0 /* SetNumberValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF35A2F0309A60082479C /* SetNumberValueIntent.swift */; }; @@ -53,13 +47,29 @@ BF0EB6DE70934966B1DC5479 /* SetDateTimeValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF40A2F0800030082479C /* SetDateTimeValueIntent.swift */; }; C314F9C9CB55D0AA25E4BB0A /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0F298FB19C2A149258FE7CC6 /* WidgetKit.framework */; }; C380A8DFA36E43D49400A87E /* SFSafeSymbols in Frameworks */ = {isa = PBXBuildFile; productRef = 1F9D3602163D4BEBB384DDEE /* SFSafeSymbols */; }; - 970584BAA2884C8287297BF6 /* SwitchItemEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAF58C7A2EF6E99500483AFD /* SwitchItemEntity.swift */; }; - 9F54768418A14674AD9E2FCA /* ContactState.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4042F06F5CA0082479C /* ContactState.swift */; }; - B2FC662F25B2461887BB67DA /* ItemEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4332F0705580082479C /* ItemEntity.swift */; }; - BF0EB6DE70934966B1DC5479 /* SetDateTimeValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF40A2F0800030082479C /* SetDateTimeValueIntent.swift */; }; C9DE79C32AF24FFEA0280115 /* ItemIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD5E85D2F02C3BC003215C0 /* ItemIdentifier.swift */; }; CE7A58C0ED3A4A1ABCB20337 /* SetLocationValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF40C2F0800040082479C /* SetLocationValueIntent.swift */; }; CF650A22059A4A00823EE656 /* SwitchAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4022F06F5950082479C /* SwitchAction.swift */; }; + DA03B5BC2FEC5EE80069A276 /* Home.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA448E772EF435B400F0893C /* Home.swift */; }; + DA03B5BD2FEC5EE80069A276 /* GetItemStateIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3512F0307A40082479C /* GetItemStateIntent.swift */; }; + DA03B5BE2FEC5EE80069A276 /* SetColorValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3542F03095D0082479C /* SetColorValueIntent.swift */; }; + DA03B5BF2FEC5EE80069A276 /* ContactStateIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3552F03095D0082479C /* ContactStateIntent.swift */; }; + DA03B5C02FEC5EE80069A276 /* SetDimmerRollerValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3582F03098B0082479C /* SetDimmerRollerValueIntent.swift */; }; + DA03B5C12FEC5EE80069A276 /* SetNumberValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF35A2F0309A60082479C /* SetNumberValueIntent.swift */; }; + DA03B5C22FEC5EE80069A276 /* SetStringValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF35C2F0309B10082479C /* SetStringValueIntent.swift */; }; + DA03B5C32FEC5EE80069A276 /* SetActiveHomeIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F77A157AAF1DD5AB45D6BA2 /* SetActiveHomeIntent.swift */; }; + DA03B5C42FEC5EE80069A276 /* SwitchAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4022F06F5950082479C /* SwitchAction.swift */; }; + DA03B5C52FEC5EE80069A276 /* ContactState.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4042F06F5CA0082479C /* ContactState.swift */; }; + DA03B5C62FEC5EE80069A276 /* PlayerAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4062F0800010082479C /* PlayerAction.swift */; }; + DA03B5C72FEC5EE80069A276 /* SetPlayerValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4082F0800020082479C /* SetPlayerValueIntent.swift */; }; + DA03B5C82FEC5EE80069A276 /* SetDateTimeValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF40A2F0800030082479C /* SetDateTimeValueIntent.swift */; }; + DA03B5C92FEC5EE80069A276 /* SetLocationValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF40C2F0800040082479C /* SetLocationValueIntent.swift */; }; + DA03B5CA2FEC5EE80069A276 /* ItemEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4332F0705580082479C /* ItemEntity.swift */; }; + DA03B5CB2FEC5EE80069A276 /* ItemEntityQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4352F07056F0082479C /* ItemEntityQuery.swift */; }; + DA03B5CC2FEC5EE80069A276 /* SetSwitchItemIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD5E85B2F02C272003215C0 /* SetSwitchItemIntent.swift */; }; + DA03B5CD2FEC5EE80069A276 /* ItemIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD5E85D2F02C3BC003215C0 /* ItemIdentifier.swift */; }; + DA03B5CE2FEC5EE80069A276 /* SwitchItemEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAF58C7A2EF6E99500483AFD /* SwitchItemEntity.swift */; }; + DA03B5CF2FEC66AA0069A276 /* OpenHABShortcuts.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA78C91F2FBBA8A700A92C6E /* OpenHABShortcuts.swift */; }; DA10161B2DC7BAE500552D14 /* SFSafeSymbols in Frameworks */ = {isa = PBXBuildFile; productRef = DA10161A2DC7BAE500552D14 /* SFSafeSymbols */; }; DA28C362225241DE00AB409C /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA28C361225241DE00AB409C /* WebKit.framework */; settings = {ATTRIBUTES = (Required, ); }; }; DA2C4FD52B4F573300D1C533 /* SDWebImageSVGCoder in Frameworks */ = {isa = PBXBuildFile; productRef = DA2C4FD42B4F573300D1C533 /* SDWebImageSVGCoder */; }; @@ -98,7 +108,6 @@ DFE10414197415F900D94943 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DFE10413197415F900D94943 /* Security.framework */; }; E120F4A14B9942449F25E788 /* SetDimmerRollerValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3582F03098B0082479C /* SetDimmerRollerValueIntent.swift */; }; EE6B566C271031A1EB5780D2 /* OpenHABCore in Frameworks */ = {isa = PBXBuildFile; productRef = 2247FFEA7206BFFC71AB02C4 /* OpenHABCore */; }; - E120F4A14B9942449F25E788 /* SetDimmerRollerValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3582F03098B0082479C /* SetDimmerRollerValueIntent.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -227,7 +236,6 @@ /* Begin PBXFileReference section */ 06A8490213945021806A13D1 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS18.0.sdk/System/Library/Frameworks/SwiftUI.framework; sourceTree = DEVELOPER_DIR; }; 0F298FB19C2A149258FE7CC6 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS18.0.sdk/System/Library/Frameworks/WidgetKit.framework; sourceTree = DEVELOPER_DIR; }; - 1224F78D228A89FC00750965 /* WatchMessageService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WatchMessageService.swift; sourceTree = ""; }; 16CFF4D57F6DF603FF1C1B9D /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS18.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; 1F77A157AAF1DD5AB45D6BA2 /* SetActiveHomeIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetActiveHomeIntent.swift; sourceTree = ""; }; 2F399AB92F5357E900F72A30 /* InputCommandFormatterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputCommandFormatterTests.swift; sourceTree = ""; }; @@ -351,6 +359,7 @@ isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( OpenHABAppShortcutsProvider.swift, + OpenHABShortcuts.swift, ); target = 6116C7DDD25EE245FE191A47 /* openHABWidgetExtension */; }; @@ -565,14 +574,6 @@ path = openHABWatch/External; sourceTree = SOURCE_ROOT; }; - 2F2150892F75C2FC001BA057 /* Watch */ = { - isa = PBXGroup; - children = ( - 1224F78D228A89FC00750965 /* WatchMessageService.swift */, - ); - name = Watch; - sourceTree = ""; - }; 6571444F2C1E438700C8A1F3 /* NotificationService */ = { isa = PBXGroup; children = ( @@ -679,7 +680,6 @@ isa = PBXGroup; children = ( DA4BF3652F0419F30082479C /* Intents */, - DA4BF3532F0307D30082479C /* iOS16 */, DAD5E85D2F02C3BC003215C0 /* ItemIdentifier.swift */, DA4BF4042F06F5CA0082479C /* ContactState.swift */, DAF58C7A2EF6E99500483AFD /* SwitchItemEntity.swift */, @@ -1393,25 +1393,26 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - DA448E852EF435B400F0893C /* Home.swift in Sources */, - DA4BF3522F0307A40082479C /* GetItemStateIntent.swift in Sources */, - DA4BF3562F03095D0082479C /* SetColorValueIntent.swift in Sources */, - DA4BF3572F03095D0082479C /* ContactStateIntent.swift in Sources */, - DA4BF3592F03098B0082479C /* SetDimmerRollerValueIntent.swift in Sources */, - DA4BF35B2F0309A60082479C /* SetNumberValueIntent.swift in Sources */, - DA4BF35D2F0309B10082479C /* SetStringValueIntent.swift in Sources */, - 772F724744CC8D15A673C246 /* SetActiveHomeIntent.swift in Sources */, - DA4BF4032F06F5950082479C /* SwitchAction.swift in Sources */, - DA4BF4052F06F5CA0082479C /* ContactState.swift in Sources */, - DA4BF4072F0800010082479C /* PlayerAction.swift in Sources */, - DA4BF4092F0800020082479C /* SetPlayerValueIntent.swift in Sources */, - DA4BF40B2F0800030082479C /* SetDateTimeValueIntent.swift in Sources */, - DA4BF40D2F0800040082479C /* SetLocationValueIntent.swift in Sources */, - DA4BF4342F0705580082479C /* ItemEntity.swift in Sources */, - DA4BF4362F07056F0082479C /* ItemEntityQuery.swift in Sources */, - DAD5E85C2F02C272003215C0 /* SetSwitchItemIntent.swift in Sources */, - DAD5E85E2F02C3C6003215C0 /* ItemIdentifier.swift in Sources */, - DAF58C7B2EF6E99500483AFD /* SwitchItemEntity.swift in Sources */, + DA03B5BC2FEC5EE80069A276 /* Home.swift in Sources */, + DA03B5BD2FEC5EE80069A276 /* GetItemStateIntent.swift in Sources */, + DA03B5BE2FEC5EE80069A276 /* SetColorValueIntent.swift in Sources */, + DA03B5BF2FEC5EE80069A276 /* ContactStateIntent.swift in Sources */, + DA03B5C02FEC5EE80069A276 /* SetDimmerRollerValueIntent.swift in Sources */, + DA03B5C12FEC5EE80069A276 /* SetNumberValueIntent.swift in Sources */, + DA03B5C22FEC5EE80069A276 /* SetStringValueIntent.swift in Sources */, + DA03B5C32FEC5EE80069A276 /* SetActiveHomeIntent.swift in Sources */, + DA03B5C42FEC5EE80069A276 /* SwitchAction.swift in Sources */, + DA03B5C52FEC5EE80069A276 /* ContactState.swift in Sources */, + DA03B5C62FEC5EE80069A276 /* PlayerAction.swift in Sources */, + DA03B5C72FEC5EE80069A276 /* SetPlayerValueIntent.swift in Sources */, + DA03B5C82FEC5EE80069A276 /* SetDateTimeValueIntent.swift in Sources */, + DA03B5C92FEC5EE80069A276 /* SetLocationValueIntent.swift in Sources */, + DA03B5CA2FEC5EE80069A276 /* ItemEntity.swift in Sources */, + DA03B5CB2FEC5EE80069A276 /* ItemEntityQuery.swift in Sources */, + DA03B5CC2FEC5EE80069A276 /* SetSwitchItemIntent.swift in Sources */, + DA03B5CD2FEC5EE80069A276 /* ItemIdentifier.swift in Sources */, + DA03B5CE2FEC5EE80069A276 /* SwitchItemEntity.swift in Sources */, + DA03B5CF2FEC66AA0069A276 /* OpenHABShortcuts.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1419,26 +1420,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - DA448E852EF435B400F0893C /* Home.swift in Sources */, - DA4BF3522F0307A40082479C /* GetItemStateIntent.swift in Sources */, - DA4BF3562F03095D0082479C /* SetColorValueIntent.swift in Sources */, - DA4BF3572F03095D0082479C /* ContactStateIntent.swift in Sources */, - DA4BF3592F03098B0082479C /* SetDimmerRollerValueIntent.swift in Sources */, - DA4BF35B2F0309A60082479C /* SetNumberValueIntent.swift in Sources */, - DA4BF35D2F0309B10082479C /* SetStringValueIntent.swift in Sources */, - 772F724744CC8D15A673C246 /* SetActiveHomeIntent.swift in Sources */, - DA4BF4032F06F5950082479C /* SwitchAction.swift in Sources */, - DA4BF4052F06F5CA0082479C /* ContactState.swift in Sources */, - DA78C9202FBBA8A700A92C6E /* OpenHABShortcuts.swift in Sources */, - DA4BF4072F0800010082479C /* PlayerAction.swift in Sources */, - DA4BF4092F0800020082479C /* SetPlayerValueIntent.swift in Sources */, - DA4BF40B2F0800030082479C /* SetDateTimeValueIntent.swift in Sources */, - DA4BF40D2F0800040082479C /* SetLocationValueIntent.swift in Sources */, - DA4BF4342F0705580082479C /* ItemEntity.swift in Sources */, - DA4BF4362F07056F0082479C /* ItemEntityQuery.swift in Sources */, - DAD5E85C2F02C272003215C0 /* SetSwitchItemIntent.swift in Sources */, - DAD5E85E2F02C3C6003215C0 /* ItemIdentifier.swift in Sources */, - DAF58C7B2EF6E99500483AFD /* SwitchItemEntity.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/openHAB/AppDelegate.swift b/openHAB/AppDelegate.swift index 0e1fb71a8..b47e81b3d 100644 --- a/openHAB/AppDelegate.swift +++ b/openHAB/AppDelegate.swift @@ -184,8 +184,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } } - - /// This is only informational - on success - DID Register func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { // TODO: remove before shipping diff --git a/openHAB/Models/SitemapPageViewModel.swift b/openHAB/Models/SitemapPageViewModel.swift index bd629e0d4..5b4263cb1 100644 --- a/openHAB/Models/SitemapPageViewModel.swift +++ b/openHAB/Models/SitemapPageViewModel.swift @@ -376,7 +376,6 @@ extension SitemapPageViewModel { pipelineStartedAt: pipelineStartedAt ) - defer { if activePageHandlingID == runID { pageHandlingTask = nil diff --git a/openHAB/NotificationCenterDelegateImpl.swift b/openHAB/NotificationCenterDelegateImpl.swift index b846fcbff..a81fcba93 100644 --- a/openHAB/NotificationCenterDelegateImpl.swift +++ b/openHAB/NotificationCenterDelegateImpl.swift @@ -43,7 +43,6 @@ struct OpenHABImageFetcher { } } - /// AVAudioPlayer must be created, used, and deallocated on the main thread. /// Using a plain `actor` placed it on a background executor, causing a crash when /// AVFoundation delivered finishedPlaying: on the main thread while the old player diff --git a/openHAB/Supporting Files/AppShortcuts.xcstrings b/openHAB/Supporting Files/AppShortcuts.xcstrings index 89617bc90..58b58030a 100644 --- a/openHAB/Supporting Files/AppShortcuts.xcstrings +++ b/openHAB/Supporting Files/AppShortcuts.xcstrings @@ -176,6 +176,7 @@ } }, "Set active home in ${applicationName}" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { diff --git a/openHAB/Supporting Files/Localizable.xcstrings b/openHAB/Supporting Files/Localizable.xcstrings index 2061dad28..44c8e1508 100644 --- a/openHAB/Supporting Files/Localizable.xcstrings +++ b/openHAB/Supporting Files/Localizable.xcstrings @@ -177,30 +177,6 @@ "state" : "translated", "value" : "%@" } - }, - "es": { - "stringUnit": { - "state": "translated", - "value": "%@" - } - }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "%@" - } - }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "%@" - } - }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "%@" - } } } }, @@ -313,30 +289,6 @@ "state" : "translated", "value" : "%@" } - }, - "es": { - "stringUnit": { - "state": "translated", - "value": "%@" - } - }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "%@" - } - }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "%@" - } - }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "%@" - } } } }, @@ -2529,30 +2481,6 @@ "state" : "translated", "value" : "Выбрать цвет" } - }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elegir color" - } - }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Valitse väri" - } - }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Velg farge" - } - }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Выбрать цвет" - } } } }, @@ -3191,30 +3119,6 @@ "state" : "translated", "value" : "Элемент цвета" } - }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento de color" - } - }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Värielementti" - } - }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Fargeelement" - } - }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент цвета" - } } } }, @@ -3269,30 +3173,6 @@ "state" : "translated", "value" : "Цветовой круг" } - }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Rueda de colores" - } - }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Väriympyrä" - } - }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Fargehjul" - } - }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Цветовой круг" - } } } }, @@ -3877,30 +3757,6 @@ "state" : "translated", "value" : "Элемент контакта" } - }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento de contacto" - } - }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kontaktielementti" - } - }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kontaktelement" - } - }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент контакта" - } } } }, @@ -4605,30 +4461,6 @@ "state" : "translated", "value" : "Элемент DateTime" } - }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento DateTime" - } - }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "DateTime-elementti" - } - }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "DateTime-element" - } - }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент DateTime" - } } } }, @@ -5278,30 +5110,6 @@ "state" : "translated", "value" : "Уровень затемнения: %@" } - }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento atenuador/persianas" - } - }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Himmennin-/rullaverhoelementti" - } - }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Dimmer-/rullegardinelement" - } - }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент диммера/рольставни" - } } } }, @@ -5601,30 +5409,6 @@ "state" : "translated", "value" : "Discovering openHAB" } - }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Descubriendo servidores openHAB…" - } - }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Etsitään openHAB-palvelimia…" - } - }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Finner openHAB-servere…" - } - }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Обнаружение серверов openHAB…" - } } } }, @@ -5741,30 +5525,6 @@ "state" : "translated", "value" : "Dismiss" } - }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Arrastrar para cambiar el tono y la saturación" - } - }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Vedä muuttaaksesi värisävyä ja kylläisyyttä" - } - }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Dra for å endre fargetone og metning" - } - }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Перетащите для изменения оттенка и насыщенности" - } } } }, @@ -5994,6 +5754,10 @@ } } }, + "Enable" : { + "comment" : "Display name for the \"Enable\" action.", + "isCommentAutoGenerated" : true + }, "Enable Dimming" : { "comment" : "A toggle that enables or disables automatic brightness dimming.", "localizations" : { @@ -6884,30 +6648,6 @@ "state" : "translated", "value" : "Foo" } - }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Obtener estado de ${itemEntity}" - } - }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Hae ${itemEntity}-tila" - } - }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Hent status for ${itemEntity}" - } - }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Получить состояние ${itemEntity}" - } } } }, @@ -8617,30 +8357,6 @@ "state" : "translated", "value" : "Загрузка карты сайта…" } - }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Se requiere acceso a la red local" - } - }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Lähiverkon käyttöoikeus vaaditaan" - } - }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Tilgang til lokalt nettverk kreves" - } - }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Требуется доступ к локальной сети" - } } } }, @@ -8867,30 +8583,6 @@ "state" : "translated", "value" : "Локальный сервер" } - }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento de ubicación" - } - }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Sijaintielementti" - } - }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Plasseringselement" - } - }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент местоположения" - } } } }, @@ -10417,30 +10109,6 @@ "state" : "translated", "value" : "Notification" } - }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento numérico" - } - }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Numeraalielementti" - } - }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Tallselement" - } - }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Числовой элемент" - } } } }, @@ -11198,30 +10866,6 @@ "state" : "translated", "value" : "Открыто" } - }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Abrir ajustes" - } - }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Avaa asetukset" - } - }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Åpne innstillinger" - } - }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Открыть настройки" - } } } }, @@ -11692,30 +11336,6 @@ "state" : "translated", "value" : "Действие плеера" } - }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento reproductor" - } - }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Soitinelementti" - } - }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Spillerelement" - } - }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент плеера" - } } } }, @@ -13376,30 +12996,6 @@ "state" : "translated", "value" : "Выберите дом для '%@'" } - }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Enviar ${action} a ${itemEntity}" - } - }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Lähetä ${action} kohteeseen ${itemEntity}" - } - }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Send ${action} til ${itemEntity}" - } - }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Отправить ${action} в ${itemEntity}" - } } } }, @@ -13983,30 +13579,6 @@ "state" : "translated", "value" : "Число %lf отправлено в %@" } - }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer ${itemEntity} en ${value}" - } - }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta ${itemEntity} arvoon ${value}" - } - }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sett ${itemEntity} til ${value}" - } - }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить ${itemEntity} в ${value}" - } } } }, @@ -14066,30 +13638,6 @@ "state" : "translated", "value" : "Строка %@ отправлена в %@" } - }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer ${itemEntity} en ${value} (HSB)" - } - }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta ${itemEntity} arvoon ${value} (HSB)" - } - }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sett ${itemEntity} til ${value} (HSB)" - } - }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить ${itemEntity} в ${value} (HSB)" - } } } }, @@ -14541,7 +14089,7 @@ } }, "Set Date & Time" : { - "comment" : "Short title for a shortcut that allows the user to set a date and time.", + "comment" : "Short title for the shortcut to set a date and time.", "isCommentAutoGenerated" : true }, "Set DateTime Control Value" : { @@ -14666,10 +14214,6 @@ } } }, - "Set Location" : { - "comment" : "Short title for the shortcut to set a location.", - "isCommentAutoGenerated" : true - }, "Set Location Control Value" : { "comment" : "Title of the Set Location Control Value app intent.", "localizations" : { @@ -14730,7 +14274,7 @@ } }, "Set Number" : { - "comment" : "Short title for the shortcut to set a number value.", + "comment" : "Text displayed in a shortcut for setting a number value.", "isCommentAutoGenerated" : true }, "Set Number Control Value" : { @@ -14911,7 +14455,7 @@ } }, "Set Switch" : { - "comment" : "Short title for the shortcut to set a switch.", + "comment" : "Short title for a shortcut that allows the user to set a switch.", "isCommentAutoGenerated" : true }, "Set Switch State" : { @@ -14974,7 +14518,7 @@ } }, "Set Text" : { - "comment" : "Short title for the shortcut to set a string value.", + "comment" : "Text displayed in a shortcut for setting a string value.", "isCommentAutoGenerated" : true }, "Set the color of a color control item" : { @@ -15092,30 +14636,6 @@ "state" : "translated", "value" : "Установить дату и время элемента DateTime" } - }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer el estado de ${itemEntity} en ${state}" - } - }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta ${itemEntity}-tila arvoon ${state}" - } - }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi status for ${itemEntity} til ${state}" - } - }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить состояние ${itemEntity} в ${state}" - } } } }, @@ -15934,30 +15454,6 @@ "state" : "translated", "value" : "Показать время" } - }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Registro de diagnóstico del mapa del sitio" - } - }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Sivukartan diagnostiikkaloki" - } - }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Diagnoslogging for nettstedskart" - } - }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Журналирование диагностики карты сайта" - } } } }, @@ -16800,30 +16296,6 @@ "state" : "translated", "value" : "SSL Certificate presented by %1$@ for %2$@ is invalid. Do you want to proceed?" } - }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento de texto" - } - }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Merkkijonoelementti" - } - }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Tekststrengselement" - } - }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Строковый элемент" - } } } }, @@ -17190,7 +16662,7 @@ } }, "Switch Home" : { - "comment" : "Short title for the shortcut to switch the active home.", + "comment" : "Shortcut title for switching between homes.", "isCommentAutoGenerated" : true }, "Switch Item" : { @@ -17483,30 +16955,6 @@ "state" : "translated", "value" : "Проверить соединение" } - }, - "es": { - "stringUnit": { - "state": "translated", - "value": "La URL no es válida. Compruebe el formato (p. ej., http://192.168.2.1:8080 o http://[::1]:8080 para IPv6)." - } - }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "URL on virheellinen. Tarkista muoto (esim. http://192.168.2.1:8080 tai http://[::1]:8080 IPv6:lle)." - } - }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "URL-en er ugyldig. Sjekk formatet (f.eks. http://192.168.2.1:8080 eller http://[::1]:8080 for IPv6)." - } - }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "URL недействителен. Проверьте формат (например, http://192.168.2.1:8080 или http://[::1]:8080 для IPv6)." - } } } }, @@ -17684,30 +17132,6 @@ "state" : "translated", "value" : "Состояние %@ равно %@" } - }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Para conectarse a su servidor openHAB local, permita el acceso a la red local cuando se le solicite. Si lo denegó anteriormente, actívelo en Ajustes → Privacidad y seguridad → Red local." - } - }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Muodosta yhteys paikalliseen openHAB-palvelimeen sallimalla lähiverkkoyhteys pyydettäessä. Jos olet aiemmin kieltänyt sen, ota se käyttöön kohdassa Asetukset → Tietosuoja ja turvallisuus → Lähiverkko." - } - }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "For å koble til din lokale openHAB-server, vennligst tillat tilgang til lokalt nettverk når du blir bedt om det. Hvis du tidligere nektet det, aktiver det i Innstillinger → Personvern og sikkerhet → Lokalt nettverk." - } - }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Для подключения к локальному серверу openHAB разрешите доступ к локальной сети при появлении запроса. Если вы ранее отказали, включите его в Настройки → Конфиденциальность и безопасность → Локальная сеть." - } } } }, @@ -18055,6 +17479,18 @@ } } }, + "Triggered" : { + "comment" : "Display name for the contact state \"OPEN\".", + "isCommentAutoGenerated" : true + }, + "Turn off" : { + "comment" : "Turn off action.", + "isCommentAutoGenerated" : true + }, + "Turn on" : { + "comment" : "Turn on action.", + "isCommentAutoGenerated" : true + }, "unable_to_add_certificate" : { "extractionState" : "manual", "localizations" : { From 7f0bc023279e4e0a25eabc70f418533f24dc7c5e Mon Sep 17 00:00:00 2001 From: Tim Bert <5411131+timbms@users.noreply.github.com> Date: Wed, 1 Jul 2026 19:39:32 +0200 Subject: [PATCH 31/91] fix: notification ping now respects the ringer/silent switch (#1278) AVAudioSession was globally set to .playback (needed for video widgets), which bypasses the silent switch. The foreground-notification ping inherited this category and played regardless of silent mode. Replace AVAudioPlayer with AudioServicesPlaySystemSound, which routes through the system sound path independently of AVAudioSession and natively honours the ringer switch. This also removes the fragile category save/restore dance that would have raced with concurrent video-widget audio. Signed-off-by: Tim Mueller-Seydlitz --- openHAB/NotificationCenterDelegateImpl.swift | 37 ++++++++++---------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/openHAB/NotificationCenterDelegateImpl.swift b/openHAB/NotificationCenterDelegateImpl.swift index a81fcba93..ace4df17f 100644 --- a/openHAB/NotificationCenterDelegateImpl.swift +++ b/openHAB/NotificationCenterDelegateImpl.swift @@ -9,7 +9,7 @@ // // SPDX-License-Identifier: EPL-2.0 -import AVFoundation +import AudioToolbox import Combine import Kingfisher import OpenHABCore @@ -43,32 +43,33 @@ struct OpenHABImageFetcher { } } -/// AVAudioPlayer must be created, used, and deallocated on the main thread. -/// Using a plain `actor` placed it on a background executor, causing a crash when -/// AVFoundation delivered finishedPlaying: on the main thread while the old player -/// was simultaneously being deallocated on the actor's background thread (mutex contention -/// in AVAudioPlayerCpp::DoAction / disposeQueue). @MainActor pins the entire lifecycle. +/// Plays the in-app notification ping using AudioServicesPlaySystemSound, which routes +/// through the system sound path and natively respects the ringer/silent switch without +/// touching the shared AVAudioSession. This avoids the .playback-vs-.ambient category +/// race that affected concurrent video-widget audio. @MainActor final class AudioPlayerActor { - private var player: AVAudioPlayer? + private var soundID: SystemSoundID = 0 - func playSound() { - guard let soundPath = Bundle.main.url(forResource: "ping", withExtension: "wav") else { - return + init() { + if let url = Bundle.main.url(forResource: "ping", withExtension: "wav") { + AudioServicesCreateSystemSoundID(url as CFURL, &soundID) } + } - do { - let newPlayer = try AVAudioPlayer(contentsOf: soundPath) - newPlayer.numberOfLoops = 0 - newPlayer.play() - player = newPlayer - } catch { - Logger.notificationCenterDelegateImpl.info("Failed to play sound \(error.localizedDescription)") + deinit { + if soundID != 0 { + AudioServicesDisposeSystemSoundID(soundID) } } + func playSound() { + guard soundID != 0 else { return } + AudioServicesPlaySystemSound(soundID) + } + func stopSound() { - player?.stop() + // System sounds cannot be stopped mid-play; the ping is short enough that this is fine. } } From 039506b27a9b259d25cfdb2ec2f7f3124a360088 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Wed, 1 Jul 2026 20:57:47 +0200 Subject: [PATCH 32/91] fix: lock screen widgets now send commands on tap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wrap the three accessory (lock screen) widget views — circular, rectangular, and inline — in Button(intent:) so tapping them executes the toggle intent instead of doing nothing. Signed-off-by: Tim Mueller-Seydlitz --- openHABWidget/SwitchWidgetEntryView.swift | 96 +++++++++++++++++------ 1 file changed, 72 insertions(+), 24 deletions(-) diff --git a/openHABWidget/SwitchWidgetEntryView.swift b/openHABWidget/SwitchWidgetEntryView.swift index aa7bc2402..e5ce2968f 100644 --- a/openHABWidget/SwitchWidgetEntryView.swift +++ b/openHABWidget/SwitchWidgetEntryView.swift @@ -360,14 +360,29 @@ struct SwitchAccessoryCircularView: View { ZStack { AccessoryWidgetBackground() if let slot = entry.slots.compactMap(\.self).first { - VStack(spacing: 2) { - Image(systemSymbol: .switch2) - .font(.caption) - Text(slot.item.state ?? "?") - .font(.caption2) - .fontWeight(.bold) - .lineLimit(1) - .minimumScaleFactor(0.5) + if let home = entry.home { + Button(intent: createToggleIntent(item: slot.item, homeUUID: slot.homeUUID, home: home)) { + VStack(spacing: 2) { + Image(systemSymbol: .switch2) + .font(.caption) + Text(slot.item.state ?? "?") + .font(.caption2) + .fontWeight(.bold) + .lineLimit(1) + .minimumScaleFactor(0.5) + } + } + .buttonStyle(.plain) + } else { + VStack(spacing: 2) { + Image(systemSymbol: .switch2) + .font(.caption) + Text(slot.item.state ?? "?") + .font(.caption2) + .fontWeight(.bold) + .lineLimit(1) + .minimumScaleFactor(0.5) + } } } else { Image(systemSymbol: .gear) @@ -383,20 +398,42 @@ struct SwitchAccessoryRectangularView: View { var body: some View { if let slot = entry.slots.compactMap(\.self).first { let label = slot.item.label.isEmpty ? slot.item.name : slot.item.label - VStack(alignment: .leading, spacing: 2) { - Text(label) - .font(.headline) - .lineLimit(1) - .minimumScaleFactor(0.7) - if let stateText = slot.item.state { - Text(stateText) - .font(.body) - .fontWeight(.semibold) + if let home = entry.home { + Button(intent: createToggleIntent(item: slot.item, homeUUID: slot.homeUUID, home: home)) { + VStack(alignment: .leading, spacing: 2) { + Text(label) + .font(.headline) + .lineLimit(1) + .minimumScaleFactor(0.7) + if let stateText = slot.item.state { + Text(stateText) + .font(.body) + .fontWeight(.semibold) + .lineLimit(1) + } else { + Text("No State") + .font(.caption) + .foregroundColor(.secondary) + } + } + } + .buttonStyle(.plain) + } else { + VStack(alignment: .leading, spacing: 2) { + Text(label) + .font(.headline) .lineLimit(1) - } else { - Text("No State") - .font(.caption) - .foregroundColor(.secondary) + .minimumScaleFactor(0.7) + if let stateText = slot.item.state { + Text(stateText) + .font(.body) + .fontWeight(.semibold) + .lineLimit(1) + } else { + Text("No State") + .font(.caption) + .foregroundColor(.secondary) + } } } } else { @@ -412,10 +449,21 @@ struct SwitchAccessoryInlineView: View { var body: some View { if let slot = entry.slots.compactMap(\.self).first { let label = slot.item.label.isEmpty ? slot.item.name : slot.item.label - if let stateText = slot.item.state { - Text("\(label): \(stateText)") + if let home = entry.home { + Button(intent: createToggleIntent(item: slot.item, homeUUID: slot.homeUUID, home: home)) { + if let stateText = slot.item.state { + Text("\(label): \(stateText)") + } else { + Text(label) + } + } + .buttonStyle(.plain) } else { - Text(label) + if let stateText = slot.item.state { + Text("\(label): \(stateText)") + } else { + Text(label) + } } } else { Text("Configure Widget") From 4c6a23314b355029629d8b07bbb16a58c885d7cd Mon Sep 17 00:00:00 2001 From: DigiH <17110652+DigiH@users.noreply.github.com> Date: Wed, 1 Jul 2026 22:08:09 +0200 Subject: [PATCH 33/91] Widget adjustments (#1279) Signed-off-by: DigiH <17110652+DigiH@users.noreply.github.com> --- openHABWidget/OpenHABIconOverlay.swift | 9 +------ openHABWidget/SensorWidgetEntryView.swift | 30 +++++++++++++---------- openHABWidget/SwitchWidgetEntryView.swift | 27 +++++++++++--------- 3 files changed, 34 insertions(+), 32 deletions(-) diff --git a/openHABWidget/OpenHABIconOverlay.swift b/openHABWidget/OpenHABIconOverlay.swift index b6cc6f32e..0adc670ac 100644 --- a/openHABWidget/OpenHABIconOverlay.swift +++ b/openHABWidget/OpenHABIconOverlay.swift @@ -16,18 +16,11 @@ import SwiftUI // MARK: - Shared Icon Overlay struct OpenHABIconOverlay: View { - let size: CGFloat - var leadingPadding: CGFloat = 14 - var topPadding: CGFloat = 14 - var body: some View { Image("openHABIcon") .resizable() .scaledToFit() - .frame(width: size, height: size) - .opacity(0.5) - .padding(.leading, leadingPadding) - .padding(.top, topPadding) + .opacity(0.1) } } diff --git a/openHABWidget/SensorWidgetEntryView.swift b/openHABWidget/SensorWidgetEntryView.swift index f61a64f51..6f4fc6059 100644 --- a/openHABWidget/SensorWidgetEntryView.swift +++ b/openHABWidget/SensorWidgetEntryView.swift @@ -182,9 +182,10 @@ private struct SensorItemRow: View { Text(label) .font(.subheadline) .fontWeight(.semibold) - .lineLimit(1) - .minimumScaleFactor(0.8) + .lineLimit(2) + .layoutPriority(1) .frame(maxWidth: .infinity, alignment: .leading) + .fixedSize(horizontal: false, vertical: true) Text(formattedState(for: item)) .font(.subheadline) .fontWeight(.semibold) @@ -202,16 +203,19 @@ struct SensorSmallWidgetView: View { var body: some View { ZStack(alignment: .topLeading) { + OpenHABIconOverlay() + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) + .padding(10) + if let slot = entry.slots.compactMap(\.self).first { let item = slot.item let label = item.label.isEmpty ? item.name : item.label VStack(spacing: 8) { Text(label) .font(.headline) - .lineLimit(1) + .lineLimit(3) .minimumScaleFactor(0.7) .padding(.top, 20) - Spacer() Text(formattedState(for: item)) @@ -230,8 +234,6 @@ struct SensorSmallWidgetView: View { .frame(maxWidth: .infinity, maxHeight: .infinity) .padding() } - - OpenHABIconOverlay(size: 16) } } } @@ -243,6 +245,10 @@ struct SensorMediumWidgetView: View { var body: some View { ZStack(alignment: .topLeading) { + OpenHABIconOverlay() + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) + .padding(10) + let filledSlots = entry.slots.compactMap(\.self) if filledSlots.isEmpty { UnconfiguredPlaceholder() @@ -253,7 +259,7 @@ struct SensorMediumWidgetView: View { ForEach(filledSlots.indices, id: \.self) { index in SensorItemRow(slot: filledSlots[index]) .padding(.horizontal) - .padding(.vertical, 10) + .padding(.vertical, 12) if index < filledSlots.count - 1 { Divider() .padding(.leading) @@ -261,10 +267,7 @@ struct SensorMediumWidgetView: View { } } .frame(maxHeight: .infinity) - .padding(.top, 22) } - - OpenHABIconOverlay(size: 18) } } } @@ -276,6 +279,10 @@ struct SensorLargeWidgetView: View { var body: some View { ZStack(alignment: .topLeading) { + OpenHABIconOverlay() + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) + .padding(14) + let filledSlots = entry.slots.compactMap(\.self) if filledSlots.isEmpty { VStack(alignment: .center, spacing: 16) { @@ -305,10 +312,7 @@ struct SensorLargeWidgetView: View { } } .frame(maxHeight: .infinity) - .padding(.top, 22) } - - OpenHABIconOverlay(size: 20) } } } diff --git a/openHABWidget/SwitchWidgetEntryView.swift b/openHABWidget/SwitchWidgetEntryView.swift index e5ce2968f..5011d3b07 100644 --- a/openHABWidget/SwitchWidgetEntryView.swift +++ b/openHABWidget/SwitchWidgetEntryView.swift @@ -194,9 +194,10 @@ private struct SwitchItemRow: View { Text(label) .font(.subheadline) .fontWeight(.semibold) - .lineLimit(1) - .minimumScaleFactor(0.8) + .lineLimit(2) + .layoutPriority(1) .frame(maxWidth: .infinity, alignment: .leading) + .fixedSize(horizontal: false, vertical: true) if let home { Toggle(isOn: isOn, intent: createToggleIntent(item: item, homeUUID: slot.homeUUID, home: home)) {} .toggleStyle(PowerButtonToggleStyle(diameter: 36, showStateLabel: false)) @@ -236,12 +237,16 @@ struct SwitchSmallWidgetView: View { var body: some View { ZStack(alignment: .topLeading) { + OpenHABIconOverlay() + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) + .padding(10) + if let slot = entry.slots.compactMap(\.self).first { let label = slot.item.label.isEmpty ? slot.item.name : slot.item.label VStack(spacing: 8) { Text(label) .font(.headline) - .lineLimit(1) + .lineLimit(3) .minimumScaleFactor(0.7) .padding(.top, 20) Spacer() @@ -268,8 +273,6 @@ struct SwitchSmallWidgetView: View { .frame(maxWidth: .infinity, maxHeight: .infinity) .padding() } - - OpenHABIconOverlay(size: 16) } } } @@ -281,6 +284,10 @@ struct SwitchMediumWidgetView: View { var body: some View { ZStack(alignment: .topLeading) { + OpenHABIconOverlay() + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) + .padding(10) + let filledSlots = Array(entry.slots.compactMap(\.self)) if filledSlots.isEmpty { UnconfiguredPlaceholder() @@ -299,10 +306,7 @@ struct SwitchMediumWidgetView: View { } } .frame(maxHeight: .infinity) - .padding(.top, 22) } - - OpenHABIconOverlay(size: 18) } } } @@ -314,6 +318,10 @@ struct SwitchLargeWidgetView: View { var body: some View { ZStack(alignment: .topLeading) { + OpenHABIconOverlay() + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) + .padding(14) + let filledSlots = Array(entry.slots.compactMap(\.self)) if filledSlots.isEmpty { VStack(alignment: .center, spacing: 16) { @@ -343,10 +351,7 @@ struct SwitchLargeWidgetView: View { } } .frame(maxHeight: .infinity) - .padding(.top, 22) } - - OpenHABIconOverlay(size: 20) } } } From cc44c2204984abea7e897f4ab1d3c620fb4cfa6d Mon Sep 17 00:00:00 2001 From: Tim Bert <5411131+timbms@users.noreply.github.com> Date: Wed, 1 Jul 2026 22:36:10 +0200 Subject: [PATCH 34/91] fix: animate GIFs in the Image sitemap widget (#1280) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit KFImage renders animated GIFs as a static first frame because it decodes data via OpenHABImageProcessor into a single UIImage. For URLs with a .gif extension, use KFAnimatedImage instead — it passes raw data to AnimatedImageView which handles frame playback. The existing KFImage + OpenHABImageProcessor path (needed for SVG support) is preserved for all other image types. Also broaden withOpenHABCredentials from KFImage to KFImageProtocol so it is available on KFAnimatedImage without duplication. Signed-off-by: Tim Mueller-Seydlitz --- .../Sources/CommonUI/KFImageExtension.swift | 4 +- openHAB/UI/SwiftUI/Rows/ImageRowView.swift | 89 +++++++++++++------ 2 files changed, 62 insertions(+), 31 deletions(-) diff --git a/CommonUI/Sources/CommonUI/KFImageExtension.swift b/CommonUI/Sources/CommonUI/KFImageExtension.swift index 8ca09d03b..730b4e3dc 100644 --- a/CommonUI/Sources/CommonUI/KFImageExtension.swift +++ b/CommonUI/Sources/CommonUI/KFImageExtension.swift @@ -12,10 +12,10 @@ import Kingfisher import OpenHABCore -public extension KFImage { +public extension KFImageProtocol { /// Applies the openHAB Basic Auth request modifier when a connection is available. /// When `connection` is nil no modifier is attached and the image loads unauthenticated. - func withOpenHABCredentials(for connection: ConnectionInfo?) -> KFImage { + func withOpenHABCredentials(for connection: ConnectionInfo?) -> Self { guard let connection else { return self } return requestModifier(OpenHABAccessTokenAdapter(connectionConfiguration: connection.configuration)) } diff --git a/openHAB/UI/SwiftUI/Rows/ImageRowView.swift b/openHAB/UI/SwiftUI/Rows/ImageRowView.swift index 5d7b44be5..31645f5d3 100644 --- a/openHAB/UI/SwiftUI/Rows/ImageRowView.swift +++ b/openHAB/UI/SwiftUI/Rows/ImageRowView.swift @@ -165,36 +165,67 @@ private struct ImageRowContent: View { .frame(maxHeight: 300) .clipShape(.rect(cornerRadius: 8)) case let .link(url): - KFImage(url) - .withOpenHABCredentials(for: networkTracker.activeConnection) - .setProcessor(OpenHABImageProcessor(svgMaxSize: nil)) - .placeholder { - if let state = lastRegularImageState, state.url == url { - Image(uiImage: state.image) - .resizable() - .aspectRatio(contentMode: .fit) - } else { - Color.gray.opacity(0.1) - .frame(height: 200) - .clipShape(.rect(cornerRadius: 8)) + if url?.pathExtension.lowercased() == "gif" { + KFAnimatedImage(url) + .withOpenHABCredentials(for: networkTracker.activeConnection) + .configure { $0.contentMode = .scaleAspectFit } + .placeholder { + if let state = lastRegularImageState, state.url == url { + Image(uiImage: state.image) + .resizable() + .aspectRatio(contentMode: .fit) + } else { + Color.gray.opacity(0.1) + .frame(height: 200) + .clipShape(.rect(cornerRadius: 8)) + } } - } - .onSuccess { result in - lastRegularImageState = RegularImageState(url: url, image: result.image) - } - .onFailure { error in - guard !error.isTaskCancelled else { return } - logger.warning("Image fetch failed for \(url?.absoluteString ?? "nil", privacy: .public): \(error.localizedDescription, privacy: .public)") - } - .fade(duration: 0) - .resizable() - .cacheMemoryOnly(!shouldCache) - .forceRefresh(shouldCache ? false : true) - .cacheOriginalImage(!shouldCache ? false : true) - .id(shouldCache ? url?.absoluteString : "\(url?.absoluteString ?? "")-\(forceRefreshKey)") - .aspectRatio(contentMode: .fit) - .frame(maxHeight: 300) - .clipShape(.rect(cornerRadius: 8)) + .onSuccess { result in + lastRegularImageState = RegularImageState(url: url, image: result.image) + } + .onFailure { error in + guard !error.isTaskCancelled else { return } + logger.warning("Image fetch failed for \(url?.absoluteString ?? "nil", privacy: .public): \(error.localizedDescription, privacy: .public)") + } + .fade(duration: 0) + .cacheMemoryOnly(!shouldCache) + .forceRefresh(shouldCache ? false : true) + .cacheOriginalImage(!shouldCache ? false : true) + .id(shouldCache ? url?.absoluteString : "\(url?.absoluteString ?? "")-\(forceRefreshKey)") + .frame(maxHeight: 300) + .clipShape(.rect(cornerRadius: 8)) + } else { + KFImage(url) + .withOpenHABCredentials(for: networkTracker.activeConnection) + .setProcessor(OpenHABImageProcessor(svgMaxSize: nil)) + .placeholder { + if let state = lastRegularImageState, state.url == url { + Image(uiImage: state.image) + .resizable() + .aspectRatio(contentMode: .fit) + } else { + Color.gray.opacity(0.1) + .frame(height: 200) + .clipShape(.rect(cornerRadius: 8)) + } + } + .onSuccess { result in + lastRegularImageState = RegularImageState(url: url, image: result.image) + } + .onFailure { error in + guard !error.isTaskCancelled else { return } + logger.warning("Image fetch failed for \(url?.absoluteString ?? "nil", privacy: .public): \(error.localizedDescription, privacy: .public)") + } + .fade(duration: 0) + .resizable() + .cacheMemoryOnly(!shouldCache) + .forceRefresh(shouldCache ? false : true) + .cacheOriginalImage(!shouldCache ? false : true) + .id(shouldCache ? url?.absoluteString : "\(url?.absoluteString ?? "")-\(forceRefreshKey)") + .aspectRatio(contentMode: .fit) + .frame(maxHeight: 300) + .clipShape(.rect(cornerRadius: 8)) + } case .empty: Rectangle() .fill(Color.gray.opacity(0.3)) From 6b4987212b3145b68e10d1d837021e62bad00bd0 Mon Sep 17 00:00:00 2001 From: openhab-bot Date: Wed, 1 Jul 2026 21:39:08 +0000 Subject: [PATCH 35/91] committed version bump: 3.4.2 (290) Signed-off-by: openhab-bot --- CHANGELOG.md | 35 +++++++++++++++++++++++++++++++++++ Version.xcconfig | 4 ++-- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d0f6d055..823085107 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,41 @@ ## [Unreleased] +- fix: animate GIFs in the Image sitemap widget (#1280) +- Widget adjustments (#1279) +- fix: lock screen widgets now send commands on tap +- fix: notification ping now respects the ringer/silent switch (#1278) +- fix: add OpenHABShortcuts.swift to app target to resolve AppShortcuts.xcstrings phrase warnings +- committed version bump: 3.4.1 (287) +- fix: add provisioning profile for openHABWidgetExtension +- l10n: add Spanish, Finnish, Norwegian, and Russian translations +- l10n: switch German and French translations to informal address (du/tu) +- chore: remove 5 stale localization keys on feature branch +- l10n: add Spanish, Finnish, Norwegian, and Russian translations +- l10n: translate 2 new widget keys into all 9 languages +- chore: update openHABWidgetExtension scheme with askForAppToLaunch +- fix: restore project.pbxproj corrupted by bad 3-way merge of pbxproj +- chore: add shared schemes for openHABWidgetExtension and OpenHABWatchComplicationsExtension +- build: raise macOS minimum to 15 for AsyncSequence.Failure support +- chore: bump openHABTestsSwift deployment target to iOS 18.0 +- Merge develop + fix build warnings; add iOS home screen widgets with item display +- Sensor widget: format state values using stateDescription numberPattern +- Sensor widget: support 1/2/4 items per small/medium/large size +- Make ScrollToTop overlay button conditional (#1231) +- Bump minimum deployment targets to iOS 18 / watchOS 11 +- Use iOS 18 onScrollGeometryChange in SitemapPageView +- Use iOS 17 animation and scroll APIs +- Replace SetLocationValueIntent with SetActiveHomeIntent in AppShortcutsProvider +- Register SetActiveHomeIntent with AppShortcutsProvider +- Linting and AppIntents SSE warnings fixes +- Bump deployment targets to iOS 17.0 / watchOS 10.0 +- Convert AppShortcuts.strings to AppShortcuts.xcstrings String Catalog +- Fix AppIntentsMetadataProcessor synonym warnings in AppEnum cases +- Add AppShortcutsProvider and fix intent dialog gaps +- Upgrade SwiftFormatPlugin 0.58.7→0.61.1, SwiftLintPlugin 0.62.2→0.63.3 (#1225) +- ItemEventStream: backport SSE watchdog and self-contained network monitor +- openHABWidget: add interactive switch and sensor widgets + - fix: add provisioning profile for openHABWidgetExtension - fix: replace UIScreen.main with scene-based screen access (iOS 27 crash) (#1269) - Bump minor version number to 3.3.1 diff --git a/Version.xcconfig b/Version.xcconfig index dd9301df6..cd7aeeb61 100644 --- a/Version.xcconfig +++ b/Version.xcconfig @@ -6,5 +6,5 @@ // https://developer.apple.com/documentation/xcode/adding-a-build-configuration-file-to-your-project -MARKETING_VERSION = 3.4.1 -CURRENT_PROJECT_VERSION = 287 +MARKETING_VERSION = 3.4.2 +CURRENT_PROJECT_VERSION = 290 From 8f8e65516e4508da6136900e561445e9ccfcb2a6 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Thu, 2 Jul 2026 14:37:27 +0200 Subject: [PATCH 36/91] fix: detect GIF images by magic bytes in ImageRowView Replace URL path extension check with content-based GIF detection. Proxy URLs (e.g. /proxy?sitemap=...&widgetId=...) carry no file extension, so the previous pathExtension == "gif" check never matched them. On first load, KFImage downloads the image and checks for GIF by: 1. imageFrameCount > 1 on the decoded UIImage (reliable on cache hits) 2. Magic bytes 0x47 0x49 0x46 ("GIF") as fallback for fresh downloads When detected, animatedGIFSourceURL is set, triggering a re-render with KFAnimatedImage. The placeholder reuses the already-decoded first frame so the transition is seamless. Embedded GIF payloads (Image item state) are now also handled: magic bytes are checked at view-construction time and routed to KFAnimatedImage directly, bypassing OpenHABImageProcessor which would strip animation frames. Signed-off-by: Tim Mueller-Seydlitz --- openHAB/UI/SwiftUI/Rows/ImageRowView.swift | 43 ++++++++++++++++++---- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/openHAB/UI/SwiftUI/Rows/ImageRowView.swift b/openHAB/UI/SwiftUI/Rows/ImageRowView.swift index 31645f5d3..3f9ef61fb 100644 --- a/openHAB/UI/SwiftUI/Rows/ImageRowView.swift +++ b/openHAB/UI/SwiftUI/Rows/ImageRowView.swift @@ -45,6 +45,7 @@ private struct ImageRowContent: View { @State private var lastChartImage: KFCrossPlatformImage? @State private var lastRegularImageState: RegularImageState? @State private var chartDisplayState: ChartDisplayState? + @State private var animatedGIFSourceURL: URL? private let logger = Logger(subsystem: "org.openhab", category: "ImageRowView") @@ -107,6 +108,9 @@ private struct ImageRowContent: View { .onChange(of: input.refresh) { setupRefreshTimer() } + .onChange(of: input.url) { + animatedGIFSourceURL = nil + } } @ViewBuilder @@ -157,15 +161,23 @@ private struct ImageRowContent: View { "\(input.widgetId)-\(forceRefreshKey)" } let provider = RawImageDataProvider(data: data, cacheKey: cacheKey) - KFImage(source: .provider(provider)) - .withOpenHABCredentials(for: networkTracker.activeConnection) - .setProcessor(OpenHABImageProcessor(svgMaxSize: nil)) - .resizable() - .aspectRatio(contentMode: .fit) - .frame(maxHeight: 300) - .clipShape(.rect(cornerRadius: 8)) + if isGIFMagic(data) { + KFAnimatedImage(source: .provider(provider)) + .withOpenHABCredentials(for: networkTracker.activeConnection) + .configure { $0.contentMode = .scaleAspectFit } + .frame(maxHeight: 300) + .clipShape(.rect(cornerRadius: 8)) + } else { + KFImage(source: .provider(provider)) + .withOpenHABCredentials(for: networkTracker.activeConnection) + .setProcessor(OpenHABImageProcessor(svgMaxSize: nil)) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(maxHeight: 300) + .clipShape(.rect(cornerRadius: 8)) + } case let .link(url): - if url?.pathExtension.lowercased() == "gif" { + if animatedGIFSourceURL == url { KFAnimatedImage(url) .withOpenHABCredentials(for: networkTracker.activeConnection) .configure { $0.contentMode = .scaleAspectFit } @@ -211,6 +223,10 @@ private struct ImageRowContent: View { } .onSuccess { result in lastRegularImageState = RegularImageState(url: url, image: result.image) + if isGIF(result) { + logger.debug("GIF detected for \(url?.absoluteString ?? "nil", privacy: .public)") + animatedGIFSourceURL = url + } } .onFailure { error in guard !error.isTaskCancelled else { return } @@ -238,6 +254,17 @@ private struct ImageRowContent: View { } } + private func isGIFMagic(_ data: Data) -> Bool { + data.count >= 3 && data[0] == 0x47 && data[1] == 0x49 && data[2] == 0x46 // "GIF" + } + + private func isGIF(_ result: RetrieveImageResult) -> Bool { + if let frameCount = result.image.kf.imageFrameCount, frameCount > 1 { + return true + } + return result.data().map(isGIFMagic) ?? false + } + private func setupRefreshTimer() { stopRefreshTimer() From 487df890f9c410eb27962cb653d179ea361be650 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Thu, 2 Jul 2026 14:38:57 +0200 Subject: [PATCH 37/91] chore: apply linter fixes Rename KFImageExtension.swift to KFImageProtocol.swift to match its contents, and reorder deinit after methods in AudioPlayerActor. Signed-off-by: Tim Mueller-Seydlitz --- ...{KFImageExtension.swift => KFImageProtocol.swift} | 0 openHAB/NotificationCenterDelegateImpl.swift | 12 ++++++------ 2 files changed, 6 insertions(+), 6 deletions(-) rename CommonUI/Sources/CommonUI/{KFImageExtension.swift => KFImageProtocol.swift} (100%) diff --git a/CommonUI/Sources/CommonUI/KFImageExtension.swift b/CommonUI/Sources/CommonUI/KFImageProtocol.swift similarity index 100% rename from CommonUI/Sources/CommonUI/KFImageExtension.swift rename to CommonUI/Sources/CommonUI/KFImageProtocol.swift diff --git a/openHAB/NotificationCenterDelegateImpl.swift b/openHAB/NotificationCenterDelegateImpl.swift index ace4df17f..3a654973d 100644 --- a/openHAB/NotificationCenterDelegateImpl.swift +++ b/openHAB/NotificationCenterDelegateImpl.swift @@ -57,12 +57,6 @@ final class AudioPlayerActor { } } - deinit { - if soundID != 0 { - AudioServicesDisposeSystemSoundID(soundID) - } - } - func playSound() { guard soundID != 0 else { return } AudioServicesPlaySystemSound(soundID) @@ -71,6 +65,12 @@ final class AudioPlayerActor { func stopSound() { // System sounds cannot be stopped mid-play; the ping is short enough that this is fine. } + + deinit { + if soundID != 0 { + AudioServicesDisposeSystemSoundID(soundID) + } + } } @MainActor From 31704968ffb7cec816c13c0a3babb4b1ab3881ad Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Thu, 2 Jul 2026 14:59:07 +0200 Subject: [PATCH 38/91] feat: make Dimmer items selectable in Set Switch State intent Dimmer items accept ON/OFF/TOGGLE commands just like Switch items. Add .dimmer to SwitchItemQuery.allowedTypes so they appear in the item picker, and update typeDisplayRepresentation accordingly. Also remove duplicate PBXBuildFile entries for AppIntents sources from the Xcode project that were added twice during target setup. Signed-off-by: Tim Mueller-Seydlitz --- AppIntents/SwitchItemEntity.swift | 4 ++-- openHAB.xcodeproj/project.pbxproj | 20 -------------------- 2 files changed, 2 insertions(+), 22 deletions(-) diff --git a/AppIntents/SwitchItemEntity.swift b/AppIntents/SwitchItemEntity.swift index 7e3a71da5..ffff9f561 100644 --- a/AppIntents/SwitchItemEntity.swift +++ b/AppIntents/SwitchItemEntity.swift @@ -309,7 +309,7 @@ struct SwitchItemEntity: ItemEntity { @IntentParameterDependency(\.$home) var intent - var allowedTypes: [OpenHABItem.ItemType] = [.switchItem] + var allowedTypes: [OpenHABItem.ItemType] = [.switchItem, .dimmer] var selectedHome: Home? { guard let intent else { return nil @@ -318,7 +318,7 @@ struct SwitchItemEntity: ItemEntity { } } - static let typeDisplayRepresentation = TypeDisplayRepresentation(name: "Switch Item") + static let typeDisplayRepresentation = TypeDisplayRepresentation(name: "Switch or Dimmer Item") static let defaultQuery = SwitchItemQuery() var id: ItemIdentifier diff --git a/openHAB.xcodeproj/project.pbxproj b/openHAB.xcodeproj/project.pbxproj index adcb96ce7..e30c0964b 100644 --- a/openHAB.xcodeproj/project.pbxproj +++ b/openHAB.xcodeproj/project.pbxproj @@ -27,7 +27,6 @@ 657144552C1E438700C8A1F3 /* NotificationService.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 6571444E2C1E438700C8A1F3 /* NotificationService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 657144962C30A16700C8A1F3 /* OpenHABCore in Frameworks */ = {isa = PBXBuildFile; productRef = 657144952C30A16700C8A1F3 /* OpenHABCore */; }; 73F094A3C17641738D6215CD /* SetSwitchItemIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD5E85B2F02C272003215C0 /* SetSwitchItemIntent.swift */; }; - 772F724744CC8D15A673C246 /* SetActiveHomeIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F77A157AAF1DD5AB45D6BA2 /* SetActiveHomeIntent.swift */; }; 791AFA47B71D4B6995369F3A /* GetItemStateIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3512F0307A40082479C /* GetItemStateIntent.swift */; }; 934E592528F16EBA00162004 /* OpenHABCore in Frameworks */ = {isa = PBXBuildFile; productRef = 934E592428F16EBA00162004 /* OpenHABCore */; }; 934E592728F16EBA00162004 /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 934E592628F16EBA00162004 /* Kingfisher */; }; @@ -73,34 +72,15 @@ DA10161B2DC7BAE500552D14 /* SFSafeSymbols in Frameworks */ = {isa = PBXBuildFile; productRef = DA10161A2DC7BAE500552D14 /* SFSafeSymbols */; }; DA28C362225241DE00AB409C /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA28C361225241DE00AB409C /* WebKit.framework */; settings = {ATTRIBUTES = (Required, ); }; }; DA2C4FD52B4F573300D1C533 /* SDWebImageSVGCoder in Frameworks */ = {isa = PBXBuildFile; productRef = DA2C4FD42B4F573300D1C533 /* SDWebImageSVGCoder */; }; - DA448E852EF435B400F0893C /* Home.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA448E772EF435B400F0893C /* Home.swift */; }; - DA4BF3522F0307A40082479C /* GetItemStateIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3512F0307A40082479C /* GetItemStateIntent.swift */; }; - DA4BF3562F03095D0082479C /* SetColorValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3542F03095D0082479C /* SetColorValueIntent.swift */; }; - DA4BF3572F03095D0082479C /* ContactStateIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3552F03095D0082479C /* ContactStateIntent.swift */; }; - DA4BF3592F03098B0082479C /* SetDimmerRollerValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3582F03098B0082479C /* SetDimmerRollerValueIntent.swift */; }; - DA4BF35B2F0309A60082479C /* SetNumberValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF35A2F0309A60082479C /* SetNumberValueIntent.swift */; }; - DA4BF35D2F0309B10082479C /* SetStringValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF35C2F0309B10082479C /* SetStringValueIntent.swift */; }; - DA4BF4032F06F5950082479C /* SwitchAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4022F06F5950082479C /* SwitchAction.swift */; }; - DA4BF4052F06F5CA0082479C /* ContactState.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4042F06F5CA0082479C /* ContactState.swift */; }; - DA4BF4072F0800010082479C /* PlayerAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4062F0800010082479C /* PlayerAction.swift */; }; - DA4BF4092F0800020082479C /* SetPlayerValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4082F0800020082479C /* SetPlayerValueIntent.swift */; }; - DA4BF40B2F0800030082479C /* SetDateTimeValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF40A2F0800030082479C /* SetDateTimeValueIntent.swift */; }; - DA4BF40D2F0800040082479C /* SetLocationValueIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF40C2F0800040082479C /* SetLocationValueIntent.swift */; }; - DA4BF4342F0705580082479C /* ItemEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4332F0705580082479C /* ItemEntity.swift */; }; - DA4BF4362F07056F0082479C /* ItemEntityQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF4352F07056F0082479C /* ItemEntityQuery.swift */; }; DA4D4DB5233F9ACB00B37E37 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = DA4D4DB4233F9ACB00B37E37 /* README.md */; }; - DA78C9202FBBA8A700A92C6E /* OpenHABShortcuts.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA78C91F2FBBA8A700A92C6E /* OpenHABShortcuts.swift */; }; DA817E7A234BF39B00C91824 /* CHANGELOG.md in Resources */ = {isa = PBXBuildFile; fileRef = DA817E79234BF39B00C91824 /* CHANGELOG.md */; }; DA9A7EFD2D668D5900824156 /* SFSafeSymbols in Frameworks */ = {isa = PBXBuildFile; productRef = DA9A7EFC2D668D5900824156 /* SFSafeSymbols */; }; DA9A7EFF2D66915900824156 /* SFSafeSymbols in Frameworks */ = {isa = PBXBuildFile; productRef = DA9A7EFE2D66915900824156 /* SFSafeSymbols */; }; DABB5E332D98972F009A4B8A /* SDWebImageSVGCoder in Frameworks */ = {isa = PBXBuildFile; productRef = DABB5E322D98972F009A4B8A /* SDWebImageSVGCoder */; }; DAC949FA2E219F0D007E67B7 /* CommonUI in Frameworks */ = {isa = PBXBuildFile; productRef = DAC949F92E219F0D007E67B7 /* CommonUI */; }; DAC949FC2E219F30007E67B7 /* CommonUI in Frameworks */ = {isa = PBXBuildFile; productRef = DAC949FB2E219F30007E67B7 /* CommonUI */; }; - DAD5E85C2F02C272003215C0 /* SetSwitchItemIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD5E85B2F02C272003215C0 /* SetSwitchItemIntent.swift */; }; - DAD5E85E2F02C3C6003215C0 /* ItemIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD5E85D2F02C3BC003215C0 /* ItemIdentifier.swift */; }; DAEFAA7F2F63536A00AC300B /* OpenHABCore in Frameworks */ = {isa = PBXBuildFile; productRef = DAEFAA7E2F63536A00AC300B /* OpenHABCore */; }; DAEFAA812F63537600AC300B /* SDWebImageSVGCoder in Frameworks */ = {isa = PBXBuildFile; productRef = DAEFAA802F63537600AC300B /* SDWebImageSVGCoder */; }; - DAF58C7B2EF6E99500483AFD /* SwitchItemEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAF58C7A2EF6E99500483AFD /* SwitchItemEntity.swift */; }; DAFF80982E4F47830084513E /* SDWebImage in Frameworks */ = {isa = PBXBuildFile; productRef = DAFF80972E4F47830084513E /* SDWebImage */; }; DFB2622B18830A3600D3244D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DFB2622A18830A3600D3244D /* Foundation.framework */; }; DFB2622D18830A3600D3244D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DFB2622C18830A3600D3244D /* CoreGraphics.framework */; }; From bd909d80206de6dcfb8c0e227d0d2dc1cea1a3c1 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Thu, 2 Jul 2026 15:01:53 +0200 Subject: [PATCH 39/91] fix: sync allowedItemTypes with query and add regression test Update SetSwitchItemIntent.allowedItemTypes to include .dimmer, keeping it consistent with SwitchItemQuery.allowedTypes (even though the property is currently unused). Add a unit test that pins SwitchItemQuery.allowedTypes to contain both .switchItem and .dimmer, making this regression harder to reintroduce silently. Signed-off-by: Tim Mueller-Seydlitz --- AppIntents/Intents/SetSwitchItemIntent.swift | 2 +- openHABTestsSwift/AppIntentsTests.swift | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/AppIntents/Intents/SetSwitchItemIntent.swift b/AppIntents/Intents/SetSwitchItemIntent.swift index 396806ced..5da297d26 100644 --- a/AppIntents/Intents/SetSwitchItemIntent.swift +++ b/AppIntents/Intents/SetSwitchItemIntent.swift @@ -32,7 +32,7 @@ struct SetSwitchItemIntent: AppIntent { } static var allowedItemTypes: [OpenHABItem.ItemType] { - [.switchItem] + [.switchItem, .dimmer] } static var parameterSummary: some ParameterSummary { diff --git a/openHABTestsSwift/AppIntentsTests.swift b/openHABTestsSwift/AppIntentsTests.swift index ef0e42db8..f212bf85e 100644 --- a/openHABTestsSwift/AppIntentsTests.swift +++ b/openHABTestsSwift/AppIntentsTests.swift @@ -184,6 +184,12 @@ struct SetLocationValueIntentTests { struct SetSwitchItemIntentTests { let homeId = UUID() + @Test func queryAllowedTypesIncludesSwitchAndDimmer() { + let query = SwitchItemEntity.SwitchItemQuery() + #expect(query.allowedTypes.contains(.switchItem)) + #expect(query.allowedTypes.contains(.dimmer)) + } + @Test func homeMismatchThrowsItemNotInHome() async { let wrongHome = Home(id: UUID().uuidString, displayString: "Wrong Home") let entity = SwitchItemEntity(makeItem(type: "Switch", label: "My Switch"), homeId: homeId) From 84fa0432526061a71ff4850b4c5f14f71b20bb2a Mon Sep 17 00:00:00 2001 From: openhab-bot Date: Thu, 2 Jul 2026 13:25:52 +0000 Subject: [PATCH 40/91] committed version bump: 3.4.3 (292) Signed-off-by: openhab-bot --- CHANGELOG.md | 38 ++++++++++++++++++++++++++++++++++++++ Version.xcconfig | 4 ++-- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b542f86b9..6445a4c37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,44 @@ ## [Unreleased] +- chore: apply linter fixes +- fix: detect GIF images by magic bytes in ImageRowView +- committed version bump: 3.4.2 (290) +- fix: animate GIFs in the Image sitemap widget (#1280) +- Widget adjustments (#1279) +- fix: lock screen widgets now send commands on tap +- fix: notification ping now respects the ringer/silent switch (#1278) +- fix: add OpenHABShortcuts.swift to app target to resolve AppShortcuts.xcstrings phrase warnings +- committed version bump: 3.4.1 (287) +- fix: add provisioning profile for openHABWidgetExtension +- l10n: add Spanish, Finnish, Norwegian, and Russian translations +- l10n: switch German and French translations to informal address (du/tu) +- chore: remove 5 stale localization keys on feature branch +- l10n: add Spanish, Finnish, Norwegian, and Russian translations +- l10n: translate 2 new widget keys into all 9 languages +- chore: update openHABWidgetExtension scheme with askForAppToLaunch +- fix: restore project.pbxproj corrupted by bad 3-way merge of pbxproj +- chore: add shared schemes for openHABWidgetExtension and OpenHABWatchComplicationsExtension +- build: raise macOS minimum to 15 for AsyncSequence.Failure support +- chore: bump openHABTestsSwift deployment target to iOS 18.0 +- Merge develop + fix build warnings; add iOS home screen widgets with item display +- Sensor widget: format state values using stateDescription numberPattern +- Sensor widget: support 1/2/4 items per small/medium/large size +- Make ScrollToTop overlay button conditional (#1231) +- Bump minimum deployment targets to iOS 18 / watchOS 11 +- Use iOS 18 onScrollGeometryChange in SitemapPageView +- Use iOS 17 animation and scroll APIs +- Replace SetLocationValueIntent with SetActiveHomeIntent in AppShortcutsProvider +- Register SetActiveHomeIntent with AppShortcutsProvider +- Linting and AppIntents SSE warnings fixes +- Bump deployment targets to iOS 17.0 / watchOS 10.0 +- Convert AppShortcuts.strings to AppShortcuts.xcstrings String Catalog +- Fix AppIntentsMetadataProcessor synonym warnings in AppEnum cases +- Add AppShortcutsProvider and fix intent dialog gaps +- Upgrade SwiftFormatPlugin 0.58.7→0.61.1, SwiftLintPlugin 0.62.2→0.63.3 (#1225) +- ItemEventStream: backport SSE watchdog and self-contained network monitor +- openHABWidget: add interactive switch and sensor widgets + - fix: animate GIFs in the Image sitemap widget (#1280) - Widget adjustments (#1279) - fix: lock screen widgets now send commands on tap diff --git a/Version.xcconfig b/Version.xcconfig index cd7aeeb61..15b35a4d0 100644 --- a/Version.xcconfig +++ b/Version.xcconfig @@ -6,5 +6,5 @@ // https://developer.apple.com/documentation/xcode/adding-a-build-configuration-file-to-your-project -MARKETING_VERSION = 3.4.2 -CURRENT_PROJECT_VERSION = 290 +MARKETING_VERSION = 3.4.3 +CURRENT_PROJECT_VERSION = 292 From 68c17d7cefc5db6d2eac8abf46e35831c23f418c Mon Sep 17 00:00:00 2001 From: openhab-bot Date: Thu, 2 Jul 2026 14:20:11 +0000 Subject: [PATCH 41/91] committed version bump: 3.4.4 (293) Signed-off-by: openhab-bot --- CHANGELOG.md | 41 +++++++++++++++++++++++++++++++++++++++++ Version.xcconfig | 4 ++-- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6445a4c37..f5a598d83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,47 @@ ## [Unreleased] +- committed version bump: 3.4.3 (292) +- fix: sync allowedItemTypes with query and add regression test +- feat: make Dimmer items selectable in Set Switch State intent +- chore: apply linter fixes +- fix: detect GIF images by magic bytes in ImageRowView +- committed version bump: 3.4.2 (290) +- fix: animate GIFs in the Image sitemap widget (#1280) +- Widget adjustments (#1279) +- fix: lock screen widgets now send commands on tap +- fix: notification ping now respects the ringer/silent switch (#1278) +- fix: add OpenHABShortcuts.swift to app target to resolve AppShortcuts.xcstrings phrase warnings +- committed version bump: 3.4.1 (287) +- fix: add provisioning profile for openHABWidgetExtension +- l10n: add Spanish, Finnish, Norwegian, and Russian translations +- l10n: switch German and French translations to informal address (du/tu) +- chore: remove 5 stale localization keys on feature branch +- l10n: add Spanish, Finnish, Norwegian, and Russian translations +- l10n: translate 2 new widget keys into all 9 languages +- chore: update openHABWidgetExtension scheme with askForAppToLaunch +- fix: restore project.pbxproj corrupted by bad 3-way merge of pbxproj +- chore: add shared schemes for openHABWidgetExtension and OpenHABWatchComplicationsExtension +- build: raise macOS minimum to 15 for AsyncSequence.Failure support +- chore: bump openHABTestsSwift deployment target to iOS 18.0 +- Merge develop + fix build warnings; add iOS home screen widgets with item display +- Sensor widget: format state values using stateDescription numberPattern +- Sensor widget: support 1/2/4 items per small/medium/large size +- Make ScrollToTop overlay button conditional (#1231) +- Bump minimum deployment targets to iOS 18 / watchOS 11 +- Use iOS 18 onScrollGeometryChange in SitemapPageView +- Use iOS 17 animation and scroll APIs +- Replace SetLocationValueIntent with SetActiveHomeIntent in AppShortcutsProvider +- Register SetActiveHomeIntent with AppShortcutsProvider +- Linting and AppIntents SSE warnings fixes +- Bump deployment targets to iOS 17.0 / watchOS 10.0 +- Convert AppShortcuts.strings to AppShortcuts.xcstrings String Catalog +- Fix AppIntentsMetadataProcessor synonym warnings in AppEnum cases +- Add AppShortcutsProvider and fix intent dialog gaps +- Upgrade SwiftFormatPlugin 0.58.7→0.61.1, SwiftLintPlugin 0.62.2→0.63.3 (#1225) +- ItemEventStream: backport SSE watchdog and self-contained network monitor +- openHABWidget: add interactive switch and sensor widgets + - chore: apply linter fixes - fix: detect GIF images by magic bytes in ImageRowView - committed version bump: 3.4.2 (290) diff --git a/Version.xcconfig b/Version.xcconfig index 15b35a4d0..eb431e6b6 100644 --- a/Version.xcconfig +++ b/Version.xcconfig @@ -6,5 +6,5 @@ // https://developer.apple.com/documentation/xcode/adding-a-build-configuration-file-to-your-project -MARKETING_VERSION = 3.4.3 -CURRENT_PROJECT_VERSION = 292 +MARKETING_VERSION = 3.4.4 +CURRENT_PROJECT_VERSION = 293 From 99d941d0ab48d9bc90bf758f5c076c0a21948161 Mon Sep 17 00:00:00 2001 From: DigiH <17110652+DigiH@users.noreply.github.com> Date: Thu, 2 Jul 2026 15:55:16 +0200 Subject: [PATCH 42/91] Fix small widget content to be cntered Signed-off-by: DigiH <17110652+DigiH@users.noreply.github.com> --- openHABWidget/SensorWidgetEntryView.swift | 2 +- openHABWidget/SwitchWidgetEntryView.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openHABWidget/SensorWidgetEntryView.swift b/openHABWidget/SensorWidgetEntryView.swift index 6f4fc6059..054e7c9dd 100644 --- a/openHABWidget/SensorWidgetEntryView.swift +++ b/openHABWidget/SensorWidgetEntryView.swift @@ -202,7 +202,7 @@ struct SensorSmallWidgetView: View { let entry: SensorEntry var body: some View { - ZStack(alignment: .topLeading) { + ZStack(alignment: .center) { OpenHABIconOverlay() .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) .padding(10) diff --git a/openHABWidget/SwitchWidgetEntryView.swift b/openHABWidget/SwitchWidgetEntryView.swift index 5011d3b07..515f0dbad 100644 --- a/openHABWidget/SwitchWidgetEntryView.swift +++ b/openHABWidget/SwitchWidgetEntryView.swift @@ -236,7 +236,7 @@ struct SwitchSmallWidgetView: View { let entry: SwitchEntry var body: some View { - ZStack(alignment: .topLeading) { + ZStack(alignment: .center) { OpenHABIconOverlay() .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) .padding(10) From 9ee5afa52d4cb45c6cc88fee7128e31f8a0b0941 Mon Sep 17 00:00:00 2001 From: openhab-bot Date: Thu, 2 Jul 2026 19:11:46 +0000 Subject: [PATCH 43/91] committed version bump: 3.4.5 (294) Signed-off-by: openhab-bot --- CHANGELOG.md | 43 +++++++++++++++++++++++++++++++++++++++++++ Version.xcconfig | 4 ++-- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5a598d83..0dafaefe2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,49 @@ ## [Unreleased] +- Fix small widget content to be cntered +- committed version bump: 3.4.4 (293) +- committed version bump: 3.4.3 (292) +- fix: sync allowedItemTypes with query and add regression test +- feat: make Dimmer items selectable in Set Switch State intent +- chore: apply linter fixes +- fix: detect GIF images by magic bytes in ImageRowView +- committed version bump: 3.4.2 (290) +- fix: animate GIFs in the Image sitemap widget (#1280) +- Widget adjustments (#1279) +- fix: lock screen widgets now send commands on tap +- fix: notification ping now respects the ringer/silent switch (#1278) +- fix: add OpenHABShortcuts.swift to app target to resolve AppShortcuts.xcstrings phrase warnings +- committed version bump: 3.4.1 (287) +- fix: add provisioning profile for openHABWidgetExtension +- l10n: add Spanish, Finnish, Norwegian, and Russian translations +- l10n: switch German and French translations to informal address (du/tu) +- chore: remove 5 stale localization keys on feature branch +- l10n: add Spanish, Finnish, Norwegian, and Russian translations +- l10n: translate 2 new widget keys into all 9 languages +- chore: update openHABWidgetExtension scheme with askForAppToLaunch +- fix: restore project.pbxproj corrupted by bad 3-way merge of pbxproj +- chore: add shared schemes for openHABWidgetExtension and OpenHABWatchComplicationsExtension +- build: raise macOS minimum to 15 for AsyncSequence.Failure support +- chore: bump openHABTestsSwift deployment target to iOS 18.0 +- Merge develop + fix build warnings; add iOS home screen widgets with item display +- Sensor widget: format state values using stateDescription numberPattern +- Sensor widget: support 1/2/4 items per small/medium/large size +- Make ScrollToTop overlay button conditional (#1231) +- Bump minimum deployment targets to iOS 18 / watchOS 11 +- Use iOS 18 onScrollGeometryChange in SitemapPageView +- Use iOS 17 animation and scroll APIs +- Replace SetLocationValueIntent with SetActiveHomeIntent in AppShortcutsProvider +- Register SetActiveHomeIntent with AppShortcutsProvider +- Linting and AppIntents SSE warnings fixes +- Bump deployment targets to iOS 17.0 / watchOS 10.0 +- Convert AppShortcuts.strings to AppShortcuts.xcstrings String Catalog +- Fix AppIntentsMetadataProcessor synonym warnings in AppEnum cases +- Add AppShortcutsProvider and fix intent dialog gaps +- Upgrade SwiftFormatPlugin 0.58.7→0.61.1, SwiftLintPlugin 0.62.2→0.63.3 (#1225) +- ItemEventStream: backport SSE watchdog and self-contained network monitor +- openHABWidget: add interactive switch and sensor widgets + - committed version bump: 3.4.3 (292) - fix: sync allowedItemTypes with query and add regression test - feat: make Dimmer items selectable in Set Switch State intent diff --git a/Version.xcconfig b/Version.xcconfig index eb431e6b6..983b209ce 100644 --- a/Version.xcconfig +++ b/Version.xcconfig @@ -6,5 +6,5 @@ // https://developer.apple.com/documentation/xcode/adding-a-build-configuration-file-to-your-project -MARKETING_VERSION = 3.4.4 -CURRENT_PROJECT_VERSION = 293 +MARKETING_VERSION = 3.4.5 +CURRENT_PROJECT_VERSION = 294 From 1c93a192d88a011e2cd7f6a31b0d024f8b8b3840 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Fri, 3 Jul 2026 08:29:46 +0200 Subject: [PATCH 44/91] refactor: remove UIImageView polling timer from MJPEG video stream Replace the dummy UIImageView + 30fps Timer polling pattern in VideoRowView with a direct @MainActor onFrame callback, eliminating the race condition that caused the __NSThreadPerformPerform crash when the UIImageView was deallocated while a frame was in flight. - SimpleMJPEGPlayer: drop imageView property, expose onFrame/onFirstFrame/onError callbacks and updateCallbacks() mutator - VideoStreamManager: accept onFrame closure instead of UIImageView - VideoRowView: pass closures directly; remove imageObservationTimer state and startImageObservation() helper Signed-off-by: Tim Mueller-Seydlitz --- openHAB/UI/SimpleMJPEGPlayer.swift | 34 ++++++-------- openHAB/UI/SwiftUI/Rows/VideoRowView.swift | 52 ++++----------------- openHAB/UI/Widgets/VideoStreamManager.swift | 16 ++++--- 3 files changed, 31 insertions(+), 71 deletions(-) diff --git a/openHAB/UI/SimpleMJPEGPlayer.swift b/openHAB/UI/SimpleMJPEGPlayer.swift index 4f058019d..92b12ef71 100644 --- a/openHAB/UI/SimpleMJPEGPlayer.swift +++ b/openHAB/UI/SimpleMJPEGPlayer.swift @@ -19,25 +19,11 @@ final class SimpleMJPEGPlayer { private var streamTask: URLSessionDataTask? private var httpClient: HTTPClient? private var delegate: SimpleMJPEGStreamDelegate? - private var imageView: UIImageView + var onFrame: (@MainActor (UIImage) -> Void)? var onFirstFrame: ((CGFloat) -> Void)? var onError: ((any Error) -> Void)? - init(imageView: UIImageView) { - self.imageView = imageView - } - - func updateImageView(_ newImageView: UIImageView, onFirstFrame: ((CGFloat) -> Void)? = nil, onError: ((any Error) -> Void)? = nil) { - imageView = newImageView - if let onFirstFrame { - self.onFirstFrame = onFirstFrame - } - if let onError { - self.onError = onError - } - } - func play(url: URL) { stop() @@ -50,11 +36,9 @@ final class SimpleMJPEGPlayer { connectionConfiguration: config, onFrame: { [weak self] image, isFirst in guard let self else { return } - imageView.image = image - + onFrame?(image) if isFirst { - let aspectRatio = image.size.width / image.size.height - onFirstFrame?(aspectRatio) + onFirstFrame?(image.size.width / image.size.height) } }, onError: { [weak self] error in @@ -77,7 +61,15 @@ final class SimpleMJPEGPlayer { streamTask = nil httpClient = nil delegate = nil - // Don't clear the image view when stopping - this allows sharing between cells - // The VideoStreamManager will handle proper cleanup + } + + func updateCallbacks( + onFrame: (@MainActor (UIImage) -> Void)?, + onFirstFrame: ((CGFloat) -> Void)?, + onError: ((any Error) -> Void)? + ) { + if let onFrame { self.onFrame = onFrame } + if let onFirstFrame { self.onFirstFrame = onFirstFrame } + if let onError { self.onError = onError } } } diff --git a/openHAB/UI/SwiftUI/Rows/VideoRowView.swift b/openHAB/UI/SwiftUI/Rows/VideoRowView.swift index 08c4a4ebd..78e152fe4 100644 --- a/openHAB/UI/SwiftUI/Rows/VideoRowView.swift +++ b/openHAB/UI/SwiftUI/Rows/VideoRowView.swift @@ -38,7 +38,6 @@ private struct VideoRowContent: View { @State private var aspectRatio: CGFloat = 16.0 / 9.0 @State private var isLoading = false @State private var currentStreamUrl: URL? - @State private var imageObservationTimer: Timer? @State private var playerObserver: NSKeyValueObservation? private let logger = Logger(subsystem: "org.openhab", category: "VideoRowView") @@ -65,7 +64,6 @@ private struct VideoRowContent: View { if let videoURL { ZStack { if isMJPEG { - // MJPEG display using UIImageView if let mjpegImage { Image(uiImage: mjpegImage) .resizable() @@ -151,49 +149,21 @@ private struct VideoRowContent: View { @MainActor private func setupMJPEG(url: URL) { - // Create a dummy UIImageView for the SimpleMJPEGPlayer - let imageView = UIImageView() - imageView.contentMode = .scaleAspectFit - mjpegPlayer = VideoStreamManager.shared.getOrCreateStream( for: url, - imageView: imageView, + onFrame: { image in + mjpegImage = image + if isLoading { isLoading = false } + }, onFirstFrame: { newAspectRatio in - Task { @MainActor in - aspectRatio = newAspectRatio - isLoading = false - } + aspectRatio = newAspectRatio + isLoading = false }, - onError: { error in - Task { @MainActor in - logger.debug("MJPEG stream error: \(error.localizedDescription)") - isLoading = false - } + onError: { [logger] error in + logger.debug("MJPEG stream error: \(error.localizedDescription)") + isLoading = false } ) - - // Observe image changes on the UIImageView - startImageObservation(imageView: imageView) - } - - @MainActor - private func startImageObservation(imageView: UIImageView) { - imageObservationTimer = Timer.scheduledTimer(withTimeInterval: 1.0 / 30.0, repeats: true) { _ in - DispatchQueue.main.async { - if mjpegPlayer == nil { - imageObservationTimer?.invalidate() - imageObservationTimer = nil - return - } - - if let image = imageView.image { - mjpegImage = image - if isLoading { - isLoading = false - } - } - } - } } private func setupHLS(url: URL) { @@ -223,10 +193,6 @@ private struct VideoRowContent: View { } private func cleanup() { - // Clean up timer - imageObservationTimer?.invalidate() - imageObservationTimer = nil - // Clean up HLS observer playerObserver = nil diff --git a/openHAB/UI/Widgets/VideoStreamManager.swift b/openHAB/UI/Widgets/VideoStreamManager.swift index 9453b7b8b..f243cd6e0 100644 --- a/openHAB/UI/Widgets/VideoStreamManager.swift +++ b/openHAB/UI/Widgets/VideoStreamManager.swift @@ -23,27 +23,30 @@ final class VideoStreamManager { private init() {} - func getOrCreateStream(for url: URL, imageView: UIImageView, onFirstFrame: ((CGFloat) -> Void)?, onError: ((any Error) -> Void)?) -> SimpleMJPEGPlayer { + func getOrCreateStream( + for url: URL, + onFrame: @escaping @MainActor (UIImage) -> Void, + onFirstFrame: ((CGFloat) -> Void)?, + onError: ((any Error) -> Void)? + ) -> SimpleMJPEGPlayer { let key = url.absoluteString if let existingPlayer = activeStreams[key] { - // Update the image view target for existing stream - existingPlayer.updateImageView(imageView, onFirstFrame: onFirstFrame, onError: onError) + existingPlayer.updateCallbacks(onFrame: onFrame, onFirstFrame: onFirstFrame, onError: onError) streamReferenceCounts[key] = (streamReferenceCounts[key] ?? 0) + 1 // swiftformat:disable:next redundantSelf Logger.widgets.debug("Reusing existing stream for URL: \(key), refs: \(self.streamReferenceCounts[key] ?? 0)") return existingPlayer } - // Create new stream - let player = SimpleMJPEGPlayer(imageView: imageView) + let player = SimpleMJPEGPlayer() + player.onFrame = onFrame player.onFirstFrame = onFirstFrame player.onError = onError activeStreams[key] = player streamReferenceCounts[key] = 1 - // Start playing the stream player.play(url: url) Logger.widgets.debug("Created new stream for URL: \(key)") @@ -64,7 +67,6 @@ final class VideoStreamManager { Logger.widgets.debug("Released stream reference for URL: \(key), remaining refs: \(newCount)") if newCount <= 0 { - // No more references, clean up the stream if let player = activeStreams[key] { player.stop() Logger.widgets.debug("Stopped and removed stream for URL: \(key)") From fb5de8020cb4eba22b48ba2561cd2dcc4a7cd7bd Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Fri, 3 Jul 2026 15:46:07 +0200 Subject: [PATCH 45/91] feat: wide-display sitemap grid and full-width media rows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On iPad, image, video, chart, webview, and map widgets now span the full row width instead of sharing a half-width grid cell (matching BasicUI). Layout: - SitemapGridLayout: image, webview, mapview classified as full-width grid cells alongside chart and video - EmbeddingRowInputView: media rows use standard regularInsets (16pt leading/trailing) — no more zero-inset edge-to-edge bleed Image rows: - KFImage (static images, charts): frame(maxWidth: .infinity) so landscape images fill the available width on iPad - KFAnimatedImage (GIFs): AnimatedGIFFrameModifier computes an exact frame from the GIF's natural aspect ratio using onGeometryChange (no GeometryReader); contentMargins(.horizontal, 0) zeroes any scroll-imposed margins before the width is observed. Link GIFs reuse the size already captured by the prior KFImage load; embedded GIFs capture it via onSuccess. Video rows: - Removed fixed frame(height: 200); aspect ratio now drives height from available width via aspectRatio(contentMode: .fit) + frame(maxWidth: .infinity) Signed-off-by: Tim Mueller-Seydlitz --- openHAB/Models/SitemapPageViewModel.swift | 7 + .../Supporting Files/Localizable.xcstrings | 5 + .../UI/SwiftUI/EmbeddingRowInputView.swift | 3 +- openHAB/UI/SwiftUI/Rows/ImageRowView.swift | 47 ++- openHAB/UI/SwiftUI/Rows/VideoRowView.swift | 7 +- openHAB/UI/SwiftUI/SitemapGridLayout.swift | 277 ++++++++++++++++++ openHAB/UI/SwiftUI/SitemapPageView.swift | 131 ++++++++- .../SwiftUI/SitemapPresentationPolicy.swift | 66 +++++ openHABTestsSwift/RowLayoutPolicyTests.swift | 68 +++++ 9 files changed, 595 insertions(+), 16 deletions(-) create mode 100644 openHAB/UI/SwiftUI/SitemapGridLayout.swift create mode 100644 openHAB/UI/SwiftUI/SitemapPresentationPolicy.swift diff --git a/openHAB/Models/SitemapPageViewModel.swift b/openHAB/Models/SitemapPageViewModel.swift index 5b4263cb1..425c3e88b 100644 --- a/openHAB/Models/SitemapPageViewModel.swift +++ b/openHAB/Models/SitemapPageViewModel.swift @@ -36,6 +36,7 @@ class SitemapPageViewModel: ObservableObject { @Published private(set) var widgetUpdateVersions: [String: Int] = [:] @Published private(set) var rowInputs: [SitemapRowInput] = [] @Published var navigationPath: [LinkedPageNavigation] = [] + private(set) var rowInputLayoutVersion = 0 let networkTracker = MainActorNetworkTracker.shared private var openAPIService: OpenAPIService? @@ -686,10 +687,16 @@ extension SitemapPageViewModel { index[rowID] = widget } + let rowInputLayoutChanged = SitemapRowLayoutIdentity.makeIdentities(from: result.inputs) != + SitemapRowLayoutIdentity.makeIdentities(from: rowInputs) + rowWidgetIndex = index previousBuildRenderKeys = result.renderKeys previousBuildRowIDs = result.rowIDs if result.inputs != rowInputs { + if rowInputLayoutChanged { + rowInputLayoutVersion += 1 + } rowInputs = result.inputs } } diff --git a/openHAB/Supporting Files/Localizable.xcstrings b/openHAB/Supporting Files/Localizable.xcstrings index 44c8e1508..a31b6c001 100644 --- a/openHAB/Supporting Files/Localizable.xcstrings +++ b/openHAB/Supporting Files/Localizable.xcstrings @@ -16667,6 +16667,7 @@ }, "Switch Item" : { "comment" : "Display name for a switch item type.", + "extractionState" : "stale", "isCommentAutoGenerated" : true, "localizations" : { "de" : { @@ -16727,6 +16728,10 @@ "comment" : "Switch action to turn a device on.", "isCommentAutoGenerated" : true }, + "Switch or Dimmer Item" : { + "comment" : "Display representation of the type of an item that can be turned on or off.", + "isCommentAutoGenerated" : true + }, "Switch the active home in the openHAB app" : { "comment" : "Description of the intent to set the active home.", "isCommentAutoGenerated" : true, diff --git a/openHAB/UI/SwiftUI/EmbeddingRowInputView.swift b/openHAB/UI/SwiftUI/EmbeddingRowInputView.swift index cc5cbf128..b586477f2 100644 --- a/openHAB/UI/SwiftUI/EmbeddingRowInputView.swift +++ b/openHAB/UI/SwiftUI/EmbeddingRowInputView.swift @@ -33,6 +33,8 @@ enum RowLayoutPolicy { return frameInsets(hasLabel: !input.displayState.labelText.isEmpty) } return regularInsets + case .media: + return regularInsets case .text, .slider, .selection, @@ -42,7 +44,6 @@ enum RowLayoutPolicy { .toggle, .input, .colorPicker, - .media, .colorTemperature, .buttonGrid, .generic: diff --git a/openHAB/UI/SwiftUI/Rows/ImageRowView.swift b/openHAB/UI/SwiftUI/Rows/ImageRowView.swift index 3f9ef61fb..e3692b371 100644 --- a/openHAB/UI/SwiftUI/Rows/ImageRowView.swift +++ b/openHAB/UI/SwiftUI/Rows/ImageRowView.swift @@ -46,6 +46,7 @@ private struct ImageRowContent: View { @State private var lastRegularImageState: RegularImageState? @State private var chartDisplayState: ChartDisplayState? @State private var animatedGIFSourceURL: URL? + @State private var embeddedGIFAspectRatio: CGFloat = 16.0 / 9.0 private let logger = Logger(subsystem: "org.openhab", category: "ImageRowView") @@ -147,7 +148,7 @@ private struct ImageRowContent: View { .resizable() .id(effectiveChartURL?.absoluteString ?? currentChartKey) .aspectRatio(contentMode: .fit) - .frame(maxHeight: 300) + .frame(maxWidth: .infinity) .clipShape(.rect(cornerRadius: 8)) } @@ -165,7 +166,11 @@ private struct ImageRowContent: View { KFAnimatedImage(source: .provider(provider)) .withOpenHABCredentials(for: networkTracker.activeConnection) .configure { $0.contentMode = .scaleAspectFit } - .frame(maxHeight: 300) + .onSuccess { result in + let size = result.image.size + if size.height > 0 { embeddedGIFAspectRatio = size.width / size.height } + } + .animatedGIFFrame(aspectRatio: embeddedGIFAspectRatio) .clipShape(.rect(cornerRadius: 8)) } else { KFImage(source: .provider(provider)) @@ -173,7 +178,7 @@ private struct ImageRowContent: View { .setProcessor(OpenHABImageProcessor(svgMaxSize: nil)) .resizable() .aspectRatio(contentMode: .fit) - .frame(maxHeight: 300) + .frame(maxWidth: .infinity) .clipShape(.rect(cornerRadius: 8)) } case let .link(url): @@ -204,7 +209,7 @@ private struct ImageRowContent: View { .forceRefresh(shouldCache ? false : true) .cacheOriginalImage(!shouldCache ? false : true) .id(shouldCache ? url?.absoluteString : "\(url?.absoluteString ?? "")-\(forceRefreshKey)") - .frame(maxHeight: 300) + .animatedGIFFrame(aspectRatio: aspectRatio(forGIFAt: url)) .clipShape(.rect(cornerRadius: 8)) } else { KFImage(url) @@ -239,7 +244,7 @@ private struct ImageRowContent: View { .cacheOriginalImage(!shouldCache ? false : true) .id(shouldCache ? url?.absoluteString : "\(url?.absoluteString ?? "")-\(forceRefreshKey)") .aspectRatio(contentMode: .fit) - .frame(maxHeight: 300) + .frame(maxWidth: .infinity) .clipShape(.rect(cornerRadius: 8)) } case .empty: @@ -254,6 +259,13 @@ private struct ImageRowContent: View { } } + private func aspectRatio(forGIFAt url: URL?) -> CGFloat { + guard let state = lastRegularImageState, state.url == url, state.image.size.height > 0 else { + return 16.0 / 9.0 + } + return state.image.size.width / state.image.size.height + } + private func isGIFMagic(_ data: Data) -> Bool { data.count >= 3 && data[0] == 0x47 && data[1] == 0x49 && data[2] == 0x46 // "GIF" } @@ -319,6 +331,25 @@ private struct ImageRowContent: View { } } +private struct AnimatedGIFFrameModifier: ViewModifier { + let aspectRatio: CGFloat + @State private var availableWidth: CGFloat = 0 + + func body(content: Content) -> some View { + content + .frame( + width: availableWidth > 0 ? availableWidth : nil, + height: availableWidth > 0 ? availableWidth / aspectRatio : nil + ) + .contentMargins(.horizontal, 0, for: .scrollContent) + .frame(maxWidth: .infinity) + .onGeometryChange(for: CGFloat.self) { $0.size.width } action: { newWidth in + guard newWidth > 0 else { return } + availableWidth = newWidth + } + } +} + struct ImageRowView: View { let input: MediaRowInput @EnvironmentObject var viewModel: SitemapPageViewModel @@ -332,3 +363,9 @@ struct ImageRowView: View { ) } } + +private extension View { + func animatedGIFFrame(aspectRatio: CGFloat) -> some View { + modifier(AnimatedGIFFrameModifier(aspectRatio: aspectRatio)) + } +} diff --git a/openHAB/UI/SwiftUI/Rows/VideoRowView.swift b/openHAB/UI/SwiftUI/Rows/VideoRowView.swift index 78e152fe4..e582741ae 100644 --- a/openHAB/UI/SwiftUI/Rows/VideoRowView.swift +++ b/openHAB/UI/SwiftUI/Rows/VideoRowView.swift @@ -69,22 +69,19 @@ private struct VideoRowContent: View { .resizable() .aspectRatio(aspectRatio, contentMode: .fit) .frame(maxWidth: .infinity) - .frame(height: 200) .clipShape(.rect(cornerRadius: 8)) } else { Rectangle() .fill(Color.gray.opacity(0.3)) - .frame(maxWidth: .infinity) - .frame(height: 200) .aspectRatio(aspectRatio, contentMode: .fit) + .frame(maxWidth: .infinity) .clipShape(.rect(cornerRadius: 8)) } } else { // HLS/other video formats using VideoPlayer VideoPlayer(player: player) - .frame(maxWidth: .infinity) - .frame(height: 200) .aspectRatio(aspectRatio, contentMode: .fit) + .frame(maxWidth: .infinity) .clipShape(.rect(cornerRadius: 8)) } diff --git a/openHAB/UI/SwiftUI/SitemapGridLayout.swift b/openHAB/UI/SwiftUI/SitemapGridLayout.swift new file mode 100644 index 000000000..985bfda33 --- /dev/null +++ b/openHAB/UI/SwiftUI/SitemapGridLayout.swift @@ -0,0 +1,277 @@ +// Copyright (c) 2010-2026 Contributors to the openHAB project +// +// See the NOTICE file(s) distributed with this work for additional +// information. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0 +// +// SPDX-License-Identifier: EPL-2.0 + +import OpenHABCore + +struct SitemapGridSection: Identifiable { + let id: String + let headerIndex: Int? + let groups: [SitemapGridRowGroup] + + static func makeSections(from rowInputs: [SitemapRowInput]) -> [SitemapGridSection] { + var sections: [SitemapGridSection] = [] + var currentHeaderIndex: Int? + var currentRows: [Int] = [] + + for (rowIndex, rowInput) in rowInputs.enumerated() { + guard rowInput.isGridSectionHeader else { + currentRows.append(rowIndex) + continue + } + + appendSection( + headerIndex: currentHeaderIndex, + rows: currentRows, + rowInputs: rowInputs, + to: §ions + ) + currentHeaderIndex = rowIndex + currentRows = [] + } + + appendSection( + headerIndex: currentHeaderIndex, + rows: currentRows, + rowInputs: rowInputs, + to: §ions + ) + return sections + } + + private static func appendSection(headerIndex: Int?, + rows: [Int], + rowInputs: [SitemapRowInput], + to sections: inout [SitemapGridSection]) { + guard headerIndex != nil || !rows.isEmpty else { return } + let id = headerIndex.flatMap { rowInputs[safe: $0]?.id } + ?? rows.first.flatMap { rowInputs[safe: $0]?.id } + ?? "empty-\(sections.count)" + sections.append( + SitemapGridSection( + id: id, + headerIndex: headerIndex, + groups: SitemapGridRowGroup.makeGroups(from: rows, rowInputs: rowInputs) + ) + ) + } +} + +struct SitemapGridRowGroup: Identifiable { + enum Content { + case regular([Int]) + case fullWidth(Int) + } + + let id: String + let content: Content + + static func makeGroups(from rows: [Int], rowInputs: [SitemapRowInput]) -> [SitemapGridRowGroup] { + var groups: [SitemapGridRowGroup] = [] + var regularRows: [Int] = [] + + for rowIndex in rows { + guard let rowInput = rowInputs[safe: rowIndex], + SitemapPresentationPolicy.usesFullWidthCell(for: rowInput) else { + regularRows.append(rowIndex) + continue + } + + appendRegularRows(regularRows, rowInputs: rowInputs, to: &groups) + regularRows = [] + groups.append( + SitemapGridRowGroup( + id: "full-width-\(rowInput.id)", + content: .fullWidth(rowIndex) + ) + ) + } + + appendRegularRows(regularRows, rowInputs: rowInputs, to: &groups) + return groups + } + + private static func appendRegularRows(_ rows: [Int], + rowInputs: [SitemapRowInput], + to groups: inout [SitemapGridRowGroup]) { + guard let firstRowIndex = rows.first, + let firstRow = rowInputs[safe: firstRowIndex] else { return } + groups.append( + SitemapGridRowGroup( + id: "regular-\(firstRow.id)", + content: .regular(rows) + ) + ) + } +} + +struct SitemapRowLayoutIdentity: Equatable { + enum Role { + case sectionHeader + case fullWidth + case regular + } + + let rowID: RowID + let role: Role + + static func makeIdentities(from rowInputs: [SitemapRowInput]) -> [SitemapRowLayoutIdentity] { + rowInputs.map { rowInput in + SitemapRowLayoutIdentity(rowID: rowInput.rowID, role: role(for: rowInput)) + } + } + + private static func role(for rowInput: SitemapRowInput) -> Role { + if RowLayoutPolicy.backgroundKind(for: rowInput) == .frame { + return .sectionHeader + } + + if SitemapPresentationPolicy.usesFullWidthCell(for: rowInput) { + return .fullWidth + } + + return .regular + } +} + +final class SitemapGridLayoutCache { + private var cachedVersion: Int? + private var cachedLayout = SitemapGridLayout(sections: []) + + func layout(for rowInputs: [SitemapRowInput], version: Int) -> SitemapGridLayout { + guard cachedVersion != version else { return cachedLayout } + + cachedLayout = SitemapGridLayout.makeLayout(from: rowInputs) + cachedVersion = version + return cachedLayout + } +} + +struct SitemapGridLayout { + let sections: [SitemapGridSection] + + static func makeLayout(from rowInputs: [SitemapRowInput]) -> SitemapGridLayout { + SitemapGridLayout(sections: SitemapGridSection.makeSections(from: rowInputs)) + } +} + +extension SitemapRowInput { + var isGridSectionHeader: Bool { + RowLayoutPolicy.backgroundKind(for: self) == .frame + } + + var showsGridNavigationAffordance: Bool { + switch self { + case .linked: + true + case let .media(_, input): + input.linkedPageLink != nil + case .frame, + .text, + .slider, + .selection, + .segmented, + .setpoint, + .rollershutter, + .toggle, + .input, + .colorPicker, + .colorTemperature, + .buttonGrid, + .generic: + false + } + } + + var usesConsistentGridCellHeight: Bool { + switch self { + case let .media(_, input): + switch input.renderingKind { + case .chart, .video: + false + case .image, + .webview, + .mapview, + .segmentedSwitch, + .toggleSwitch, + .rollershutterSwitch, + .slider, + .dateInput, + .textInput, + .text, + .frame, + .setpoint, + .selection, + .colorPicker, + .colorTemperaturePicker, + .buttonGrid, + .generic: + true + } + case .linked, + .text, + .slider, + .selection, + .segmented, + .setpoint, + .rollershutter, + .toggle, + .input, + .colorPicker, + .colorTemperature, + .generic: + true + case .frame, + .buttonGrid: + false + } + } + + var usesFullWidthGridCell: Bool { + switch self { + case let .media(_, input): + switch input.renderingKind { + case .chart, .video, .image, .webview, .mapview: + true + case .segmentedSwitch, + .toggleSwitch, + .rollershutterSwitch, + .slider, + .dateInput, + .textInput, + .text, + .frame, + .setpoint, + .selection, + .colorPicker, + .colorTemperaturePicker, + .buttonGrid, + .generic: + false + } + case .buttonGrid: + true + case .frame, + .linked, + .text, + .slider, + .selection, + .segmented, + .setpoint, + .rollershutter, + .toggle, + .input, + .colorPicker, + .colorTemperature, + .generic: + false + } + } +} diff --git a/openHAB/UI/SwiftUI/SitemapPageView.swift b/openHAB/UI/SwiftUI/SitemapPageView.swift index 38d30bdbb..78093f5a8 100644 --- a/openHAB/UI/SwiftUI/SitemapPageView.swift +++ b/openHAB/UI/SwiftUI/SitemapPageView.swift @@ -18,9 +18,11 @@ import UIKit struct SitemapPageView: View { @StateObject var viewModel = SitemapPageViewModel() @Environment(\.scenePhase) private var scenePhase + @Environment(\.horizontalSizeClass) private var horizontalSizeClass @State private var idleTimerDisabled = false @State private var scrollPosition = ScrollPosition() @State private var showScrollToTop = false + @State private var gridLayoutCache = SitemapGridLayoutCache() /// Sub-pages are created while the app is already active, so the first .active transition they /// observe is a genuine return from background — don't skip it. Root pages start false to skip /// the launch-time .active that races the initial .task startup. @@ -49,11 +51,8 @@ struct SitemapPageView: View { } } } else { - List(viewModel.rowInputs) { rowInput in - EmbeddingRowInputView(rowInput: rowInput) - .equatable() - .listRowInsets(RowLayoutPolicy.rowInsets(for: rowInput)) - .listRowBackground(RowLayoutPolicy.rowBackground(for: rowInput)) + GeometryReader { proxy in + pageContent(width: proxy.size.width) } .scrollPosition($scrollPosition) .onScrollGeometryChange(for: Bool.self) { geo in @@ -162,6 +161,128 @@ extension SitemapPageView { .listRowBackground(Color(UIColor.ohSecondarySystemGroupedBackground)) } } + + private var listContent: some View { + List(viewModel.rowInputs) { rowInput in + rowContent(rowInput) + .listRowInsets(RowLayoutPolicy.rowInsets(for: rowInput)) + .listRowBackground(RowLayoutPolicy.rowBackground(for: rowInput)) + } + } + + private func pageContent(width: CGFloat) -> some View { + let columnCount = SitemapPresentationPolicy.columnCount( + for: width, + horizontalSizeClass: horizontalSizeClass + ) + + return Group { + if width <= 0 { + Color.clear + } else if columnCount > 1 { + gridContent(columnCount: columnCount) + } else { + listContent + } + } + } + + private func gridContent(columnCount: Int) -> some View { + let rowInputs = viewModel.rowInputs + let layout = gridLayoutCache.layout( + for: rowInputs, + version: viewModel.rowInputLayoutVersion + ) + + return ScrollView { + LazyVStack(alignment: .leading, spacing: SitemapPresentationPolicy.gridSpacing) { + ForEach(layout.sections) { section in + if let headerIndex = section.headerIndex, + let header = rowInputs[safe: headerIndex] { + gridFrameRow(header) + } + + ForEach(section.groups) { group in + switch group.content { + case let .regular(rowIndices): + LazyVGrid( + columns: SitemapPresentationPolicy.columns(count: columnCount), + alignment: .leading, + spacing: SitemapPresentationPolicy.gridSpacing + ) { + ForEach(rowIndices, id: \.self) { rowIndex in + if let rowInput = rowInputs[safe: rowIndex] { + gridCell(rowInput) + } + } + } + case let .fullWidth(rowIndex): + if let rowInput = rowInputs[safe: rowIndex] { + gridFullWidthCell(rowInput) + } + } + } + } + } + .padding(.horizontal, SitemapPresentationPolicy.gridHorizontalPadding) + .padding(.vertical, SitemapPresentationPolicy.gridVerticalPadding) + } + .background(Color(UIColor.ohSystemGroupedBackground)) + } + + private func gridFrameRow(_ rowInput: SitemapRowInput) -> some View { + rowContent(rowInput) + .padding(RowLayoutPolicy.rowInsets(for: rowInput)) + .frame(maxWidth: .infinity, alignment: .center) + .background(RowLayoutPolicy.rowBackground(for: rowInput)) + .overlay(alignment: .bottom) { + Rectangle() + .fill(Color(UIColor.separator).opacity(0.5)) + .frame(height: SitemapPresentationPolicy.gridFrameSeparatorHeight) + } + } + + private func gridFullWidthCell(_ rowInput: SitemapRowInput) -> some View { + rowContent(rowInput) + .padding(RowLayoutPolicy.rowInsets(for: rowInput)) + .padding(.trailing, rowInput.showsGridNavigationAffordance ? 12 : 0) + .frame(maxWidth: .infinity, minHeight: 32, alignment: .center) + .background(RowLayoutPolicy.rowBackground(for: rowInput)) + .overlay(alignment: .trailing) { + if rowInput.showsGridNavigationAffordance { + Image(systemSymbol: .chevronRight) + .foregroundStyle(.tertiary) + .ohTextToken(.secondary) + .padding(.trailing, 10) + } + } + } + + private func gridCell(_ rowInput: SitemapRowInput) -> some View { + rowContent(rowInput) + .padding(RowLayoutPolicy.rowInsets(for: rowInput)) + .padding(.trailing, rowInput.showsGridNavigationAffordance ? 12 : 0) + .frame( + maxWidth: .infinity, + minHeight: 32, + alignment: .center + ) + .frame(height: SitemapPresentationPolicy.cellHeight(for: rowInput), alignment: .center) + .background(RowLayoutPolicy.rowBackground(for: rowInput)) + .overlay(alignment: .trailing) { + if rowInput.showsGridNavigationAffordance { + Image(systemSymbol: .chevronRight) + .foregroundStyle(.tertiary) + .ohTextToken(.secondary) + .padding(.trailing, 10) + } + } + } + + private func rowContent(_ rowInput: SitemapRowInput) -> some View { + EmbeddingRowInputView(rowInput: rowInput) + .equatable() + } } #Preview { diff --git a/openHAB/UI/SwiftUI/SitemapPresentationPolicy.swift b/openHAB/UI/SwiftUI/SitemapPresentationPolicy.swift new file mode 100644 index 000000000..3a9299291 --- /dev/null +++ b/openHAB/UI/SwiftUI/SitemapPresentationPolicy.swift @@ -0,0 +1,66 @@ +// Copyright (c) 2010-2026 Contributors to the openHAB project +// +// See the NOTICE file(s) distributed with this work for additional +// information. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0 +// +// SPDX-License-Identifier: EPL-2.0 + +import SwiftUI + +enum SitemapPresentationPolicy { + static let minimumColumnWidth: CGFloat = 340 + static let maximumColumnCount = 3 + static let gridSpacing: CGFloat = 8 + static let gridHorizontalPadding: CGFloat = 16 + static let gridVerticalPadding: CGFloat = 8 + static let regularGridCellHeight: CGFloat = 72 + static let gridFrameSeparatorHeight: CGFloat = 0.5 + + static var minimumGridWidth: CGFloat { + widthNeeded(for: 2) + } + + static func columnCount(for availableWidth: CGFloat, horizontalSizeClass: UserInterfaceSizeClass?) -> Int { + guard horizontalSizeClass == .regular else { + return 1 + } + + let availableContentWidth = availableWidth - (gridHorizontalPadding * 2) + let widestPossibleCount = min(maximumColumnCount, Int(availableContentWidth / minimumColumnWidth)) + guard widestPossibleCount >= 2 else { return 1 } + + for count in stride(from: widestPossibleCount, through: 2, by: -1) + where availableContentWidth >= contentWidthNeeded(for: count) { + return count + } + + return 1 + } + + static func columns(count: Int) -> [GridItem] { + Array( + repeating: GridItem(.flexible(minimum: minimumColumnWidth), spacing: gridSpacing, alignment: .top), + count: count + ) + } + + static func cellHeight(for rowInput: SitemapRowInput) -> CGFloat? { + rowInput.usesConsistentGridCellHeight ? regularGridCellHeight : nil + } + + static func usesFullWidthCell(for rowInput: SitemapRowInput) -> Bool { + rowInput.usesFullWidthGridCell + } + + private static func widthNeeded(for columnCount: Int) -> CGFloat { + contentWidthNeeded(for: columnCount) + (gridHorizontalPadding * 2) + } + + private static func contentWidthNeeded(for columnCount: Int) -> CGFloat { + (CGFloat(columnCount) * minimumColumnWidth) + (CGFloat(columnCount - 1) * gridSpacing) + } +} diff --git a/openHABTestsSwift/RowLayoutPolicyTests.swift b/openHABTestsSwift/RowLayoutPolicyTests.swift index 8a252684a..5497d92ac 100644 --- a/openHABTestsSwift/RowLayoutPolicyTests.swift +++ b/openHABTestsSwift/RowLayoutPolicyTests.swift @@ -41,9 +41,77 @@ struct RowLayoutPolicyTests { #expect(RowLayoutPolicy.rowInsets(for: input) == RowLayoutPolicy.regularInsets) #expect(RowLayoutPolicy.backgroundKind(for: input) == .regular) } + + @Test + func sitemapGridStartsOnRegularWideLayouts() { + #expect(SitemapPresentationPolicy.columnCount(for: 719, horizontalSizeClass: .regular) == 1) + #expect(SitemapPresentationPolicy.columnCount(for: 720, horizontalSizeClass: .regular) == 2) + #expect(SitemapPresentationPolicy.columnCount(for: 720, horizontalSizeClass: .compact) == 1) + } + + @Test + func sitemapGridCapsColumnCount() { + #expect(SitemapPresentationPolicy.columnCount(for: 1067, horizontalSizeClass: .regular) == 2) + #expect(SitemapPresentationPolicy.columnCount(for: 1068, horizontalSizeClass: .regular) == 3) + #expect(SitemapPresentationPolicy.columnCount(for: 1400, horizontalSizeClass: .regular) == 3) + } + + @Test + func regularGridRowsUseConsistentHeight() { + let widget = makeLinkedWidget(widgetID: "text-linked", type: .text, label: "Linked Text") + let input = mappedRowInput(widget) + + #expect(SitemapPresentationPolicy.cellHeight(for: input) == SitemapPresentationPolicy.regularGridCellHeight) + } + + @Test + func buttonGridChartAndVideoRowsUseNaturalGridHeight() { + let buttonGridInput = mappedRowInput(makeWidget(widgetID: "buttonGrid", type: .buttongrid, label: "Buttons")) + let chartInput = mappedRowInput(makeWidget(widgetID: "chart", type: .chart, label: "Chart")) + let videoInput = mappedRowInput(makeWidget(widgetID: "video", type: .video, label: "Video")) + + #expect(SitemapPresentationPolicy.cellHeight(for: buttonGridInput) == nil) + #expect(SitemapPresentationPolicy.cellHeight(for: chartInput) == nil) + #expect(SitemapPresentationPolicy.cellHeight(for: videoInput) == nil) + } + + @Test + func buttonGridChartAndVideoRowsUseFullGridWidth() { + let buttonGridInput = mappedRowInput(makeWidget(widgetID: "buttonGrid", type: .buttongrid, label: "Buttons")) + let chartInput = mappedRowInput(makeWidget(widgetID: "chart", type: .chart, label: "Chart")) + let videoInput = mappedRowInput(makeWidget(widgetID: "video", type: .video, label: "Video")) + let regularInput = mappedRowInput(makeLinkedWidget(widgetID: "text-linked", type: .text, label: "Linked Text")) + + #expect(SitemapPresentationPolicy.usesFullWidthCell(for: buttonGridInput)) + #expect(SitemapPresentationPolicy.usesFullWidthCell(for: chartInput)) + #expect(SitemapPresentationPolicy.usesFullWidthCell(for: videoInput)) + #expect(!SitemapPresentationPolicy.usesFullWidthCell(for: regularInput)) + } } private extension RowLayoutPolicyTests { + func makeWidget(widgetID: String, type: OpenHABWidget.WidgetType, label: String) -> OpenHABWidget { + OpenHABWidget( + widgetId: widgetID, + label: label, + icon: "text", + type: type, + url: nil, period: nil, minValue: nil, maxValue: nil, step: nil, + refresh: nil, height: nil, isLeaf: nil, iconColor: nil, + labelColor: nil, valueColor: nil, service: nil, state: nil, + text: nil, legend: nil, inputHint: nil, encoding: nil, + item: nil, + linkedPage: nil, + mappings: [], + widgets: [], + visibility: true, + switchSupport: nil, + forceAsItem: nil, + labelSource: .sitemapDefinition, + releaseOnly: nil + ) + } + func makeLinkedWidget(widgetID: String, type: OpenHABWidget.WidgetType, label: String) -> OpenHABWidget { OpenHABWidget( widgetId: widgetID, From 5a3cc5fcae6db86423b046bd3e9b1f470b217911 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Fri, 3 Jul 2026 23:28:13 +0200 Subject: [PATCH 46/91] feat: add accessory widget previews for Switch and Sensor widgets Signed-off-by: Tim Mueller-Seydlitz --- openHABWidget/SensorWidgetEntryView.swift | 18 ++++++++++++++++++ openHABWidget/SwitchWidgetEntryView.swift | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/openHABWidget/SensorWidgetEntryView.swift b/openHABWidget/SensorWidgetEntryView.swift index 054e7c9dd..4d4b886a4 100644 --- a/openHABWidget/SensorWidgetEntryView.swift +++ b/openHABWidget/SensorWidgetEntryView.swift @@ -440,3 +440,21 @@ extension SensorLargeWidgetItemEntity: SensorSlotResolvable {} } timeline: { SensorEntry(date: .now, home: nil, slots: sampleSlots) } + +#Preview("Accessory Circular", as: .accessoryCircular) { + SensorSmallWidget() +} timeline: { + SensorEntry(date: .now, home: nil, slots: [sampleSlots[0]]) +} + +#Preview("Accessory Rectangular", as: .accessoryRectangular) { + SensorSmallWidget() +} timeline: { + SensorEntry(date: .now, home: nil, slots: [sampleSlots[0]]) +} + +#Preview("Accessory Inline", as: .accessoryInline) { + SensorSmallWidget() +} timeline: { + SensorEntry(date: .now, home: nil, slots: [sampleSlots[0]]) +} diff --git a/openHABWidget/SwitchWidgetEntryView.swift b/openHABWidget/SwitchWidgetEntryView.swift index 515f0dbad..056618dc0 100644 --- a/openHABWidget/SwitchWidgetEntryView.swift +++ b/openHABWidget/SwitchWidgetEntryView.swift @@ -525,3 +525,21 @@ extension SwitchLargeWidgetItemEntity: SwitchSlotResolvable {} } timeline: { SwitchEntry(date: .now, home: nil, slots: sampleSlots) } + +#Preview("Accessory Circular", as: .accessoryCircular) { + SwitchSmallWidget() +} timeline: { + SwitchEntry(date: .now, home: nil, slots: [sampleSlots[0]]) +} + +#Preview("Accessory Rectangular", as: .accessoryRectangular) { + SwitchSmallWidget() +} timeline: { + SwitchEntry(date: .now, home: nil, slots: [sampleSlots[0]]) +} + +#Preview("Accessory Inline", as: .accessoryInline) { + SwitchSmallWidget() +} timeline: { + SwitchEntry(date: .now, home: nil, slots: [sampleSlots[0]]) +} From 4aed35505a867c0f600751db13c56d3355a55662 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Fri, 3 Jul 2026 23:36:11 +0200 Subject: [PATCH 47/91] chore: apply linter fixes Signed-off-by: Tim Mueller-Seydlitz --- openHAB/Supporting Files/Localizable.xcstrings | 5 +++++ openHAB/UI/SimpleMJPEGPlayer.swift | 8 +++----- openHAB/UI/Widgets/VideoStreamManager.swift | 10 ++++------ 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/openHAB/Supporting Files/Localizable.xcstrings b/openHAB/Supporting Files/Localizable.xcstrings index 44c8e1508..a31b6c001 100644 --- a/openHAB/Supporting Files/Localizable.xcstrings +++ b/openHAB/Supporting Files/Localizable.xcstrings @@ -16667,6 +16667,7 @@ }, "Switch Item" : { "comment" : "Display name for a switch item type.", + "extractionState" : "stale", "isCommentAutoGenerated" : true, "localizations" : { "de" : { @@ -16727,6 +16728,10 @@ "comment" : "Switch action to turn a device on.", "isCommentAutoGenerated" : true }, + "Switch or Dimmer Item" : { + "comment" : "Display representation of the type of an item that can be turned on or off.", + "isCommentAutoGenerated" : true + }, "Switch the active home in the openHAB app" : { "comment" : "Description of the intent to set the active home.", "isCommentAutoGenerated" : true, diff --git a/openHAB/UI/SimpleMJPEGPlayer.swift b/openHAB/UI/SimpleMJPEGPlayer.swift index 92b12ef71..5022ee8c0 100644 --- a/openHAB/UI/SimpleMJPEGPlayer.swift +++ b/openHAB/UI/SimpleMJPEGPlayer.swift @@ -63,11 +63,9 @@ final class SimpleMJPEGPlayer { delegate = nil } - func updateCallbacks( - onFrame: (@MainActor (UIImage) -> Void)?, - onFirstFrame: ((CGFloat) -> Void)?, - onError: ((any Error) -> Void)? - ) { + func updateCallbacks(onFrame: (@MainActor (UIImage) -> Void)?, + onFirstFrame: ((CGFloat) -> Void)?, + onError: ((any Error) -> Void)?) { if let onFrame { self.onFrame = onFrame } if let onFirstFrame { self.onFirstFrame = onFirstFrame } if let onError { self.onError = onError } diff --git a/openHAB/UI/Widgets/VideoStreamManager.swift b/openHAB/UI/Widgets/VideoStreamManager.swift index f243cd6e0..0225f5c19 100644 --- a/openHAB/UI/Widgets/VideoStreamManager.swift +++ b/openHAB/UI/Widgets/VideoStreamManager.swift @@ -23,12 +23,10 @@ final class VideoStreamManager { private init() {} - func getOrCreateStream( - for url: URL, - onFrame: @escaping @MainActor (UIImage) -> Void, - onFirstFrame: ((CGFloat) -> Void)?, - onError: ((any Error) -> Void)? - ) -> SimpleMJPEGPlayer { + func getOrCreateStream(for url: URL, + onFrame: @escaping @MainActor (UIImage) -> Void, + onFirstFrame: ((CGFloat) -> Void)?, + onError: ((any Error) -> Void)?) -> SimpleMJPEGPlayer { let key = url.absoluteString if let existingPlayer = activeStreams[key] { From 56c9fa851c4ca75f1c924a9bd47594a0f3f416a8 Mon Sep 17 00:00:00 2001 From: DigiH <17110652+DigiH@users.noreply.github.com> Date: Sat, 4 Jul 2026 01:12:48 +0200 Subject: [PATCH 48/91] Allow for two lines label and value for iOS Lock Screen Accessories (#1286) Signed-off-by: DigiH <17110652+DigiH@users.noreply.github.com> --- openHABWidget/SensorWidgetEntryView.swift | 5 +++-- openHABWidget/SwitchWidgetEntryView.swift | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/openHABWidget/SensorWidgetEntryView.swift b/openHABWidget/SensorWidgetEntryView.swift index 4d4b886a4..71d92b4ef 100644 --- a/openHABWidget/SensorWidgetEntryView.swift +++ b/openHABWidget/SensorWidgetEntryView.swift @@ -332,8 +332,9 @@ struct SensorAccessoryCircularView: View { Text(formattedState(for: slot.item)) .font(.caption2) .fontWeight(.bold) - .lineLimit(1) + .lineLimit(2) .minimumScaleFactor(0.5) + .multilineTextAlignment(.center) } } else { Image(systemSymbol: .gear) @@ -353,7 +354,7 @@ struct SensorAccessoryRectangularView: View { VStack(alignment: .leading, spacing: 2) { Text(label) .font(.headline) - .lineLimit(1) + .lineLimit(2) .minimumScaleFactor(0.7) if item.state != nil { Text(formattedState(for: item)) diff --git a/openHABWidget/SwitchWidgetEntryView.swift b/openHABWidget/SwitchWidgetEntryView.swift index 056618dc0..328152f44 100644 --- a/openHABWidget/SwitchWidgetEntryView.swift +++ b/openHABWidget/SwitchWidgetEntryView.swift @@ -427,7 +427,7 @@ struct SwitchAccessoryRectangularView: View { VStack(alignment: .leading, spacing: 2) { Text(label) .font(.headline) - .lineLimit(1) + .lineLimit(2) .minimumScaleFactor(0.7) if let stateText = slot.item.state { Text(stateText) From 452a5643f42cb56246e62fed9f9b7ff3e8a7c03a Mon Sep 17 00:00:00 2001 From: openhab-bot Date: Sat, 4 Jul 2026 07:32:14 +0000 Subject: [PATCH 49/91] committed version bump: 3.4.6 (296) Signed-off-by: openhab-bot --- CHANGELOG.md | 49 ++++++++++++++++++++++++++++++++++++++++++++++++ Version.xcconfig | 4 ++-- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc1645b32..5f764581e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,55 @@ ## [Unreleased] +- Allow for two lines label and value for iOS Lock Screen Accessories (#1286) +- Swiftlinting +- chore: apply linter fixes +- feat: add accessory widget previews for Switch and Sensor widgets +- refactor: remove UIImageView polling timer from MJPEG video stream +- committed version bump: 3.4.5 (294) +- Fix small widget content to be cntered +- committed version bump: 3.4.4 (293) +- committed version bump: 3.4.3 (292) +- fix: sync allowedItemTypes with query and add regression test +- feat: make Dimmer items selectable in Set Switch State intent +- chore: apply linter fixes +- fix: detect GIF images by magic bytes in ImageRowView +- committed version bump: 3.4.2 (290) +- fix: animate GIFs in the Image sitemap widget (#1280) +- Widget adjustments (#1279) +- fix: lock screen widgets now send commands on tap +- fix: notification ping now respects the ringer/silent switch (#1278) +- fix: add OpenHABShortcuts.swift to app target to resolve AppShortcuts.xcstrings phrase warnings +- committed version bump: 3.4.1 (287) +- fix: add provisioning profile for openHABWidgetExtension +- l10n: add Spanish, Finnish, Norwegian, and Russian translations +- l10n: switch German and French translations to informal address (du/tu) +- chore: remove 5 stale localization keys on feature branch +- l10n: add Spanish, Finnish, Norwegian, and Russian translations +- l10n: translate 2 new widget keys into all 9 languages +- chore: update openHABWidgetExtension scheme with askForAppToLaunch +- fix: restore project.pbxproj corrupted by bad 3-way merge of pbxproj +- chore: add shared schemes for openHABWidgetExtension and OpenHABWatchComplicationsExtension +- build: raise macOS minimum to 15 for AsyncSequence.Failure support +- chore: bump openHABTestsSwift deployment target to iOS 18.0 +- Merge develop + fix build warnings; add iOS home screen widgets with item display +- Sensor widget: format state values using stateDescription numberPattern +- Sensor widget: support 1/2/4 items per small/medium/large size +- Make ScrollToTop overlay button conditional (#1231) +- Bump minimum deployment targets to iOS 18 / watchOS 11 +- Use iOS 18 onScrollGeometryChange in SitemapPageView +- Use iOS 17 animation and scroll APIs +- Replace SetLocationValueIntent with SetActiveHomeIntent in AppShortcutsProvider +- Register SetActiveHomeIntent with AppShortcutsProvider +- Linting and AppIntents SSE warnings fixes +- Bump deployment targets to iOS 17.0 / watchOS 10.0 +- Convert AppShortcuts.strings to AppShortcuts.xcstrings String Catalog +- Fix AppIntentsMetadataProcessor synonym warnings in AppEnum cases +- Add AppShortcutsProvider and fix intent dialog gaps +- Upgrade SwiftFormatPlugin 0.58.7→0.61.1, SwiftLintPlugin 0.62.2→0.63.3 (#1225) +- ItemEventStream: backport SSE watchdog and self-contained network monitor +- openHABWidget: add interactive switch and sensor widgets + - Fix small widget content to be cntered - committed version bump: 3.4.4 (293) - committed version bump: 3.4.3 (292) diff --git a/Version.xcconfig b/Version.xcconfig index 983b209ce..0876c413a 100644 --- a/Version.xcconfig +++ b/Version.xcconfig @@ -6,5 +6,5 @@ // https://developer.apple.com/documentation/xcode/adding-a-build-configuration-file-to-your-project -MARKETING_VERSION = 3.4.5 -CURRENT_PROJECT_VERSION = 294 +MARKETING_VERSION = 3.4.6 +CURRENT_PROJECT_VERSION = 296 From 000af3540575f246c0984e45d358f11790da0a57 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Mon, 6 Jul 2026 18:51:19 +0200 Subject: [PATCH 50/91] fix: remove duplicate knownRegions from project.pbxproj Spurious "de 2", "en 2", "es 2" etc. entries in knownRegions caused Xcode to display phantom duplicate languages in the localisation editor. Signed-off-by: Tim Mueller-Seydlitz --- openHAB.xcodeproj/project.pbxproj | 9 --------- 1 file changed, 9 deletions(-) diff --git a/openHAB.xcodeproj/project.pbxproj b/openHAB.xcodeproj/project.pbxproj index e30c0964b..ab64b391c 100644 --- a/openHAB.xcodeproj/project.pbxproj +++ b/openHAB.xcodeproj/project.pbxproj @@ -1155,15 +1155,6 @@ nl, ru, nb, - "en 2", - "de 2", - "es 2", - "fi 2", - "fr 2", - "it 2", - "nb 2", - "nl 2", - "ru 2", ); mainGroup = DFB2621E18830A3600D3244D; packageReferences = ( From 19df9dd251245fb267b3d0454830b4e5ca8faead Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Mon, 6 Jul 2026 18:57:16 +0200 Subject: [PATCH 51/91] fix: add Localizable.xcstrings to widget extension target membership Allows Xcode's string reference scanner to find keys used in the widget extension, eliminating false-positive "not found in source" warnings. Signed-off-by: Tim Mueller-Seydlitz --- openHAB.xcodeproj/project.pbxproj | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/openHAB.xcodeproj/project.pbxproj b/openHAB.xcodeproj/project.pbxproj index ab64b391c..25edfc3fe 100644 --- a/openHAB.xcodeproj/project.pbxproj +++ b/openHAB.xcodeproj/project.pbxproj @@ -411,6 +411,13 @@ ); target = 6116C7DDD25EE245FE191A47 /* openHABWidgetExtension */; }; + DAB9BC8B2FFC158B00F25A53 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + "Supporting Files/Localizable.xcstrings", + ); + target = 6116C7DDD25EE245FE191A47 /* openHABWidgetExtension */; + }; /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ @@ -422,7 +429,7 @@ 2FD4E2CD2F66F9F600EBB340 /* openHABUITests */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (2FD4E2CF2F66F9F700EBB340 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = openHABUITests; sourceTree = ""; }; 2FD4E2E02F66F9FE00EBB340 /* openHABTestsSwift */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (2FD4E2F02F66F9FE00EBB340 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = openHABTestsSwift; sourceTree = ""; }; 2FD4E2F42F66FA2900EBB340 /* TestPlans */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (2FD4E2F62F66FA2900EBB340 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = TestPlans; sourceTree = ""; }; - 2FD4E52B2F670BA500EBB340 /* openHAB */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (2FD4E58D2F670BA500EBB340 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, 2FD4E58E2F670BA600EBB340 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = openHAB; sourceTree = ""; }; + 2FD4E52B2F670BA500EBB340 /* openHAB */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (2FD4E58D2F670BA500EBB340 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, DAB9BC8B2FFC158B00F25A53 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, 2FD4E58E2F670BA600EBB340 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = openHAB; sourceTree = ""; }; 3A5DAEE62F1FD8D300CFA482 /* OpenHABWatchComplications */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (3A5DAEF32F1FD8D300CFA482 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = OpenHABWatchComplications; sourceTree = ""; }; D41D0290EB2365F7E247EC0A /* openHABWidget */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (4D140F69148E28C6B524B346 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = openHABWidget; sourceTree = ""; }; DA8B15522F3BB74B007753FD /* openHABWatchTests */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = openHABWatchTests; sourceTree = ""; }; From c1b535c5d38903feb65fdd20492e5f3ce53039f7 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Mon, 6 Jul 2026 19:07:16 +0200 Subject: [PATCH 52/91] fix: show message body below title in notification list When a notification has both a payload title and a message body, only the title was displayed. Add subtitle property to OpenHABNotification and render it beneath the title in NotificationRow. Signed-off-by: Tim Mueller-Seydlitz --- .../Sources/OpenHABCore/Model/OpenHABNotification.swift | 8 ++++++++ openHAB/UI/NotificationsView.swift | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/OpenHABCore/Sources/OpenHABCore/Model/OpenHABNotification.swift b/OpenHABCore/Sources/OpenHABCore/Model/OpenHABNotification.swift index e3b252fd3..0319b3c98 100644 --- a/OpenHABCore/Sources/OpenHABCore/Model/OpenHABNotification.swift +++ b/OpenHABCore/Sources/OpenHABCore/Model/OpenHABNotification.swift @@ -53,6 +53,14 @@ public struct OpenHABNotification: Identifiable, Hashable, Sendable { return payload?.message ?? "" } + /// The message body shown beneath the title, only present when title comes from payload.title. + public var subtitle: String? { + guard let payloadTitle = payload?.title, !payloadTitle.isEmpty else { return nil } + if let msg = message, !msg.isEmpty { return msg } + if let payloadMsg = payload?.message, !payloadMsg.isEmpty { return payloadMsg } + return nil + } + /// Prefer root message, fall back to payload.message public init(id: String, message: String?, diff --git a/openHAB/UI/NotificationsView.swift b/openHAB/UI/NotificationsView.swift index d0bd603d3..e55afcab5 100644 --- a/openHAB/UI/NotificationsView.swift +++ b/openHAB/UI/NotificationsView.swift @@ -40,6 +40,12 @@ struct NotificationRow: View { VStack(alignment: .leading, spacing: 2) { Text(notification.title.isEmpty ? String(localized: "message_not_decoded", comment: "") : notification.title) .font(.body) + if let subtitle = notification.subtitle { + Text(subtitle) + .font(.subheadline) + .foregroundStyle(.secondary) + .lineLimit(3) + } if let timeStamp = notification.created { Text(dateString(from: timeStamp)) .font(.caption) From c98545cdeee5a7ebaa80a981366328a483a3974f Mon Sep 17 00:00:00 2001 From: DigiH <17110652+DigiH@users.noreply.github.com> Date: Mon, 6 Jul 2026 19:18:21 +0200 Subject: [PATCH 53/91] Small Widget background watermark alignment adjustments (#1289) Signed-off-by: DigiH <17110652+DigiH@users.noreply.github.com> --- openHABWidget/OpenHABIconOverlay.swift | 2 +- openHABWidget/SensorWidgetEntryView.swift | 15 +++++++++------ openHABWidget/SwitchWidgetEntryView.swift | 15 +++++++++------ 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/openHABWidget/OpenHABIconOverlay.swift b/openHABWidget/OpenHABIconOverlay.swift index 0adc670ac..706a19da6 100644 --- a/openHABWidget/OpenHABIconOverlay.swift +++ b/openHABWidget/OpenHABIconOverlay.swift @@ -15,7 +15,7 @@ import SwiftUI // MARK: - Shared Icon Overlay -struct OpenHABIconOverlay: View { +struct OpenHABIconWatermark: View { var body: some View { Image("openHABIcon") .resizable() diff --git a/openHABWidget/SensorWidgetEntryView.swift b/openHABWidget/SensorWidgetEntryView.swift index 71d92b4ef..2a887c3a2 100644 --- a/openHABWidget/SensorWidgetEntryView.swift +++ b/openHABWidget/SensorWidgetEntryView.swift @@ -203,9 +203,10 @@ struct SensorSmallWidgetView: View { var body: some View { ZStack(alignment: .center) { - OpenHABIconOverlay() + OpenHABIconWatermark() .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) - .padding(10) + .padding(11) + .offset(x: 0.5, y: 0.5) if let slot = entry.slots.compactMap(\.self).first { let item = slot.item @@ -245,9 +246,10 @@ struct SensorMediumWidgetView: View { var body: some View { ZStack(alignment: .topLeading) { - OpenHABIconOverlay() + OpenHABIconWatermark() .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) - .padding(10) + .padding(11) + .offset(x: 0.5, y: 0.5) let filledSlots = entry.slots.compactMap(\.self) if filledSlots.isEmpty { @@ -279,9 +281,10 @@ struct SensorLargeWidgetView: View { var body: some View { ZStack(alignment: .topLeading) { - OpenHABIconOverlay() + OpenHABIconWatermark() .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) - .padding(14) + .padding(15) + .offset(x: 0.5, y: 0.5) let filledSlots = entry.slots.compactMap(\.self) if filledSlots.isEmpty { diff --git a/openHABWidget/SwitchWidgetEntryView.swift b/openHABWidget/SwitchWidgetEntryView.swift index 328152f44..1a29bc20e 100644 --- a/openHABWidget/SwitchWidgetEntryView.swift +++ b/openHABWidget/SwitchWidgetEntryView.swift @@ -237,9 +237,10 @@ struct SwitchSmallWidgetView: View { var body: some View { ZStack(alignment: .center) { - OpenHABIconOverlay() + OpenHABIconWatermark() .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) - .padding(10) + .padding(11) + .offset(x: 0.5, y: 0.5) if let slot = entry.slots.compactMap(\.self).first { let label = slot.item.label.isEmpty ? slot.item.name : slot.item.label @@ -284,9 +285,10 @@ struct SwitchMediumWidgetView: View { var body: some View { ZStack(alignment: .topLeading) { - OpenHABIconOverlay() + OpenHABIconWatermark() .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) - .padding(10) + .padding(11) + .offset(x: 0.5, y: 0.5) let filledSlots = Array(entry.slots.compactMap(\.self)) if filledSlots.isEmpty { @@ -318,9 +320,10 @@ struct SwitchLargeWidgetView: View { var body: some View { ZStack(alignment: .topLeading) { - OpenHABIconOverlay() + OpenHABIconWatermark() .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) - .padding(14) + .padding(15) + .offset(x: 0.5, y: 0.5) let filledSlots = Array(entry.slots.compactMap(\.self)) if filledSlots.isEmpty { From a41cd1d74c1ed241490dccffdf0fa3f826a25c34 Mon Sep 17 00:00:00 2001 From: Tim Bert <5411131+timbms@users.noreply.github.com> Date: Mon, 6 Jul 2026 20:19:14 +0200 Subject: [PATCH 54/91] Migrate sitemap page updates from long-polling to SSE (#1168) Signed-off-by: DigiH <17110652+DigiH@users.noreply.github.com> Signed-off-by: Tim Mueller-Seydlitz --- .../OpenHABCore/Model/OpenHABPage.swift | 35 ++- .../Model/OpenHABServerProperties.swift | 13 + .../Model/OpenHABSitemapWidgetEvent.swift | 65 +++-- .../OpenHABCore/Model/OpenHABWidget.swift | 51 ++++ .../OpenHABCore/Model/WidgetRendering.swift | 3 +- .../OpenHABCore/Util/OpenAPIService.swift | 127 +++++---- .../OpenHABCore/Util/SitemapEventStream.swift | 205 ++++++++++++++ .../NetworkTrackerTests.swift | 15 + .../OpenHABCoreTests/NumberStateTests.swift | 3 +- .../OpenHABWidgetEventApplicationTests.swift | 161 +++++++++++ openHAB.xcodeproj/project.pbxproj | 6 +- .../SitemapPageViewModel+Commands.swift | 214 +++++++++++++++ openHAB/Models/SitemapPageViewModel+SSE.swift | 159 +++++++++++ openHAB/Models/SitemapPageViewModel.swift | 258 ++++-------------- .../Supporting Files/Localizable.xcstrings | 163 +++++++++-- openHAB/UI/OpenHABRootViewController.swift | 9 +- .../UI/ScreenSaver/ScreenSaverManager.swift | 2 +- 17 files changed, 1162 insertions(+), 327 deletions(-) create mode 100644 OpenHABCore/Sources/OpenHABCore/Util/SitemapEventStream.swift create mode 100644 OpenHABCore/Tests/OpenHABCoreTests/OpenHABWidgetEventApplicationTests.swift create mode 100644 openHAB/Models/SitemapPageViewModel+Commands.swift create mode 100644 openHAB/Models/SitemapPageViewModel+SSE.swift diff --git a/OpenHABCore/Sources/OpenHABCore/Model/OpenHABPage.swift b/OpenHABCore/Sources/OpenHABCore/Model/OpenHABPage.swift index bd2744b68..ea6d89151 100644 --- a/OpenHABCore/Sources/OpenHABCore/Model/OpenHABPage.swift +++ b/OpenHABCore/Sources/OpenHABCore/Model/OpenHABPage.swift @@ -51,19 +51,6 @@ public class OpenHABPage: NSObject, @unchecked Sendable { } } -public extension OpenHABPage { - func filter(_ isIncluded: (OpenHABWidget) throws -> Bool) rethrows -> OpenHABPage { - try OpenHABPage( - pageId: pageId, - title: title, - link: link, - leaf: leaf, - widgets: widgets.filter(isIncluded), - icon: icon - ) - } -} - public extension OpenHABPage { convenience init?(_ page: Components.Schemas.PageDTO?) { if let page { @@ -79,4 +66,26 @@ public extension OpenHABPage { return nil } } + + func filter(_ isIncluded: (OpenHABWidget) throws -> Bool) rethrows -> OpenHABPage { + try OpenHABPage( + pageId: pageId, + title: title, + link: link, + leaf: leaf, + widgets: widgets.filter(isIncluded), + icon: icon + ) + } + + @discardableResult + func apply(event: OpenHABSitemapWidgetEvent) -> SitemapWidgetEventApplicationResult { + for widget in widgets { + let result = widget.apply(event: event) + if result != .notFound { + return result + } + } + return .notFound + } } diff --git a/OpenHABCore/Sources/OpenHABCore/Model/OpenHABServerProperties.swift b/OpenHABCore/Sources/OpenHABCore/Model/OpenHABServerProperties.swift index 25e30b65a..5ba87eacd 100644 --- a/OpenHABCore/Sources/OpenHABCore/Model/OpenHABServerProperties.swift +++ b/OpenHABCore/Sources/OpenHABCore/Model/OpenHABServerProperties.swift @@ -36,3 +36,16 @@ extension OpenHABServerProperties { ) } } + +public extension OpenHABServerProperties { + var majorVersion: Int? { + guard let version else { return nil } + let trimmed = version.trimmingCharacters(in: .whitespacesAndNewlines) + let parts = trimmed.split(separator: ".") + return Int(parts.first ?? "") + } + + func hasSseSupport() -> Bool { + (majorVersion ?? 0) >= 3 + } +} diff --git a/OpenHABCore/Sources/OpenHABCore/Model/OpenHABSitemapWidgetEvent.swift b/OpenHABCore/Sources/OpenHABCore/Model/OpenHABSitemapWidgetEvent.swift index 52ff8f15e..38bd2d3c9 100644 --- a/OpenHABCore/Sources/OpenHABCore/Model/OpenHABSitemapWidgetEvent.swift +++ b/OpenHABCore/Sources/OpenHABCore/Model/OpenHABSitemapWidgetEvent.swift @@ -11,23 +11,36 @@ import Foundation -public class OpenHABSitemapWidgetEvent { - var sitemapName: String? - var pageId: String? - var widgetId: String? - var label: String? - var labelSource: String? - var icon: String? - var reloadIcon: Bool? - var labelcolor: String? - var valuecolor: String? - var iconcolor: String? - var visibility: Bool? - var state: String? - var enrichedItem: OpenHABItem? - var descriptionChanged: Bool? +public struct OpenHABSitemapWidgetEvent: Sendable { + public let sitemapName: String? + public let pageId: String? + public let widgetId: String? + public let label: String? + public let labelSource: String? + public let icon: String? + public let reloadIcon: Bool? + public let labelcolor: String? + public let valuecolor: String? + public let iconcolor: String? + public let visibility: Bool? + public let state: String? + public let enrichedItem: OpenHABItem? + public let descriptionChanged: Bool? - init(sitemapName: String? = nil, pageId: String? = nil, widgetId: String? = nil, label: String? = nil, labelSource: String? = nil, icon: String? = nil, reloadIcon: Bool? = nil, labelcolor: String? = nil, valuecolor: String? = nil, iconcolor: String? = nil, visibility: Bool? = nil, state: String? = nil, enrichedItem: OpenHABItem? = nil, descriptionChanged: Bool? = nil) { + public init(sitemapName: String? = nil, + pageId: String? = nil, + widgetId: String? = nil, + label: String? = nil, + labelSource: String? = nil, + icon: String? = nil, + reloadIcon: Bool? = nil, + labelcolor: String? = nil, + valuecolor: String? = nil, + iconcolor: String? = nil, + visibility: Bool? = nil, + state: String? = nil, + enrichedItem: OpenHABItem? = nil, + descriptionChanged: Bool? = nil) { self.sitemapName = sitemapName self.pageId = pageId self.widgetId = widgetId @@ -44,10 +57,24 @@ public class OpenHABSitemapWidgetEvent { self.descriptionChanged = descriptionChanged } - convenience init?(_ event: Components.Schemas.SitemapWidgetEvent?) { + public init?(_ event: Components.Schemas.SitemapWidgetEvent?) { guard let event else { return nil } - // swiftlint:disable:next line_length - self.init(sitemapName: event.sitemapName, pageId: event.pageId, widgetId: event.widgetId, label: event.label, labelSource: event.labelSource, icon: event.icon, reloadIcon: event.reloadIcon, labelcolor: event.labelcolor, valuecolor: event.valuecolor, iconcolor: event.iconcolor, visibility: event.visibility, state: event.state, enrichedItem: OpenHABItem(event.item), descriptionChanged: event.descriptionChanged) + self.init( + sitemapName: event.sitemapName, + pageId: event.pageId, + widgetId: event.widgetId, + label: event.label, + labelSource: event.labelSource, + icon: event.icon, + reloadIcon: event.reloadIcon, + labelcolor: event.labelcolor, + valuecolor: event.valuecolor, + iconcolor: event.iconcolor, + visibility: event.visibility, + state: event.state, + enrichedItem: OpenHABItem(event.item), + descriptionChanged: event.descriptionChanged + ) } } diff --git a/OpenHABCore/Sources/OpenHABCore/Model/OpenHABWidget.swift b/OpenHABCore/Sources/OpenHABCore/Model/OpenHABWidget.swift index 55b0fb02e..0fe972fdc 100644 --- a/OpenHABCore/Sources/OpenHABCore/Model/OpenHABWidget.swift +++ b/OpenHABCore/Sources/OpenHABCore/Model/OpenHABWidget.swift @@ -14,6 +14,13 @@ import Foundation @_exported import MapKit import os.log +public enum SitemapWidgetEventApplicationResult: Equatable, Sendable { + case applied + case unchanged + case notFound + case requiresPageReload +} + public class OpenHABWidget: NSObject, MKAnnotation, Identifiable, ObservableObject { public enum WidgetType: String, Decodable, Sendable { case chart = "Chart" @@ -241,6 +248,50 @@ public extension [OpenHABWidget] { } } +public extension OpenHABWidget { + @discardableResult + func apply(event: OpenHABSitemapWidgetEvent) -> SitemapWidgetEventApplicationResult { + guard let eventWidgetId = event.widgetId else { return .notFound } + + if widgetId == eventWidgetId { + guard event.descriptionChanged != true else { + return .requiresPageReload + } + // reloadIcon arrives on virtually every SSE event but carries no + // displayable payload of its own — skip the rebuild if nothing else changed. + guard event.label != nil || event.icon != nil || event.state != nil + || event.enrichedItem != nil || event.labelcolor != nil + || event.valuecolor != nil || event.iconcolor != nil + || event.visibility != nil || event.labelSource != nil + else { return .unchanged } + if let label = event.label { self.label = label } + if let icon = event.icon { self.icon = icon } + if let labelcolor = event.labelcolor { self.labelcolor = labelcolor } + if let valuecolor = event.valuecolor { self.valuecolor = valuecolor } + if let iconcolor = event.iconcolor { iconColor = iconcolor } + if let visibility = event.visibility { self.visibility = visibility } + if let state = event.state { + self.state = state + } else if let itemState = event.enrichedItem?.state { + state = itemState + } + if let item = event.enrichedItem { self.item = item } + if let labelSource = event.labelSource { + self.labelSource = OpenHABWidget.LabelSource(rawValue: labelSource) ?? .unknown + } + return .applied + } + + for widget in widgets { + let result = widget.apply(event: event) + if result != .notFound { + return result + } + } + return .notFound + } +} + extension OpenHABWidget { convenience init(_ widget: Components.Schemas.WidgetDTO) { self.init( diff --git a/OpenHABCore/Sources/OpenHABCore/Model/WidgetRendering.swift b/OpenHABCore/Sources/OpenHABCore/Model/WidgetRendering.swift index cd0407de4..4de776cea 100644 --- a/OpenHABCore/Sources/OpenHABCore/Model/WidgetRendering.swift +++ b/OpenHABCore/Sources/OpenHABCore/Model/WidgetRendering.swift @@ -153,7 +153,8 @@ public extension WidgetRendering { return itemState.parseAsNumber(format: item.stateDescription?.numberPattern) .toString(locale: Locale(identifier: "US")) } else if widgetType == .switchWidget, mappings.isEmpty, !item.isOfTypeOrGroupType(.rollershutter) { - return (itemState == "0" || itemState == "OFF") ? "OFF" : "ON" + let switchState = state.isEmpty ? itemState : state + return (switchState == "0" || switchState == "OFF") ? "OFF" : "ON" } return itemState } diff --git a/OpenHABCore/Sources/OpenHABCore/Util/OpenAPIService.swift b/OpenHABCore/Sources/OpenHABCore/Util/OpenAPIService.swift index bdb9036ee..7b50491f9 100644 --- a/OpenHABCore/Sources/OpenHABCore/Util/OpenAPIService.swift +++ b/OpenHABCore/Sources/OpenHABCore/Util/OpenAPIService.swift @@ -29,7 +29,7 @@ public enum OpenAPIServiceConfiguration { case longTerm } -protocol OpenAPIServiceProtocol: AnyObject, Sendable { +public protocol OpenAPIServiceProtocol: AnyObject, Sendable { func getRootVersion() async throws -> Int @discardableResult func getRoot() async throws -> OpenHABServerProperties func sendItemCommand(itemname: String, command: String, sourcePrefix: String?, deviceId: String?) async throws @@ -39,11 +39,12 @@ protocol OpenAPIServiceProtocol: AnyObject, Sendable { func getItemByName(id: String) async throws -> OpenHABItem? func pollDataForPage(sitemapname: String, pageId: String, longPolling: Bool) async throws -> OpenHABPage? func runNow(ruleUID: String, payload: [String: any Sendable]) async throws + func openHABcreateSubscription() async throws -> String? + func openHABSitemapWidgetEvents(subscriptionid: String, sitemap: String, pageId: String) async throws -> any AsyncSequence & Sendable + func openHABEvents(topics: String?) async throws -> any AsyncSequence & Sendable } /// The generated OpenAPI client is wrapped by this curated API. -/// The library leaks the fact that it uses Swift OpenAPI Generator under the hood in 'openHABSitemapWidgetEvents'. -/// It will require the migration to Swift 6.1 before this can be changed. public actor OpenAPIService { private var client: any APIProtocol private var url: URL? @@ -135,6 +136,11 @@ public actor OpenAPIService { return "\(sourcePrefix)=>\(base)" } + private func subscriptionId(from location: String?) -> String? { + guard let location else { return nil } + return URL(string: location)?.lastPathComponent + } + deinit { urlSession?.invalidateAndCancel() } @@ -213,73 +219,94 @@ public extension OpenAPIService { } public extension OpenAPIService { - // Will need swift 6.0 SE-0421 and iOS 18 to return an opaque sequence without the type eraser AnyAsyncSequence -// func openHABSitemapWidgetEvents(subscriptionid: String, sitemap: String) async throws -> some AsyncSequence { -// -// let path = Operations.getSitemapEvents_1.Input.Path(subscriptionid: subscriptionid) -// let query = Operations.getSitemapEvents_1.Input.Query(sitemap: sitemap, pageid: sitemap) -// let decodedSequence = try await client.getSitemapEvents_1(path: path, query: query) -// .ok.body.text_event_hyphen_stream -// .asDecodedServerSentEventsWithJSONData(of: Components.Schemas.SitemapWidgetEvent.self) -// return decodedSequence.compactMap { OpenHABSitemapWidgetEvent($0.data) } -// } - struct AnyAsyncSequence: AsyncSequence { - public struct Iterator: AsyncIteratorProtocol { - private var _next: () async throws -> Element? - - init(_ base: S) where S.Element == Element { - var it = base.makeAsyncIterator() - _next = { try await it.next() } - } - - public mutating func next() async throws -> Element? { - try await _next() + private static func parseSitemapEvent(_ sse: ServerSentEvent) -> SitemapEventMessage? { + if let event = sse.event?.lowercased(), event == "alive" { + Logger.openAPIService.debug("Sitemap SSE alive event") + return .alive + } + guard let raw = sse.data else { return nil } + Logger.openAPIService.debug("Sitemap SSE raw event: \(raw, privacy: .public)") + let data = Data(raw.utf8) + if let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any], + let type = json["TYPE"] as? String { + switch type { + case "ALIVE": + return .alive + case "SITEMAP_CHANGED": + Logger.openAPIService.info("Sitemap SSE SITEMAP_CHANGED event") + return .sitemapChanged( + sitemap: json["sitemapName"] as? String, + pageId: json["pageId"] as? String + ) + default: + break } } - - public let _make: () -> Iterator - - init(_ base: S) where S.Element == Element { - _make = { Iterator(base) } - } - - public func makeAsyncIterator() -> Iterator { - _make() + if let decoded = try? JSONDecoder().decode(Components.Schemas.SitemapWidgetEvent.self, from: data), + let event = OpenHABSitemapWidgetEvent(decoded) { + Logger.openAPIService.debug("Sitemap SSE widget event decoded: \(event.widgetId.orEmpty, privacy: .public)") + return .widget(event) } + return .unknown(raw: raw) } /// Returns subscription id or nil func openHABcreateSubscription() async throws -> String? { Logger.openAPIService.info("Creating subscription") - let result = try await client.createSitemapEventSubscription() - guard let urlString = try result.ok.body.json.context?.headers?.Location?.first else { return nil } - return URL(string: urlString)?.lastPathComponent + guard let acceptValue = OpenAPIRuntime.AcceptHeaderContentType(rawValue: "application/json") else { + return nil + } + let accept: [OpenAPIRuntime.AcceptHeaderContentType] = [ + acceptValue + ] + let headers = Operations.createSitemapEventSubscription.Input.Headers(accept: accept) + let result = try await client.createSitemapEventSubscription(.init(headers: headers)) + + switch result { + case let .ok(ok): + // openHAB < 3.3: returns 200 with Location in JSON body. + guard let urlString = try ok.body.json.context?.headers?.Location?.first else { return nil } + let subscriptionId = subscriptionId(from: urlString) + Logger.openAPIService.info("Sitemap SSE subscription created from JSON body: \(subscriptionId.orEmpty, privacy: .public)") + return subscriptionId + case let .created(created): + // openHAB >= 3.3: returns 201 with Location in the HTTP header. + let subscriptionId = subscriptionId(from: created.headers.Location) + Logger.openAPIService.info("Sitemap SSE subscription created from Location header: \(subscriptionId.orEmpty, privacy: .public)") + return subscriptionId + case .serviceUnavailable: + // Subscriptions limit reached on the server. + Logger.openAPIService.warning("Sitemap SSE subscription limit reached (503) — server has too many open subscriptions") + throw URLError(.badServerResponse) + default: + Logger.openAPIService.warning("Sitemap SSE subscription returned unexpected response") + throw URLError(.badServerResponse) + } } - func openHABSitemapWidgetEvents(subscriptionid: String, sitemap: String) - async throws -> AnyAsyncSequence { + func openHABSitemapWidgetEvents(subscriptionid: String, sitemap: String, pageId: String) + async throws -> any AsyncSequence & Sendable { let path = Operations.getSitemapEvents_1.Input.Path(subscriptionid: subscriptionid) - let query = Operations.getSitemapEvents_1.Input.Query(sitemap: sitemap, pageid: sitemap) - - let seq = try await client.getSitemapEvents_1(path: path, query: query) - .ok.body.text_event_hyphen_stream - .asDecodedServerSentEventsWithJSONData(of: Components.Schemas.SitemapWidgetEvent.self) - .compactMap { OpenHABSitemapWidgetEvent($0.data) } - - return AnyAsyncSequence(seq) + let query = Operations.getSitemapEvents_1.Input.Query(sitemap: sitemap, pageid: pageId) + do { + return try await client.getSitemapEvents_1(path: path, query: query) + .ok.body.text_event_hyphen_stream + .asDecodedServerSentEvents() + .compactMap { @Sendable sse in Self.parseSitemapEvent(sse) } + } catch let clientError as ClientError where clientError.response?.status.code == 404 { + throw SitemapSseError.unsupported + } } func openHABEvents(topics: String? = nil) - async throws -> AnyAsyncSequence { + async throws -> any AsyncSequence & Sendable { let query: Operations.getEvents.Input.Query = (topics == nil) ? .init() : .init(topics: topics) - let seq = try await client.getEvents(query: query) + return try await client.getEvents(query: query) .ok.body.text_event_hyphen_stream .asDecodedServerSentEventsWithJSONData(of: OpenHABEvent.self) .compactMap(\.data) - - return AnyAsyncSequence(seq) } } diff --git a/OpenHABCore/Sources/OpenHABCore/Util/SitemapEventStream.swift b/OpenHABCore/Sources/OpenHABCore/Util/SitemapEventStream.swift new file mode 100644 index 000000000..2b4010f35 --- /dev/null +++ b/OpenHABCore/Sources/OpenHABCore/Util/SitemapEventStream.swift @@ -0,0 +1,205 @@ +// Copyright (c) 2010-2026 Contributors to the openHAB project +// +// See the NOTICE file(s) distributed with this work for additional +// information. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0 +// +// SPDX-License-Identifier: EPL-2.0 + +import Foundation +import OSLog + +public enum SitemapSseError: Error, Equatable { + case unsupported +} + +public enum SitemapEventMessage: Sendable { + case alive + case sitemapChanged(sitemap: String?, pageId: String?) + case widget(OpenHABSitemapWidgetEvent) + case unknown(raw: String) +} + +public actor SitemapEventStream { + private struct Target: Equatable { + let sitemap: String + let pageId: String + } + + private var continuations = [UUID: AsyncStream>.Continuation]() + private var listenTask: Task? + private var networkMonitoringTask: Task? + private var currentConfig: ConnectionConfiguration? + private var currentTarget: Target? + private var isStopped = false + private var lastEventTime = Date.now + private let makeService: @Sendable (ConnectionConfiguration) throws -> any OpenAPIServiceProtocol + + public init(makeService: @escaping @Sendable (ConnectionConfiguration) throws -> any OpenAPIServiceProtocol = { + try OpenAPIService(connectionConfiguration: $0) + }) { + self.makeService = makeService + } + + public func stream(sitemap: String, pageId: String) -> AsyncStream> { + isStopped = false + let target = Target(sitemap: sitemap, pageId: pageId) + if currentTarget != target { + Logger.restAPI.info("Sitemap SSE target set to \(sitemap)/\(pageId)") + currentTarget = target + listenTask?.cancel() + listenTask = nil + } + + // Flush stale continuations synchronously before registering a new one. + // Because stream(sitemap:pageId:) is actor-isolated this is atomic — no race with broadcast(). + continuations.values.forEach { $0.finish() } + continuations.removeAll() + + let stream = AsyncStream> { continuation in + let id = UUID() + continuations[id] = continuation + continuation.onTermination = { [weak self] _ in + guard let self else { return } + Task { [id] in + await self.cleanupContinuation(id) + } + } + } + + // Always ensure the listen task is running, even when the target didn't change. + // cleanupContinuation may have cancelled it when the last consumer left. + restartListeningIfNeeded() + return stream + } + + public func stop() { + isStopped = true + currentTarget = nil + listenTask?.cancel() + listenTask = nil + } + + public func startMonitoringNetworkIfNeeded(initialConnection: ConnectionInfo?) { + updateConnection(initialConnection) + + // Keep network monitoring alive for the actor lifetime. Stop/restart + // behavior is handled by updateConnection cancelling listenTask. + guard networkMonitoringTask == nil else { return } + + networkMonitoringTask = Task { [weak self] in + for await conn in await NetworkTracker.shared.activeConnectionStream() { + guard let self else { return } + await updateConnection(conn) + } + } + } + + private func cleanupContinuation(_ id: UUID) { + continuations[id]?.finish() + continuations.removeValue(forKey: id) + if continuations.isEmpty { + listenTask?.cancel() + listenTask = nil + } + } + + private func updateConnection(_ info: ConnectionInfo?) { + let newConfig = info?.configuration + + guard currentConfig != newConfig else { return } + + currentConfig = info?.configuration + + Logger.restAPI.info("Network changed – restarting sitemap SSE connection") + + listenTask?.cancel() + listenTask = nil + + restartListeningIfPossible() + } + + private func restartListeningIfNeeded() { + guard !isStopped, listenTask == nil else { return } + guard let cfg = currentConfig, let target = currentTarget else { return } + Logger.restAPI.info("Starting sitemap SSE listener for \(target.sitemap)/\(target.pageId)") + listenTask = Task { await listen(using: cfg, target: target) } + } + + private func restartListeningIfPossible() { + guard !isStopped else { return } + guard let cfg = currentConfig, let target = currentTarget else { + broadcast(.disconnected(nil)) + return + } + Logger.restAPI.info("Starting sitemap SSE listener for \(target.sitemap)/\(target.pageId)") + listenTask?.cancel() + listenTask = Task { await listen(using: cfg, target: target) } + } + + private func listen(using config: ConnectionConfiguration, target: Target) async { + var backoff: TimeInterval = 1 + let maxBackoff: TimeInterval = 30 + + while !Task.isCancelled { + do { + let service = try makeService(config) + guard let subscriptionId = try await service.openHABcreateSubscription() else { + throw SitemapSseError.unsupported + } + Logger.restAPI.info("Sitemap SSE subscription created: \(subscriptionId, privacy: .public)") + + let eventStream = try await service.openHABSitemapWidgetEvents( + subscriptionid: subscriptionId, + sitemap: target.sitemap, + pageId: target.pageId + ) + + broadcast(.connected) + backoff = 1 // Reset backoff on successful connection + lastEventTime = .now + + let watchdog = Task { [weak self] in + while !Task.isCancelled { + try? await Task.sleep(for: .seconds(10)) + guard !Task.isCancelled else { return } + guard let self else { return } + await checkAliveWatchdog() + } + } + defer { watchdog.cancel() } + + for try await message in eventStream { + lastEventTime = .now + broadcast(.event(message)) + } + + // Stream ended unexpectedly; trigger reconnect. + throw URLError(.networkConnectionLost) + } catch is CancellationError { + return + } catch { + broadcast(.disconnected(error)) + Logger.restAPI.error("Sitemap SSE error: \(error.localizedDescription, privacy: .public) – retrying in \(backoff, privacy: .public)s") + try? await Task.sleep(for: .seconds(backoff)) + backoff = min(backoff * 2, maxBackoff) + } + } + } + + /// Restarts the SSE connection if no events have been received for 30 seconds. + /// The server sends ALIVE events every ~10 seconds, so 30 seconds without any + /// event indicates a silently dead connection (e.g. after a server restart). + private func checkAliveWatchdog() { + guard Date.now.timeIntervalSince(lastEventTime) > 30 else { return } + Logger.restAPI.warning("Sitemap SSE watchdog: no events for 30s, reconnecting") + restartListeningIfPossible() + } + + private func broadcast(_ msg: StreamOutput) { + continuations.values.forEach { $0.yield(msg) } + } +} diff --git a/OpenHABCore/Tests/OpenHABCoreTests/NetworkTrackerTests.swift b/OpenHABCore/Tests/OpenHABCoreTests/NetworkTrackerTests.swift index 6c5ae6a8b..de7ea57f6 100644 --- a/OpenHABCore/Tests/OpenHABCoreTests/NetworkTrackerTests.swift +++ b/OpenHABCore/Tests/OpenHABCoreTests/NetworkTrackerTests.swift @@ -101,6 +101,21 @@ final actor MockOpenAPIService: OpenAPIServiceProtocol { } return mockServerProperties } + + // swiftlint:disable async_without_await + func openHABcreateSubscription() async throws -> String? { + nil + } + + func openHABSitemapWidgetEvents(subscriptionid: String, sitemap: String, pageId: String) + async throws -> any AsyncSequence & Sendable { + AsyncThrowingStream { $0.finish() } + } + + func openHABEvents(topics: String?) async throws -> any AsyncSequence & Sendable { + AsyncThrowingStream { $0.finish() } + } + // swiftlint:enable async_without_await } actor PathMonitor { diff --git a/OpenHABCore/Tests/OpenHABCoreTests/NumberStateTests.swift b/OpenHABCore/Tests/OpenHABCoreTests/NumberStateTests.swift index 546ae5ce2..f88d440fe 100644 --- a/OpenHABCore/Tests/OpenHABCoreTests/NumberStateTests.swift +++ b/OpenHABCore/Tests/OpenHABCoreTests/NumberStateTests.swift @@ -194,7 +194,8 @@ struct NumberStateTests { let col1 = try #require("Uninitialized".parseAsUIColor()) let col2 = UIColor(hue: 0, saturation: 0, brightness: 0, alpha: 1.0) - #expect(col1.equals(col2)) + let unwrappedCol1 = try #require(col1) + #expect(unwrappedCol1.equals(col2)) #expect("360,100,100".parseAsUIColor() == UIColor( hue: CGFloat(state: "360", divisor: 360), saturation: CGFloat(state: "100", divisor: 100), diff --git a/OpenHABCore/Tests/OpenHABCoreTests/OpenHABWidgetEventApplicationTests.swift b/OpenHABCore/Tests/OpenHABCoreTests/OpenHABWidgetEventApplicationTests.swift new file mode 100644 index 000000000..5ff8f0bdd --- /dev/null +++ b/OpenHABCore/Tests/OpenHABCoreTests/OpenHABWidgetEventApplicationTests.swift @@ -0,0 +1,161 @@ +// Copyright (c) 2010-2026 Contributors to the openHAB project +// +// See the NOTICE file(s) distributed with this work for additional +// information. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0 +// +// SPDX-License-Identifier: EPL-2.0 + +@testable import OpenHABCore +import Testing + +struct OpenHABWidgetEventApplicationTests { + @Test + func itemOnlySitemapEventUpdatesWidgetState() { + let widget = OpenHABWidget( + widgetId: "0600", + label: "Light", + icon: "light", + type: .switchWidget, + url: nil, + period: nil, + minValue: nil, + maxValue: nil, + step: nil, + refresh: nil, + height: nil, + isLeaf: nil, + iconColor: nil, + labelColor: nil, + valueColor: nil, + service: nil, + state: "ON", + text: nil, + legend: nil, + inputHint: nil, + encoding: nil, + item: item(state: "ON"), + linkedPage: nil, + mappings: [], + widgets: [], + visibility: true, + switchSupport: true, + forceAsItem: nil + ) + + let result = widget.apply(event: OpenHABSitemapWidgetEvent( + widgetId: "0600", + enrichedItem: item(state: "OFF") + )) + + #expect(result == .applied) + #expect(widget.state == "OFF") + #expect(widget.item?.state == "OFF") + #expect(widget.displayState.effectiveState == "OFF") + } + + @Test + func reloadIconSitemapEventIsAppliedWithoutPageReload() { + let widget = OpenHABWidget( + widgetId: "0600", + label: "Light", + icon: "light", + type: .switchWidget, + url: nil, + period: nil, + minValue: nil, + maxValue: nil, + step: nil, + refresh: nil, + height: nil, + isLeaf: nil, + iconColor: nil, + labelColor: nil, + valueColor: nil, + service: nil, + state: "ON", + text: nil, + legend: nil, + inputHint: nil, + encoding: nil, + item: item(state: "ON"), + linkedPage: nil, + mappings: [], + widgets: [], + visibility: true, + switchSupport: true, + forceAsItem: nil + ) + + // reloadIcon:true must NOT trigger a page reload — the server sends it on + // virtually every SSE event (icon URL may change with state), so treating it + // as a reload trigger causes a reload storm. Only descriptionChanged:true + // warrants a full reload. + let result = widget.apply(event: OpenHABSitemapWidgetEvent( + widgetId: "0600", + reloadIcon: true + )) + + #expect(result == .unchanged) + } + + @Test + func descriptionChangedSitemapEventRequiresPageReload() { + let widget = OpenHABWidget( + widgetId: "0600", + label: "Light", + icon: "light", + type: .switchWidget, + url: nil, + period: nil, + minValue: nil, + maxValue: nil, + step: nil, + refresh: nil, + height: nil, + isLeaf: nil, + iconColor: nil, + labelColor: nil, + valueColor: nil, + service: nil, + state: "ON", + text: nil, + legend: nil, + inputHint: nil, + encoding: nil, + item: item(state: "ON"), + linkedPage: nil, + mappings: [], + widgets: [], + visibility: true, + switchSupport: true, + forceAsItem: nil + ) + + let result = widget.apply(event: OpenHABSitemapWidgetEvent( + widgetId: "0600", + descriptionChanged: true + )) + + #expect(result == .requiresPageReload) + } + + private func item(state: String) -> OpenHABItem { + OpenHABItem( + name: "LightItem", + type: "Switch", + state: state, + link: "", + label: "Light", + groupType: nil, + stateDescription: nil, + commandDescription: nil, + members: [], + category: nil, + options: nil + ) + } +} diff --git a/openHAB.xcodeproj/project.pbxproj b/openHAB.xcodeproj/project.pbxproj index 25edfc3fe..351d083da 100644 --- a/openHAB.xcodeproj/project.pbxproj +++ b/openHAB.xcodeproj/project.pbxproj @@ -1707,7 +1707,7 @@ SWIFT_PRECOMPILE_BRIDGING_HEADER = NO; TARGETED_DEVICE_FAMILY = "1,2"; TEST_TARGET_NAME = openHAB; - WATCHOS_DEPLOYMENT_TARGET = 8.0; + WATCHOS_DEPLOYMENT_TARGET = 11.0; }; name = Debug; }; @@ -2167,7 +2167,7 @@ SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; - WATCHOS_DEPLOYMENT_TARGET = 9.0; + WATCHOS_DEPLOYMENT_TARGET = 11.0; }; name = Debug; }; @@ -2245,7 +2245,7 @@ TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; - WATCHOS_DEPLOYMENT_TARGET = 9.0; + WATCHOS_DEPLOYMENT_TARGET = 11.0; }; name = Release; }; diff --git a/openHAB/Models/SitemapPageViewModel+Commands.swift b/openHAB/Models/SitemapPageViewModel+Commands.swift new file mode 100644 index 000000000..27ab09fdf --- /dev/null +++ b/openHAB/Models/SitemapPageViewModel+Commands.swift @@ -0,0 +1,214 @@ +// Copyright (c) 2010-2026 Contributors to the openHAB project +// +// See the NOTICE file(s) distributed with this work for additional +// information. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0 +// +// SPDX-License-Identifier: EPL-2.0 + +import OpenHABCore +import os.log +import UIKit + +private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "org.openhab.app", category: "SitemapPageViewModel") + +@MainActor +extension SitemapPageViewModel { + func sendCommand(_ command: String?, + for widget: OpenHABWidget, + policy: WidgetCommandPolicy = .immediate, + phase: WidgetCommandPhase = .change, + key: String? = nil, + fallbackItem: OpenHABItem? = nil) { + commandDispatcher.send( + command, + for: widget, + policy: policy, + phase: phase, + key: key, + fallbackItem: fallbackItem + ) + } + + func cancelPendingCommand(for widget: OpenHABWidget, key: String? = nil) { + commandDispatcher.cancelPending(for: widget, key: key) + } + + func cancelPendingCommand(for item: OpenHABItem, key: String? = nil) { + commandDispatcher.cancelPending(for: item, key: key) + } + + func cancelPendingCommand(for itemname: String, key: String? = nil) { + commandDispatcher.cancelPending(for: itemname, key: key) + if key == nil { + queuedCommands.removeValue(forKey: itemname) + clearSliderOverride(for: itemname) + if case .queued = commandStates[itemname] { + setCommandState(.idle, for: itemname) + } + } + } + + func sendCommand(_ command: String?, + for itemname: String, + policy: WidgetCommandPolicy = .immediate, + phase: WidgetCommandPhase = .change, + key: String? = nil) { + commandDispatcher.send( + command, + for: itemname, + policy: policy, + phase: phase, + key: key + ) { [weak self] itemname, command in + self?.sendCommand(itemname: itemname, command: command, origin: .command) + } + } + + func sendCommand(_ item: OpenHABItem?, commandToSend command: String?) { + commandDispatcher.send(command, for: item, policy: .immediate, phase: .change) { [weak self] itemname, command in + self?.sendCommand(itemname: itemname, command: command, origin: .command) + } + } + + func sendCommand(itemname: String, command: String) { + sendCommand(itemname: itemname, command: command, origin: .command) + } + + func sendToUpdate(item: OpenHABItem?, + state: NumberState?, + policy: WidgetCommandPolicy = .immediate, + phase: WidgetCommandPhase = .change, + key: String? = nil) { + guard let item, let state else { + logger.info("ItemUpdate for Item or State = nil") + return + } + let command = state.commandString + commandDispatcher.send(command, for: item, policy: policy, phase: phase, key: key) { [weak self] itemname, command in + self?.sendCommand(itemname: itemname, command: command, origin: .update) + } + } + + func sendToUpdate(itemname: String, + state: NumberState?, + policy: WidgetCommandPolicy = .immediate, + phase: WidgetCommandPhase = .change, + key: String? = nil) { + guard !itemname.isEmpty, let state else { + logger.info("ItemUpdate for itemname or state = nil") + return + } + let command = state.commandString + commandDispatcher.send(command, for: itemname, policy: policy, phase: phase, key: key) { [weak self] itemname, command in + self?.sendCommand(itemname: itemname, command: command, origin: .update) + } + } + + func flushQueuedCommands() { + guard trackerStatus == .connected, !queuedCommands.isEmpty else { return } + let queued = queuedCommands + queuedCommands.removeAll() + for (itemname, queuedCommand) in queued { + guard commandStateVersions[itemname] == queuedCommand.version else { continue } + sendCommandNow(itemname: itemname, command: queuedCommand.command, version: queuedCommand.version) + } + } + + func sendCommand(itemname: String, command: String, origin: CommandSendOrigin) { + logger.debug("Dispatching command origin=\(origin.rawValue, privacy: .public), item=\(itemname, privacy: .public), command=\(command, privacy: .private(mask: .hash))") + let version = nextCommandVersion(for: itemname) + if trackerStatus != .connected { + queuedCommands[itemname] = QueuedCommand(command: command, version: version) + setCommandState(.queued, for: itemname) + return + } + sendCommandNow(itemname: itemname, command: command, version: version) + } + + func sendCommandNow(itemname: String, command: String, version: Int) { + setCommandState(.sending, for: itemname) + let deviceId = UIDevice.current.identifierForVendor?.uuidString + let sourcePrefix = pageId.isEmpty ? nil : "org.openhab.ui.basic$\(defaultSitemap):\(pageId)" + Task { [weak self] in + guard let self else { return } + do { + try await openAPIService?.sendItemCommand( + itemname: itemname, + command: command, + sourcePrefix: sourcePrefix, + deviceId: deviceId + ) + logger.info("Successfully sent command \(command, privacy: .private(mask: .hash)) to \(itemname, privacy: .public)") + handleCommandSuccess(for: itemname, version: version) + } catch { + logger.info("Failed to send command \(command, privacy: .private(mask: .hash)) to \(itemname, privacy: .public): \(error.localizedDescription, privacy: .public)") + handleCommandFailure(for: itemname, version: version, errorDescription: error.localizedDescription) + } + } + } +} + +@MainActor +extension SitemapPageViewModel { + func nextCommandVersion(for itemname: String) -> Int { + let newVersion = (commandStateVersions[itemname] ?? 0) + 1 + commandStateVersions[itemname] = newVersion + return newVersion + } + + func setCommandState(_ state: WidgetCommandLifecycleState, for itemname: String) { + commandStateResetTasks[itemname]?.cancel() + commandStateResetTasks[itemname] = nil + + switch state { + case .idle: + commandStates.removeValue(forKey: itemname) + case .queued, .sending, .failed: + commandStates[itemname] = state + } + } + + func handleCommandSuccess(for itemname: String, version: Int) { + guard commandStateVersions[itemname] == version else { return } + scheduleCommandStateReset(for: itemname, version: version, after: .milliseconds(450)) + scheduleSliderOverrideResetFallback(for: itemname, version: version, after: .seconds(1)) + } + + func handleCommandFailure(for itemname: String, version: Int, errorDescription: String) { + guard commandStateVersions[itemname] == version else { return } + clearSliderOverride(for: itemname) + setCommandState(.failed(message: errorDescription), for: itemname) + } + + func scheduleCommandStateReset(for itemname: String, version: Int, after delay: Duration) { + commandStateResetTasks[itemname]?.cancel() + commandStateResetTasks[itemname] = Task { @MainActor [weak self] in + try? await Task.sleep(for: delay) + guard let self else { return } + guard commandStateVersions[itemname] == version else { return } + setCommandState(.idle, for: itemname) + } + } + + func scheduleSliderOverrideResetFallback(for itemname: String, version: Int, after delay: Duration) { + sliderOverrideResetTasks[itemname]?.cancel() + sliderOverrideResetTasks[itemname] = Task { @MainActor [weak self] in + try? await Task.sleep(for: delay) + guard let self else { return } + guard commandStateVersions[itemname] == version else { return } + clearSliderOverride(for: itemname) + } + } + + func clearSliderOverride(for itemname: String) { + guard sliderValueOverrides[itemname] != nil else { return } + sliderOverrideResetTasks[itemname]?.cancel() + sliderOverrideResetTasks[itemname] = nil + objectWillChange.send() + sliderValueOverrides.removeValue(forKey: itemname) + } +} diff --git a/openHAB/Models/SitemapPageViewModel+SSE.swift b/openHAB/Models/SitemapPageViewModel+SSE.swift new file mode 100644 index 000000000..50722fa4f --- /dev/null +++ b/openHAB/Models/SitemapPageViewModel+SSE.swift @@ -0,0 +1,159 @@ +// Copyright (c) 2010-2026 Contributors to the openHAB project +// +// See the NOTICE file(s) distributed with this work for additional +// information. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0 +// +// SPDX-License-Identifier: EPL-2.0 + +import OpenHABCore +import os.log + +private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "org.openhab.app", category: "SitemapPageViewModel") + +@MainActor +extension SitemapPageViewModel { + func shouldUseSSE() -> Bool { + ssePreferred && serverProperties?.hasSseSupport() == true + } + + func startSSE(sitemap: String, pageId: String) async { + sseConnected = false + sseNeedsRefreshOnReconnect = false + sseStreamTask?.cancel() + + logger.info("Starting sitemap SSE for \(sitemap, privacy: .public)/\(pageId, privacy: .public)") + await sitemapEventStream.startMonitoringNetworkIfNeeded(initialConnection: networkTracker.activeConnection) + let stream = await sitemapEventStream.stream(sitemap: sitemap, pageId: pageId) + + sseStreamTask = Task { [weak self] in + guard let self else { return } + for await msg in stream { + guard !Task.isCancelled else { break } + handleSseOutput(msg) + } + } + } + + func handleSseOutput(_ msg: StreamOutput) { + switch msg { + case .connected: + logger.info("Sitemap SSE connected") + sseConnected = true + ssePreferred = true + isUpdating = false + if sseNeedsRefreshOnReconnect { + sseNeedsRefreshOnReconnect = false + logger.info("Sitemap SSE reconnected, refreshing page to recover missed events") + startPageHandling( + forceRestart: true, + reason: "sse-reconnected", + preserveCurrentContent: true, + recreateService: false + ) + } + case let .disconnected(error): + logger.warning("Sitemap SSE disconnected: \(error?.localizedDescription ?? "nil", privacy: .public)") + isUpdating = false + guard shouldFallbackToLongPolling(after: error) else { + sseNeedsRefreshOnReconnect = true + return + } + startLongPollingFallback(error) + case let .event(message): + handleSseMessage(message) + } + } + + func shouldFallbackToLongPolling(after error: (any Error)?) -> Bool { + guard let error else { return !sseConnected } + if let sseError = error as? SitemapSseError { + switch sseError { + case .unsupported: + return true + } + } + return !sseConnected + } + + func startLongPollingFallback(_ error: (any Error)?) { + ssePreferred = false + sseStreamTask?.cancel() + sseStreamTask = nil + Task { + await sitemapEventStream.stop() + } + + if let error { + logger.warning("SSE unavailable (\(error.localizedDescription, privacy: .public)), falling back to long polling") + } else { + logger.warning("SSE unavailable, falling back to long polling") + } + startPageHandling( + forceRestart: true, + reason: "sse-fallback", + preserveCurrentContent: true, + recreateService: false + ) + } + + func handleSseMessage(_ message: SitemapEventMessage) { + switch message { + case .alive: + return + case let .sitemapChanged(sitemap, pageId): + logger.info("SSE sitemap changed \(sitemap.orEmpty, privacy: .public)/\(pageId.orEmpty, privacy: .public)") + startPageHandling( + forceRestart: true, + reason: "sse-sitemap-changed", + preserveCurrentContent: true, + recreateService: false + ) + case let .widget(event): + applySseWidgetEvent(event) + case let .unknown(raw): + logger.debug("SSE unknown event: \(raw, privacy: .public)") + } + } + + func applySseWidgetEvent(_ event: OpenHABSitemapWidgetEvent) { + guard let currentPage else { return } + guard let widgetId = event.widgetId else { return } + + if widgetId == pageId, let label = event.label { + objectWillChange.send() + currentPage.title = label + return + } + + switch currentPage.apply(event: event) { + case .unchanged: + isUpdating = false + case .applied: + objectWillChange.send() + widgetUpdateVersions[widgetId, default: 0] += 1 + _ = clearSyncedSliderOverrides(using: currentPage.widgets) + rebuildRowInputs() + isUpdating = false + case .requiresPageReload: + logger.info("SSE widget \(widgetId, privacy: .public) requires full sitemap reload") + startPageHandling( + forceRestart: true, + reason: "sse-widget-reload-required", + preserveCurrentContent: true, + recreateService: false + ) + case .notFound: + logger.info("SSE widget \(widgetId, privacy: .public) not found, reloading sitemap") + startPageHandling( + forceRestart: true, + reason: "sse-widget-missing", + preserveCurrentContent: true, + recreateService: false + ) + } + } +} diff --git a/openHAB/Models/SitemapPageViewModel.swift b/openHAB/Models/SitemapPageViewModel.swift index 5b4263cb1..78ebf53ca 100644 --- a/openHAB/Models/SitemapPageViewModel.swift +++ b/openHAB/Models/SitemapPageViewModel.swift @@ -31,37 +31,43 @@ class SitemapPageViewModel: ObservableObject { @Published var isUpdating = false @Published var openHABRootUrl: String? @Published var showSearchField = false - @Published private(set) var commandStates: [String: WidgetCommandLifecycleState] = [:] + @Published var commandStates: [String: WidgetCommandLifecycleState] = [:] @Published private(set) var trackerStatus: NetworkStatus = .stopped - @Published private(set) var widgetUpdateVersions: [String: Int] = [:] + @Published var widgetUpdateVersions: [String: Int] = [:] @Published private(set) var rowInputs: [SitemapRowInput] = [] @Published var navigationPath: [LinkedPageNavigation] = [] let networkTracker = MainActorNetworkTracker.shared - private var openAPIService: OpenAPIService? + var openAPIService: OpenAPIService? private var activeConnectionInfo: ConnectionInfo? + var serverProperties: OpenHABServerProperties? private var pageHandlingTask: Task? + var sseStreamTask: Task? private var foregroundRefreshTask: Task? private var connectionObserverTask: Task? private var networkStatusObserverTask: Task? - private let commandDispatcher = WidgetCommandDispatcher() - private var defaultSitemap = "" + let commandDispatcher = WidgetCommandDispatcher() + let sitemapEventStream = SitemapEventStream() + var defaultSitemap = "" private var defaultSitemapLabel = "" private var fallbackTitle = "" @Published var pageId = "" private var isLinkedPage = false private var pageNetworkStatus: NetworkStatus? private var pageNetworkStatusAvailable = false + var sseConnected = false + var sseNeedsRefreshOnReconnect = false + var ssePreferred = true private var activePageHandlingKey: String? private var activePageHandlingID: UUID? - private var commandStateResetTasks: [String: Task] = [:] - private var commandStateVersions: [String: Int] = [:] - private var queuedCommands: [String: QueuedCommand] = [:] + var commandStateResetTasks: [String: Task] = [:] + var commandStateVersions: [String: Int] = [:] + var queuedCommands: [String: QueuedCommand] = [:] private var rowWidgetIndex: [RowID: OpenHABWidget] = [:] private var previousBuildRenderKeys: [WidgetRenderKey] = [] private var previousBuildRowIDs: [RowID] = [] - private var sliderValueOverrides: [String: Double] = [:] - private var sliderOverrideResetTasks: [String: Task] = [:] + var sliderValueOverrides: [String: Double] = [:] + var sliderOverrideResetTasks: [String: Task] = [:] var lastForegroundRefreshAt: Date = .distantPast private var isPageVisibleForRefresh = false private var lastUIUpdateAt: Date = .distantPast @@ -235,6 +241,9 @@ class SitemapPageViewModel: ObservableObject { networkStatusObserverTask?.cancel() foregroundObserverTask?.cancel() pageHandlingTask?.cancel() + sseStreamTask?.cancel() + let stream = sitemapEventStream + Task { await stream.stop() } foregroundRefreshTask?.cancel() longPollDebounceTask?.cancel() commandStateResetTasks.values.forEach { $0.cancel() } @@ -277,6 +286,10 @@ extension SitemapPageViewModel { isPageVisibleForRefresh = false pageHandlingTask?.cancel() pageHandlingTask = nil + sseStreamTask?.cancel() + sseStreamTask = nil + let stream = sitemapEventStream + Task { await stream.stop() } foregroundRefreshTask?.cancel() foregroundRefreshTask = nil longPollDebounceTask?.cancel() @@ -335,12 +348,17 @@ extension SitemapPageViewModel { } pageHandlingTask?.cancel() + sseStreamTask?.cancel() + sseStreamTask = nil longPollDebounceTask?.cancel() longPollDebounceTask = nil pendingLongPollPage = nil lastUIUpdateAt = .distantPast coalescedLongPollUpdateCount = 0 error = nil // Clear any previous errors when starting a new page handling session + if reason != "sse-fallback" { + ssePreferred = true + } if preserveCurrentContent, currentPage != nil { isLoading = false isUpdating = true @@ -407,6 +425,9 @@ extension SitemapPageViewModel { diagnostics.beginServiceSetup(connection: activeConnection.configuration) try setupServiceIfNeeded(activeConnection: activeConnection, forceRecreate: recreateService) + if serverProperties == nil { + serverProperties = try? await openAPIService?.getRoot() + } if defaultSitemapLabel.isEmpty { diagnostics.beginLabelFetch() @@ -463,7 +484,13 @@ extension SitemapPageViewModel { page: page, rowCount: rowInputs.count ) - logger.info("Sitemap pipeline initial load completed") + if shouldUseSSE() { + logger.info("Using SSE for sitemap updates") + let ssePageId = currentPage?.pageId.isEmpty == false ? currentPage!.pageId : defaultSitemap + await startSSE(sitemap: defaultSitemap, pageId: ssePageId) + return // SSE takes over; stream task cancelled via onTermination + } + logger.info("Using long polling for sitemap updates") case let .longPoll(page, requestMs): let responseGapMs = lastResponseAt.map { Int((responseAt.timeIntervalSince($0) * 1000).rounded()) } SitemapDiagnostics.logLongPoll( @@ -517,6 +544,7 @@ extension SitemapPageViewModel { } activeConnectionInfo = activeConnection openHABRootUrl = activeConnection.configuration.url + serverProperties = nil return activeConnection } @@ -700,7 +728,7 @@ extension SitemapPageViewModel { } } - private func clearSyncedSliderOverrides(using widgets: [OpenHABWidget]) -> Int { + func clearSyncedSliderOverrides(using widgets: [OpenHABWidget]) -> Int { guard !sliderValueOverrides.isEmpty else { return 0 } var cleared = 0 @@ -757,6 +785,7 @@ extension SitemapPageViewModel { activeConnectionInfo = activeConnection openAPIService = try makeSitemapService(for: activeConnection) + serverProperties = try? await openAPIService?.getRoot() } private func loadCurrentPage() async throws { @@ -817,6 +846,7 @@ extension SitemapPageViewModel { navigationPath = [] pendingLinkedPageNavigation = nil error = nil + ssePreferred = true } @MainActor @@ -941,7 +971,6 @@ extension SitemapPageViewModel { return true } - // swiftlint:disable:next async_without_await private func handleActiveConnection(_ connection: ConnectionInfo) async { let previousURL = activeConnectionInfo?.configuration.url let newURL = connection.configuration.url @@ -950,10 +979,15 @@ extension SitemapPageViewModel { // Save the active connection information activeConnectionInfo = connection openHABRootUrl = newURL + if connectionDidChange { + serverProperties = nil + } + ssePreferred = true do { // Setup the OpenAPI service based on the new connection openAPIService = try makeSitemapService(for: connection) + serverProperties = try? await openAPIService?.getRoot() // Restart when connection changed, or when polling is currently inactive. let shouldRestart = connectionDidChange || pageHandlingTask == nil @@ -968,6 +1002,7 @@ extension SitemapPageViewModel { // swiftlint:disable:next async_without_await func selectSitemap() async { + ssePreferred = true startPageHandling(forceRestart: true, reason: "select-sitemap") } @@ -979,201 +1014,4 @@ extension SitemapPageViewModel { serviceConfiguration: .longTerm ) } - - // MARK: - Command Sending - - func sendCommand(_ command: String?, - for widget: OpenHABWidget, - policy: WidgetCommandPolicy = .immediate, - phase: WidgetCommandPhase = .change, - key: String? = nil, - fallbackItem: OpenHABItem? = nil) { - commandDispatcher.send( - command, - for: widget, - policy: policy, - phase: phase, - key: key, - fallbackItem: fallbackItem - ) - } - - func cancelPendingCommand(for widget: OpenHABWidget, key: String? = nil) { - commandDispatcher.cancelPending(for: widget, key: key) - } - - func cancelPendingCommand(for item: OpenHABItem, key: String? = nil) { - commandDispatcher.cancelPending(for: item, key: key) - } - - func cancelPendingCommand(for itemname: String, key: String? = nil) { - commandDispatcher.cancelPending(for: itemname, key: key) - if key == nil { - queuedCommands.removeValue(forKey: itemname) - clearSliderOverride(for: itemname) - if case .queued = commandStates[itemname] { - setCommandState(.idle, for: itemname) - } - } - } - - func sendCommand(_ command: String?, - for itemname: String, - policy: WidgetCommandPolicy = .immediate, - phase: WidgetCommandPhase = .change, - key: String? = nil) { - commandDispatcher.send( - command, - for: itemname, - policy: policy, - phase: phase, - key: key - ) { [weak self] itemname, command in - self?.sendCommand(itemname: itemname, command: command, origin: .command) - } - } - - func sendCommand(_ item: OpenHABItem?, commandToSend command: String?) { - commandDispatcher.send(command, for: item, policy: .immediate, phase: .change) { [weak self] itemname, command in - self?.sendCommand(itemname: itemname, command: command, origin: .command) - } - } - - func sendCommand(itemname: String, command: String) { - sendCommand(itemname: itemname, command: command, origin: .command) - } - - private func sendCommand(itemname: String, command: String, origin: CommandSendOrigin) { - logger.debug("Dispatching command origin=\(origin.rawValue, privacy: .public), item=\(itemname, privacy: .public), command=\(command, privacy: .private(mask: .hash))") - let version = nextCommandVersion(for: itemname) - if trackerStatus != .connected { - queuedCommands[itemname] = QueuedCommand(command: command, version: version) - setCommandState(.queued, for: itemname) - return - } - sendCommandNow(itemname: itemname, command: command, version: version) - } - - private func sendCommandNow(itemname: String, command: String, version: Int) { - setCommandState(.sending, for: itemname) - let deviceId = UIDevice.current.identifierForVendor?.uuidString - let sourcePrefix = pageId.isEmpty ? nil : "org.openhab.ui.basic$\(defaultSitemap):\(pageId)" - Task { [weak self] in - guard let self else { return } - do { - try await openAPIService?.sendItemCommand( - itemname: itemname, - command: command, - sourcePrefix: sourcePrefix, - deviceId: deviceId - ) - logger.info("Successfully sent command \(command, privacy: .private(mask: .hash)) to \(itemname, privacy: .public)") - handleCommandSuccess(for: itemname, version: version) - } catch { - logger.info("Failed to send command \(command, privacy: .private(mask: .hash)) to \(itemname, privacy: .public): \(error.localizedDescription, privacy: .public)") - handleCommandFailure(for: itemname, version: version, errorDescription: error.localizedDescription) - } - } - } - - private func flushQueuedCommands() { - guard trackerStatus == .connected, !queuedCommands.isEmpty else { return } - let queued = queuedCommands - queuedCommands.removeAll() - for (itemname, queuedCommand) in queued { - guard commandStateVersions[itemname] == queuedCommand.version else { continue } - sendCommandNow(itemname: itemname, command: queuedCommand.command, version: queuedCommand.version) - } - } - - func sendToUpdate(item: OpenHABItem?, - state: NumberState?, - policy: WidgetCommandPolicy = .immediate, - phase: WidgetCommandPhase = .change, - key: String? = nil) { - guard let item, let state else { - logger.info("ItemUpdate for Item or State = nil") - return - } - let command = state.commandString - commandDispatcher.send(command, for: item, policy: policy, phase: phase, key: key) { [weak self] itemname, command in - self?.sendCommand(itemname: itemname, command: command, origin: .update) - } - } - - func sendToUpdate(itemname: String, - state: NumberState?, - policy: WidgetCommandPolicy = .immediate, - phase: WidgetCommandPhase = .change, - key: String? = nil) { - guard !itemname.isEmpty, let state else { - logger.info("ItemUpdate for itemname or state = nil") - return - } - let command = state.commandString - commandDispatcher.send(command, for: itemname, policy: policy, phase: phase, key: key) { [weak self] itemname, command in - self?.sendCommand(itemname: itemname, command: command, origin: .update) - } - } -} - -@MainActor -private extension SitemapPageViewModel { - func nextCommandVersion(for itemname: String) -> Int { - let newVersion = (commandStateVersions[itemname] ?? 0) + 1 - commandStateVersions[itemname] = newVersion - return newVersion - } - - func setCommandState(_ state: WidgetCommandLifecycleState, for itemname: String) { - commandStateResetTasks[itemname]?.cancel() - commandStateResetTasks[itemname] = nil - - switch state { - case .idle: - commandStates.removeValue(forKey: itemname) - case .queued, .sending, .failed: - commandStates[itemname] = state - } - } - - func handleCommandSuccess(for itemname: String, version: Int) { - guard commandStateVersions[itemname] == version else { return } - scheduleCommandStateReset(for: itemname, version: version, after: .milliseconds(450)) - scheduleSliderOverrideResetFallback(for: itemname, version: version, after: .seconds(1)) - } - - func handleCommandFailure(for itemname: String, version: Int, errorDescription: String) { - guard commandStateVersions[itemname] == version else { return } - clearSliderOverride(for: itemname) - setCommandState(.failed(message: errorDescription), for: itemname) - } - - func scheduleCommandStateReset(for itemname: String, version: Int, after delay: Duration) { - commandStateResetTasks[itemname]?.cancel() - commandStateResetTasks[itemname] = Task { @MainActor [weak self] in - try? await Task.sleep(for: delay) - guard let self else { return } - guard commandStateVersions[itemname] == version else { return } - setCommandState(.idle, for: itemname) - } - } - - func scheduleSliderOverrideResetFallback(for itemname: String, version: Int, after delay: Duration) { - sliderOverrideResetTasks[itemname]?.cancel() - sliderOverrideResetTasks[itemname] = Task { @MainActor [weak self] in - try? await Task.sleep(for: delay) - guard let self else { return } - guard commandStateVersions[itemname] == version else { return } - clearSliderOverride(for: itemname) - } - } - - func clearSliderOverride(for itemname: String) { - guard sliderValueOverrides[itemname] != nil else { return } - sliderOverrideResetTasks[itemname]?.cancel() - sliderOverrideResetTasks[itemname] = nil - objectWillChange.send() - sliderValueOverrides.removeValue(forKey: itemname) - } } diff --git a/openHAB/Supporting Files/Localizable.xcstrings b/openHAB/Supporting Files/Localizable.xcstrings index a31b6c001..60103cc9a 100644 --- a/openHAB/Supporting Files/Localizable.xcstrings +++ b/openHAB/Supporting Files/Localizable.xcstrings @@ -2839,6 +2839,7 @@ }, "Clock Size: %@" : { "comment" : "A label describing the percentage of the screen that is used for the clock face.", + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -2896,6 +2897,10 @@ } } }, + "Clock Size: %lld %%" : { + "comment" : "A label that shows the current value of the clock size slider. The value is shown in percent.", + "isCommentAutoGenerated" : true + }, "closed" : { "extractionState" : "manual", "localizations" : { @@ -4263,7 +4268,7 @@ "fr" : { "stringUnit" : { "state" : "translated", - "value" : "En activant le rapport de d'erreurs, tu acceptes que les informations relatives à l'appareil et à son utilisation soient collectées et partagées avec Crashlytics (une entreprise Google). Pour plus d'informations, consulte notre politique de confidentialité." + "value" : "En activant le rapport de d’erreurs, tu acceptes que les informations relatives à l’appareil et à son utilisation soient collectées et partagées avec Crashlytics (une entreprise Google). Pour plus d’informations, consulte notre politique de confidentialité." } }, "it" : { @@ -5056,6 +5061,7 @@ }, "Dim Level: %@" : { "comment" : "A label displaying the current dim level of the screen saver. The value is shown as a percentage.", + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -5113,6 +5119,10 @@ } } }, + "Dim Level: %lld %%" : { + "comment" : "A label displaying the current brightness level of the screen. The argument is the brightness level as a percentage.", + "isCommentAutoGenerated" : true + }, "Dimmer or Roller Item" : { "comment" : "Display name for a dimmer or roller item type.", "isCommentAutoGenerated" : true, @@ -5170,6 +5180,24 @@ "state" : "translated", "value" : "Элемент диммера или ролеты" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément gradateur/volet" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento dimmer/tapparella" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dimmer/Roller-item" + } } } }, @@ -6968,8 +6996,8 @@ }, "fi" : { "stringUnit" : { - "state" : "translated", - "value" : "Koti" + "state" : "new", + "value" : "" } }, "fr" : { @@ -6986,8 +7014,8 @@ }, "nb" : { "stringUnit" : { - "state" : "translated", - "value" : "Hjem" + "state" : "new", + "value" : "" } }, "nl" : { @@ -10054,6 +10082,10 @@ } } }, + "None" : { + "comment" : "A label for the \"Sitemap for CarPlay\" picker.", + "isCommentAutoGenerated" : true + }, "notification" : { "localizations" : { "de" : { @@ -10240,6 +10272,42 @@ "value" : "Numerisches-Item" } }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément numérique" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento numerico" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Numeriek item" + } + } + } + }, + "off" : { + "comment" : "Label for the \"off\" state of a contact.", + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "aus" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "off" + } + }, "es" : { "stringUnit" : { "state" : "translated", @@ -10307,8 +10375,8 @@ }, "fi" : { "stringUnit" : { - "state" : "translated", - "value" : "Pois" + "state" : "new", + "value" : "" } }, "fr" : { @@ -10325,8 +10393,8 @@ }, "nb" : { "stringUnit" : { - "state" : "translated", - "value" : "Av" + "state" : "new", + "value" : "" } }, "nl" : { @@ -10337,8 +10405,8 @@ }, "ru" : { "stringUnit" : { - "state" : "translated", - "value" : "Выкл." + "state" : "new", + "value" : "" } } } @@ -10661,8 +10729,8 @@ }, "fi" : { "stringUnit" : { - "state" : "translated", - "value" : "Päällä" + "state" : "new", + "value" : "" } }, "fr" : { @@ -10679,8 +10747,8 @@ }, "nb" : { "stringUnit" : { - "state" : "translated", - "value" : "På" + "state" : "new", + "value" : "" } }, "nl" : { @@ -10691,8 +10759,8 @@ }, "ru" : { "stringUnit" : { - "state" : "translated", - "value" : "Вкл." + "state" : "new", + "value" : "" } } } @@ -11929,6 +11997,7 @@ }, "Relative Date: %@" : { "comment" : "A label describing the relative size of the date text compared to the clock text.", + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -11986,6 +12055,10 @@ } } }, + "Relative Date: %lld %%" : { + "comment" : "A label that shows the relative size of the date font.", + "isCommentAutoGenerated" : true + }, "Remote server" : { "comment" : "Header text for the remote server section in the connection settings view.", "localizations" : { @@ -12232,6 +12305,7 @@ }, "Restore Brightness: %@" : { "comment" : "A label under the slider that shows the current brightness level and allows the user to adjust it. The value shown is the brightness level as a percentage.", + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -12289,6 +12363,10 @@ } } }, + "Restore Brightness: %lld %%" : { + "comment" : "A label that shows the brightness level at which the screen saver restores the brightness of the device.", + "isCommentAutoGenerated" : true + }, "Restore Previous Brightness on Wake" : { "comment" : "A toggle that allows the user to restore the brightness level to its previous value when the device wakes up.", "localizations" : { @@ -15750,6 +15828,10 @@ } } }, + "Sitemap for CarPlay" : { + "comment" : "A label for selecting a sitemap to be used for CarPlay.", + "isCommentAutoGenerated" : true + }, "sitemap_settings" : { "localizations" : { "de" : { @@ -16270,7 +16352,7 @@ "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Le certificat SSL présenté par %1$@ pour %2$@ n'est pas valide. Veux-tu continuer ?" + "value" : "Le certificat SSL présenté par %1$@ pour %2$@ n’est pas valide. Veux-tu continuer ?" } }, "it" : { @@ -16328,7 +16410,7 @@ "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Le certificat SSL présenté par %1$@ pour %2$@ ne correspond pas à l'enregistrement. Veux-tu continuer ?" + "value" : "Le certificat SSL présenté par %1$@ pour %2$@ ne correspond pas à l’enregistrement. Veux-tu continuer ?" } }, "it" : { @@ -17374,6 +17456,37 @@ "To connect to your local openHAB server, please allow Local Network access when prompted. If you previously denied it, enable it in Settings → Privacy & Security → Local Network." : { "comment" : "A message displayed in the alert when the user tries to connect to a local openHAB server.", "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Um eine Verbindung zu deinem lokalen openHAB-Server herzustellen, erlauben bitte den Zugriff auf das lokale Netzwerk, wenn du dazu aufgefordert wirst. Wenn du es zuvor abgelehnt hast, aktiviere es unter Einstellungen → Datenschutz & Sicherheit → Lokales Netzwerk." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pour te connecter à ton serveur openHAB local, autorise l’accès au réseau local lorsque tu y es invité. Si tu l’as précédemment refusé, active-le dans Réglages → Confidentialité et sécurité → Réseau local." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Per connetterti al tuo server openHAB locale, consenti l'accesso alla rete locale quando richiesto. Se in precedenza hai negato l'accesso, abilitalo in Impostazioni → Privacy e sicurezza → Rete locale." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Om verbinding te maken met uw lokale openHAB-server, sta toegang tot het lokale netwerk toe wanneer hierom wordt gevraagd. Als u dit eerder heeft geweigerd, schakel het in via Instellingen → Privacy en beveiliging → Lokaal netwerk." + } + } + } + }, + "toggle" : { + "comment" : "Action to toggle a switch between on and off states", + "extractionState" : "stale", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { @@ -17442,14 +17555,14 @@ }, "es" : { "stringUnit" : { - "state" : "translated", - "value" : "Alternar" + "state" : "new", + "value" : "" } }, "fi" : { "stringUnit" : { - "state" : "translated", - "value" : "Vaihda" + "state" : "new", + "value" : "" } }, "fr" : { @@ -17478,8 +17591,8 @@ }, "ru" : { "stringUnit" : { - "state" : "translated", - "value" : "Переключить" + "state" : "new", + "value" : "" } } } diff --git a/openHAB/UI/OpenHABRootViewController.swift b/openHAB/UI/OpenHABRootViewController.swift index 4e0c900e9..e88c07eeb 100644 --- a/openHAB/UI/OpenHABRootViewController.swift +++ b/openHAB/UI/OpenHABRootViewController.swift @@ -193,14 +193,15 @@ class OpenHABRootViewController: UIViewController { } private func startSSEListening() { - Task { - await ItemEventStream.startMonitoringNetwork() - } Logger.viewController.debug("Starting SSE") streamTask = Task { [weak self] in guard let self else { return } + await ItemEventStream.startMonitoringNetwork( + initialConnection: NetworkTracker.shared.activeConnection + ) for await msg in await ItemEventStream.shared.stream() { - await MainActor.run { self.handleSSEMessage(msg) } + guard !Task.isCancelled else { break } + handleSSEMessage(msg) } } } diff --git a/openHAB/UI/ScreenSaver/ScreenSaverManager.swift b/openHAB/UI/ScreenSaver/ScreenSaverManager.swift index 47117e24c..23925e921 100644 --- a/openHAB/UI/ScreenSaver/ScreenSaverManager.swift +++ b/openHAB/UI/ScreenSaver/ScreenSaverManager.swift @@ -112,7 +112,7 @@ final class ScreenSaverManager: NSObject { overlay = UIWindow(windowScene: scene) overlay.frame = scene.coordinateSpace.bounds } else { - overlay = UIWindow(frame: (UIApplication.shared.connectedScenes.first as? UIWindowScene)?.screen.bounds ?? .zero) + overlay = UIWindow(frame: UIScreen.main.bounds) } overlay.windowLevel = .alert + 1 // ensure above status bar overlay.backgroundColor = .clear From 28455e2737351015c5c2389564a7565d0ce27448 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Sun, 5 Jul 2026 00:07:10 +0200 Subject: [PATCH 55/91] feat: add CarPlay support with sitemap-based favorites scene Adds a CarPlay scene that streams a designated sitemap (max 8 widgets) and allows toggling switches, sending button commands, and picking from selection lists directly from CarPlay. - CarPlaySceneDelegate: CPListTemplate scene with live long-poll stream, preference change observation via UserDefaults notification, and item command dispatch for Switch/Button/Selection widgets - AppDelegate: branches scene configuration for CPTemplateApplicationScene - Info.plist: UIApplicationSupportsMultipleScenes + CarPlay scene config - Entitlements: com.apple.developer.carplay-driving-task - Preferences/HomePreferences: sitemapForCarPlay field - SettingsView/SitemapSettingsView: CarPlay sitemap picker - DrawerView: steering wheel indicator; long-press to assign CarPlay sitemap without triggering navigation - LoggerExtension: carPlay log category Signed-off-by: Tim Mueller-Seydlitz --- .../OpenHABCore/Util/LoggerExtension.swift | 2 + .../OpenHABCore/Util/Preferences.swift | 2 + openHAB.xcodeproj/project.pbxproj | 4 + .../xcshareddata/swiftpm/Package.resolved | 6 +- openHAB/AppDelegate.swift | 8 +- openHAB/CarPlaySceneDelegate.swift | 189 ++++++++++++++++++ .../Supporting Files/Localizable.xcstrings | 43 +++- openHAB/Supporting Files/openHAB-Info.plist | 11 +- openHAB/Supporting Files/openHAB.entitlements | 2 + openHAB/UI/DrawerView.swift | 40 +++- .../UI/ScreenSaver/ScreenSaverManager.swift | 5 +- openHAB/UI/SettingsView/SettingsView.swift | 4 + .../UI/SettingsView/SitemapSettingsView.swift | 16 ++ 13 files changed, 316 insertions(+), 16 deletions(-) create mode 100644 openHAB/CarPlaySceneDelegate.swift diff --git a/OpenHABCore/Sources/OpenHABCore/Util/LoggerExtension.swift b/OpenHABCore/Sources/OpenHABCore/Util/LoggerExtension.swift index 4db12a34b..b97d795a0 100644 --- a/OpenHABCore/Sources/OpenHABCore/Util/LoggerExtension.swift +++ b/OpenHABCore/Sources/OpenHABCore/Util/LoggerExtension.swift @@ -19,6 +19,8 @@ public extension Logger { static let appDelegate = Logger(subsystem: subsystem, category: "AppDelegate") + static let carPlay = Logger(subsystem: subsystem, category: "CarPlay") + static let clientCert = Logger(subsystem: subsystem, category: "ClientCert") static let connectionFailureTracker = Logger(subsystem: subsystem, category: "ConnectionFailureTracker") diff --git a/OpenHABCore/Sources/OpenHABCore/Util/Preferences.swift b/OpenHABCore/Sources/OpenHABCore/Util/Preferences.swift index 3579d3bd6..a8c64ec65 100644 --- a/OpenHABCore/Sources/OpenHABCore/Util/Preferences.swift +++ b/OpenHABCore/Sources/OpenHABCore/Util/Preferences.swift @@ -103,6 +103,7 @@ public struct HomePreferences: Codable, Equatable { public var sitemapForWatchLabel = "watch" public var homeName = "Home#1" public var sseCommandItem = "" + public var sitemapForCarPlay = "" fileprivate init(id: UUID) { self.id = id @@ -132,6 +133,7 @@ public struct HomePreferences: Codable, Equatable { sitemapForWatchLabel = try container.decodeIfPresent(String.self, forKey: .sitemapForWatchLabel) ?? "watch" homeName = try container.decodeIfPresent(String.self, forKey: .homeName) ?? "Home#1" sseCommandItem = try container.decodeIfPresent(String.self, forKey: .sseCommandItem) ?? "" + sitemapForCarPlay = try container.decodeIfPresent(String.self, forKey: .sitemapForCarPlay) ?? "" } } diff --git a/openHAB.xcodeproj/project.pbxproj b/openHAB.xcodeproj/project.pbxproj index 351d083da..0a6654362 100644 --- a/openHAB.xcodeproj/project.pbxproj +++ b/openHAB.xcodeproj/project.pbxproj @@ -28,6 +28,7 @@ 657144962C30A16700C8A1F3 /* OpenHABCore in Frameworks */ = {isa = PBXBuildFile; productRef = 657144952C30A16700C8A1F3 /* OpenHABCore */; }; 73F094A3C17641738D6215CD /* SetSwitchItemIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD5E85B2F02C272003215C0 /* SetSwitchItemIntent.swift */; }; 791AFA47B71D4B6995369F3A /* GetItemStateIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4BF3512F0307A40082479C /* GetItemStateIntent.swift */; }; + 856AA30C33F548E58EA00F6C /* CarPlay.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F572C79726447B8B9C40B49 /* CarPlay.framework */; settings = {ATTRIBUTES = (Required, ); }; }; 934E592528F16EBA00162004 /* OpenHABCore in Frameworks */ = {isa = PBXBuildFile; productRef = 934E592428F16EBA00162004 /* OpenHABCore */; }; 934E592728F16EBA00162004 /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 934E592628F16EBA00162004 /* Kingfisher */; }; 937E4471270B36D000A98C26 /* OpenHABCore in Frameworks */ = {isa = PBXBuildFile; productRef = 937E4470270B36D000A98C26 /* OpenHABCore */; }; @@ -229,6 +230,7 @@ 657144502C1E438700C8A1F3 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; }; 657144522C1E438700C8A1F3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 657144972C30A3E300C8A1F3 /* NotificationService.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NotificationService.entitlements; sourceTree = ""; }; + 6F572C79726447B8B9C40B49 /* CarPlay.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CarPlay.framework; path = System/Library/Frameworks/CarPlay.framework; sourceTree = SDKROOT; }; 933D7F0422E7015000621A03 /* openHABUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = openHABUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 933D7F0622E7015000621A03 /* OpenHABUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenHABUITests.swift; sourceTree = ""; }; 933D7F0822E7015100621A03 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -522,6 +524,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 856AA30C33F548E58EA00F6C /* CarPlay.framework in Frameworks */, 6557AF922C039D140094D0C8 /* FirebaseMessaging in Frameworks */, DFB2622B18830A3600D3244D /* Foundation.framework in Frameworks */, DABB5E332D98972F009A4B8A /* SDWebImageSVGCoder in Frameworks */, @@ -731,6 +734,7 @@ isa = PBXGroup; children = ( 032C4C3A922FABCBEB20EAA3 /* AppIntents */, + 6F572C79726447B8B9C40B49 /* CarPlay.framework */, ); name = "Recovered References"; sourceTree = ""; diff --git a/openHAB.xcworkspace/xcshareddata/swiftpm/Package.resolved b/openHAB.xcworkspace/xcshareddata/swiftpm/Package.resolved index e89032708..871a6c8aa 100644 --- a/openHAB.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/openHAB.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "214cb25929c81b8374a9dceea32f4e1f453b6c9cd25f37b20276bc3568fcbc7b", + "originHash" : "7ae5b793473bab1cdc69b2dcfe6db385d53dd3bd95acd381742d850780088cc1", "pins" : [ { "identity" : "abseil-cpp-binary", @@ -168,8 +168,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-collections", "state" : { - "revision" : "a0cb0954ecb21e4e31b0070e6ed5674e8556685a", - "version" : "1.6.0" + "revision" : "7b847a3b7008b2dc2f47ca3110d8c782fb2e5c7e", + "version" : "1.3.0" } }, { diff --git a/openHAB/AppDelegate.swift b/openHAB/AppDelegate.swift index b47e81b3d..75d4b476a 100644 --- a/openHAB/AppDelegate.swift +++ b/openHAB/AppDelegate.swift @@ -10,6 +10,7 @@ // SPDX-License-Identifier: EPL-2.0 import AVFoundation +import CarPlay import Combine import Firebase import FirebaseMessaging @@ -233,7 +234,12 @@ extension Notification.Name { extension AppDelegate { func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { - UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + switch connectingSceneSession.role { + case .carTemplateApplication: + UISceneConfiguration(name: "CarPlay Configuration", sessionRole: connectingSceneSession.role) + default: + UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } } func applicationDidEnterBackground(_ application: UIApplication) {} diff --git a/openHAB/CarPlaySceneDelegate.swift b/openHAB/CarPlaySceneDelegate.swift new file mode 100644 index 000000000..b56cd3ec9 --- /dev/null +++ b/openHAB/CarPlaySceneDelegate.swift @@ -0,0 +1,189 @@ +// Copyright (c) 2010-2026 Contributors to the openHAB project +// +// See the NOTICE file(s) distributed with this work for additional +// information. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0 +// +// SPDX-License-Identifier: EPL-2.0 + +import CarPlay +import OpenHABCore +import os.log + +private let carPlayMaxItems = 8 + +final class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegate { + private var interfaceController: CPInterfaceController? + private var streamTask: Task? + private var preferencesTask: Task? + + func templateApplicationScene(_ templateApplicationScene: CPTemplateApplicationScene, + didConnect interfaceController: CPInterfaceController) { + self.interfaceController = interfaceController + interfaceController.setRootTemplate(placeholderTemplate(), animated: false, completion: nil) + startStreaming() + startObservingPreferences() + Logger.carPlay.info("CarPlay scene connected") + } + + func templateApplicationScene(_ templateApplicationScene: CPTemplateApplicationScene, + didDisconnectInterfaceController interfaceController: CPInterfaceController) { + streamTask?.cancel() + streamTask = nil + preferencesTask?.cancel() + preferencesTask = nil + self.interfaceController = nil + Logger.carPlay.info("CarPlay scene disconnected") + } + + // MARK: - Streaming + + private func startStreaming() { + streamTask?.cancel() + streamTask = Task { [weak self] in + await self?.runStream() + } + } + + private func startObservingPreferences() { + preferencesTask?.cancel() + preferencesTask = Task { @MainActor [weak self] in + guard let self else { return } + var lastSitemap = Preferences.shared.currentHomePreferences.sitemapForCarPlay + for await _ in NotificationCenter.default.notifications(named: UserDefaults.didChangeNotification) { + guard !Task.isCancelled else { break } + let newSitemap = Preferences.shared.currentHomePreferences.sitemapForCarPlay + guard newSitemap != lastSitemap else { continue } + lastSitemap = newSitemap + startStreaming() + } + } + } + + @MainActor + private func runStream() async { + guard let connection = await NetworkTracker.shared.waitForActiveConnection() else { + Logger.carPlay.warning("CarPlay: no active connection") + return + } + let sitemapName = Preferences.shared.currentHomePreferences.sitemapForCarPlay + guard !sitemapName.isEmpty else { + Logger.carPlay.info("CarPlay: no sitemap configured") + return + } + do { + let service = try OpenAPIService(connectionConfiguration: connection.configuration) + for try await event in SitemapPageLoader.stream(sitemapName: sitemapName, service: service) { + guard !Task.isCancelled else { break } + let page: OpenHABPage? = switch event { + case let .initialFetch(page): page + case let .longPoll(page, _): page + } + if let page { updateTemplate(page: page, service: service) } + } + } catch { + Logger.carPlay.error("CarPlay stream error: \(error)") + } + } + + // MARK: - Template building + + @MainActor + private func updateTemplate(page: OpenHABPage, service: OpenAPIService) { + let items = page.widgets + .filter { $0.visibility && isCarPlayCompatible($0) } + .prefix(carPlayMaxItems) + .map { makeItem(for: $0, service: service) } + + let section = CPListSection(items: Array(items)) + let template = CPListTemplate(title: "openHAB", sections: [section]) + interfaceController?.setRootTemplate(template, animated: false, completion: nil) + } + + private func isCarPlayCompatible(_ widget: OpenHABWidget) -> Bool { + switch widget.type { + case .switchWidget, .text, .button, .selection: true + default: false + } + } + + private func makeItem(for widget: OpenHABWidget, service: OpenAPIService) -> CPListItem { + let ds = widget.displayState + + switch widget.type { + case .switchWidget where !ds.mappings.isEmpty: + let item = CPListItem(text: ds.labelText, detailText: ds.selectedLabel ?? ds.labelValue) + item.accessoryType = .disclosureIndicator + item.handler = { [weak self] _, done in + self?.pushMappingList(title: ds.labelText, mappings: ds.mappings, widget: widget, service: service) + done() + } + return item + + case .switchWidget: + let item = CPListItem(text: ds.labelText, detailText: ds.isOn ? "ON" : "OFF") + item.handler = { _, done in + Task { + guard let name = widget.item?.name else { return } + try? await service.sendItemCommand(itemname: name, command: ds.isOn ? "OFF" : "ON") + } + done() + } + return item + + case .button: + let item = CPListItem(text: ds.labelText, detailText: ds.labelValue) + item.handler = { _, done in + Task { + guard let name = widget.item?.name, let command = widget.command else { return } + try? await service.sendItemCommand(itemname: name, command: command) + } + done() + } + return item + + case .selection: + let item = CPListItem(text: ds.labelText, detailText: ds.selectedLabel ?? ds.labelValue) + item.accessoryType = .disclosureIndicator + item.handler = { [weak self] _, done in + self?.pushMappingList(title: ds.labelText, mappings: ds.mappings, widget: widget, service: service) + done() + } + return item + + default: + return CPListItem(text: ds.labelText, detailText: ds.labelValue) + } + } + + private func pushMappingList(title: String, + mappings: [OpenHABWidgetMapping], + widget: OpenHABWidget, + service: OpenAPIService) { + let items = mappings.map { mapping -> CPListItem in + let item = CPListItem(text: mapping.label, detailText: nil) + item.handler = { [weak self] _, done in + Task { + guard let name = widget.item?.name else { return } + try? await service.sendItemCommand(itemname: name, command: mapping.command) + } + self?.interfaceController?.popTemplate(animated: true, completion: nil) + done() + } + return item + } + let template = CPListTemplate(title: title, sections: [CPListSection(items: items)]) + interfaceController?.pushTemplate(template, animated: true, completion: nil) + } + + private func placeholderTemplate() -> CPListTemplate { + let item = CPListItem( + text: String(localized: "carplay_not_configured"), + detailText: String(localized: "carplay_not_configured_detail") + ) + return CPListTemplate(title: "openHAB", sections: [CPListSection(items: [item])]) + } +} diff --git a/openHAB/Supporting Files/Localizable.xcstrings b/openHAB/Supporting Files/Localizable.xcstrings index 60103cc9a..47468d6bd 100644 --- a/openHAB/Supporting Files/Localizable.xcstrings +++ b/openHAB/Supporting Files/Localizable.xcstrings @@ -2134,6 +2134,28 @@ } } }, + "carplay_not_configured" : { + "comment" : "CarPlay root list placeholder shown before a CarPlay favorites page is set up.", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No CarPlay favorites configured" + } + } + } + }, + "carplay_not_configured_detail" : { + "comment" : "CarPlay root list placeholder detail text shown before a CarPlay favorites page is set up.", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set up a CarPlay sitemap page in Settings" + } + } + } + }, "certficate_exists" : { "extractionState" : "manual", "localizations" : { @@ -5193,6 +5215,24 @@ "value" : "Elemento dimmer/tapparella" } }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dimmer/Roller-item" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément gradateur/volet" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento dimmer/tapparella" + } + }, "nl" : { "stringUnit" : { "state" : "translated", @@ -16561,7 +16601,8 @@ } }, "Stop" : { - + "comment" : "Synonyms for the pause action.", + "isCommentAutoGenerated" : true }, "String Item" : { "comment" : "Display name for a string item type.", diff --git a/openHAB/Supporting Files/openHAB-Info.plist b/openHAB/Supporting Files/openHAB-Info.plist index 1fd6f46e9..befe1759e 100644 --- a/openHAB/Supporting Files/openHAB-Info.plist +++ b/openHAB/Supporting Files/openHAB-Info.plist @@ -94,7 +94,7 @@ UIApplicationSceneManifest UIApplicationSupportsMultipleScenes - + UISceneConfigurations UIWindowSceneSessionRoleApplication @@ -108,6 +108,15 @@ Main + CPTemplateApplicationSceneSessionRoleApplication + + + UISceneConfigurationName + CarPlay Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).CarPlaySceneDelegate + + UIBackgroundModes diff --git a/openHAB/Supporting Files/openHAB.entitlements b/openHAB/Supporting Files/openHAB.entitlements index 0c740482e..ffe558678 100644 --- a/openHAB/Supporting Files/openHAB.entitlements +++ b/openHAB/Supporting Files/openHAB.entitlements @@ -4,6 +4,8 @@ aps-environment development + com.apple.developer.carplay-driving-task + com.apple.developer.siri com.apple.security.application-groups diff --git a/openHAB/UI/DrawerView.swift b/openHAB/UI/DrawerView.swift index 1f35c4a8e..1bb1f15d5 100644 --- a/openHAB/UI/DrawerView.swift +++ b/openHAB/UI/DrawerView.swift @@ -120,6 +120,7 @@ struct DrawerView: View { @ScaledMetric var iconWidth = 20.0 @State private var sitemapForWatch: String? + @State private var sitemapForCarPlay: String? var mainSection: some View { Section(header: Text("Main")) { @@ -152,19 +153,27 @@ struct DrawerView: View { var sitemapsSection: some View { Section(header: Text("Sitemaps")) { ForEach(sitemaps, id: \.name) { sitemap in - menuEntry( - image: sitemapIcon(for: sitemap), - goTo: .sitemap(sitemap.name) - ) { - HStack { - Text(sitemap.label) - if sitemap.name == sitemapForWatch { - Spacer() - Image(systemSymbol: .applewatchWatchface) + HStack { + sitemapIcon(for: sitemap) + .aspectRatio(contentMode: .fit) + .frame(width: iconWidth, height: iconWidth) + Text(sitemap.label) + Spacer() + let isWatch = sitemap.name == sitemapForWatch + let isCar = sitemap.name == sitemapForCarPlay + if isWatch { Image(systemSymbol: .applewatchWatchface) } + if isCar { + if #available(iOS 16.1, *) { + Image(systemSymbol: .steeringwheel) + } else { + Image(systemSymbol: .car) } } } + .contentShape(Rectangle()) .onTapGesture(count: 2) { toggleWatchSitemap(sitemap) } + .onTapGesture { onDismiss(.sitemap(sitemap.name)) } + .onLongPressGesture { toggleCarPlaySitemap(sitemap) } } } } @@ -202,6 +211,7 @@ struct DrawerView: View { .task(id: networkTracker.activeConnection) { await updateSitemapsAndUITiles(activeConnection: networkTracker.activeConnection) sitemapForWatch = Preferences.shared.currentHomePreferences.sitemapForWatch + sitemapForCarPlay = Preferences.shared.currentHomePreferences.sitemapForCarPlay } } @@ -276,6 +286,18 @@ struct DrawerView: View { } } + func toggleCarPlaySitemap(_ sitemap: OpenHABSitemap) { + Preferences.shared.modifyActiveHome { prefs in + if sitemap.name == sitemapForCarPlay { + sitemapForCarPlay = nil + prefs.sitemapForCarPlay = "" + } else { + sitemapForCarPlay = sitemap.name + prefs.sitemapForCarPlay = sitemap.name + } + } + } + private func updateSitemapsAndUITiles(activeConnection: ConnectionInfo?) async { guard let activeConnection else { return } diff --git a/openHAB/UI/ScreenSaver/ScreenSaverManager.swift b/openHAB/UI/ScreenSaver/ScreenSaverManager.swift index 23925e921..25687d052 100644 --- a/openHAB/UI/ScreenSaver/ScreenSaverManager.swift +++ b/openHAB/UI/ScreenSaver/ScreenSaverManager.swift @@ -112,7 +112,10 @@ final class ScreenSaverManager: NSObject { overlay = UIWindow(windowScene: scene) overlay.frame = scene.coordinateSpace.bounds } else { - overlay = UIWindow(frame: UIScreen.main.bounds) + let fallbackScene = UIApplication.shared.connectedScenes + .compactMap { $0 as? UIWindowScene } + .first + overlay = UIWindow(frame: fallbackScene?.screen.bounds ?? .zero) } overlay.windowLevel = .alert + 1 // ensure above status bar overlay.backgroundColor = .clear diff --git a/openHAB/UI/SettingsView/SettingsView.swift b/openHAB/UI/SettingsView/SettingsView.swift index 6ca72fcc7..39df52caa 100644 --- a/openHAB/UI/SettingsView/SettingsView.swift +++ b/openHAB/UI/SettingsView/SettingsView.swift @@ -28,6 +28,7 @@ struct SettingsView: View { @State private var settingsDefaultMainUIPath = "" @State private var settingsAlwaysAllowWebRTC = true @State private var settingsSitemapForWatch = "" + @State private var settingsSitemapForCarPlay = "" @State private var sitemaps: [OpenHABSitemap] = [] @State private var settingsLocalConnectionConfiguration = ConnectionConfiguration(url: "", username: "", password: "") @@ -67,6 +68,7 @@ struct SettingsView: View { settingsIconType: $settingsIconType, settingsSortSitemapsBy: $settingsSortSitemapsBy, settingsSitemapForWatch: $settingsSitemapForWatch, + settingsSitemapForCarPlay: $settingsSitemapForCarPlay, sitemaps: $sitemaps ) @@ -160,6 +162,7 @@ struct SettingsView: View { settingsDefaultMainUIPath = Preferences.shared.currentHomePreferences.defaultMainUIPath settingsAlwaysAllowWebRTC = Preferences.shared.currentHomePreferences.alwaysAllowWebRTC settingsSitemapForWatch = Preferences.shared.currentHomePreferences.sitemapForWatch + settingsSitemapForCarPlay = Preferences.shared.currentHomePreferences.sitemapForCarPlay settingsLocalConnectionConfiguration = Preferences.shared.currentHomePreferences.localConnectionConfig settingsRemoteConnectionConfiguration = Preferences.shared.currentHomePreferences.remoteConnectionConfig loadedLocalURL = Preferences.shared.currentHomePreferences.localConnectionConfig.url @@ -177,6 +180,7 @@ struct SettingsView: View { homePreferences.alwaysAllowWebRTC = settingsAlwaysAllowWebRTC homePreferences.sitemapForWatch = settingsSitemapForWatch homePreferences.sitemapForWatchLabel = sitemaps.first { $0.name == settingsSitemapForWatch }?.label ?? "unknown" + homePreferences.sitemapForCarPlay = settingsSitemapForCarPlay homePreferences.localConnectionConfig = settingsLocalConnectionConfiguration homePreferences.remoteConnectionConfig = settingsRemoteConnectionConfiguration homePreferences.sseCommandItem = settingsSSECommandItem diff --git a/openHAB/UI/SettingsView/SitemapSettingsView.swift b/openHAB/UI/SettingsView/SitemapSettingsView.swift index 1c9ec96b7..b54b24c4e 100644 --- a/openHAB/UI/SettingsView/SitemapSettingsView.swift +++ b/openHAB/UI/SettingsView/SitemapSettingsView.swift @@ -20,6 +20,7 @@ struct SitemapSettingsView: View { @Binding var settingsIconType: IconType @Binding var settingsSortSitemapsBy: SortSitemapsOrder @Binding var settingsSitemapForWatch: String + @Binding var settingsSitemapForCarPlay: String @Binding var sitemaps: [OpenHABSitemap] @State private var showingCacheAlert = false @@ -33,6 +34,7 @@ struct SitemapSettingsView: View { iconTypePicker sortOrderPicker watchSitemapPicker + carPlaySitemapPicker } } @@ -102,6 +104,18 @@ struct SitemapSettingsView: View { .disabled(sitemaps.isEmpty) } + private var carPlaySitemapPicker: some View { + Picker("Sitemap for CarPlay", selection: $settingsSitemapForCarPlay) { + Text("None").tag("") + if !sitemaps.isEmpty { + ForEach(sitemaps, id: \.name) { sitemap in + Text(sitemap.label).tag(sitemap.name) + } + } + } + .disabled(sitemaps.isEmpty) + } + @ViewBuilder private func cacheAlertActions(_ result: Result) -> some View { switch result { @@ -142,6 +156,7 @@ struct SitemapSettingsView: View { @State var iconType: IconType = .svg @State var sortSitemapsBy: SortSitemapsOrder = .label @State var sitemapForWatch = "Home" + @State var sitemapForCarPlay = "" @State var sitemaps: [OpenHABSitemap] = [ OpenHABSitemap( name: "home", @@ -167,6 +182,7 @@ struct SitemapSettingsView: View { settingsIconType: $iconType, settingsSortSitemapsBy: $sortSitemapsBy, settingsSitemapForWatch: $sitemapForWatch, + settingsSitemapForCarPlay: $sitemapForCarPlay, sitemaps: $sitemaps ) } From 9aa479170b496b69ea7e77840a113dd6a87483d4 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Sun, 5 Jul 2026 12:25:16 +0200 Subject: [PATCH 56/91] refactor: migrate CarPlay streaming from long-poll to SSE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use SitemapEventStream for widget updates instead of SitemapPageLoader. After the initial page fetch the delegate probes server capabilities: - SSE supported → subscribe via SitemapEventStream; apply widget events in-place with OpenHABPage.apply(event:) and only rebuild the CPListTemplate when something actually changed - SSE unsupported → fall back to the existing long-poll loop SitemapEventStream handles network changes and reconnection internally, so the delegate no longer needs to restart on connection changes beyond sitemap preference changes. Signed-off-by: Tim Mueller-Seydlitz --- openHAB/CarPlaySceneDelegate.swift | 82 ++++++++++++++++++++++++++---- 1 file changed, 73 insertions(+), 9 deletions(-) diff --git a/openHAB/CarPlaySceneDelegate.swift b/openHAB/CarPlaySceneDelegate.swift index b56cd3ec9..ecab08640 100644 --- a/openHAB/CarPlaySceneDelegate.swift +++ b/openHAB/CarPlaySceneDelegate.swift @@ -19,6 +19,7 @@ final class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegat private var interfaceController: CPInterfaceController? private var streamTask: Task? private var preferencesTask: Task? + private let sitemapEventStream = SitemapEventStream() func templateApplicationScene(_ templateApplicationScene: CPTemplateApplicationScene, didConnect interfaceController: CPInterfaceController) { @@ -35,6 +36,7 @@ final class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegat streamTask = nil preferencesTask?.cancel() preferencesTask = nil + Task { await sitemapEventStream.stop() } self.interfaceController = nil Logger.carPlay.info("CarPlay scene disconnected") } @@ -43,9 +45,7 @@ final class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegat private func startStreaming() { streamTask?.cancel() - streamTask = Task { [weak self] in - await self?.runStream() - } + streamTask = Task { [weak self] in await self?.runStream() } } private func startObservingPreferences() { @@ -76,16 +76,80 @@ final class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegat } do { let service = try OpenAPIService(connectionConfiguration: connection.configuration) - for try await event in SitemapPageLoader.stream(sitemapName: sitemapName, service: service) { + + // Initial fetch to populate the template and learn the pageId + guard let page = try await service.pollDataForPage( + sitemapname: sitemapName, pageId: "", longPolling: false + ) else { return } + updateTemplate(page: page, service: service) + + // Prefer SSE; fall back to long-poll on older servers + let serverProps = try? await service.getRoot() + if serverProps?.hasSseSupport() == true { + await runSSE(page: page, sitemapName: sitemapName, connection: connection, service: service) + } else { + await runLongPoll(sitemapName: sitemapName, pageId: page.pageId, service: service) + } + } catch { + Logger.carPlay.error("CarPlay stream error: \(error)") + } + } + + @MainActor + private func runSSE(page: OpenHABPage, sitemapName: String, connection: ConnectionInfo, service: OpenAPIService) async { + await sitemapEventStream.startMonitoringNetworkIfNeeded(initialConnection: connection) + let pageId = page.pageId.isEmpty ? sitemapName : page.pageId + let stream = await sitemapEventStream.stream(sitemap: sitemapName, pageId: pageId) + Logger.carPlay.info("CarPlay SSE starting for \(sitemapName)/\(pageId)") + + for await msg in stream { + guard !Task.isCancelled else { break } + switch msg { + case .connected: + Logger.carPlay.info("CarPlay SSE connected") + case let .disconnected(error): + if let error { Logger.carPlay.warning("CarPlay SSE disconnected: \(error)") } + case let .event(message): + handleSseMessage(message, page: page, service: service) + } + } + } + + @MainActor + private func handleSseMessage(_ message: SitemapEventMessage, page: OpenHABPage, service: OpenAPIService) { + switch message { + case .alive: + break + case .sitemapChanged: + Logger.carPlay.info("CarPlay SSE: sitemap changed, reloading") + startStreaming() + case let .widget(event): + switch page.apply(event: event) { + case .applied: + updateTemplate(page: page, service: service) + case .requiresPageReload, .notFound: + Logger.carPlay.info("CarPlay SSE: widget requires reload") + startStreaming() + case .unchanged: + break + } + case let .unknown(raw): + Logger.carPlay.debug("CarPlay SSE unknown: \(raw)") + } + } + + @MainActor + private func runLongPoll(sitemapName: String, pageId: String, service: OpenAPIService) async { + Logger.carPlay.info("CarPlay using long-poll for \(sitemapName)") + do { + for try await event in SitemapPageLoader.stream(sitemapName: sitemapName, pageId: pageId, service: service) { guard !Task.isCancelled else { break } - let page: OpenHABPage? = switch event { - case let .initialFetch(page): page - case let .longPoll(page, _): page + if case let .longPoll(page, _) = event { + updateTemplate(page: page, service: service) } - if let page { updateTemplate(page: page, service: service) } } } catch { - Logger.carPlay.error("CarPlay stream error: \(error)") + Logger.carPlay.error("CarPlay long-poll error: \(error)") } } From 6afb57d409fadf0d0f42ad945fa57d33d82599a2 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Sun, 5 Jul 2026 14:57:30 +0200 Subject: [PATCH 57/91] fix: show placeholder instead of empty CPListTemplate when no compatible widgets CarPlay rejects a CPListTemplate with zero items; fall back to the placeholder template when the filtered widget list is empty. Signed-off-by: Tim Mueller-Seydlitz --- openHAB/CarPlaySceneDelegate.swift | 5 + .../Supporting Files/Localizable.xcstrings | 103 ++---------------- 2 files changed, 13 insertions(+), 95 deletions(-) diff --git a/openHAB/CarPlaySceneDelegate.swift b/openHAB/CarPlaySceneDelegate.swift index ecab08640..c9d934bec 100644 --- a/openHAB/CarPlaySceneDelegate.swift +++ b/openHAB/CarPlaySceneDelegate.swift @@ -162,6 +162,11 @@ final class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegat .prefix(carPlayMaxItems) .map { makeItem(for: $0, service: service) } + guard !items.isEmpty else { + interfaceController?.setRootTemplate(placeholderTemplate(), animated: false, completion: nil) + return + } + let section = CPListSection(items: Array(items)) let template = CPListTemplate(title: "openHAB", sections: [section]) interfaceController?.setRootTemplate(template, animated: false, completion: nil) diff --git a/openHAB/Supporting Files/Localizable.xcstrings b/openHAB/Supporting Files/Localizable.xcstrings index 47468d6bd..0fe3cc820 100644 --- a/openHAB/Supporting Files/Localizable.xcstrings +++ b/openHAB/Supporting Files/Localizable.xcstrings @@ -292,18 +292,6 @@ } } }, - "%@ • %@" : { - "comment" : "The item name and the name of the home the item belongs to.", - "isCommentAutoGenerated" : true, - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "new", - "value" : "%1$@ • %2$@" - } - } - } - }, "%@ Settings" : { "comment" : "The title of the settings view. The placeholder is replaced with the user's home name.", "localizations" : { @@ -481,6 +469,10 @@ } } }, + "%lld %%" : { + "comment" : "A label that shows the percentage of brightness. The value is rounded to the nearest whole number.", + "isCommentAutoGenerated" : true + }, "%lldK" : { "comment" : "A text view displaying the selected color temperature in Kelvin. The value is shown in a smaller font next to a separator text (\" - \").", "localizations" : { @@ -833,10 +825,6 @@ } } }, - "Active" : { - "comment" : "Display name for the contact state \"Open\".", - "isCommentAutoGenerated" : true - }, "active_url" : { "extractionState" : "manual", "localizations" : { @@ -3846,10 +3834,6 @@ } } }, - "Control Player" : { - "comment" : "Text displayed in the shortcut picker for controlling a player.", - "isCommentAutoGenerated" : true - }, "Cool Daylight" : { "comment" : "A description for a color temperature between 6500 and 8000 Kelvin.", "localizations" : { @@ -5145,6 +5129,10 @@ "comment" : "A label displaying the current brightness level of the screen. The argument is the brightness level as a percentage.", "isCommentAutoGenerated" : true }, + "Dimmer or Roller Item" : { + "comment" : "Display name for a dimmer or roller item type.", + "isCommentAutoGenerated" : true + }, "Dimmer or Roller Item" : { "comment" : "Display name for a dimmer or roller item type.", "isCommentAutoGenerated" : true, @@ -5822,10 +5810,6 @@ } } }, - "Enable" : { - "comment" : "Display name for the \"Enable\" action.", - "isCommentAutoGenerated" : true - }, "Enable Dimming" : { "comment" : "A toggle that enables or disables automatic brightness dimming.", "localizations" : { @@ -6888,9 +6872,6 @@ } } } - }, - "Go backward" : { - }, "habpanel" : { "extractionState" : "manual", @@ -7009,9 +6990,6 @@ } } } - }, - "Hold" : { - }, "Home" : { "comment" : "The name of the \"Home\" menu item.", @@ -11031,10 +11009,6 @@ } } }, - "Opened" : { - "comment" : "A synonym for the \"Open\" contact state.", - "isCommentAutoGenerated" : true - }, "openHAB Cloud Service" : { "comment" : "A toggle that allows the user to enable or disable notifications from the openHAB Cloud Service.", "localizations" : { @@ -12466,10 +12440,6 @@ } } }, - "Resume" : { - "comment" : "Action to resume playback.", - "isCommentAutoGenerated" : true - }, "Retrieve the current state of an item" : { "comment" : "Description of the Get Item State app intent.", "localizations" : { @@ -14080,10 +14050,6 @@ } } }, - "Set Color" : { - "comment" : "Short title for the shortcut to set a color value.", - "isCommentAutoGenerated" : true - }, "Set Color Control Value" : { "comment" : "Title of the Set Color Control Value app intent.", "localizations" : { @@ -14206,10 +14172,6 @@ } } }, - "Set Date & Time" : { - "comment" : "Short title for the shortcut to set a date and time.", - "isCommentAutoGenerated" : true - }, "Set DateTime Control Value" : { "comment" : "Title of the Set DateTime Control Value app intent.", "localizations" : { @@ -14269,10 +14231,6 @@ } } }, - "Set Dimmer / Roller" : { - "comment" : "Short title for a shortcut that allows the user to set the value of a dimmer or roller shutter.", - "isCommentAutoGenerated" : true - }, "Set Dimmer or Roller Shutter Value" : { "comment" : "Title of the Set Dimmer or Roller Shutter Value app intent.", "localizations" : { @@ -14391,10 +14349,6 @@ } } }, - "Set Number" : { - "comment" : "Text displayed in a shortcut for setting a number value.", - "isCommentAutoGenerated" : true - }, "Set Number Control Value" : { "comment" : "Title of the Set Number Control Value app intent.", "localizations" : { @@ -14572,10 +14526,6 @@ } } }, - "Set Switch" : { - "comment" : "Short title for a shortcut that allows the user to set a switch.", - "isCommentAutoGenerated" : true - }, "Set Switch State" : { "comment" : "Title of the Set Switch State app intent.", "localizations" : { @@ -14635,10 +14585,6 @@ } } }, - "Set Text" : { - "comment" : "Text displayed in a shortcut for setting a string value.", - "isCommentAutoGenerated" : true - }, "Set the color of a color control item" : { "comment" : "Description of the Set Color Control Value app intent.", "localizations" : { @@ -16241,10 +16187,6 @@ } } }, - "Speed up" : { - "comment" : "Synonyms for the \"Fast Forward\" action.", - "isCommentAutoGenerated" : true - }, "SSL error. The connection couldn’t be established securely." : { "comment" : "Error message displayed when a connection to the OpenHAB server fails due to a SSL error.", "localizations" : { @@ -16537,10 +16479,6 @@ } } }, - "Start" : { - "comment" : "A synonym for the \"Play\" action.", - "isCommentAutoGenerated" : true - }, "State" : { "comment" : "A label for a picker that lets the user select a state.", "localizations" : { @@ -16600,10 +16538,6 @@ } } }, - "Stop" : { - "comment" : "Synonyms for the pause action.", - "isCommentAutoGenerated" : true - }, "String Item" : { "comment" : "Display name for a string item type.", "isCommentAutoGenerated" : true, @@ -16721,10 +16655,6 @@ } } }, - "Switch" : { - "comment" : "The type of action to perform on a switch.", - "isCommentAutoGenerated" : true - }, "Switch Action" : { "comment" : "Type display name for the SwitchAction enum used in app intents.", "localizations" : { @@ -16784,13 +16714,8 @@ } } }, - "Switch Home" : { - "comment" : "Shortcut title for switching between homes.", - "isCommentAutoGenerated" : true - }, "Switch Item" : { "comment" : "Display name for a switch item type.", - "extractionState" : "stale", "isCommentAutoGenerated" : true, "localizations" : { "de" : { @@ -17638,18 +17563,6 @@ } } }, - "Triggered" : { - "comment" : "Display name for the contact state \"OPEN\".", - "isCommentAutoGenerated" : true - }, - "Turn off" : { - "comment" : "Turn off action.", - "isCommentAutoGenerated" : true - }, - "Turn on" : { - "comment" : "Turn on action.", - "isCommentAutoGenerated" : true - }, "unable_to_add_certificate" : { "extractionState" : "manual", "localizations" : { From 3a295d1919c0ef92f7fc8ac0c6f6151f54cd2ec9 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Sun, 5 Jul 2026 21:55:04 +0200 Subject: [PATCH 58/91] feat: switch CarPlay to CPGridTemplate with openHAB icons Replace CPListTemplate with CPGridTemplate for a grid-of-buttons layout matching native CarPlay apps. Icons are fetched from the openHAB server via Kingfisher (state-aware, authenticated) and updated in-place using updateGridButtons to avoid flicker on SSE state changes. Signed-off-by: Tim Mueller-Seydlitz --- openHAB/CarPlaySceneDelegate.swift | 142 ++++++++++++++++++----------- 1 file changed, 90 insertions(+), 52 deletions(-) diff --git a/openHAB/CarPlaySceneDelegate.swift b/openHAB/CarPlaySceneDelegate.swift index c9d934bec..b25bf82e1 100644 --- a/openHAB/CarPlaySceneDelegate.swift +++ b/openHAB/CarPlaySceneDelegate.swift @@ -10,16 +10,21 @@ // SPDX-License-Identifier: EPL-2.0 import CarPlay +import Kingfisher import OpenHABCore import os.log -private let carPlayMaxItems = 8 +private var carPlayMaxItems: Int { + Int(CPGridTemplateMaximumItems) +} final class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegate { private var interfaceController: CPInterfaceController? private var streamTask: Task? private var preferencesTask: Task? private let sitemapEventStream = SitemapEventStream() + private var currentConnection: ConnectionInfo? + private var currentGridTemplate: CPGridTemplate? func templateApplicationScene(_ templateApplicationScene: CPTemplateApplicationScene, didConnect interfaceController: CPInterfaceController) { @@ -38,6 +43,8 @@ final class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegat preferencesTask = nil Task { await sitemapEventStream.stop() } self.interfaceController = nil + currentConnection = nil + currentGridTemplate = nil Logger.carPlay.info("CarPlay scene disconnected") } @@ -69,6 +76,7 @@ final class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegat Logger.carPlay.warning("CarPlay: no active connection") return } + currentConnection = connection let sitemapName = Preferences.shared.currentHomePreferences.sitemapForCarPlay guard !sitemapName.isEmpty else { Logger.carPlay.info("CarPlay: no sitemap configured") @@ -157,74 +165,97 @@ final class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegat @MainActor private func updateTemplate(page: OpenHABPage, service: OpenAPIService) { - let items = page.widgets - .filter { $0.visibility && isCarPlayCompatible($0) } - .prefix(carPlayMaxItems) - .map { makeItem(for: $0, service: service) } + let widgets = Array( + page.widgets + .filter { $0.visibility && isCarPlayCompatible($0) } + .prefix(carPlayMaxItems) + ) - guard !items.isEmpty else { + guard !widgets.isEmpty else { + currentGridTemplate = nil interfaceController?.setRootTemplate(placeholderTemplate(), animated: false, completion: nil) return } - let section = CPListSection(items: Array(items)) - let template = CPListTemplate(title: "openHAB", sections: [section]) - interfaceController?.setRootTemplate(template, animated: false, completion: nil) + let title = page.title.isEmpty ? "openHAB" : "openHAB – \(page.title)" + let template: CPGridTemplate + + if let existing = currentGridTemplate { + // Update in place — avoids full-screen replacement flash + existing.updateTitle(title) + template = existing + } else { + let buttons = widgets.map { makeGridButton(for: $0, service: service, image: placeholderButtonImage()) } + template = CPGridTemplate(title: title, gridButtons: buttons) + currentGridTemplate = template + interfaceController?.setRootTemplate(template, animated: false, completion: nil) + } + + guard let connection = currentConnection else { return } + Task { [weak self] in + await self?.fetchAndUpdateIcons(widgets: widgets, service: service, template: template, connection: connection) + } + } + + @MainActor + private func fetchAndUpdateIcons(widgets: [OpenHABWidget], + service: OpenAPIService, + template: CPGridTemplate, + connection: ConnectionInfo) async { + var updatedButtons: [CPGridButton] = [] + for widget in widgets { + let image = await fetchIcon(for: widget, connection: connection) ?? placeholderButtonImage() + updatedButtons.append(makeGridButton(for: widget, service: service, image: image)) + } + guard template === currentGridTemplate else { return } + template.updateGridButtons(updatedButtons) + } + + private func fetchIcon(for widget: OpenHABWidget, connection: ConnectionInfo) async -> UIImage? { + guard let url = Endpoint.icon( + rootUrl: connection.configuration.url, + version: connection.version, + icon: widget.icon, + state: widget.item?.state, + iconType: .png, + iconColor: widget.iconColor, + staticIcon: widget.staticIcon + )?.url else { return nil } + + let options: KingfisherOptionsInfo = [ + .processor(OpenHABImageProcessor()), + .requestModifier(OpenHABAccessTokenAdapter(connectionConfiguration: connection.configuration)) + ] + return try? await KingfisherManager.shared.retrieveImage(with: url, options: options).image } private func isCarPlayCompatible(_ widget: OpenHABWidget) -> Bool { switch widget.type { - case .switchWidget, .text, .button, .selection: true + case .switchWidget, .button, .selection: true default: false } } - private func makeItem(for widget: OpenHABWidget, service: OpenAPIService) -> CPListItem { + private func makeGridButton(for widget: OpenHABWidget, service: OpenAPIService, image: UIImage) -> CPGridButton { let ds = widget.displayState - - switch widget.type { - case .switchWidget where !ds.mappings.isEmpty: - let item = CPListItem(text: ds.labelText, detailText: ds.selectedLabel ?? ds.labelValue) - item.accessoryType = .disclosureIndicator - item.handler = { [weak self] _, done in - self?.pushMappingList(title: ds.labelText, mappings: ds.mappings, widget: widget, service: service) - done() - } - return item - - case .switchWidget: - let item = CPListItem(text: ds.labelText, detailText: ds.isOn ? "ON" : "OFF") - item.handler = { _, done in + return CPGridButton(titleVariants: [ds.labelText], image: image) { [weak self] _ in + guard let self else { return } + switch widget.type { + case .switchWidget where !ds.mappings.isEmpty, .selection: + pushMappingList(title: ds.labelText, mappings: ds.mappings, widget: widget, service: service) + case .switchWidget: Task { guard let name = widget.item?.name else { return } try? await service.sendItemCommand(itemname: name, command: ds.isOn ? "OFF" : "ON") } - done() - } - return item - - case .button: - let item = CPListItem(text: ds.labelText, detailText: ds.labelValue) - item.handler = { _, done in + case .button: Task { guard let name = widget.item?.name, let command = widget.command else { return } try? await service.sendItemCommand(itemname: name, command: command) } - done() - } - return item - - case .selection: - let item = CPListItem(text: ds.labelText, detailText: ds.selectedLabel ?? ds.labelValue) - item.accessoryType = .disclosureIndicator - item.handler = { [weak self] _, done in - self?.pushMappingList(title: ds.labelText, mappings: ds.mappings, widget: widget, service: service) - done() + default: + break } - return item - - default: - return CPListItem(text: ds.labelText, detailText: ds.labelValue) } } @@ -248,11 +279,18 @@ final class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegat interfaceController?.pushTemplate(template, animated: true, completion: nil) } - private func placeholderTemplate() -> CPListTemplate { - let item = CPListItem( - text: String(localized: "carplay_not_configured"), - detailText: String(localized: "carplay_not_configured_detail") - ) - return CPListTemplate(title: "openHAB", sections: [CPListSection(items: [item])]) + private func placeholderButtonImage() -> UIImage { + UIImage(named: "openHABIcon")? + .withRenderingMode(.alwaysTemplate) + .withTintColor(.secondaryLabel, renderingMode: .alwaysOriginal) + ?? UIImage() + } + + private func placeholderTemplate() -> CPGridTemplate { + let button = CPGridButton( + titleVariants: [String(localized: "carplay_not_configured")], + image: placeholderButtonImage() + ) { _ in } + return CPGridTemplate(title: "openHAB", gridButtons: [button]) } } From d0095c5c5f51b04d7233f29c9e37fe9759a1dd4b Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Mon, 6 Jul 2026 19:43:19 +0200 Subject: [PATCH 59/91] refactor: replace remote icon fetch with SF symbol mapping in CarPlay MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Maps widget icon name and ON/OFF state to SFSafeSymbols (power, lamp; default circle). Removes async Kingfisher fetch path and currentConnection dependency — icons are now resolved synchronously on every state update. Signed-off-by: Tim Mueller-Seydlitz --- openHAB/CarPlaySceneDelegate.swift | 61 ++++++------------- .../Supporting Files/Localizable.xcstrings | 1 + 2 files changed, 18 insertions(+), 44 deletions(-) diff --git a/openHAB/CarPlaySceneDelegate.swift b/openHAB/CarPlaySceneDelegate.swift index b25bf82e1..33aa3620a 100644 --- a/openHAB/CarPlaySceneDelegate.swift +++ b/openHAB/CarPlaySceneDelegate.swift @@ -10,9 +10,9 @@ // SPDX-License-Identifier: EPL-2.0 import CarPlay -import Kingfisher import OpenHABCore import os.log +import SFSafeSymbols private var carPlayMaxItems: Int { Int(CPGridTemplateMaximumItems) @@ -23,7 +23,6 @@ final class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegat private var streamTask: Task? private var preferencesTask: Task? private let sitemapEventStream = SitemapEventStream() - private var currentConnection: ConnectionInfo? private var currentGridTemplate: CPGridTemplate? func templateApplicationScene(_ templateApplicationScene: CPTemplateApplicationScene, @@ -43,7 +42,6 @@ final class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegat preferencesTask = nil Task { await sitemapEventStream.stop() } self.interfaceController = nil - currentConnection = nil currentGridTemplate = nil Logger.carPlay.info("CarPlay scene disconnected") } @@ -76,7 +74,6 @@ final class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegat Logger.carPlay.warning("CarPlay: no active connection") return } - currentConnection = connection let sitemapName = Preferences.shared.currentHomePreferences.sitemapForCarPlay guard !sitemapName.isEmpty else { Logger.carPlay.info("CarPlay: no sitemap configured") @@ -178,55 +175,30 @@ final class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegat } let title = page.title.isEmpty ? "openHAB" : "openHAB – \(page.title)" - let template: CPGridTemplate + let buttons = widgets.map { makeGridButton(for: $0, service: service) } if let existing = currentGridTemplate { - // Update in place — avoids full-screen replacement flash existing.updateTitle(title) - template = existing + existing.updateGridButtons(buttons) } else { - let buttons = widgets.map { makeGridButton(for: $0, service: service, image: placeholderButtonImage()) } - template = CPGridTemplate(title: title, gridButtons: buttons) + let template = CPGridTemplate(title: title, gridButtons: buttons) currentGridTemplate = template interfaceController?.setRootTemplate(template, animated: false, completion: nil) } - - guard let connection = currentConnection else { return } - Task { [weak self] in - await self?.fetchAndUpdateIcons(widgets: widgets, service: service, template: template, connection: connection) - } } - @MainActor - private func fetchAndUpdateIcons(widgets: [OpenHABWidget], - service: OpenAPIService, - template: CPGridTemplate, - connection: ConnectionInfo) async { - var updatedButtons: [CPGridButton] = [] - for widget in widgets { - let image = await fetchIcon(for: widget, connection: connection) ?? placeholderButtonImage() - updatedButtons.append(makeGridButton(for: widget, service: service, image: image)) + private func sfSymbol(for widget: OpenHABWidget) -> UIImage { + let isOn = widget.displayState.isOn + let symbolName: SFSymbol = switch widget.icon.lowercased() { + case "power", "poweroutlet", "poweroutlet_eu": + isOn ? .powerCircleFill : .powerCircle + case "lamp", "light", "lightbulb": + isOn ? .lightbulbFill : .lightbulb + default: + isOn ? .circleFill : .circle } - guard template === currentGridTemplate else { return } - template.updateGridButtons(updatedButtons) - } - - private func fetchIcon(for widget: OpenHABWidget, connection: ConnectionInfo) async -> UIImage? { - guard let url = Endpoint.icon( - rootUrl: connection.configuration.url, - version: connection.version, - icon: widget.icon, - state: widget.item?.state, - iconType: .png, - iconColor: widget.iconColor, - staticIcon: widget.staticIcon - )?.url else { return nil } - - let options: KingfisherOptionsInfo = [ - .processor(OpenHABImageProcessor()), - .requestModifier(OpenHABAccessTokenAdapter(connectionConfiguration: connection.configuration)) - ] - return try? await KingfisherManager.shared.retrieveImage(with: url, options: options).image + return UIImage(systemSymbol: symbolName) + .withRenderingMode(.alwaysTemplate) } private func isCarPlayCompatible(_ widget: OpenHABWidget) -> Bool { @@ -236,8 +208,9 @@ final class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegat } } - private func makeGridButton(for widget: OpenHABWidget, service: OpenAPIService, image: UIImage) -> CPGridButton { + private func makeGridButton(for widget: OpenHABWidget, service: OpenAPIService) -> CPGridButton { let ds = widget.displayState + let image = sfSymbol(for: widget) return CPGridButton(titleVariants: [ds.labelText], image: image) { [weak self] _ in guard let self else { return } switch widget.type { diff --git a/openHAB/Supporting Files/Localizable.xcstrings b/openHAB/Supporting Files/Localizable.xcstrings index 0fe3cc820..5306f0ff1 100644 --- a/openHAB/Supporting Files/Localizable.xcstrings +++ b/openHAB/Supporting Files/Localizable.xcstrings @@ -2135,6 +2135,7 @@ }, "carplay_not_configured_detail" : { "comment" : "CarPlay root list placeholder detail text shown before a CarPlay favorites page is set up.", + "extractionState" : "stale", "localizations" : { "en" : { "stringUnit" : { From bcf991b9b3fcdf5f19c9f6bd11f8155a98c3bfb9 Mon Sep 17 00:00:00 2001 From: openhab-bot Date: Mon, 6 Jul 2026 18:52:39 +0000 Subject: [PATCH 60/91] committed version bump: 3.4.7 (297) Signed-off-by: openhab-bot --- CHANGELOG.md | 55 ++++++++++++++++++++++++++++++++++++++++++++++++ Version.xcconfig | 4 ++-- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f764581e..e40cc5fd4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,61 @@ ## [Unreleased] +- Migrate sitemap page updates from long-polling to SSE (#1168) +- Small Widget background watermark alignment adjustments (#1289) +- fix: show message body below title in notification list +- fix: add Localizable.xcstrings to widget extension target membership +- fix: remove duplicate knownRegions from project.pbxproj +- committed version bump: 3.4.6 (296) +- Allow for two lines label and value for iOS Lock Screen Accessories (#1286) +- Swiftlinting +- chore: apply linter fixes +- feat: add accessory widget previews for Switch and Sensor widgets +- refactor: remove UIImageView polling timer from MJPEG video stream +- committed version bump: 3.4.5 (294) +- Fix small widget content to be cntered +- committed version bump: 3.4.4 (293) +- committed version bump: 3.4.3 (292) +- fix: sync allowedItemTypes with query and add regression test +- feat: make Dimmer items selectable in Set Switch State intent +- chore: apply linter fixes +- fix: detect GIF images by magic bytes in ImageRowView +- committed version bump: 3.4.2 (290) +- fix: animate GIFs in the Image sitemap widget (#1280) +- Widget adjustments (#1279) +- fix: lock screen widgets now send commands on tap +- fix: notification ping now respects the ringer/silent switch (#1278) +- fix: add OpenHABShortcuts.swift to app target to resolve AppShortcuts.xcstrings phrase warnings +- committed version bump: 3.4.1 (287) +- fix: add provisioning profile for openHABWidgetExtension +- l10n: add Spanish, Finnish, Norwegian, and Russian translations +- l10n: switch German and French translations to informal address (du/tu) +- chore: remove 5 stale localization keys on feature branch +- l10n: add Spanish, Finnish, Norwegian, and Russian translations +- l10n: translate 2 new widget keys into all 9 languages +- chore: update openHABWidgetExtension scheme with askForAppToLaunch +- fix: restore project.pbxproj corrupted by bad 3-way merge of pbxproj +- chore: add shared schemes for openHABWidgetExtension and OpenHABWatchComplicationsExtension +- build: raise macOS minimum to 15 for AsyncSequence.Failure support +- chore: bump openHABTestsSwift deployment target to iOS 18.0 +- Merge develop + fix build warnings; add iOS home screen widgets with item display +- Sensor widget: format state values using stateDescription numberPattern +- Sensor widget: support 1/2/4 items per small/medium/large size +- Make ScrollToTop overlay button conditional (#1231) +- Bump minimum deployment targets to iOS 18 / watchOS 11 +- Use iOS 18 onScrollGeometryChange in SitemapPageView +- Use iOS 17 animation and scroll APIs +- Replace SetLocationValueIntent with SetActiveHomeIntent in AppShortcutsProvider +- Register SetActiveHomeIntent with AppShortcutsProvider +- Linting and AppIntents SSE warnings fixes +- Bump deployment targets to iOS 17.0 / watchOS 10.0 +- Convert AppShortcuts.strings to AppShortcuts.xcstrings String Catalog +- Fix AppIntentsMetadataProcessor synonym warnings in AppEnum cases +- Add AppShortcutsProvider and fix intent dialog gaps +- Upgrade SwiftFormatPlugin 0.58.7→0.61.1, SwiftLintPlugin 0.62.2→0.63.3 (#1225) +- ItemEventStream: backport SSE watchdog and self-contained network monitor +- openHABWidget: add interactive switch and sensor widgets + - Allow for two lines label and value for iOS Lock Screen Accessories (#1286) - Swiftlinting - chore: apply linter fixes diff --git a/Version.xcconfig b/Version.xcconfig index 0876c413a..b22cfd738 100644 --- a/Version.xcconfig +++ b/Version.xcconfig @@ -6,5 +6,5 @@ // https://developer.apple.com/documentation/xcode/adding-a-build-configuration-file-to-your-project -MARKETING_VERSION = 3.4.6 -CURRENT_PROJECT_VERSION = 296 +MARKETING_VERSION = 3.4.7 +CURRENT_PROJECT_VERSION = 297 From 669773df43630ff93aca8962da1c5d7f64e97154 Mon Sep 17 00:00:00 2001 From: DigiH <17110652+DigiH@users.noreply.github.com> Date: Mon, 6 Jul 2026 21:28:52 +0200 Subject: [PATCH 61/91] Make Circular Accessory Switch icon state aware (#1291) Signed-off-by: DigiH <17110652+DigiH@users.noreply.github.com> --- openHABWidget/SwitchWidgetEntryView.swift | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/openHABWidget/SwitchWidgetEntryView.swift b/openHABWidget/SwitchWidgetEntryView.swift index 1a29bc20e..92fd56eed 100644 --- a/openHABWidget/SwitchWidgetEntryView.swift +++ b/openHABWidget/SwitchWidgetEntryView.swift @@ -368,11 +368,13 @@ struct SwitchAccessoryCircularView: View { ZStack { AccessoryWidgetBackground() if let slot = entry.slots.compactMap(\.self).first { + let isOn = slot.item.state == "ON" if let home = entry.home { Button(intent: createToggleIntent(item: slot.item, homeUUID: slot.homeUUID, home: home)) { VStack(spacing: 2) { - Image(systemSymbol: .switch2) - .font(.caption) + let symbol: SFSymbol = isOn ? .powerCircleFill : .powerCircle + Image(systemSymbol: symbol) + .font(.callout) Text(slot.item.state ?? "?") .font(.caption2) .fontWeight(.bold) @@ -383,8 +385,9 @@ struct SwitchAccessoryCircularView: View { .buttonStyle(.plain) } else { VStack(spacing: 2) { - Image(systemSymbol: .switch2) - .font(.caption) + let symbol: SFSymbol = isOn ? .powerCircleFill : .powerCircle + Image(systemSymbol: symbol) + .font(.callout) Text(slot.item.state ?? "?") .font(.caption2) .fontWeight(.bold) From 034e4cb5410df1a6dcc9e0b026bc1a1f3c125d5d Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Mon, 6 Jul 2026 22:29:36 +0200 Subject: [PATCH 62/91] fix: remove stale %lld %% localization keys for screensaver percentage labels Clock Size, Dim Level, Relative Date, and Restore Brightness all use .formatted(.percent) in code; the old String(format:) keys are unreferenced and trigger an Xcode percentage-formatting warning. Signed-off-by: Tim Mueller-Seydlitz --- openHAB/Supporting Files/Localizable.xcstrings | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/openHAB/Supporting Files/Localizable.xcstrings b/openHAB/Supporting Files/Localizable.xcstrings index 60103cc9a..2b597d084 100644 --- a/openHAB/Supporting Files/Localizable.xcstrings +++ b/openHAB/Supporting Files/Localizable.xcstrings @@ -2897,10 +2897,6 @@ } } }, - "Clock Size: %lld %%" : { - "comment" : "A label that shows the current value of the clock size slider. The value is shown in percent.", - "isCommentAutoGenerated" : true - }, "closed" : { "extractionState" : "manual", "localizations" : { @@ -5119,10 +5115,6 @@ } } }, - "Dim Level: %lld %%" : { - "comment" : "A label displaying the current brightness level of the screen. The argument is the brightness level as a percentage.", - "isCommentAutoGenerated" : true - }, "Dimmer or Roller Item" : { "comment" : "Display name for a dimmer or roller item type.", "isCommentAutoGenerated" : true, @@ -12055,10 +12047,6 @@ } } }, - "Relative Date: %lld %%" : { - "comment" : "A label that shows the relative size of the date font.", - "isCommentAutoGenerated" : true - }, "Remote server" : { "comment" : "Header text for the remote server section in the connection settings view.", "localizations" : { @@ -12363,10 +12351,6 @@ } } }, - "Restore Brightness: %lld %%" : { - "comment" : "A label that shows the brightness level at which the screen saver restores the brightness of the device.", - "isCommentAutoGenerated" : true - }, "Restore Previous Brightness on Wake" : { "comment" : "A toggle that allows the user to restore the brightness level to its previous value when the device wakes up.", "localizations" : { From 6529c4ed9dbdbc4ff99b23715e545a2c0237bef7 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Mon, 6 Jul 2026 22:35:41 +0200 Subject: [PATCH 63/91] fix: remove 7 stale localization keys with no source references Removes Clock Size/Dim Level/Relative Date/Restore Brightness %@ (screensaver labels not extracted by Xcode's tool), Switch Item (widget extension, no xcstrings of its own), off and toggle (raw string literals, not localized). Signed-off-by: Tim Mueller-Seydlitz --- .../Supporting Files/Localizable.xcstrings | 448 +----------------- 1 file changed, 7 insertions(+), 441 deletions(-) diff --git a/openHAB/Supporting Files/Localizable.xcstrings b/openHAB/Supporting Files/Localizable.xcstrings index 2b597d084..2bfc76490 100644 --- a/openHAB/Supporting Files/Localizable.xcstrings +++ b/openHAB/Supporting Files/Localizable.xcstrings @@ -2837,66 +2837,6 @@ } } }, - "Clock Size: %@" : { - "comment" : "A label describing the percentage of the screen that is used for the clock face.", - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Uhrgröße: %@" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Clock Size: %@" - } - }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tamaño del reloj: %@" - } - }, - "fi" : { - "stringUnit" : { - "state" : "translated", - "value" : "Kellon koko: %@" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Taille de l'horloge : %@" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dimensione orologio: %@" - } - }, - "nb" : { - "stringUnit" : { - "state" : "translated", - "value" : "Klokkestørrelse: %@" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Klokgrootte: %@" - } - }, - "ru" : { - "stringUnit" : { - "state" : "translated", - "value" : "Размер часов: %@" - } - } - } - }, "closed" : { "extractionState" : "manual", "localizations" : { @@ -5055,66 +4995,6 @@ } } }, - "Dim Level: %@" : { - "comment" : "A label displaying the current dim level of the screen saver. The value is shown as a percentage.", - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dimmwert: %@" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dim Level: %@" - } - }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nivel de atenuación: %@" - } - }, - "fi" : { - "stringUnit" : { - "state" : "translated", - "value" : "Himmennystaso: %@" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Niveau de gradation : %@" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Livello dimmeraggio: %@" - } - }, - "nb" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dimmenivå: %@" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dimniveau: %@" - } - }, - "ru" : { - "stringUnit" : { - "state" : "translated", - "value" : "Уровень затемнения: %@" - } - } - } - }, "Dimmer or Roller Item" : { "comment" : "Display name for a dimmer or roller item type.", "isCommentAutoGenerated" : true, @@ -5146,13 +5026,13 @@ "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Élément variateur ou volet" + "value" : "Élément gradateur/volet" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Elemento dimmer o tapparella" + "value" : "Elemento dimmer/tapparella" } }, "nb" : { @@ -5164,7 +5044,7 @@ "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Dimmer- of rolluik-item" + "value" : "Dimmer/Roller-item" } }, "ru" : { @@ -5172,24 +5052,6 @@ "state" : "translated", "value" : "Элемент диммера или ролеты" } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Élément gradateur/volet" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Elemento dimmer/tapparella" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dimmer/Roller-item" - } } } }, @@ -6841,9 +6703,7 @@ } } }, - "Go backward" : { - - }, + "Go backward" : {}, "habpanel" : { "extractionState" : "manual", "localizations" : { @@ -6962,9 +6822,7 @@ } } }, - "Hold" : { - - }, + "Hold" : {}, "Home" : { "comment" : "The name of the \"Home\" menu item.", "localizations" : { @@ -10284,66 +10142,6 @@ } } }, - "off" : { - "comment" : "Label for the \"off\" state of a contact.", - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "aus" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "off" - } - }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Elemento numérico" - } - }, - "fi" : { - "stringUnit" : { - "state" : "translated", - "value" : "Numeraalielementti" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Élément numérique" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Elemento numerico" - } - }, - "nb" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tallselement" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Numeriek item" - } - }, - "ru" : { - "stringUnit" : { - "state" : "translated", - "value" : "Числовой элемент" - } - } - } - }, "Off" : { "comment" : "The text \"Off\" displayed in the accessibility value of the toggle.", "localizations" : { @@ -11987,66 +11785,6 @@ } } }, - "Relative Date: %@" : { - "comment" : "A label describing the relative size of the date text compared to the clock text.", - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Relatives Datum: %@" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Relative Date: %@" - } - }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Fecha relativa: %@" - } - }, - "fi" : { - "stringUnit" : { - "state" : "translated", - "value" : "Suhteellinen päivämäärä: %@" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Date relative : %@" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Data relativa: %@" - } - }, - "nb" : { - "stringUnit" : { - "state" : "translated", - "value" : "Relativ dato: %@" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Relatieve datum: %@" - } - }, - "ru" : { - "stringUnit" : { - "state" : "translated", - "value" : "Относительная дата: %@" - } - } - } - }, "Remote server" : { "comment" : "Header text for the remote server section in the connection settings view.", "localizations" : { @@ -12291,66 +12029,6 @@ "comment" : "Synonyms for the \"Reset\" state of a contact.", "isCommentAutoGenerated" : true }, - "Restore Brightness: %@" : { - "comment" : "A label under the slider that shows the current brightness level and allows the user to adjust it. The value shown is the brightness level as a percentage.", - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Helligkeit wiederherstellen: %@" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Restore Brightness: %@" - } - }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Restaurar brillo: %@" - } - }, - "fi" : { - "stringUnit" : { - "state" : "translated", - "value" : "Palauta kirkkaus: %@" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Restaurer la luminosité : %@" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ripristina luminosità: %@" - } - }, - "nb" : { - "stringUnit" : { - "state" : "translated", - "value" : "Gjenopprett lysstyrke: %@" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Helderheid herstellen: %@" - } - }, - "ru" : { - "stringUnit" : { - "state" : "translated", - "value" : "Восстановить яркость: %@" - } - } - } - }, "Restore Previous Brightness on Wake" : { "comment" : "A toggle that allows the user to restore the brightness level to its previous value when the device wakes up.", "localizations" : { @@ -16544,9 +16222,7 @@ } } }, - "Stop" : { - - }, + "Stop" : {}, "String Item" : { "comment" : "Display name for a string item type.", "isCommentAutoGenerated" : true, @@ -16731,61 +16407,6 @@ "comment" : "Shortcut title for switching between homes.", "isCommentAutoGenerated" : true }, - "Switch Item" : { - "comment" : "Display name for a switch item type.", - "extractionState" : "stale", - "isCommentAutoGenerated" : true, - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Switch-Item" - } - }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Elemento interruptor" - } - }, - "fi" : { - "stringUnit" : { - "state" : "translated", - "value" : "Kytkinelementti" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Élément interrupteur" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Elemento interruttore" - } - }, - "nb" : { - "stringUnit" : { - "state" : "translated", - "value" : "Bryterelement" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Schakelaar-item" - } - }, - "ru" : { - "stringUnit" : { - "state" : "translated", - "value" : "Элемент переключателя" - } - } - } - }, "Switch off" : { "comment" : "Displayed when the user disables a device.", "isCommentAutoGenerated" : true @@ -17467,61 +17088,6 @@ } } }, - "toggle" : { - "comment" : "Action to toggle a switch between on and off states", - "extractionState" : "stale", - "isCommentAutoGenerated" : true, - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Um eine Verbindung zu deinem lokalen openHAB-Server herzustellen, erlauben bitte den Zugriff auf das lokale Netzwerk, wenn du dazu aufgefordert wirst. Wenn du es zuvor abgelehnt hast, aktiviere es unter Einstellungen → Datenschutz & Sicherheit → Lokales Netzwerk." - } - }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Para conectarse a su servidor openHAB local, permita el acceso a la red local cuando se le solicite. Si lo denegó anteriormente, actívelo en Ajustes → Privacidad y seguridad → Red local." - } - }, - "fi" : { - "stringUnit" : { - "state" : "translated", - "value" : "Muodosta yhteys paikalliseen openHAB-palvelimeen sallimalla lähiverkkoyhteys pyydettäessä. Jos olet aiemmin kieltänyt sen, ota se käyttöön kohdassa Asetukset → Tietosuoja ja turvallisuus → Lähiverkko." - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Pour te connecter à ton serveur openHAB local, autorise l’accès au réseau local lorsque tu y es invité. Si tu l’as précédemment refusé, active-le dans Réglages → Confidentialité et sécurité → Réseau local." - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Per connetterti al tuo server openHAB locale, consenti l'accesso alla rete locale quando richiesto. Se in precedenza hai negato l'accesso, abilitalo in Impostazioni → Privacy e sicurezza → Rete locale." - } - }, - "nb" : { - "stringUnit" : { - "state" : "translated", - "value" : "For å koble til din lokale openHAB-server, vennligst tillat tilgang til lokalt nettverk når du blir bedt om det. Hvis du tidligere nektet det, aktiver det i Innstillinger → Personvern og sikkerhet → Lokalt nettverk." - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Om verbinding te maken met uw lokale openHAB-server, sta toegang tot het lokale netwerk toe wanneer hierom wordt gevraagd. Als u dit eerder heeft geweigerd, schakel het in via Instellingen → Privacy en beveiliging → Lokaal netwerk." - } - }, - "ru" : { - "stringUnit" : { - "state" : "translated", - "value" : "Для подключения к локальному серверу openHAB разрешите доступ к локальной сети при появлении запроса. Если вы ранее отказали, включите его в Настройки → Конфиденциальность и безопасность → Локальная сеть." - } - } - } - }, "Toggle" : { "comment" : "Display name for the toggle action in the SwitchAction enum.", "localizations" : { @@ -18649,4 +18215,4 @@ } }, "version" : "1.1" -} \ No newline at end of file +} From 3d06c9a1f698417ccab74774e71e24a7ab8e5ba6 Mon Sep 17 00:00:00 2001 From: openhab-bot Date: Mon, 6 Jul 2026 21:45:12 +0000 Subject: [PATCH 64/91] committed version bump: 3.4.8 (298) Signed-off-by: openhab-bot --- CHANGELOG.md | 60 ++++++++++++++++++++++++++++++++++++++++++++++++ Version.xcconfig | 4 ++-- 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e40cc5fd4..c4454cbce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,66 @@ ## [Unreleased] +- fix: remove 7 stale localization keys with no source references +- fix: remove stale %lld %% localization keys for screensaver percentage labels +- Make Circular Accessory Switch icon state aware (#1291) +- committed version bump: 3.4.7 (297) +- Migrate sitemap page updates from long-polling to SSE (#1168) +- Small Widget background watermark alignment adjustments (#1289) +- fix: show message body below title in notification list +- fix: add Localizable.xcstrings to widget extension target membership +- fix: remove duplicate knownRegions from project.pbxproj +- committed version bump: 3.4.6 (296) +- Allow for two lines label and value for iOS Lock Screen Accessories (#1286) +- Swiftlinting +- chore: apply linter fixes +- feat: add accessory widget previews for Switch and Sensor widgets +- feat: wide-display sitemap grid and full-width media rows +- refactor: remove UIImageView polling timer from MJPEG video stream +- committed version bump: 3.4.5 (294) +- Fix small widget content to be cntered +- committed version bump: 3.4.4 (293) +- committed version bump: 3.4.3 (292) +- fix: sync allowedItemTypes with query and add regression test +- feat: make Dimmer items selectable in Set Switch State intent +- chore: apply linter fixes +- fix: detect GIF images by magic bytes in ImageRowView +- committed version bump: 3.4.2 (290) +- fix: animate GIFs in the Image sitemap widget (#1280) +- Widget adjustments (#1279) +- fix: lock screen widgets now send commands on tap +- fix: notification ping now respects the ringer/silent switch (#1278) +- fix: add OpenHABShortcuts.swift to app target to resolve AppShortcuts.xcstrings phrase warnings +- committed version bump: 3.4.1 (287) +- fix: add provisioning profile for openHABWidgetExtension +- l10n: add Spanish, Finnish, Norwegian, and Russian translations +- l10n: switch German and French translations to informal address (du/tu) +- chore: remove 5 stale localization keys on feature branch +- l10n: add Spanish, Finnish, Norwegian, and Russian translations +- l10n: translate 2 new widget keys into all 9 languages +- chore: update openHABWidgetExtension scheme with askForAppToLaunch +- fix: restore project.pbxproj corrupted by bad 3-way merge of pbxproj +- chore: add shared schemes for openHABWidgetExtension and OpenHABWatchComplicationsExtension +- build: raise macOS minimum to 15 for AsyncSequence.Failure support +- chore: bump openHABTestsSwift deployment target to iOS 18.0 +- Merge develop + fix build warnings; add iOS home screen widgets with item display +- Sensor widget: format state values using stateDescription numberPattern +- Sensor widget: support 1/2/4 items per small/medium/large size +- Make ScrollToTop overlay button conditional (#1231) +- Bump minimum deployment targets to iOS 18 / watchOS 11 +- Use iOS 18 onScrollGeometryChange in SitemapPageView +- Use iOS 17 animation and scroll APIs +- Replace SetLocationValueIntent with SetActiveHomeIntent in AppShortcutsProvider +- Register SetActiveHomeIntent with AppShortcutsProvider +- Linting and AppIntents SSE warnings fixes +- Bump deployment targets to iOS 17.0 / watchOS 10.0 +- Convert AppShortcuts.strings to AppShortcuts.xcstrings String Catalog +- Fix AppIntentsMetadataProcessor synonym warnings in AppEnum cases +- Add AppShortcutsProvider and fix intent dialog gaps +- Upgrade SwiftFormatPlugin 0.58.7→0.61.1, SwiftLintPlugin 0.62.2→0.63.3 (#1225) +- ItemEventStream: backport SSE watchdog and self-contained network monitor +- openHABWidget: add interactive switch and sensor widgets + - Migrate sitemap page updates from long-polling to SSE (#1168) - Small Widget background watermark alignment adjustments (#1289) - fix: show message body below title in notification list diff --git a/Version.xcconfig b/Version.xcconfig index b22cfd738..9d52ac4c4 100644 --- a/Version.xcconfig +++ b/Version.xcconfig @@ -6,5 +6,5 @@ // https://developer.apple.com/documentation/xcode/adding-a-build-configuration-file-to-your-project -MARKETING_VERSION = 3.4.7 -CURRENT_PROJECT_VERSION = 297 +MARKETING_VERSION = 3.4.8 +CURRENT_PROJECT_VERSION = 298 From 08e93eb346d80725634cd80e215dddb1aa8693e1 Mon Sep 17 00:00:00 2001 From: DigiH <17110652+DigiH@users.noreply.github.com> Date: Tue, 7 Jul 2026 19:10:18 +0200 Subject: [PATCH 65/91] Widgets layout adjustments (#1292) Signed-off-by: DigiH <17110652+DigiH@users.noreply.github.com> --- openHABWidget/SensorWidgetEntryView.swift | 7 ++++++- openHABWidget/SwitchWidgetEntryView.swift | 6 +++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/openHABWidget/SensorWidgetEntryView.swift b/openHABWidget/SensorWidgetEntryView.swift index 2a887c3a2..39b2a8534 100644 --- a/openHABWidget/SensorWidgetEntryView.swift +++ b/openHABWidget/SensorWidgetEntryView.swift @@ -216,6 +216,9 @@ struct SensorSmallWidgetView: View { .font(.headline) .lineLimit(3) .minimumScaleFactor(0.7) + .multilineTextAlignment(.center) + .fixedSize(horizontal: false, vertical: true) + .layoutPriority(1) .padding(.top, 20) Spacer() @@ -225,6 +228,7 @@ struct SensorSmallWidgetView: View { .lineLimit(2) .minimumScaleFactor(0.5) .multilineTextAlignment(.center) + .fixedSize(horizontal: false, vertical: true) Spacer() } @@ -307,7 +311,7 @@ struct SensorLargeWidgetView: View { ForEach(filledSlots.indices, id: \.self) { index in SensorItemRow(slot: filledSlots[index]) .padding(.horizontal) - .padding(.vertical, 12) + .padding(.vertical, 18) if index < filledSlots.count - 1 { Divider() .padding(.leading) @@ -339,6 +343,7 @@ struct SensorAccessoryCircularView: View { .minimumScaleFactor(0.5) .multilineTextAlignment(.center) } + .offset(y: -1) } else { Image(systemSymbol: .gear) .font(.title3) diff --git a/openHABWidget/SwitchWidgetEntryView.swift b/openHABWidget/SwitchWidgetEntryView.swift index 92fd56eed..678695cd8 100644 --- a/openHABWidget/SwitchWidgetEntryView.swift +++ b/openHABWidget/SwitchWidgetEntryView.swift @@ -249,6 +249,9 @@ struct SwitchSmallWidgetView: View { .font(.headline) .lineLimit(3) .minimumScaleFactor(0.7) + .multilineTextAlignment(.center) + .fixedSize(horizontal: false, vertical: true) + .layoutPriority(1) .padding(.top, 20) Spacer() if let home = entry.home { @@ -256,7 +259,7 @@ struct SwitchSmallWidgetView: View { isOn: slot.item.state == "ON", intent: createToggleIntent(item: slot.item, homeUUID: slot.homeUUID, home: home) ) {} - .toggleStyle(PowerButtonToggleStyle(diameter: 44)) + .toggleStyle(PowerButtonToggleStyle(diameter: 44, showStateLabel: false)) } else { let isOn = slot.item.state == "ON" Image(systemSymbol: .power) @@ -394,6 +397,7 @@ struct SwitchAccessoryCircularView: View { .lineLimit(1) .minimumScaleFactor(0.5) } + .offset(y: -1) } } else { Image(systemSymbol: .gear) From c3a64051484f502617a2389a35d3414e4d687ed3 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Tue, 7 Jul 2026 21:38:34 +0200 Subject: [PATCH 66/91] fix: improve CarPlay robustness and localizations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Start network tracking from preferences before waitForActiveConnection so CarPlay works when it is the first scene to launch - Show actionable "Set up a CarPlay sitemap page in Settings" message when no sitemap is configured instead of the generic placeholder - Separate CarPlay entitlement into openHAB-CarPlay.entitlements so normal builds remain App Store-safe without Apple CarPlay approval - Add translations for carplay_not_configured(_detail) in all 9 project languages; remove stale "off"/"toggle" keys with corrupted translations - Rename OpenHABIconOverlay.swift → OpenHABIconWatermark.swift to match the declared type name Signed-off-by: Tim Mueller-Seydlitz --- openHAB/CarPlaySceneDelegate.swift | 31 +- .../Supporting Files/Localizable.xcstrings | 610 ++++++++++-------- .../openHAB-CarPlay.entitlements | 20 + openHAB/Supporting Files/openHAB.entitlements | 2 - ...erlay.swift => OpenHABIconWatermark.swift} | 0 5 files changed, 400 insertions(+), 263 deletions(-) create mode 100644 openHAB/Supporting Files/openHAB-CarPlay.entitlements rename openHABWidget/{OpenHABIconOverlay.swift => OpenHABIconWatermark.swift} (100%) diff --git a/openHAB/CarPlaySceneDelegate.swift b/openHAB/CarPlaySceneDelegate.swift index 33aa3620a..d2f28c207 100644 --- a/openHAB/CarPlaySceneDelegate.swift +++ b/openHAB/CarPlaySceneDelegate.swift @@ -70,6 +70,11 @@ final class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegat @MainActor private func runStream() async { + let prefs = Preferences.shared.currentHomePreferences + await NetworkTracker.shared.startTracking(connectionConfigurations: [ + prefs.localConnectionConfig, + prefs.remoteConnectionConfig + ]) guard let connection = await NetworkTracker.shared.waitForActiveConnection() else { Logger.carPlay.warning("CarPlay: no active connection") return @@ -77,6 +82,10 @@ final class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegat let sitemapName = Preferences.shared.currentHomePreferences.sitemapForCarPlay guard !sitemapName.isEmpty else { Logger.carPlay.info("CarPlay: no sitemap configured") + interfaceController?.setRootTemplate( + placeholderTemplate(message: String(localized: "carplay_not_configured_detail")), + animated: false, completion: nil + ) return } do { @@ -188,7 +197,10 @@ final class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegat } private func sfSymbol(for widget: OpenHABWidget) -> UIImage { + let targetSize = carPlayGridButtonImageSize() + let pointSize = min(targetSize.width, targetSize.height) let isOn = widget.displayState.isOn + let symbolName: SFSymbol = switch widget.icon.lowercased() { case "power", "poweroutlet", "poweroutlet_eu": isOn ? .powerCircleFill : .powerCircle @@ -197,8 +209,13 @@ final class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegat default: isOn ? .circleFill : .circle } + + let config = UIImage.SymbolConfiguration(pointSize: pointSize, weight: .regular) + return UIImage(systemSymbol: symbolName) + .applyingSymbolConfiguration(config)? .withRenderingMode(.alwaysTemplate) + ?? UIImage(systemSymbol: symbolName).withRenderingMode(.alwaysTemplate) } private func isCarPlayCompatible(_ widget: OpenHABWidget) -> Bool { @@ -208,6 +225,15 @@ final class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegat } } + private func carPlayGridButtonImageSize() -> CGSize { + if #available(iOS 26.0, *) { + return CPGridTemplate.maximumGridButtonImageSize + } + + // Conservative fallback for older CarPlay systems. + return CGSize(width: 80, height: 80) + } + private func makeGridButton(for widget: OpenHABWidget, service: OpenAPIService) -> CPGridButton { let ds = widget.displayState let image = sfSymbol(for: widget) @@ -259,9 +285,10 @@ final class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegat ?? UIImage() } - private func placeholderTemplate() -> CPGridTemplate { + private func placeholderTemplate(message: String? = nil) -> CPGridTemplate { + let label = message ?? String(localized: "carplay_not_configured") let button = CPGridButton( - titleVariants: [String(localized: "carplay_not_configured")], + titleVariants: [label], image: placeholderButtonImage() ) { _ in } return CPGridTemplate(title: "openHAB", gridButtons: [button]) diff --git a/openHAB/Supporting Files/Localizable.xcstrings b/openHAB/Supporting Files/Localizable.xcstrings index 5306f0ff1..bd75ea8e4 100644 --- a/openHAB/Supporting Files/Localizable.xcstrings +++ b/openHAB/Supporting Files/Localizable.xcstrings @@ -292,6 +292,18 @@ } } }, + "%@ • %@" : { + "comment" : "The item name and the name of the home the item belongs to.", + "isCommentAutoGenerated" : true, + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "%1$@ • %2$@" + } + } + } + }, "%@ Settings" : { "comment" : "The title of the settings view. The placeholder is replaced with the user's home name.", "localizations" : { @@ -410,6 +422,18 @@ } } }, + "%@: %@" : { + "comment" : "A label displaying the name of a sensor with its current value. The first argument is the label of the sensor. The second argument is the value of the sensor.", + "isCommentAutoGenerated" : true, + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "%1$@: %2$@" + } + } + } + }, "%lld" : { "comment" : "A label indicating that there are multiple commands currently being sent to the device. The number in the parentheses is the count of these commands.", "localizations" : { @@ -469,10 +493,6 @@ } } }, - "%lld %%" : { - "comment" : "A label that shows the percentage of brightness. The value is rounded to the nearest whole number.", - "isCommentAutoGenerated" : true - }, "%lldK" : { "comment" : "A text view displaying the selected color temperature in Kelvin. The value is shown in a smaller font next to a separator text (\" - \").", "localizations" : { @@ -825,6 +845,10 @@ } } }, + "Active" : { + "comment" : "Display name for the contact state \"Open\".", + "isCommentAutoGenerated" : true + }, "active_url" : { "extractionState" : "manual", "localizations" : { @@ -2124,24 +2148,121 @@ }, "carplay_not_configured" : { "comment" : "CarPlay root list placeholder shown before a CarPlay favorites page is set up.", + "extractionState" : "manual", "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine CarPlay-Favoriten konfiguriert" + } + }, "en" : { "stringUnit" : { "state" : "translated", "value" : "No CarPlay favorites configured" } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No hay favoritos de CarPlay configurados" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "CarPlay-suosikkeja ei ole määritetty" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucun favori CarPlay configuré" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nessun preferito CarPlay configurato" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen CarPlay-favoritter er konfigurert" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geen CarPlay-favorieten geconfigureerd" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Избранное CarPlay не настроено" + } } } }, "carplay_not_configured_detail" : { "comment" : "CarPlay root list placeholder detail text shown before a CarPlay favorites page is set up.", - "extractionState" : "stale", + "extractionState" : "manual", "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "CarPlay-Sitemapseite in den Einstellungen einrichten" + } + }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Set up a CarPlay sitemap page in Settings" } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configura una página de mapa del sitio de CarPlay en Ajustes" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Määritä CarPlay-sivustokarttasivu Asetuksissa" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configurez une page de plan de site CarPlay dans les réglages" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configura una pagina sitemap CarPlay nelle Impostazioni" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Konfigurer en CarPlay-nettstedskartside i Innstillinger" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stel een CarPlay-sitemappagina in via Instellingen" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Настройте страницу карты сайта CarPlay в Настройках" + } } } }, @@ -2850,7 +2971,6 @@ }, "Clock Size: %@" : { "comment" : "A label describing the percentage of the screen that is used for the clock face.", - "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -2908,10 +3028,6 @@ } } }, - "Clock Size: %lld %%" : { - "comment" : "A label that shows the current value of the clock size slider. The value is shown in percent.", - "isCommentAutoGenerated" : true - }, "closed" : { "extractionState" : "manual", "localizations" : { @@ -3369,6 +3485,30 @@ } } }, + "Configure" : { + "comment" : "A label displayed when a widget slot is not configured.", + "isCommentAutoGenerated" : true + }, + "Configure which sensor item to display in the widget." : { + "comment" : "Title of the intent for configuring a sensor widget.", + "isCommentAutoGenerated" : true + }, + "Configure which sensor items to display in the widget." : { + "comment" : "Title of the sensor widget configuration intent.", + "isCommentAutoGenerated" : true + }, + "Configure which switch item to control in the widget." : { + "comment" : "Title of the intent for configuring a switch widget.", + "isCommentAutoGenerated" : true + }, + "Configure which switch items to control in the widget." : { + "comment" : "Title of the intent to configure a widget that displays two switches.", + "isCommentAutoGenerated" : true + }, + "Configure Widget" : { + "comment" : "A description of the placeholder that appears when a widget is not configured.", + "isCommentAutoGenerated" : true + }, "connecting" : { "localizations" : { "de" : { @@ -3835,6 +3975,22 @@ } } }, + "Control an openHAB switch item" : { + "comment" : "Description of the openHAB Switch widget.", + "isCommentAutoGenerated" : true + }, + "Control Player" : { + "comment" : "Text displayed in the shortcut picker for controlling a player.", + "isCommentAutoGenerated" : true + }, + "Control two openHAB switch items" : { + "comment" : "Description of the openHAB Switch widget with 2 items.", + "isCommentAutoGenerated" : true + }, + "Control up to four openHAB switch items" : { + "comment" : "Description of the openHAB Switch widget for large screens.", + "isCommentAutoGenerated" : true + }, "Cool Daylight" : { "comment" : "A description for a color temperature between 6500 and 8000 Kelvin.", "localizations" : { @@ -5068,7 +5224,6 @@ }, "Dim Level: %@" : { "comment" : "A label displaying the current dim level of the screen saver. The value is shown as a percentage.", - "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -5126,110 +5281,10 @@ } } }, - "Dim Level: %lld %%" : { - "comment" : "A label displaying the current brightness level of the screen. The argument is the brightness level as a percentage.", - "isCommentAutoGenerated" : true - }, "Dimmer or Roller Item" : { "comment" : "Display name for a dimmer or roller item type.", "isCommentAutoGenerated" : true }, - "Dimmer or Roller Item" : { - "comment" : "Display name for a dimmer or roller item type.", - "isCommentAutoGenerated" : true, - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dimmer- oder Rolladen-Item" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dimmer or Roller Item" - } - }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Elemento regulador o persiana" - } - }, - "fi" : { - "stringUnit" : { - "state" : "translated", - "value" : "Himmennin- tai rullakaihdinelementti" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Élément variateur ou volet" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Elemento dimmer o tapparella" - } - }, - "nb" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dimmer- eller rullelukkelement" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dimmer- of rolluik-item" - } - }, - "ru" : { - "stringUnit" : { - "state" : "translated", - "value" : "Элемент диммера или ролеты" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Élément gradateur/volet" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Elemento dimmer/tapparella" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dimmer/Roller-item" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Élément gradateur/volet" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Elemento dimmer/tapparella" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dimmer/Roller-item" - } - } - } - }, "Disable" : { "comment" : "Displayed when the user disables a setting.", "isCommentAutoGenerated" : true @@ -5585,6 +5640,18 @@ } } }, + "Display an openHAB sensor value" : { + "comment" : "Description of the openHAB sensor widget.", + "isCommentAutoGenerated" : true + }, + "Display two openHAB sensor values" : { + "comment" : "Description of the SensorMedium widget.", + "isCommentAutoGenerated" : true + }, + "Display up to four openHAB sensor values" : { + "comment" : "Description of the SensorLarge widget.", + "isCommentAutoGenerated" : true + }, "Done" : { "comment" : "A button that dismisses a sheet.", "isCommentAutoGenerated" : true, @@ -5811,6 +5878,10 @@ } } }, + "Enable" : { + "comment" : "Display name for the \"Enable\" action.", + "isCommentAutoGenerated" : true + }, "Enable Dimming" : { "comment" : "A toggle that enables or disables automatic brightness dimming.", "localizations" : { @@ -6873,6 +6944,9 @@ } } } + }, + "Go backward" : { + }, "habpanel" : { "extractionState" : "manual", @@ -6991,6 +7065,9 @@ } } } + }, + "Hold" : { + }, "Home" : { "comment" : "The name of the \"Home\" menu item.", @@ -7015,8 +7092,8 @@ }, "fi" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Koti" } }, "fr" : { @@ -7033,8 +7110,8 @@ }, "nb" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Hjem" } }, "nl" : { @@ -8863,6 +8940,14 @@ } } }, + "Long press the widget to configure which sensors to display" : { + "comment" : "A description of the action to configure the widget.", + "isCommentAutoGenerated" : true + }, + "Long press the widget to configure which switches to control" : { + "comment" : "A description of the action to configure a widget.", + "isCommentAutoGenerated" : true + }, "Longitude" : { "comment" : "Parameter title for longitude in the Set Location Control Value app intent.", "localizations" : { @@ -9690,6 +9775,10 @@ } } }, + "No Data" : { + "comment" : "A label displayed when a sensor has no data.", + "isCommentAutoGenerated" : true + }, "No Image URL" : { "localizations" : { "de" : { @@ -9807,6 +9896,10 @@ } } }, + "No State" : { + "comment" : "A label displayed when a device is not available.", + "isCommentAutoGenerated" : true + }, "No Video URL" : { "comment" : "A message displayed when a video URL is not provided for a video row.", "localizations" : { @@ -10311,66 +10404,6 @@ } } }, - "off" : { - "comment" : "Label for the \"off\" state of a contact.", - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "aus" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "off" - } - }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Elemento numérico" - } - }, - "fi" : { - "stringUnit" : { - "state" : "translated", - "value" : "Numeraalielementti" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Élément numérique" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Elemento numerico" - } - }, - "nb" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tallselement" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Numeriek item" - } - }, - "ru" : { - "stringUnit" : { - "state" : "translated", - "value" : "Числовой элемент" - } - } - } - }, "Off" : { "comment" : "The text \"Off\" displayed in the accessibility value of the toggle.", "localizations" : { @@ -10394,8 +10427,8 @@ }, "fi" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Pois" } }, "fr" : { @@ -10412,8 +10445,8 @@ }, "nb" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Av" } }, "nl" : { @@ -10424,12 +10457,16 @@ }, "ru" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Выкл" } } } }, + "OFF" : { + "comment" : "A label that shows the state of a switch.", + "isCommentAutoGenerated" : true + }, "Offline" : { "comment" : "A label indicating that the device is currently offline.", "localizations" : { @@ -10748,8 +10785,8 @@ }, "fi" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Päällä" } }, "fr" : { @@ -10766,8 +10803,8 @@ }, "nb" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "På" } }, "nl" : { @@ -10778,12 +10815,16 @@ }, "ru" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Вкл" } } } }, + "ON" : { + "comment" : "A label that shows the state of a switch.", + "isCommentAutoGenerated" : true + }, "Once" : { "comment" : "Button to allow the app to connect to the server only once, even if the certificate is invalid.", "localizations" : { @@ -11010,6 +11051,10 @@ } } }, + "Opened" : { + "comment" : "A synonym for the \"Open\" contact state.", + "isCommentAutoGenerated" : true + }, "openHAB Cloud Service" : { "comment" : "A toggle that allows the user to enable or disable notifications from the openHAB Cloud Service.", "localizations" : { @@ -11069,6 +11114,14 @@ } } }, + "openHAB Sensor" : { + "comment" : "Widget title.", + "isCommentAutoGenerated" : true + }, + "openHAB Switch" : { + "comment" : "Widget name.", + "isCommentAutoGenerated" : true + }, "openhab_connection" : { "extractionState" : "manual", "localizations" : { @@ -12012,7 +12065,6 @@ }, "Relative Date: %@" : { "comment" : "A label describing the relative size of the date text compared to the clock text.", - "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -12070,10 +12122,6 @@ } } }, - "Relative Date: %lld %%" : { - "comment" : "A label that shows the relative size of the date font.", - "isCommentAutoGenerated" : true - }, "Remote server" : { "comment" : "Header text for the remote server section in the connection settings view.", "localizations" : { @@ -12320,7 +12368,6 @@ }, "Restore Brightness: %@" : { "comment" : "A label under the slider that shows the current brightness level and allows the user to adjust it. The value shown is the brightness level as a percentage.", - "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -12378,10 +12425,6 @@ } } }, - "Restore Brightness: %lld %%" : { - "comment" : "A label that shows the brightness level at which the screen saver restores the brightness of the device.", - "isCommentAutoGenerated" : true - }, "Restore Previous Brightness on Wake" : { "comment" : "A toggle that allows the user to restore the brightness level to its previous value when the device wakes up.", "localizations" : { @@ -12441,6 +12484,10 @@ } } }, + "Resume" : { + "comment" : "Action to resume playback.", + "isCommentAutoGenerated" : true + }, "Retrieve the current state of an item" : { "comment" : "Description of the Get Item State app intent.", "localizations" : { @@ -13375,6 +13422,30 @@ } } }, + "Sensor Item" : { + "comment" : "Label for the sensor item parameter in the sensor widget configuration intent.", + "isCommentAutoGenerated" : true + }, + "Sensor Item 1" : { + "comment" : "The first sensor item to display in the widget.", + "isCommentAutoGenerated" : true + }, + "Sensor Item 2" : { + "comment" : "Label for the second sensor item in the sensor widget configuration intent.", + "isCommentAutoGenerated" : true + }, + "Sensor Item 3" : { + "comment" : "Label for the sensor item 3 parameter in the sensor large widget configuration intent.", + "isCommentAutoGenerated" : true + }, + "Sensor Item 4" : { + "comment" : "Label for the sensor item 4 parameter in the sensor large widget configuration intent.", + "isCommentAutoGenerated" : true + }, + "Sensor Widget Configuration" : { + "comment" : "Title of the sensor widget configuration intent.", + "isCommentAutoGenerated" : true + }, "Sent %@ to %@" : { "comment" : "The result text displayed in the dialog. The argument is the action, and the second argument is the item label.", "isCommentAutoGenerated" : true, @@ -14051,6 +14122,10 @@ } } }, + "Set Color" : { + "comment" : "Short title for the shortcut to set a color value.", + "isCommentAutoGenerated" : true + }, "Set Color Control Value" : { "comment" : "Title of the Set Color Control Value app intent.", "localizations" : { @@ -14173,6 +14248,10 @@ } } }, + "Set Date & Time" : { + "comment" : "Short title for the shortcut to set a date and time.", + "isCommentAutoGenerated" : true + }, "Set DateTime Control Value" : { "comment" : "Title of the Set DateTime Control Value app intent.", "localizations" : { @@ -14232,6 +14311,10 @@ } } }, + "Set Dimmer / Roller" : { + "comment" : "Short title for a shortcut that allows the user to set the value of a dimmer or roller shutter.", + "isCommentAutoGenerated" : true + }, "Set Dimmer or Roller Shutter Value" : { "comment" : "Title of the Set Dimmer or Roller Shutter Value app intent.", "localizations" : { @@ -14350,6 +14433,10 @@ } } }, + "Set Number" : { + "comment" : "Text displayed in a shortcut for setting a number value.", + "isCommentAutoGenerated" : true + }, "Set Number Control Value" : { "comment" : "Title of the Set Number Control Value app intent.", "localizations" : { @@ -14527,6 +14614,10 @@ } } }, + "Set Switch" : { + "comment" : "Short title for a shortcut that allows the user to set a switch.", + "isCommentAutoGenerated" : true + }, "Set Switch State" : { "comment" : "Title of the Set Switch State app intent.", "localizations" : { @@ -14586,6 +14677,10 @@ } } }, + "Set Text" : { + "comment" : "Text displayed in a shortcut for setting a string value.", + "isCommentAutoGenerated" : true + }, "Set the color of a color control item" : { "comment" : "Description of the Set Color Control Value app intent.", "localizations" : { @@ -16188,6 +16283,10 @@ } } }, + "Speed up" : { + "comment" : "Synonyms for the \"Fast Forward\" action.", + "isCommentAutoGenerated" : true + }, "SSL error. The connection couldn’t be established securely." : { "comment" : "Error message displayed when a connection to the OpenHAB server fails due to a SSL error.", "localizations" : { @@ -16480,6 +16579,10 @@ } } }, + "Start" : { + "comment" : "A synonym for the \"Play\" action.", + "isCommentAutoGenerated" : true + }, "State" : { "comment" : "A label for a picker that lets the user select a state.", "localizations" : { @@ -16539,6 +16642,10 @@ } } }, + "Stop" : { + "comment" : "Synonyms for the pause action.", + "isCommentAutoGenerated" : true + }, "String Item" : { "comment" : "Display name for a string item type.", "isCommentAutoGenerated" : true, @@ -16656,6 +16763,10 @@ } } }, + "Switch" : { + "comment" : "The type of action to perform on a switch.", + "isCommentAutoGenerated" : true + }, "Switch Action" : { "comment" : "Type display name for the SwitchAction enum used in app intents.", "localizations" : { @@ -16715,6 +16826,10 @@ } } }, + "Switch Home" : { + "comment" : "Shortcut title for switching between homes.", + "isCommentAutoGenerated" : true + }, "Switch Item" : { "comment" : "Display name for a switch item type.", "isCommentAutoGenerated" : true, @@ -16769,6 +16884,22 @@ } } }, + "Switch Item 1" : { + "comment" : "Name of the first switch item to be configured in the widget.", + "isCommentAutoGenerated" : true + }, + "Switch Item 2" : { + "comment" : "Label for the second switch item in the widget configuration intent.", + "isCommentAutoGenerated" : true + }, + "Switch Item 3" : { + "comment" : "Label for the third switch item in the widget configuration.", + "isCommentAutoGenerated" : true + }, + "Switch Item 4" : { + "comment" : "Label for the fourth switch item in the widget configuration.", + "isCommentAutoGenerated" : true + }, "Switch off" : { "comment" : "Displayed when the user disables a device.", "isCommentAutoGenerated" : true @@ -16835,6 +16966,10 @@ } } }, + "Switch Widget Configuration" : { + "comment" : "Title of the intent to configure a switch widget.", + "isCommentAutoGenerated" : true + }, "sync_prefs" : { "extractionState" : "manual", "localizations" : { @@ -17450,61 +17585,6 @@ } } }, - "toggle" : { - "comment" : "Action to toggle a switch between on and off states", - "extractionState" : "stale", - "isCommentAutoGenerated" : true, - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Um eine Verbindung zu deinem lokalen openHAB-Server herzustellen, erlauben bitte den Zugriff auf das lokale Netzwerk, wenn du dazu aufgefordert wirst. Wenn du es zuvor abgelehnt hast, aktiviere es unter Einstellungen → Datenschutz & Sicherheit → Lokales Netzwerk." - } - }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Para conectarse a su servidor openHAB local, permita el acceso a la red local cuando se le solicite. Si lo denegó anteriormente, actívelo en Ajustes → Privacidad y seguridad → Red local." - } - }, - "fi" : { - "stringUnit" : { - "state" : "translated", - "value" : "Muodosta yhteys paikalliseen openHAB-palvelimeen sallimalla lähiverkkoyhteys pyydettäessä. Jos olet aiemmin kieltänyt sen, ota se käyttöön kohdassa Asetukset → Tietosuoja ja turvallisuus → Lähiverkko." - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Pour te connecter à ton serveur openHAB local, autorise l’accès au réseau local lorsque tu y es invité. Si tu l’as précédemment refusé, active-le dans Réglages → Confidentialité et sécurité → Réseau local." - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Per connetterti al tuo server openHAB locale, consenti l'accesso alla rete locale quando richiesto. Se in precedenza hai negato l'accesso, abilitalo in Impostazioni → Privacy e sicurezza → Rete locale." - } - }, - "nb" : { - "stringUnit" : { - "state" : "translated", - "value" : "For å koble til din lokale openHAB-server, vennligst tillat tilgang til lokalt nettverk når du blir bedt om det. Hvis du tidligere nektet det, aktiver det i Innstillinger → Personvern og sikkerhet → Lokalt nettverk." - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Om verbinding te maken met uw lokale openHAB-server, sta toegang tot het lokale netwerk toe wanneer hierom wordt gevraagd. Als u dit eerder heeft geweigerd, schakel het in via Instellingen → Privacy en beveiliging → Lokaal netwerk." - } - }, - "ru" : { - "stringUnit" : { - "state" : "translated", - "value" : "Для подключения к локальному серверу openHAB разрешите доступ к локальной сети при появлении запроса. Если вы ранее отказали, включите его в Настройки → Конфиденциальность и безопасность → Локальная сеть." - } - } - } - }, "Toggle" : { "comment" : "Display name for the toggle action in the SwitchAction enum.", "localizations" : { @@ -17522,14 +17602,14 @@ }, "es" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Alternar" } }, "fi" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Vaihda" } }, "fr" : { @@ -17558,12 +17638,24 @@ }, "ru" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Переключить" } } } }, + "Triggered" : { + "comment" : "Display name for the contact state \"OPEN\".", + "isCommentAutoGenerated" : true + }, + "Turn off" : { + "comment" : "Turn off action.", + "isCommentAutoGenerated" : true + }, + "Turn on" : { + "comment" : "Turn on action.", + "isCommentAutoGenerated" : true + }, "unable_to_add_certificate" : { "extractionState" : "manual", "localizations" : { @@ -18620,4 +18712,4 @@ } }, "version" : "1.1" -} \ No newline at end of file +} diff --git a/openHAB/Supporting Files/openHAB-CarPlay.entitlements b/openHAB/Supporting Files/openHAB-CarPlay.entitlements new file mode 100644 index 000000000..ffe558678 --- /dev/null +++ b/openHAB/Supporting Files/openHAB-CarPlay.entitlements @@ -0,0 +1,20 @@ + + + + + aps-environment + development + com.apple.developer.carplay-driving-task + + com.apple.developer.siri + + com.apple.security.application-groups + + group.org.openhab.app + + keychain-access-groups + + $(AppIdentifierPrefix)org.openhab.app + + + diff --git a/openHAB/Supporting Files/openHAB.entitlements b/openHAB/Supporting Files/openHAB.entitlements index ffe558678..0c740482e 100644 --- a/openHAB/Supporting Files/openHAB.entitlements +++ b/openHAB/Supporting Files/openHAB.entitlements @@ -4,8 +4,6 @@ aps-environment development - com.apple.developer.carplay-driving-task - com.apple.developer.siri com.apple.security.application-groups diff --git a/openHABWidget/OpenHABIconOverlay.swift b/openHABWidget/OpenHABIconWatermark.swift similarity index 100% rename from openHABWidget/OpenHABIconOverlay.swift rename to openHABWidget/OpenHABIconWatermark.swift From 62ef00028525596ac9775fb4b4bfab5ed9961631 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Tue, 7 Jul 2026 21:38:53 +0200 Subject: [PATCH 67/91] fix: use LocalizedStringResource in AppIntents display representations Replaces plain string literals with LocalizedStringResource so Xcode's string extractor can discover them and the App Intents framework returns properly localized strings to Siri and Shortcuts. Signed-off-by: Tim Mueller-Seydlitz --- AppIntents/Home.swift | 4 +++- AppIntents/SwitchAction.swift | 31 +++++++++++++++++++++++++++---- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/AppIntents/Home.swift b/AppIntents/Home.swift index 311a2ebf5..c2cc28e7e 100644 --- a/AppIntents/Home.swift +++ b/AppIntents/Home.swift @@ -219,7 +219,9 @@ struct Home: AppEntity { } } - static let typeDisplayRepresentation = TypeDisplayRepresentation(name: "Home") + static let typeDisplayRepresentation = TypeDisplayRepresentation( + name: LocalizedStringResource("Home", defaultValue: "Home") + ) static let defaultQuery = HomeQuery() diff --git a/AppIntents/SwitchAction.swift b/AppIntents/SwitchAction.swift index e50773d66..989963bdd 100644 --- a/AppIntents/SwitchAction.swift +++ b/AppIntents/SwitchAction.swift @@ -17,12 +17,35 @@ enum SwitchAction: String, AppEnum { case off = "OFF" case toggle = "TOGGLE" - static let typeDisplayRepresentation = TypeDisplayRepresentation(name: "Switch Action") + static let typeDisplayRepresentation = TypeDisplayRepresentation( + name: LocalizedStringResource("Switch Action", defaultValue: "Switch Action") + ) static let caseDisplayRepresentations: [Self: DisplayRepresentation] = [ - .on: DisplayRepresentation(title: "On", synonyms: ["Turn on", "Switch on", "Enable"]), - .off: DisplayRepresentation(title: "Off", synonyms: ["Turn off", "Switch off", "Disable"]), - .toggle: DisplayRepresentation(title: "Toggle", synonyms: ["Flip", "Switch", "Change"]) + .on: DisplayRepresentation( + title: LocalizedStringResource("On", defaultValue: "On"), + synonyms: [ + LocalizedStringResource("Turn on", defaultValue: "Turn on"), + LocalizedStringResource("Switch on", defaultValue: "Switch on"), + LocalizedStringResource("Enable", defaultValue: "Enable") + ] + ), + .off: DisplayRepresentation( + title: LocalizedStringResource("Off", defaultValue: "Off"), + synonyms: [ + LocalizedStringResource("Turn off", defaultValue: "Turn off"), + LocalizedStringResource("Switch off", defaultValue: "Switch off"), + LocalizedStringResource("Disable", defaultValue: "Disable") + ] + ), + .toggle: DisplayRepresentation( + title: LocalizedStringResource("Toggle", defaultValue: "Toggle"), + synonyms: [ + LocalizedStringResource("Flip", defaultValue: "Flip"), + LocalizedStringResource("Switch", defaultValue: "Switch"), + LocalizedStringResource("Change", defaultValue: "Change") + ] + ) ] } From 5fce976416e5513551f4f02bb96f3c85b788d5e8 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Wed, 8 Jul 2026 08:30:32 +0200 Subject: [PATCH 68/91] chore: switch main app target to CarPlay entitlements file Enables the com.apple.developer.carplay-driving-task entitlement now that Apple has approved the capability for this account. Signed-off-by: Tim Mueller-Seydlitz --- openHAB.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openHAB.xcodeproj/project.pbxproj b/openHAB.xcodeproj/project.pbxproj index 0a6654362..5d36f40a8 100644 --- a/openHAB.xcodeproj/project.pbxproj +++ b/openHAB.xcodeproj/project.pbxproj @@ -2259,7 +2259,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_CXX_LANGUAGE_STANDARD = "$(inherited)"; CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = "openHAB/Supporting Files/openHAB.entitlements"; + CODE_SIGN_ENTITLEMENTS = "openHAB/Supporting Files/openHAB-CarPlay.entitlements"; ENABLE_APP_SANDBOX = YES; ENABLE_DEBUG_DYLIB = YES; ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES; @@ -2303,7 +2303,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_CXX_LANGUAGE_STANDARD = "$(inherited)"; CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = "openHAB/Supporting Files/openHAB.entitlements"; + CODE_SIGN_ENTITLEMENTS = "openHAB/Supporting Files/openHAB-CarPlay.entitlements"; CODE_SIGN_IDENTITY = "Apple Distribution"; CODE_SIGN_STYLE = Manual; ENABLE_APP_SANDBOX = YES; From c2fe5f5afe69025b381429dd6eaa5f995d2a4be3 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Wed, 8 Jul 2026 21:13:40 +0200 Subject: [PATCH 69/91] refactor: rename OpenHABIconOverlay to OpenHABIconWatermark and complete translations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rename OpenHABIconOverlay.swift → OpenHABIconWatermark.swift to better reflect its purpose as a watermark overlay on widget preview images - Complete missing translations in Localizable.xcstrings for all supported languages, resolving all NEW-state entries Signed-off-by: Tim Mueller-Seydlitz --- .../Supporting Files/Localizable.xcstrings | 9930 ++++++++++++----- ...erlay.swift => OpenHABIconWatermark.swift} | 0 2 files changed, 6982 insertions(+), 2948 deletions(-) rename openHABWidget/{OpenHABIconOverlay.swift => OpenHABIconWatermark.swift} (100%) diff --git a/openHAB/Supporting Files/Localizable.xcstrings b/openHAB/Supporting Files/Localizable.xcstrings index 2bfc76490..3c5ae1bb0 100644 --- a/openHAB/Supporting Files/Localizable.xcstrings +++ b/openHAB/Supporting Files/Localizable.xcstrings @@ -17,43 +17,43 @@ }, "es" : { "stringUnit" : { - "state" : "new", + "state" : "translated", "value" : "" } }, "fi" : { "stringUnit" : { - "state" : "new", + "state" : "translated", "value" : "" } }, "fr" : { "stringUnit" : { - "state" : "new", + "state" : "translated", "value" : "" } }, "it" : { "stringUnit" : { - "state" : "new", + "state" : "translated", "value" : "" } }, "nb" : { "stringUnit" : { - "state" : "new", + "state" : "translated", "value" : "" } }, "nl" : { "stringUnit" : { - "state" : "new", + "state" : "translated", "value" : "" } }, "ru" : { "stringUnit" : { - "state" : "new", + "state" : "translated", "value" : "" } } @@ -121,7 +121,6 @@ }, "__item_state_passthrough__" : { "comment" : "Placeholder for a numeric or custom item state that should be displayed as-is.", - "extractionState" : "extracted_with_value", "isCommentAutoGenerated" : true, "localizations" : { "de" : { @@ -132,7 +131,7 @@ }, "en" : { "stringUnit" : { - "state" : "new", + "state" : "translated", "value" : "%@" } }, @@ -298,7 +297,7 @@ "localizations" : { "en" : { "stringUnit" : { - "state" : "new", + "state" : "translated", "value" : "%1$@ • %2$@" } } @@ -422,6 +421,64 @@ } } }, + "%@: %@" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@: %2$@" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@: %2$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@: %2$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@: %2$@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ : %2$@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@: %2$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@: %2$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@: %2$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@: %2$@" + } + } + } + }, "%lld" : { "comment" : "A label indicating that there are multiple commands currently being sent to the device. The number in the parentheses is the count of these commands.", "localizations" : { @@ -835,7 +892,57 @@ }, "Active" : { "comment" : "Display name for the contact state \"Open\".", - "isCommentAutoGenerated" : true + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktiv" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activo" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktiivinen" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actif" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Attivo" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktiv" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actief" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Активен" + } + } + } }, "active_url" : { "extractionState" : "manual", @@ -1605,7 +1712,57 @@ }, "Begin" : { "comment" : "The action to play music.", - "isCommentAutoGenerated" : true + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Beginnen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Comenzar" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aloita" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Commencer" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inizia" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Begynn" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Beginnen" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Начать" + } + } + } }, "bonjour_discovery_disclaimer" : { "localizations" : { @@ -2369,7 +2526,57 @@ }, "Change" : { "comment" : "Display representation for the toggle action.", - "isCommentAutoGenerated" : true + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ändern" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cambiar" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Muuta" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Changer" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modifica" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Endre" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wijzigen" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Изменить" + } + } + } }, "Check & Clear Image Cache" : { "comment" : "A button that triggers a check and clear action for the image cache.", @@ -2837,6 +3044,60 @@ } } }, + "Clock Size: %@" : { + "comment" : "A label that shows the current value of the clock size slider.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uhrengröße: %@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tamaño del reloj: %@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kellon koko: %@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Taille de l'horloge : %@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dimensione orologio: %@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Klokkestørrelse: %@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Klokgrootte: %@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Размер часов: %@" + } + } + } + }, "closed" : { "extractionState" : "manual", "localizations" : { @@ -3294,13181 +3555,16804 @@ } } }, - "connecting" : { + "Configure" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Verbinden" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Connecting" + "value" : "Konfigurieren" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Conectando" + "value" : "Configurar" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Connecting" + "value" : "Määritä" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Connexion en cours" + "value" : "Configurer" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Connessione" + "value" : "Configura" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Kobler til" + "value" : "Konfigurer" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Verbinden" + "value" : "Configureren" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Connecting" + "value" : "Настроить" } } } }, - "Connecting" : { - "comment" : "A label displayed while waiting for a connection to the OpenHAB server.", + "Configure which sensor item to display in the widget." : { + "comment" : "Title of the intent for configuring a sensor widget.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Verbinden" + "value" : "Konfigurieren Sie, welches Sensorelement im Widget angezeigt werden soll." } }, - "en" : { + "es" : { "stringUnit" : { "state" : "translated", - "value" : "Connecting" + "value" : "Configure qué elemento sensor mostrar en el widget." } }, - "es" : { + "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Conectando" - } - }, - "fi" : { - "stringUnit" : { - "state" : "translated", - "value" : "Yhdistetään" + "value" : "Määritä, mikä anturikohde näytetään widgetissä." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Connexion" + "value" : "Configurez quel élément capteur afficher dans le widget." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Connetto" + "value" : "Configura quale elemento sensore mostrare nel widget." } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Kobler til" + "value" : "Konfigurer hvilket sensorelement som skal vises i widgeten." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Verbinden" + "value" : "Configureer welk sensoritem in de widget wordt weergegeven." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Подключение" + "value" : "Настройте, какой элемент датчика отображать в виджете." } } } }, - "connecting_discovered" : { - "extractionState" : "manual", + "Configure which sensor items to display in the widget." : { + "comment" : "Title of the sensor widget configuration intent.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Mit entdeckter URL verbinden" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Connecting to discovered URL" + "value" : "Konfigurieren Sie, welche Sensorelemente im Widget angezeigt werden sollen." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Conectando a URL descubierta" + "value" : "Configure qué elementos de sensor mostrar en el widget." } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Connecting to discovered URL" + "value" : "Määritä, mitkä anturikohteet näytetään widgetissä." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Connexion à l'URL découverte" + "value" : "Configurez quels éléments capteur afficher dans le widget." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Connessione all'URL trovato" + "value" : "Configura quali elementi sensore mostrare nel widget." } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Kobler til oppdaget URL" + "value" : "Konfigurer hvilke sensorelementer som skal vises i widgeten." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Verbinden naar gevonden URL" + "value" : "Configureer welke sensorItems in de widget worden weergegeven." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Connecting to discovered URL" + "value" : "Настройте, какие элементы датчиков отображать в виджете." } } } }, - "connecting_local" : { - "extractionState" : "manual", + "Configure which switch item to control in the widget." : { + "comment" : "Title of the intent for configuring a switch widget.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Mit lokaler URL verbinden" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Connecting to local URL" + "value" : "Konfigurieren Sie, welches Schalterelement im Widget gesteuert werden soll." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Conectando a la URL local" + "value" : "Configure qué elemento de interruptor controlar en el widget." } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Connecting to local URL" + "value" : "Määritä, mitä kytkinkohde ohjataan widgetissä." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Connexion à l'adresse locale" + "value" : "Configurez quel élément commutateur contrôler dans le widget." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Connessione a URL locale" + "value" : "Configura quale elemento interruttore controllare nel widget." } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Kobler til lokal URL" + "value" : "Konfigurer hvilket brytterelement som skal styres i widgeten." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Verbinden naar lokale URL" + "value" : "Configureer welk schakelaaritem in de widget wordt bediend." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Connecting to local URL" + "value" : "Настройте, каким элементом переключателя управлять в виджете." } } } }, - "connecting_remote" : { - "extractionState" : "manual", + "Configure which switch items to control in the widget." : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Mit Remote-URL verbinden" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Connecting to remote URL" + "value" : "Konfigurieren Sie, welche Schalterelemente im Widget gesteuert werden sollen." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Conectando a la URL remota" + "value" : "Configure qué elementos de interruptor controlar en el widget." } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Connecting to remote URL" + "value" : "Määritä, mitä kytkinkohteita ohjataan widgetissä." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Connexion à l'URL distante" + "value" : "Configurez quels éléments commutateur contrôler dans le widget." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Connessione a URL remoto" + "value" : "Configura quali elementi interruttore controllare nel widget." } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Connecting to remote URL" + "value" : "Konfigurer hvilke bryterelementer som skal styres i widgeten." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Verbinden met externe URL" + "value" : "Configureer welke schakelaaritems in de widget worden bediend." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Connecting to remote URL" + "value" : "Настройте, какими элементами переключателей управлять в виджете." } } } }, - "Connection successful" : { - "comment" : "Message displayed when a network connection test is successful.", + "Configure Widget" : { + "comment" : "A description of the placeholder that appears when a widget is not configured.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Verbindung erfolgreich" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Connection successful" + "value" : "Widget konfigurieren" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Conexión exitosa" + "value" : "Configurar widget" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Yhteys muodostettu" + "value" : "Määritä widget" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Connexion réussie" + "value" : "Configurer le widget" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Connessione riuscita" + "value" : "Configura widget" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Tilkobling vellykket" + "value" : "Konfigurer widget" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Verbinding geslaagd" + "value" : "Widget configureren" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Соединение установлено" + "value" : "Настроить виджет" } } } }, - "Contact Item" : { - "comment" : "Display name for a contact item type.", - "isCommentAutoGenerated" : true, + "connecting" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Kontakt-Item" + "value" : "Verbinden" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecting" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Elemento de contacto" + "value" : "Conectando" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Kontaktielementti" + "value" : "Connecting" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Élément de contact" + "value" : "Connexion en cours" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Elemento contatto" + "value" : "Connessione" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Kontaktelement" + "value" : "Kobler til" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Contact-item" + "value" : "Verbinden" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Элемент контакта" + "value" : "Connecting" } } } }, - "Contact State" : { - "comment" : "Type display name for the ContactState enum used in app intents.", + "Connecting" : { + "comment" : "A label displayed while waiting for a connection to the OpenHAB server.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Kontakt-Status" + "value" : "Verbinden" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Contact State" + "value" : "Connecting" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Estado de contacto" + "value" : "Conectando" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Kontaktin tila" + "value" : "Yhdistetään" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "État du contact" + "value" : "Connexion" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Stato contatto" + "value" : "Connetto" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Kontaktstatus" + "value" : "Kobler til" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Contactstatus" + "value" : "Verbinden" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Состояние контакта" + "value" : "Подключение" } } } }, - "Control Player" : { - "comment" : "Text displayed in the shortcut picker for controlling a player.", - "isCommentAutoGenerated" : true - }, - "Cool Daylight" : { - "comment" : "A description for a color temperature between 6500 and 8000 Kelvin.", + "connecting_discovered" : { + "extractionState" : "manual", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Kaltes Tageslicht" + "value" : "Mit entdeckter URL verbinden" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Cool Daylight" + "value" : "Connecting to discovered URL" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Luz diurna fría" + "value" : "Conectando a URL descubierta" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Viileä päivänvalo" + "value" : "Connecting to discovered URL" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Lumière du jour froide" + "value" : "Connexion à l'URL découverte" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Luce del giorno fredda" + "value" : "Connessione all'URL trovato" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Kjølig dagslys" + "value" : "Kobler til oppdaget URL" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Koel daglicht" + "value" : "Verbinden naar gevonden URL" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Холодный дневной свет" + "value" : "Connecting to discovered URL" } } } }, - "Cool White" : { - "comment" : "A color temperature range that maps to \"Cool White\".", + "connecting_local" : { + "extractionState" : "manual", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Kaltweiß" + "value" : "Mit lokaler URL verbinden" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Cool White" + "value" : "Connecting to local URL" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Blanco frío" + "value" : "Conectando a la URL local" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Viileä valkoinen" + "value" : "Connecting to local URL" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Blanc froid" + "value" : "Connexion à l'adresse locale" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Bianco freddo" + "value" : "Connessione a URL locale" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Kjølig hvit" + "value" : "Kobler til lokal URL" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Koel wit" + "value" : "Verbinden naar lokale URL" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Холодный белый" + "value" : "Connecting to local URL" } } } }, - "Copy" : { - "comment" : "Label for a button that copies the text of a text row to the clipboard.", + "connecting_remote" : { + "extractionState" : "manual", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Kopieren" + "value" : "Mit Remote-URL verbinden" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Copy" + "value" : "Connecting to remote URL" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Copiar" + "value" : "Conectando a la URL remota" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Kopioi" + "value" : "Connecting to remote URL" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Copier" + "value" : "Connexion à l'URL distante" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Copia" + "value" : "Connessione a URL remoto" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Kopier" + "value" : "Connecting to remote URL" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Kopieer" + "value" : "Verbinden met externe URL" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Копировать" + "value" : "Connecting to remote URL" } } } }, - "copy_label" : { - "extractionState" : "manual", + "Connection successful" : { + "comment" : "Message displayed when a network connection test is successful.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Item-Bezeichnung kopieren" + "value" : "Verbindung erfolgreich" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Copy item label" + "value" : "Connection successful" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Copiar etiqueta del ítem" + "value" : "Conexión exitosa" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Copy item label" + "value" : "Yhteys muodostettu" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Copier le label de l'item" + "value" : "Connexion réussie" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Copia etichetta Item" + "value" : "Connessione riuscita" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Kopier Item-etikett" + "value" : "Tilkobling vellykket" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Item label kopiëren" + "value" : "Verbinding geslaagd" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Copy item label" + "value" : "Соединение установлено" } } } }, - "Crash Reporting" : { - "comment" : "A toggle that allows the user to enable or disable crash reporting.", + "Contact Item" : { + "comment" : "Display name for a contact item type.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Absturzberichterstattung" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Crash Reporting" + "value" : "Kontakt-Item" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Informe de fallos" + "value" : "Elemento de contacto" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Kaatumisraportointi" + "value" : "Kontaktielementti" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Rapport de plantage" + "value" : "Élément de contact" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Segnalazione arresti anomali" + "value" : "Elemento contatto" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Krasjirapportering" + "value" : "Kontaktelement" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Crashrapportage" + "value" : "Contact-item" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Отчёты о сбоях" + "value" : "Элемент контакта" } } } }, - "crash_detected" : { + "Contact State" : { + "comment" : "Type display name for the ContactState enum used in app intents.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Absturz festgestellt" + "value" : "Kontakt-Status" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Crash detected" + "value" : "Contact State" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Error detectado" + "value" : "Estado de contacto" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Crash detected" + "value" : "Kontaktin tila" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Crash détecté" + "value" : "État du contact" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Crash rilevato" + "value" : "Stato contatto" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Krasj oppdaget" + "value" : "Kontaktstatus" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Crash gedetecteerd" + "value" : "Contactstatus" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Crash detected" + "value" : "Состояние контакта" } } } }, - "crash_reporting" : { + "Control an openHAB switch item" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Absturzberichterstattung" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Crash Reporting" + "value" : "Ein openHAB-Schalterelement steuern" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Informe de errores" + "value" : "Controlar un elemento de interruptor de openHAB" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Crash Reporting" + "value" : "Ohjaa openHAB-kytkinkohde" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Rapports d'erreurs" + "value" : "Contrôler un élément commutateur openHAB" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Segnalazioni di crash" + "value" : "Controlla un elemento interruttore openHAB" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Krasjrapportering" + "value" : "Styr et openHAB-brytterelement" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Foutenrapportering" + "value" : "Een openHAB-schakelaaritem bedienen" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Crash Reporting" + "value" : "Управлять элементом переключателя openHAB" } } } }, - "crash_reporting_info" : { + "Control Player" : { + "comment" : "Text displayed in the shortcut picker for controlling a player.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Durch die Aktivierung der Absturzberichterstattung stimmst du zu, dass Informationen zu deinem Gerät und der Nutzung der App erfasst und mit Crashlytics (einem Unternehmen von Google) geteilt werden. Weitere Informationen findest du in unserer Datenschutzerklärung." - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "By activating crash reporting you agree that device and usage information will be collected and shared with Crashlytics (a Google company). For further information view our privacy policy." + "value" : "Player steuern" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Al activar los informes de fallos, usted acepta que la información de uso y del dispositivo se recopilará y compartirá con Crashlytics (una empresa de Google). Para obtener más información, consulte nuestra política de privacidad." + "value" : "Controlar reproductor" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "By activating crash reporting you agree that device and usage information will be collected and shared with Crashlytics (a Google company). For further information view our privacy policy." + "value" : "Ohjaa soitinta" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "En activant le rapport de d’erreurs, tu acceptes que les informations relatives à l’appareil et à son utilisation soient collectées et partagées avec Crashlytics (une entreprise Google). Pour plus d’informations, consulte notre politique de confidentialité." + "value" : "Contrôler le lecteur" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Attivando la segnalazione di crash si accetta che le informazioni sul dispositivo e sull'utilizzo saranno raccolte e condivise con Crashlytics (un'azienda di Google). Per ulteriori informazioni consultare la nostra informativa sulla privacy." + "value" : "Controlla il lettore" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Ved å aktivere krasjrapportering samtykker du i at enhet og bruksinformasjon samles inn og deles med Crashlytics (et Google-firma). For mer informasjon se våre retningslinjer for personvern." + "value" : "Kontroller avspiller" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Door het activeren van crash-rapportage gaat u akkoord dat apparaat- en gebruiksinformatie zal worden verzameld en gedeeld met Crashlytics (een Google-bedrijf). Voor meer informatie bekijk ons privacybeleid." + "value" : "Speler bedienen" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "By activating crash reporting you agree that device and usage information will be collected and shared with Crashlytics (a Google company). For further information view our privacy policy." + "value" : "Управлять плеером" } } } }, - "Create" : { - "comment" : "The text on a button that creates a new home.", + "Control two openHAB switch items" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Erstellen" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Create" + "value" : "Zwei openHAB-Schalterelemente steuern" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Crear" + "value" : "Controlar dos elementos de interruptor de openHAB" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Luo" + "value" : "Ohjaa kahta openHAB-kytkinkohteita" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Créer" + "value" : "Contrôler deux éléments commutateur openHAB" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Crea" + "value" : "Controlla due elementi interruttore openHAB" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Opprett" + "value" : "Styr to openHAB-brytterelementer" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Maak aan" + "value" : "Twee openHAB-schakelaaritems bedienen" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Создать" + "value" : "Управлять двумя элементами переключателей openHAB" } } } }, - "Date and Time" : { - "comment" : "Parameter title for date and time in the Set DateTime Control Value app intent.", + "Control up to four openHAB switch items" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Datum und Uhrzeit" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Date and Time" + "value" : "Bis zu vier openHAB-Schalterelemente steuern" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Fecha y hora" + "value" : "Controlar hasta cuatro elementos de interruptor de openHAB" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Päivämäärä ja aika" + "value" : "Ohjaa enintään neljää openHAB-kytkinkohteita" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Date et heure" + "value" : "Contrôler jusqu'à quatre éléments commutateur openHAB" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Data e ora" + "value" : "Controlla fino a quattro elementi interruttore openHAB" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Dato og tid" + "value" : "Styr opptil fire openHAB-brytterelementer" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Datum en tijd" + "value" : "Maximaal vier openHAB-schakelaaritems bedienen" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Дата и время" + "value" : "Управлять до четырёх элементов переключателей openHAB" } } } }, - "DateTime Item" : { - "comment" : "Display name for a DateTimeItemEntity.", - "isCommentAutoGenerated" : true, + "Cool Daylight" : { + "comment" : "A description for a color temperature between 6500 and 8000 Kelvin.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "DateTime-Item" + "value" : "Kaltes Tageslicht" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cool Daylight" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Elemento DateTime" + "value" : "Luz diurna fría" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "DateTime-elementti" + "value" : "Viileä päivänvalo" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Élément DateHeure" + "value" : "Lumière du jour froide" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Elemento DateTime" + "value" : "Luce del giorno fredda" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "DateTime-element" + "value" : "Kjølig dagslys" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "DateTime-item" + "value" : "Koel daglicht" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Элемент DateTime" + "value" : "Холодный дневной свет" } } } }, - "Daylight" : { - "comment" : "A descriptive label for a color temperature range.", + "Cool White" : { + "comment" : "A color temperature range that maps to \"Cool White\".", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Tageslicht" + "value" : "Kaltweiß" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Daylight" + "value" : "Cool White" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Luz diurna" + "value" : "Blanco frío" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Päivänvalo" + "value" : "Viileä valkoinen" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Lumière du jour" + "value" : "Blanc froid" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Luce del giorno" + "value" : "Bianco freddo" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Dagslys" + "value" : "Kjølig hvit" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Daglicht" + "value" : "Koel wit" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Дневной свет" + "value" : "Холодный белый" } } } }, - "debug" : { - "comment" : "Section header text in the Debug Settings view.", + "Copy" : { + "comment" : "Label for a button that copies the text of a text row to the clipboard.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Debug" + "value" : "Kopieren" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Debug" + "value" : "Copy" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Debug" + "value" : "Copiar" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Debug" + "value" : "Kopioi" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Debug" + "value" : "Copier" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Debug" + "value" : "Copia" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Debug" + "value" : "Kopier" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Debug" + "value" : "Kopieer" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Debug" + "value" : "Копировать" } } } }, - "Debug" : { - "comment" : "Tab label for the Debug tab in the watch app.", + "copy_label" : { + "extractionState" : "manual", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Debug" + "value" : "Item-Bezeichnung kopieren" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Debug" + "value" : "Copy item label" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Debug" + "value" : "Copiar etiqueta del ítem" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Debug" + "value" : "Copy item label" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Debug" + "value" : "Copier le label de l'item" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Debug" + "value" : "Copia etichetta Item" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Debug" + "value" : "Kopier Item-etikett" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Debug" + "value" : "Item label kopiëren" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Debug" + "value" : "Copy item label" } } } }, - "Decrease %@" : { - "comment" : "Accessibility labels and hints for the increase and decrease buttons.", + "Crash Reporting" : { + "comment" : "A toggle that allows the user to enable or disable crash reporting.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "%@ verringern" + "value" : "Absturzberichterstattung" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Decrease %@" + "value" : "Crash Reporting" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Disminuir %@" + "value" : "Informe de fallos" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Pienennä %@" + "value" : "Kaatumisraportointi" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Diminuer %@" + "value" : "Rapport de plantage" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Diminuisci %@" + "value" : "Segnalazione arresti anomali" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Reduser %@" + "value" : "Krasjirapportering" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "%@ verlagen" + "value" : "Crashrapportage" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Уменьшить %@" + "value" : "Отчёты о сбоях" } } } }, - "Default Path" : { - "comment" : "A label displayed above the text field for the default path of the main UI.", + "crash_detected" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Standardpfad" + "value" : "Absturz festgestellt" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Default Path" + "value" : "Crash detected" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Ruta predeterminada" + "value" : "Error detectado" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Oletuspolku" + "value" : "Crash detected" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Chemin par défaut" + "value" : "Crash détecté" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Percorso predefinito" + "value" : "Crash rilevato" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Standard sti" + "value" : "Krasj oppdaget" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Standaardpad" + "value" : "Crash gedetecteerd" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Путь по умолчанию" + "value" : "Crash detected" } } } }, - "Delete" : { - "comment" : "The text on the \"Delete\" button in the alert that appears when you long-press a home in the list.", + "crash_reporting" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Löschen" + "value" : "Absturzberichterstattung" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Delete" + "value" : "Crash Reporting" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Eliminar" + "value" : "Informe de errores" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Poista" + "value" : "Crash Reporting" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Supprimer" + "value" : "Rapports d'erreurs" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Elimina" + "value" : "Segnalazioni di crash" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Slett" + "value" : "Krasjrapportering" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Verwijder" + "value" : "Foutenrapportering" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Удалить" + "value" : "Crash Reporting" } } } }, - "Delete home '%@'?" : { - "comment" : "An alert that appears when the user swipes to delete a home. The argument is the name of the home that is about to be deleted.", + "crash_reporting_info" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Zuhause „%@“ löschen?" + "value" : "Durch die Aktivierung der Absturzberichterstattung stimmst du zu, dass Informationen zu deinem Gerät und der Nutzung der App erfasst und mit Crashlytics (einem Unternehmen von Google) geteilt werden. Weitere Informationen findest du in unserer Datenschutzerklärung." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Delete home '%@'?" + "value" : "By activating crash reporting you agree that device and usage information will be collected and shared with Crashlytics (a Google company). For further information view our privacy policy." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "¿Eliminar hogar ‘%@’?" + "value" : "Al activar los informes de fallos, usted acepta que la información de uso y del dispositivo se recopilará y compartirá con Crashlytics (una empresa de Google). Para obtener más información, consulte nuestra política de privacidad." } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Poistetaanko koti ‘%@’?" + "value" : "By activating crash reporting you agree that device and usage information will be collected and shared with Crashlytics (a Google company). For further information view our privacy policy." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Supprimer la maison '%@' ?" + "value" : "En activant le rapport de d’erreurs, tu acceptes que les informations relatives à l’appareil et à son utilisation soient collectées et partagées avec Crashlytics (une entreprise Google). Pour plus d’informations, consulte notre politique de confidentialité." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Eliminare la casa '%@'?" + "value" : "Attivando la segnalazione di crash si accetta che le informazioni sul dispositivo e sull'utilizzo saranno raccolte e condivise con Crashlytics (un'azienda di Google). Per ulteriori informazioni consultare la nostra informativa sulla privacy." } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Slette hjem ‘%@’?" + "value" : "Ved å aktivere krasjrapportering samtykker du i at enhet og bruksinformasjon samles inn og deles med Crashlytics (et Google-firma). For mer informasjon se våre retningslinjer for personvern." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Huis '%@' verwijderen?" + "value" : "Door het activeren van crash-rapportage gaat u akkoord dat apparaat- en gebruiksinformatie zal worden verzameld en gedeeld met Crashlytics (een Google-bedrijf). Voor meer informatie bekijk ons privacybeleid." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Удалить дом «%@»?" + "value" : "By activating crash reporting you agree that device and usage information will be collected and shared with Crashlytics (a Google company). For further information view our privacy policy." } } } }, - "Demo Mode" : { - "comment" : "A toggle that enables or disables demo mode.", + "Create" : { + "comment" : "The text on a button that creates a new home.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Demomodus" + "value" : "Erstellen" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Demo Mode" + "value" : "Create" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Modo demo" + "value" : "Crear" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Esittelytila" + "value" : "Luo" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Mode démo" + "value" : "Créer" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Modalità demo" + "value" : "Crea" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Demomodus" + "value" : "Opprett" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Demomodus" + "value" : "Maak aan" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Демо-режим" + "value" : "Создать" } } } }, - "demo_mode" : { - "extractionState" : "manual", + "Date and Time" : { + "comment" : "Parameter title for date and time in the Set DateTime Control Value app intent.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Demomodus" + "value" : "Datum und Uhrzeit" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Demo mode" + "value" : "Date and Time" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Modo de demostración" + "value" : "Fecha y hora" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Demo mode" + "value" : "Päivämäärä ja aika" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Mode démo" + "value" : "Date et heure" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Modalità demo" + "value" : "Data e ora" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Demomodus" + "value" : "Dato og tid" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Demomodus" + "value" : "Datum en tijd" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Demo mode" + "value" : "Дата и время" } } } }, - "Deny" : { - "comment" : "A button label that denies access to a website.", + "DateTime Item" : { + "comment" : "Display name for a DateTimeItemEntity.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Ablehnen" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Deny" + "value" : "DateTime-Item" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Denegar" + "value" : "Elemento DateTime" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Hylkää" + "value" : "DateTime-elementti" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Refuser" + "value" : "Élément DateHeure" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Non consentire" + "value" : "Elemento DateTime" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Avslå" + "value" : "DateTime-element" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Weiger" + "value" : "DateTime-item" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Отклонить" + "value" : "Элемент DateTime" } } } }, - "Dimmer or Roller Item" : { - "comment" : "Display name for a dimmer or roller item type.", - "isCommentAutoGenerated" : true, + "Daylight" : { + "comment" : "A descriptive label for a color temperature range.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Dimmer- oder Rolladen-Item" + "value" : "Tageslicht" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Dimmer or Roller Item" + "value" : "Daylight" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Elemento regulador o persiana" + "value" : "Luz diurna" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Himmennin- tai rullakaihdinelementti" + "value" : "Päivänvalo" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Élément gradateur/volet" + "value" : "Lumière du jour" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Elemento dimmer/tapparella" + "value" : "Luce del giorno" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Dimmer- eller rullelukkelement" + "value" : "Dagslys" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Dimmer/Roller-item" + "value" : "Daglicht" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Элемент диммера или ролеты" + "value" : "Дневной свет" } } } }, - "Disable" : { - "comment" : "Displayed when the user disables a setting.", - "isCommentAutoGenerated" : true - }, - "Disable Idle Timeout" : { - "comment" : "A toggle that allows the user to disable the idle timeout feature.", + "debug" : { + "comment" : "Section header text in the Debug Settings view.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Ruhezustand-Timeout deaktivieren" + "value" : "Debug" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Disable Idle Timeout" + "value" : "Debug" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Desactivar tiempo de espera en reposo" + "value" : "Debug" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Poista käytöstä lepoajan aikakatkaisu" + "value" : "Debug" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Désactiver le délai d'inactivité" + "value" : "Debug" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Disabilita timeout inattività" + "value" : "Debug" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Deaktiver tidsavbrudd ved inaktivitet" + "value" : "Debug" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Time-out bij inactiviteit uitschakelen" + "value" : "Debug" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Отключить тайм-аут простоя" + "value" : "Debug" } } } }, - "disable_idle_timeout" : { - "extractionState" : "manual", + "Debug" : { + "comment" : "Tab label for the Debug tab in the watch app.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Bildschirm-Timeout deaktivieren" + "value" : "Debug" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Disable Idle Timeout" + "value" : "Debug" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Desactivar tiempo de espera de inactividad" + "value" : "Debug" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Disable Idle Timeout" + "value" : "Debug" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Désactiver le délai d'inactivité" + "value" : "Debug" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Disabilita Timeout Inattività" + "value" : "Debug" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Deaktiver Tidsavbrudd for Inaktivitet" + "value" : "Debug" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Schermtimeout uitschakelen" + "value" : "Debug" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Disable Idle Timeout" + "value" : "Debug" } } } }, - "discovered_servers" : { + "Decrease %@" : { + "comment" : "Accessibility labels and hints for the increase and decrease buttons.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Gefundene Server" + "value" : "%@ verringern" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Discovered Servers" + "value" : "Decrease %@" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Discovered Servers" + "value" : "Disminuir %@" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Discovered Servers" + "value" : "Pienennä %@" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Discovered Servers" + "value" : "Diminuer %@" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Discovered Servers" + "value" : "Diminuisci %@" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Discovered Servers" + "value" : "Reduser %@" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Discovered Servers" + "value" : "%@ verlagen" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Discovered Servers" + "value" : "Уменьшить %@" } } } }, - "discovering_oh" : { - "extractionState" : "manual", + "Default Path" : { + "comment" : "A label displayed above the text field for the default path of the main UI.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "OpenHAB entdecken" + "value" : "Standardpfad" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Discovering openHAB" + "value" : "Default Path" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Descubriendo openHAB" + "value" : "Ruta predeterminada" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Discovering openHAB" + "value" : "Oletuspolku" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Découvrir openHAB" + "value" : "Chemin par défaut" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Cercando openHAB" + "value" : "Percorso predefinito" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Oppdager openHAB" + "value" : "Standard sti" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Bezig met zoeken naar openHAB" + "value" : "Standaardpad" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Discovering openHAB" + "value" : "Путь по умолчанию" } } } }, - "discovering_servers" : { + "Delete" : { + "comment" : "The text on the \"Delete\" button in the alert that appears when you long-press a home in the list.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB Server werden entdeckt …" + "value" : "Löschen" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Discovering openHAB servers…" + "value" : "Delete" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Descubriendo servidores openHAB…" + "value" : "Eliminar" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Etsitään openHAB-palvelimia…" + "value" : "Poista" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Recherche des serveurs openHAB…" + "value" : "Supprimer" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Ricerca server openHAB in corso…" + "value" : "Elimina" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Finner openHAB-servere…" + "value" : "Slett" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB-servers worden ontdekt…" + "value" : "Verwijder" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Обнаружение серверов openHAB…" + "value" : "Удалить" } } } }, - "dismiss" : { + "Delete home '%@'?" : { + "comment" : "An alert that appears when the user swipes to delete a home. The argument is the name of the home that is about to be deleted.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Verwerfen" + "value" : "Zuhause „%@“ löschen?" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Dismiss" + "value" : "Delete home '%@'?" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Descartar" + "value" : "¿Eliminar hogar ‘%@’?" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Dismiss" + "value" : "Poistetaanko koti ‘%@’?" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Ignorer" + "value" : "Supprimer la maison '%@' ?" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Ignora" + "value" : "Eliminare la casa '%@'?" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Avvis" + "value" : "Slette hjem ‘%@’?" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Afwijzen" + "value" : "Huis '%@' verwijderen?" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Dismiss" + "value" : "Удалить дом «%@»?" } } } }, - "Done" : { - "comment" : "A button that dismisses a sheet.", - "isCommentAutoGenerated" : true, + "Demo Mode" : { + "comment" : "A toggle that enables or disables demo mode.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Fertig" + "value" : "Demomodus" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Demo Mode" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Hecho" + "value" : "Modo demo" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Valmis" + "value" : "Esittelytila" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Terminé" + "value" : "Mode démo" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Fine" + "value" : "Modalità demo" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Ferdig" + "value" : "Demomodus" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Gereed" + "value" : "Demomodus" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Готово" + "value" : "Демо-режим" } } } }, - "Drag to change hue and saturation" : { - "comment" : "A hint that describes how to interact with a color wheel.", - "isCommentAutoGenerated" : true, + "demo_mode" : { + "extractionState" : "manual", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Ziehen, um Farbton und Sättigung zu ändern" + "value" : "Demomodus" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Demo mode" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Arrastrar para cambiar el tono y la saturación" + "value" : "Modo de demostración" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Vedä muuttaaksesi värisävyä ja kylläisyyttä" + "value" : "Demo mode" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Faire glisser pour modifier la teinte et la saturation" + "value" : "Mode démo" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Trascina per modificare tonalità e saturazione" + "value" : "Modalità demo" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Dra for å endre fargetone og metning" + "value" : "Demomodus" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Sleep om tint en verzadiging te wijzigen" + "value" : "Demomodus" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Перетащите для изменения оттенка и насыщенности" + "value" : "Demo mode" } } } }, - "empty" : { - "comment" : "local or remote URL: empty", + "Deny" : { + "comment" : "A button label that denies access to a website.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "leer" + "value" : "Ablehnen" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "empty" + "value" : "Deny" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "vacío" + "value" : "Denegar" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "tyhjä" + "value" : "Hylkää" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "vide" + "value" : "Refuser" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "vuoto" + "value" : "Non consentire" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "tom" + "value" : "Avslå" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "leeg" + "value" : "Weiger" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "пусто" + "value" : "Отклонить" } } } }, - "empty_sitemap" : { - "extractionState" : "manual", + "Dim Level: %@" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB lieferte eine leere Sitemap-Liste" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "openHAB returned empty sitemap list" + "value" : "Dimmstufe: %@" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "lista de mapas web de openHAB vacía" + "value" : "Nivel de atenuación: %@" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB returned empty sitemap list" + "value" : "Himmennystaso: %@" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB a renvoyé une liste vide de sitemaps" + "value" : "Niveau d'atténuation : %@" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB ha ritornato una lista vuota di sitemap" + "value" : "Livello dimmer: %@" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB returnerte en tom Sitemap-liste" + "value" : "Dimmenivå: %@" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB heeft een lege Sitemap geladen" + "value" : "Dimniveau: %@" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB returned empty sitemap list" + "value" : "Уровень диммера: %@" } } } }, - "Enable" : { - "comment" : "Display name for the \"Enable\" action.", - "isCommentAutoGenerated" : true - }, - "Enable Dimming" : { - "comment" : "A toggle that enables or disables automatic brightness dimming.", + "Dimmer or Roller Item" : { + "comment" : "Display name for a dimmer or roller item type.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Dimmen aktivieren" + "value" : "Dimmer- oder Rolladen-Item" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Enable Dimming" + "value" : "Dimmer or Roller Item" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Activar atenuación" + "value" : "Elemento regulador o persiana" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Ota himmennys käyttöön" + "value" : "Himmennin- tai rullakaihdinelementti" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Activer la gradation" + "value" : "Élément gradateur/volet" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Abilita dimmeraggio" + "value" : "Elemento dimmer/tapparella" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Aktiver dimming" + "value" : "Dimmer- eller rullelukkelement" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Dimmen inschakelen" + "value" : "Dimmer/Roller-item" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Включить затемнение" + "value" : "Элемент диммера или ролеты" } } } }, - "Enable Screen Saver" : { - "comment" : "A toggle switch that enables or disables the screen saver feature.", + "Disable" : { + "comment" : "Displayed when the user disables a setting.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Bildschirmschoner aktivieren" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Enable Screen Saver" + "value" : "Deaktivieren" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Activar salvapantallas" + "value" : "Deshabilitar" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Ota näytönsäästäjä käyttöön" + "value" : "Poista käytöstä" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Activer l'économiseur d'écran" + "value" : "Désactiver" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Abilita salvaschermo" + "value" : "Disabilita" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Aktiver skjermsparer" + "value" : "Deaktiver" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Schermbeveiliging inschakelen" + "value" : "Uitschakelen" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Включить заставку экрана" + "value" : "Отключить" } } } }, - "Enter a name for the new home" : { - "comment" : "A prompt that appears when creating a new home, asking for a name.", + "Disable Idle Timeout" : { + "comment" : "A toggle that allows the user to disable the idle timeout feature.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Name für das neue Zuhause eingeben" + "value" : "Ruhezustand-Timeout deaktivieren" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Enter a name for the new home" + "value" : "Disable Idle Timeout" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Introducir un nombre para el nuevo hogar" + "value" : "Desactivar tiempo de espera en reposo" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Syötä nimi uudelle kodille" + "value" : "Poista käytöstä lepoajan aikakatkaisu" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Saisissez un nom pour la nouvelle maison" + "value" : "Désactiver le délai d'inactivité" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Inserisci un nome per la nuova casa" + "value" : "Disabilita timeout inattività" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Angi et navn for det nye hjemmet" + "value" : "Deaktiver tidsavbrudd ved inaktivitet" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Voer een naam in voor het nieuwe huis" + "value" : "Time-out bij inactiviteit uitschakelen" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Введите имя для нового дома" + "value" : "Отключить тайм-аут простоя" } } } }, - "Enter a new name for the home '%@'" : { - "comment" : "Alerts for renaming or deleting a home.", + "disable_idle_timeout" : { + "extractionState" : "manual", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Neuer Name für das Zuhause „%@“ eingeben" + "value" : "Bildschirm-Timeout deaktivieren" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Enter a new name for the home '%@'" + "value" : "Disable Idle Timeout" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Introducir un nuevo nombre para el hogar ‘%@’" + "value" : "Desactivar tiempo de espera de inactividad" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Syötä uusi nimi kodille ‘%@’" + "value" : "Disable Idle Timeout" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Saisissez un nouveau nom pour la maison '%@'" + "value" : "Désactiver le délai d'inactivité" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Inserisci un nuovo nome per la casa '%@'" + "value" : "Disabilita Timeout Inattività" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Angi et nytt navn for hjemmet ‘%@’" + "value" : "Deaktiver Tidsavbrudd for Inaktivitet" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Voer een nieuwe naam in voor het huis '%@'" + "value" : "Schermtimeout uitschakelen" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Введите новое имя для дома «%@»" + "value" : "Disable Idle Timeout" } } } }, - "Enter new value" : { - "comment" : "The title of an alert that appears when the user taps the \"Add Time\" button in the example.", + "discovered_servers" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Neuen Wert eingeben" + "value" : "Gefundene Server" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Enter new value" + "value" : "Discovered Servers" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Introducir nuevo valor" + "value" : "Discovered Servers" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Syötä uusi arvo" + "value" : "Discovered Servers" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Saisir une nouvelle valeur" + "value" : "Discovered Servers" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Inserisci nuovo valore" + "value" : "Discovered Servers" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Angi ny verdi" + "value" : "Discovered Servers" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Nieuwe waarde invoeren" + "value" : "Discovered Servers" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Введите новое значение" + "value" : "Discovered Servers" } } } }, - "Enter password for server" : { - "comment" : "A prompt instructing the user to enter a password for the server. Try to keep shortish.", + "discovering_oh" : { + "extractionState" : "manual", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Server-Passwort eingeben" + "value" : "OpenHAB entdecken" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Enter password for server" + "value" : "Discovering openHAB" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Introduce la contraseña del servidor" + "value" : "Descubriendo openHAB" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Anna palvelimen salasana" + "value" : "Discovering openHAB" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Entrez le mot de passe du serveur" + "value" : "Découvrir openHAB" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Inserisci la password del server" + "value" : "Cercando openHAB" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Skriv inn passord for server" + "value" : "Oppdager openHAB" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Voer wachtwoord voor server in" + "value" : "Bezig met zoeken naar openHAB" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Введите пароль для сервера" + "value" : "Discovering openHAB" } } } }, - "Enter text" : { - "comment" : "A placeholder text for a text input field.", + "discovering_servers" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Text eingeben" + "value" : "openHAB Server werden entdeckt …" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Enter text" + "value" : "Discovering openHAB servers…" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Introducir texto" + "value" : "Descubriendo servidores openHAB…" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Syötä teksti" + "value" : "Etsitään openHAB-palvelimia…" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Saisir du texte" + "value" : "Recherche des serveurs openHAB…" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Inserisci testo" + "value" : "Ricerca server openHAB in corso…" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Angi tekst" + "value" : "Finner openHAB-servere…" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Tekst invoeren" + "value" : "openHAB-servers worden ontdekt…" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Введите текст" + "value" : "Обнаружение серверов openHAB…" } } } }, - "Enter URL of remote server" : { - "comment" : "A message displayed when the URL field in the connection settings is empty.", + "dismiss" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "URL des entfernten Servers eingeben" + "value" : "Verwerfen" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Enter URL of remote server" + "value" : "Dismiss" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Introducir URL del servidor remoto" + "value" : "Descartar" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Syötä etäpalvelimen URL" + "value" : "Dismiss" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Saisir l'URL du serveur distant" + "value" : "Ignorer" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Inserisci URL del server remoto" + "value" : "Ignora" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Angi URL til fjernserver" + "value" : "Avvis" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "URL van externe server invoeren" + "value" : "Afwijzen" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Введите URL удалённого сервера" + "value" : "Dismiss" } } } }, - "Enter username for server, if required" : { - "comment" : "A message displayed below the username field, instructing the user to enter a username if one is required by the server. Try to keep shortish.", + "Display an openHAB sensor value" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Server-Benutzername eingeben, falls erforderlich" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Enter username for server, if required" + "value" : "Einen openHAB-Sensorwert anzeigen" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Introduce el nombre de usuario del servidor, si es necesario" + "value" : "Mostrar un valor de sensor de openHAB" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Anna palvelimen käyttäjänimi tarvittaessa" + "value" : "Näytä openHAB-anturiarvo" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Entrez le nom d'utilisateur du serveur, si requis" + "value" : "Afficher une valeur de capteur openHAB" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Inserisci il nome utente del server, se richiesto" + "value" : "Visualizza un valore sensore openHAB" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Skriv inn brukernavn for server, om nødvendig" + "value" : "Vis en openHAB-sensorverdi" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Voer gebruikersnaam voor server in, indien vereist" + "value" : "Een openHAB-sensorwaarde weergeven" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Введите имя пользователя для сервера, если требуется" + "value" : "Показать значение датчика openHAB" } } } }, - "Error" : { - "comment" : "The title of an alert that appears when an error occurs.", + "Display two openHAB sensor values" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Fehler" + "value" : "Zwei openHAB-Sensorwerte anzeigen" } }, - "en" : { + "es" : { "stringUnit" : { "state" : "translated", - "value" : "Error" + "value" : "Mostrar dos valores de sensor de openHAB" } }, - "es" : { + "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Error" - } - }, - "fi" : { - "stringUnit" : { - "state" : "translated", - "value" : "Virhe" + "value" : "Näytä kaksi openHAB-anturiarvoa" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Erreur" + "value" : "Afficher deux valeurs de capteur openHAB" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Errore" + "value" : "Visualizza due valori sensore openHAB" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Feil" + "value" : "Vis to openHAB-sensorverdier" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Fout" + "value" : "Twee openHAB-sensorwaarden weergeven" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Ошибка" + "value" : "Показать два значения датчиков openHAB" } } } }, - "Fade Duration: %@ s" : { - "comment" : "A slider that lets the user adjust the duration of the fade-in animation when the screen saver is activated. The argument is the string “%.1f”.", + "Display up to four openHAB sensor values" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Fade-Dauer: %@ s" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Fade Duration: %@ s" + "value" : "Bis zu vier openHAB-Sensorwerte anzeigen" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Duración del fundido: %@ s" + "value" : "Mostrar hasta cuatro valores de sensor de openHAB" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Häivytyksen kesto: %@ s" + "value" : "Näytä enintään neljä openHAB-anturiarvoa" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Durée de fondu : %@ s" + "value" : "Afficher jusqu'à quatre valeurs de capteur openHAB" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Durata dissolvenza: %@ s" + "value" : "Visualizza fino a quattro valori sensore openHAB" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Falming varighet: %@ s" + "value" : "Vis opptil fire openHAB-sensorverdier" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Vervaagtijd: %@ s" + "value" : "Maximaal vier openHAB-sensorwaarden weergeven" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Длительность затухания: %@ с" + "value" : "Показать до четырёх значений датчиков openHAB" } } } }, - "Fast Forward" : { - "comment" : "Display name for the fast forward action in the PlayerAction enum.", + "Done" : { + "comment" : "A button that dismisses a sheet.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Schneller Vorlauf" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Fast Forward" + "value" : "Fertig" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Avance rápido" + "value" : "Hecho" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Pikakelaus eteenpäin" + "value" : "Valmis" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Avance rapide" + "value" : "Terminé" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Avanzamento rapido" + "value" : "Fine" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Spol frem" + "value" : "Ferdig" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Vooruitspoelen" + "value" : "Gereed" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Перемотка вперёд" + "value" : "Готово" } } } }, - "FF" : { - "comment" : "Synonyms for the \"Fast Forward\" player action.", - "isCommentAutoGenerated" : true - }, - "Flip" : { - "comment" : "Display name for the switch action \"Toggle\".", - "isCommentAutoGenerated" : true - }, - "Font" : { - "comment" : "A label for the font picker in the screen saver settings view.", + "Drag to change hue and saturation" : { + "comment" : "A hint that describes how to interact with a color wheel.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Schrift" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Font" + "value" : "Ziehen, um Farbton und Sättigung zu ändern" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Tipo de letra" + "value" : "Arrastrar para cambiar el tono y la saturación" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Fontti" + "value" : "Vedä muuttaaksesi värisävyä ja kylläisyyttä" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Police" + "value" : "Faire glisser pour modifier la teinte et la saturation" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Font" + "value" : "Trascina per modificare tonalità e saturazione" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Skrift" + "value" : "Dra for å endre fargetone og metning" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Lettertype" + "value" : "Sleep om tint en verzadiging te wijzigen" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Шрифт" + "value" : "Перетащите для изменения оттенка и насыщенности" } } } }, - "Font Size" : { - "comment" : "A section header that indicates the font size settings.", + "empty" : { + "comment" : "local or remote URL: empty", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Schriftgröße" + "value" : "leer" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Font Size" + "value" : "empty" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Tamaño de letra" + "value" : "vacío" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Fonttikoko" + "value" : "tyhjä" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Taille de la police" + "value" : "vide" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Dimensioni font" + "value" : "vuoto" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Skriftstørrelse" + "value" : "tom" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Lettergrootte" + "value" : "leeg" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Размер шрифта" + "value" : "пусто" } } } }, - "Foo" : { - "comment" : "A label for a text field where the user can input \"Foo\".", + "empty_sitemap" : { + "extractionState" : "manual", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Foo" + "value" : "openHAB lieferte eine leere Sitemap-Liste" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Foo" + "value" : "openHAB returned empty sitemap list" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Foo" + "value" : "lista de mapas web de openHAB vacía" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Foo" + "value" : "openHAB returned empty sitemap list" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Foo" + "value" : "openHAB a renvoyé une liste vide de sitemaps" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Foo" + "value" : "openHAB ha ritornato una lista vuota di sitemap" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Foo" + "value" : "openHAB returnerte en tom Sitemap-liste" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Foo" + "value" : "openHAB heeft een lege Sitemap geladen" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Foo" + "value" : "openHAB returned empty sitemap list" } } } }, - "For Shortcuts to work across multiple devices, each home must have the same name on every device." : { - "comment" : "Instruction shown in Manage Homes explaining that home names must match across devices for Shortcuts to work.", + "Enable" : { + "comment" : "Display name for the \"Enable\" action.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Damit Kurzbefehle geräteübergreifend funktionieren, muss jedes Zuhause auf jedem Gerät denselben Namen haben." - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "For Shortcuts to work across multiple devices, each home must have the same name on every device." + "value" : "Aktivieren" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Para que los atajos funcionen en varios dispositivos, cada hogar debe tener el mismo nombre en cada dispositivo." + "value" : "Habilitar" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Jotta pikakuvakkeet toimisivat useilla laitteilla, jokaisella kodilla täytyy olla sama nimi joka laitteella." + "value" : "Ota käyttöön" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Pour que les raccourcis fonctionnent sur plusieurs appareils, chaque maison doit avoir le même nom sur chaque appareil." + "value" : "Activer" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Per far funzionare i comandi rapidi su più dispositivi, ogni casa deve avere lo stesso nome su ogni dispositivo." + "value" : "Abilita" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "For at snarveier skal fungere på tvers av enheter, må hvert hjem ha samme navn på hver enhet." + "value" : "Aktiver" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Om snelkoppelingen op meerdere apparaten te laten werken, moet elk thuis dezelfde naam hebben op elk apparaat." + "value" : "Inschakelen" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Чтобы быстрые команды работали на нескольких устройствах, у каждого дома должно быть одинаковое название на каждом устройстве." + "value" : "Включить" } } } }, - "Get ${itemEntity} State" : { + "Enable Dimming" : { + "comment" : "A toggle that enables or disables automatic brightness dimming.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "${itemEntity}-Status erhalten" + "value" : "Dimmen aktivieren" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enable Dimming" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Obtener estado de ${itemEntity}" + "value" : "Activar atenuación" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Hae ${itemEntity}-tila" + "value" : "Ota himmennys käyttöön" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Obtenir l'état de ${itemEntity}" + "value" : "Activer la gradation" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Ottieni lo stato di ${itemEntity}" + "value" : "Abilita dimmeraggio" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Hent status for ${itemEntity}" + "value" : "Aktiver dimming" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Status van ${itemEntity} ophalen" + "value" : "Dimmen inschakelen" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Получить состояние ${itemEntity}" + "value" : "Включить затемнение" } } } }, - "Get Item State" : { - "comment" : "Title of the Get Item State app intent.", + "Enable Screen Saver" : { + "comment" : "A toggle switch that enables or disables the screen saver feature.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Status eines Items erhalten" + "value" : "Bildschirmschoner aktivieren" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Get Item State" + "value" : "Enable Screen Saver" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Obtener estado del ítem" + "value" : "Activar salvapantallas" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Hae elementin tila" + "value" : "Ota näytönsäästäjä käyttöön" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Récupérer l'état de l'item" + "value" : "Activer l'économiseur d'écran" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Leggi lo stato dell'Item" + "value" : "Abilita salvaschermo" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Få Item-tilstand" + "value" : "Aktiver skjermsparer" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Haal item state op" + "value" : "Schermbeveiliging inschakelen" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Получить состояние элемента" + "value" : "Включить заставку экрана" } } } }, - "Go backward" : {}, - "habpanel" : { - "extractionState" : "manual", + "Enter a name for the new home" : { + "comment" : "A prompt that appears when creating a new home, asking for a name.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "HABPanel" + "value" : "Name für das neue Zuhause eingeben" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "HABPanel" + "value" : "Enter a name for the new home" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "HABPanel" + "value" : "Introducir un nombre para el nuevo hogar" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "HABPanel" + "value" : "Syötä nimi uudelle kodille" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "HABPanel" + "value" : "Saisissez un nom pour la nouvelle maison" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "HABPanel" + "value" : "Inserisci un nome per la nuova casa" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "HABPanel" + "value" : "Angi et navn for det nye hjemmet" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "HABPanel" + "value" : "Voer een naam in voor het nieuwe huis" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "HABPanel" + "value" : "Введите имя для нового дома" } } } }, - "Hide Status Bar" : { - "comment" : "A toggle that allows the user to hide the status bar in the app.", + "Enter a new name for the home '%@'" : { + "comment" : "Alerts for renaming or deleting a home.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Statusleiste ausblenden" + "value" : "Neuer Name für das Zuhause „%@“ eingeben" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Hide Status Bar" + "value" : "Enter a new name for the home '%@'" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Ocultar barra de estado" + "value" : "Introducir un nuevo nombre para el hogar ‘%@’" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Piilota tilapalkki" + "value" : "Syötä uusi nimi kodille ‘%@’" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Masquer la barre d'état" + "value" : "Saisissez un nouveau nom pour la maison '%@'" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Nascondi barra di stato" + "value" : "Inserisci un nuovo nome per la casa '%@'" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Skjul statuslinje" + "value" : "Angi et nytt navn for hjemmet ‘%@’" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Statusbalk verbergen" + "value" : "Voer een nieuwe naam in voor het huis '%@'" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Скрыть строку состояния" + "value" : "Введите новое имя для дома «%@»" } } } }, - "Hold" : {}, - "Home" : { - "comment" : "The name of the \"Home\" menu item.", + "Enter new value" : { + "comment" : "The title of an alert that appears when the user taps the \"Add Time\" button in the example.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Zuhause" + "value" : "Neuen Wert eingeben" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Home" + "value" : "Enter new value" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Casa" + "value" : "Introducir nuevo valor" } }, "fi" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Syötä uusi arvo" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Domicile" + "value" : "Saisir une nouvelle valeur" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Casa" + "value" : "Inserisci nuovo valore" } }, "nb" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Angi ny verdi" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Thuis" + "value" : "Nieuwe waarde invoeren" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Дом" + "value" : "Введите новое значение" } } } }, - "Icon Type" : { - "comment" : "A label describing the option to change the icon type.", + "Enter password for server" : { + "comment" : "A prompt instructing the user to enter a password for the server. Try to keep shortish.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Icon-Typ" + "value" : "Server-Passwort eingeben" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Icon Type" + "value" : "Enter password for server" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Tipo de icono" + "value" : "Introduce la contraseña del servidor" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Kuvaketyyppi" + "value" : "Anna palvelimen salasana" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Type d'icône" + "value" : "Entrez le mot de passe du serveur" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Tipo icona" + "value" : "Inserisci la password del server" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Ikonstil" + "value" : "Skriv inn passord for server" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Pictogramtype" + "value" : "Voer wachtwoord voor server in" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Тип значка" + "value" : "Введите пароль для сервера" } } } }, - "icon_type" : { - "extractionState" : "manual", + "Enter text" : { + "comment" : "A placeholder text for a text input field.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Icon-Typ" + "value" : "Text eingeben" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Icon Type" + "value" : "Enter text" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Tipo de icono" + "value" : "Introducir texto" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Icon Type" + "value" : "Syötä teksti" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Type d'icône" + "value" : "Saisir du texte" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Tipo di icona" + "value" : "Inserisci testo" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Ikontype" + "value" : "Angi tekst" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Icoontype" + "value" : "Tekst invoeren" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Icon Type" + "value" : "Введите текст" } } } }, - "Idle Interval: %lld s" : { - "comment" : "A stepper that allows the user to set the number of seconds after which the screen saver will activate if no movement is detected. The value shown is the current idle interval in seconds.", + "Enter URL of remote server" : { + "comment" : "A message displayed when the URL field in the connection settings is empty.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Ruhezustand-Intervall: %lld s" + "value" : "URL des entfernten Servers eingeben" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Idle Interval: %lld s" + "value" : "Enter URL of remote server" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Intervalo de reposo: %lld s" + "value" : "Introducir URL del servidor remoto" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Lepoväli: %lld s" + "value" : "Syötä etäpalvelimen URL" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Intervalle d'inactivité : %lld s" + "value" : "Saisir l'URL du serveur distant" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Intervallo inattività: %lld s" + "value" : "Inserisci URL del server remoto" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Inaktivitetsintervall: %lld s" + "value" : "Angi URL til fjernserver" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Inactiviteitsinterval: %lld s" + "value" : "URL van externe server invoeren" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Интервал простоя: %lld с" + "value" : "Введите URL удалённого сервера" } } } }, - "Ignore SSL certificates" : { - "comment" : "A toggle that lets the user ignore SSL certificate warnings.", + "Enter username for server, if required" : { + "comment" : "A message displayed below the username field, instructing the user to enter a username if one is required by the server. Try to keep shortish.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "SSL-Zertifikate ignorieren" + "value" : "Server-Benutzername eingeben, falls erforderlich" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Ignore SSL certificates" + "value" : "Enter username for server, if required" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Ignorar certificados SSL" + "value" : "Introduce el nombre de usuario del servidor, si es necesario" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Ohita SSL-varmenteet" + "value" : "Anna palvelimen käyttäjänimi tarvittaessa" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Ignorer les certificats SSL" + "value" : "Entrez le nom d'utilisateur du serveur, si requis" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Ignora certificati SSL" + "value" : "Inserisci il nome utente del server, se richiesto" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Ignorer SSL-sertifikater" + "value" : "Skriv inn brukernavn for server, om nødvendig" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "SSL-certificaten negeren" + "value" : "Voer gebruikersnaam voor server in, indien vereist" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Игнорировать SSL-сертификаты" + "value" : "Введите имя пользователя для сервера, если требуется" } } } }, - "ignore_ssl_certificates" : { - "extractionState" : "manual", + "Error" : { + "comment" : "The title of an alert that appears when an error occurs.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "SSL-Zertifikate ignorieren" + "value" : "Fehler" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Ignore SSL Certificates" + "value" : "Error" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Ignorar certificados SSL" + "value" : "Error" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Ignore SSL Certificates" + "value" : "Virhe" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Ignorer les certificats SSL" + "value" : "Erreur" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Ignora certificati SSL" + "value" : "Errore" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Ignorer SSL-sertifikater" + "value" : "Feil" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Negeer SSL-certificaten" + "value" : "Fout" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Игнорировать SSL-сертификаты" + "value" : "Ошибка" } } } }, - "Image Cache" : { - "comment" : "The title of an alert that shows the result of checking and clearing the image cache.", + "Fade Duration: %@ s" : { + "comment" : "A slider that lets the user adjust the duration of the fade-in animation when the screen saver is activated. The argument is the string “%.1f”.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Bilder-Cache" + "value" : "Fade-Dauer: %@ s" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Image Cache" + "value" : "Fade Duration: %@ s" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Caché de imágenes" + "value" : "Duración del fundido: %@ s" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Kuvavälimuisti" + "value" : "Häivytyksen kesto: %@ s" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Cache d'images" + "value" : "Durée de fondu : %@ s" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Cache immagini" + "value" : "Durata dissolvenza: %@ s" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Bildebuffer" + "value" : "Falming varighet: %@ s" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Afbeeldingscache" + "value" : "Vervaagtijd: %@ s" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Кэш изображений" + "value" : "Длительность затухания: %@ с" } } } }, - "Import" : { - "comment" : "\"Import\" button title.", + "Fast Forward" : { + "comment" : "Display name for the fast forward action in the PlayerAction enum.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Importieren" + "value" : "Schneller Vorlauf" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Import" + "value" : "Fast Forward" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Importar" + "value" : "Avance rápido" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Tuo" + "value" : "Pikakelaus eteenpäin" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Importer" + "value" : "Avance rapide" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Importa" + "value" : "Avanzamento rapido" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Importer" + "value" : "Spol frem" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Importeer" + "value" : "Vooruitspoelen" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Импорт" + "value" : "Перемотка вперёд" } } } }, - "Inactive" : { - "comment" : "Displayed when the contact is closed.", - "isCommentAutoGenerated" : true - }, - "Increase %@" : { - "comment" : "A button that increases the value of a setpoint. The argument is the name of the setpoint.", + "FF" : { + "comment" : "Synonyms for the \"Fast Forward\" player action.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "%@ erhöhen" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Increase %@" + "value" : "FF" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Aumentar %@" + "value" : "FF" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Suurenna %@" + "value" : "FF" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Augmenter %@" + "value" : "FF" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Aumenta %@" + "value" : "FF" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Øk %@" + "value" : "FF" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "%@ verhogen" + "value" : "FF" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Увеличить %@" + "value" : "FF" } } } }, - "info" : { - "extractionState" : "manual", + "Flip" : { + "comment" : "Display name for the switch action \"Toggle\".", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Info" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Info" + "value" : "Umschalten" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Información" + "value" : "Cambiar" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Info" + "value" : "Vaihda" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Infos" + "value" : "Basculer" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Informazioni" + "value" : "Inverti" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Informasjon" + "value" : "Bytt" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Info" + "value" : "Omzetten" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Info" + "value" : "Переключить" } } } }, - "Invalid value %lld for %@ (0-100)" : { - "comment" : "Error message when a dimmer or roller shutter value is out of range. First placeholder is the invalid value, second is the item name.", + "Font" : { + "comment" : "A label for the font picker in the screen saver settings view.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Ungültiger Wert %lld für %@ (0-100)" + "value" : "Schrift" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Invalid value %lld for %@ (0-100)" + "value" : "Font" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Valor no válido %lld para %@ (0-100)" + "value" : "Tipo de letra" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Virheellinen arvo %lld kohteelle %@ (0–100)" + "value" : "Fontti" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Valeur %lld invalide pour %@ (0-100)" + "value" : "Police" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Valore non valido %lld per %@ (0-100)" + "value" : "Font" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Ugyldig verdi %lld for %@ (0-100)" + "value" : "Skrift" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Ongeldige waarde %lld voor %@ (0-100)" + "value" : "Lettertype" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Недопустимое значение %lld для %@ (0–100)" + "value" : "Шрифт" } } } }, - "Invalid value: %@ for %@ must be HSB (0-360,0-100,0-100)" : { - "comment" : "Error message when a color value is not valid HSB. First placeholder is the invalid value, second is the item name.", + "Font Size" : { + "comment" : "A section header that indicates the font size settings.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Ungültiger Wert: %@ für %@ muss HSB sein (0-360,0-100,0-100)" + "value" : "Schriftgröße" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Invalid value: %@ for %@ must be HSB (0-360,0-100,0-100)" + "value" : "Font Size" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Valor no válido: %@ para %@ debe ser HSB (0-360,0-100,0-100)" + "value" : "Tamaño de letra" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Virheellinen arvo: %@ kohteelle %@ täytyy olla HSB (0–360, 0–100, 0–100)" + "value" : "Fonttikoko" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Valeur invalide : %@ pour %@ doit être HSB (0-360,0-100,0-100)" + "value" : "Taille de la police" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Valore non valido: %@ per %@ deve essere HSB (0-360,0-100,0-100)" + "value" : "Dimensioni font" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Ugyldig verdi: %@ for %@ må være HSB (0-360,0-100,0-100)" + "value" : "Skriftstørrelse" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Ongeldige waarde: %@ voor %@ moet HSB zijn (0-360,0-100,0-100)" + "value" : "Lettergrootte" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Недопустимое значение: %@ для %@ должно быть HSB (0–360, 0–100, 0–100)" + "value" : "Размер шрифта" } } } }, - "invalid_connection_configuration" : { - "extractionState" : "manual", + "Foo" : { + "comment" : "A label for a text field where the user can input \"Foo\".", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Ungültige Verbindungskonfiguration." + "value" : "Foo" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Invalid connection configuration." + "value" : "Foo" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Configuración de conexión no válida." + "value" : "Foo" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Virheellinen yhteysasetukset." + "value" : "Foo" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Configuration de connexion invalide." + "value" : "Foo" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Configurazione della connessione non valida." + "value" : "Foo" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Ugyldig tilkoblingskonfigurasjon." + "value" : "Foo" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Ongeldige verbindingsconfiguratie." + "value" : "Foo" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Недопустимая конфигурация подключения." + "value" : "Foo" } } } }, - "Item" : { - "comment" : "Parameter title for an openHAB item in app intents.", + "For Shortcuts to work across multiple devices, each home must have the same name on every device." : { + "comment" : "Instruction shown in Manage Homes explaining that home names must match across devices for Shortcuts to work.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Item" + "value" : "Damit Kurzbefehle geräteübergreifend funktionieren, muss jedes Zuhause auf jedem Gerät denselben Namen haben." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Item" + "value" : "For Shortcuts to work across multiple devices, each home must have the same name on every device." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Ítem" + "value" : "Para que los atajos funcionen en varios dispositivos, cada hogar debe tener el mismo nombre en cada dispositivo." } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Elementti" + "value" : "Jotta pikakuvakkeet toimisivat useilla laitteilla, jokaisella kodilla täytyy olla sama nimi joka laitteella." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Item" + "value" : "Pour que les raccourcis fonctionnent sur plusieurs appareils, chaque maison doit avoir le même nom sur chaque appareil." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Item" + "value" : "Per far funzionare i comandi rapidi su più dispositivi, ogni casa deve avere lo stesso nome su ogni dispositivo." } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Item" + "value" : "For at snarveier skal fungere på tvers av enheter, må hvert hjem ha samme navn på hver enhet." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Item" + "value" : "Om snelkoppelingen op meerdere apparaten te laten werken, moet elk thuis dezelfde naam hebben op elk apparaat." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Элемент" + "value" : "Чтобы быстрые команды работали на нескольких устройствах, у каждого дома должно быть одинаковое название на каждом устройстве." } } } }, - "Item '%@' is not in home '%@'" : { - "comment" : "Error message when the selected item does not belong to the selected home. First placeholder is the item name, second is the home name.", + "Get ${itemEntity} State" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Das Item „%@“ befindet sich nicht im Zuhause „%@“" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Item '%@' is not in home '%@'" + "value" : "${itemEntity}-Status erhalten" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "El elemento ‘%@’ no está en el hogar ‘%@’" + "value" : "Obtener estado de ${itemEntity}" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Elementti ‘%@’ ei ole kodissa ‘%@’" + "value" : "Hae ${itemEntity}-tila" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "L'élément '%@' n'est pas dans la maison '%@'" + "value" : "Obtenir l'état de ${itemEntity}" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "L'elemento '%@' non si trova nella casa '%@'" + "value" : "Ottieni lo stato di ${itemEntity}" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Element ‘%@’ er ikke i hjemmet ‘%@’" + "value" : "Hent status for ${itemEntity}" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Item '%@' staat niet in huis '%@'" + "value" : "Status van ${itemEntity} ophalen" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Элемент «%@» не входит в дом «%@»" + "value" : "Получить состояние ${itemEntity}" } } } }, - "Item '%@' not found" : { - "comment" : "Error message when an item is not found. The argument is the name of the item.", - "isCommentAutoGenerated" : true, + "Get Item State" : { + "comment" : "Title of the Get Item State app intent.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Element '%@' nicht gefunden" + "value" : "Status eines Items erhalten" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Get Item State" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Elemento ‘%@’ no encontrado" + "value" : "Obtener estado del ítem" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Elementtiä ‘%@’ ei löydy" + "value" : "Hae elementin tila" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Élément '%@' introuvable" + "value" : "Récupérer l'état de l'item" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Elemento '%@' non trovato" + "value" : "Leggi lo stato dell'Item" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Element ‘%@’ ikke funnet" + "value" : "Få Item-tilstand" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Item '%@' niet gevonden" + "value" : "Haal item state op" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Элемент «%@» не найден" + "value" : "Получить состояние элемента" } } } }, - "Items" : { - "comment" : "The title of the view that lists available items.", + "Go backward" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Items" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Items" + "value" : "Zurückgehen" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Ítems" + "value" : "Ir atrás" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Elementit" + "value" : "Mene taaksepäin" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Éléments" + "value" : "Aller en arrière" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Elementi" + "value" : "Vai indietro" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Elementer" + "value" : "Gå bakover" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Onderdelen" + "value" : "Ga terug" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Элементы" + "value" : "Назад" } } } }, - "Label" : { + "habpanel" : { "extractionState" : "manual", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Label" + "value" : "HABPanel" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Label" + "value" : "HABPanel" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Etiqueta" + "value" : "HABPanel" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Etiketti" + "value" : "HABPanel" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Étiquette" + "value" : "HABPanel" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Etichetta" + "value" : "HABPanel" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Etikett" + "value" : "HABPanel" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Label" + "value" : "HABPanel" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Метка" + "value" : "HABPanel" } } } }, - "Latitude" : { - "comment" : "Parameter title for latitude in the Set Location Control Value app intent.", + "Hide Status Bar" : { + "comment" : "A toggle that allows the user to hide the status bar in the app.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Breitengrad" + "value" : "Statusleiste ausblenden" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Latitude" + "value" : "Hide Status Bar" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Latitud" + "value" : "Ocultar barra de estado" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Leveysaste" + "value" : "Piilota tilapalkki" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Latitude" + "value" : "Masquer la barre d'état" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Latitudine" + "value" : "Nascondi barra di stato" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Breddegrad" + "value" : "Skjul statuslinje" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Breedtegraad" + "value" : "Statusbalk verbergen" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Широта" + "value" : "Скрыть строку состояния" } } } }, - "Latitude must be between -90 and 90" : { - "comment" : "Error message when a latitude value is out of the valid range.", + "Hold" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Breitengrad muss zwischen -90 and 90 sein" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Latitude must be between -90 and 90" + "value" : "Halten" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "La latitud debe estar entre -90 y 90" + "value" : "Pausar" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Leveysasteen on oltava välillä −90 ja 90" + "value" : "Pidä tauko" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "La latitude doit être comprise entre -90 et 90" + "value" : "Mettre en pause" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "La latitudine deve essere compresa tra -90 e 90" + "value" : "Pausa" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Breddegraden må være mellom -90 og 90" + "value" : "Hold" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Breedtegraad moet tussen -90 en 90 liggen" + "value" : "Pauzeren" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Широта должна быть от -90 до 90" + "value" : "Приостановить" } } } }, - "legal" : { - "extractionState" : "manual", + "Home" : { + "comment" : "The name of the \"Home\" menu item.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Rechtliches" + "value" : "Zuhause" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Legal" + "value" : "Home" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Legal" + "value" : "Casa" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Legal" + "value" : "Koti" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Légal" + "value" : "Domicile" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Legale" + "value" : "Casa" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Juridisk" + "value" : "Hjem" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Juridisch" + "value" : "Thuis" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Legal" + "value" : "Дом" } } } }, - "Legal" : { - "comment" : "A link to the app's legal information.", + "Icon Type" : { + "comment" : "A label describing the option to change the icon type.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Rechtliches" + "value" : "Icon-Typ" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Legal" + "value" : "Icon Type" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Aviso legal" + "value" : "Tipo de icono" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Oikeudelliset tiedot" + "value" : "Kuvaketyyppi" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Mentions légales" + "value" : "Type d'icône" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Note legali" + "value" : "Tipo icona" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Juridisk" + "value" : "Ikonstil" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Juridische informatie" + "value" : "Pictogramtype" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Юридическая информация" + "value" : "Тип значка" } } } }, - "Loading Items…" : { - "comment" : "A message displayed while waiting to load items.", + "icon_type" : { + "extractionState" : "manual", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Items werden geladen …" + "value" : "Icon-Typ" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Loading Items…" + "value" : "Icon Type" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Cargando elementos…" + "value" : "Tipo de icono" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Ladataan elementtejä…" + "value" : "Icon Type" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Chargement des éléments…" + "value" : "Type d'icône" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Caricamento elementi…" + "value" : "Tipo di icona" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Laster elementer…" + "value" : "Ikontype" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Items worden geladen…" + "value" : "Icoontype" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Загрузка элементов…" + "value" : "Icon Type" } } } }, - "Loading sitemap…" : { - "comment" : "A placeholder text indicating that the sitemap is being loaded.", + "Idle Interval: %lld s" : { + "comment" : "A stepper that allows the user to set the number of seconds after which the screen saver will activate if no movement is detected. The value shown is the current idle interval in seconds.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Sitemap wird geladen …" + "value" : "Ruhezustand-Intervall: %lld s" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Loading sitemap…" + "value" : "Idle Interval: %lld s" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Cargando mapa del sitio…" + "value" : "Intervalo de reposo: %lld s" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Ladataan sivukarttaa…" + "value" : "Lepoväli: %lld s" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Chargement du plan du site…" + "value" : "Intervalle d'inactivité : %lld s" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Caricamento sitemap…" + "value" : "Intervallo inattività: %lld s" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Laster nettstedskart…" + "value" : "Inaktivitetsintervall: %lld s" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Sitemap wordt geladen…" + "value" : "Inactiviteitsinterval: %lld s" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Загрузка карты сайта…" + "value" : "Интервал простоя: %lld с" } } } }, - "Loading…" : { - "extractionState" : "manual", + "Ignore SSL certificates" : { + "comment" : "A toggle that lets the user ignore SSL certificate warnings.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Wird geladen …" + "value" : "SSL-Zertifikate ignorieren" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Loading…" + "value" : "Ignore SSL certificates" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Cargando…" + "value" : "Ignorar certificados SSL" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Ladataan …" + "value" : "Ohita SSL-varmenteet" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Chargement…" + "value" : "Ignorer les certificats SSL" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Caricamento …" + "value" : "Ignora certificati SSL" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Laster …" + "value" : "Ignorer SSL-sertifikater" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Laden…" + "value" : "SSL-certificaten negeren" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Загрузка …" + "value" : "Игнорировать SSL-сертификаты" } } } }, - "Local Network access may be required." : { - "comment" : "A warning message that appears when the user's local network access is required to connect to a server.", - "isCommentAutoGenerated" : true, + "ignore_ssl_certificates" : { + "extractionState" : "manual", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Möglicherweise ist der Zugriff auf das lokale Netzwerk erforderlich." + "value" : "SSL-Zertifikate ignorieren" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ignore SSL Certificates" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Es posible que se requiera acceso a la red local." + "value" : "Ignorar certificados SSL" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Lähiverkon käyttöoikeus voi olla tarpeen." + "value" : "Ignore SSL Certificates" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "L'accès au réseau local peut être requis." + "value" : "Ignorer les certificats SSL" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Potrebbe essere necessario l'accesso alla rete locale." + "value" : "Ignora certificati SSL" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Tilgang til lokalt nettverk kan være nødvendig." + "value" : "Ignorer SSL-sertifikater" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Toegang tot lokaal netwerk kan vereist zijn." + "value" : "Negeer SSL-certificaten" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Может потребоваться доступ к локальной сети." + "value" : "Игнорировать SSL-сертификаты" } } } }, - "Local Network Access Required" : { - "comment" : "A title for an alert that warns the user that local network access is required to connect to a local openHAB server.", - "isCommentAutoGenerated" : true, + "Image Cache" : { + "comment" : "The title of an alert that shows the result of checking and clearing the image cache.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Zugriff auf lokales Netzwerk erforderlich" + "value" : "Bilder-Cache" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Image Cache" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Se requiere acceso a la red local" + "value" : "Caché de imágenes" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Lähiverkon käyttöoikeus vaaditaan" + "value" : "Kuvavälimuisti" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Accès au réseau local requis" + "value" : "Cache d'images" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Accesso alla rete locale richiesto" + "value" : "Cache immagini" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Tilgang til lokalt nettverk kreves" + "value" : "Bildebuffer" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Toegang tot lokaal netwerk vereist" + "value" : "Afbeeldingscache" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Требуется доступ к локальной сети" + "value" : "Кэш изображений" } } } }, - "Local server" : { - "comment" : "Title of the section in the connection settings view that pertains to the local OpenHAB server.", + "Import" : { + "comment" : "\"Import\" button title.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Lokaler Server" + "value" : "Importieren" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Local server" + "value" : "Import" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Servidor local" + "value" : "Importar" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Paikallinen palvelin" + "value" : "Tuo" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Serveur local" + "value" : "Importer" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Server locale" + "value" : "Importa" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Lokal server" + "value" : "Importer" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Lokale server" + "value" : "Importeer" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Локальный сервер" + "value" : "Импорт" } } } }, - "local_url" : { + "Inactive" : { + "comment" : "Displayed when the contact is closed.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Lokale URL" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Local URL" + "value" : "Inaktiv" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "URL local" + "value" : "Inactivo" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Local URL" + "value" : "Ei aktiivinen" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "URL locale" + "value" : "Inactif" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "URL locale" + "value" : "Inattivo" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Lokal URL" + "value" : "Inaktiv" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Lokale URL" + "value" : "Inactief" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Локальный URL-адрес" + "value" : "Неактивен" } } } }, - "Location" : { - "comment" : "Name of a marker displayed on a map view.", + "Increase %@" : { + "comment" : "A button that increases the value of a setpoint. The argument is the name of the setpoint.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Standort" + "value" : "%@ erhöhen" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Location" + "value" : "Increase %@" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Ubicación" + "value" : "Aumentar %@" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Sijainti" + "value" : "Suurenna %@" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Emplacement" + "value" : "Augmenter %@" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Posizione" + "value" : "Aumenta %@" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Plassering" + "value" : "Øk %@" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Locatie" + "value" : "%@ verhogen" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Местоположение" + "value" : "Увеличить %@" } } } }, - "Location Item" : { - "comment" : "Display name for a location item type.", - "isCommentAutoGenerated" : true, + "info" : { + "extractionState" : "manual", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Standort-Item" + "value" : "Info" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Info" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Elemento de ubicación" + "value" : "Información" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Sijaintielementti" + "value" : "Info" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Élément de localisation" + "value" : "Infos" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Elemento posizione" + "value" : "Informazioni" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Plasseringselement" + "value" : "Informasjon" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Locatie-item" + "value" : "Info" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Элемент местоположения" + "value" : "Info" } } } }, - "Logs" : { - "comment" : "A link in the debug settings that navigates to the logs viewer.", + "Invalid value %lld for %@ (0-100)" : { + "comment" : "Error message when a dimmer or roller shutter value is out of range. First placeholder is the invalid value, second is the item name.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Protokolle" + "value" : "Ungültiger Wert %lld für %@ (0-100)" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Logs" + "value" : "Invalid value %lld for %@ (0-100)" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Registros" + "value" : "Valor no válido %lld para %@ (0-100)" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Lokit" + "value" : "Virheellinen arvo %lld kohteelle %@ (0–100)" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Historiques" + "value" : "Valeur %lld invalide pour %@ (0-100)" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Log" + "value" : "Valore non valido %lld per %@ (0-100)" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Logger" + "value" : "Ugyldig verdi %lld for %@ (0-100)" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Logbestanden" + "value" : "Ongeldige waarde %lld voor %@ (0-100)" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Журналы" + "value" : "Недопустимое значение %lld для %@ (0–100)" } } } }, - "Longitude" : { - "comment" : "Parameter title for longitude in the Set Location Control Value app intent.", + "Invalid value: %@ for %@ must be HSB (0-360,0-100,0-100)" : { + "comment" : "Error message when a color value is not valid HSB. First placeholder is the invalid value, second is the item name.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Längengrad" + "value" : "Ungültiger Wert: %@ für %@ muss HSB sein (0-360,0-100,0-100)" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Longitude" + "value" : "Invalid value: %@ for %@ must be HSB (0-360,0-100,0-100)" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Longitud" + "value" : "Valor no válido: %@ para %@ debe ser HSB (0-360,0-100,0-100)" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Pituusaste" + "value" : "Virheellinen arvo: %@ kohteelle %@ täytyy olla HSB (0–360, 0–100, 0–100)" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Longitude" + "value" : "Valeur invalide : %@ pour %@ doit être HSB (0-360,0-100,0-100)" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Longitudine" + "value" : "Valore non valido: %@ per %@ deve essere HSB (0-360,0-100,0-100)" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Lengdegrad" + "value" : "Ugyldig verdi: %@ for %@ må være HSB (0-360,0-100,0-100)" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Lengtegraad" + "value" : "Ongeldige waarde: %@ voor %@ moet HSB zijn (0-360,0-100,0-100)" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Долгота" + "value" : "Недопустимое значение: %@ для %@ должно быть HSB (0–360, 0–100, 0–100)" } } } }, - "Longitude must be between -180 and 180" : { - "comment" : "Error message when a longitude value is out of the valid range.", + "invalid_connection_configuration" : { + "extractionState" : "manual", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Längengrad muss zwischen -180 and 180 sein" + "value" : "Ungültige Verbindungskonfiguration." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Longitude must be between -180 and 180" + "value" : "Invalid connection configuration." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "La longitud debe estar entre -180 y 180" + "value" : "Configuración de conexión no válida." } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Pituusasteen on oltava välillä −180 ja 180" + "value" : "Virheellinen yhteysasetukset." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "La longitude doit être comprise entre -180 et 180" + "value" : "Configuration de connexion invalide." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "La longitudine deve essere compresa tra -180 e 180" + "value" : "Configurazione della connessione non valida." } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Lengdegraden må være mellom -180 og 180" + "value" : "Ugyldig tilkoblingskonfigurasjon." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Lengtegraad moet tussen -180 en 180 liggen" + "value" : "Ongeldige verbindingsconfiguratie." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Долгота должна быть от -180 до 180" + "value" : "Недопустимая конфигурация подключения." } } } }, - "Lowers by %@" : { - "comment" : "A hint that describes how much the value can be decreased by.", + "Item" : { + "comment" : "Parameter title for an openHAB item in app intents.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Um %@ senken" + "value" : "Item" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Lowers by %@" + "value" : "Item" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Baja %@" + "value" : "Ítem" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Laskee %@" + "value" : "Elementti" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Abaisse de %@" + "value" : "Item" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Abbassa di %@" + "value" : "Item" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Senker med %@" + "value" : "Item" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Verlaagt met %@" + "value" : "Item" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Снижает на %@" + "value" : "Элемент" } } } }, - "Main" : { - "comment" : "The header text for the \"Main\" section in the drawer view.", + "Item '%@' is not in home '%@'" : { + "comment" : "Error message when the selected item does not belong to the selected home. First placeholder is the item name, second is the home name.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Main" + "value" : "Das Item „%@“ befindet sich nicht im Zuhause „%@“" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Main" + "value" : "Item '%@' is not in home '%@'" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Principal" + "value" : "El elemento ‘%@’ no está en el hogar ‘%@’" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Pää" + "value" : "Elementti ‘%@’ ei ole kodissa ‘%@’" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Principal" + "value" : "L'élément '%@' n'est pas dans la maison '%@'" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Principale" + "value" : "L'elemento '%@' non si trova nella casa '%@'" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Hoved" + "value" : "Element ‘%@’ er ikke i hjemmet ‘%@’" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Hoofd" + "value" : "Item '%@' staat niet in huis '%@'" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Главная" + "value" : "Элемент «%@» не входит в дом «%@»" } } } }, - "mainui_settings" : { + "Item '%@' not found" : { + "comment" : "Error message when an item is not found. The argument is the name of the item.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Main UI-Einstellungen" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Main UI Settings" + "value" : "Element '%@' nicht gefunden" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Ajustes de la interfaz de usuario principal" + "value" : "Elemento ‘%@’ no encontrado" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Main UI Settings" + "value" : "Elementtiä ‘%@’ ei löydy" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Paramètres Main UI" + "value" : "Élément '%@' introuvable" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Impostazioni Main UI" + "value" : "Elemento '%@' non trovato" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Innstillinger for Main UI" + "value" : "Element ‘%@’ ikke funnet" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Main UI Instellingen" + "value" : "Item '%@' niet gevonden" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Main UI Settings" + "value" : "Элемент «%@» не найден" } } } }, - "Manage Homes" : { - "comment" : "The title of a view that allows users to manage their homes.", + "Items" : { + "comment" : "The title of the view that lists available items.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Zuhause verwalten" + "value" : "Items" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Manage Homes" + "value" : "Items" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Gestionar hogares" + "value" : "Ítems" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Hallitse koteja" + "value" : "Elementit" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Gérer les maisons" + "value" : "Éléments" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Gestisci case" + "value" : "Elementi" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Administrer hjem" + "value" : "Elementer" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Huizen beheren" + "value" : "Onderdelen" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Управление домами" + "value" : "Элементы" } } } }, - "message_not_decoded" : { + "Label" : { + "extractionState" : "manual", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Nachricht konnte nicht dekodiert werden" + "value" : "Label" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Message could not be decoded" + "value" : "Label" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "No se ha podido decodificar el mensaje" + "value" : "Etiqueta" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Message could not be decoded" + "value" : "Etiketti" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Le message n'a pas pu être décodé" + "value" : "Étiquette" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Il messaggio non può essere decodificato" + "value" : "Etichetta" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Melding kunne ikke dekodes" + "value" : "Etikett" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Bericht kon niet gedecodeerd worden" + "value" : "Label" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Message could not be decoded" + "value" : "Метка" } } } }, - "Movement Interval: %lld s" : { - "comment" : "A stepper that allows the user to set the interval in seconds between when the screen saver will activate and when it will deactivate after a user is detected moving.", + "Latitude" : { + "comment" : "Parameter title for latitude in the Set Location Control Value app intent.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Bewegungsintervall: %lld s" + "value" : "Breitengrad" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Movement Interval: %lld s" + "value" : "Latitude" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Intervalo de movimiento: %lld s" + "value" : "Latitud" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Liikeväli: %lld s" + "value" : "Leveysaste" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Intervalle de déplacement : %lld s" + "value" : "Latitude" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Intervallo di movimento: %lld s" + "value" : "Latitudine" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Bevegelsesintervall: %lld s" + "value" : "Breddegrad" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Bewegingsinterval: %lld s" + "value" : "Breedtegraad" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Интервал движения: %lld с" + "value" : "Широта" } } } }, - "Name" : { - "extractionState" : "manual", + "Latitude must be between -90 and 90" : { + "comment" : "Error message when a latitude value is out of the valid range.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Name" + "value" : "Breitengrad muss zwischen -90 and 90 sein" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Name" + "value" : "Latitude must be between -90 and 90" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Nombre" + "value" : "La latitud debe estar entre -90 y 90" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Nimi" + "value" : "Leveysasteen on oltava välillä −90 ja 90" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Nom" + "value" : "La latitude doit être comprise entre -90 et 90" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Nome" + "value" : "La latitudine deve essere compresa tra -90 e 90" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Navn" + "value" : "Breddegraden må være mellom -90 og 90" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Naam" + "value" : "Breedtegraad moet tussen -90 en 90 liggen" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Имя" + "value" : "Широта должна быть от -90 до 90" } } } }, - "Name for new home" : { - "comment" : "A placeholder label for a text field in an alert used to enter the name of a new home.", + "legal" : { + "extractionState" : "manual", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Name für neues Zuhause" + "value" : "Rechtliches" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Name for new home" + "value" : "Legal" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Nombre para el nuevo hogar" + "value" : "Legal" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Nimi uudelle kodille" + "value" : "Legal" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Nom de la nouvelle maison" + "value" : "Légal" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Nome per la nuova casa" + "value" : "Legale" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Navn for nytt hjem" + "value" : "Juridisk" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Naam voor nieuw huis" + "value" : "Juridisch" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Имя для нового дома" + "value" : "Legal" } } } }, - "network_not_available" : { + "Legal" : { + "comment" : "A link to the app's legal information.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Netzwerk ist nicht verfügbar." + "value" : "Rechtliches" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Network is not available." + "value" : "Legal" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Red no disponible." + "value" : "Aviso legal" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Network is not available." + "value" : "Oikeudelliset tiedot" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Aucune connexion réseau disponible." + "value" : "Mentions légales" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Rete non disponibile." + "value" : "Note legali" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Nettverk er ikke tilgjengelig." + "value" : "Juridisk" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Netwerk is niet beschikbaar." + "value" : "Juridische informatie" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Network is not available." + "value" : "Юридическая информация" } } } }, - "New name" : { - "comment" : "A label for a text field where the user can enter a new name for a home.", + "Loading Items…" : { + "comment" : "A message displayed while waiting to load items.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Neuer Name" + "value" : "Items werden geladen …" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "New name" + "value" : "Loading Items…" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Nuevo nombre" + "value" : "Cargando elementos…" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Uusi nimi" + "value" : "Ladataan elementtejä…" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Nouveau nom" + "value" : "Chargement des éléments…" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Nuovo nome" + "value" : "Caricamento elementi…" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Nytt navn" + "value" : "Laster elementer…" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Nieuwe naam" + "value" : "Items worden geladen…" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Новое имя" + "value" : "Загрузка элементов…" } } } }, - "Next" : { - "comment" : "Display name for the next action in the PlayerAction enum.", + "Loading sitemap…" : { + "comment" : "A placeholder text indicating that the sitemap is being loaded.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Nächstes" + "value" : "Sitemap wird geladen …" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Next" + "value" : "Loading sitemap…" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Siguiente" + "value" : "Cargando mapa del sitio…" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Seuraava" + "value" : "Ladataan sivukarttaa…" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Suivant" + "value" : "Chargement du plan du site…" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Successivo" + "value" : "Caricamento sitemap…" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Neste" + "value" : "Laster nettstedskart…" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Volgende" + "value" : "Sitemap wordt geladen…" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Следующий" + "value" : "Загрузка карты сайта…" } } } }, - "Next track" : { - "comment" : "Synonyms for the \"Next\" player action.", - "isCommentAutoGenerated" : true - }, - "No accepted server certificates" : { - "comment" : "A message displayed when there are no accepted server certificates.", + "Loading…" : { + "extractionState" : "manual", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Keine akzeptierten Server-Zertifikate" + "value" : "Wird geladen …" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "No accepted server certificates" + "value" : "Loading…" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "No hay certificados de servidor aceptados" + "value" : "Cargando…" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Ei hyväksyttyjä palvelinvarmenteita" + "value" : "Ladataan …" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Aucun certificat serveur accepté" + "value" : "Chargement…" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Nessun certificato server accettato" + "value" : "Caricamento …" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Ingen godkjente serversertifikater" + "value" : "Laster …" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Geen geaccepteerde servercertificaten" + "value" : "Laden…" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Нет принятых сертификатов сервера" + "value" : "Загрузка …" } } } }, - "No Image URL" : { + "Local Network access may be required." : { + "comment" : "A warning message that appears when the user's local network access is required to connect to a server.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Keine Bild-URL" + "value" : "Möglicherweise ist der Zugriff auf das lokale Netzwerk erforderlich." } }, - "en" : { + "es" : { "stringUnit" : { "state" : "translated", - "value" : "No Image URL" - } - }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sin URL de imagen" + "value" : "Es posible que se requiera acceso a la red local." } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Ei kuvan URL-osoitetta" + "value" : "Lähiverkon käyttöoikeus voi olla tarpeen." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Aucune URL d'image" + "value" : "L'accès au réseau local peut être requis." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Nessun URL immagine" + "value" : "Potrebbe essere necessario l'accesso alla rete locale." } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Ingen bilde-URL" + "value" : "Tilgang til lokalt nettverk kan være nødvendig." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Geen afbeeldings-URL" + "value" : "Toegang tot lokaal netwerk kan vereist zijn." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Нет URL изображения" + "value" : "Может потребоваться доступ к локальной сети." } } } }, - "No sitemaps available" : { - "comment" : "A message indicating that there are no sitemaps available to choose from in the \"Sitemap For Apple Watch\" picker.", + "Local Network Access Required" : { + "comment" : "A title for an alert that warns the user that local network access is required to connect to a local openHAB server.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Keine Sitemaps verfügbar" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "No sitemaps available" + "value" : "Zugriff auf lokales Netzwerk erforderlich" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "No hay mapas del sitio disponibles" + "value" : "Se requiere acceso a la red local" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Ei sivukarttoja saatavilla" + "value" : "Lähiverkon käyttöoikeus vaaditaan" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Aucun plan du site disponible" + "value" : "Accès au réseau local requis" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Nessuna sitemap disponibile" + "value" : "Accesso alla rete locale richiesto" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Ingen nettstedskart tilgjengelig" + "value" : "Tilgang til lokalt nettverk kreves" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Geen sitemaps beschikbaar" + "value" : "Toegang tot lokaal netwerk vereist" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Нет доступных карт сайта" + "value" : "Требуется доступ к локальной сети" } } } }, - "No Video URL" : { - "comment" : "A message displayed when a video URL is not provided for a video row.", + "Local server" : { + "comment" : "Title of the section in the connection settings view that pertains to the local OpenHAB server.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Keine Video-URL" + "value" : "Lokaler Server" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "No Video URL" + "value" : "Local server" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Sin URL de vídeo" + "value" : "Servidor local" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Ei videon URL-osoitetta" + "value" : "Paikallinen palvelin" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Aucune URL vidéo" + "value" : "Serveur local" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Nessun URL video" + "value" : "Server locale" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Ingen video-URL" + "value" : "Lokal server" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Geen video-URL" + "value" : "Lokale server" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Нет URL видео" + "value" : "Локальный сервер" } } } }, - "No widgets available." : { - "comment" : "A message displayed when a user has no widgets configured.", + "local_url" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Keine Widgets verfügbar." + "value" : "Lokale URL" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "No widgets available." + "value" : "Local URL" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "No hay widgets disponibles." + "value" : "URL local" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Ei widgettejä saatavilla." + "value" : "Local URL" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Aucun widget disponible." + "value" : "URL locale" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Nessun widget disponibile." + "value" : "URL locale" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Ingen widgeter tilgjengelig." + "value" : "Lokal URL" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Geen widgets beschikbaar." + "value" : "Lokale URL" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Нет доступных виджетов." + "value" : "Локальный URL-адрес" } } } }, - "no_active_connection" : { - "extractionState" : "manual", + "Location" : { + "comment" : "Name of a marker displayed on a map view.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Keine aktive Verbindung vorhanden. Bitte Einstellungen prüfen" + "value" : "Standort" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "No active connection available. Please check your settings." + "value" : "Location" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "No active connection available. Please check your settings." + "value" : "Ubicación" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "No active connection available. Please check your settings." + "value" : "Sijainti" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "No active connection available. Please check your settings." + "value" : "Emplacement" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "No active connection available. Please check your settings." + "value" : "Posizione" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "No active connection available. Please check your settings." + "value" : "Plassering" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "No active connection available. Please check your settings." + "value" : "Locatie" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "No active connection available. Please check your settings." + "value" : "Местоположение" } } } }, - "no_connection_will_reconnect" : { - "comment" : "Title of a popup message displayed when the OpenHAB client is not connected to the server and will automatically try to reconnect.", + "Location Item" : { + "comment" : "Display name for a location item type.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Keine Verbindung, erneuter Verbindungsversuch" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "no_connection_will_reconnect" + "value" : "Standort-Item" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Sin conexión, reintentando…" + "value" : "Elemento de ubicación" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Ei yhteyttä, yritetään uudelleen…" + "value" : "Sijaintielementti" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Pas de connexion, reconnexion en cours" + "value" : "Élément de localisation" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Nessuna connessione, riconnessione in corso" + "value" : "Elemento posizione" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Ingen tilkobling, kobler til igjen…" + "value" : "Plasseringselement" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Geen verbinding, opnieuw verbinden" + "value" : "Locatie-item" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Нет подключения, повторное подключение…" + "value" : "Элемент местоположения" } } } }, - "no_servers_found" : { + "Logs" : { + "comment" : "A link in the debug settings that navigates to the logs viewer.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "No servers found" + "value" : "Protokolle" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "No servers found" + "value" : "Logs" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "No servers found" + "value" : "Registros" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "No servers found" + "value" : "Lokit" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Aucun serveur trouvé" + "value" : "Historiques" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "No servers found" + "value" : "Log" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "No servers found" + "value" : "Logger" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "No servers found" + "value" : "Logbestanden" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "No servers found" + "value" : "Журналы" } } } }, - "None" : { - "comment" : "A label for the \"Sitemap for CarPlay\" picker.", - "isCommentAutoGenerated" : true - }, - "notification" : { + "Long press the widget to configure which sensors to display" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Benachrichtigung" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Notification" + "value" : "Widget lange drücken, um die anzuzeigenden Sensoren zu konfigurieren" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Notificación" + "value" : "Mantén presionado el widget para configurar qué sensores mostrar" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Notification" + "value" : "Paina widgettiä pitkään määrittääksesi, mitkä anturit näytetään" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Notification" + "value" : "Appuyez longuement sur le widget pour configurer les capteurs à afficher" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Notifica" + "value" : "Tieni premuto il widget per configurare quali sensori mostrare" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Varsel" + "value" : "Trykk og hold på widgeten for å konfigurere hvilke sensorer som skal vises" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Notificatie" + "value" : "Houd de widget ingedrukt om te configureren welke sensoren worden weergegeven" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Notification" + "value" : "Удерживайте виджет, чтобы настроить, какие датчики отображать" } } } }, - "notifications" : { - "extractionState" : "manual", + "Long press the widget to configure which switches to control" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Benachrichtigungen" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Notifications" + "value" : "Widget lange drücken, um die zu steuernden Schalter zu konfigurieren" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Notificaciones" + "value" : "Mantén presionado el widget para configurar qué interruptores controlar" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Notifications" + "value" : "Paina widgettiä pitkään määrittääksesi, mitä kytkimiä ohjataan" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Notifications" + "value" : "Appuyez longuement sur le widget pour configurer les commutateurs à contrôler" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Notifiche" + "value" : "Tieni premuto il widget per configurare quali interruttori controllare" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Varsler" + "value" : "Trykk og hold på widgeten for å konfigurere hvilke brytere som skal styres" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Notificaties" + "value" : "Houd de widget ingedrukt om te configureren welke schakelaars worden bediend" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Notifications" + "value" : "Удерживайте виджет, чтобы настроить, какими переключателями управлять" } } } }, - "Notifications" : { - "comment" : "The title of the notifications view.", + "Longitude" : { + "comment" : "Parameter title for longitude in the Set Location Control Value app intent.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Benachrichtigungen" + "value" : "Längengrad" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Notifications" + "value" : "Longitude" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Notificaciones" + "value" : "Longitud" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Ilmoitukset" + "value" : "Pituusaste" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Notifications" + "value" : "Longitude" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Notifiche" + "value" : "Longitudine" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Varsler" + "value" : "Lengdegrad" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Notificaties" + "value" : "Lengtegraad" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Уведомления" + "value" : "Долгота" } } } }, - "Number Item" : { - "comment" : "Display name for a number item type.", - "isCommentAutoGenerated" : true, + "Longitude must be between -180 and 180" : { + "comment" : "Error message when a longitude value is out of the valid range.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Numerisches-Item" + "value" : "Längengrad muss zwischen -180 and 180 sein" } }, - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Élément numérique" + "value" : "Longitude must be between -180 and 180" } }, - "it" : { + "es" : { "stringUnit" : { "state" : "translated", - "value" : "Elemento numerico" + "value" : "La longitud debe estar entre -180 y 180" } }, - "nl" : { + "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Numeriek item" + "value" : "Pituusasteen on oltava välillä −180 ja 180" } - } - } - }, - "Off" : { - "comment" : "The text \"Off\" displayed in the accessibility value of the toggle.", - "localizations" : { - "de" : { + }, + "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Aus" + "value" : "La longitude doit être comprise entre -180 et 180" } }, - "en" : { + "it" : { "stringUnit" : { "state" : "translated", - "value" : "Off" + "value" : "La longitudine deve essere compresa tra -180 e 180" } }, - "es" : { + "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Desactivado" + "value" : "Lengdegraden må være mellom -180 og 180" } }, - "fi" : { + "nl" : { "stringUnit" : { - "state" : "new", - "value" : "" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Désactivé" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "No" - } - }, - "nb" : { - "stringUnit" : { - "state" : "new", - "value" : "" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Uit" + "state" : "translated", + "value" : "Lengtegraad moet tussen -180 en 180 liggen" } }, "ru" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Долгота должна быть от -180 до 180" } } } }, - "Offline" : { - "comment" : "A label indicating that the device is currently offline.", + "Lowers by %@" : { + "comment" : "A hint that describes how much the value can be decreased by.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Offline" + "value" : "Um %@ senken" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Offline" + "value" : "Lowers by %@" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Sin conexión" + "value" : "Baja %@" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Offline" + "value" : "Laskee %@" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Déconnecté" + "value" : "Abaisse de %@" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Non in linea" + "value" : "Abbassa di %@" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Frakoblet" + "value" : "Senker med %@" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Offline" + "value" : "Verlaagt met %@" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Не в сети" + "value" : "Снижает на %@" } } } }, - "oh_secret" : { - "extractionState" : "manual", + "Main" : { + "comment" : "The header text for the \"Main\" section in the drawer view.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB-Secret" + "value" : "Main" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB Secret" + "value" : "Main" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "código secreto de openHAB" + "value" : "Principal" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB Secret" + "value" : "Pää" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Phrase secrète openHAB" + "value" : "Principal" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB Secret" + "value" : "Principale" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB Hemmelighet" + "value" : "Hoved" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB geheim" + "value" : "Hoofd" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB Secret" + "value" : "Главная" } } } }, - "oh_uuid" : { - "extractionState" : "manual", + "mainui_settings" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB-UUID" + "value" : "Main UI-Einstellungen" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB UUID" + "value" : "Main UI Settings" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB UUID" + "value" : "Ajustes de la interfaz de usuario principal" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB UUID" + "value" : "Main UI Settings" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB UUID" + "value" : "Paramètres Main UI" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB UUID" + "value" : "Impostazioni Main UI" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB UUID" + "value" : "Innstillinger for Main UI" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB UUID" + "value" : "Main UI Instellingen" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB UUID" + "value" : "Main UI Settings" } } } }, - "oh_version" : { - "extractionState" : "manual", + "Manage Homes" : { + "comment" : "The title of a view that allows users to manage their homes.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB-Version" + "value" : "Zuhause verwalten" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB Version" + "value" : "Manage Homes" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Versión de openHAB" + "value" : "Gestionar hogares" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB Version" + "value" : "Hallitse koteja" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB Version" + "value" : "Gérer les maisons" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Versione openHAB" + "value" : "Gestisci case" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB versjon" + "value" : "Administrer hjem" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB Versie" + "value" : "Huizen beheren" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB Version" + "value" : "Управление домами" } } } }, - "OK" : { - "comment" : "The text for an OK button.", + "message_not_decoded" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "OK" + "value" : "Nachricht konnte nicht dekodiert werden" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "OK" + "value" : "Message could not be decoded" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Aceptar" + "value" : "No se ha podido decodificar el mensaje" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "OK" + "value" : "Message could not be decoded" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "OK" + "value" : "Le message n'a pas pu être décodé" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "OK" + "value" : "Il messaggio non può essere decodificato" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "OK" + "value" : "Melding kunne ikke dekodes" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "OK" + "value" : "Bericht kon niet gedecodeerd worden" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "ОК" + "value" : "Message could not be decoded" } } } }, - "On" : { - "comment" : "A label indicating that a switch is on.", + "Movement Interval: %lld s" : { + "comment" : "A stepper that allows the user to set the interval in seconds between when the screen saver will activate and when it will deactivate after a user is detected moving.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "An" + "value" : "Bewegungsintervall: %lld s" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "On" + "value" : "Movement Interval: %lld s" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Encendido" + "value" : "Intervalo de movimiento: %lld s" } }, "fi" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Liikeväli: %lld s" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Activé" + "value" : "Intervalle de déplacement : %lld s" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Attivo" + "value" : "Intervallo di movimento: %lld s" } }, "nb" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Bevegelsesintervall: %lld s" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Aan" + "value" : "Bewegingsinterval: %lld s" } }, "ru" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Интервал движения: %lld с" } } } }, - "Once" : { - "comment" : "Button to allow the app to connect to the server only once, even if the certificate is invalid.", + "Name" : { + "extractionState" : "manual", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Einmal" + "value" : "Name" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Once" + "value" : "Name" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Una vez" + "value" : "Nombre" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Kerran" + "value" : "Nimi" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Une fois" + "value" : "Nom" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Una volta" + "value" : "Nome" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Én gang" + "value" : "Navn" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Eenmalig" + "value" : "Naam" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Один раз" + "value" : "Имя" } } } }, - "open" : { - "extractionState" : "manual", + "Name for new home" : { + "comment" : "A placeholder label for a text field in an alert used to enter the name of a new home.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "offen" + "value" : "Name für neues Zuhause" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "open" + "value" : "Name for new home" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "abierto" + "value" : "Nombre para el nuevo hogar" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "open" + "value" : "Nimi uudelle kodille" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "ouvert" + "value" : "Nom de la nouvelle maison" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "aperto" + "value" : "Nome per la nuova casa" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "åpen" + "value" : "Navn for nytt hjem" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "open" + "value" : "Naam voor nieuw huis" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "open" + "value" : "Имя для нового дома" } } } }, - "Open" : { - "comment" : "Displayed name for the 'Open' contact state.", - "isCommentAutoGenerated" : true, + "network_not_available" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Geöffnet" + "value" : "Netzwerk ist nicht verfügbar." + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Network is not available." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Abierto" + "value" : "Red no disponible." } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Auki" + "value" : "Network is not available." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Ouvert" + "value" : "Aucune connexion réseau disponible." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Aperto" + "value" : "Rete non disponibile." } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Åpen" + "value" : "Nettverk er ikke tilgjengelig." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Open" + "value" : "Netwerk is niet beschikbaar." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Открыто" + "value" : "Network is not available." } } } }, - "Open Settings" : { - "comment" : "A button that opens the user's system settings.", - "isCommentAutoGenerated" : true, + "New name" : { + "comment" : "A label for a text field where the user can enter a new name for a home.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Einstellungen öffnen" + "value" : "Neuer Name" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "New name" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Abrir ajustes" + "value" : "Nuevo nombre" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Avaa asetukset" + "value" : "Uusi nimi" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Ouvrir les réglages" + "value" : "Nouveau nom" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Apri Impostazioni" + "value" : "Nuovo nome" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Åpne innstillinger" + "value" : "Nytt navn" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Instellingen openen" + "value" : "Nieuwe naam" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Открыть настройки" + "value" : "Новое имя" } } } }, - "Opened" : { - "comment" : "A synonym for the \"Open\" contact state.", - "isCommentAutoGenerated" : true - }, - "openHAB Cloud Service" : { - "comment" : "A toggle that allows the user to enable or disable notifications from the openHAB Cloud Service.", + "Next" : { + "comment" : "Display name for the next action in the PlayerAction enum.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB Cloud-Dienst" + "value" : "Nächstes" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB Cloud Service" + "value" : "Next" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Servicio en la nube openHAB" + "value" : "Siguiente" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB-pilvipalvelu" + "value" : "Seuraava" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Service openHAB Cloud" + "value" : "Suivant" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Servizio openHAB Cloud" + "value" : "Successivo" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB-skytjeneste" + "value" : "Neste" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB Cloud-dienst" + "value" : "Volgende" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Облачный сервис openHAB" + "value" : "Следующий" } } } }, - "openhab_connection" : { - "extractionState" : "manual", + "Next track" : { + "comment" : "Synonyms for the \"Next\" player action.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB Verbindung" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "openHAB Connection" + "value" : "Nächster Titel" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "conexión openHAB" + "value" : "Siguiente pista" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB Connection" + "value" : "Seuraava raita" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Connexion openHAB" + "value" : "Piste suivante" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Connessione openHAB" + "value" : "Traccia successiva" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB Tilkobling" + "value" : "Neste spor" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "openHAB Verbinding" + "value" : "Volgende nummer" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "подключение к OpenHAB" + "value" : "Следующий трек" } } } }, - "password" : { + "No accepted server certificates" : { + "comment" : "A message displayed when there are no accepted server certificates.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Passwort" + "value" : "Keine akzeptierten Server-Zertifikate" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Password" + "value" : "No accepted server certificates" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Contraseña" + "value" : "No hay certificados de servidor aceptados" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Password" + "value" : "Ei hyväksyttyjä palvelinvarmenteita" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Mot de passe" + "value" : "Aucun certificat serveur accepté" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Password" + "value" : "Nessun certificato server accettato" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Passord" + "value" : "Ingen godkjente serversertifikater" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Wachtwoord" + "value" : "Geen geaccepteerde servercertificaten" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Password" + "value" : "Нет принятых сертификатов сервера" } } } }, - "Password" : { - "comment" : "A label displayed above a text field for entering a password.", + "No Data" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Passwort" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Password" + "value" : "Keine Daten" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Contraseña" + "value" : "Sin datos" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Salasana" + "value" : "Ei tietoja" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Mot de passe" + "value" : "Aucune donnée" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Password" + "value" : "Nessun dato" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Passord" + "value" : "Ingen data" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Wachtwoord" + "value" : "Geen gegevens" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Пароль" + "value" : "Нет данных" } } } }, - "Pause" : { - "comment" : "Display name for the pause action in the PlayerAction enum.", + "No Image URL" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Pause" + "value" : "Keine Bild-URL" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Pause" + "value" : "No Image URL" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Pausa" + "value" : "Sin URL de imagen" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Tauko" + "value" : "Ei kuvan URL-osoitetta" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Pause" + "value" : "Aucune URL d'image" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Pausa" + "value" : "Nessun URL immagine" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Pause" + "value" : "Ingen bilde-URL" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Pauzeren" + "value" : "Geen afbeeldings-URL" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Пауза" + "value" : "Нет URL изображения" } } } }, - "Play" : { - "comment" : "Display name for the play action in the PlayerAction enum.", + "No sitemaps available" : { + "comment" : "A message indicating that there are no sitemaps available to choose from in the \"Sitemap For Apple Watch\" picker.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Abspielen" + "value" : "Keine Sitemaps verfügbar" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Play" + "value" : "No sitemaps available" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Reproducir" + "value" : "No hay mapas del sitio disponibles" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Toista" + "value" : "Ei sivukarttoja saatavilla" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Lire" + "value" : "Aucun plan du site disponible" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Riproduci" + "value" : "Nessuna sitemap disponibile" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Spill av" + "value" : "Ingen nettstedskart tilgjengelig" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Afspelen" + "value" : "Geen sitemaps beschikbaar" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Воспроизвести" + "value" : "Нет доступных карт сайта" } } } }, - "Player Action" : { - "comment" : "Type display name for the PlayerAction enum used in app intents.", + "No State" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Player-Aktion" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Player Action" + "value" : "Kein Status" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Acción del reproductor" + "value" : "Sin estado" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Soittimen toiminto" + "value" : "Ei tilaa" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Action du lecteur" + "value" : "Aucun état" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Azione player" + "value" : "Nessuno stato" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Spillerhandling" + "value" : "Ingen tilstand" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Speleractie" + "value" : "Geen status" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Действие плеера" + "value" : "Нет состояния" } } } }, - "Player Item" : { - "comment" : "Display name for a player item type.", - "isCommentAutoGenerated" : true, + "No Video URL" : { + "comment" : "A message displayed when a video URL is not provided for a video row.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Player-Item" + "value" : "Keine Video-URL" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No Video URL" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Elemento reproductor" + "value" : "Sin URL de vídeo" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Soitinelementti" + "value" : "Ei videon URL-osoitetta" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Élément lecteur" + "value" : "Aucune URL vidéo" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Elemento player" + "value" : "Nessun URL video" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Spillerelement" + "value" : "Ingen video-URL" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Speler-item" + "value" : "Geen video-URL" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Элемент плеера" + "value" : "Нет URL видео" } } } }, - "PNG" : { - "extractionState" : "manual", + "No widgets available." : { + "comment" : "A message displayed when a user has no widgets configured.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "PNG" + "value" : "Keine Widgets verfügbar." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "PNG" + "value" : "No widgets available." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "PNG" + "value" : "No hay widgets disponibles." } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "PNG" + "value" : "Ei widgettejä saatavilla." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "PNG" + "value" : "Aucun widget disponible." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "PNG" + "value" : "Nessun widget disponibile." } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "PNG" + "value" : "Ingen widgeter tilgjengelig." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "PNG" + "value" : "Geen widgets beschikbaar." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "PNG" + "value" : "Нет доступных виджетов." } } } }, - "Preferences" : { - "comment" : "Label for the preferences tab in the watch app.", + "no_active_connection" : { + "extractionState" : "manual", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Einstellungen" + "value" : "Keine aktive Verbindung vorhanden. Bitte Einstellungen prüfen" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Preferences" + "value" : "No active connection available. Please check your settings." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Preferencias" + "value" : "No active connection available. Please check your settings." } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Asetukset" + "value" : "No active connection available. Please check your settings." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Préférences" + "value" : "No active connection available. Please check your settings." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Preferenze" + "value" : "No active connection available. Please check your settings." } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Innstillinger" + "value" : "No active connection available. Please check your settings." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Voorkeuren" + "value" : "No active connection available. Please check your settings." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Настройки" + "value" : "No active connection available. Please check your settings." } } } }, - "Preview state" : { - "comment" : "The accessibility label for the picker that allows the user to select and preview a different state of an interactive state token.", + "no_connection_will_reconnect" : { + "comment" : "Title of a popup message displayed when the OpenHAB client is not connected to the server and will automatically try to reconnect.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Vorschauzustand" + "value" : "Keine Verbindung, erneuter Verbindungsversuch" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Preview state" + "value" : "no_connection_will_reconnect" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Estado de vista previa" + "value" : "Sin conexión, reintentando…" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Esikatselutila" + "value" : "Ei yhteyttä, yritetään uudelleen…" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "État d'aperçu" + "value" : "Pas de connexion, reconnexion en cours" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Stato anteprima" + "value" : "Nessuna connessione, riconnessione in corso" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Forhåndsvisningstilstand" + "value" : "Ingen tilkobling, kobler til igjen…" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Voorbeeldstatus" + "value" : "Geen verbinding, opnieuw verbinden" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Состояние предварительного просмотра" + "value" : "Нет подключения, повторное подключение…" } } } }, - "Previous" : { - "comment" : "Display name for the previous action in the PlayerAction enum.", + "no_servers_found" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Vorheriges" + "value" : "No servers found" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Previous" + "value" : "No servers found" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Anterior" + "value" : "No servers found" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Edellinen" + "value" : "No servers found" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Précédent" + "value" : "Aucun serveur trouvé" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Precedente" + "value" : "No servers found" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Forrige" + "value" : "No servers found" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Vorige" + "value" : "No servers found" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Предыдущий" + "value" : "No servers found" } } } }, - "Previous track" : { - "comment" : "Display name for the previous action.", - "isCommentAutoGenerated" : true - }, - "privacy_policy" : { + "notification" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Datenschutzerklärung" + "value" : "Benachrichtigung" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Privacy Policy" + "value" : "Notification" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Política de privacidad" + "value" : "Notificación" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Privacy Policy" + "value" : "Notification" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Politique de Confidentialité" + "value" : "Notification" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Politica sulla privacy" + "value" : "Notifica" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Personvernregler" + "value" : "Varsel" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Privacybeleid" + "value" : "Notificatie" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Privacy Policy" + "value" : "Notification" } } } }, - "Queued commands: %lld" : { - "comment" : "A label indicating that there are one or more commands in the queue. The placeholder inside the label is replaced with the actual number of queued commands.", + "notifications" : { + "extractionState" : "manual", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Wartende Befehle: %lld" + "value" : "Benachrichtigungen" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Queued commands: %lld" + "value" : "Notifications" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Comandos en cola: %lld" + "value" : "Notificaciones" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Jonossa olevat komennot: %lld" + "value" : "Notifications" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Commandes en attente : %lld" + "value" : "Notifications" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Comandi in coda: %lld" + "value" : "Notifiche" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Kommandoer i kø: %lld" + "value" : "Varsler" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Opdrachten in wachtrij: %lld" + "value" : "Notificaties" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Команды в очереди: %lld" + "value" : "Notifications" } } } }, - "Raises by %@" : { - "comment" : "A hint that appears when a user hovers over the \"Increase\" button in the setpoint row. The argument is the step size for the current setpoint.", + "Notifications" : { + "comment" : "The title of the notifications view.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Um %@ erhöhen" + "value" : "Benachrichtigungen" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Raises by %@" + "value" : "Notifications" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Sube %@" + "value" : "Notificaciones" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Nostaa %@" + "value" : "Ilmoitukset" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Monte de %@" + "value" : "Notifications" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Aumenta di %@" + "value" : "Notifiche" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Øker med %@" + "value" : "Varsler" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Verhoogt met %@" + "value" : "Notificaties" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Повышает на %@" + "value" : "Уведомления" } } } }, - "real_time_sliders" : { - "extractionState" : "manual", + "Number Item" : { + "comment" : "Display name for a number item type.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Echtzeitschieberegler" + "value" : "Numerisches-Item" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément numérique" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento numerico" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Numeriek item" + } + } + } + }, + "Off" : { + "comment" : "The text \"Off\" displayed in the accessibility value of the toggle.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aus" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Real-time Sliders" + "value" : "Off" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Deslizadores en tiempo real" + "value" : "Desactivado" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Real-time Sliders" + "value" : "Pois" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Curseurs en temps réel" + "value" : "Désactivé" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Cursori In Tempo Reale" + "value" : "No" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Sanntids Skyveknapper" + "value" : "Av" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Realtime schuifregelaars" + "value" : "Uit" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Real-time Sliders" + "value" : "Выкл" } } } }, - "Real-time Sliders" : { - "comment" : "A toggle that enables or disables the real-time slider feature.", + "OFF" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Echtzeitschieberegler" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Real-time Sliders" + "value" : "AUS" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Controles deslizantes en tiempo real" + "value" : "APAGADO" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Reaaliaikaiset liukusäätimet" + "value" : "POIS" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Curseurs en temps réel" + "value" : "ÉTEINT" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Cursori in tempo reale" + "value" : "SPENTO" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Sanntidsskyvere" + "value" : "AV" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Realtime schuifregelaars" + "value" : "UIT" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Слайдеры реального времени" + "value" : "ВЫКЛ" } } } }, - "Remote server" : { - "comment" : "Header text for the remote server section in the connection settings view.", + "Offline" : { + "comment" : "A label indicating that the device is currently offline.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Fernzugriff-Server" + "value" : "Offline" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Remote server" + "value" : "Offline" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Servidor remoto" + "value" : "Sin conexión" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Etäpalvelin" + "value" : "Offline" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Serveur distant" + "value" : "Déconnecté" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Server remoto" + "value" : "Non in linea" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Fjernserver" + "value" : "Frakoblet" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Externe server" + "value" : "Offline" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Удалённый сервер" + "value" : "Не в сети" } } } }, - "remote_url" : { - "comment" : "Space constraint string which has only about half the screen width on Apple Watch. Best to try and keep it no longer than the English string.", + "oh_secret" : { + "extractionState" : "manual", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Fern-URL" + "value" : "openHAB-Secret" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Remote URL" + "value" : "openHAB Secret" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "URL remota" + "value" : "código secreto de openHAB" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Remote URL" + "value" : "openHAB Secret" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "URL distante" + "value" : "Phrase secrète openHAB" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "URL remoto" + "value" : "openHAB Secret" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Ekstern URL" + "value" : "openHAB Hemmelighet" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Externe URL" + "value" : "openHAB geheim" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Удаленный URL-адрес" + "value" : "openHAB Secret" } } } }, - "remote_url_not_configured" : { + "oh_uuid" : { "extractionState" : "manual", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "URL für Fernzugriff ist nicht konfiguriert." + "value" : "openHAB-UUID" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Remote URL is not configured." + "value" : "openHAB UUID" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "La URL remota no está configurada." + "value" : "openHAB UUID" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Remote URL is not configured." + "value" : "openHAB UUID" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "L'URL distante n'est pas configurée." + "value" : "openHAB UUID" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "L'URL remoto non è configurato." + "value" : "openHAB UUID" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Ekstern URL er ikke konfigurert." + "value" : "openHAB UUID" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Externe URL is niet geconfigureerd." + "value" : "openHAB UUID" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Remote URL is not configured." + "value" : "openHAB UUID" } } } }, - "Rename" : { - "comment" : "A button that renames a home.", + "oh_version" : { + "extractionState" : "manual", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Umbenennen" + "value" : "openHAB-Version" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Rename" + "value" : "openHAB Version" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Renombrar" + "value" : "Versión de openHAB" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Nimeä uudelleen" + "value" : "openHAB Version" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Renommer" + "value" : "openHAB Version" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Rinomina" + "value" : "Versione openHAB" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Gi nytt navn" + "value" : "openHAB versjon" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Wijzig naam" + "value" : "openHAB Versie" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Переименовать" + "value" : "openHAB Version" } } } }, - "Replay" : { - "comment" : "A synonym for the rewind action.", - "isCommentAutoGenerated" : true - }, - "Reset" : { - "comment" : "Synonyms for the \"Reset\" state of a contact.", - "isCommentAutoGenerated" : true - }, - "Restore Previous Brightness on Wake" : { - "comment" : "A toggle that allows the user to restore the brightness level to its previous value when the device wakes up.", + "OK" : { + "comment" : "The text for an OK button.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Vorherige Helligkeit beim Aufwecken wiederherstellen" + "value" : "OK" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Restore Previous Brightness on Wake" + "value" : "OK" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Restaurar brillo anterior al despertar" + "value" : "Aceptar" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Palauta aiempi kirkkaus herätessä" + "value" : "OK" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Restaurer la luminosité précédente au réveil" + "value" : "OK" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Ripristina luminosità precedente alla riattivazione" + "value" : "OK" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Gjenopprett tidligere lysstyrke ved vekking" + "value" : "OK" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Vorige helderheid herstellen bij activering" + "value" : "OK" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Восстановить предыдущую яркость при пробуждении" + "value" : "ОК" } } } }, - "Resume" : { - "comment" : "Action to resume playback.", - "isCommentAutoGenerated" : true - }, - "Retrieve the current state of an item" : { - "comment" : "Description of the Get Item State app intent.", + "On" : { + "comment" : "A label indicating that a switch is on.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Aktuellen Status eines Items abrufen" + "value" : "An" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Retrieve the current state of an item" + "value" : "On" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Recuperar el estado actual de un elemento" + "value" : "Encendido" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Hae elementin nykyinen tila" + "value" : "Päällä" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Récupérer l'état actuel d'un item" + "value" : "Activé" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Recupera lo stato attuale di un Item" + "value" : "Attivo" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Hent nåværende tilstand for et Item" + "value" : "På" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Haal de huidige status van een item op" + "value" : "Aan" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Получить текущее состояние элемента" + "value" : "Вкл" } } } }, - "retry" : { - "comment" : "retry connection", + "ON" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Erneut versuchen" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Retry" + "value" : "EIN" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Reintentar" + "value" : "ENCENDIDO" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Retry" + "value" : "PÄÄLLÄ" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Réessayer" + "value" : "ALLUMÉ" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Riprova" + "value" : "ACCESO" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Prøv på nytt" + "value" : "PÅ" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Opnieuw" + "value" : "AAN" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Retry" + "value" : "ВКЛ" } } } }, - "Rewind" : { - "comment" : "Display name for the rewind action in the PlayerAction enum.", + "Once" : { + "comment" : "Button to allow the app to connect to the server only once, even if the certificate is invalid.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Zurückspulen" + "value" : "Einmal" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Rewind" + "value" : "Once" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Rebobinar" + "value" : "Una vez" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Kelaa taaksepäin" + "value" : "Kerran" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Retour arrière" + "value" : "Une fois" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Riavvolgi" + "value" : "Una volta" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Spol tilbake" + "value" : "Én gang" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Terugspoelen" + "value" : "Eenmalig" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Перемотка назад" + "value" : "Один раз" } } } }, - "Rewind back" : { - "comment" : "A synonym for the rewind action.", - "isCommentAutoGenerated" : true - }, - "running_demo_mode" : { + "open" : { "extractionState" : "manual", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Läuft im Demomodus. Überprüfe die Einstellungen, um den Demomodus zu deaktivieren." + "value" : "offen" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Running in demo mode. Check settings to disable demo mode." + "value" : "open" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Funccionando en modo demostración. Comprueba la configuración para desactivarlo." + "value" : "abierto" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Running in demo mode. Check settings to disable demo mode." + "value" : "open" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Exécution en mode démo. Vérifiez les paramètres pour désactiver le mode démo." + "value" : "ouvert" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Esecuzione in modalità demo. Controlla le impostazioni per disabilitare la modalità demo." + "value" : "aperto" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Kjører i demomodus. Sjekk innstillingene for å deaktivere demomodus." + "value" : "åpen" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Uitvoeren in demo-modus. Controleer instellingen om demomodus uit te schakelen." + "value" : "open" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Running in demo mode. Check settings to disable demo mode." + "value" : "open" } } } }, - "Save" : { - "comment" : "The text for a button that saves current settings.", + "Open" : { + "comment" : "Displayed name for the 'Open' contact state.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Sichern" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Save" + "value" : "Geöffnet" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Guardar" + "value" : "Abierto" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Tallenna" + "value" : "Auki" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Enregistrer" + "value" : "Ouvert" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Salva" + "value" : "Aperto" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Lagre" + "value" : "Åpen" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Bewaar" + "value" : "Open" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Сохранить" + "value" : "Открыто" } } } }, - "Screen Saver" : { - "comment" : "The title of the screen.", + "Open Settings" : { + "comment" : "A button that opens the user's system settings.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Bildschirmschoner" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Screen Saver" + "value" : "Einstellungen öffnen" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Salvapantallas" + "value" : "Abrir ajustes" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Näytönsäästäjä" + "value" : "Avaa asetukset" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Économiseur d’écran" + "value" : "Ouvrir les réglages" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Salvaschermo" + "value" : "Apri Impostazioni" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Skjermsparer" + "value" : "Åpne innstillinger" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Schermbeveiliging" + "value" : "Instellingen openen" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Заставка экрана" + "value" : "Открыть настройки" } } } }, - "Screen Saver Settings" : { - "comment" : "A link to the settings related to the screen saver.", + "Opened" : { + "comment" : "A synonym for the \"Open\" contact state.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Bildschirmschoner-Einstellungen" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Screen Saver Settings" + "value" : "Geöffnet" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Ajustes del salvapantallas" + "value" : "Abierto" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Näytönsäästäjän asetukset" + "value" : "Avattu" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Paramètres de l'économiseur d'écran" + "value" : "Ouvert" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Impostazioni salvaschermo" + "value" : "Aperto" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Skjermsparer-innstillinger" + "value" : "Åpnet" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Schermbeveiligingsinstellingen" + "value" : "Geopend" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Настройки заставки" + "value" : "Открыт" } } } }, - "Search" : { - "comment" : "A text field for searching through a list of items.", + "openHAB Cloud Service" : { + "comment" : "A toggle that allows the user to enable or disable notifications from the openHAB Cloud Service.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Suchen" + "value" : "openHAB Cloud-Dienst" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Search" + "value" : "openHAB Cloud Service" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Buscar" + "value" : "Servicio en la nube openHAB" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Haku" + "value" : "openHAB-pilvipalvelu" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Rechercher" + "value" : "Service openHAB Cloud" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Cerca" + "value" : "Servizio openHAB Cloud" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Søk" + "value" : "openHAB-skytjeneste" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Zoek" + "value" : "openHAB Cloud-dienst" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Поиск" + "value" : "Облачный сервис openHAB" } } } }, - "Search for an item" : { - "comment" : "Dialog text prompting the user to search for an openHAB item in app intents.", + "openHAB Sensor" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Nach Item suchen" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Search for an item" + "value" : "openHAB-Sensor" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Buscar un elemento" + "value" : "Sensor de openHAB" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Etsi elementtiä" + "value" : "openHAB-anturi" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Rechercher un élément" + "value" : "Capteur openHAB" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Cerca un elemento" + "value" : "Sensore openHAB" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Søk etter et element" + "value" : "openHAB-sensor" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Zoeken naar een item" + "value" : "openHAB-sensor" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Найти элемент" + "value" : "Датчик openHAB" } } } }, - "search_items" : { + "openHAB Switch" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Suche openHAB Items" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Search openHAB items" + "value" : "openHAB-Schalter" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Buscar ítems en openHAB" + "value" : "Interruptor de openHAB" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Search openHAB items" + "value" : "openHAB-kytkin" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Rechercher des items openHAB" + "value" : "Commutateur openHAB" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Cerca Item openHAB" + "value" : "Interruttore openHAB" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Søk i openHAB Items" + "value" : "openHAB-bryter" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Zoek openHAB items" + "value" : "openHAB-schakelaar" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Search openHAB items" + "value" : "Переключатель openHAB" } } } }, - "Select a home for '%@'" : { - "comment" : "A message to be displayed in a dialog box when the user needs to select a home. The argument is the name of the item that needs a home.", - "isCommentAutoGenerated" : true, + "openhab_connection" : { + "extractionState" : "manual", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Zuhause für '%@' auswählen" + "value" : "openHAB Verbindung" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Connection" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Seleccionar una casa para '%@'" + "value" : "conexión openHAB" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Valitse koti kohteelle '%@'" + "value" : "openHAB Connection" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Sélectionner un domicile pour '%@'" + "value" : "Connexion openHAB" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Seleziona una casa per '%@'" + "value" : "Connessione openHAB" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Velg et hjem for '%@'" + "value" : "openHAB Tilkobling" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Selecteer een thuis voor '%@'" + "value" : "openHAB Verbinding" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Выберите дом для '%@'" + "value" : "подключение к OpenHAB" } } } }, - "Select color" : { - "comment" : "A button that, when pressed, opens a view allowing the user to select a color.", + "password" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Farbe wählen" + "value" : "Passwort" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Select color" + "value" : "Password" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Seleccionar color" + "value" : "Contraseña" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Valitse väri" + "value" : "Password" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Sélectionner une couleur" + "value" : "Mot de passe" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Seleziona colore" + "value" : "Password" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Velg farge" + "value" : "Passord" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Kleur selecteren" + "value" : "Wachtwoord" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Выбрать цвет" + "value" : "Password" } } } }, - "select_sitemap" : { - "extractionState" : "manual", + "Password" : { + "comment" : "A label displayed above a text field for entering a password.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Sitemap auswählen" + "value" : "Passwort" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Select Sitemap" + "value" : "Password" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Seleccionar mapa web" + "value" : "Contraseña" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Select Sitemap" + "value" : "Salasana" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Sélectionner le Sitemap" + "value" : "Mot de passe" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Seleziona Sitemap" + "value" : "Password" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Velg Sitemap" + "value" : "Passord" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Selecteer Sitemap" + "value" : "Wachtwoord" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Выбрать Sitemap" + "value" : "Пароль" } } } }, - "Send ${action} to ${itemEntity}" : { + "Pause" : { + "comment" : "Display name for the pause action in the PlayerAction enum.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "${action} an ${itemEntity} senden" + "value" : "Pause" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pause" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Enviar ${action} a ${itemEntity}" + "value" : "Pausa" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Lähetä ${action} kohteeseen ${itemEntity}" + "value" : "Tauko" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Envoyer ${action} à ${itemEntity}" + "value" : "Pause" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Invia ${action} a ${itemEntity}" + "value" : "Pausa" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Send ${action} til ${itemEntity}" + "value" : "Pause" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "${action} verzenden naar ${itemEntity}" + "value" : "Pauzeren" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Отправить ${action} в ${itemEntity}" + "value" : "Пауза" } } } }, - "Send a player command such as play, pause, next, or previous" : { - "comment" : "Description of the Set Player Control Value app intent.", + "Play" : { + "comment" : "Display name for the play action in the PlayerAction enum.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Einen Player-Befehl senden, z. B. Abspielen, Pause, Weiter oder Zurück" + "value" : "Abspielen" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Send a player command such as play, pause, next, or previous" + "value" : "Play" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Enviar un comando al reproductor como reproducir, pausar, siguiente o anterior" + "value" : "Reproducir" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Lähetä soittimelle komento, kuten toista, tauko, seuraava tai edellinen" + "value" : "Toista" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Envoyer une commande au lecteur telle que lire, pause, suivant ou précédent" + "value" : "Lire" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Invia un comando al player come riproduci, pausa, successivo o precedente" + "value" : "Riproduci" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Send en spillerkommando som spill av, pause, neste eller forrige" + "value" : "Spill av" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Een spelersopdracht verzenden zoals afspelen, pauzeren, volgende of vorige" + "value" : "Afspelen" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Отправить команду плееру: воспроизвести, пауза, следующий или предыдущий" + "value" : "Воспроизвести" } } } }, - "Sending commands: %lld" : { + "Player Action" : { + "comment" : "Type display name for the PlayerAction enum used in app intents.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Befehle werden gesendet: %lld" + "value" : "Player-Aktion" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Sending commands: %lld" + "value" : "Player Action" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Enviando comandos: %lld" + "value" : "Acción del reproductor" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Lähetetään komentoja: %lld" + "value" : "Soittimen toiminto" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Envoi de commandes : %lld" + "value" : "Action du lecteur" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Invio comandi: %lld" + "value" : "Azione player" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Sender kommandoer: %lld" + "value" : "Spillerhandling" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Opdrachten verzenden: %lld" + "value" : "Speleractie" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Отправка команд: %lld" + "value" : "Действие плеера" } } } }, - "Sent %@ to %@" : { - "comment" : "The result text displayed in the dialog. The argument is the action, and the second argument is the item label.", + "Player Item" : { + "comment" : "Display name for a player item type.", "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "%1$@ an %2$@ gesendet" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sent %1$@ to %2$@" + "value" : "Player-Item" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Enviado %1$@ a %2$@" + "value" : "Elemento reproductor" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "%1$@ lähetetty kohteeseen %2$@" + "value" : "Soitinelementti" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "%1$@ envoyé à %2$@" + "value" : "Élément lecteur" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "%1$@ inviato a %2$@" + "value" : "Elemento player" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "%1$@ sendt til %2$@" + "value" : "Spillerelement" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "%1$@ verzonden naar %2$@" + "value" : "Speler-item" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "%1$@ отправлено в %2$@" + "value" : "Элемент плеера" } } } }, - "Sent location %lf, %lf to %@" : { - "comment" : "Dialog shown after setting a location value. First two placeholders are latitude and longitude, third is the item name.", + "PNG" : { + "extractionState" : "manual", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "%lf, %lf wurde an Standort %@ gesendet" + "value" : "PNG" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Sent location %lf, %lf to %@" + "value" : "PNG" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Ubicación %lf, %lf enviada a %@" + "value" : "PNG" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Sijainti %lf, %lf lähetetty kohteeseen %@" + "value" : "PNG" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Emplacement %lf, %lf envoyé à %@" + "value" : "PNG" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Posizione %lf, %lf inviata a %@" + "value" : "PNG" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Plassering %lf, %lf sendt til %@" + "value" : "PNG" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Locatie %lf, %lf verzonden naar %@" + "value" : "PNG" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Местоположение %lf, %lf отправлено в %@" + "value" : "PNG" } } } }, - "Sent the color value of %@ to %@" : { - "comment" : "Dialog shown after setting a color value. First placeholder is the color value, second is the item name.", + "Preferences" : { + "comment" : "Label for the preferences tab in the watch app.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Der Farbwert %@ wurde an %@ gesendet" + "value" : "Einstellungen" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Sent the color value of %@ to %@" + "value" : "Preferences" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Enviado el valor de color de %@ a %@" + "value" : "Preferencias" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Väriarvo %@ lähetetty kohteeseen %@" + "value" : "Asetukset" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Envoyé la valeur de couleur de %@ à %@" + "value" : "Préférences" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Inviato il valore di colore di %@ a %@" + "value" : "Preferenze" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Sendte fargeverdien %@ til %@" + "value" : "Innstillinger" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "De kleurwaarde van %@ is verzonden naar %@" + "value" : "Voorkeuren" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Значение цвета %@ отправлено в %@" + "value" : "Настройки" } } } }, - "Sent the date %@ to %@" : { - "comment" : "Dialog shown after setting a date/time value. First placeholder is the date string, second is the item name.", + "Preview state" : { + "comment" : "The accessibility label for the picker that allows the user to select and preview a different state of an interactive state token.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Datum %@ auf %@ setzen" + "value" : "Vorschauzustand" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Sent the date %@ to %@" + "value" : "Preview state" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Fecha %@ enviada a %@" + "value" : "Estado de vista previa" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Päivämäärä %@ lähetetty kohteeseen %@" + "value" : "Esikatselutila" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Date %@ envoyée à %@" + "value" : "État d'aperçu" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Data %@ inviata a %@" + "value" : "Stato anteprima" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Dato %@ sendt til %@" + "value" : "Forhåndsvisningstilstand" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Datum %@ verzonden naar %@" + "value" : "Voorbeeldstatus" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Дата %@ отправлена в %@" + "value" : "Состояние предварительного просмотра" } } } }, - "Sent the number %lf to %@" : { - "comment" : "Dialog shown after setting a number control value. First placeholder is the decimal value, second is the item name.", + "Previous" : { + "comment" : "Display name for the previous action in the PlayerAction enum.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Die Zahl %lf wurde an %@ gesendet" + "value" : "Vorheriges" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Sent the number %lf to %@" + "value" : "Previous" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Definir el número %lf a %@" + "value" : "Anterior" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Luku %lf lähetetty kohteeseen %@" + "value" : "Edellinen" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Envoyé le numéro %lf à %@" + "value" : "Précédent" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Inviato il numero %lf a %@" + "value" : "Precedente" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Send den numeriske verdien %lf til %@" + "value" : "Forrige" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "De waarde %lf is verzonden naar %@" + "value" : "Vorige" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Число %lf отправлено в %@" + "value" : "Предыдущий" } } } }, - "Sent the string %@ to %@" : { - "comment" : "Dialog shown after setting a string control value. First placeholder is the string value, second is the item name.", + "Previous track" : { + "comment" : "Display name for the previous action.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Der String %@ wurde an %@ gesendet" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sent the string %@ to %@" + "value" : "Vorheriger Titel" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Enviada la cadena %@ a %@" + "value" : "Pista anterior" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Merkkijono %@ lähetetty kohteeseen %@" + "value" : "Edellinen raita" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Envoi de la chaîne %@ à %@" + "value" : "Piste précédente" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Inviata la stringa %@ a %@" + "value" : "Traccia precedente" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Send strengverdien %@ til %@" + "value" : "Forrige spor" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "De waarde %@ is verzonden naar %@" + "value" : "Vorig nummer" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Строка %@ отправлена в %@" + "value" : "Предыдущий трек" } } } }, - "Sent the value of %lld to %@" : { - "comment" : "Dialog shown after setting a dimmer or roller shutter value. First placeholder is the integer value, second is the item name.", + "privacy_policy" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Der Wert %lld wurde an %@ gesendet" + "value" : "Datenschutzerklärung" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Sent the value of %lld to %@" + "value" : "Privacy Policy" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Enviado el valor de %lld a %@" + "value" : "Política de privacidad" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Arvo %lld lähetetty kohteeseen %@" + "value" : "Privacy Policy" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Envoyé la valeur %lld à %@" + "value" : "Politique de Confidentialité" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Inviato il valore %lld a %@" + "value" : "Politica sulla privacy" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Sendte verdien %lld til %@" + "value" : "Personvernregler" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "De waarde %lld is verzonden naar %@" + "value" : "Privacybeleid" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Значение %lld отправлено в %@" + "value" : "Privacy Policy" } } } }, - "Set ${itemEntity} to ${latitude}, ${longitude}" : { + "Queued commands: %lld" : { + "comment" : "A label indicating that there are one or more commands in the queue. The placeholder inside the label is replaced with the actual number of queued commands.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Standort ${itemEntity} auf ${latitude}, ${longitude} setzen" + "value" : "Wartende Befehle: %lld" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Queued commands: %lld" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Establecer ${itemEntity} en ${latitude}, ${longitude}" + "value" : "Comandos en cola: %lld" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Aseta ${itemEntity} arvoon ${latitude}, ${longitude}" + "value" : "Jonossa olevat komennot: %lld" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Définir ${itemEntity} à ${latitude}, ${longitude}" + "value" : "Commandes en attente : %lld" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Imposta ${itemEntity} su ${latitude}, ${longitude}" + "value" : "Comandi in coda: %lld" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Sett ${itemEntity} til ${latitude}, ${longitude}" + "value" : "Kommandoer i kø: %lld" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opdrachten in wachtrij: %lld" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Команды в очереди: %lld" + } + } + } + }, + "Raises by %@" : { + "comment" : "A hint that appears when a user hovers over the \"Increase\" button in the setpoint row. The argument is the step size for the current setpoint.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Um %@ erhöhen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Raises by %@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sube %@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nostaa %@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Monte de %@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aumenta di %@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Øker med %@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verhoogt met %@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Повышает на %@" + } + } + } + }, + "real_time_sliders" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Echtzeitschieberegler" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Real-time Sliders" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Deslizadores en tiempo real" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Real-time Sliders" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Curseurs en temps réel" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cursori In Tempo Reale" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sanntids Skyveknapper" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Realtime schuifregelaars" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Real-time Sliders" + } + } + } + }, + "Real-time Sliders" : { + "comment" : "A toggle that enables or disables the real-time slider feature.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Echtzeitschieberegler" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Real-time Sliders" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Controles deslizantes en tiempo real" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reaaliaikaiset liukusäätimet" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Curseurs en temps réel" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cursori in tempo reale" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sanntidsskyvere" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Realtime schuifregelaars" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Слайдеры реального времени" + } + } + } + }, + "Relative Date: %@" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Relatives Datum: %@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fecha relativa: %@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suhteellinen päivämäärä: %@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Date relative : %@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Data relativa: %@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Relativ dato: %@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Relatieve datum: %@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Относительная дата: %@" + } + } + } + }, + "Remote server" : { + "comment" : "Header text for the remote server section in the connection settings view.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fernzugriff-Server" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Remote server" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Servidor remoto" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Etäpalvelin" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Serveur distant" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Server remoto" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fjernserver" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Externe server" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Удалённый сервер" + } + } + } + }, + "remote_url" : { + "comment" : "Space constraint string which has only about half the screen width on Apple Watch. Best to try and keep it no longer than the English string.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fern-URL" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Remote URL" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL remota" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Remote URL" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL distante" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL remoto" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ekstern URL" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Externe URL" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Удаленный URL-адрес" + } + } + } + }, + "remote_url_not_configured" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL für Fernzugriff ist nicht konfiguriert." + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Remote URL is not configured." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "La URL remota no está configurada." + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Remote URL is not configured." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "L'URL distante n'est pas configurée." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "L'URL remoto non è configurato." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ekstern URL er ikke konfigurert." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Externe URL is niet geconfigureerd." + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Remote URL is not configured." + } + } + } + }, + "Rename" : { + "comment" : "A button that renames a home.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Umbenennen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rename" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Renombrar" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nimeä uudelleen" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Renommer" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rinomina" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gi nytt navn" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wijzig naam" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Переименовать" + } + } + } + }, + "Replay" : { + "comment" : "A synonym for the rewind action.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wiederholen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reproducir de nuevo" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toista uudelleen" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rejouer" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Riproduci" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Spill av igjen" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opnieuw afspelen" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Повторить" + } + } + } + }, + "Reset" : { + "comment" : "Synonyms for the \"Reset\" state of a contact.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zurückgesetzt" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Restablecer" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nollattu" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Réinitialisé" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ripristinato" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tilbakestilt" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Teruggezet" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Сброшен" + } + } + } + }, + "Restore Brightness: %@" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Helligkeit wiederherstellen: %@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Restaurar brillo: %@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Palauta kirkkaus: %@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Restaurer la luminosité : %@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ripristina luminosità: %@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gjenopprett lysstyrke: %@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Helderheid herstellen: %@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Восстановить яркость: %@" + } + } + } + }, + "Restore Previous Brightness on Wake" : { + "comment" : "A toggle that allows the user to restore the brightness level to its previous value when the device wakes up.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vorherige Helligkeit beim Aufwecken wiederherstellen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Restore Previous Brightness on Wake" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Restaurar brillo anterior al despertar" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Palauta aiempi kirkkaus herätessä" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Restaurer la luminosité précédente au réveil" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ripristina luminosità precedente alla riattivazione" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gjenopprett tidligere lysstyrke ved vekking" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vorige helderheid herstellen bij activering" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Восстановить предыдущую яркость при пробуждении" + } + } + } + }, + "Resume" : { + "comment" : "Action to resume playback.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fortsetzen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reanudar" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jatka" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reprendre" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Riprendi" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fortsett" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hervatten" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Возобновить" + } + } + } + }, + "Retrieve the current state of an item" : { + "comment" : "Description of the Get Item State app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktuellen Status eines Items abrufen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Retrieve the current state of an item" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Recuperar el estado actual de un elemento" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hae elementin nykyinen tila" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Récupérer l'état actuel d'un item" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Recupera lo stato attuale di un Item" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hent nåværende tilstand for et Item" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Haal de huidige status van een item op" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Получить текущее состояние элемента" + } + } + } + }, + "retry" : { + "comment" : "retry connection", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erneut versuchen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Retry" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reintentar" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Retry" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Réessayer" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Riprova" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Prøv på nytt" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opnieuw" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Retry" + } + } + } + }, + "Rewind" : { + "comment" : "Display name for the rewind action in the PlayerAction enum.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zurückspulen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rewind" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rebobinar" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kelaa taaksepäin" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Retour arrière" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Riavvolgi" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Spol tilbake" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terugspoelen" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Перемотка назад" + } + } + } + }, + "Rewind back" : { + "comment" : "A synonym for the rewind action.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zurückspulen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rebobinar" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kelaa taaksepäin" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rembobiner" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Riavvolgi" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Spol tilbake" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terugspoelen" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Перемотать" + } + } + } + }, + "running_demo_mode" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Läuft im Demomodus. Überprüfe die Einstellungen, um den Demomodus zu deaktivieren." + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Running in demo mode. Check settings to disable demo mode." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Funccionando en modo demostración. Comprueba la configuración para desactivarlo." + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Running in demo mode. Check settings to disable demo mode." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Exécution en mode démo. Vérifiez les paramètres pour désactiver le mode démo." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Esecuzione in modalità demo. Controlla le impostazioni per disabilitare la modalità demo." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kjører i demomodus. Sjekk innstillingene for å deaktivere demomodus." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uitvoeren in demo-modus. Controleer instellingen om demomodus uit te schakelen." + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Running in demo mode. Check settings to disable demo mode." + } + } + } + }, + "Save" : { + "comment" : "The text for a button that saves current settings.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sichern" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Save" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Guardar" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tallenna" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enregistrer" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Salva" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lagre" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bewaar" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Сохранить" + } + } + } + }, + "Screen Saver" : { + "comment" : "The title of the screen.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bildschirmschoner" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Screen Saver" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Salvapantallas" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Näytönsäästäjä" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Économiseur d’écran" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Salvaschermo" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skjermsparer" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schermbeveiliging" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Заставка экрана" + } + } + } + }, + "Screen Saver Settings" : { + "comment" : "A link to the settings related to the screen saver.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bildschirmschoner-Einstellungen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Screen Saver Settings" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajustes del salvapantallas" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Näytönsäästäjän asetukset" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Paramètres de l'économiseur d'écran" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impostazioni salvaschermo" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skjermsparer-innstillinger" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schermbeveiligingsinstellingen" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Настройки заставки" + } + } + } + }, + "Search" : { + "comment" : "A text field for searching through a list of items.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suchen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Search" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Buscar" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Haku" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rechercher" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cerca" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Søk" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zoek" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Поиск" + } + } + } + }, + "Search for an item" : { + "comment" : "Dialog text prompting the user to search for an openHAB item in app intents.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nach Item suchen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Search for an item" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Buscar un elemento" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Etsi elementtiä" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rechercher un élément" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cerca un elemento" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Søk etter et element" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zoeken naar een item" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Найти элемент" + } + } + } + }, + "search_items" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suche openHAB Items" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Search openHAB items" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Buscar ítems en openHAB" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Search openHAB items" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rechercher des items openHAB" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cerca Item openHAB" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Søk i openHAB Items" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zoek openHAB items" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Search openHAB items" + } + } + } + }, + "Select a home for '%@'" : { + "comment" : "A message to be displayed in a dialog box when the user needs to select a home. The argument is the name of the item that needs a home.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zuhause für '%@' auswählen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seleccionar una casa para '%@'" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valitse koti kohteelle '%@'" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sélectionner un domicile pour '%@'" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seleziona una casa per '%@'" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Velg et hjem for '%@'" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Selecteer een thuis voor '%@'" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Выберите дом для '%@'" + } + } + } + }, + "Select color" : { + "comment" : "A button that, when pressed, opens a view allowing the user to select a color.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Farbe wählen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Select color" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seleccionar color" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valitse väri" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sélectionner une couleur" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seleziona colore" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Velg farge" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kleur selecteren" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Выбрать цвет" + } + } + } + }, + "select_sitemap" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap auswählen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Select Sitemap" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seleccionar mapa web" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Select Sitemap" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sélectionner le Sitemap" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seleziona Sitemap" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Velg Sitemap" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Selecteer Sitemap" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Выбрать Sitemap" + } + } + } + }, + "Send ${action} to ${itemEntity}" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "${action} an ${itemEntity} senden" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviar ${action} a ${itemEntity}" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lähetä ${action} kohteeseen ${itemEntity}" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Envoyer ${action} à ${itemEntity}" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Invia ${action} a ${itemEntity}" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Send ${action} til ${itemEntity}" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "${action} verzenden naar ${itemEntity}" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Отправить ${action} в ${itemEntity}" + } + } + } + }, + "Send a player command such as play, pause, next, or previous" : { + "comment" : "Description of the Set Player Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Einen Player-Befehl senden, z. B. Abspielen, Pause, Weiter oder Zurück" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Send a player command such as play, pause, next, or previous" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviar un comando al reproductor como reproducir, pausar, siguiente o anterior" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lähetä soittimelle komento, kuten toista, tauko, seuraava tai edellinen" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Envoyer une commande au lecteur telle que lire, pause, suivant ou précédent" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Invia un comando al player come riproduci, pausa, successivo o precedente" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Send en spillerkommando som spill av, pause, neste eller forrige" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Een spelersopdracht verzenden zoals afspelen, pauzeren, volgende of vorige" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Отправить команду плееру: воспроизвести, пауза, следующий или предыдущий" + } + } + } + }, + "Sending commands: %lld" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Befehle werden gesendet: %lld" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sending commands: %lld" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviando comandos: %lld" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lähetetään komentoja: %lld" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Envoi de commandes : %lld" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Invio comandi: %lld" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sender kommandoer: %lld" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opdrachten verzenden: %lld" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Отправка команд: %lld" + } + } + } + }, + "Sensor Item" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensorelement" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento sensor" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anturikohde" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément capteur" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento sensore" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensorelement" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensoritem" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент датчика" + } + } + } + }, + "Sensor Item 1" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensorelement 1" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento sensor 1" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anturikohde 1" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément capteur 1" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento sensore 1" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensorelement 1" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensoritem 1" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент датчика 1" + } + } + } + }, + "Sensor Item 2" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensorelement 2" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento sensor 2" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anturikohde 2" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément capteur 2" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento sensore 2" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensorelement 2" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensoritem 2" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент датчика 2" + } + } + } + }, + "Sensor Item 3" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensorelement 3" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento sensor 3" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anturikohde 3" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément capteur 3" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento sensore 3" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensorelement 3" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensoritem 3" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент датчика 3" + } + } + } + }, + "Sensor Item 4" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensorelement 4" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento sensor 4" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anturikohde 4" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément capteur 4" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento sensore 4" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensorelement 4" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensoritem 4" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент датчика 4" + } + } + } + }, + "Sensor Widget Configuration" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensor-Widget-Konfiguration" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración del widget de sensor" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anturiwidgetin määritys" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuration du widget capteur" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configurazione widget sensore" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensor-widgetkonfigurasjon" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensorwidgetconfiguratie" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Настройка виджета датчика" + } + } + } + }, + "Sent %@ to %@" : { + "comment" : "The result text displayed in the dialog. The argument is the action, and the second argument is the item label.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ an %2$@ gesendet" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sent %1$@ to %2$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviado %1$@ a %2$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ lähetetty kohteeseen %2$@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ envoyé à %2$@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ inviato a %2$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ sendt til %2$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ verzonden naar %2$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ отправлено в %2$@" + } + } + } + }, + "Sent location %lf, %lf to %@" : { + "comment" : "Dialog shown after setting a location value. First two placeholders are latitude and longitude, third is the item name.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lf, %lf wurde an Standort %@ gesendet" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sent location %lf, %lf to %@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ubicación %lf, %lf enviada a %@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sijainti %lf, %lf lähetetty kohteeseen %@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Emplacement %lf, %lf envoyé à %@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Posizione %lf, %lf inviata a %@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Plassering %lf, %lf sendt til %@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Locatie %lf, %lf verzonden naar %@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Местоположение %lf, %lf отправлено в %@" + } + } + } + }, + "Sent the color value of %@ to %@" : { + "comment" : "Dialog shown after setting a color value. First placeholder is the color value, second is the item name.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Der Farbwert %@ wurde an %@ gesendet" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sent the color value of %@ to %@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviado el valor de color de %@ a %@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Väriarvo %@ lähetetty kohteeseen %@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Envoyé la valeur de couleur de %@ à %@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inviato il valore di colore di %@ a %@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sendte fargeverdien %@ til %@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De kleurwaarde van %@ is verzonden naar %@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Значение цвета %@ отправлено в %@" + } + } + } + }, + "Sent the date %@ to %@" : { + "comment" : "Dialog shown after setting a date/time value. First placeholder is the date string, second is the item name.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datum %@ auf %@ setzen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sent the date %@ to %@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fecha %@ enviada a %@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Päivämäärä %@ lähetetty kohteeseen %@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Date %@ envoyée à %@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Data %@ inviata a %@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dato %@ sendt til %@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datum %@ verzonden naar %@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Дата %@ отправлена в %@" + } + } + } + }, + "Sent the number %lf to %@" : { + "comment" : "Dialog shown after setting a number control value. First placeholder is the decimal value, second is the item name.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Die Zahl %lf wurde an %@ gesendet" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sent the number %lf to %@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Definir el número %lf a %@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Luku %lf lähetetty kohteeseen %@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Envoyé le numéro %lf à %@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inviato il numero %lf a %@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Send den numeriske verdien %lf til %@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De waarde %lf is verzonden naar %@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Число %lf отправлено в %@" + } + } + } + }, + "Sent the string %@ to %@" : { + "comment" : "Dialog shown after setting a string control value. First placeholder is the string value, second is the item name.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Der String %@ wurde an %@ gesendet" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sent the string %@ to %@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviada la cadena %@ a %@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Merkkijono %@ lähetetty kohteeseen %@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Envoi de la chaîne %@ à %@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inviata la stringa %@ a %@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Send strengverdien %@ til %@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De waarde %@ is verzonden naar %@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Строка %@ отправлена в %@" + } + } + } + }, + "Sent the value of %lld to %@" : { + "comment" : "Dialog shown after setting a dimmer or roller shutter value. First placeholder is the integer value, second is the item name.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Der Wert %lld wurde an %@ gesendet" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sent the value of %lld to %@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviado el valor de %lld a %@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Arvo %lld lähetetty kohteeseen %@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Envoyé la valeur %lld à %@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inviato il valore %lld a %@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sendte verdien %lld til %@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De waarde %lld is verzonden naar %@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Значение %lld отправлено в %@" + } + } + } + }, + "Set ${itemEntity} to ${latitude}, ${longitude}" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Standort ${itemEntity} auf ${latitude}, ${longitude} setzen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer ${itemEntity} en ${latitude}, ${longitude}" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta ${itemEntity} arvoon ${latitude}, ${longitude}" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir ${itemEntity} à ${latitude}, ${longitude}" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta ${itemEntity} su ${latitude}, ${longitude}" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sett ${itemEntity} til ${latitude}, ${longitude}" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "${itemEntity} instellen op ${latitude}, ${longitude}" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить ${itemEntity} в ${latitude}, ${longitude}" + } + } + } + }, + "Set ${itemEntity} to ${value}" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "${itemEntity} auf ${value} setzen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer ${itemEntity} en ${value}" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta ${itemEntity} arvoon ${value}" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir ${itemEntity} sur ${value}" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta ${itemEntity} su ${value}" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sett ${itemEntity} til ${value}" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "${itemEntity} instellen op ${value}" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить ${itemEntity} в ${value}" + } + } + } + }, + "Set ${itemEntity} to ${value} (HSB)" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "${itemEntity} auf ${value} (HSB) setzen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer ${itemEntity} en ${value} (HSB)" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta ${itemEntity} arvoon ${value} (HSB)" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir ${itemEntity} sur ${value} (TSL)" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta ${itemEntity} su ${value} (HSB)" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sett ${itemEntity} til ${value} (HSB)" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "${itemEntity} instellen op ${value} (HSB)" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить ${itemEntity} в ${value} (HSB)" + } + } + } + }, + "Set Active Home" : { + "comment" : "Title of the intent to set the active home.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktives Zuhause setzen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer casa activa" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta aktiivinen koti" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir le domicile actif" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta casa attiva" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi aktivt hjem" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actief thuis instellen" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить активный дом" + } + } + } + }, + "Set active home to ${home}" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktives Zuhause auf ${home} setzen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer casa activa como ${home}" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta aktiiviseksi kodiksi ${home}" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir ${home} comme domicile actif" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta ${home} come casa attiva" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi ${home} som aktivt hjem" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "${home} instellen als actief thuis" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить ${home} как активный дом" + } + } + } + }, + "Set Color" : { + "comment" : "Short title for the shortcut to set a color value.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Farbe einstellen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer color" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta väri" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la couleur" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta il colore" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi farge" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kleur instellen" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить цвет" + } + } + } + }, + "Set Color Control Value" : { + "comment" : "Title of the Set Color Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Farbwert festlegen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set Color Control Value" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Define el valor de control de color" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta värisäätimen arvo" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la valeur de contrôle de couleur" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta Valore Controllo Colore" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi Fargekontrollverdi" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kleurwaarde instellen" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить значение элемента управления цветом" + } + } + } + }, + "Set Contact State" : { + "comment" : "Short title for the shortcut to set the contact state.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontaktstatus setzen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer estado de contacto" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta kontaktin tila" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir l'état du contact" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta lo stato del contatto" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi kontaktstatus" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contactstatus instellen" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить состояние контакта" + } + } + } + }, + "Set Contact State Value" : { + "comment" : "Title of the Set Contact State Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wert eines Kontakt=Status festlegen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set Contact State Value" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer el valor del estado del pulsador" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta kontaktin tilanarvo" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la valeur de l'état du contacteur" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta lo Stato del Contatto" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sett Tilstand for Bryter" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stel contact status waarde in" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить значение состояния контакта" + } + } + } + }, + "Set Date & Time" : { + "comment" : "Short title for the shortcut to set a date and time.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datum & Uhrzeit setzen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer fecha y hora" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta päivämäärä ja aika" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la date et l'heure" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta data e ora" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi dato og klokkeslett" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datum en tijd instellen" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить дату и время" + } + } + } + }, + "Set DateTime Control Value" : { + "comment" : "Title of the Set DateTime Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wert eines DateTime-Kontrollelements setzen " + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set DateTime Control Value" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer valor del control DateTime" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta DateTime-säätimen arvo" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la valeur du contrôle DateHeure" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta valore controllo DateTime" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi DateTime-kontrollverdi" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "DateTime-regelaarswaarde instellen" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить значение элемента управления DateTime" + } + } + } + }, + "Set Dimmer / Roller" : { + "comment" : "Short title for a shortcut that allows the user to set the value of a dimmer or roller shutter.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dimmer / Rollo einstellen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configurar regulador / persiana" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta himmentimen / rullakaihdin arvo" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Régler le variateur / volet" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta dimmer / tapparella" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi dimmer / rullegardin" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dimmer / rolluik instellen" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить диммер / жалюзи" + } + } + } + }, + "Set Dimmer or Roller Shutter Value" : { + "comment" : "Title of the Set Dimmer or Roller Shutter Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wert eines Dimmers oder Rollladens festlegen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set Dimmer or Roller Shutter Value" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Definir el valor del regulador o persiana" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta himmentimen tai rullaverkon arvo" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Régler la valeur du Dimmer ou du Volet Roulant" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta il valore di Dimmer o Tapparella" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi Verdi for Dimmer eller Rullegardin" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stel Dimmer of Rolluik waarde in" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить значение диммера или рольставни" + } + } + } + }, + "Set Location Control Value" : { + "comment" : "Title of the Set Location Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Standort-Kontrollwert setzen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set Location Control Value" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer valor del control de ubicación" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta sijaintisäätimen arvo" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la valeur du contrôle de localisation" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta valore controllo posizione" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi plasseringskontrollverdi" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Locatieregelaarswaarde instellen" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить значение элемента управления местоположением" + } + } + } + }, + "Set Number" : { + "comment" : "Text displayed in a shortcut for setting a number value.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zahl setzen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer número" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta luku" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir un nombre" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta numero" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi tall" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Getal instellen" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить число" + } + } + } + }, + "Set Number Control Value" : { + "comment" : "Title of the Set Number Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wert eines numerischen Kontrollelements setzen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set Number Control Value" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer valor de control de número" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta numerosäätimen arvo" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la valeur de contrôle du numéro" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta Valore del Numero di Controllo" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sett Verdi for Numerisk Kontroll" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zet Nummer Control Waarde" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить числовое значение элемента управления" + } + } + } + }, + "Set Player Control Value" : { + "comment" : "Title of the Set Player Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Player-Kontrollelement setzen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set Player Control Value" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer valor del control del reproductor" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta soittimen säätimen arvo" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la valeur du contrôle du lecteur" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta valore controllo player" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi spillerkontrollverdi" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Spelerregelaarswaarde instellen" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить значение элемента управления плеером" + } + } + } + }, + "Set String Control Value" : { + "comment" : "Title of the Set String Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "String-Kontrollwert setzen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set String Control Value" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer el valor de control de cadena" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta merkkijonosäätimen arvo" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la valeur de contrôle de chaîne" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta Valore della Stringa di Controllo" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sett Strengkontroll-Verdi" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zet String Control Waarde in" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить строковое значение элемента управления" + } + } + } + }, + "Set Switch" : { + "comment" : "Short title for a shortcut that allows the user to set a switch.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schalter setzen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configurar interruptor" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta kytkin" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir un interrupteur" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta interruttore" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi bryter" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schakelaar instellen" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить переключатель" + } + } + } + }, + "Set Switch State" : { + "comment" : "Title of the Set Switch State app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schalterzustand setzen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set Switch State" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Definir el estado del interruptor" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta kytkimen tila" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir l'état du Switch" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta stato Interruttore" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi Brytertilstand" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stel schakelstatus in" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить состояние переключателя" + } + } + } + }, + "Set Text" : { + "comment" : "Text displayed in a shortcut for setting a string value.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Text setzen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer texto" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta teksti" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir le texte" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta testo" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi tekst" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tekst instellen" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить текст" + } + } + } + }, + "Set the color of a color control item" : { + "comment" : "Description of the Set Color Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Farbe eines Kontrollelements festlegen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set the color of a color control item" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Definir el color de un ítem de control de color" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta värielementin väri" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la couleur d'un élément de contrôle de couleur" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta il colore di un Item di controllo colore" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi fargen til et fargekontroll-Item" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stel de kleur van een kleurbesturingsitem in" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить цвет элемента управления цветом" + } + } + } + }, + "Set the date and time of a DateTime control item" : { + "comment" : "Description of the Set DateTime Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datum und Uhrzeit eines DateTime-Kontrollelements setzen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set the date and time of a DateTime control item" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer la fecha y hora de un elemento de control DateTime" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta DateTime-elementin päivämäärä ja aika" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la date et l'heure d'un élément de contrôle DateHeure" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta la data e l'ora di un elemento di controllo DateTime" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi dato og tid for et DateTime-kontrollelement" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De datum en tijd instellen van een DateTime-regelaarselement" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить дату и время элемента DateTime" + } + } + } + }, + "Set the decimal value of a number control item" : { + "comment" : "Description of the Set Number Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dezimalwert eines numerischen Kontrollelements setzen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set the decimal value of a number control item" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer el valor decimal de un ítem de control numérico" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta numeroelementin desimaaliarvo" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la valeur décimale d'un item de contrôle de nombre" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta il valore decimale di un Item di controllo numerico" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sett desimalverdien til en numerisk kontroll-Item" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stel de decimale waarde van een nummer control item in" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить десятичное значение числового элемента" + } + } + } + }, + "Set the integer value of a dimmer or roller shutter" : { + "comment" : "Description of the Set Dimmer or Roller Shutter Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Festlegen des Wertes eines Dimmers oder Rollladens" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set the integer value of a dimmer or roller shutter" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Define el valor entero de un regulador o persiana" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta himmentimen tai rullaverkon kokonaislukuarvo" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la valeur entière d'un dimmer ou d'un volet roulant" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta il valore intero di un dimmer o una tapparella" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sett heltallsverdien til en dimmer eller rullegardin" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zet de integerwaarde van een dimmer of rolluik" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить целочисленное значение диммера или рольставни" + } + } + } + }, + "Set the latitude and longitude of a location control item" : { + "comment" : "Description of the Set Location Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Breitengrad und Längengrad eines Standort-Kontrollelements setzen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set the latitude and longitude of a location control item" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer la latitud y longitud de un elemento de control de ubicación" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta sijaintielementin leveys- ja pituusaste" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la latitude et la longitude d'un élément de contrôle de localisation" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta la latitudine e la longitudine di un elemento di controllo posizione" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi bredde- og lengdegrad for et plasseringskontrollelement" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De breedte- en lengtegraad instellen van een locatieregelaarselement" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить широту и долготу элемента местоположения" + } + } + } + }, + "Set the state of ${itemEntity} to ${state}" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Den Status von ${itemEntity} auf ${state} setzen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer el estado de ${itemEntity} en ${state}" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta ${itemEntity}-tila arvoon ${state}" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir l'état de ${itemEntity} sur ${state}" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta lo stato di ${itemEntity} su ${state}" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi status for ${itemEntity} til ${state}" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De status van ${itemEntity} instellen op ${state}" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить состояние ${itemEntity} в ${state}" + } + } + } + }, + "Set the state of a contact open or closed" : { + "comment" : "Description of the Set Contact State Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Status eines Kontakts auf offen oder geschlossen setzen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set the state of a contact open or closed" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer el estado de un interruptor encendido o apagado" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta kontaktin tilaksi auki tai kiinni" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir l'état d'un contacteur ouvert ou fermé" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta lo stato di un contatto aperto o chiuso" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sett tilstanden til en bryter til åpen eller lukket" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "${itemEntity} instellen op ${latitude}, ${longitude}" + "value" : "Zet de status van een contact open of gesloten" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Установить ${itemEntity} в ${latitude}, ${longitude}" + "value" : "Установить состояние контакта: открыт или закрыт" } } } }, - "Set ${itemEntity} to ${value}" : { + "Set the state of a switch on or off, or toggle its state" : { + "comment" : "Description of the Set Switch State app intent.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "${itemEntity} auf ${value} setzen" + "value" : "Switch-Status auf An, Aus oder Umschalten setzen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set the state of a switch on or off, or toggle its state" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Establecer ${itemEntity} en ${value}" + "value" : "Establecer el estado de un interruptor en encendido o apagado, o alternar su estado" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Aseta ${itemEntity} arvoon ${value}" + "value" : "Aseta kytkimen tilaksi päällä tai pois tai vaihda sen tilaa" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Définir ${itemEntity} sur ${value}" + "value" : "Définir l'état d'un interrupteur sur activé ou désactivé, ou basculer son état" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Imposta ${itemEntity} su ${value}" + "value" : "Imposta lo stato di un interruttore su acceso o spento, o attiva/disattiva il suo stato" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Sett ${itemEntity} til ${value}" + "value" : "Angi brytertilstanden til på eller av, eller bytt tilstand" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "${itemEntity} instellen op ${value}" + "value" : "De status van een schakelaar op aan of uit zetten, of de status omschakelen" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Установить ${itemEntity} в ${value}" + "value" : "Установить состояние переключателя вкл. или выкл., или переключить его состояние" } } } }, - "Set ${itemEntity} to ${value} (HSB)" : { + "Set the string of a string control item" : { + "comment" : "Description of the Set String Control Value app intent.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "${itemEntity} auf ${value} (HSB) setzen" + "value" : "Zeichenkette eines String-Kontrollelements setzen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set the string of a string control item" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Establecer ${itemEntity} en ${value} (HSB)" + "value" : "Establecer la cadena de un ítem controlador de cadena" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Aseta ${itemEntity} arvoon ${value} (HSB)" + "value" : "Aseta merkkijonoelementin merkkijono" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Définir ${itemEntity} sur ${value} (TSL)" + "value" : "Définir la chaîne d'un item de contrôle de chaîne" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Imposta ${itemEntity} su ${value} (HSB)" + "value" : "Imposta il valore di una item di controllo di tipo stringa" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Sett ${itemEntity} til ${value} (HSB)" + "value" : "Angi teksten til et teksterkontroll-Item" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "${itemEntity} instellen op ${value} (HSB)" + "value" : "De tekenreeks van een string controle item instellen" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Установить ${itemEntity} в ${value} (HSB)" + "value" : "Установить строку строкового элемента" } } } }, - "Set Active Home" : { - "comment" : "Title of the intent to set the active home.", - "isCommentAutoGenerated" : true, + "Set value" : { + "comment" : "A button that sets the value of a text input.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Aktives Zuhause setzen" + "value" : "Wert setzen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set value" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Establecer casa activa" + "value" : "Establecer valor" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Aseta aktiivinen koti" + "value" : "Aseta arvo" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Définir le domicile actif" + "value" : "Définir la valeur" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Imposta casa attiva" + "value" : "Imposta valore" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Angi aktivt hjem" + "value" : "Angi verdi" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Actief thuis instellen" + "value" : "Waarde instellen" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Установить активный дом" + "value" : "Установить значение" } } } }, - "Set active home to ${home}" : { + "settings" : { + "extractionState" : "manual", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Aktives Zuhause auf ${home} setzen" + "value" : "Einstellungen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Settings" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Establecer casa activa como ${home}" + "value" : "Configuración" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Aseta aktiiviseksi kodiksi ${home}" + "value" : "Settings" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Définir ${home} comme domicile actif" + "value" : "Paramétrage" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Imposta ${home} come casa attiva" + "value" : "Impostazioni" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Angi ${home} som aktivt hjem" + "value" : "Innstillinger" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "${home} instellen als actief thuis" + "value" : "Instellingen" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Установить ${home} как активный дом" + "value" : "Settings" } } } }, - "Set Color" : { - "comment" : "Short title for the shortcut to set a color value.", - "isCommentAutoGenerated" : true - }, - "Set Color Control Value" : { - "comment" : "Title of the Set Color Control Value app intent.", + "settings_not_received" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Farbwert festlegen" + "value" : "Einstellungen noch nicht vom iPhone empfangen." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Set Color Control Value" + "value" : "Settings not yet received from iPhone." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Define el valor de control de color" + "value" : "Configuración aún no recibida del iPhone." } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Aseta värisäätimen arvo" + "value" : "Asetuksia ei ole vielä vastaanotettu iPhonelta." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Définir la valeur de contrôle de couleur" + "value" : "Paramètres pas encore reçus de l'iPhone." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Imposta Valore Controllo Colore" + "value" : "Impostazioni non ancora ricevute da iPhone." } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Angi Fargekontrollverdi" + "value" : "Innstillinger som ennå ikke er mottatt fra iPhone." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Kleurwaarde instellen" + "value" : "Instellingen nog niet ontvangen van iPhone." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Установить значение элемента управления цветом" + "value" : "Настройки ещё не получены с iPhone." } } } }, - "Set Contact State" : { - "comment" : "Short title for the shortcut to set the contact state.", - "isCommentAutoGenerated" : true - }, - "Set Contact State Value" : { - "comment" : "Title of the Set Contact State Value app intent.", + "Show Date" : { + "comment" : "A toggle that enables or disables the display of the date in the screen saver.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Wert eines Kontakt=Status festlegen" + "value" : "Datum anzeigen" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Set Contact State Value" + "value" : "Show Date" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Establecer el valor del estado del pulsador" + "value" : "Mostrar fecha" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Aseta kontaktin tilanarvo" + "value" : "Näytä päivämäärä" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Définir la valeur de l'état du contacteur" + "value" : "Afficher la date" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Imposta lo Stato del Contatto" + "value" : "Mostra data" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Sett Tilstand for Bryter" + "value" : "Vis dato" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Stel contact status waarde in" + "value" : "Datum weergeven" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Установить значение состояния контакта" + "value" : "Показать дату" } } } }, - "Set Date & Time" : { - "comment" : "Short title for the shortcut to set a date and time.", - "isCommentAutoGenerated" : true - }, - "Set DateTime Control Value" : { - "comment" : "Title of the Set DateTime Control Value app intent.", + "Show Search Field" : { + "comment" : "A toggle that enables or disables the search field in the app.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Wert eines DateTime-Kontrollelements setzen " + "value" : "Suchfeld anzeigen" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Set DateTime Control Value" + "value" : "Show Search Field" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Establecer valor del control DateTime" + "value" : "Mostrar campo de búsqueda" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Aseta DateTime-säätimen arvo" + "value" : "Näytä hakukenttä" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Définir la valeur du contrôle DateHeure" + "value" : "Afficher le champ de recherche" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Imposta valore controllo DateTime" + "value" : "Mostra campo di ricerca" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Angi DateTime-kontrollverdi" + "value" : "Vis søkefelt" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "DateTime-regelaarswaarde instellen" + "value" : "Zoekveld weergeven" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Установить значение элемента управления DateTime" + "value" : "Показать поле поиска" } } } }, - "Set Dimmer / Roller" : { - "comment" : "Short title for a shortcut that allows the user to set the value of a dimmer or roller shutter.", - "isCommentAutoGenerated" : true - }, - "Set Dimmer or Roller Shutter Value" : { - "comment" : "Title of the Set Dimmer or Roller Shutter Value app intent.", + "Show Seconds" : { + "comment" : "A toggle that lets the user enable or disable the display of seconds in the screen saver.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Wert eines Dimmers oder Rollladens festlegen" + "value" : "Sekunden anzeigen" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Set Dimmer or Roller Shutter Value" + "value" : "Show Seconds" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Definir el valor del regulador o persiana" + "value" : "Mostrar segundos" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Aseta himmentimen tai rullaverkon arvo" + "value" : "Näytä sekunnit" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Régler la valeur du Dimmer ou du Volet Roulant" + "value" : "Afficher les secondes" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Imposta il valore di Dimmer o Tapparella" + "value" : "Mostra secondi" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Angi Verdi for Dimmer eller Rullegardin" + "value" : "Vis sekunder" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Stel Dimmer of Rolluik waarde in" + "value" : "Seconden weergeven" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Установить значение диммера или рольставни" + "value" : "Показать секунды" } } } }, - "Set Location Control Value" : { - "comment" : "Title of the Set Location Control Value app intent.", + "Show Time" : { + "comment" : "A toggle that enables or disables the display of the time.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Standort-Kontrollwert setzen" + "value" : "Zeit anzeigen" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Set Location Control Value" + "value" : "Show Time" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Establecer valor del control de ubicación" + "value" : "Mostrar hora" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Aseta sijaintisäätimen arvo" + "value" : "Näytä aika" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Définir la valeur du contrôle de localisation" + "value" : "Afficher l'heure" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Imposta valore controllo posizione" + "value" : "Mostra ora" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Angi plasseringskontrollverdi" + "value" : "Vis tid" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Locatieregelaarswaarde instellen" + "value" : "Tijd weergeven" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Установить значение элемента управления местоположением" + "value" : "Показать время" } } } }, - "Set Number" : { - "comment" : "Text displayed in a shortcut for setting a number value.", - "isCommentAutoGenerated" : true - }, - "Set Number Control Value" : { - "comment" : "Title of the Set Number Control Value app intent.", + "show_search_field" : { + "extractionState" : "manual", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Wert eines numerischen Kontrollelements setzen" + "value" : "Suchfeld anzeigen" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Set Number Control Value" + "value" : "Show Search Field" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Establecer valor de control de número" + "value" : "Show Search Field" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Aseta numerosäätimen arvo" + "value" : "Show Search Field" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Définir la valeur de contrôle du numéro" + "value" : "Show Search Field" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Imposta Valore del Numero di Controllo" + "value" : "Show Search Field" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Sett Verdi for Numerisk Kontroll" + "value" : "Show Search Field" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Zet Nummer Control Waarde" + "value" : "Show Search Field" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Установить числовое значение элемента управления" + "value" : "Show Search Field" } } } }, - "Set Player Control Value" : { - "comment" : "Title of the Set Player Control Value app intent.", + "Shut" : { + "comment" : "Displayed title for the \"Shut\" state of a contact.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Player-Kontrollelement setzen" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Set Player Control Value" + "value" : "Geschlossen" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Establecer valor del control del reproductor" + "value" : "Cerrado" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Aseta soittimen säätimen arvo" + "value" : "Suljettu" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Définir la valeur du contrôle du lecteur" + "value" : "Fermé" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Imposta valore controllo player" + "value" : "Chiuso" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Angi spillerkontrollverdi" + "value" : "Lukket" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Spelerregelaarswaarde instellen" + "value" : "Gesloten" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Установить значение элемента управления плеером" + "value" : "Закрыт" } } } }, - "Set String Control Value" : { - "comment" : "Title of the Set String Control Value app intent.", + "sitemap" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "String-Kontrollwert setzen" + "value" : "Sitemap" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Set String Control Value" + "value" : "Sitemap" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Establecer el valor de control de cadena" + "value" : "Mapa web" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Aseta merkkijonosäätimen arvo" + "value" : "Sitemap" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Définir la valeur de contrôle de chaîne" + "value" : "Sitemap" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Imposta Valore della Stringa di Controllo" + "value" : "Sitemap" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Sett Strengkontroll-Verdi" + "value" : "Sitemap" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Zet String Control Waarde in" + "value" : "Sitemap" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Установить строковое значение элемента управления" + "value" : "Sitemap" } } } }, - "Set Switch" : { - "comment" : "Short title for a shortcut that allows the user to set a switch.", - "isCommentAutoGenerated" : true - }, - "Set Switch State" : { - "comment" : "Title of the Set Switch State app intent.", + "Sitemap" : { + "comment" : "Label for the tab item representing the sitemap view.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Schalterzustand setzen" + "value" : "Sitemap" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Set Switch State" + "value" : "Sitemap" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Definir el estado del interruptor" + "value" : "Mapa del sitio" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Aseta kytkimen tila" + "value" : "Sivukartta" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Définir l'état du Switch" + "value" : "Plan du site" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Imposta stato Interruttore" + "value" : "Sitemap" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Angi Brytertilstand" + "value" : "Nettstedskart" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Stel schakelstatus in" + "value" : "Sitemap" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Установить состояние переключателя" + "value" : "Карта сайта" } } } }, - "Set Text" : { - "comment" : "Text displayed in a shortcut for setting a string value.", - "isCommentAutoGenerated" : true - }, - "Set the color of a color control item" : { - "comment" : "Description of the Set Color Control Value app intent.", + "Sitemap Diagnostics Logging" : { + "comment" : "A toggle that enables or disables logging of sitemap diagnostics.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Farbe eines Kontrollelements festlegen" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Set the color of a color control item" + "value" : "Sitemap-Diagnoseprotokollierung" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Definir el color de un ítem de control de color" + "value" : "Registro de diagnóstico del mapa del sitio" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Aseta värielementin väri" + "value" : "Sivukartan diagnostiikkaloki" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Définir la couleur d'un élément de contrôle de couleur" + "value" : "Journalisation des diagnostics du plan du site" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Imposta il colore di un Item di controllo colore" + "value" : "Registrazione diagnostica sitemap" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Angi fargen til et fargekontroll-Item" + "value" : "Diagnoslogging for nettstedskart" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Stel de kleur van een kleurbesturingsitem in" + "value" : "Sitemap-diagnoselogboek" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Установить цвет элемента управления цветом" + "value" : "Журналирование диагностики карты сайта" } } } }, - "Set the date and time of a DateTime control item" : { - "comment" : "Description of the Set DateTime Control Value app intent.", + "Sitemap for Apple Watch" : { + "comment" : "A label for selecting a sitemap to be used with an Apple Watch app.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Datum und Uhrzeit eines DateTime-Kontrollelements setzen" + "value" : "Apple Watch Sitemap" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Set the date and time of a DateTime control item" + "value" : "Sitemap for Apple Watch" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Establecer la fecha y hora de un elemento de control DateTime" + "value" : "Mapa del sitio para Apple Watch" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Aseta DateTime-elementin päivämäärä ja aika" + "value" : "Apple Watch -sivukartta" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Définir la date et l'heure d'un élément de contrôle DateHeure" + "value" : "Plan du site pour Apple Watch" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Imposta la data e l'ora di un elemento di controllo DateTime" + "value" : "Sitemap per Apple Watch" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Angi dato og tid for et DateTime-kontrollelement" + "value" : "Nettstedskart for Apple Watch" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "De datum en tijd instellen van een DateTime-regelaarselement" + "value" : "Sitemap voor Apple Watch" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Установить дату и время элемента DateTime" + "value" : "Карта сайта для Apple Watch" } } } }, - "Set the decimal value of a number control item" : { - "comment" : "Description of the Set Number Control Value app intent.", + "sitemap_settings" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Dezimalwert eines numerischen Kontrollelements setzen" + "value" : "Sitemap-Einstellungen" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Set the decimal value of a number control item" + "value" : "Sitemap Settings" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Establecer el valor decimal de un ítem de control numérico" + "value" : "Ajustes del esquema" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Aseta numeroelementin desimaaliarvo" + "value" : "Sitemap Settings" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Définir la valeur décimale d'un item de contrôle de nombre" + "value" : "Paramètres Sitemap" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Imposta il valore decimale di un Item di controllo numerico" + "value" : "Impostazioni Sitemap" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Sett desimalverdien til en numerisk kontroll-Item" + "value" : "Innstillinger for Sitemap" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Stel de decimale waarde van een nummer control item in" + "value" : "Sitemap instellingen" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Установить десятичное значение числового элемента" + "value" : "Sitemap Settings" } } } }, - "Set the integer value of a dimmer or roller shutter" : { - "comment" : "Description of the Set Dimmer or Roller Shutter Value app intent.", + "Sitemaps" : { + "comment" : "A section header that lists the user's available sitemaps.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Festlegen des Wertes eines Dimmers oder Rollladens" + "value" : "Sitemaps" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Set the integer value of a dimmer or roller shutter" + "value" : "Sitemaps" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Define el valor entero de un regulador o persiana" + "value" : "Mapas del sitio" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Aseta himmentimen tai rullaverkon kokonaislukuarvo" + "value" : "Sivukartat" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Définir la valeur entière d'un dimmer ou d'un volet roulant" + "value" : "Plans du site" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Imposta il valore intero di un dimmer o una tapparella" + "value" : "Sitemap" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Sett heltallsverdien til en dimmer eller rullegardin" + "value" : "Nettstedskart" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Zet de integerwaarde van een dimmer of rolluik" + "value" : "Sitemaps" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Установить целочисленное значение диммера или рольставни" + "value" : "Карты сайта" } } } }, - "Set the latitude and longitude of a location control item" : { - "comment" : "Description of the Set Location Control Value app intent.", + "Size: %llu MB" : { + "comment" : "A message showing the size of the image cache. The argument is the size of the image cache in MB.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Breitengrad und Längengrad eines Standort-Kontrollelements setzen" + "value" : "Größe: %llu MB" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Set the latitude and longitude of a location control item" + "value" : "Size: %llu MB" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Establecer la latitud y longitud de un elemento de control de ubicación" + "value" : "Tamaño: %llu MB" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Aseta sijaintielementin leveys- ja pituusaste" + "value" : "Koko: %llu Mt" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Définir la latitude et la longitude d'un élément de contrôle de localisation" + "value" : "Taille : %llu Mo" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Imposta la latitudine e la longitudine di un elemento di controllo posizione" + "value" : "Dimensione: %llu MB" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Angi bredde- og lengdegrad for et plasseringskontrollelement" + "value" : "Størrelse: %llu MB" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "De breedte- en lengtegraad instellen van een locatieregelaarselement" + "value" : "Grootte: %llu MB" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Установить широту и долготу элемента местоположения" + "value" : "Размер: %llu МБ" } } } }, - "Set the state of ${itemEntity} to ${state}" : { + "Skip" : { + "comment" : "A synonym for the action \"Skip\".", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Den Status von ${itemEntity} auf ${state} setzen" + "value" : "Überspringen" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Establecer el estado de ${itemEntity} en ${state}" + "value" : "Saltar" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Aseta ${itemEntity}-tila arvoon ${state}" + "value" : "Ohita" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Définir l'état de ${itemEntity} sur ${state}" + "value" : "Passer" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Imposta lo stato di ${itemEntity} su ${state}" + "value" : "Salta" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Angi status for ${itemEntity} til ${state}" + "value" : "Hopp over" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "De status van ${itemEntity} instellen op ${state}" + "value" : "Overslaan" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Установить состояние ${itemEntity} в ${state}" + "value" : "Пропустить" } } } }, - "Set the state of a contact open or closed" : { - "comment" : "Description of the Set Contact State Value app intent.", + "Skip ahead" : { + "comment" : "Synonyms for the \"Skip ahead\" action.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Status eines Kontakts auf offen oder geschlossen setzen" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Set the state of a contact open or closed" + "value" : "Vorwärts überspringen" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Establecer el estado de un interruptor encendido o apagado" + "value" : "Saltar adelante" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Aseta kontaktin tilaksi auki tai kiinni" + "value" : "Hyppää eteenpäin" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Définir l'état d'un contacteur ouvert ou fermé" + "value" : "Sauter en avant" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Imposta lo stato di un contatto aperto o chiuso" + "value" : "Salta avanti" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Sett tilstanden til en bryter til åpen eller lukket" + "value" : "Hopp fremover" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Zet de status van een contact open of gesloten" + "value" : "Vooruitspringen" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Установить состояние контакта: открыт или закрыт" + "value" : "Пропустить вперёд" } } } }, - "Set the state of a switch on or off, or toggle its state" : { - "comment" : "Description of the Set Switch State app intent.", + "Skip back" : { + "comment" : "Synonyms for the previous action.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Switch-Status auf An, Aus oder Umschalten setzen" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Set the state of a switch on or off, or toggle its state" + "value" : "Zurückspringen" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Establecer el estado de un interruptor en encendido o apagado, o alternar su estado" + "value" : "Retroceder" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Aseta kytkimen tilaksi päällä tai pois tai vaihda sen tilaa" + "value" : "Siirry taaksepäin" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Définir l'état d'un interrupteur sur activé ou désactivé, ou basculer son état" + "value" : "Reculer" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Imposta lo stato di un interruttore su acceso o spento, o attiva/disattiva il suo stato" + "value" : "Torna indietro" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Angi brytertilstanden til på eller av, eller bytt tilstand" + "value" : "Hopp tilbake" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "De status van een schakelaar op aan of uit zetten, of de status omschakelen" + "value" : "Terugspoelen" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Установить состояние переключателя вкл. или выкл., или переключить его состояние" + "value" : "Перемотать назад" } } } }, - "Set the string of a string control item" : { - "comment" : "Description of the Set String Control Value app intent.", + "Skip forward" : { + "comment" : "A synonym for the \"Next\" action.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Zeichenkette eines String-Kontrollelements setzen" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Set the string of a string control item" + "value" : "Vorwärts springen" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Establecer la cadena de un ítem controlador de cadena" + "value" : "Avanzar" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Aseta merkkijonoelementin merkkijono" + "value" : "Siirry eteenpäin" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Définir la chaîne d'un item de contrôle de chaîne" + "value" : "Avancer" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Imposta il valore di una item di controllo di tipo stringa" + "value" : "Avanti veloce" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Angi teksten til et teksterkontroll-Item" + "value" : "Hopp fremover" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "De tekenreeks van een string controle item instellen" + "value" : "Vooruitspoelen" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Установить строку строкового элемента" + "value" : "Перемотать вперёд" } } } }, - "Set value" : { - "comment" : "A button that sets the value of a text input.", + "Soft White" : { + "comment" : "A color temperature description.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Wert setzen" + "value" : "Weiches Weiß" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Set value" + "value" : "Soft White" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Establecer valor" + "value" : "Blanco suave" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Aseta arvo" + "value" : "Pehmeä valkoinen" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Définir la valeur" + "value" : "Blanc doux" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Imposta valore" + "value" : "Bianco morbido" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Angi verdi" + "value" : "Myk hvit" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Waarde instellen" + "value" : "Zacht wit" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Установить значение" + "value" : "Мягкий белый" } } } }, - "settings" : { - "extractionState" : "manual", + "Sort sitemaps by" : { + "comment" : "A label describing the option to sort sitemaps.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Einstellungen" + "value" : "Sitemaps sortieren nach" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Settings" + "value" : "Sort sitemaps by" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Configuración" + "value" : "Ordenar mapas del sitio por" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Settings" + "value" : "Lajittele sivukartat" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Paramétrage" + "value" : "Trier les plans du site par" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Impostazioni" + "value" : "Ordina sitemap per" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Innstillinger" + "value" : "Sorter nettstedskart etter" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Instellingen" + "value" : "Sitemaps sorteren op" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Settings" + "value" : "Сортировать карты сайта по" } } } }, - "settings_not_received" : { + "sortSitemapsBy" : { + "extractionState" : "manual", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Einstellungen noch nicht vom iPhone empfangen." + "value" : "Sitemaps sortieren nach" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Settings not yet received from iPhone." + "value" : "Sort sitemaps by" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Configuración aún no recibida del iPhone." + "value" : "Ordenar mapas del sitio por" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Asetuksia ei ole vielä vastaanotettu iPhonelta." + "value" : "Sitemapien järjestys" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Paramètres pas encore reçus de l'iPhone." + "value" : "Trier les sitemaps par" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Impostazioni non ancora ricevute da iPhone." + "value" : "Ordina le Sitemaps per" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Innstillinger som ennå ikke er mottatt fra iPhone." + "value" : "Sorter SiteMaps etter" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Instellingen nog niet ontvangen van iPhone." + "value" : "Sorteer sitemaps op" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Настройки ещё не получены с iPhone." + "value" : "Sort sitemaps by" } } } }, - "Show Date" : { - "comment" : "A toggle that enables or disables the display of the date in the screen saver.", + "Speed up" : { + "comment" : "Synonyms for the \"Fast Forward\" action.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Datum anzeigen" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Show Date" + "value" : "Beschleunigen" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Mostrar fecha" + "value" : "Acelerar" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Näytä päivämäärä" + "value" : "Nopeuta" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Afficher la date" + "value" : "Accélérer" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Mostra data" + "value" : "Accelera" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Vis dato" + "value" : "Øk hastighet" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Datum weergeven" + "value" : "Versnellen" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Показать дату" + "value" : "Ускорить" } } } }, - "Show Search Field" : { - "comment" : "A toggle that enables or disables the search field in the app.", + "SSL error. The connection couldn’t be established securely." : { + "comment" : "Error message displayed when a connection to the OpenHAB server fails due to a SSL error.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Suchfeld anzeigen" + "value" : "SSL-Fehler. Die Verbindung konnte nicht sicher hergestellt werden." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Show Search Field" + "value" : "SSL error. The connection couldn’t be established securely." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Mostrar campo de búsqueda" + "value" : "Error SSL. No se pudo establecer la conexión de forma segura." } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Näytä hakukenttä" + "value" : "SSL-virhe. Yhteyttä ei voitu muodostaa turvallisesti." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Afficher le champ de recherche" + "value" : "Erreur SSL. La connexion n'a pas pu être établie de manière sécurisée." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Mostra campo di ricerca" + "value" : "Errore SSL. Impossibile stabilire la connessione in modo sicuro." } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Vis søkefelt" + "value" : "SSL-feil. Tilkoblingen kunne ikke opprettes sikkert." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Zoekveld weergeven" + "value" : "SSL-fout. De verbinding kon niet veilig worden opgezet." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Показать поле поиска" + "value" : "Ошибка SSL. Не удалось установить безопасное соединение." } } } }, - "Show Seconds" : { - "comment" : "A toggle that lets the user enable or disable the display of seconds in the screen saver.", + "ssl_certificate_error" : { + "extractionState" : "manual", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Sekunden anzeigen" + "value" : "SSL-Zertifikatfehler" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Show Seconds" + "value" : "SSL Certificate Error" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Mostrar segundos" + "value" : "Error de certificado SSL" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Näytä sekunnit" + "value" : "SSL Certificate Error" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Afficher les secondes" + "value" : "Erreur de certificat SSL" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Mostra secondi" + "value" : "Errore certificato SSL" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Vis sekunder" + "value" : "Feil med SSL sertifikat" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Seconden weergeven" + "value" : "SSL-certificaat fout" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Показать секунды" + "value" : "SSL Certificate Error" } } } }, - "Show Time" : { - "comment" : "A toggle that enables or disables the display of the time.", + "ssl_certificate_invalid" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Zeit anzeigen" + "value" : "Das von %1$@ für %2$@ vorgestellte SSL-Zertifikat ist ungültig. Möchtest du fortfahren?" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Show Time" + "value" : "SSL Certificate presented by %1$@ for %2$@ is invalid. Do you want to proceed?" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Mostrar hora" + "value" : "El certificado SSL presentado por %1$@ para %2$@ no es válido. ¿Desea continuar?" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Näytä aika" + "value" : "SSL Certificate presented by %1$@ for %2$@ is invalid. Do you want to proceed?" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Afficher l'heure" + "value" : "Le certificat SSL présenté par %1$@ pour %2$@ n’est pas valide. Veux-tu continuer ?" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Mostra ora" + "value" : "Il certificato SSL presentato da %1$@ per %2$@ non è valido. Vuoi procedere?" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Vis tid" + "value" : "SSL-sertifikat presentert av %1$@ for %2$@ er ugyldig. Vil du fortsette?" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Tijd weergeven" + "value" : "SSL-certificaat gepresenteerd door %1$@ voor %2$@ is ongeldig. Wilt u toch doorgaan?" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Показать время" + "value" : "SSL Certificate presented by %1$@ for %2$@ is invalid. Do you want to proceed?" } } } }, - "show_search_field" : { - "extractionState" : "manual", + "ssl_certificate_no_match" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Suchfeld anzeigen" + "value" : "Das von %1$@ für %2$@ vorgestellte SSL-Zertifikat stimmt nicht mit dem Datensatz überein. Möchtest du fortfahren?" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Show Search Field" + "value" : "SSL Certificate presented by %1$@ for %2$@ doesn't match the record. Do you want to proceed?" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Show Search Field" + "value" : "El certificado SSL presentado por %1$@ para %2$@ no coincide con el registro. ¿Desea continuar?" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Show Search Field" + "value" : "SSL Certificate presented by %1$@ for %2$@ doesn't match the record. Do you want to proceed?" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Show Search Field" + "value" : "Le certificat SSL présenté par %1$@ pour %2$@ ne correspond pas à l’enregistrement. Veux-tu continuer ?" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Show Search Field" + "value" : "Il certificato SSL presentato da %1$@ per %2$@ non corrisponde al record. Vuoi procedere?" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Show Search Field" + "value" : "SSL-sertifikat presentert av %1$@ for %2$@ samsvarer ikke med det som er lagret fra før av. Vil du fortsette?" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Show Search Field" + "value" : "SSL-certificaat gepresenteerd door %1$@ voor %2$@ komt niet overeen met het record. Wilt u toch doorgaan?" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Show Search Field" + "value" : "SSL Certificate presented by %1$@ for %2$@ doesn't match the record. Do you want to proceed?" } } } }, - "Shut" : { - "comment" : "Displayed title for the \"Shut\" state of a contact.", - "isCommentAutoGenerated" : true - }, - "sitemap" : { + "ssl_certificate_warning" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Sitemap" + "value" : "SSL-Zertifikatwarnung" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Sitemap" + "value" : "SSL Certificate Warning" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Mapa web" + "value" : "Aviso de certificado SSL" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Sitemap" + "value" : "SSL Certificate Warning" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Sitemap" + "value" : "Avertissement de certificat SSL" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Sitemap" + "value" : "Avviso Certificato SSL" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Sitemap" + "value" : "Advarsel for SSL sertifikat" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Sitemap" + "value" : "SSL-certificaat waarschuwing" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Sitemap" + "value" : "SSL Certificate Warning" } } } }, - "Sitemap" : { - "comment" : "Label for the tab item representing the sitemap view.", + "Start" : { + "comment" : "A synonym for the \"Play\" action.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Sitemap" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sitemap" + "value" : "Start" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Mapa del sitio" + "value" : "Iniciar" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Sivukartta" + "value" : "Käynnistä" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Plan du site" + "value" : "Démarrer" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Sitemap" + "value" : "Avvia" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Nettstedskart" + "value" : "Start" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Sitemap" + "value" : "Start" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Карта сайта" + "value" : "Запустить" } } } }, - "Sitemap Diagnostics Logging" : { - "comment" : "A toggle that enables or disables logging of sitemap diagnostics.", - "isCommentAutoGenerated" : true, + "State" : { + "comment" : "A label for a picker that lets the user select a state.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Sitemap-Diagnoseprotokollierung" + "value" : "Zustand" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "State" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Registro de diagnóstico del mapa del sitio" + "value" : "Provincia" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Sivukartan diagnostiikkaloki" + "value" : "Tila" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Journalisation des diagnostics du plan du site" + "value" : "État" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Registrazione diagnostica sitemap" + "value" : "Stato" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Diagnoslogging for nettstedskart" + "value" : "Tilstand" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Sitemap-diagnoselogboek" + "value" : "Status" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Журналирование диагностики карты сайта" + "value" : "Состояние" } } } }, - "Sitemap for Apple Watch" : { - "comment" : "A label for selecting a sitemap to be used with an Apple Watch app.", + "Stop" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Apple Watch Sitemap" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sitemap for Apple Watch" + "value" : "Stopp" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Mapa del sitio para Apple Watch" + "value" : "Parar" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Apple Watch -sivukartta" + "value" : "Pysäytä" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Plan du site pour Apple Watch" + "value" : "Arrêter" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Sitemap per Apple Watch" + "value" : "Ferma" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Nettstedskart for Apple Watch" + "value" : "Stopp" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Sitemap voor Apple Watch" + "value" : "Stoppen" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Карта сайта для Apple Watch" + "value" : "Остановить" } } } }, - "Sitemap for CarPlay" : { - "comment" : "A label for selecting a sitemap to be used for CarPlay.", - "isCommentAutoGenerated" : true - }, - "sitemap_settings" : { + "String Item" : { + "comment" : "Display name for a string item type.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Sitemap-Einstellungen" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sitemap Settings" + "value" : "String-Item" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Ajustes del esquema" + "value" : "Elemento de texto" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Sitemap Settings" + "value" : "Merkkijonoelementti" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Paramètres Sitemap" + "value" : "Élément texte" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Impostazioni Sitemap" + "value" : "Elemento stringa" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Innstillinger for Sitemap" + "value" : "Tekststrengselement" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Sitemap instellingen" + "value" : "Tekst-item" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Sitemap Settings" + "value" : "Строковый элемент" } } } }, - "Sitemaps" : { - "comment" : "A section header that lists the user's available sitemaps.", + "Suspend" : { + "comment" : "Display name for the pause action.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Sitemaps" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sitemaps" + "value" : "Anhalten" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Mapas del sitio" + "value" : "Suspender" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Sivukartat" + "value" : "Keskeytä" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Plans du site" + "value" : "Suspendre" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Sitemap" + "value" : "Sospendi" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Nettstedskart" + "value" : "Sett på vent" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Sitemaps" + "value" : "Onderbreken" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Карты сайта" + "value" : "Приостановить" } } } }, - "Size: %llu MB" : { - "comment" : "A message showing the size of the image cache. The argument is the size of the image cache in MB.", + "SVG" : { + "extractionState" : "manual", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Größe: %llu MB" + "value" : "SVG" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Size: %llu MB" + "value" : "SVG" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Tamaño: %llu MB" + "value" : "SVG" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Koko: %llu Mt" + "value" : "SVG" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Taille : %llu Mo" + "value" : "SVG" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Dimensione: %llu MB" + "value" : "SVG" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Størrelse: %llu MB" + "value" : "SVG" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Grootte: %llu MB" + "value" : "SVG" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Размер: %llu МБ" + "value" : "SVG" } } } }, - "Skip" : { - "comment" : "A synonym for the action \"Skip\".", - "isCommentAutoGenerated" : true - }, - "Skip ahead" : { - "comment" : "Synonyms for the \"Skip ahead\" action.", - "isCommentAutoGenerated" : true - }, - "Skip back" : { - "comment" : "Synonyms for the previous action.", - "isCommentAutoGenerated" : true - }, - "Skip forward" : { - "comment" : "A synonym for the \"Next\" action.", - "isCommentAutoGenerated" : true - }, - "Soft White" : { - "comment" : "A color temperature description.", + "Switch" : { + "comment" : "The type of action to perform on a switch.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Weiches Weiß" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Soft White" + "value" : "Schalten" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Blanco suave" + "value" : "Alternar" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Pehmeä valkoinen" + "value" : "Kytke" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Blanc doux" + "value" : "Commuter" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Bianco morbido" + "value" : "Cambia" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Myk hvit" + "value" : "Bytt" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Zacht wit" + "value" : "Schakelen" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Мягкий белый" + "value" : "Переключить" } } } }, - "Sort sitemaps by" : { - "comment" : "A label describing the option to sort sitemaps.", + "Switch Action" : { + "comment" : "Type display name for the SwitchAction enum used in app intents.", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Sitemaps sortieren nach" + "value" : "Switch-Aktion" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Sort sitemaps by" + "value" : "Switch Action" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Ordenar mapas del sitio por" + "value" : "Acción del interruptor" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Lajittele sivukartat" + "value" : "Kytkimen toiminto" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Trier les plans du site par" + "value" : "Action d'interrupteur" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Ordina sitemap per" + "value" : "Azione interruttore" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Sorter nettstedskart etter" + "value" : "Bryterhandling" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Sitemaps sorteren op" + "value" : "Schakelaaractie" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Сортировать карты сайта по" + "value" : "Действие переключателя" } } } }, - "sortSitemapsBy" : { - "extractionState" : "manual", + "Switch Home" : { + "comment" : "Shortcut title for switching between homes.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Sitemaps sortieren nach" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sort sitemaps by" + "value" : "Zuhause wechseln" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Ordenar mapas del sitio por" + "value" : "Cambiar hogar" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Sitemapien järjestys" + "value" : "Vaihda koti" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Trier les sitemaps par" + "value" : "Changer de maison" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Ordina le Sitemaps per" + "value" : "Cambia casa" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Sorter SiteMaps etter" + "value" : "Bytt hjem" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Sorteer sitemaps op" + "value" : "Thuis wisselen" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Sort sitemaps by" + "value" : "Переключить дом" } } } }, - "Speed up" : { - "comment" : "Synonyms for the \"Fast Forward\" action.", - "isCommentAutoGenerated" : true - }, - "SSL error. The connection couldn’t be established securely." : { - "comment" : "Error message displayed when a connection to the OpenHAB server fails due to a SSL error.", + "Switch Item" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "SSL-Fehler. Die Verbindung konnte nicht sicher hergestellt werden." - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "SSL error. The connection couldn’t be established securely." + "value" : "Schalterelement" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Error SSL. No se pudo establecer la conexión de forma segura." + "value" : "Elemento de interruptor" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "SSL-virhe. Yhteyttä ei voitu muodostaa turvallisesti." + "value" : "Kytkinkohde" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Erreur SSL. La connexion n'a pas pu être établie de manière sécurisée." + "value" : "Élément commutateur" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Errore SSL. Impossibile stabilire la connessione in modo sicuro." + "value" : "Elemento interruttore" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "SSL-feil. Tilkoblingen kunne ikke opprettes sikkert." + "value" : "Brytterelement" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "SSL-fout. De verbinding kon niet veilig worden opgezet." + "value" : "Schakelaaritem" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Ошибка SSL. Не удалось установить безопасное соединение." + "value" : "Элемент переключателя" } } } }, - "ssl_certificate_error" : { - "extractionState" : "manual", + "Switch Item 1" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "SSL-Zertifikatfehler" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "SSL Certificate Error" + "value" : "Schalterelement 1" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Error de certificado SSL" + "value" : "Elemento de interruptor 1" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "SSL Certificate Error" + "value" : "Kytkinkohde 1" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Erreur de certificat SSL" + "value" : "Élément commutateur 1" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Errore certificato SSL" + "value" : "Elemento interruttore 1" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Feil med SSL sertifikat" + "value" : "Brytterelement 1" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "SSL-certificaat fout" + "value" : "Schakelaaritem 1" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "SSL Certificate Error" + "value" : "Элемент переключателя 1" } } } }, - "ssl_certificate_invalid" : { + "Switch Item 2" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Das von %1$@ für %2$@ vorgestellte SSL-Zertifikat ist ungültig. Möchtest du fortfahren?" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "SSL Certificate presented by %1$@ for %2$@ is invalid. Do you want to proceed?" + "value" : "Schalterelement 2" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "El certificado SSL presentado por %1$@ para %2$@ no es válido. ¿Desea continuar?" + "value" : "Elemento de interruptor 2" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "SSL Certificate presented by %1$@ for %2$@ is invalid. Do you want to proceed?" + "value" : "Kytkinkohde 2" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Le certificat SSL présenté par %1$@ pour %2$@ n’est pas valide. Veux-tu continuer ?" + "value" : "Élément commutateur 2" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Il certificato SSL presentato da %1$@ per %2$@ non è valido. Vuoi procedere?" + "value" : "Elemento interruttore 2" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "SSL-sertifikat presentert av %1$@ for %2$@ er ugyldig. Vil du fortsette?" + "value" : "Brytterelement 2" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "SSL-certificaat gepresenteerd door %1$@ voor %2$@ is ongeldig. Wilt u toch doorgaan?" + "value" : "Schakelaaritem 2" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "SSL Certificate presented by %1$@ for %2$@ is invalid. Do you want to proceed?" + "value" : "Элемент переключателя 2" } } } }, - "ssl_certificate_no_match" : { + "Switch Item 3" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Das von %1$@ für %2$@ vorgestellte SSL-Zertifikat stimmt nicht mit dem Datensatz überein. Möchtest du fortfahren?" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "SSL Certificate presented by %1$@ for %2$@ doesn't match the record. Do you want to proceed?" + "value" : "Schalterelement 3" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "El certificado SSL presentado por %1$@ para %2$@ no coincide con el registro. ¿Desea continuar?" + "value" : "Elemento de interruptor 3" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "SSL Certificate presented by %1$@ for %2$@ doesn't match the record. Do you want to proceed?" + "value" : "Kytkinkohde 3" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Le certificat SSL présenté par %1$@ pour %2$@ ne correspond pas à l’enregistrement. Veux-tu continuer ?" + "value" : "Élément commutateur 3" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Il certificato SSL presentato da %1$@ per %2$@ non corrisponde al record. Vuoi procedere?" + "value" : "Elemento interruttore 3" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "SSL-sertifikat presentert av %1$@ for %2$@ samsvarer ikke med det som er lagret fra før av. Vil du fortsette?" + "value" : "Brytterelement 3" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "SSL-certificaat gepresenteerd door %1$@ voor %2$@ komt niet overeen met het record. Wilt u toch doorgaan?" + "value" : "Schakelaaritem 3" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "SSL Certificate presented by %1$@ for %2$@ doesn't match the record. Do you want to proceed?" + "value" : "Элемент переключателя 3" } } } }, - "ssl_certificate_warning" : { + "Switch Item 4" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "SSL-Zertifikatwarnung" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "SSL Certificate Warning" + "value" : "Schalterelement 4" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Aviso de certificado SSL" + "value" : "Elemento de interruptor 4" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "SSL Certificate Warning" + "value" : "Kytkinkohde 4" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Avertissement de certificat SSL" + "value" : "Élément commutateur 4" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Avviso Certificato SSL" + "value" : "Elemento interruttore 4" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Advarsel for SSL sertifikat" + "value" : "Brytterelement 4" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "SSL-certificaat waarschuwing" + "value" : "Schakelaaritem 4" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "SSL Certificate Warning" + "value" : "Элемент переключателя 4" } } } }, - "Start" : { - "comment" : "A synonym for the \"Play\" action.", - "isCommentAutoGenerated" : true - }, - "State" : { - "comment" : "A label for a picker that lets the user select a state.", + "Switch off" : { + "comment" : "Displayed when the user disables a device.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Zustand" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "State" + "value" : "Abschalten" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Provincia" + "value" : "Desactivar" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Tila" + "value" : "Kytke pois" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "État" + "value" : "Désactiver" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Stato" + "value" : "Disattiva" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Tilstand" + "value" : "Deaktiver" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Status" + "value" : "Uitzetten" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Состояние" + "value" : "Отключить" } } } }, - "Stop" : {}, - "String Item" : { - "comment" : "Display name for a string item type.", + "Switch on" : { + "comment" : "Switch action to turn a device on.", "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "String-Item" + "value" : "Anschalten" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Elemento de texto" + "value" : "Activar" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Merkkijonoelementti" + "value" : "Kytke päälle" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Élément texte" + "value" : "Activer" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Elemento stringa" + "value" : "Attiva" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Tekststrengselement" + "value" : "Aktiver" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Tekst-item" + "value" : "Aanzetten" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Строковый элемент" + "value" : "Включить" } } } }, - "Suspend" : { - "comment" : "Display name for the pause action.", - "isCommentAutoGenerated" : true - }, - "SVG" : { - "extractionState" : "manual", + "Switch or Dimmer Item" : { + "comment" : "Display representation of the type of an item that can be turned on or off.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "SVG" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "SVG" + "value" : "Schalter- oder Dimmerelement" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "SVG" + "value" : "Elemento de interruptor o regulador" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "SVG" + "value" : "Kytkin- tai himmenninkohde" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "SVG" + "value" : "Élément commutateur ou variateur" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "SVG" + "value" : "Elemento interruttore o dimmer" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "SVG" + "value" : "Bryter- eller dimmer-element" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "SVG" + "value" : "Schakelaar- of dimmeritem" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "SVG" + "value" : "Элемент переключателя или диммера" } } } }, - "Switch" : { - "comment" : "The type of action to perform on a switch.", - "isCommentAutoGenerated" : true - }, - "Switch Action" : { - "comment" : "Type display name for the SwitchAction enum used in app intents.", + "Switch the active home in the openHAB app" : { + "comment" : "Description of the intent to set the active home.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Switch-Aktion" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Switch Action" + "value" : "Aktives Zuhause in der openHAB-App wechseln" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Acción del interruptor" + "value" : "Cambiar la casa activa en la app openHAB" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Kytkimen toiminto" + "value" : "Vaihda aktiivinen koti openHAB-sovelluksessa" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Action d'interrupteur" + "value" : "Changer le domicile actif dans l'application openHAB" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Azione interruttore" + "value" : "Cambia la casa attiva nell'app openHAB" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Bryterhandling" + "value" : "Bytt aktivt hjem i openHAB-appen" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Schakelaaractie" + "value" : "Actief thuis wijzigen in de openHAB-app" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Действие переключателя" + "value" : "Сменить активный дом в приложении openHAB" } } } }, - "Switch Home" : { - "comment" : "Shortcut title for switching between homes.", - "isCommentAutoGenerated" : true - }, - "Switch off" : { - "comment" : "Displayed when the user disables a device.", - "isCommentAutoGenerated" : true - }, - "Switch on" : { - "comment" : "Switch action to turn a device on.", - "isCommentAutoGenerated" : true - }, - "Switch or Dimmer Item" : { - "comment" : "Display representation of the type of an item that can be turned on or off.", - "isCommentAutoGenerated" : true - }, - "Switch the active home in the openHAB app" : { - "comment" : "Description of the intent to set the active home.", - "isCommentAutoGenerated" : true, + "Switch Widget Configuration" : { "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Aktives Zuhause in der openHAB-App wechseln" + "value" : "Schalter-Widget-Konfiguration" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Cambiar la casa activa en la app openHAB" + "value" : "Configuración del widget de interruptor" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Vaihda aktiivinen koti openHAB-sovelluksessa" + "value" : "Kytkinwidgetin määritys" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Changer le domicile actif dans l'application openHAB" + "value" : "Configuration du widget commutateur" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Cambia la casa attiva nell'app openHAB" + "value" : "Configurazione widget interruttore" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Bytt aktivt hjem i openHAB-appen" + "value" : "Bryter-widgetkonfigurasjon" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Actief thuis wijzigen in de openHAB-app" + "value" : "Schakelaarwidgetconfiguratie" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Сменить активный дом в приложении openHAB" + "value" : "Настройka виджета переключателя" } } } @@ -17105,14 +20989,14 @@ }, "es" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Alternar" } }, "fi" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Vaihda" } }, "fr" : { @@ -17141,23 +21025,173 @@ }, "ru" : { "stringUnit" : { - "state" : "new", - "value" : "" + "state" : "translated", + "value" : "Переключить" } } } }, "Triggered" : { "comment" : "Display name for the contact state \"OPEN\".", - "isCommentAutoGenerated" : true + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ausgelöst" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activado" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lauennut" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Déclenché" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Attivato" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Utløst" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geactiveerd" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Сработал" + } + } + } }, "Turn off" : { "comment" : "Turn off action.", - "isCommentAutoGenerated" : true + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ausschalten" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apagar" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Laita pois" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Éteindre" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Spegni" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Slå av" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uitschakelen" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Выключить" + } + } + } }, "Turn on" : { "comment" : "Turn on action.", - "isCommentAutoGenerated" : true + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Einschalten" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Encender" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Laita päälle" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Allumer" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Accendi" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Slå på" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inschakelen" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Включить" + } + } + } }, "unable_to_add_certificate" : { "extractionState" : "manual", @@ -18215,4 +22249,4 @@ } }, "version" : "1.1" -} +} \ No newline at end of file diff --git a/openHABWidget/OpenHABIconOverlay.swift b/openHABWidget/OpenHABIconWatermark.swift similarity index 100% rename from openHABWidget/OpenHABIconOverlay.swift rename to openHABWidget/OpenHABIconWatermark.swift From 3d0747e21a54944c5890115f11447f906d2d7ad5 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Thu, 9 Jul 2026 16:50:35 +0200 Subject: [PATCH 70/91] fix: complete missing localizations for 3 strings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - '%@ • %@': add de/es/fi/fr/it/nb/nl/ru (format string, identical to en) - 'Number Item': add es/fi/nb/ru translations - Local Network alert message: add es/fi/nb/ru translations Signed-off-by: Tim Mueller-Seydlitz --- .../Supporting Files/Localizable.xcstrings | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/openHAB/Supporting Files/Localizable.xcstrings b/openHAB/Supporting Files/Localizable.xcstrings index bacd3a635..9fb0b7029 100644 --- a/openHAB/Supporting Files/Localizable.xcstrings +++ b/openHAB/Supporting Files/Localizable.xcstrings @@ -300,6 +300,54 @@ "state": "translated", "value": "%1$@ • %2$@" } + }, + "de": { + "stringUnit": { + "state": "translated", + "value": "%1$@ • %2$@" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "%1$@ • %2$@" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "%1$@ • %2$@" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "%1$@ • %2$@" + } + }, + "it": { + "stringUnit": { + "state": "translated", + "value": "%1$@ • %2$@" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "%1$@ • %2$@" + } + }, + "nl": { + "stringUnit": { + "state": "translated", + "value": "%1$@ • %2$@" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "%1$@ • %2$@" + } } } }, @@ -11779,6 +11827,30 @@ "state": "translated", "value": "Numeriek item" } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Elemento numérico" + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Numerinen kohde" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Numerisk element" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Числовой элемент" + } } } }, @@ -21056,6 +21128,30 @@ "state": "translated", "value": "Om verbinding te maken met uw lokale openHAB-server, sta toegang tot het lokale netwerk toe wanneer hierom wordt gevraagd. Als u dit eerder heeft geweigerd, schakel het in via Instellingen → Privacy en beveiliging → Lokaal netwerk." } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Para conectarte a tu servidor openHAB local, permite el acceso a la Red local cuando se te solicite. Si anteriormente lo denegaste, actívalo en Configuración → Privacidad y seguridad → Red local." + } + }, + "fi": { + "stringUnit": { + "state": "translated", + "value": "Yhdistääksesi paikalliseen openHAB-palvelimeesi, salli lähiverkon käyttö pyydettäessä. Jos aiemmin kieltäydyit, ota se käyttöön kohdasta Asetukset → Yksityisyys ja turvallisuus → Paikallinen verkko." + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "For å koble til den lokale openHAB-serveren din, tillat tilgang til lokalt nettverk når du blir bedt om det. Hvis du tidligere nektet det, aktiver det under Innstillinger → Personvern og sikkerhet → Lokalt nettverk." + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Чтобы подключиться к локальному серверу openHAB, разрешите доступ к локальной сети при появлении запроса. Если вы ранее отказали в доступе, включите его в разделе Настройки → Конфиденциальность и безопасность → Локальная сеть." + } } } }, From 3facccd9f2fd3ce6f360ca1d9d780ff5f1deb122 Mon Sep 17 00:00:00 2001 From: openhab-bot Date: Thu, 9 Jul 2026 15:15:43 +0000 Subject: [PATCH 71/91] committed version bump: 3.4.9 (300) Signed-off-by: openhab-bot --- CHANGELOG.md | 71 ++++++++++++++++++++++++++++++++++++++++++++++++ Version.xcconfig | 4 +-- 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4454cbce..5185a0f81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,77 @@ ## [Unreleased] +- refactor: rename OpenHABIconOverlay to OpenHABIconWatermark and complete translations +- chore: switch main app target to CarPlay entitlements file +- fix: use LocalizedStringResource in AppIntents display representations +- fix: improve CarPlay robustness and localizations +- Widgets layout adjustments (#1292) +- committed version bump: 3.4.8 (298) +- fix: remove 7 stale localization keys with no source references +- fix: remove stale %lld %% localization keys for screensaver percentage labels +- Make Circular Accessory Switch icon state aware (#1291) +- committed version bump: 3.4.7 (297) +- refactor: replace remote icon fetch with SF symbol mapping in CarPlay +- feat: switch CarPlay to CPGridTemplate with openHAB icons +- fix: show placeholder instead of empty CPListTemplate when no compatible widgets +- refactor: migrate CarPlay streaming from long-poll to SSE +- feat: add CarPlay support with sitemap-based favorites scene +- Migrate sitemap page updates from long-polling to SSE (#1168) +- Small Widget background watermark alignment adjustments (#1289) +- fix: show message body below title in notification list +- fix: add Localizable.xcstrings to widget extension target membership +- fix: remove duplicate knownRegions from project.pbxproj +- committed version bump: 3.4.6 (296) +- Allow for two lines label and value for iOS Lock Screen Accessories (#1286) +- Swiftlinting +- chore: apply linter fixes +- feat: add accessory widget previews for Switch and Sensor widgets +- feat: wide-display sitemap grid and full-width media rows +- refactor: remove UIImageView polling timer from MJPEG video stream +- committed version bump: 3.4.5 (294) +- Fix small widget content to be cntered +- committed version bump: 3.4.4 (293) +- committed version bump: 3.4.3 (292) +- fix: sync allowedItemTypes with query and add regression test +- feat: make Dimmer items selectable in Set Switch State intent +- chore: apply linter fixes +- fix: detect GIF images by magic bytes in ImageRowView +- committed version bump: 3.4.2 (290) +- fix: animate GIFs in the Image sitemap widget (#1280) +- Widget adjustments (#1279) +- fix: lock screen widgets now send commands on tap +- fix: notification ping now respects the ringer/silent switch (#1278) +- fix: add OpenHABShortcuts.swift to app target to resolve AppShortcuts.xcstrings phrase warnings +- committed version bump: 3.4.1 (287) +- fix: add provisioning profile for openHABWidgetExtension +- l10n: add Spanish, Finnish, Norwegian, and Russian translations +- l10n: switch German and French translations to informal address (du/tu) +- chore: remove 5 stale localization keys on feature branch +- l10n: add Spanish, Finnish, Norwegian, and Russian translations +- l10n: translate 2 new widget keys into all 9 languages +- chore: update openHABWidgetExtension scheme with askForAppToLaunch +- fix: restore project.pbxproj corrupted by bad 3-way merge of pbxproj +- chore: add shared schemes for openHABWidgetExtension and OpenHABWatchComplicationsExtension +- build: raise macOS minimum to 15 for AsyncSequence.Failure support +- chore: bump openHABTestsSwift deployment target to iOS 18.0 +- Merge develop + fix build warnings; add iOS home screen widgets with item display +- Sensor widget: format state values using stateDescription numberPattern +- Sensor widget: support 1/2/4 items per small/medium/large size +- Make ScrollToTop overlay button conditional (#1231) +- Bump minimum deployment targets to iOS 18 / watchOS 11 +- Use iOS 18 onScrollGeometryChange in SitemapPageView +- Use iOS 17 animation and scroll APIs +- Replace SetLocationValueIntent with SetActiveHomeIntent in AppShortcutsProvider +- Register SetActiveHomeIntent with AppShortcutsProvider +- Linting and AppIntents SSE warnings fixes +- Bump deployment targets to iOS 17.0 / watchOS 10.0 +- Convert AppShortcuts.strings to AppShortcuts.xcstrings String Catalog +- Fix AppIntentsMetadataProcessor synonym warnings in AppEnum cases +- Add AppShortcutsProvider and fix intent dialog gaps +- Upgrade SwiftFormatPlugin 0.58.7→0.61.1, SwiftLintPlugin 0.62.2→0.63.3 (#1225) +- ItemEventStream: backport SSE watchdog and self-contained network monitor +- openHABWidget: add interactive switch and sensor widgets + - fix: remove 7 stale localization keys with no source references - fix: remove stale %lld %% localization keys for screensaver percentage labels - Make Circular Accessory Switch icon state aware (#1291) diff --git a/Version.xcconfig b/Version.xcconfig index 9d52ac4c4..d3f5ec1de 100644 --- a/Version.xcconfig +++ b/Version.xcconfig @@ -6,5 +6,5 @@ // https://developer.apple.com/documentation/xcode/adding-a-build-configuration-file-to-your-project -MARKETING_VERSION = 3.4.8 -CURRENT_PROJECT_VERSION = 298 +MARKETING_VERSION = 3.4.9 +CURRENT_PROJECT_VERSION = 300 From 7d57f639389d729d0ef82f2ab7ab70d1ef94e4aa Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Sun, 12 Jul 2026 12:31:32 +0200 Subject: [PATCH 72/91] fix: keep SSE stream alive across CarPlay page refreshes Three bugs fixed: 1. sitemapChanged and widget-notFound called startStreaming(), which cancelled and rebuilt the entire SSE stack (NetworkTracker reset + new subscription). The onTermination handler on the old continuation could fire while the new task was still doing network round-trips, causing SitemapEventStream.cleanupContinuation to cancel listenTask and create a gap in event coverage. Replace with schedulePageRefresh(), which re-fetches only the page without touching the SSE stream. 2. No page refresh on SSE reconnect: if SITEMAP_CHANGED was missed during a disconnect the page stayed permanently stale. Add a needsRefreshOnReconnect flag (matching SitemapPageViewModel) that triggers schedulePageRefresh() on the next .connected event. 3. Calling NetworkTracker.shared.startTracking() on every restart (sitemapChanged, notFound) could reset the shared tracker's active connection and briefly break the main app's SSE. Now startTracking is only called on initial connect or sitemap-selection change. currentPage and currentService are now stored properties so handleSseMessage and refreshPage can read them without being threaded through every function parameter. Signed-off-by: Tim Mueller-Seydlitz --- openHAB/CarPlaySceneDelegate.swift | 66 +++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 11 deletions(-) diff --git a/openHAB/CarPlaySceneDelegate.swift b/openHAB/CarPlaySceneDelegate.swift index d2f28c207..a789a330d 100644 --- a/openHAB/CarPlaySceneDelegate.swift +++ b/openHAB/CarPlaySceneDelegate.swift @@ -21,9 +21,13 @@ private var carPlayMaxItems: Int { final class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegate { private var interfaceController: CPInterfaceController? private var streamTask: Task? + private var refreshTask: Task? private var preferencesTask: Task? private let sitemapEventStream = SitemapEventStream() private var currentGridTemplate: CPGridTemplate? + // Retained across SSE restarts so page refreshes don't need a full stream teardown. + private var currentPage: OpenHABPage? + private var currentService: OpenAPIService? func templateApplicationScene(_ templateApplicationScene: CPTemplateApplicationScene, didConnect interfaceController: CPInterfaceController) { @@ -38,11 +42,15 @@ final class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegat didDisconnectInterfaceController interfaceController: CPInterfaceController) { streamTask?.cancel() streamTask = nil + refreshTask?.cancel() + refreshTask = nil preferencesTask?.cancel() preferencesTask = nil Task { await sitemapEventStream.stop() } self.interfaceController = nil currentGridTemplate = nil + currentPage = nil + currentService = nil Logger.carPlay.info("CarPlay scene disconnected") } @@ -63,6 +71,7 @@ final class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegat let newSitemap = Preferences.shared.currentHomePreferences.sitemapForCarPlay guard newSitemap != lastSitemap else { continue } lastSitemap = newSitemap + // Sitemap selection changed — full restart needed (different page/subscription). startStreaming() } } @@ -90,17 +99,17 @@ final class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegat } do { let service = try OpenAPIService(connectionConfiguration: connection.configuration) + currentService = service - // Initial fetch to populate the template and learn the pageId guard let page = try await service.pollDataForPage( sitemapname: sitemapName, pageId: "", longPolling: false ) else { return } + currentPage = page updateTemplate(page: page, service: service) - // Prefer SSE; fall back to long-poll on older servers let serverProps = try? await service.getRoot() if serverProps?.hasSseSupport() == true { - await runSSE(page: page, sitemapName: sitemapName, connection: connection, service: service) + await runSSE(sitemapName: sitemapName, connection: connection) } else { await runLongPoll(sitemapName: sitemapName, pageId: page.pageId, service: service) } @@ -110,40 +119,50 @@ final class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegat } @MainActor - private func runSSE(page: OpenHABPage, sitemapName: String, connection: ConnectionInfo, service: OpenAPIService) async { + private func runSSE(sitemapName: String, connection: ConnectionInfo) async { await sitemapEventStream.startMonitoringNetworkIfNeeded(initialConnection: connection) - let pageId = page.pageId.isEmpty ? sitemapName : page.pageId + let pageId = currentPage.map { $0.pageId.isEmpty ? sitemapName : $0.pageId } ?? sitemapName let stream = await sitemapEventStream.stream(sitemap: sitemapName, pageId: pageId) Logger.carPlay.info("CarPlay SSE starting for \(sitemapName)/\(pageId)") + // Refresh on reconnect so structural changes missed during a disconnect are caught. + var needsRefreshOnReconnect = false + for await msg in stream { guard !Task.isCancelled else { break } switch msg { case .connected: Logger.carPlay.info("CarPlay SSE connected") + if needsRefreshOnReconnect { + needsRefreshOnReconnect = false + schedulePageRefresh(sitemapName: sitemapName) + } case let .disconnected(error): + needsRefreshOnReconnect = true if let error { Logger.carPlay.warning("CarPlay SSE disconnected: \(error)") } case let .event(message): - handleSseMessage(message, page: page, service: service) + handleSseMessage(message, sitemapName: sitemapName) } } } @MainActor - private func handleSseMessage(_ message: SitemapEventMessage, page: OpenHABPage, service: OpenAPIService) { + private func handleSseMessage(_ message: SitemapEventMessage, sitemapName: String) { switch message { case .alive: break case .sitemapChanged: - Logger.carPlay.info("CarPlay SSE: sitemap changed, reloading") - startStreaming() + Logger.carPlay.info("CarPlay SSE: sitemap changed, refreshing page") + // Refresh page content only — do not restart the SSE stream. + schedulePageRefresh(sitemapName: sitemapName) case let .widget(event): + guard let page = currentPage, let service = currentService else { return } switch page.apply(event: event) { case .applied: updateTemplate(page: page, service: service) case .requiresPageReload, .notFound: - Logger.carPlay.info("CarPlay SSE: widget requires reload") - startStreaming() + Logger.carPlay.info("CarPlay SSE: widget \(event.widgetId ?? "") requires reload") + schedulePageRefresh(sitemapName: sitemapName) case .unchanged: break } @@ -152,6 +171,30 @@ final class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegat } } + /// Refreshes `currentPage` from the server and updates the template without restarting the SSE stream. + /// Cancels any in-flight refresh so rapid events coalesce into a single fetch. + private func schedulePageRefresh(sitemapName: String) { + refreshTask?.cancel() + refreshTask = Task { [weak self] in + guard let self, !Task.isCancelled else { return } + await refreshPage(sitemapName: sitemapName) + } + } + + @MainActor + private func refreshPage(sitemapName: String) async { + guard let service = currentService else { return } + do { + guard let page = try await service.pollDataForPage( + sitemapname: sitemapName, pageId: "", longPolling: false + ) else { return } + currentPage = page + updateTemplate(page: page, service: service) + } catch { + Logger.carPlay.error("CarPlay page refresh error: \(error)") + } + } + @MainActor private func runLongPoll(sitemapName: String, pageId: String, service: OpenAPIService) async { Logger.carPlay.info("CarPlay using long-poll for \(sitemapName)") @@ -159,6 +202,7 @@ final class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegat for try await event in SitemapPageLoader.stream(sitemapName: sitemapName, pageId: pageId, service: service) { guard !Task.isCancelled else { break } if case let .longPoll(page, _) = event { + currentPage = page updateTemplate(page: page, service: service) } } From 1224651fc4a5e0ea89cc7693c74e8c22f1dce4d9 Mon Sep 17 00:00:00 2001 From: DigiH <17110652+DigiH@users.noreply.github.com> Date: Sun, 12 Jul 2026 18:42:57 +0200 Subject: [PATCH 73/91] Minor widget layout adjustments Signed-off-by: DigiH <17110652+DigiH@users.noreply.github.com> --- openHABWidget/SensorWidgetEntryView.swift | 2 +- openHABWidget/SwitchWidgetEntryView.swift | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/openHABWidget/SensorWidgetEntryView.swift b/openHABWidget/SensorWidgetEntryView.swift index 39b2a8534..5c182edc7 100644 --- a/openHABWidget/SensorWidgetEntryView.swift +++ b/openHABWidget/SensorWidgetEntryView.swift @@ -220,7 +220,7 @@ struct SensorSmallWidgetView: View { .fixedSize(horizontal: false, vertical: true) .layoutPriority(1) .padding(.top, 20) - Spacer() + Spacer(minLength: 2) Text(formattedState(for: item)) .font(.title2) diff --git a/openHABWidget/SwitchWidgetEntryView.swift b/openHABWidget/SwitchWidgetEntryView.swift index 678695cd8..6c3c506ca 100644 --- a/openHABWidget/SwitchWidgetEntryView.swift +++ b/openHABWidget/SwitchWidgetEntryView.swift @@ -253,7 +253,7 @@ struct SwitchSmallWidgetView: View { .fixedSize(horizontal: false, vertical: true) .layoutPriority(1) .padding(.top, 20) - Spacer() + Spacer(minLength: 2) if let home = entry.home { Toggle( isOn: slot.item.state == "ON", @@ -269,6 +269,7 @@ struct SwitchSmallWidgetView: View { .background(isOn ? Color.green : Color(uiColor: .systemFill)) .clipShape(Circle()) } + Spacer() } .frame(maxHeight: .infinity) .padding() From 4c981ed3eb321c07ac46b352ea82de96a5da71f7 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Sun, 12 Jul 2026 20:53:59 +0200 Subject: [PATCH 74/91] fix: show inline error for inaccessible images and suppress connectivity alerts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ImageRowView: show an inline error card (icon + HTTP status message) when a linked image fails to load instead of leaving a blank area. Clears on URL change or successful reload. SitemapPageView: exclude SitemapPageError (noActiveConnection, serviceUnavailable, noData) from the modal alert — these are transient connectivity states already reflected by the offline/loading indicators and should not block the user. Signed-off-by: Tim Mueller-Seydlitz --- openHAB/UI/SwiftUI/Rows/ImageRowView.swift | 45 +++++++++++++++++++++- openHAB/UI/SwiftUI/SitemapPageView.swift | 4 +- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/openHAB/UI/SwiftUI/Rows/ImageRowView.swift b/openHAB/UI/SwiftUI/Rows/ImageRowView.swift index e3692b371..762454da0 100644 --- a/openHAB/UI/SwiftUI/Rows/ImageRowView.swift +++ b/openHAB/UI/SwiftUI/Rows/ImageRowView.swift @@ -13,6 +13,7 @@ import CommonUI import Kingfisher import OpenHABCore import os.log +import SFSafeSymbols import SwiftUI private struct ImageRowConfig { @@ -47,6 +48,7 @@ private struct ImageRowContent: View { @State private var chartDisplayState: ChartDisplayState? @State private var animatedGIFSourceURL: URL? @State private var embeddedGIFAspectRatio: CGFloat = 16.0 / 9.0 + @State private var imageLoadError: KingfisherError? private let logger = Logger(subsystem: "org.openhab", category: "ImageRowView") @@ -111,6 +113,7 @@ private struct ImageRowContent: View { } .onChange(of: input.url) { animatedGIFSourceURL = nil + imageLoadError = nil } } @@ -162,6 +165,8 @@ private struct ImageRowContent: View { "\(input.widgetId)-\(forceRefreshKey)" } let provider = RawImageDataProvider(data: data, cacheKey: cacheKey) + // swiftlint:disable:next redundant_discardable_let + let _ = logger.info("ImageRow embedded: \(data.count, privacy: .public) bytes, isGIF=\(isGIFMagic(data), privacy: .public)") if isGIFMagic(data) { KFAnimatedImage(source: .provider(provider)) .withOpenHABCredentials(for: networkTracker.activeConnection) @@ -182,7 +187,9 @@ private struct ImageRowContent: View { .clipShape(.rect(cornerRadius: 8)) } case let .link(url): - if animatedGIFSourceURL == url { + if let error = imageLoadError { + imageErrorView(for: error) + } else if animatedGIFSourceURL == url { KFAnimatedImage(url) .withOpenHABCredentials(for: networkTracker.activeConnection) .configure { $0.contentMode = .scaleAspectFit } @@ -198,11 +205,13 @@ private struct ImageRowContent: View { } } .onSuccess { result in + imageLoadError = nil lastRegularImageState = RegularImageState(url: url, image: result.image) } .onFailure { error in guard !error.isTaskCancelled else { return } logger.warning("Image fetch failed for \(url?.absoluteString ?? "nil", privacy: .public): \(error.localizedDescription, privacy: .public)") + imageLoadError = error } .fade(duration: 0) .cacheMemoryOnly(!shouldCache) @@ -227,6 +236,8 @@ private struct ImageRowContent: View { } } .onSuccess { result in + imageLoadError = nil + logger.info("Image fetch successful for \(url?.absoluteString ?? "nil", privacy: .public)") lastRegularImageState = RegularImageState(url: url, image: result.image) if isGIF(result) { logger.debug("GIF detected for \(url?.absoluteString ?? "nil", privacy: .public)") @@ -236,6 +247,7 @@ private struct ImageRowContent: View { .onFailure { error in guard !error.isTaskCancelled else { return } logger.warning("Image fetch failed for \(url?.absoluteString ?? "nil", privacy: .public): \(error.localizedDescription, privacy: .public)") + imageLoadError = error } .fade(duration: 0) .resizable() @@ -277,6 +289,37 @@ private struct ImageRowContent: View { return result.data().map(isGIFMagic) ?? false } + private func imageErrorView(for error: KingfisherError) -> some View { + VStack(spacing: 8) { + Image(systemSymbol: .exclamationmarkTriangle) + .font(.title2) + .foregroundStyle(.secondary) + Text(describeImageError(error)) + .font(.caption) + .foregroundStyle(.secondary) + .multilineTextAlignment(.center) + .padding(.horizontal) + } + .frame(maxWidth: .infinity, minHeight: 80) + .padding(.vertical, 12) + .background(Color.secondary.opacity(0.08)) + .clipShape(.rect(cornerRadius: 8)) + } + + private func describeImageError(_ error: KingfisherError) -> String { + if case let .responseError(reason) = error, + case let .invalidHTTPStatusCode(response) = reason { + switch response.statusCode { + case 401: return "Authentication required (HTTP 401)" + case 403: return "Access denied (HTTP 403)" + case 404: return "Image not found (HTTP 404)" + case 500...: return "Server error (HTTP \(response.statusCode))" + default: return "HTTP error \(response.statusCode)" + } + } + return error.localizedDescription + } + private func setupRefreshTimer() { stopRefreshTimer() diff --git a/openHAB/UI/SwiftUI/SitemapPageView.swift b/openHAB/UI/SwiftUI/SitemapPageView.swift index 78093f5a8..002ca1dd9 100644 --- a/openHAB/UI/SwiftUI/SitemapPageView.swift +++ b/openHAB/UI/SwiftUI/SitemapPageView.swift @@ -121,8 +121,8 @@ struct SitemapPageView: View { .navigationTitle(viewModel.pageTitle) .navigationBarTitleDisplayMode(.large) .alert("Error", isPresented: Binding( - get: { viewModel.error != nil }, - set: { if !$0 { viewModel.error = nil } } + get: { viewModel.error != nil && !(viewModel.error is SitemapPageError) }, + set: { if !$0 { Task { @MainActor in viewModel.error = nil } } } ), actions: { Button("OK", role: .cancel) {} }, message: { From e9e4680aaf6f2a70531a069606c611df37c5eae1 Mon Sep 17 00:00:00 2001 From: openhab-bot Date: Wed, 15 Jul 2026 08:48:29 +0000 Subject: [PATCH 75/91] committed version bump: 3.4.10 (302) Signed-off-by: openhab-bot --- CHANGELOG.md | 75 ++++++++++++++++++++++++++++++++++++++++++++++++ Version.xcconfig | 4 +-- 2 files changed, 77 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ff88234f..78be5fc5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,81 @@ ## [Unreleased] +- fix: show inline error for inaccessible images and suppress connectivity alerts +- Minor widget layout adjustments +- fix: keep SSE stream alive across CarPlay page refreshes +- committed version bump: 3.4.9 (300) +- fix: complete missing localizations for 3 strings +- refactor: rename OpenHABIconOverlay to OpenHABIconWatermark and complete translations +- chore: switch main app target to CarPlay entitlements file +- fix: use LocalizedStringResource in AppIntents display representations +- fix: improve CarPlay robustness and localizations +- Widgets layout adjustments (#1292) +- committed version bump: 3.4.8 (298) +- fix: remove 7 stale localization keys with no source references +- fix: remove stale %lld %% localization keys for screensaver percentage labels +- Make Circular Accessory Switch icon state aware (#1291) +- committed version bump: 3.4.7 (297) +- refactor: replace remote icon fetch with SF symbol mapping in CarPlay +- feat: switch CarPlay to CPGridTemplate with openHAB icons +- fix: show placeholder instead of empty CPListTemplate when no compatible widgets +- refactor: migrate CarPlay streaming from long-poll to SSE +- feat: add CarPlay support with sitemap-based favorites scene +- Migrate sitemap page updates from long-polling to SSE (#1168) +- Small Widget background watermark alignment adjustments (#1289) +- fix: show message body below title in notification list +- fix: add Localizable.xcstrings to widget extension target membership +- fix: remove duplicate knownRegions from project.pbxproj +- committed version bump: 3.4.6 (296) +- Allow for two lines label and value for iOS Lock Screen Accessories (#1286) +- chore: apply linter fixes +- feat: add accessory widget previews for Switch and Sensor widgets +- feat: wide-display sitemap grid and full-width media rows +- refactor: remove UIImageView polling timer from MJPEG video stream +- committed version bump: 3.4.5 (294) +- Fix small widget content to be cntered +- committed version bump: 3.4.4 (293) +- committed version bump: 3.4.3 (292) +- fix: sync allowedItemTypes with query and add regression test +- feat: make Dimmer items selectable in Set Switch State intent +- chore: apply linter fixes +- fix: detect GIF images by magic bytes in ImageRowView +- committed version bump: 3.4.2 (290) +- fix: animate GIFs in the Image sitemap widget (#1280) +- Widget adjustments (#1279) +- fix: lock screen widgets now send commands on tap +- fix: notification ping now respects the ringer/silent switch (#1278) +- fix: add OpenHABShortcuts.swift to app target to resolve AppShortcuts.xcstrings phrase warnings +- committed version bump: 3.4.1 (287) +- fix: add provisioning profile for openHABWidgetExtension +- l10n: add Spanish, Finnish, Norwegian, and Russian translations +- l10n: switch German and French translations to informal address (du/tu) +- chore: remove 5 stale localization keys on feature branch +- l10n: add Spanish, Finnish, Norwegian, and Russian translations +- l10n: translate 2 new widget keys into all 9 languages +- chore: update openHABWidgetExtension scheme with askForAppToLaunch +- fix: restore project.pbxproj corrupted by bad 3-way merge of pbxproj +- chore: add shared schemes for openHABWidgetExtension and OpenHABWatchComplicationsExtension +- build: raise macOS minimum to 15 for AsyncSequence.Failure support +- chore: bump openHABTestsSwift deployment target to iOS 18.0 +- Merge develop + fix build warnings; add iOS home screen widgets with item display +- Sensor widget: format state values using stateDescription numberPattern +- Sensor widget: support 1/2/4 items per small/medium/large size +- Make ScrollToTop overlay button conditional (#1231) +- Bump minimum deployment targets to iOS 18 / watchOS 11 +- Use iOS 18 onScrollGeometryChange in SitemapPageView +- Use iOS 17 animation and scroll APIs +- Replace SetLocationValueIntent with SetActiveHomeIntent in AppShortcutsProvider +- Register SetActiveHomeIntent with AppShortcutsProvider +- Linting and AppIntents SSE warnings fixes +- Bump deployment targets to iOS 17.0 / watchOS 10.0 +- Convert AppShortcuts.strings to AppShortcuts.xcstrings String Catalog +- Fix AppIntentsMetadataProcessor synonym warnings in AppEnum cases +- Add AppShortcutsProvider and fix intent dialog gaps +- Upgrade SwiftFormatPlugin 0.58.7→0.61.1, SwiftLintPlugin 0.62.2→0.63.3 (#1225) +- ItemEventStream: backport SSE watchdog and self-contained network monitor +- openHABWidget: add interactive switch and sensor widgets + - refactor: rename OpenHABIconOverlay to OpenHABIconWatermark and complete translations - feature: add CarPlay functionality - Migrate sitemap page updates from long-polling to SSE (#1168) diff --git a/Version.xcconfig b/Version.xcconfig index d3f5ec1de..8898cc4fb 100644 --- a/Version.xcconfig +++ b/Version.xcconfig @@ -6,5 +6,5 @@ // https://developer.apple.com/documentation/xcode/adding-a-build-configuration-file-to-your-project -MARKETING_VERSION = 3.4.9 -CURRENT_PROJECT_VERSION = 300 +MARKETING_VERSION = 3.4.10 +CURRENT_PROJECT_VERSION = 302 From 0ee8452642abfcc269c65ed8271cdaf0ef989488 Mon Sep 17 00:00:00 2001 From: DigiH <17110652+DigiH@users.noreply.github.com> Date: Wed, 15 Jul 2026 15:51:20 +0200 Subject: [PATCH 76/91] singleMappingButton toggle button fix (#1296) Signed-off-by: DigiH <17110652+DigiH@users.noreply.github.com> --- .../UI/SwiftUI/Rows/SegmentedRowView.swift | 73 +++++++++++-------- 1 file changed, 41 insertions(+), 32 deletions(-) diff --git a/openHAB/UI/SwiftUI/Rows/SegmentedRowView.swift b/openHAB/UI/SwiftUI/Rows/SegmentedRowView.swift index cbf50a058..890ea4ef2 100644 --- a/openHAB/UI/SwiftUI/Rows/SegmentedRowView.swift +++ b/openHAB/UI/SwiftUI/Rows/SegmentedRowView.swift @@ -202,45 +202,54 @@ private struct SegmentedRowContent: View { .frame(minWidth: 50) .foregroundStyle(.primary) .background( - RoundedRectangle(cornerRadius: 6) + RoundedRectangle(cornerRadius: 7) .fill( - (singlePressed || isSelected) - ? (colorScheme == .dark ? Color(uiColor: .systemGray2) : Color(uiColor: .systemBackground)) - : Color.clear + isSelected + ? Color(uiColor: colorScheme == .dark ? .systemGray2 : .systemBackground) + : Color(uiColor: colorScheme == .dark ? .systemGray4 : .systemGray5) ) ) - .ohMinimumHitTarget() - .contentShape(Rectangle()) - .gesture( - DragGesture(minimumDistance: 0) - .onChanged { _ in - if singlePressed == false { - singlePressed = true - triggerSelectionFeedback.toggle() - startOptimisticSelection( - command: mapping.command, - displayState: displayState, - mappings: mappings, - widgetVersion: widgetVersion - ) - logger.info("Segment mapping pressed, command: \(mapping.command)") - sendCommand(mapping.command, .immediate, .change) - } - } - .onEnded { _ in - singlePressed = false - } - ) - .background( - RoundedRectangle(cornerRadius: 7) - .fill(Color(uiColor: colorScheme == .dark ? .systemGray4 : .systemGray5)) - ) .overlay( RoundedRectangle(cornerRadius: 7) - .stroke(Color.secondary.opacity(0.3), lineWidth: 0.5) + .stroke( + (isSelected ? Color.secondary.opacity(0.8) : Color.secondary.opacity(0.3)), + lineWidth: 0.5 + ) ) + .ohMinimumHitTarget() + .contentShape(Rectangle()) + .onTapGesture { + if !isSelected { + // Toggle ON + triggerSelectionFeedback.toggle() + startOptimisticSelection( + command: mapping.command, + displayState: displayState, + mappings: mappings, + widgetVersion: widgetVersion + ) + logger.info("Single mapping toggle ON, command: \(mapping.command)") + sendCommand(mapping.command, .immediate, .change) + } else { + // Toggle OFF + let offCommand: String = { + if let rc = mapping.releaseCommand, !rc.isEmpty { return rc } + // Default to OFF if no explicit releaseCommand is provided + return "OFF" + }() + triggerSelectionFeedback.toggle() + // Clear optimistic selection to reflect OFF state locally until server echoes + optimisticSelectedIndex = nil + optimisticBaseState = displayState.effectiveState + optimisticWidgetId = displayState.widgetId + optimisticStartVersion = widgetVersion + revertTask?.cancel() + revertTask = nil + logger.info("Single mapping toggle OFF, command: \(offCommand)") + sendCommand(offCommand, .immediate, .change) + } + } .animation(.spring(response: 0.3, dampingFraction: 0.7), value: isSelected) - .animation(.easeInOut(duration: 0.1), value: singlePressed) } // MARK: - Helper Methods From 13391447cc1f82f01d9b3613b275b1af83ecb5eb Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Wed, 15 Jul 2026 16:52:53 +0200 Subject: [PATCH 77/91] docs: expand ohMinimumHitTarget doc comment Signed-off-by: Tim Mueller-Seydlitz --- CommonUI/Sources/CommonUI/OHTextTokenStyle.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CommonUI/Sources/CommonUI/OHTextTokenStyle.swift b/CommonUI/Sources/CommonUI/OHTextTokenStyle.swift index be9c5a26a..6de8f03e3 100644 --- a/CommonUI/Sources/CommonUI/OHTextTokenStyle.swift +++ b/CommonUI/Sources/CommonUI/OHTextTokenStyle.swift @@ -101,7 +101,10 @@ public extension View { modifier(OHTextTokenModifier(token: token)) } - /// Applies the standard minimum tappable target used across row controls. + /// Applies the standard minimum tappable target to compact interactive controls. + /// + /// Use this on small buttons, text pills, or custom gesture targets embedded in + /// rows. Full-width controls and rows with their own hit area do not need it. func ohMinimumHitTarget(_ minHeight: CGFloat = OHAccessibilityToken.minimumHitTarget) -> some View { frame(minHeight: minHeight) } From c42077ef67511e5c12af403519ef7363d752efcd Mon Sep 17 00:00:00 2001 From: DigiH <17110652+DigiH@users.noreply.github.com> Date: Wed, 15 Jul 2026 17:56:45 +0200 Subject: [PATCH 78/91] Remove ohMinimumHitTarget() to reinstate consistent sitemap row heights (#1298) Signed-off-by: DigiH <17110652+DigiH@users.noreply.github.com> --- CommonUI/Sources/CommonUI/OHTextTokenStyle.swift | 12 ------------ openHAB/UI/SwiftUI/Rows/ColorPickerRowView.swift | 1 - openHAB/UI/SwiftUI/Rows/RollershutterRowView.swift | 3 --- openHAB/UI/SwiftUI/Rows/SegmentedRowView.swift | 3 --- openHAB/UI/SwiftUI/Rows/SetpointRowView.swift | 2 -- openHAB/UI/SwiftUI/SitemapNavigationView.swift | 3 --- 6 files changed, 24 deletions(-) diff --git a/CommonUI/Sources/CommonUI/OHTextTokenStyle.swift b/CommonUI/Sources/CommonUI/OHTextTokenStyle.swift index 6de8f03e3..f8605bc99 100644 --- a/CommonUI/Sources/CommonUI/OHTextTokenStyle.swift +++ b/CommonUI/Sources/CommonUI/OHTextTokenStyle.swift @@ -22,10 +22,6 @@ public enum OHTextToken { case emphasis } -public enum OHAccessibilityToken { - public static let minimumHitTarget: CGFloat = 44 -} - private struct OHTextTokenModifier: ViewModifier { let token: OHTextToken @@ -100,12 +96,4 @@ public extension View { func ohTextToken(_ token: OHTextToken) -> some View { modifier(OHTextTokenModifier(token: token)) } - - /// Applies the standard minimum tappable target to compact interactive controls. - /// - /// Use this on small buttons, text pills, or custom gesture targets embedded in - /// rows. Full-width controls and rows with their own hit area do not need it. - func ohMinimumHitTarget(_ minHeight: CGFloat = OHAccessibilityToken.minimumHitTarget) -> some View { - frame(minHeight: minHeight) - } } diff --git a/openHAB/UI/SwiftUI/Rows/ColorPickerRowView.swift b/openHAB/UI/SwiftUI/Rows/ColorPickerRowView.swift index 992e7cad3..f558afe05 100644 --- a/openHAB/UI/SwiftUI/Rows/ColorPickerRowView.swift +++ b/openHAB/UI/SwiftUI/Rows/ColorPickerRowView.swift @@ -296,7 +296,6 @@ private struct BrightnessButton: View { Image(systemSymbol: symbol) .font(.title2) .foregroundStyle(Color(UIColor.systemBlue)) - .ohMinimumHitTarget() .contentShape(Rectangle()) .accessibilityLabel(accessibilityLabel) .accessibilityAddTraits(.isButton) diff --git a/openHAB/UI/SwiftUI/Rows/RollershutterRowView.swift b/openHAB/UI/SwiftUI/Rows/RollershutterRowView.swift index a82870ff5..997691226 100644 --- a/openHAB/UI/SwiftUI/Rows/RollershutterRowView.swift +++ b/openHAB/UI/SwiftUI/Rows/RollershutterRowView.swift @@ -82,7 +82,6 @@ private struct RollershutterRowContent: View { .foregroundStyle(Color(UIColor.systemBlue)) } .buttonStyle(.plain) - .ohMinimumHitTarget() .sensoryHeavyFeedbackIfAvailable(trigger: triggerDownFeedback) Button { @@ -95,7 +94,6 @@ private struct RollershutterRowContent: View { .foregroundStyle(Color(UIColor.systemBlue)) } .buttonStyle(.plain) - .ohMinimumHitTarget() .sensoryStopFeedbackIfAvailable(trigger: triggerStopFeedback) Button { @@ -108,7 +106,6 @@ private struct RollershutterRowContent: View { .foregroundStyle(Color(UIColor.systemBlue)) } .buttonStyle(.plain) - .ohMinimumHitTarget() .sensoryHeavyFeedbackIfAvailable(trigger: triggerUpFeedback) } .padding(.trailing, -2) diff --git a/openHAB/UI/SwiftUI/Rows/SegmentedRowView.swift b/openHAB/UI/SwiftUI/Rows/SegmentedRowView.swift index 890ea4ef2..2d7693ac1 100644 --- a/openHAB/UI/SwiftUI/Rows/SegmentedRowView.swift +++ b/openHAB/UI/SwiftUI/Rows/SegmentedRowView.swift @@ -169,7 +169,6 @@ private struct SegmentedRowContent: View { RoundedRectangle(cornerRadius: 7) .stroke(Color.secondary.opacity(0.3), lineWidth: 0.5) ) - .ohMinimumHitTarget() .fixedSize(horizontal: false, vertical: true) } @@ -216,7 +215,6 @@ private struct SegmentedRowContent: View { lineWidth: 0.5 ) ) - .ohMinimumHitTarget() .contentShape(Rectangle()) .onTapGesture { if !isSelected { @@ -318,7 +316,6 @@ private struct SegmentedRowContent: View { RoundedRectangle(cornerRadius: 7) .stroke(Color.secondary.opacity(0.3), lineWidth: 0.5) ) - .ohMinimumHitTarget() .contentShape(Rectangle()) .gesture( DragGesture(minimumDistance: 0) diff --git a/openHAB/UI/SwiftUI/Rows/SetpointRowView.swift b/openHAB/UI/SwiftUI/Rows/SetpointRowView.swift index 900e1d30c..29667f056 100644 --- a/openHAB/UI/SwiftUI/Rows/SetpointRowView.swift +++ b/openHAB/UI/SwiftUI/Rows/SetpointRowView.swift @@ -57,7 +57,6 @@ private struct SetpointRowContent: View { .foregroundStyle(currentValue <= displayState.minValue ? Color(.systemGray2) : Color(UIColor.systemBlue)) } .buttonStyle(.plain) - .ohMinimumHitTarget() .disabled(currentValue <= displayState.minValue) .sensoryHeavyFeedbackIfAvailable(trigger: triggerFeedback) .disabled(input.readOnly) @@ -76,7 +75,6 @@ private struct SetpointRowContent: View { .foregroundStyle(currentValue >= displayState.maxValue ? Color(.systemGray2) : Color(UIColor.systemBlue)) } .buttonStyle(.plain) - .ohMinimumHitTarget() .disabled(currentValue >= displayState.maxValue) .sensoryHeavyFeedbackIfAvailable(trigger: triggerFeedback) .disabled(input.readOnly) diff --git a/openHAB/UI/SwiftUI/SitemapNavigationView.swift b/openHAB/UI/SwiftUI/SitemapNavigationView.swift index 6cb872b35..a5fda5421 100644 --- a/openHAB/UI/SwiftUI/SitemapNavigationView.swift +++ b/openHAB/UI/SwiftUI/SitemapNavigationView.swift @@ -50,7 +50,6 @@ struct SitemapNavigationView: View { } label: { Image(systemSymbol: .magnifyingglass) } - .ohMinimumHitTarget() .accessibilityLabel("Search") } else { Button { @@ -59,7 +58,6 @@ struct SitemapNavigationView: View { } label: { Image(systemSymbol: .magnifyingglass) } - .ohMinimumHitTarget() .accessibilityLabel("Search") } } @@ -71,7 +69,6 @@ struct SitemapNavigationView: View { Image(systemSymbol: .line3Horizontal) .font(.title) } - .ohMinimumHitTarget() } } From 00267e7ce8c1ced52ae5418ca4ce4f6139947d2b Mon Sep 17 00:00:00 2001 From: openhab-bot Date: Wed, 15 Jul 2026 16:39:51 +0000 Subject: [PATCH 79/91] committed version bump: 3.4.11 (303) Signed-off-by: openhab-bot --- CHANGELOG.md | 81 ++++++++++++++++++++++++++++++++++++++++++++++++ Version.xcconfig | 4 +-- 2 files changed, 83 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78be5fc5b..982191e0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,87 @@ ## [Unreleased] +- Remove ohMinimumHitTarget() to reinstate consistent sitemap row heights (#1298) +- docs: expand ohMinimumHitTarget doc comment +- fix: guard against empty string in device:tts and empty action parts (#1295) +- singleMappingButton toggle button fix (#1297) +- singleMappingButton toggle button fix (#1296) +- committed version bump: 3.4.10 (302) +- fix: show inline error for inaccessible images and suppress connectivity alerts +- Minor widget layout adjustments +- fix: keep SSE stream alive across CarPlay page refreshes +- committed version bump: 3.4.9 (300) +- fix: complete missing localizations for 3 strings +- refactor: rename OpenHABIconOverlay to OpenHABIconWatermark and complete translations +- chore: switch main app target to CarPlay entitlements file +- fix: use LocalizedStringResource in AppIntents display representations +- fix: improve CarPlay robustness and localizations +- Widgets layout adjustments (#1292) +- committed version bump: 3.4.8 (298) +- fix: remove 7 stale localization keys with no source references +- fix: remove stale %lld %% localization keys for screensaver percentage labels +- Make Circular Accessory Switch icon state aware (#1291) +- committed version bump: 3.4.7 (297) +- refactor: replace remote icon fetch with SF symbol mapping in CarPlay +- feat: switch CarPlay to CPGridTemplate with openHAB icons +- fix: show placeholder instead of empty CPListTemplate when no compatible widgets +- refactor: migrate CarPlay streaming from long-poll to SSE +- feat: add CarPlay support with sitemap-based favorites scene +- Migrate sitemap page updates from long-polling to SSE (#1168) +- Small Widget background watermark alignment adjustments (#1289) +- fix: show message body below title in notification list +- fix: add Localizable.xcstrings to widget extension target membership +- fix: remove duplicate knownRegions from project.pbxproj +- committed version bump: 3.4.6 (296) +- Allow for two lines label and value for iOS Lock Screen Accessories (#1286) +- chore: apply linter fixes +- feat: add accessory widget previews for Switch and Sensor widgets +- feat: wide-display sitemap grid and full-width media rows +- refactor: remove UIImageView polling timer from MJPEG video stream +- committed version bump: 3.4.5 (294) +- Fix small widget content to be cntered +- committed version bump: 3.4.4 (293) +- committed version bump: 3.4.3 (292) +- fix: sync allowedItemTypes with query and add regression test +- feat: make Dimmer items selectable in Set Switch State intent +- chore: apply linter fixes +- fix: detect GIF images by magic bytes in ImageRowView +- committed version bump: 3.4.2 (290) +- fix: animate GIFs in the Image sitemap widget (#1280) +- Widget adjustments (#1279) +- fix: lock screen widgets now send commands on tap +- fix: notification ping now respects the ringer/silent switch (#1278) +- fix: add OpenHABShortcuts.swift to app target to resolve AppShortcuts.xcstrings phrase warnings +- committed version bump: 3.4.1 (287) +- fix: add provisioning profile for openHABWidgetExtension +- l10n: add Spanish, Finnish, Norwegian, and Russian translations +- l10n: switch German and French translations to informal address (du/tu) +- chore: remove 5 stale localization keys on feature branch +- l10n: add Spanish, Finnish, Norwegian, and Russian translations +- l10n: translate 2 new widget keys into all 9 languages +- chore: update openHABWidgetExtension scheme with askForAppToLaunch +- fix: restore project.pbxproj corrupted by bad 3-way merge of pbxproj +- chore: add shared schemes for openHABWidgetExtension and OpenHABWatchComplicationsExtension +- build: raise macOS minimum to 15 for AsyncSequence.Failure support +- chore: bump openHABTestsSwift deployment target to iOS 18.0 +- Merge develop + fix build warnings; add iOS home screen widgets with item display +- Sensor widget: format state values using stateDescription numberPattern +- Sensor widget: support 1/2/4 items per small/medium/large size +- Make ScrollToTop overlay button conditional (#1231) +- Bump minimum deployment targets to iOS 18 / watchOS 11 +- Use iOS 18 onScrollGeometryChange in SitemapPageView +- Use iOS 17 animation and scroll APIs +- Replace SetLocationValueIntent with SetActiveHomeIntent in AppShortcutsProvider +- Register SetActiveHomeIntent with AppShortcutsProvider +- Linting and AppIntents SSE warnings fixes +- Bump deployment targets to iOS 17.0 / watchOS 10.0 +- Convert AppShortcuts.strings to AppShortcuts.xcstrings String Catalog +- Fix AppIntentsMetadataProcessor synonym warnings in AppEnum cases +- Add AppShortcutsProvider and fix intent dialog gaps +- Upgrade SwiftFormatPlugin 0.58.7→0.61.1, SwiftLintPlugin 0.62.2→0.63.3 (#1225) +- ItemEventStream: backport SSE watchdog and self-contained network monitor +- openHABWidget: add interactive switch and sensor widgets + - fix: show inline error for inaccessible images and suppress connectivity alerts - Minor widget layout adjustments - fix: keep SSE stream alive across CarPlay page refreshes diff --git a/Version.xcconfig b/Version.xcconfig index 8898cc4fb..4fac3b07d 100644 --- a/Version.xcconfig +++ b/Version.xcconfig @@ -6,5 +6,5 @@ // https://developer.apple.com/documentation/xcode/adding-a-build-configuration-file-to-your-project -MARKETING_VERSION = 3.4.10 -CURRENT_PROJECT_VERSION = 302 +MARKETING_VERSION = 3.4.11 +CURRENT_PROJECT_VERSION = 303 From e331753eaa667c732840a737356fc2a6daa4b2d0 Mon Sep 17 00:00:00 2001 From: DigiH <17110652+DigiH@users.noreply.github.com> Date: Fri, 17 Jul 2026 16:15:18 +0200 Subject: [PATCH 80/91] SensorItemRow adjustments for consistent value font sizes (#1300) Signed-off-by: DigiH <17110652+DigiH@users.noreply.github.com> --- openHABWidget/SensorWidgetEntryView.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/openHABWidget/SensorWidgetEntryView.swift b/openHABWidget/SensorWidgetEntryView.swift index 5c182edc7..1bc4f702e 100644 --- a/openHABWidget/SensorWidgetEntryView.swift +++ b/openHABWidget/SensorWidgetEntryView.swift @@ -183,14 +183,12 @@ private struct SensorItemRow: View { .font(.subheadline) .fontWeight(.semibold) .lineLimit(2) - .layoutPriority(1) .frame(maxWidth: .infinity, alignment: .leading) .fixedSize(horizontal: false, vertical: true) Text(formattedState(for: item)) .font(.subheadline) .fontWeight(.semibold) .lineLimit(1) - .minimumScaleFactor(0.8) .foregroundStyle(.secondary) } } From 743ca5ee435d79228f616ef157de3be9b58a0f02 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Sat, 18 Jul 2026 07:14:06 +0200 Subject: [PATCH 81/91] fix: use CPInformationTemplate for CarPlay not-configured placeholder CPGridButton only supports a single text line, causing the setup instruction to be clipped on all display sizes and in all languages. Switch to CPInformationTemplate which renders the full text without truncation. Signed-off-by: Tim Mueller-Seydlitz --- openHAB/CarPlaySceneDelegate.swift | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/openHAB/CarPlaySceneDelegate.swift b/openHAB/CarPlaySceneDelegate.swift index a789a330d..d605c78ef 100644 --- a/openHAB/CarPlaySceneDelegate.swift +++ b/openHAB/CarPlaySceneDelegate.swift @@ -329,12 +329,15 @@ final class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegat ?? UIImage() } - private func placeholderTemplate(message: String? = nil) -> CPGridTemplate { - let label = message ?? String(localized: "carplay_not_configured") - let button = CPGridButton( - titleVariants: [label], - image: placeholderButtonImage() - ) { _ in } - return CPGridTemplate(title: "openHAB", gridButtons: [button]) + private func placeholderTemplate(message: String? = nil) -> CPTemplate { + guard let message else { + let button = CPGridButton( + titleVariants: [String(localized: "carplay_not_configured")], + image: placeholderButtonImage() + ) { _ in } + return CPGridTemplate(title: "openHAB", gridButtons: [button]) + } + let item = CPInformationItem(title: nil, detail: message) + return CPInformationTemplate(title: "openHAB", layout: .leading, items: [item], actions: []) } } From 63a55b8905b6afb3e3676a93c983600a38f069b7 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Sat, 18 Jul 2026 17:48:11 +0200 Subject: [PATCH 82/91] fix: widget visibility filtering and test-layer crash fixes - Add parentWidgetId to OpenHABWidget via ObjC associated object (no stored-property layout change) and propagate it in flatten(_:); this was required for frame-child visibility tracking without corrupting Combine's @Published field scan under concurrent test execution. - Extract visibility logic into sitemapVisibleWidgets(_:) so both SitemapPageViewModel.relevantWidgets and the tests call the same code path instead of a duplicated helper. - Fix frame visibility: empty frames (no children) are now hidden to match Android parity; previously only frames with children where all were invisible were hidden. - Fix #expect crash in PreviewWidgetFactoryTests: extract non-Sendable OpenHABWidget properties to local Sendable variables before #expect to avoid null-function-pointer crash from -enable-actor-data-race-checks. - Fix redundant try #require on already-unwrapped UIColor in NumberStateTests. - Add emptyFrameIsHidden test. - Remove stale FlexColorPicker pin from Package.resolved. Signed-off-by: Tim Mueller-Seydlitz --- .../PreviewWidgetFactoryTests.swift | 13 ++-- .../OpenHABCore/Model/OpenHABWidget.swift | 16 ++++- .../OpenHABCoreTests/NumberStateTests.swift | 3 +- .../xcshareddata/swiftpm/Package.resolved | 11 +-- ...itemapPageViewModel+InteractionState.swift | 30 +++++++- .../SitemapRowInputBuilderTests.swift | 72 +++++++++++++++++++ 6 files changed, 125 insertions(+), 20 deletions(-) diff --git a/CommonUI/Tests/CommonUITests/PreviewWidgetFactoryTests.swift b/CommonUI/Tests/CommonUITests/PreviewWidgetFactoryTests.swift index eedabe318..05f9952d6 100644 --- a/CommonUI/Tests/CommonUITests/PreviewWidgetFactoryTests.swift +++ b/CommonUI/Tests/CommonUITests/PreviewWidgetFactoryTests.swift @@ -49,9 +49,14 @@ struct PreviewWidgetFactoryTests { valueText: "Mode" ) - #expect(widget.type == .switchWidget) - #expect(widget.mappings.count == 2) - #expect(widget.item?.state == "OFF") - #expect(widget.labelValue == "Mode") + // OpenHABWidget is non-Sendable; intermediate sub-expression captures in #expect crash with -enable-actor-data-race-checks. + let widgetType = widget.type + let mappingCount = widget.mappings.count + let itemState = widget.item?.state + let labelValue = widget.labelValue + #expect(widgetType == .switchWidget) + #expect(mappingCount == 2) + #expect(itemState == "OFF") + #expect(labelValue == "Mode") } } diff --git a/OpenHABCore/Sources/OpenHABCore/Model/OpenHABWidget.swift b/OpenHABCore/Sources/OpenHABCore/Model/OpenHABWidget.swift index 0fe972fdc..28c7682fd 100644 --- a/OpenHABCore/Sources/OpenHABCore/Model/OpenHABWidget.swift +++ b/OpenHABCore/Sources/OpenHABCore/Model/OpenHABWidget.swift @@ -236,13 +236,25 @@ public extension OpenHABWidget { } } +/// Associated-object key for parentWidgetId — avoids changing OpenHABWidget's stored-property +/// layout, which would corrupt Combine's @Published field scan under concurrent test execution. +private nonisolated(unsafe) var parentWidgetIdKey: UInt8 = 0 + +public extension OpenHABWidget { + var parentWidgetId: String? { + get { objc_getAssociatedObject(self, &parentWidgetIdKey) as? String } + set { objc_setAssociatedObject(self, &parentWidgetIdKey, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC) } + } +} + /// Recursive parsing of nested widget structure public extension [OpenHABWidget] { - mutating func flatten(_ widgets: [Element]) { + mutating func flatten(_ widgets: [Element], parentWidgetId: String? = nil) { for widget in widgets { + widget.parentWidgetId = parentWidgetId append(widget) if widget.type != .buttongrid { - flatten(widget.widgets) + flatten(widget.widgets, parentWidgetId: widget.widgetId) } } } diff --git a/OpenHABCore/Tests/OpenHABCoreTests/NumberStateTests.swift b/OpenHABCore/Tests/OpenHABCoreTests/NumberStateTests.swift index f88d440fe..546ae5ce2 100644 --- a/OpenHABCore/Tests/OpenHABCoreTests/NumberStateTests.swift +++ b/OpenHABCore/Tests/OpenHABCoreTests/NumberStateTests.swift @@ -194,8 +194,7 @@ struct NumberStateTests { let col1 = try #require("Uninitialized".parseAsUIColor()) let col2 = UIColor(hue: 0, saturation: 0, brightness: 0, alpha: 1.0) - let unwrappedCol1 = try #require(col1) - #expect(unwrappedCol1.equals(col2)) + #expect(col1.equals(col2)) #expect("360,100,100".parseAsUIColor() == UIColor( hue: CGFloat(state: "360", divisor: 360), saturation: CGFloat(state: "100", divisor: 100), diff --git a/openHAB.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/openHAB.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index a53c02f77..0df97bf73 100644 --- a/openHAB.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/openHAB.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "6a0dbe66704e542f2c43f6f9ff3e1a2ae7244ffe448c8d1a8779c162f17fe8eb", + "originHash" : "0df9f0bce154b2c8b91d645cceab9c7d95cd8d47142bf41a3dccf34934bbc71e", "pins" : [ { "identity" : "abseil-cpp-binary", @@ -28,15 +28,6 @@ "version" : "12.6.0" } }, - { - "identity" : "flexcolorpicker", - "kind" : "remoteSourceControl", - "location" : "https://github.com/RastislavMirek/FlexColorPicker.git", - "state" : { - "revision" : "72a5c2c5e28074e6c5f13efe3c98eb780ae2f906", - "version" : "1.4.4" - } - }, { "identity" : "google-ads-on-device-conversion-ios-sdk", "kind" : "remoteSourceControl", diff --git a/openHAB/Models/SitemapPageViewModel+InteractionState.swift b/openHAB/Models/SitemapPageViewModel+InteractionState.swift index ed02b34f9..17f903006 100644 --- a/openHAB/Models/SitemapPageViewModel+InteractionState.swift +++ b/openHAB/Models/SitemapPageViewModel+InteractionState.swift @@ -11,12 +11,38 @@ import OpenHABCore +/// Filters a flat, pre-flattened widget list to only those that should be displayed, +/// mirroring Android's visibility logic: +/// - widget.visibility == false → hidden +/// - frame with no visible direct children (including empty frames) → hidden +/// - widget whose nearest frame ancestor is hidden → hidden +func sitemapVisibleWidgets(_ widgets: [OpenHABWidget]) -> [OpenHABWidget] { + var widgetsById: [String: OpenHABWidget] = [:] + for widget in widgets { + widgetsById[widget.widgetId] = widget + } + + func shouldShow(_ widget: OpenHABWidget) -> Bool { + guard widget.visibility else { return false } + if widget.type == .frame { + let children = widgets.filter { $0.parentWidgetId == widget.widgetId } + if !children.contains(where: \.visibility) { return false } + } + guard let parentId = widget.parentWidgetId, + let parent = widgetsById[parentId] else { return true } + return shouldShow(parent) + } + + return widgets.filter { shouldShow($0) } +} + @MainActor extension SitemapPageViewModel { var relevantWidgets: [OpenHABWidget] { let widgets = currentPage?.widgets ?? [] - guard !searchText.isEmpty else { return widgets } - return widgets.filter { + let visibleWidgets = sitemapVisibleWidgets(widgets) + guard !searchText.isEmpty else { return visibleWidgets } + return visibleWidgets.filter { $0.label.lowercased().contains(searchText.lowercased()) && $0.type != .frame } } diff --git a/openHABTestsSwift/SitemapRowInputBuilderTests.swift b/openHABTestsSwift/SitemapRowInputBuilderTests.swift index ce5dd2efc..e3eadaf0a 100644 --- a/openHABTestsSwift/SitemapRowInputBuilderTests.swift +++ b/openHABTestsSwift/SitemapRowInputBuilderTests.swift @@ -96,6 +96,70 @@ struct SitemapRowInputBuilderTests { #expect(updated.reusedInputCount == 0) #expect(updated.inputs.count == 2) } + + /// Regression (#1301): widgets with visibility=false must not appear in row inputs. + /// In long-polling (≤3.4.6) the server omitted invisible widgets from the payload, + /// so this was implicit. SSE (≥3.4.7) mutates widget.visibility in-place, requiring + /// relevantWidgets to apply shouldShowWidget logic before building. + @Test + func invisibleWidgetsAreExcludedFromRowInputs() { + let mode = makeTextWidget(widgetID: "mode", label: "Mode") + let fan = makeTextWidget(widgetID: "fan", label: "Fan") + fan.visibility = false + + let result = buildInitial(pageKey: "default|home", widgets: sitemapVisibleWidgets([mode, fan])) + + #expect(result.inputs.count == 1) + #expect(result.rowIDs[0].widgetId == "mode") + } + + @Test + func sseVisibilityEventExcludesWidgetFromRowInputs() { + let mode = makeTextWidget(widgetID: "mode", label: "Mode") + let fan = makeTextWidget(widgetID: "fan", label: "Fan") + + // SSE delivers visibility=false for fan when mode selection changes + fan.apply(event: OpenHABSitemapWidgetEvent(widgetId: "fan", visibility: false)) + + let result = buildInitial(pageKey: "default|home", widgets: sitemapVisibleWidgets([mode, fan])) + + #expect(result.inputs.count == 1) + #expect(result.rowIDs[0].widgetId == "mode") + } + + @Test + func frameWithNoVisibleChildrenIsHidden() { + let frame = makeFrameWidget(widgetID: "frame-1", label: "HVAC") + let child = makeTextWidget(widgetID: "fan", label: "Fan") + child.parentWidgetId = "frame-1" + child.visibility = false + + let result = buildInitial(pageKey: "default|home", widgets: sitemapVisibleWidgets([frame, child])) + + #expect(result.inputs.isEmpty) + } + + @Test + func emptyFrameIsHidden() { + let frame = makeFrameWidget(widgetID: "frame-1", label: "HVAC") + + let result = buildInitial(pageKey: "default|home", widgets: sitemapVisibleWidgets([frame])) + + #expect(result.inputs.isEmpty) + } + + @Test + func childOfInvisibleFrameIsHiddenEvenWhenOwnVisibilityIsTrue() { + let frame = makeFrameWidget(widgetID: "frame-1", label: "HVAC") + frame.visibility = false + let child = makeTextWidget(widgetID: "fan", label: "Fan") + child.parentWidgetId = "frame-1" + child.visibility = true + + let result = buildInitial(pageKey: "default|home", widgets: sitemapVisibleWidgets([frame, child])) + + #expect(result.inputs.isEmpty) + } } private extension SitemapRowInputBuilderTests { @@ -123,6 +187,14 @@ private extension SitemapRowInputBuilderTests { return widget } + func makeFrameWidget(widgetID: String, label: String) -> OpenHABWidget { + let widget = OpenHABWidget() + widget.widgetId = widgetID + widget.type = .frame + widget.label = label + return widget + } + func makeButtonGridWidget(widgetID: String, children: [OpenHABWidget]) -> OpenHABWidget { let widget = OpenHABWidget() widget.widgetId = widgetID From 48a8905dbd35f3875c72ada9f68044f2505423dd Mon Sep 17 00:00:00 2001 From: openhab-bot Date: Sat, 18 Jul 2026 16:24:19 +0000 Subject: [PATCH 83/91] committed version bump: 3.4.12 (306) Signed-off-by: openhab-bot --- CHANGELOG.md | 84 ++++++++++++++++++++++++++++++++++++++++++++++++ Version.xcconfig | 4 +-- 2 files changed, 86 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ea157bf7..497756571 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,90 @@ ## [Unreleased] +- fix: widget visibility filtering and test-layer crash fixes +- fix: use CPInformationTemplate for CarPlay not-configured placeholder +- SensorItemRow adjustments for consistent value font sizes (#1300) +- committed version bump: 3.3.9 (305) +- committed version bump: 3.4.11 (303) +- Remove ohMinimumHitTarget() to reinstate consistent sitemap row heights (#1298) +- docs: expand ohMinimumHitTarget doc comment +- singleMappingButton toggle button fix (#1296) +- committed version bump: 3.4.10 (302) +- fix: show inline error for inaccessible images and suppress connectivity alerts +- Minor widget layout adjustments +- fix: keep SSE stream alive across CarPlay page refreshes +- committed version bump: 3.4.9 (300) +- fix: complete missing localizations for 3 strings +- refactor: rename OpenHABIconOverlay to OpenHABIconWatermark and complete translations +- chore: switch main app target to CarPlay entitlements file +- fix: use LocalizedStringResource in AppIntents display representations +- fix: improve CarPlay robustness and localizations +- Widgets layout adjustments (#1292) +- committed version bump: 3.4.8 (298) +- fix: remove 7 stale localization keys with no source references +- fix: remove stale %lld %% localization keys for screensaver percentage labels +- Make Circular Accessory Switch icon state aware (#1291) +- committed version bump: 3.4.7 (297) +- refactor: replace remote icon fetch with SF symbol mapping in CarPlay +- feat: switch CarPlay to CPGridTemplate with openHAB icons +- fix: show placeholder instead of empty CPListTemplate when no compatible widgets +- refactor: migrate CarPlay streaming from long-poll to SSE +- feat: add CarPlay support with sitemap-based favorites scene +- Migrate sitemap page updates from long-polling to SSE (#1168) +- Small Widget background watermark alignment adjustments (#1289) +- fix: show message body below title in notification list +- fix: add Localizable.xcstrings to widget extension target membership +- fix: remove duplicate knownRegions from project.pbxproj +- committed version bump: 3.4.6 (296) +- Allow for two lines label and value for iOS Lock Screen Accessories (#1286) +- chore: apply linter fixes +- feat: add accessory widget previews for Switch and Sensor widgets +- feat: wide-display sitemap grid and full-width media rows +- refactor: remove UIImageView polling timer from MJPEG video stream +- committed version bump: 3.4.5 (294) +- Fix small widget content to be cntered +- committed version bump: 3.4.4 (293) +- committed version bump: 3.4.3 (292) +- fix: sync allowedItemTypes with query and add regression test +- feat: make Dimmer items selectable in Set Switch State intent +- chore: apply linter fixes +- fix: detect GIF images by magic bytes in ImageRowView +- committed version bump: 3.4.2 (290) +- fix: animate GIFs in the Image sitemap widget (#1280) +- Widget adjustments (#1279) +- fix: lock screen widgets now send commands on tap +- fix: notification ping now respects the ringer/silent switch (#1278) +- fix: add OpenHABShortcuts.swift to app target to resolve AppShortcuts.xcstrings phrase warnings +- committed version bump: 3.4.1 (287) +- fix: add provisioning profile for openHABWidgetExtension +- l10n: add Spanish, Finnish, Norwegian, and Russian translations +- l10n: switch German and French translations to informal address (du/tu) +- chore: remove 5 stale localization keys on feature branch +- l10n: add Spanish, Finnish, Norwegian, and Russian translations +- l10n: translate 2 new widget keys into all 9 languages +- chore: update openHABWidgetExtension scheme with askForAppToLaunch +- fix: restore project.pbxproj corrupted by bad 3-way merge of pbxproj +- chore: add shared schemes for openHABWidgetExtension and OpenHABWatchComplicationsExtension +- build: raise macOS minimum to 15 for AsyncSequence.Failure support +- chore: bump openHABTestsSwift deployment target to iOS 18.0 +- Merge develop + fix build warnings; add iOS home screen widgets with item display +- Sensor widget: format state values using stateDescription numberPattern +- Sensor widget: support 1/2/4 items per small/medium/large size +- Make ScrollToTop overlay button conditional (#1231) +- Bump minimum deployment targets to iOS 18 / watchOS 11 +- Use iOS 18 onScrollGeometryChange in SitemapPageView +- Use iOS 17 animation and scroll APIs +- Replace SetLocationValueIntent with SetActiveHomeIntent in AppShortcutsProvider +- Register SetActiveHomeIntent with AppShortcutsProvider +- Linting and AppIntents SSE warnings fixes +- Bump deployment targets to iOS 17.0 / watchOS 10.0 +- Convert AppShortcuts.strings to AppShortcuts.xcstrings String Catalog +- Fix AppIntentsMetadataProcessor synonym warnings in AppEnum cases +- Add AppShortcutsProvider and fix intent dialog gaps +- Upgrade SwiftFormatPlugin 0.58.7→0.61.1, SwiftLintPlugin 0.62.2→0.63.3 (#1225) +- ItemEventStream: backport SSE watchdog and self-contained network monitor +- openHABWidget: add interactive switch and sensor widgets + - Remove ohMinimumHitTarget() to reinstate consistent sitemap row heights (#1298) - docs: expand ohMinimumHitTarget doc comment - fix: guard against empty string in device:tts and empty action parts (#1295) diff --git a/Version.xcconfig b/Version.xcconfig index 4fac3b07d..61fd12348 100644 --- a/Version.xcconfig +++ b/Version.xcconfig @@ -6,5 +6,5 @@ // https://developer.apple.com/documentation/xcode/adding-a-build-configuration-file-to-your-project -MARKETING_VERSION = 3.4.11 -CURRENT_PROJECT_VERSION = 303 +MARKETING_VERSION = 3.4.12 +CURRENT_PROJECT_VERSION = 306 From 3c198b9ea8c175c558b949ca13d56a79c8b7b979 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Sun, 19 Jul 2026 09:39:45 +0200 Subject: [PATCH 84/91] feat(carplay): restrict widgets to plain switches and press-release buttons - Drop .button and .selection from CarPlay compatibility; neither type appears at page level in practice. - Remove pushMappingList and segmented-switch handling; mapping lists are not a good fit for the CarPlay grid interaction model. - Add press-release support: plain switch toggles ON/OFF; a switch with a single mapping carrying hasPressReleaseBehavior sends the press command, sleeps 500 ms, then sends the release command; a switch with widget-level releaseCommand is handled the same way. - Exclude itemless Switch widgets (used as sitemap frames/containers) from the CarPlay grid via a widget.item != nil guard. Signed-off-by: Tim Mueller-Seydlitz --- openHAB/CarPlaySceneDelegate.swift | 60 +++++++++++------------------- 1 file changed, 22 insertions(+), 38 deletions(-) diff --git a/openHAB/CarPlaySceneDelegate.swift b/openHAB/CarPlaySceneDelegate.swift index d605c78ef..d2b5fe258 100644 --- a/openHAB/CarPlaySceneDelegate.swift +++ b/openHAB/CarPlaySceneDelegate.swift @@ -263,10 +263,9 @@ final class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegat } private func isCarPlayCompatible(_ widget: OpenHABWidget) -> Bool { - switch widget.type { - case .switchWidget, .button, .selection: true - default: false - } + guard widget.type == .switchWidget, widget.item != nil else { return false } + return widget.mappings.isEmpty || + (widget.mappings.count == 1 && widget.mappings[0].hasPressReleaseBehavior) } private func carPlayGridButtonImageSize() -> CGSize { @@ -281,45 +280,30 @@ final class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegat private func makeGridButton(for widget: OpenHABWidget, service: OpenAPIService) -> CPGridButton { let ds = widget.displayState let image = sfSymbol(for: widget) - return CPGridButton(titleVariants: [ds.labelText], image: image) { [weak self] _ in - guard let self else { return } - switch widget.type { - case .switchWidget where !ds.mappings.isEmpty, .selection: - pushMappingList(title: ds.labelText, mappings: ds.mappings, widget: widget, service: service) - case .switchWidget: - Task { - guard let name = widget.item?.name else { return } - try? await service.sendItemCommand(itemname: name, command: ds.isOn ? "OFF" : "ON") + return CPGridButton(titleVariants: [ds.labelText], image: image) { _ in + Task { + guard let name = widget.item?.name else { return } + // Resolve press/release source: single mapping takes precedence over widget-level fields + let pressCommand: String? + let releaseCommand: String? + if let mapping = widget.mappings.first, mapping.hasPressReleaseBehavior { + pressCommand = mapping.command.isEmpty ? nil : mapping.command + releaseCommand = mapping.releaseCommand + } else { + pressCommand = widget.releaseOnly != true ? widget.command : nil + releaseCommand = widget.releaseCommand } - case .button: - Task { - guard let name = widget.item?.name, let command = widget.command else { return } - try? await service.sendItemCommand(itemname: name, command: command) + if let pressCommand { + try? await service.sendItemCommand(itemname: name, command: pressCommand) + try? await Task.sleep(for: .milliseconds(500)) } - default: - break - } - } - } - - private func pushMappingList(title: String, - mappings: [OpenHABWidgetMapping], - widget: OpenHABWidget, - service: OpenAPIService) { - let items = mappings.map { mapping -> CPListItem in - let item = CPListItem(text: mapping.label, detailText: nil) - item.handler = { [weak self] _, done in - Task { - guard let name = widget.item?.name else { return } - try? await service.sendItemCommand(itemname: name, command: mapping.command) + if let releaseCommand { + try? await service.sendItemCommand(itemname: name, command: releaseCommand) + } else { + try? await service.sendItemCommand(itemname: name, command: ds.isOn ? "OFF" : "ON") } - self?.interfaceController?.popTemplate(animated: true, completion: nil) - done() } - return item } - let template = CPListTemplate(title: title, sections: [CPListSection(items: items)]) - interfaceController?.pushTemplate(template, animated: true, completion: nil) } private func placeholderButtonImage() -> UIImage { From e75214dab4d01ee8ac77885ab92a83cca55db2a6 Mon Sep 17 00:00:00 2001 From: openhab-bot Date: Sun, 19 Jul 2026 16:49:59 +0000 Subject: [PATCH 85/91] committed version bump: 3.4.13 (307) Signed-off-by: openhab-bot --- CHANGELOG.md | 86 ++++++++++++++++++++++++++++++++++++++++++++++++ Version.xcconfig | 4 +-- 2 files changed, 88 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 497756571..47c3c8aab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,92 @@ ## [Unreleased] +- feat(carplay): restrict widgets to plain switches and press-release buttons +- committed version bump: 3.4.12 (306) +- fix: widget visibility filtering and test-layer crash fixes +- fix: use CPInformationTemplate for CarPlay not-configured placeholder +- SensorItemRow adjustments for consistent value font sizes (#1300) +- committed version bump: 3.3.9 (305) +- committed version bump: 3.4.11 (303) +- Remove ohMinimumHitTarget() to reinstate consistent sitemap row heights (#1298) +- docs: expand ohMinimumHitTarget doc comment +- singleMappingButton toggle button fix (#1296) +- committed version bump: 3.4.10 (302) +- fix: show inline error for inaccessible images and suppress connectivity alerts +- Minor widget layout adjustments +- fix: keep SSE stream alive across CarPlay page refreshes +- committed version bump: 3.4.9 (300) +- fix: complete missing localizations for 3 strings +- refactor: rename OpenHABIconOverlay to OpenHABIconWatermark and complete translations +- chore: switch main app target to CarPlay entitlements file +- fix: use LocalizedStringResource in AppIntents display representations +- fix: improve CarPlay robustness and localizations +- Widgets layout adjustments (#1292) +- committed version bump: 3.4.8 (298) +- fix: remove 7 stale localization keys with no source references +- fix: remove stale %lld %% localization keys for screensaver percentage labels +- Make Circular Accessory Switch icon state aware (#1291) +- committed version bump: 3.4.7 (297) +- refactor: replace remote icon fetch with SF symbol mapping in CarPlay +- feat: switch CarPlay to CPGridTemplate with openHAB icons +- fix: show placeholder instead of empty CPListTemplate when no compatible widgets +- refactor: migrate CarPlay streaming from long-poll to SSE +- feat: add CarPlay support with sitemap-based favorites scene +- Migrate sitemap page updates from long-polling to SSE (#1168) +- Small Widget background watermark alignment adjustments (#1289) +- fix: show message body below title in notification list +- fix: add Localizable.xcstrings to widget extension target membership +- fix: remove duplicate knownRegions from project.pbxproj +- committed version bump: 3.4.6 (296) +- Allow for two lines label and value for iOS Lock Screen Accessories (#1286) +- chore: apply linter fixes +- feat: add accessory widget previews for Switch and Sensor widgets +- feat: wide-display sitemap grid and full-width media rows +- refactor: remove UIImageView polling timer from MJPEG video stream +- committed version bump: 3.4.5 (294) +- Fix small widget content to be cntered +- committed version bump: 3.4.4 (293) +- committed version bump: 3.4.3 (292) +- fix: sync allowedItemTypes with query and add regression test +- feat: make Dimmer items selectable in Set Switch State intent +- chore: apply linter fixes +- fix: detect GIF images by magic bytes in ImageRowView +- committed version bump: 3.4.2 (290) +- fix: animate GIFs in the Image sitemap widget (#1280) +- Widget adjustments (#1279) +- fix: lock screen widgets now send commands on tap +- fix: notification ping now respects the ringer/silent switch (#1278) +- fix: add OpenHABShortcuts.swift to app target to resolve AppShortcuts.xcstrings phrase warnings +- committed version bump: 3.4.1 (287) +- fix: add provisioning profile for openHABWidgetExtension +- l10n: add Spanish, Finnish, Norwegian, and Russian translations +- l10n: switch German and French translations to informal address (du/tu) +- chore: remove 5 stale localization keys on feature branch +- l10n: add Spanish, Finnish, Norwegian, and Russian translations +- l10n: translate 2 new widget keys into all 9 languages +- chore: update openHABWidgetExtension scheme with askForAppToLaunch +- fix: restore project.pbxproj corrupted by bad 3-way merge of pbxproj +- chore: add shared schemes for openHABWidgetExtension and OpenHABWatchComplicationsExtension +- build: raise macOS minimum to 15 for AsyncSequence.Failure support +- chore: bump openHABTestsSwift deployment target to iOS 18.0 +- Merge develop + fix build warnings; add iOS home screen widgets with item display +- Sensor widget: format state values using stateDescription numberPattern +- Sensor widget: support 1/2/4 items per small/medium/large size +- Make ScrollToTop overlay button conditional (#1231) +- Bump minimum deployment targets to iOS 18 / watchOS 11 +- Use iOS 18 onScrollGeometryChange in SitemapPageView +- Use iOS 17 animation and scroll APIs +- Replace SetLocationValueIntent with SetActiveHomeIntent in AppShortcutsProvider +- Register SetActiveHomeIntent with AppShortcutsProvider +- Linting and AppIntents SSE warnings fixes +- Bump deployment targets to iOS 17.0 / watchOS 10.0 +- Convert AppShortcuts.strings to AppShortcuts.xcstrings String Catalog +- Fix AppIntentsMetadataProcessor synonym warnings in AppEnum cases +- Add AppShortcutsProvider and fix intent dialog gaps +- Upgrade SwiftFormatPlugin 0.58.7→0.61.1, SwiftLintPlugin 0.62.2→0.63.3 (#1225) +- ItemEventStream: backport SSE watchdog and self-contained network monitor +- openHABWidget: add interactive switch and sensor widgets + - fix: widget visibility filtering and test-layer crash fixes - fix: use CPInformationTemplate for CarPlay not-configured placeholder - SensorItemRow adjustments for consistent value font sizes (#1300) diff --git a/Version.xcconfig b/Version.xcconfig index 61fd12348..2a49e7d01 100644 --- a/Version.xcconfig +++ b/Version.xcconfig @@ -6,5 +6,5 @@ // https://developer.apple.com/documentation/xcode/adding-a-build-configuration-file-to-your-project -MARKETING_VERSION = 3.4.12 -CURRENT_PROJECT_VERSION = 306 +MARKETING_VERSION = 3.4.13 +CURRENT_PROJECT_VERSION = 307 From c78274617830696968108cd8f5557803f22245a7 Mon Sep 17 00:00:00 2001 From: DigiH <17110652+DigiH@users.noreply.github.com> Date: Sun, 19 Jul 2026 19:34:26 +0200 Subject: [PATCH 86/91] Extend CarPlay icons (#1302) Signed-off-by: DigiH <17110652+DigiH@users.noreply.github.com> --- openHAB/CarPlaySceneDelegate.swift | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/openHAB/CarPlaySceneDelegate.swift b/openHAB/CarPlaySceneDelegate.swift index d2b5fe258..90320e2f8 100644 --- a/openHAB/CarPlaySceneDelegate.swift +++ b/openHAB/CarPlaySceneDelegate.swift @@ -245,12 +245,16 @@ final class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegat let pointSize = min(targetSize.width, targetSize.height) let isOn = widget.displayState.isOn - let symbolName: SFSymbol = switch widget.icon.lowercased() { - case "power", "poweroutlet", "poweroutlet_eu": + let icon = widget.icon.lowercased() + let symbolName: SFSymbol = if icon.contains("power") { isOn ? .powerCircleFill : .powerCircle - case "lamp", "light", "lightbulb": + } else if icon.contains("lamp") || icon.contains("light") || icon.contains("bulb") { isOn ? .lightbulbFill : .lightbulb - default: + } else if icon.contains("lock") || icon.contains("security") || icon.contains("alarm") { + isOn ? .lockFill : .lockOpen + } else if icon.contains("home") || icon.contains("house") { + isOn ? .houseFill : .house + } else { isOn ? .circleFill : .circle } From 40abeac50d946f355c527399bfcb73f1117e83fa Mon Sep 17 00:00:00 2001 From: DigiH <17110652+DigiH@users.noreply.github.com> Date: Mon, 20 Jul 2026 06:59:17 +0200 Subject: [PATCH 87/91] CarPlay placeholder text adjustments (#1303) Signed-off-by: DigiH <17110652+DigiH@users.noreply.github.com> --- .../Supporting Files/Localizable.xcstrings | 30012 ++++++++-------- 1 file changed, 15006 insertions(+), 15006 deletions(-) diff --git a/openHAB/Supporting Files/Localizable.xcstrings b/openHAB/Supporting Files/Localizable.xcstrings index 9fb0b7029..28e29a980 100644 --- a/openHAB/Supporting Files/Localizable.xcstrings +++ b/openHAB/Supporting Files/Localizable.xcstrings @@ -1,22563 +1,22563 @@ { - "sourceLanguage": "en", - "strings": { - "": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "" + "sourceLanguage" : "en", + "strings" : { + "" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "" } } }, - "shouldTranslate": false + "shouldTranslate" : false }, - " - ": { - "comment": "A separator text displayed between the temperature value and its description in the color temperature picker row.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": " - " + " - " : { + "comment" : "A separator text displayed between the temperature value and its description in the color temperature picker row.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : " - " } }, - "en": { - "stringUnit": { - "state": "translated", - "value": " - " + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : " - " } }, - "es": { - "stringUnit": { - "state": "translated", - "value": " - " + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : " - " } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": " - " + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : " - " } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": " - " + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : " - " } }, - "it": { - "stringUnit": { - "state": "translated", - "value": " - " + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : " - " } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": " - " + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : " - " } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": " - " + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : " - " } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": " - " + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : " - " } } } }, - "__item_state_passthrough__": { - "comment": "Placeholder for a numeric or custom item state that should be displayed as-is.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "%@" + "__item_state_passthrough__" : { + "comment" : "Placeholder for a numeric or custom item state that should be displayed as-is.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "%@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "%@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "%@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "%@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "%@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "%@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "%@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "%@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } } } }, - "/overview/": { - "comment": "A placeholder text in the text field of the default path setting.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "/overview/" + "/overview/" : { + "comment" : "A placeholder text in the text field of the default path setting.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "/overview/" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "/overview/" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "/overview/" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "/overview/" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "/overview/" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "/overview/" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "/overview/" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "/overview/" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "/overview/" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "/overview/" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "/overview/" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "/overview/" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "/overview/" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "/overview/" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "/overview/" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "/overview/" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "/overview/" } } } }, - "%@": { - "comment": "A label that shows the percentage of brightness. The value is rounded to the nearest whole number.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "%@" + "%@" : { + "comment" : "A label that shows the percentage of brightness. The value is rounded to the nearest whole number.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "%@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "%@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "%@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "%@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "%@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "%@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "%@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } } } }, - "%@ • %@": { - "comment": "The item name and the name of the home the item belongs to.", - "isCommentAutoGenerated": true, - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "%1$@ • %2$@" + "%@ • %@" : { + "comment" : "The item name and the name of the home the item belongs to.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ • %2$@" } }, - "de": { - "stringUnit": { - "state": "translated", - "value": "%1$@ • %2$@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ • %2$@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "%1$@ • %2$@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ • %2$@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "%1$@ • %2$@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ • %2$@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "%1$@ • %2$@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ • %2$@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "%1$@ • %2$@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ • %2$@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "%1$@ • %2$@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ • %2$@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "%1$@ • %2$@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ • %2$@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "%1$@ • %2$@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ • %2$@" } } } }, - "%@ Settings": { - "comment": "The title of the settings view. The placeholder is replaced with the user's home name.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "%@-Einstellungen" + "%@ Settings" : { + "comment" : "The title of the settings view. The placeholder is replaced with the user's home name.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@-Einstellungen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "%@ Settings" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ Settings" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Ajustes de %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajustes de %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "%@-asetukset" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@-asetukset" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Paramètres %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Paramètres %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Impostazioni %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impostazioni %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "%@-innstillinger" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@-innstillinger" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "%@ Instellingen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ Instellingen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Настройки %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Настройки %@" } } } }, - "%@ value": { - "comment": "A text field displaying the current value of a setpoint.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "%@ Wert" + "%@ value" : { + "comment" : "A text field displaying the current value of a setpoint.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ Wert" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "%@ value" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ value" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Valor de %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valor de %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "%@-arvo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@-arvo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Valeur %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valeur %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Valore %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valore %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "%@-verdi" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@-verdi" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "%@ waarde" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ waarde" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Значение %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Значение %@" } } } }, - "%@: %@": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "%1$@: %2$@" + "%@: %@" : { + "comment" : "A label displaying the name of a sensor with its current value. The first argument is the label of the sensor. The second argument is the value of the sensor.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@: %2$@" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "%1$@: %2$@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@: %2$@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "%1$@: %2$@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@: %2$@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "%1$@: %2$@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@: %2$@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "%1$@ : %2$@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ : %2$@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "%1$@: %2$@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@: %2$@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "%1$@: %2$@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@: %2$@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "%1$@: %2$@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@: %2$@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "%1$@: %2$@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@: %2$@" } } - }, - "comment": "A label displaying the name of a sensor with its current value. The first argument is the label of the sensor. The second argument is the value of the sensor.", - "isCommentAutoGenerated": true + } }, - "%lld": { - "comment": "A label indicating that there are multiple commands currently being sent to the device. The number in the parentheses is the count of these commands.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "%lld" + "%lld" : { + "comment" : "A label indicating that there are multiple commands currently being sent to the device. The number in the parentheses is the count of these commands.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "%lld" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "%lld" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "%lld" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "%lld" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "%lld" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "%lld" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "%lld" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "%lld" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld" } } } }, - "%lldK": { - "comment": "A text view displaying the selected color temperature in Kelvin. The value is shown in a smaller font next to a separator text (\" - \").", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "%lldK" + "%lldK" : { + "comment" : "A text view displaying the selected color temperature in Kelvin. The value is shown in a smaller font next to a separator text (\" - \").", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lldK" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "%lldK" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lldK" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "%lldK" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lldK" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "%lldK" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lldK" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "%lldK" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lldK" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "%lldK" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lldK" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "%lldK" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lldK" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "%lldK" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lldK" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "%lldK" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lldK" } } } }, - "24-Hour Clock": { - "comment": "A toggle that allows the user to switch between 12-hour and 24-hour time formats.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "24-Stunden Uhr" + "24-Hour Clock" : { + "comment" : "A toggle that allows the user to switch between 12-hour and 24-hour time formats.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "24-Stunden Uhr" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "24-Hour Clock" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "24-Hour Clock" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Reloj de 24 horas" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reloj de 24 horas" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "24 tunnin kello" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "24 tunnin kello" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Horloge 24 heures" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Horloge 24 heures" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Orologio 24 ore" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Orologio 24 ore" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "24-timers klokke" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "24-timers klokke" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "24-uurs klok" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "24-uurs klok" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "24-часовые часы" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "24-часовые часы" } } } }, - "about_settings": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Über" + "about_settings" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Über" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "About" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "About" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Acerca de" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Acerca de" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "About" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "About" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "A propos" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "A propos" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Info" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Info" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Om" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Om" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Over" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Over" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "About" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "About" } } } }, - "Accepted Server Certificates": { - "comment": "A link to view and manage the user's accepted server certificates.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Akzeptierte Server-Zertifikate" + "Accepted Server Certificates" : { + "comment" : "A link to view and manage the user's accepted server certificates.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Akzeptierte Server-Zertifikate" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Accepted Server Certificates" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Accepted Server Certificates" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Certificados de servidor aceptados" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Certificados de servidor aceptados" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Hyväksytyt palvelinvarmenteet" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hyväksytyt palvelinvarmenteet" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Certificats serveur acceptés" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Certificats serveur acceptés" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Certificati server accettati" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Certificati server accettati" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Godkjente serversertifikater" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Godkjente serversertifikater" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Geaccepteerde servercertificaten" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geaccepteerde servercertificaten" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Принятые сертификаты сервера" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Принятые сертификаты сервера" } } } }, - "Action": { - "comment": "Parameter title for an action in app intents.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Aktion" + "Action" : { + "comment" : "Parameter title for an action in app intents.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktion" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Action" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Action" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Acción" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Acción" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Toiminto" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toiminto" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Action" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Action" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Azione" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Azione" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Handling" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Handling" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Actie" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actie" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Действие" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Действие" } } } }, - "activate": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Aktivieren" + "activate" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktivieren" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Activate" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activate" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Activar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Activate" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activate" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Activer" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activer" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Attiva" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Attiva" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Aktiver" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktiver" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Activeren" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activeren" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Activate" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activate" } } } }, - "Active": { - "comment": "Display name for the contact state \"Open\".", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Aktiv" + "Active" : { + "comment" : "Display name for the contact state \"Open\".", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktiv" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Activo" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activo" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aktiivinen" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktiivinen" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Actif" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actif" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Attivo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Attivo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Aktiv" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktiv" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Actief" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actief" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Активен" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Активен" } } } }, - "active_url": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Aktive URL" + "active_url" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktive URL" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Active URL" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Active URL" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "URL activa" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL activa" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Active URL" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Active URL" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "URL active" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL active" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "URL Attivo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL Attivo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Aktiv URL" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktiv URL" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Actieve URL" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actieve URL" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Активный URL-адрес" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Активный URL-адрес" } } } }, - "Added: %@": { - "comment": "A label displaying the date and time when a server certificate was added to the app.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Hinzugefügt am: %@" + "Added: %@" : { + "comment" : "A label displaying the date and time when a server certificate was added to the app.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hinzugefügt am: %@" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Added: %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Added: %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Añadido: %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Añadido: %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Lisätty: %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lisätty: %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Ajouté : %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajouté : %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Aggiunto: %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aggiunto: %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Lagt til: %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lagt til: %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Toegevoegd: %@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toegevoegd: %@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Добавлено: %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Добавлено: %@" } } } }, - "Always": { - "comment": "Button title for allowing the app to use the invalid server certificate, even if it is self-signed.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Immer" + "Always" : { + "comment" : "Button title for allowing the app to use the invalid server certificate, even if it is self-signed.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Immer" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Always" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Always" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Siempre" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Siempre" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aina" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aina" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Toujours" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toujours" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Sempre" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sempre" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Alltid" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alltid" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Altijd" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Altijd" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Всегда" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Всегда" } } } }, - "Always allow WebRTC": { - "comment": "A toggle that allows the user to enable or disable WebRTC.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "WebRTC immer erlauben" + "Always allow WebRTC" : { + "comment" : "A toggle that allows the user to enable or disable WebRTC.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "WebRTC immer erlauben" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Always allow WebRTC" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Always allow WebRTC" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Permitir WebRTC siempre" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Permitir WebRTC siempre" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Salli WebRTC aina" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Salli WebRTC aina" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Toujours autoriser WebRTC" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toujours autoriser WebRTC" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Consenti sempre WebRTC" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Consenti sempre WebRTC" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Tillat alltid WebRTC" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tillat alltid WebRTC" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "WebRTC altijd toestaan" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "WebRTC altijd toestaan" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Всегда разрешать WebRTC" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Всегда разрешать WebRTC" } } } }, - "Always send credentials": { - "comment": "A toggle that lets the user specify whether to always send basic authentication credentials with requests.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Anmeldedaten immer senden" + "Always send credentials" : { + "comment" : "A toggle that lets the user specify whether to always send basic authentication credentials with requests.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anmeldedaten immer senden" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Always send credentials" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Always send credentials" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Enviar credenciales siempre" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviar credenciales siempre" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Lähetä tunnistetiedot aina" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lähetä tunnistetiedot aina" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Toujours envoyer les identifiants" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toujours envoyer les identifiants" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Invia sempre le credenziali" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Invia sempre le credenziali" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Send alltid legitimasjon" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Send alltid legitimasjon" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Inloggegevens altijd verzenden" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inloggegevens altijd verzenden" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Всегда отправлять учётные данные" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Всегда отправлять учётные данные" } } } }, - "always_allow_webrtc": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "WebRTC immer erlauben" + "always_allow_webrtc" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "WebRTC immer erlauben" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Always Allow WebRTC" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Always Allow WebRTC" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Consenti sempre WebRTC" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Consenti sempre WebRTC" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Always Allow WebRTC" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Always Allow WebRTC" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Toujours autoriser WebRTC" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toujours autoriser WebRTC" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Consenti sempre WebRTC" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Consenti sempre WebRTC" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Always Allow WebRTC" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Always Allow WebRTC" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "WebRTC altijd toestaan" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "WebRTC altijd toestaan" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Always Allow WebRTC" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Always Allow WebRTC" } } } }, - "always_send_credentials": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Anmeldedaten immer senden" + "always_send_credentials" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anmeldedaten immer senden" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Always send credentials" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Always send credentials" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Siempre enviar credenciales" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Siempre enviar credenciales" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Always send credentials" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Always send credentials" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Toujours envoyer les identifiants" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toujours envoyer les identifiants" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Invia sempre le credenziali" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Invia sempre le credenziali" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Alltid send innloggingsinformasjon" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alltid send innloggingsinformasjon" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Altijd inloggegevens versturen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Altijd inloggegevens versturen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Always send credentials" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Always send credentials" } } } }, - "Animation": { - "comment": "A section header for the animation settings in the screen saver view.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Animation" + "Animation" : { + "comment" : "A section header for the animation settings in the screen saver view.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Animation" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Animation" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Animation" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Animación" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Animación" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Animaatio" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Animaatio" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Animation" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Animation" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Animazione" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Animazione" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Animasjon" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Animasjon" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Animatie" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Animatie" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Анимация" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Анимация" } } } }, - "App Version": { - "comment": "The label and value for the application version in the \"About\" settings view.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "App-Version" + "App Version" : { + "comment" : "The label and value for the application version in the \"About\" settings view.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "App-Version" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "App Version" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "App Version" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Versión de la aplicación" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Versión de la aplicación" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Sovelluksen versio" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sovelluksen versio" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Version de l'application" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Version de l'application" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Versione app" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Versione app" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "App-versjon" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "App-versjon" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "App-versie" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "App-versie" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Версия приложения" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Версия приложения" } } } }, - "app_version": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "App-Version" + "app_version" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "App-Version" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "App Version" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "App Version" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Versión de la aplicación" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Versión de la aplicación" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "App Version" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "App Version" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Version de l'application" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Version de l'application" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Versione App" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Versione App" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Programversjon" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Programversjon" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "App Versie" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "App Versie" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "App Version" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "App Version" } } } }, - "Appearance": { - "comment": "The header for the \"Appearance\" section in the screen saver settings view.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Anzeige" + "Appearance" : { + "comment" : "The header for the \"Appearance\" section in the screen saver settings view.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anzeige" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Appearance" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Appearance" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Aspecto" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aspecto" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ulkoasu" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ulkoasu" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Apparence" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apparence" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Aspetto" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aspetto" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Utseende" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Utseende" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Weergave" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Weergave" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Внешний вид" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Внешний вид" } } } }, - "application_settings": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Anwendungseinstellungen" + "application_settings" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anwendungseinstellungen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Application Settings" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Application Settings" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Configuración de la aplicación" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración de la aplicación" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Application Settings" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Application Settings" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Paramètres de l'application" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Paramètres de l'application" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Impostazioni Applicazione" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impostazioni Applicazione" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Programinnstillinger" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Programinnstillinger" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Applicatie Instellingen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Applicatie Instellingen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Application Settings" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Application Settings" } } } }, - "Back": { - "comment": "A button label that goes back to the previous screen.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zurück" + "Back" : { + "comment" : "A button label that goes back to the previous screen.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zurück" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Back" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Back" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Atrás" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Atrás" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Takaisin" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Takaisin" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Retour" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Retour" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Indietro" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Indietro" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Tilbake" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tilbake" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Vorige" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vorige" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Назад" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Назад" } } } }, - "Begin": { - "comment": "The action to play music.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Beginnen" + "Begin" : { + "comment" : "The action to play music.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Beginnen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Comenzar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Comenzar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aloita" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aloita" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Commencer" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Commencer" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Inizia" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inizia" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Begynn" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Begynn" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Beginnen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Beginnen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Начать" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Начать" } } } }, - "bonjour_discovery_disclaimer": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Bonjour-Entdeckung kann vielleicht nicht alle Server finden. Zusätzliche Server könnten im Netzwerk verfügbar sein." + "bonjour_discovery_disclaimer" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bonjour-Entdeckung kann vielleicht nicht alle Server finden. Zusätzliche Server könnten im Netzwerk verfügbar sein." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Bonjour discovery may not find all servers. Additional servers may be available on your network." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bonjour discovery may not find all servers. Additional servers may be available on your network." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Bonjour discovery may not find all servers. Additional servers may be available on your network." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bonjour discovery may not find all servers. Additional servers may be available on your network." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Bonjour discovery may not find all servers. Additional servers may be available on your network." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bonjour discovery may not find all servers. Additional servers may be available on your network." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Bonjour discovery may not find all servers. Additional servers may be available on your network." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bonjour discovery may not find all servers. Additional servers may be available on your network." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Bonjour discovery may not find all servers. Additional servers may be available on your network." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bonjour discovery may not find all servers. Additional servers may be available on your network." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Bonjour discovery may not find all servers. Additional servers may be available on your network." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bonjour discovery may not find all servers. Additional servers may be available on your network." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Bonjour discovery may not find all servers. Additional servers may be available on your network." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bonjour discovery may not find all servers. Additional servers may be available on your network." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Bonjour discovery may not find all servers. Additional servers may be available on your network." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bonjour discovery may not find all servers. Additional servers may be available on your network." } } } }, - "Brightness": { - "comment": "A section header for brightness settings.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Helligkeit" + "Brightness" : { + "comment" : "A section header for brightness settings.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Helligkeit" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Brightness" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brightness" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Brillo" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brillo" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kirkkaus" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kirkkaus" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Luminosité" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Luminosité" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Luminosità" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Luminosità" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Lysstyrke" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lysstyrke" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Helderheid" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Helderheid" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Яркость" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Яркость" } } } }, - "cache_cleared": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Cache gelöscht" + "cache_cleared" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cache gelöscht" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Cache Cleared" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cache Cleared" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Caché limpiada" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Caché limpiada" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Cache Cleared" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cache Cleared" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Cache effacé" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cache effacé" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Cache svuotata" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cache svuotata" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Cache Cleared" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cache Cleared" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Cache gewist" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cache gewist" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Cache Cleared" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cache Cleared" } } } }, - "cancel": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Abbrechen" + "cancel" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abbrechen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Cancel" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cancel" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Cancelar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cancelar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Cancel" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cancel" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Annuler" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Annuler" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Annulla" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Annulla" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Avbryt" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Avbryt" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Annuleer" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Annuleer" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Cancel" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cancel" } } } }, - "Cancel": { - "comment": "A button to cancel renaming the home.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Abbrechen" + "Cancel" : { + "comment" : "A button to cancel renaming the home.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abbrechen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Cancel" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cancel" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Cancelar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cancelar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Peruuta" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Peruuta" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Annuler" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Annuler" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Annulla" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Annulla" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Avbryt" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Avbryt" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Annuleer" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Annuleer" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Отмена" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Отмена" } } } }, - "Cancellation occurred": { - "comment": "Message displayed when a connection test is cancelled.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Abbruch aufgetreten" + "Cancellation occurred" : { + "comment" : "Message displayed when a connection test is cancelled.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abbruch aufgetreten" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Cancellation occurred" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cancellation occurred" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Se produjo una cancelación" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Se produjo una cancelación" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Peruutus tapahtui" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Peruutus tapahtui" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Annulation survenue" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Annulation survenue" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Annullamento eseguito" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Annullamento eseguito" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Avbrudd inntraff" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Avbrudd inntraff" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Annulering opgetreden" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Annulering opgetreden" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Произошла отмена" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Произошла отмена" } } } }, - "Candlelight": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Kerzenlicht" + "Candlelight" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kerzenlicht" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Candlelight" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Candlelight" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Luz de vela" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Luz de vela" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kynttilänvalo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kynttilänvalo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Lumière de bougie" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lumière de bougie" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Luce di candela" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Luce di candela" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Stearinlys" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stearinlys" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Kaarslicht" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kaarslicht" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Свет свечи" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Свет свечи" } } } }, - "Cannot connect to the server. Is it online?": { - "comment": "Error message indicating that the device cannot connect to the server.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Verbindung zum Server kann nicht hergestellt werden. Ist er online?" + "Cannot connect to the server. Is it online?" : { + "comment" : "Error message indicating that the device cannot connect to the server.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbindung zum Server kann nicht hergestellt werden. Ist er online?" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Cannot connect to the server. Is it online?" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cannot connect to the server. Is it online?" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "No se puede conectar al servidor. ¿Está en línea?" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se puede conectar al servidor. ¿Está en línea?" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Yhteyden muodostaminen palvelimeen epäonnistui. Onko se verkossa?" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Yhteyden muodostaminen palvelimeen epäonnistui. Onko se verkossa?" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Impossible de se connecter au serveur. Est-il en ligne ?" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossible de se connecter au serveur. Est-il en ligne ?" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Impossibile connettersi al server. È online?" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossibile connettersi al server. È online?" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kan ikke koble til serveren. Er den online?" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kan ikke koble til serveren. Er den online?" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Kan geen verbinding maken met de server. Is deze online?" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kan geen verbinding maken met de server. Is deze online?" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Не удаётся подключиться к серверу. Он в сети?" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Не удаётся подключиться к серверу. Он в сети?" } } } }, - "Cannot find the server. Is the URL correct?": { - "comment": "Error message when the URL cannot be resolved to a host.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Der Server kann nicht gefunden werden. Ist die URL korrekt?" + "Cannot find the server. Is the URL correct?" : { + "comment" : "Error message when the URL cannot be resolved to a host.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Der Server kann nicht gefunden werden. Ist die URL korrekt?" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Cannot find the server. Is the URL correct?" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cannot find the server. Is the URL correct?" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "No se puede encontrar el servidor. ¿Es correcta la URL?" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se puede encontrar el servidor. ¿Es correcta la URL?" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Palvelinta ei löydy. Onko URL oikein?" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Palvelinta ei löydy. Onko URL oikein?" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Impossible de trouver le serveur. L'URL est-elle correcte ?" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossible de trouver le serveur. L'URL est-elle correcte ?" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Impossibile trovare il server. L'URL è corretto?" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossibile trovare il server. L'URL è corretto?" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kan ikke finne serveren. Er URL-en riktig?" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kan ikke finne serveren. Er URL-en riktig?" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Kan de server niet vinden. Is de URL correct?" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kan de server niet vinden. Is de URL correct?" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Не удаётся найти сервер. URL верный?" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Не удаётся найти сервер. URL верный?" } } } }, - "certficate_exists": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zertifikat existiert bereits im Schlüsselbund." + "carplay_not_configured" : { + "comment" : "CarPlay root list placeholder stating that CarPlay sitemap only allows Switch items.", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "Keine CarPlay-Favoriten konfiguriert" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Certificate already exists in the keychain." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No CarPlay compatible Switch items configured." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "El certificado ya existe en el llavero." + "es" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "No hay favoritos de CarPlay configurados" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Certificate already exists in the keychain." + "fi" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "CarPlay-suosikkeja ei ole määritetty" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Le certificat existe déjà dans le trousseau." + "fr" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "Aucun favori CarPlay configuré" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Il certificato esiste già nel portachiavi." + "it" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "Nessun preferito CarPlay configurato" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sertifikatet finnes allerede i nøkkelkjeden." + "nb" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "Ingen CarPlay-favoritter er konfigurert" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Certificaat bestaat al in de sleutelhanger." + "nl" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "Geen CarPlay-favorieten geconfigureerd" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Certificate already exists in the keychain." + "ru" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "Избранное CarPlay не настроено" } } } }, - "certificate_import_password": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Passwort wird für den Import benötigt." + "carplay_not_configured_detail" : { + "comment" : "CarPlay root list placeholder detail text shown before a CarPlay favorites page is set up.", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "CarPlay-Sitemapseite in den Einstellungen einrichten" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Password required for import." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set up a CarPlay sitemap page in the iOS app settings" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Se requiere contraseña para importar." + "es" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "Configura una página de mapa del sitio de CarPlay en Ajustes" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Password required for import." + "fi" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "Määritä CarPlay-sivustokarttasivu Asetuksissa" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Mot de passe requis pour l'importation." + "fr" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "Configurez une page de plan de site CarPlay dans les réglages" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Password richiesta per l'importazione." + "it" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "Configura una pagina sitemap CarPlay nelle Impostazioni" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Passord kreves for import." + "nb" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "Konfigurer en CarPlay-nettstedskartside i Innstillinger" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Wachtwoord vereist voor import." + "nl" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "Stel een CarPlay-sitemappagina in via Instellingen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Password required for import." + "ru" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "Настройте страницу карты сайта CarPlay в Настройках" } } } }, - "certificate_import_text": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Clientzertifikat in den Schlüsselbund importieren?" + "certficate_exists" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zertifikat existiert bereits im Schlüsselbund." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Import client certificate into the keychain?" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Certificate already exists in the keychain." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "¿Importar certificado de cliente al llavero?" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "El certificado ya existe en el llavero." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Import client certificate into the keychain?" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Certificate already exists in the keychain." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Importer le certificat client dans la trousseau ?" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Le certificat existe déjà dans le trousseau." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Importare il certificato client nel portachiavi?" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Il certificato esiste già nel portachiavi." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Importer klientsertifikat til nøkkelringen?" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sertifikatet finnes allerede i nøkkelkjeden." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Client-certificaat in de sleutelhanger importeren?" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Certificaat bestaat al in de sleutelhanger." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Import client certificate into the keychain?" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Certificate already exists in the keychain." } } } }, - "certificate_import_title": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Client-Zertifikatimport" + "certificate_import_password" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Passwort wird für den Import benötigt." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Client Certificate Import" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Password required for import." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Importar certificado de cliente" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Se requiere contraseña para importar." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Client Certificate Import" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Password required for import." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Importation du certificat client" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mot de passe requis pour l'importation." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Importazione Certificato Client" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Password richiesta per l'importazione." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Import av Klientsertifikat" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Passord kreves for import." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Cliëntcertificaat Importeren" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wachtwoord vereist voor import." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Client Certificate Import" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Password required for import." } } } }, - "Change": { - "comment": "Display representation for the toggle action.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Ändern" + "certificate_import_text" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clientzertifikat in den Schlüsselbund importieren?" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Cambiar" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Import client certificate into the keychain?" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Muuta" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Importar certificado de cliente al llavero?" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Changer" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Import client certificate into the keychain?" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Modifica" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Importer le certificat client dans la trousseau ?" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Endre" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Importare il certificato client nel portachiavi?" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Wijzigen" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Importer klientsertifikat til nøkkelringen?" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Изменить" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client-certificaat in de sleutelhanger importeren?" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Import client certificate into the keychain?" } } } }, - "Check & Clear Image Cache": { - "comment": "A button that triggers a check and clear action for the image cache.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Bilder-Cache checken & löschen" + "certificate_import_title" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client-Zertifikatimport" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Check & Clear Image Cache" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client Certificate Import" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Comprobar y limpiar caché de imágenes" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Importar certificado de cliente" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Tarkista ja tyhjennä kuvavälimuisti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client Certificate Import" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Vérifier et vider le cache d'images" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Importation du certificat client" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Controlla e svuota la cache immagini" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Importazione Certificato Client" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sjekk og tøm bildebuffer" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Import av Klientsertifikat" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Afbeeldingscache controleren & wissen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cliëntcertificaat Importeren" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Проверить и очистить кэш изображений" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client Certificate Import" } } } }, - "Choose color": { - "comment": "A label for the color picker button.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Farbe wählen" + "Change" : { + "comment" : "Display representation for the toggle action.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ändern" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elegir color" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cambiar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Valitse väri" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Muuta" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Choisir une couleur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Changer" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Scegli colore" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modifica" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Velg farge" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Endre" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Kleur kiezen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wijzigen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Выбрать цвет" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Изменить" } } } }, - "Clear": { - "comment": "The title of a button that clears the website cache.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Löschen" + "Check & Clear Image Cache" : { + "comment" : "A button that triggers a check and clear action for the image cache.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bilder-Cache checken & löschen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Check & Clear Image Cache" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Comprobar y limpiar caché de imágenes" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tarkista ja tyhjennä kuvavälimuisti" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Clear" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vérifier et vider le cache d'images" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Controlla e svuota la cache immagini" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sjekk og tøm bildebuffer" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Afbeeldingscache controleren & wissen" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Проверить и очистить кэш изображений" + } + } + } + }, + "Choose color" : { + "comment" : "A label for the color picker button.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Farbe wählen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Borrar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elegir color" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Tyhjennä" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valitse väri" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Effacer" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Choisir une couleur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Cancella" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Scegli colore" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Tøm" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Velg farge" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Wis" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kleur kiezen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Очистить" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Выбрать цвет" } } } }, - "Clear Web Cache": { - "comment": "A button that clears the web cache.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Web-Cache löschen" + "Clear" : { + "comment" : "The title of a button that clears the website cache.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Löschen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Clear Web Cache" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clear" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Limpiar caché web" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Borrar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Tyhjennä verkkovälimuisti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tyhjennä" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Vider le cache web" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Effacer" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Svuota cache web" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cancella" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Tøm nettbuffer" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tøm" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Webcache wissen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wis" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Очистить веб-кэш" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Очистить" } } } }, - "clear_image_cache": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Bilder-Cache löschen" + "Clear Web Cache" : { + "comment" : "A button that clears the web cache.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Web-Cache löschen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Clear Image Cache" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clear Web Cache" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Limpiar caché de imágenes" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Limpiar caché web" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Clear Image Cache" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tyhjennä verkkovälimuisti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Vider le cache des images" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vider le cache web" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Pulisci Cache Immagini" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Svuota cache web" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Tøm Hurtigbuffer for Bilder" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tøm nettbuffer" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Leeg de afbeeldingencache" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Webcache wissen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Clear Image Cache" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Очистить веб-кэш" } } } }, - "clear_web_cache": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Web-Cache löschen" + "clear_image_cache" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bilder-Cache löschen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Clear Web Cache" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clear Image Cache" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Limpiar caché web" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Limpiar caché de imágenes" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Clear Web Cache" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clear Image Cache" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Vider le cache Web" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vider le cache des images" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Svuota memoria cache Web" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pulisci Cache Immagini" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Clear Web Cache" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tøm Hurtigbuffer for Bilder" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Wis webcache" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Leeg de afbeeldingencache" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Clear Web Cache" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clear Image Cache" } } } }, - "Client Certificates": { - "comment": "A link to manage client certificates.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Client-Zertifikate" + "clear_web_cache" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Web-Cache löschen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Client Certificates" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clear Web Cache" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Certificados de cliente" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Limpiar caché web" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Asiakasvarmenteet" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clear Web Cache" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Certificats client" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vider le cache Web" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Certificati client" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Svuota memoria cache Web" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Klientsertifikater" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clear Web Cache" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Clientcertificaten" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wis webcache" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Клиентские сертификаты" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clear Web Cache" } } } }, - "client_certificates": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Client-Zertifikate" + "Client Certificates" : { + "comment" : "A link to manage client certificates.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client-Zertifikate" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Client Certificates" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client Certificates" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Certificados de cliente" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Certificados de cliente" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Client Certificates" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Asiakasvarmenteet" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Certificats du client" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Certificats client" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Certificati Client" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Certificati client" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Klientsertifikater" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Klientsertifikater" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Cliëntcertificaat" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clientcertificaten" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Client Certificates" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Клиентские сертификаты" } } } }, - "Clock Size: %@": { - "comment": "A label that shows the current value of the clock size slider.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Uhrengröße: %@" + "client_certificates" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client-Zertifikate" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Tamaño del reloj: %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client Certificates" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kellon koko: %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Certificados de cliente" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Taille de l'horloge : %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client Certificates" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Dimensione orologio: %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Certificats du client" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Klokkestørrelse: %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Certificati Client" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Klokgrootte: %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Klientsertifikater" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Размер часов: %@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cliëntcertificaat" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Clock Size: %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client Certificates" } } } }, - "closed": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "geschlossen" + "Clock Size: %@" : { + "comment" : "A label that shows the current value of the clock size slider.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uhrengröße: %@" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "closed" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clock Size: %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "cerrado" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tamaño del reloj: %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "closed" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kellon koko: %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "fermé" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Taille de l'horloge : %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "chiuso" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dimensione orologio: %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "lukket" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Klokkestørrelse: %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "gesloten" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Klokgrootte: %@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "closed" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Размер часов: %@" } } } }, - "Closed": { - "comment": "Displayed name for the closed contact state.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Geschlossen" + "closed" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "geschlossen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "closed" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Cerrado" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "cerrado" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Suljettu" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "closed" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Fermé" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "fermé" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Chiuso" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "chiuso" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Lukket" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "lukket" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Gesloten" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "gesloten" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Закрыто" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "closed" } } } }, - "Color": { - "comment": "The title of the color picker in the widget.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Farbe" + "Closed" : { + "comment" : "Displayed name for the closed contact state.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geschlossen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Color" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cerrado" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Color" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suljettu" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fermé" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chiuso" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lukket" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gesloten" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Закрыто" + } + } + } + }, + "Color" : { + "comment" : "The title of the color picker in the widget.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Farbe" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Väri" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Color" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Couleur" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Color" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Colore" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Väri" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Farge" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Couleur" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Kleur" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Colore" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Цвет" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Farge" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kleur" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Цвет" } } } }, - "Color Item": { - "comment": "Display name for a color item type.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Farb-Item" + "Color Item" : { + "comment" : "Display name for a color item type.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Farb-Item" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento de color" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento de color" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Värielementti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Värielementti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Élément couleur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément couleur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elemento colore" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento colore" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Fargeelement" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fargeelement" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Kleur-item" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kleur-item" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент цвета" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент цвета" } } } }, - "Color wheel": { - "comment": "A description of a color wheel.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Farbrad" + "Color wheel" : { + "comment" : "A description of a color wheel.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Farbrad" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Rueda de colores" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rueda de colores" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Väriympyrä" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Väriympyrä" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Roue des couleurs" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Roue des couleurs" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Ruota dei colori" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ruota dei colori" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Fargehjul" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fargehjul" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Kleurwiel" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kleurwiel" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Цветовой круг" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Цветовой круг" } } } }, - "Command failed: %@": { - "comment": "Error message when sending a command to an item fails. Placeholder is the error description.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Befehlsfehler: %@" + "Command failed: %@" : { + "comment" : "Error message when sending a command to an item fails. Placeholder is the error description.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Befehlsfehler: %@" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Command failed: %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Command failed: %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Comando fallido: %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Comando fallido: %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Komento epäonnistui: %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Komento epäonnistui: %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Commande échouée : %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Commande échouée : %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Comando non riuscito: %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Comando non riuscito: %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kommando mislyktes: %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kommando mislyktes: %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Opdracht mislukt: %@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opdracht mislukt: %@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Команда не выполнена: %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Команда не выполнена: %@" } } } }, - "Command failures: %lld": { - "comment": "A label describing the number of failed commands. The argument is the number of failed commands.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Befehlsfehler: %lld" + "Command failures: %lld" : { + "comment" : "A label describing the number of failed commands. The argument is the number of failed commands.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Befehlsfehler: %lld" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Command failures: %lld" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Command failures: %lld" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Errores de comando: %lld" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Errores de comando: %lld" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Komentoviat: %lld" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Komentoviat: %lld" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Échecs de commande : %lld" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Échecs de commande : %lld" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Errori di comando: %lld" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Errori di comando: %lld" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kommandofeil: %lld" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kommandofeil: %lld" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Opdrachtfouten: %lld" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opdrachtfouten: %lld" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Сбоев команд: %lld" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Сбоев команд: %lld" } } } }, - "Command Item %@": { - "comment": "A label for the command item setting in the application settings view.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Befehl-Item %@" + "Command Item %@" : { + "comment" : "A label for the command item setting in the application settings view.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Befehl-Item %@" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Command Item %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Command Item %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento de comando %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento de comando %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Komentoelementti %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Komentoelementti %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Élément de commande %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément de commande %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elemento comando %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento comando %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kommandoelement %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kommandoelement %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Opdracht-item %@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opdracht-item %@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент команды %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент команды %@" } } } }, - "Configure": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Konfigurieren" + "Configure" : { + "comment" : "A label displayed when a widget slot is not configured.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Konfigurieren" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Configurar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configurar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Määritä" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Määritä" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Configurer" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configurer" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Configura" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configura" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Konfigurer" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Konfigurer" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Configureren" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configureren" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Настроить" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Настроить" } } - }, - "comment": "A label displayed when a widget slot is not configured.", - "isCommentAutoGenerated": true + } }, - "Configure which sensor item to display in the widget.": { - "comment": "Title of the intent for configuring a sensor widget.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Konfigurieren Sie, welches Sensorelement im Widget angezeigt werden soll." + "Configure which sensor item to display in the widget." : { + "comment" : "Title of the intent for configuring a sensor widget.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Konfigurieren Sie, welches Sensorelement im Widget angezeigt werden soll." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Configure qué elemento sensor mostrar en el widget." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configure qué elemento sensor mostrar en el widget." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Määritä, mikä anturikohde näytetään widgetissä." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Määritä, mikä anturikohde näytetään widgetissä." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Configurez quel élément capteur afficher dans le widget." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configurez quel élément capteur afficher dans le widget." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Configura quale elemento sensore mostrare nel widget." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configura quale elemento sensore mostrare nel widget." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Konfigurer hvilket sensorelement som skal vises i widgeten." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Konfigurer hvilket sensorelement som skal vises i widgeten." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Configureer welk sensoritem in de widget wordt weergegeven." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configureer welk sensoritem in de widget wordt weergegeven." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Настройте, какой элемент датчика отображать в виджете." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Настройте, какой элемент датчика отображать в виджете." } } } }, - "Configure which sensor items to display in the widget.": { - "comment": "Title of the sensor widget configuration intent.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Konfigurieren Sie, welche Sensorelemente im Widget angezeigt werden sollen." + "Configure which sensor items to display in the widget." : { + "comment" : "Title of the sensor widget configuration intent.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Konfigurieren Sie, welche Sensorelemente im Widget angezeigt werden sollen." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Configure qué elementos de sensor mostrar en el widget." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configure qué elementos de sensor mostrar en el widget." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Määritä, mitkä anturikohteet näytetään widgetissä." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Määritä, mitkä anturikohteet näytetään widgetissä." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Configurez quels éléments capteur afficher dans le widget." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configurez quels éléments capteur afficher dans le widget." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Configura quali elementi sensore mostrare nel widget." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configura quali elementi sensore mostrare nel widget." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Konfigurer hvilke sensorelementer som skal vises i widgeten." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Konfigurer hvilke sensorelementer som skal vises i widgeten." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Configureer welke sensorItems in de widget worden weergegeven." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configureer welke sensorItems in de widget worden weergegeven." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Настройте, какие элементы датчиков отображать в виджете." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Настройте, какие элементы датчиков отображать в виджете." } } } }, - "Configure which switch item to control in the widget.": { - "comment": "Title of the intent for configuring a switch widget.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Konfigurieren Sie, welches Schalterelement im Widget gesteuert werden soll." + "Configure which switch item to control in the widget." : { + "comment" : "Title of the intent for configuring a switch widget.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Konfigurieren Sie, welches Schalterelement im Widget gesteuert werden soll." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Configure qué elemento de interruptor controlar en el widget." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configure qué elemento de interruptor controlar en el widget." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Määritä, mitä kytkinkohde ohjataan widgetissä." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Määritä, mitä kytkinkohde ohjataan widgetissä." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Configurez quel élément commutateur contrôler dans le widget." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configurez quel élément commutateur contrôler dans le widget." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Configura quale elemento interruttore controllare nel widget." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configura quale elemento interruttore controllare nel widget." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Konfigurer hvilket brytterelement som skal styres i widgeten." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Konfigurer hvilket brytterelement som skal styres i widgeten." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Configureer welk schakelaaritem in de widget wordt bediend." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configureer welk schakelaaritem in de widget wordt bediend." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Настройте, каким элементом переключателя управлять в виджете." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Настройте, каким элементом переключателя управлять в виджете." } } } }, - "Configure which switch items to control in the widget.": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Konfigurieren Sie, welche Schalterelemente im Widget gesteuert werden sollen." + "Configure which switch items to control in the widget." : { + "comment" : "Title of the intent to configure a widget that displays two switches.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Konfigurieren Sie, welche Schalterelemente im Widget gesteuert werden sollen." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Configure qué elementos de interruptor controlar en el widget." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configure qué elementos de interruptor controlar en el widget." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Määritä, mitä kytkinkohteita ohjataan widgetissä." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Määritä, mitä kytkinkohteita ohjataan widgetissä." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Configurez quels éléments commutateur contrôler dans le widget." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configurez quels éléments commutateur contrôler dans le widget." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Configura quali elementi interruttore controllare nel widget." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configura quali elementi interruttore controllare nel widget." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Konfigurer hvilke bryterelementer som skal styres i widgeten." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Konfigurer hvilke bryterelementer som skal styres i widgeten." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Configureer welke schakelaaritems in de widget worden bediend." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configureer welke schakelaaritems in de widget worden bediend." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Настройте, какими элементами переключателей управлять в виджете." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Настройте, какими элементами переключателей управлять в виджете." } } - }, - "comment": "Title of the intent to configure a widget that displays two switches.", - "isCommentAutoGenerated": true + } }, - "Configure Widget": { - "comment": "A description of the placeholder that appears when a widget is not configured.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Widget konfigurieren" + "Configure Widget" : { + "comment" : "A description of the placeholder that appears when a widget is not configured.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Widget konfigurieren" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Configurar widget" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configurar widget" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Määritä widget" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Määritä widget" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Configurer le widget" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configurer le widget" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Configura widget" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configura widget" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Konfigurer widget" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Konfigurer widget" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Widget configureren" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Widget configureren" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Настроить виджет" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Настроить виджет" } } } }, - "connecting": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Verbinden" + "connecting" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbinden" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Connecting" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecting" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Conectando" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectando" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Connecting" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecting" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Connexion en cours" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connexion en cours" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Connessione" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connessione" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kobler til" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kobler til" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Verbinden" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbinden" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Connecting" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecting" } } } }, - "Connecting": { - "comment": "A label displayed while waiting for a connection to the OpenHAB server.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Verbinden" + "Connecting" : { + "comment" : "A label displayed while waiting for a connection to the OpenHAB server.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbinden" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Connecting" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecting" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Conectando" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectando" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Yhdistetään" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Yhdistetään" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Connexion" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connexion" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Connetto" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connetto" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kobler til" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kobler til" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Verbinden" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbinden" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Подключение" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Подключение" } } } }, - "connecting_discovered": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Mit entdeckter URL verbinden" + "connecting_discovered" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mit entdeckter URL verbinden" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Connecting to discovered URL" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecting to discovered URL" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Conectando a URL descubierta" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectando a URL descubierta" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Connecting to discovered URL" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecting to discovered URL" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Connexion à l'URL découverte" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connexion à l'URL découverte" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Connessione all'URL trovato" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connessione all'URL trovato" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kobler til oppdaget URL" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kobler til oppdaget URL" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Verbinden naar gevonden URL" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbinden naar gevonden URL" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Connecting to discovered URL" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecting to discovered URL" } } } }, - "connecting_local": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Mit lokaler URL verbinden" + "connecting_local" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mit lokaler URL verbinden" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Connecting to local URL" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecting to local URL" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Conectando a la URL local" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectando a la URL local" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Connecting to local URL" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecting to local URL" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Connexion à l'adresse locale" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connexion à l'adresse locale" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Connessione a URL locale" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connessione a URL locale" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kobler til lokal URL" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kobler til lokal URL" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Verbinden naar lokale URL" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbinden naar lokale URL" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Connecting to local URL" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecting to local URL" } } } }, - "connecting_remote": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Mit Remote-URL verbinden" + "connecting_remote" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mit Remote-URL verbinden" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Connecting to remote URL" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecting to remote URL" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Conectando a la URL remota" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectando a la URL remota" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Connecting to remote URL" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecting to remote URL" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Connexion à l'URL distante" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connexion à l'URL distante" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Connessione a URL remoto" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connessione a URL remoto" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Connecting to remote URL" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecting to remote URL" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Verbinden met externe URL" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbinden met externe URL" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Connecting to remote URL" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecting to remote URL" } } } }, - "Connection successful": { - "comment": "Message displayed when a network connection test is successful.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Verbindung erfolgreich" + "Connection successful" : { + "comment" : "Message displayed when a network connection test is successful.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbindung erfolgreich" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Connection successful" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connection successful" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Conexión exitosa" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conexión exitosa" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Yhteys muodostettu" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Yhteys muodostettu" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Connexion réussie" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connexion réussie" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Connessione riuscita" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connessione riuscita" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Tilkobling vellykket" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tilkobling vellykket" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Verbinding geslaagd" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbinding geslaagd" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Соединение установлено" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Соединение установлено" } } } }, - "Contact Item": { - "comment": "Display name for a contact item type.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Kontakt-Item" + "Contact Item" : { + "comment" : "Display name for a contact item type.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontakt-Item" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento de contacto" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento de contacto" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kontaktielementti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontaktielementti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Élément de contact" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément de contact" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elemento contatto" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento contatto" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kontaktelement" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontaktelement" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Contact-item" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contact-item" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент контакта" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент контакта" } } } }, - "Contact State": { - "comment": "Type display name for the ContactState enum used in app intents.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Kontakt-Status" + "Contact State" : { + "comment" : "Type display name for the ContactState enum used in app intents.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontakt-Status" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Contact State" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contact State" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Estado de contacto" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Estado de contacto" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kontaktin tila" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontaktin tila" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "État du contact" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "État du contact" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Stato contatto" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stato contatto" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kontaktstatus" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontaktstatus" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Contactstatus" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contactstatus" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Состояние контакта" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Состояние контакта" } } } }, - "Control an openHAB switch item": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Ein openHAB-Schalterelement steuern" + "Control an openHAB switch item" : { + "comment" : "Description of the openHAB Switch widget.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ein openHAB-Schalterelement steuern" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Controlar un elemento de interruptor de openHAB" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Controlar un elemento de interruptor de openHAB" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ohjaa openHAB-kytkinkohde" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ohjaa openHAB-kytkinkohde" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Contrôler un élément commutateur openHAB" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contrôler un élément commutateur openHAB" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Controlla un elemento interruttore openHAB" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Controlla un elemento interruttore openHAB" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Styr et openHAB-brytterelement" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Styr et openHAB-brytterelement" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Een openHAB-schakelaaritem bedienen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Een openHAB-schakelaaritem bedienen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Управлять элементом переключателя openHAB" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Управлять элементом переключателя openHAB" } } - }, - "comment": "Description of the openHAB Switch widget.", - "isCommentAutoGenerated": true + } }, - "Control Player": { - "comment": "Text displayed in the shortcut picker for controlling a player.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Player steuern" + "Control Player" : { + "comment" : "Text displayed in the shortcut picker for controlling a player.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Player steuern" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Controlar reproductor" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Controlar reproductor" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ohjaa soitinta" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ohjaa soitinta" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Contrôler le lecteur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contrôler le lecteur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Controlla il lettore" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Controlla il lettore" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kontroller avspiller" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontroller avspiller" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Speler bedienen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Speler bedienen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Управлять плеером" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Управлять плеером" } } } }, - "Control two openHAB switch items": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zwei openHAB-Schalterelemente steuern" + "Control two openHAB switch items" : { + "comment" : "Description of the openHAB Switch widget with 2 items.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zwei openHAB-Schalterelemente steuern" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Controlar dos elementos de interruptor de openHAB" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Controlar dos elementos de interruptor de openHAB" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ohjaa kahta openHAB-kytkinkohteita" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ohjaa kahta openHAB-kytkinkohteita" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Contrôler deux éléments commutateur openHAB" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contrôler deux éléments commutateur openHAB" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Controlla due elementi interruttore openHAB" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Controlla due elementi interruttore openHAB" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Styr to openHAB-brytterelementer" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Styr to openHAB-brytterelementer" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Twee openHAB-schakelaaritems bedienen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Twee openHAB-schakelaaritems bedienen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Управлять двумя элементами переключателей openHAB" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Управлять двумя элементами переключателей openHAB" } } - }, - "comment": "Description of the openHAB Switch widget with 2 items.", - "isCommentAutoGenerated": true + } }, - "Control up to four openHAB switch items": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Bis zu vier openHAB-Schalterelemente steuern" + "Control up to four openHAB switch items" : { + "comment" : "Description of the openHAB Switch widget for large screens.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bis zu vier openHAB-Schalterelemente steuern" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Controlar hasta cuatro elementos de interruptor de openHAB" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Controlar hasta cuatro elementos de interruptor de openHAB" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ohjaa enintään neljää openHAB-kytkinkohteita" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ohjaa enintään neljää openHAB-kytkinkohteita" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Contrôler jusqu'à quatre éléments commutateur openHAB" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contrôler jusqu'à quatre éléments commutateur openHAB" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Controlla fino a quattro elementi interruttore openHAB" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Controlla fino a quattro elementi interruttore openHAB" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Styr opptil fire openHAB-brytterelementer" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Styr opptil fire openHAB-brytterelementer" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Maximaal vier openHAB-schakelaaritems bedienen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Maximaal vier openHAB-schakelaaritems bedienen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Управлять до четырёх элементов переключателей openHAB" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Управлять до четырёх элементов переключателей openHAB" } } - }, - "comment": "Description of the openHAB Switch widget for large screens.", - "isCommentAutoGenerated": true + } }, - "Cool Daylight": { - "comment": "A description for a color temperature between 6500 and 8000 Kelvin.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Kaltes Tageslicht" + "Cool Daylight" : { + "comment" : "A description for a color temperature between 6500 and 8000 Kelvin.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kaltes Tageslicht" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Cool Daylight" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cool Daylight" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Luz diurna fría" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Luz diurna fría" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Viileä päivänvalo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Viileä päivänvalo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Lumière du jour froide" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lumière du jour froide" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Luce del giorno fredda" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Luce del giorno fredda" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kjølig dagslys" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kjølig dagslys" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Koel daglicht" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Koel daglicht" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Холодный дневной свет" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Холодный дневной свет" } } } }, - "Cool White": { - "comment": "A color temperature range that maps to \"Cool White\".", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Kaltweiß" + "Cool White" : { + "comment" : "A color temperature range that maps to \"Cool White\".", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kaltweiß" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Cool White" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cool White" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Blanco frío" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blanco frío" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Viileä valkoinen" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Viileä valkoinen" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Blanc froid" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blanc froid" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Bianco freddo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bianco freddo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kjølig hvit" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kjølig hvit" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Koel wit" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Koel wit" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Холодный белый" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Холодный белый" } } } }, - "Copy": { - "comment": "Label for a button that copies the text of a text row to the clipboard.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Kopieren" + "Copy" : { + "comment" : "Label for a button that copies the text of a text row to the clipboard.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kopieren" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Copy" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copy" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Copiar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copiar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kopioi" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kopioi" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Copier" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copier" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Copia" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copia" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kopier" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kopier" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Kopieer" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kopieer" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Копировать" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Копировать" } } } }, - "copy_label": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Item-Bezeichnung kopieren" + "copy_label" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Item-Bezeichnung kopieren" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Copy item label" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copy item label" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Copiar etiqueta del ítem" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copiar etiqueta del ítem" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Copy item label" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copy item label" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Copier le label de l'item" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copier le label de l'item" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Copia etichetta Item" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copia etichetta Item" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kopier Item-etikett" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kopier Item-etikett" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Item label kopiëren" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Item label kopiëren" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Copy item label" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copy item label" } } } }, - "Crash Reporting": { - "comment": "A toggle that allows the user to enable or disable crash reporting.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Absturzberichterstattung" + "Crash Reporting" : { + "comment" : "A toggle that allows the user to enable or disable crash reporting.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Absturzberichterstattung" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Crash Reporting" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crash Reporting" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Informe de fallos" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Informe de fallos" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kaatumisraportointi" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kaatumisraportointi" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Rapport de plantage" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rapport de plantage" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Segnalazione arresti anomali" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Segnalazione arresti anomali" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Krasjirapportering" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Krasjirapportering" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Crashrapportage" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crashrapportage" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Отчёты о сбоях" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Отчёты о сбоях" } } } }, - "crash_detected": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Absturz festgestellt" + "crash_detected" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Absturz festgestellt" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Crash detected" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crash detected" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Error detectado" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error detectado" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Crash detected" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crash detected" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Crash détecté" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crash détecté" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Crash rilevato" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crash rilevato" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Krasj oppdaget" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Krasj oppdaget" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Crash gedetecteerd" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crash gedetecteerd" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Crash detected" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crash detected" } } } }, - "crash_reporting": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Absturzberichterstattung" + "crash_reporting" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Absturzberichterstattung" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Crash Reporting" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crash Reporting" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Informe de errores" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Informe de errores" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Crash Reporting" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crash Reporting" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Rapports d'erreurs" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rapports d'erreurs" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Segnalazioni di crash" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Segnalazioni di crash" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Krasjrapportering" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Krasjrapportering" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Foutenrapportering" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Foutenrapportering" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Crash Reporting" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crash Reporting" } } } }, - "crash_reporting_info": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Durch die Aktivierung der Absturzberichterstattung stimmst du zu, dass Informationen zu deinem Gerät und der Nutzung der App erfasst und mit Crashlytics (einem Unternehmen von Google) geteilt werden. Weitere Informationen findest du in unserer Datenschutzerklärung." + "crash_reporting_info" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Durch die Aktivierung der Absturzberichterstattung stimmst du zu, dass Informationen zu deinem Gerät und der Nutzung der App erfasst und mit Crashlytics (einem Unternehmen von Google) geteilt werden. Weitere Informationen findest du in unserer Datenschutzerklärung." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "By activating crash reporting you agree that device and usage information will be collected and shared with Crashlytics (a Google company). For further information view our privacy policy." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "By activating crash reporting you agree that device and usage information will be collected and shared with Crashlytics (a Google company). For further information view our privacy policy." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Al activar los informes de fallos, usted acepta que la información de uso y del dispositivo se recopilará y compartirá con Crashlytics (una empresa de Google). Para obtener más información, consulte nuestra política de privacidad." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Al activar los informes de fallos, usted acepta que la información de uso y del dispositivo se recopilará y compartirá con Crashlytics (una empresa de Google). Para obtener más información, consulte nuestra política de privacidad." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "By activating crash reporting you agree that device and usage information will be collected and shared with Crashlytics (a Google company). For further information view our privacy policy." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "By activating crash reporting you agree that device and usage information will be collected and shared with Crashlytics (a Google company). For further information view our privacy policy." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "En activant le rapport de d’erreurs, tu acceptes que les informations relatives à l’appareil et à son utilisation soient collectées et partagées avec Crashlytics (une entreprise Google). Pour plus d’informations, consulte notre politique de confidentialité." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "En activant le rapport de d’erreurs, tu acceptes que les informations relatives à l’appareil et à son utilisation soient collectées et partagées avec Crashlytics (une entreprise Google). Pour plus d’informations, consulte notre politique de confidentialité." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Attivando la segnalazione di crash si accetta che le informazioni sul dispositivo e sull'utilizzo saranno raccolte e condivise con Crashlytics (un'azienda di Google). Per ulteriori informazioni consultare la nostra informativa sulla privacy." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Attivando la segnalazione di crash si accetta che le informazioni sul dispositivo e sull'utilizzo saranno raccolte e condivise con Crashlytics (un'azienda di Google). Per ulteriori informazioni consultare la nostra informativa sulla privacy." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ved å aktivere krasjrapportering samtykker du i at enhet og bruksinformasjon samles inn og deles med Crashlytics (et Google-firma). For mer informasjon se våre retningslinjer for personvern." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ved å aktivere krasjrapportering samtykker du i at enhet og bruksinformasjon samles inn og deles med Crashlytics (et Google-firma). For mer informasjon se våre retningslinjer for personvern." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Door het activeren van crash-rapportage gaat u akkoord dat apparaat- en gebruiksinformatie zal worden verzameld en gedeeld met Crashlytics (een Google-bedrijf). Voor meer informatie bekijk ons privacybeleid." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Door het activeren van crash-rapportage gaat u akkoord dat apparaat- en gebruiksinformatie zal worden verzameld en gedeeld met Crashlytics (een Google-bedrijf). Voor meer informatie bekijk ons privacybeleid." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "By activating crash reporting you agree that device and usage information will be collected and shared with Crashlytics (a Google company). For further information view our privacy policy." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "By activating crash reporting you agree that device and usage information will be collected and shared with Crashlytics (a Google company). For further information view our privacy policy." } } } }, - "Create": { - "comment": "The text on a button that creates a new home.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Erstellen" + "Create" : { + "comment" : "The text on a button that creates a new home.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erstellen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Create" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Create" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Crear" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crear" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Luo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Luo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Créer" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Créer" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Crea" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crea" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Opprett" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opprett" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Maak aan" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Maak aan" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Создать" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Создать" } } } }, - "Date and Time": { - "comment": "Parameter title for date and time in the Set DateTime Control Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Datum und Uhrzeit" + "Date and Time" : { + "comment" : "Parameter title for date and time in the Set DateTime Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datum und Uhrzeit" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Date and Time" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Date and Time" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Fecha y hora" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fecha y hora" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Päivämäärä ja aika" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Päivämäärä ja aika" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Date et heure" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Date et heure" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Data e ora" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Data e ora" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Dato og tid" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dato og tid" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Datum en tijd" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datum en tijd" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Дата и время" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Дата и время" } } } }, - "DateTime Item": { - "comment": "Display name for a DateTimeItemEntity.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "DateTime-Item" + "DateTime Item" : { + "comment" : "Display name for a DateTimeItemEntity.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "DateTime-Item" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento DateTime" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento DateTime" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "DateTime-elementti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "DateTime-elementti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Élément DateHeure" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément DateHeure" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elemento DateTime" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento DateTime" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "DateTime-element" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "DateTime-element" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "DateTime-item" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "DateTime-item" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент DateTime" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент DateTime" } } } }, - "Daylight": { - "comment": "A descriptive label for a color temperature range.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Tageslicht" + "Daylight" : { + "comment" : "A descriptive label for a color temperature range.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tageslicht" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Daylight" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Daylight" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Luz diurna" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Luz diurna" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Päivänvalo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Päivänvalo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Lumière du jour" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lumière du jour" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Luce del giorno" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Luce del giorno" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Dagslys" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dagslys" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Daglicht" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Daglicht" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Дневной свет" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Дневной свет" } } } }, - "debug": { - "comment": "Section header text in the Debug Settings view.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Debug" + "debug" : { + "comment" : "Section header text in the Debug Settings view.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Debug" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Debug" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Debug" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Debug" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Debug" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Debug" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Debug" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Debug" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" } } } }, - "Debug": { - "comment": "Tab label for the Debug tab in the watch app.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Debug" + "Debug" : { + "comment" : "Tab label for the Debug tab in the watch app.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Debug" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Debug" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Debug" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Debug" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Debug" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Debug" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Debug" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Debug" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" } } } }, - "Decrease %@": { - "comment": "Accessibility labels and hints for the increase and decrease buttons.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "%@ verringern" + "Decrease %@" : { + "comment" : "Accessibility labels and hints for the increase and decrease buttons.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ verringern" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Decrease %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Decrease %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Disminuir %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Disminuir %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Pienennä %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pienennä %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Diminuer %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Diminuer %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Diminuisci %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Diminuisci %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Reduser %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reduser %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "%@ verlagen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ verlagen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Уменьшить %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Уменьшить %@" } } } }, - "Default Path": { - "comment": "A label displayed above the text field for the default path of the main UI.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Standardpfad" + "Default Path" : { + "comment" : "A label displayed above the text field for the default path of the main UI.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Standardpfad" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Default Path" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Default Path" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Ruta predeterminada" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ruta predeterminada" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Oletuspolku" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Oletuspolku" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Chemin par défaut" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chemin par défaut" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Percorso predefinito" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Percorso predefinito" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Standard sti" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Standard sti" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Standaardpad" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Standaardpad" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Путь по умолчанию" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Путь по умолчанию" } } } }, - "Delete": { - "comment": "The text on the \"Delete\" button in the alert that appears when you long-press a home in the list.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Löschen" + "Delete" : { + "comment" : "The text on the \"Delete\" button in the alert that appears when you long-press a home in the list.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Löschen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Delete" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delete" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Eliminar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eliminar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Poista" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Poista" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Supprimer" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Supprimer" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elimina" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elimina" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Slett" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Slett" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Verwijder" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verwijder" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Удалить" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Удалить" } } } }, - "Delete home '%@'?": { - "comment": "An alert that appears when the user swipes to delete a home. The argument is the name of the home that is about to be deleted.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zuhause „%@“ löschen?" + "Delete home '%@'?" : { + "comment" : "An alert that appears when the user swipes to delete a home. The argument is the name of the home that is about to be deleted.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zuhause „%@“ löschen?" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Delete home '%@'?" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delete home '%@'?" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "¿Eliminar hogar ‘%@’?" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Eliminar hogar ‘%@’?" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Poistetaanko koti ‘%@’?" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Poistetaanko koti ‘%@’?" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Supprimer la maison '%@' ?" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Supprimer la maison '%@' ?" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Eliminare la casa '%@'?" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eliminare la casa '%@'?" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Slette hjem ‘%@’?" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Slette hjem ‘%@’?" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Huis '%@' verwijderen?" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Huis '%@' verwijderen?" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Удалить дом «%@»?" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Удалить дом «%@»?" } } } }, - "Demo Mode": { - "comment": "A toggle that enables or disables demo mode.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Demomodus" + "Demo Mode" : { + "comment" : "A toggle that enables or disables demo mode.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Demomodus" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Demo Mode" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Demo Mode" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Modo demo" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modo demo" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Esittelytila" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Esittelytila" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Mode démo" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mode démo" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Modalità demo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modalità demo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Demomodus" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Demomodus" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Demomodus" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Demomodus" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Демо-режим" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Демо-режим" } } } }, - "demo_mode": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Demomodus" + "demo_mode" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Demomodus" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Demo mode" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Demo mode" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Modo de demostración" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modo de demostración" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Demo mode" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Demo mode" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Mode démo" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mode démo" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Modalità demo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modalità demo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Demomodus" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Demomodus" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Demomodus" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Demomodus" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Demo mode" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Demo mode" } } } }, - "Deny": { - "comment": "A button label that denies access to a website.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Ablehnen" + "Deny" : { + "comment" : "A button label that denies access to a website.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ablehnen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Deny" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Deny" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Denegar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Denegar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Hylkää" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hylkää" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Refuser" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Refuser" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Non consentire" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Non consentire" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Avslå" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Avslå" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Weiger" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Weiger" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Отклонить" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Отклонить" } } } }, - "Dim Level: %@": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Dimmstufe: %@" + "Dim Level: %@" : { + "comment" : "A label displaying the current dim level of the screen saver. The value is shown as a percentage.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dimmstufe: %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Nivel de atenuación: %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dim Level: %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Himmennystaso: %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nivel de atenuación: %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Niveau d'atténuation : %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Himmennystaso: %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Livello dimmer: %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Niveau d'atténuation : %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Dimmenivå: %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Livello dimmer: %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Dimniveau: %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dimmenivå: %@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Уровень диммера: %@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dimniveau: %@" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Dim Level: %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Уровень диммера: %@" } } - }, - "comment": "A label displaying the current dim level of the screen saver. The value is shown as a percentage." + } }, - "Dimmer or Roller Item": { - "comment": "Display name for a dimmer or roller item type.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Dimmer- oder Rolladen-Item" + "Dimmer or Roller Item" : { + "comment" : "Display name for a dimmer or roller item type.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dimmer- oder Rolladen-Item" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Dimmer or Roller Item" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dimmer or Roller Item" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento regulador o persiana" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento regulador o persiana" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Himmennin- tai rullakaihdinelementti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Himmennin- tai rullakaihdinelementti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Élément gradateur/volet" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément gradateur/volet" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elemento dimmer/tapparella" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento dimmer/tapparella" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Dimmer- eller rullelukkelement" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dimmer- eller rullelukkelement" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Dimmer/Roller-item" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dimmer/Roller-item" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент диммера или ролеты" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент диммера или ролеты" } } } }, - "Disable": { - "comment": "Displayed when the user disables a setting.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Deaktivieren" + "Disable" : { + "comment" : "Displayed when the user disables a setting.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Deaktivieren" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Deshabilitar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Deshabilitar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Poista käytöstä" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Poista käytöstä" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Désactiver" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Désactiver" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Disabilita" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Disabilita" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Deaktiver" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Deaktiver" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Uitschakelen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uitschakelen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Отключить" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Отключить" } } } }, - "Disable Idle Timeout": { - "comment": "A toggle that allows the user to disable the idle timeout feature.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Ruhezustand-Timeout deaktivieren" + "Disable Idle Timeout" : { + "comment" : "A toggle that allows the user to disable the idle timeout feature.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ruhezustand-Timeout deaktivieren" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Disable Idle Timeout" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Disable Idle Timeout" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Desactivar tiempo de espera en reposo" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Desactivar tiempo de espera en reposo" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Poista käytöstä lepoajan aikakatkaisu" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Poista käytöstä lepoajan aikakatkaisu" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Désactiver le délai d'inactivité" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Désactiver le délai d'inactivité" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Disabilita timeout inattività" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Disabilita timeout inattività" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Deaktiver tidsavbrudd ved inaktivitet" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Deaktiver tidsavbrudd ved inaktivitet" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Time-out bij inactiviteit uitschakelen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Time-out bij inactiviteit uitschakelen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Отключить тайм-аут простоя" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Отключить тайм-аут простоя" } } } }, - "disable_idle_timeout": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Bildschirm-Timeout deaktivieren" + "disable_idle_timeout" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bildschirm-Timeout deaktivieren" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Disable Idle Timeout" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Disable Idle Timeout" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Desactivar tiempo de espera de inactividad" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Desactivar tiempo de espera de inactividad" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Disable Idle Timeout" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Disable Idle Timeout" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Désactiver le délai d'inactivité" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Désactiver le délai d'inactivité" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Disabilita Timeout Inattività" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Disabilita Timeout Inattività" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Deaktiver Tidsavbrudd for Inaktivitet" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Deaktiver Tidsavbrudd for Inaktivitet" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Schermtimeout uitschakelen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schermtimeout uitschakelen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Disable Idle Timeout" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Disable Idle Timeout" } } } }, - "discovered_servers": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Gefundene Server" + "discovered_servers" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gefundene Server" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Discovered Servers" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Discovered Servers" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Discovered Servers" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Discovered Servers" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Discovered Servers" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Discovered Servers" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Discovered Servers" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Discovered Servers" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Discovered Servers" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Discovered Servers" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Discovered Servers" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Discovered Servers" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Discovered Servers" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Discovered Servers" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Discovered Servers" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Discovered Servers" } } } }, - "discovering_oh": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "OpenHAB entdecken" + "discovering_oh" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "OpenHAB entdecken" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Discovering openHAB" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Discovering openHAB" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Descubriendo openHAB" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Descubriendo openHAB" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Discovering openHAB" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Discovering openHAB" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Découvrir openHAB" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Découvrir openHAB" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Cercando openHAB" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cercando openHAB" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Oppdager openHAB" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Oppdager openHAB" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Bezig met zoeken naar openHAB" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bezig met zoeken naar openHAB" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Discovering openHAB" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Discovering openHAB" } } } }, - "discovering_servers": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "openHAB Server werden entdeckt …" + "discovering_servers" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Server werden entdeckt …" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Discovering openHAB servers…" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Discovering openHAB servers…" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Descubriendo servidores openHAB…" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Descubriendo servidores openHAB…" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Etsitään openHAB-palvelimia…" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Etsitään openHAB-palvelimia…" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Recherche des serveurs openHAB…" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Recherche des serveurs openHAB…" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Ricerca server openHAB in corso…" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ricerca server openHAB in corso…" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Finner openHAB-servere…" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Finner openHAB-servere…" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "openHAB-servers worden ontdekt…" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB-servers worden ontdekt…" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Обнаружение серверов openHAB…" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Обнаружение серверов openHAB…" } } } }, - "dismiss": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Verwerfen" + "dismiss" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verwerfen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Dismiss" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dismiss" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Descartar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Descartar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Dismiss" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dismiss" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Ignorer" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ignorer" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Ignora" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ignora" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Avvis" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Avvis" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Afwijzen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Afwijzen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Dismiss" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dismiss" } } } }, - "Display an openHAB sensor value": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Einen openHAB-Sensorwert anzeigen" + "Display an openHAB sensor value" : { + "comment" : "Description of the openHAB sensor widget.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Einen openHAB-Sensorwert anzeigen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Mostrar un valor de sensor de openHAB" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostrar un valor de sensor de openHAB" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Näytä openHAB-anturiarvo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Näytä openHAB-anturiarvo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Afficher une valeur de capteur openHAB" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Afficher une valeur de capteur openHAB" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Visualizza un valore sensore openHAB" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Visualizza un valore sensore openHAB" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Vis en openHAB-sensorverdi" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vis en openHAB-sensorverdi" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Een openHAB-sensorwaarde weergeven" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Een openHAB-sensorwaarde weergeven" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Показать значение датчика openHAB" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Показать значение датчика openHAB" } } - }, - "comment": "Description of the openHAB sensor widget.", - "isCommentAutoGenerated": true + } }, - "Display two openHAB sensor values": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zwei openHAB-Sensorwerte anzeigen" + "Display two openHAB sensor values" : { + "comment" : "Description of the SensorMedium widget.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zwei openHAB-Sensorwerte anzeigen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Mostrar dos valores de sensor de openHAB" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostrar dos valores de sensor de openHAB" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Näytä kaksi openHAB-anturiarvoa" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Näytä kaksi openHAB-anturiarvoa" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Afficher deux valeurs de capteur openHAB" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Afficher deux valeurs de capteur openHAB" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Visualizza due valori sensore openHAB" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Visualizza due valori sensore openHAB" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Vis to openHAB-sensorverdier" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vis to openHAB-sensorverdier" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Twee openHAB-sensorwaarden weergeven" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Twee openHAB-sensorwaarden weergeven" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Показать два значения датчиков openHAB" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Показать два значения датчиков openHAB" } } - }, - "comment": "Description of the SensorMedium widget.", - "isCommentAutoGenerated": true + } }, - "Display up to four openHAB sensor values": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Bis zu vier openHAB-Sensorwerte anzeigen" + "Display up to four openHAB sensor values" : { + "comment" : "Description of the SensorLarge widget.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bis zu vier openHAB-Sensorwerte anzeigen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Mostrar hasta cuatro valores de sensor de openHAB" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostrar hasta cuatro valores de sensor de openHAB" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Näytä enintään neljä openHAB-anturiarvoa" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Näytä enintään neljä openHAB-anturiarvoa" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Afficher jusqu'à quatre valeurs de capteur openHAB" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Afficher jusqu'à quatre valeurs de capteur openHAB" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Visualizza fino a quattro valori sensore openHAB" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Visualizza fino a quattro valori sensore openHAB" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Vis opptil fire openHAB-sensorverdier" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vis opptil fire openHAB-sensorverdier" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Maximaal vier openHAB-sensorwaarden weergeven" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Maximaal vier openHAB-sensorwaarden weergeven" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Показать до четырёх значений датчиков openHAB" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Показать до четырёх значений датчиков openHAB" } } - }, - "comment": "Description of the SensorLarge widget.", - "isCommentAutoGenerated": true + } }, - "Done": { - "comment": "A button that dismisses a sheet.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Fertig" + "Done" : { + "comment" : "A button that dismisses a sheet.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fertig" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Hecho" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hecho" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Valmis" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valmis" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Terminé" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terminé" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Fine" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fine" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ferdig" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ferdig" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Gereed" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gereed" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Готово" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Готово" } } } }, - "Drag to change hue and saturation": { - "comment": "A hint that describes how to interact with a color wheel.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Ziehen, um Farbton und Sättigung zu ändern" + "Drag to change hue and saturation" : { + "comment" : "A hint that describes how to interact with a color wheel.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ziehen, um Farbton und Sättigung zu ändern" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Arrastrar para cambiar el tono y la saturación" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Arrastrar para cambiar el tono y la saturación" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Vedä muuttaaksesi värisävyä ja kylläisyyttä" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vedä muuttaaksesi värisävyä ja kylläisyyttä" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Faire glisser pour modifier la teinte et la saturation" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Faire glisser pour modifier la teinte et la saturation" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Trascina per modificare tonalità e saturazione" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trascina per modificare tonalità e saturazione" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Dra for å endre fargetone og metning" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dra for å endre fargetone og metning" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Sleep om tint en verzadiging te wijzigen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sleep om tint en verzadiging te wijzigen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Перетащите для изменения оттенка и насыщенности" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Перетащите для изменения оттенка и насыщенности" } } } }, - "empty": { - "comment": "local or remote URL: empty", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "leer" + "empty" : { + "comment" : "local or remote URL: empty", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "leer" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "empty" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "empty" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "vacío" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "vacío" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "tyhjä" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "tyhjä" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "vide" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "vide" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "vuoto" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "vuoto" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "tom" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "tom" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "leeg" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "leeg" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "пусто" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "пусто" } } } }, - "empty_sitemap": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "openHAB lieferte eine leere Sitemap-Liste" + "empty_sitemap" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB lieferte eine leere Sitemap-Liste" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "openHAB returned empty sitemap list" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB returned empty sitemap list" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "lista de mapas web de openHAB vacía" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "lista de mapas web de openHAB vacía" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "openHAB returned empty sitemap list" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB returned empty sitemap list" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "openHAB a renvoyé une liste vide de sitemaps" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB a renvoyé une liste vide de sitemaps" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "openHAB ha ritornato una lista vuota di sitemap" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB ha ritornato una lista vuota di sitemap" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "openHAB returnerte en tom Sitemap-liste" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB returnerte en tom Sitemap-liste" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "openHAB heeft een lege Sitemap geladen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB heeft een lege Sitemap geladen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "openHAB returned empty sitemap list" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB returned empty sitemap list" } } } }, - "Enable": { - "comment": "Display name for the \"Enable\" action.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Aktivieren" + "Enable" : { + "comment" : "Display name for the \"Enable\" action.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktivieren" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Habilitar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Habilitar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ota käyttöön" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ota käyttöön" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Activer" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activer" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Abilita" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abilita" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Aktiver" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktiver" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Inschakelen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inschakelen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Включить" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Включить" } } } }, - "Enable Dimming": { - "comment": "A toggle that enables or disables automatic brightness dimming.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Dimmen aktivieren" + "Enable Dimming" : { + "comment" : "A toggle that enables or disables automatic brightness dimming.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dimmen aktivieren" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Enable Dimming" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enable Dimming" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Activar atenuación" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activar atenuación" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ota himmennys käyttöön" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ota himmennys käyttöön" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Activer la gradation" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activer la gradation" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Abilita dimmeraggio" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abilita dimmeraggio" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Aktiver dimming" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktiver dimming" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Dimmen inschakelen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dimmen inschakelen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Включить затемнение" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Включить затемнение" } } } }, - "Enable Screen Saver": { - "comment": "A toggle switch that enables or disables the screen saver feature.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Bildschirmschoner aktivieren" + "Enable Screen Saver" : { + "comment" : "A toggle switch that enables or disables the screen saver feature.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bildschirmschoner aktivieren" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Enable Screen Saver" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enable Screen Saver" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Activar salvapantallas" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activar salvapantallas" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ota näytönsäästäjä käyttöön" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ota näytönsäästäjä käyttöön" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Activer l'économiseur d'écran" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activer l'économiseur d'écran" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Abilita salvaschermo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abilita salvaschermo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Aktiver skjermsparer" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktiver skjermsparer" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Schermbeveiliging inschakelen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schermbeveiliging inschakelen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Включить заставку экрана" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Включить заставку экрана" } } } }, - "Enter a name for the new home": { - "comment": "A prompt that appears when creating a new home, asking for a name.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Name für das neue Zuhause eingeben" + "Enter a name for the new home" : { + "comment" : "A prompt that appears when creating a new home, asking for a name.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Name für das neue Zuhause eingeben" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Enter a name for the new home" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enter a name for the new home" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Introducir un nombre para el nuevo hogar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Introducir un nombre para el nuevo hogar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Syötä nimi uudelle kodille" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Syötä nimi uudelle kodille" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Saisissez un nom pour la nouvelle maison" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Saisissez un nom pour la nouvelle maison" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Inserisci un nome per la nuova casa" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inserisci un nome per la nuova casa" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi et navn for det nye hjemmet" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi et navn for det nye hjemmet" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Voer een naam in voor het nieuwe huis" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Voer een naam in voor het nieuwe huis" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Введите имя для нового дома" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Введите имя для нового дома" } } } }, - "Enter a new name for the home '%@'": { - "comment": "Alerts for renaming or deleting a home.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Neuer Name für das Zuhause „%@“ eingeben" + "Enter a new name for the home '%@'" : { + "comment" : "Alerts for renaming or deleting a home.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Neuer Name für das Zuhause „%@“ eingeben" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Enter a new name for the home '%@'" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enter a new name for the home '%@'" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Introducir un nuevo nombre para el hogar ‘%@’" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Introducir un nuevo nombre para el hogar ‘%@’" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Syötä uusi nimi kodille ‘%@’" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Syötä uusi nimi kodille ‘%@’" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Saisissez un nouveau nom pour la maison '%@'" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Saisissez un nouveau nom pour la maison '%@'" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Inserisci un nuovo nome per la casa '%@'" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inserisci un nuovo nome per la casa '%@'" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi et nytt navn for hjemmet ‘%@’" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi et nytt navn for hjemmet ‘%@’" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Voer een nieuwe naam in voor het huis '%@'" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Voer een nieuwe naam in voor het huis '%@'" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Введите новое имя для дома «%@»" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Введите новое имя для дома «%@»" } } } }, - "Enter new value": { - "comment": "The title of an alert that appears when the user taps the \"Add Time\" button in the example.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Neuen Wert eingeben" + "Enter new value" : { + "comment" : "The title of an alert that appears when the user taps the \"Add Time\" button in the example.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Neuen Wert eingeben" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Enter new value" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enter new value" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Introducir nuevo valor" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Introducir nuevo valor" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Syötä uusi arvo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Syötä uusi arvo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Saisir une nouvelle valeur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Saisir une nouvelle valeur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Inserisci nuovo valore" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inserisci nuovo valore" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi ny verdi" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi ny verdi" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Nieuwe waarde invoeren" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nieuwe waarde invoeren" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Введите новое значение" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Введите новое значение" } } } }, - "Enter password for server": { - "comment": "A prompt instructing the user to enter a password for the server. Try to keep shortish.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Server-Passwort eingeben" + "Enter password for server" : { + "comment" : "A prompt instructing the user to enter a password for the server. Try to keep shortish.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Server-Passwort eingeben" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Enter password for server" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enter password for server" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Introduce la contraseña del servidor" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Introduce la contraseña del servidor" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Anna palvelimen salasana" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anna palvelimen salasana" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Entrez le mot de passe du serveur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Entrez le mot de passe du serveur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Inserisci la password del server" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inserisci la password del server" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Skriv inn passord for server" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skriv inn passord for server" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Voer wachtwoord voor server in" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Voer wachtwoord voor server in" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Введите пароль для сервера" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Введите пароль для сервера" } } } }, - "Enter text": { - "comment": "A placeholder text for a text input field.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Text eingeben" + "Enter text" : { + "comment" : "A placeholder text for a text input field.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Text eingeben" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Enter text" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enter text" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Introducir texto" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Introducir texto" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Syötä teksti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Syötä teksti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Saisir du texte" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Saisir du texte" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Inserisci testo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inserisci testo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi tekst" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi tekst" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Tekst invoeren" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tekst invoeren" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Введите текст" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Введите текст" } } } }, - "Enter URL of remote server": { - "comment": "A message displayed when the URL field in the connection settings is empty.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "URL des entfernten Servers eingeben" + "Enter URL of remote server" : { + "comment" : "A message displayed when the URL field in the connection settings is empty.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL des entfernten Servers eingeben" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Enter URL of remote server" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enter URL of remote server" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Introducir URL del servidor remoto" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Introducir URL del servidor remoto" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Syötä etäpalvelimen URL" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Syötä etäpalvelimen URL" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Saisir l'URL du serveur distant" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Saisir l'URL du serveur distant" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Inserisci URL del server remoto" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inserisci URL del server remoto" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi URL til fjernserver" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi URL til fjernserver" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "URL van externe server invoeren" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL van externe server invoeren" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Введите URL удалённого сервера" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Введите URL удалённого сервера" } } } }, - "Enter username for server, if required": { - "comment": "A message displayed below the username field, instructing the user to enter a username if one is required by the server. Try to keep shortish.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Server-Benutzername eingeben, falls erforderlich" + "Enter username for server, if required" : { + "comment" : "A message displayed below the username field, instructing the user to enter a username if one is required by the server. Try to keep shortish.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Server-Benutzername eingeben, falls erforderlich" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Enter username for server, if required" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enter username for server, if required" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Introduce el nombre de usuario del servidor, si es necesario" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Introduce el nombre de usuario del servidor, si es necesario" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Anna palvelimen käyttäjänimi tarvittaessa" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anna palvelimen käyttäjänimi tarvittaessa" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Entrez le nom d'utilisateur du serveur, si requis" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Entrez le nom d'utilisateur du serveur, si requis" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Inserisci il nome utente del server, se richiesto" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inserisci il nome utente del server, se richiesto" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Skriv inn brukernavn for server, om nødvendig" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skriv inn brukernavn for server, om nødvendig" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Voer gebruikersnaam voor server in, indien vereist" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Voer gebruikersnaam voor server in, indien vereist" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Введите имя пользователя для сервера, если требуется" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Введите имя пользователя для сервера, если требуется" } } } }, - "Error": { - "comment": "The title of an alert that appears when an error occurs.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Fehler" + "Error" : { + "comment" : "The title of an alert that appears when an error occurs.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fehler" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Error" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Error" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Virhe" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Virhe" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Erreur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erreur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Errore" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Errore" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Feil" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Feil" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Fout" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fout" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Ошибка" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ошибка" } } } }, - "Fade Duration: %@ s": { - "comment": "A slider that lets the user adjust the duration of the fade-in animation when the screen saver is activated. The argument is the string “%.1f”.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Fade-Dauer: %@ s" + "Fade Duration: %@ s" : { + "comment" : "A slider that lets the user adjust the duration of the fade-in animation when the screen saver is activated. The argument is the string “%.1f”.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fade-Dauer: %@ s" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Fade Duration: %@ s" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fade Duration: %@ s" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Duración del fundido: %@ s" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Duración del fundido: %@ s" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Häivytyksen kesto: %@ s" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Häivytyksen kesto: %@ s" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Durée de fondu : %@ s" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Durée de fondu : %@ s" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Durata dissolvenza: %@ s" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Durata dissolvenza: %@ s" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Falming varighet: %@ s" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Falming varighet: %@ s" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Vervaagtijd: %@ s" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vervaagtijd: %@ s" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Длительность затухания: %@ с" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Длительность затухания: %@ с" } } } }, - "Fast Forward": { - "comment": "Display name for the fast forward action in the PlayerAction enum.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Schneller Vorlauf" + "Fast Forward" : { + "comment" : "Display name for the fast forward action in the PlayerAction enum.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schneller Vorlauf" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Fast Forward" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fast Forward" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Avance rápido" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Avance rápido" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Pikakelaus eteenpäin" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pikakelaus eteenpäin" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Avance rapide" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Avance rapide" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Avanzamento rapido" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Avanzamento rapido" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Spol frem" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Spol frem" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Vooruitspoelen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vooruitspoelen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Перемотка вперёд" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Перемотка вперёд" } } } }, - "FF": { - "comment": "Synonyms for the \"Fast Forward\" player action.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "FF" + "FF" : { + "comment" : "Synonyms for the \"Fast Forward\" player action.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "FF" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "FF" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "FF" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "FF" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "FF" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "FF" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "FF" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "FF" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "FF" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "FF" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "FF" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "FF" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "FF" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "FF" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "FF" } } } }, - "Flip": { - "comment": "Display name for the switch action \"Toggle\".", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Umschalten" + "Flip" : { + "comment" : "Display name for the switch action \"Toggle\".", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Umschalten" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Cambiar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cambiar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Vaihda" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vaihda" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Basculer" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basculer" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Inverti" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inverti" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Bytt" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bytt" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Omzetten" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Omzetten" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Переключить" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Переключить" } } } }, - "Font": { - "comment": "A label for the font picker in the screen saver settings view.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Schrift" + "Font" : { + "comment" : "A label for the font picker in the screen saver settings view.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schrift" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Font" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Font" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Tipo de letra" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tipo de letra" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Fontti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fontti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Police" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Police" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Font" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Font" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Skrift" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skrift" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Lettertype" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lettertype" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Шрифт" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Шрифт" } } } }, - "Font Size": { - "comment": "A section header that indicates the font size settings.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Schriftgröße" + "Font Size" : { + "comment" : "A section header that indicates the font size settings.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schriftgröße" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Font Size" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Font Size" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Tamaño de letra" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tamaño de letra" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Fonttikoko" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fonttikoko" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Taille de la police" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Taille de la police" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Dimensioni font" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dimensioni font" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Skriftstørrelse" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skriftstørrelse" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Lettergrootte" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lettergrootte" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Размер шрифта" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Размер шрифта" } } } }, - "Foo": { - "comment": "A label for a text field where the user can input \"Foo\".", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Foo" + "Foo" : { + "comment" : "A label for a text field where the user can input \"Foo\".", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Foo" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Foo" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Foo" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Foo" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Foo" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Foo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Foo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Foo" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Foo" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Foo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Foo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Foo" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Foo" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Foo" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Foo" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Foo" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Foo" } } } }, - "For Shortcuts to work across multiple devices, each home must have the same name on every device.": { - "comment": "Instruction shown in Manage Homes explaining that home names must match across devices for Shortcuts to work.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Damit Kurzbefehle geräteübergreifend funktionieren, muss jedes Zuhause auf jedem Gerät denselben Namen haben." + "For Shortcuts to work across multiple devices, each home must have the same name on every device." : { + "comment" : "Instruction shown in Manage Homes explaining that home names must match across devices for Shortcuts to work.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Damit Kurzbefehle geräteübergreifend funktionieren, muss jedes Zuhause auf jedem Gerät denselben Namen haben." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "For Shortcuts to work across multiple devices, each home must have the same name on every device." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "For Shortcuts to work across multiple devices, each home must have the same name on every device." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Para que los atajos funcionen en varios dispositivos, cada hogar debe tener el mismo nombre en cada dispositivo." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Para que los atajos funcionen en varios dispositivos, cada hogar debe tener el mismo nombre en cada dispositivo." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Jotta pikakuvakkeet toimisivat useilla laitteilla, jokaisella kodilla täytyy olla sama nimi joka laitteella." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jotta pikakuvakkeet toimisivat useilla laitteilla, jokaisella kodilla täytyy olla sama nimi joka laitteella." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Pour que les raccourcis fonctionnent sur plusieurs appareils, chaque maison doit avoir le même nom sur chaque appareil." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pour que les raccourcis fonctionnent sur plusieurs appareils, chaque maison doit avoir le même nom sur chaque appareil." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Per far funzionare i comandi rapidi su più dispositivi, ogni casa deve avere lo stesso nome su ogni dispositivo." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Per far funzionare i comandi rapidi su più dispositivi, ogni casa deve avere lo stesso nome su ogni dispositivo." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "For at snarveier skal fungere på tvers av enheter, må hvert hjem ha samme navn på hver enhet." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "For at snarveier skal fungere på tvers av enheter, må hvert hjem ha samme navn på hver enhet." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Om snelkoppelingen op meerdere apparaten te laten werken, moet elk thuis dezelfde naam hebben op elk apparaat." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Om snelkoppelingen op meerdere apparaten te laten werken, moet elk thuis dezelfde naam hebben op elk apparaat." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Чтобы быстрые команды работали на нескольких устройствах, у каждого дома должно быть одинаковое название на каждом устройстве." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Чтобы быстрые команды работали на нескольких устройствах, у каждого дома должно быть одинаковое название на каждом устройстве." } } } }, - "Get ${itemEntity} State": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "${itemEntity}-Status erhalten" + "Get ${itemEntity} State" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "${itemEntity}-Status erhalten" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Obtener estado de ${itemEntity}" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Obtener estado de ${itemEntity}" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Hae ${itemEntity}-tila" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hae ${itemEntity}-tila" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Obtenir l'état de ${itemEntity}" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Obtenir l'état de ${itemEntity}" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Ottieni lo stato di ${itemEntity}" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ottieni lo stato di ${itemEntity}" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Hent status for ${itemEntity}" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hent status for ${itemEntity}" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Status van ${itemEntity} ophalen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Status van ${itemEntity} ophalen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Получить состояние ${itemEntity}" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Получить состояние ${itemEntity}" } } } }, - "Get Item State": { - "comment": "Title of the Get Item State app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Status eines Items erhalten" + "Get Item State" : { + "comment" : "Title of the Get Item State app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Status eines Items erhalten" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Get Item State" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Get Item State" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Obtener estado del ítem" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Obtener estado del ítem" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Hae elementin tila" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hae elementin tila" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Récupérer l'état de l'item" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Récupérer l'état de l'item" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Leggi lo stato dell'Item" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Leggi lo stato dell'Item" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Få Item-tilstand" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Få Item-tilstand" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Haal item state op" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Haal item state op" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Получить состояние элемента" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Получить состояние элемента" } } } }, - "Go backward": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zurückgehen" + "Go backward" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zurückgehen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Ir atrás" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ir atrás" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Mene taaksepäin" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mene taaksepäin" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Aller en arrière" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aller en arrière" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Vai indietro" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vai indietro" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Gå bakover" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gå bakover" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Ga terug" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ga terug" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Назад" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Назад" } } } }, - "habpanel": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "HABPanel" + "habpanel" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "HABPanel" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "HABPanel" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "HABPanel" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "HABPanel" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "HABPanel" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "HABPanel" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "HABPanel" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "HABPanel" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "HABPanel" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "HABPanel" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "HABPanel" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "HABPanel" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "HABPanel" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "HABPanel" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "HABPanel" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "HABPanel" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "HABPanel" } } } }, - "Hide Status Bar": { - "comment": "A toggle that allows the user to hide the status bar in the app.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Statusleiste ausblenden" + "Hide Status Bar" : { + "comment" : "A toggle that allows the user to hide the status bar in the app.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Statusleiste ausblenden" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Hide Status Bar" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hide Status Bar" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Ocultar barra de estado" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ocultar barra de estado" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Piilota tilapalkki" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Piilota tilapalkki" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Masquer la barre d'état" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Masquer la barre d'état" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Nascondi barra di stato" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nascondi barra di stato" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Skjul statuslinje" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skjul statuslinje" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Statusbalk verbergen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Statusbalk verbergen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Скрыть строку состояния" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Скрыть строку состояния" } } } }, - "Hold": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Halten" + "Hold" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Halten" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Pausar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pausar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Pidä tauko" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pidä tauko" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Mettre en pause" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mettre en pause" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Pausa" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pausa" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Hold" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hold" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Pauzeren" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pauzeren" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Приостановить" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Приостановить" } } } }, - "Home": { - "comment": "The name of the \"Home\" menu item.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zuhause" + "Home" : { + "comment" : "The name of the \"Home\" menu item.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zuhause" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Home" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Home" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Casa" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Casa" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Koti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Koti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Domicile" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Domicile" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Casa" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Casa" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Hjem" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hjem" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Thuis" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Thuis" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Дом" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Дом" } } } }, - "Icon Type": { - "comment": "A label describing the option to change the icon type.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Icon-Typ" + "Icon Type" : { + "comment" : "A label describing the option to change the icon type.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Icon-Typ" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Icon Type" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Icon Type" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Tipo de icono" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tipo de icono" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kuvaketyyppi" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kuvaketyyppi" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Type d'icône" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Type d'icône" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Tipo icona" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tipo icona" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ikonstil" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ikonstil" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Pictogramtype" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pictogramtype" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Тип значка" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Тип значка" } } } }, - "icon_type": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Icon-Typ" + "icon_type" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Icon-Typ" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Icon Type" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Icon Type" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Tipo de icono" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tipo de icono" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Icon Type" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Icon Type" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Type d'icône" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Type d'icône" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Tipo di icona" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tipo di icona" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ikontype" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ikontype" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Icoontype" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Icoontype" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Icon Type" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Icon Type" } } } }, - "Idle Interval: %lld s": { - "comment": "A stepper that allows the user to set the number of seconds after which the screen saver will activate if no movement is detected. The value shown is the current idle interval in seconds.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Ruhezustand-Intervall: %lld s" + "Idle Interval: %lld s" : { + "comment" : "A stepper that allows the user to set the number of seconds after which the screen saver will activate if no movement is detected. The value shown is the current idle interval in seconds.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ruhezustand-Intervall: %lld s" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Idle Interval: %lld s" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Idle Interval: %lld s" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Intervalo de reposo: %lld s" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Intervalo de reposo: %lld s" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Lepoväli: %lld s" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lepoväli: %lld s" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Intervalle d'inactivité : %lld s" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Intervalle d'inactivité : %lld s" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Intervallo inattività: %lld s" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Intervallo inattività: %lld s" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Inaktivitetsintervall: %lld s" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inaktivitetsintervall: %lld s" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Inactiviteitsinterval: %lld s" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inactiviteitsinterval: %lld s" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Интервал простоя: %lld с" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Интервал простоя: %lld с" } } } }, - "Ignore SSL certificates": { - "comment": "A toggle that lets the user ignore SSL certificate warnings.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "SSL-Zertifikate ignorieren" + "Ignore SSL certificates" : { + "comment" : "A toggle that lets the user ignore SSL certificate warnings.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL-Zertifikate ignorieren" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Ignore SSL certificates" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ignore SSL certificates" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Ignorar certificados SSL" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ignorar certificados SSL" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ohita SSL-varmenteet" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ohita SSL-varmenteet" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Ignorer les certificats SSL" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ignorer les certificats SSL" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Ignora certificati SSL" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ignora certificati SSL" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ignorer SSL-sertifikater" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ignorer SSL-sertifikater" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "SSL-certificaten negeren" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL-certificaten negeren" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Игнорировать SSL-сертификаты" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Игнорировать SSL-сертификаты" } } } }, - "ignore_ssl_certificates": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "SSL-Zertifikate ignorieren" + "ignore_ssl_certificates" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL-Zertifikate ignorieren" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Ignore SSL Certificates" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ignore SSL Certificates" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Ignorar certificados SSL" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ignorar certificados SSL" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ignore SSL Certificates" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ignore SSL Certificates" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Ignorer les certificats SSL" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ignorer les certificats SSL" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Ignora certificati SSL" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ignora certificati SSL" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ignorer SSL-sertifikater" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ignorer SSL-sertifikater" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Negeer SSL-certificaten" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Negeer SSL-certificaten" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Игнорировать SSL-сертификаты" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Игнорировать SSL-сертификаты" } } } }, - "Image Cache": { - "comment": "The title of an alert that shows the result of checking and clearing the image cache.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Bilder-Cache" + "Image Cache" : { + "comment" : "The title of an alert that shows the result of checking and clearing the image cache.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bilder-Cache" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Image Cache" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Image Cache" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Caché de imágenes" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Caché de imágenes" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kuvavälimuisti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kuvavälimuisti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Cache d'images" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cache d'images" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Cache immagini" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cache immagini" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Bildebuffer" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bildebuffer" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Afbeeldingscache" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Afbeeldingscache" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Кэш изображений" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Кэш изображений" } } } }, - "Import": { - "comment": "\"Import\" button title.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Importieren" + "Import" : { + "comment" : "\"Import\" button title.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Importieren" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Import" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Import" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Importar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Importar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Tuo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tuo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Importer" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Importer" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Importa" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Importa" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Importer" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Importer" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Importeer" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Importeer" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Импорт" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Импорт" } } } }, - "Inactive": { - "comment": "Displayed when the contact is closed.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Inaktiv" + "Inactive" : { + "comment" : "Displayed when the contact is closed.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inaktiv" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Inactivo" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inactivo" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ei aktiivinen" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ei aktiivinen" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Inactif" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inactif" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Inattivo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inattivo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Inaktiv" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inaktiv" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Inactief" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inactief" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Неактивен" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Неактивен" } } } }, - "Increase %@": { - "comment": "A button that increases the value of a setpoint. The argument is the name of the setpoint.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "%@ erhöhen" + "Increase %@" : { + "comment" : "A button that increases the value of a setpoint. The argument is the name of the setpoint.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ erhöhen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Increase %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Increase %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Aumentar %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aumentar %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Suurenna %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suurenna %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Augmenter %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Augmenter %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Aumenta %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aumenta %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Øk %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Øk %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "%@ verhogen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ verhogen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Увеличить %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Увеличить %@" } } } }, - "info": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Info" + "info" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Info" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Info" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Info" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Información" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Información" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Info" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Info" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Infos" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Infos" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Informazioni" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Informazioni" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Informasjon" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Informasjon" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Info" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Info" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Info" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Info" } } } }, - "Invalid value %lld for %@ (0-100)": { - "comment": "Error message when a dimmer or roller shutter value is out of range. First placeholder is the invalid value, second is the item name.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Ungültiger Wert %lld für %@ (0-100)" + "Invalid value %lld for %@ (0-100)" : { + "comment" : "Error message when a dimmer or roller shutter value is out of range. First placeholder is the invalid value, second is the item name.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ungültiger Wert %lld für %@ (0-100)" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Invalid value %lld for %@ (0-100)" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Invalid value %lld for %@ (0-100)" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Valor no válido %lld para %@ (0-100)" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valor no válido %lld para %@ (0-100)" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Virheellinen arvo %lld kohteelle %@ (0–100)" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Virheellinen arvo %lld kohteelle %@ (0–100)" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Valeur %lld invalide pour %@ (0-100)" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valeur %lld invalide pour %@ (0-100)" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Valore non valido %lld per %@ (0-100)" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valore non valido %lld per %@ (0-100)" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ugyldig verdi %lld for %@ (0-100)" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ugyldig verdi %lld for %@ (0-100)" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Ongeldige waarde %lld voor %@ (0-100)" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ongeldige waarde %lld voor %@ (0-100)" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Недопустимое значение %lld для %@ (0–100)" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Недопустимое значение %lld для %@ (0–100)" } } } }, - "Invalid value: %@ for %@ must be HSB (0-360,0-100,0-100)": { - "comment": "Error message when a color value is not valid HSB. First placeholder is the invalid value, second is the item name.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Ungültiger Wert: %@ für %@ muss HSB sein (0-360,0-100,0-100)" + "Invalid value: %@ for %@ must be HSB (0-360,0-100,0-100)" : { + "comment" : "Error message when a color value is not valid HSB. First placeholder is the invalid value, second is the item name.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ungültiger Wert: %@ für %@ muss HSB sein (0-360,0-100,0-100)" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Invalid value: %@ for %@ must be HSB (0-360,0-100,0-100)" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Invalid value: %@ for %@ must be HSB (0-360,0-100,0-100)" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Valor no válido: %@ para %@ debe ser HSB (0-360,0-100,0-100)" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valor no válido: %@ para %@ debe ser HSB (0-360,0-100,0-100)" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Virheellinen arvo: %@ kohteelle %@ täytyy olla HSB (0–360, 0–100, 0–100)" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Virheellinen arvo: %@ kohteelle %@ täytyy olla HSB (0–360, 0–100, 0–100)" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Valeur invalide : %@ pour %@ doit être HSB (0-360,0-100,0-100)" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valeur invalide : %@ pour %@ doit être HSB (0-360,0-100,0-100)" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Valore non valido: %@ per %@ deve essere HSB (0-360,0-100,0-100)" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valore non valido: %@ per %@ deve essere HSB (0-360,0-100,0-100)" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ugyldig verdi: %@ for %@ må være HSB (0-360,0-100,0-100)" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ugyldig verdi: %@ for %@ må være HSB (0-360,0-100,0-100)" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Ongeldige waarde: %@ voor %@ moet HSB zijn (0-360,0-100,0-100)" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ongeldige waarde: %@ voor %@ moet HSB zijn (0-360,0-100,0-100)" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Недопустимое значение: %@ для %@ должно быть HSB (0–360, 0–100, 0–100)" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Недопустимое значение: %@ для %@ должно быть HSB (0–360, 0–100, 0–100)" } } } }, - "invalid_connection_configuration": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Ungültige Verbindungskonfiguration." + "invalid_connection_configuration" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ungültige Verbindungskonfiguration." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Invalid connection configuration." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Invalid connection configuration." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Configuración de conexión no válida." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración de conexión no válida." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Virheellinen yhteysasetukset." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Virheellinen yhteysasetukset." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Configuration de connexion invalide." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuration de connexion invalide." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Configurazione della connessione non valida." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configurazione della connessione non valida." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ugyldig tilkoblingskonfigurasjon." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ugyldig tilkoblingskonfigurasjon." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Ongeldige verbindingsconfiguratie." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ongeldige verbindingsconfiguratie." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Недопустимая конфигурация подключения." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Недопустимая конфигурация подключения." } } } }, - "Item": { - "comment": "Parameter title for an openHAB item in app intents.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Item" + "Item" : { + "comment" : "Parameter title for an openHAB item in app intents.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Item" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Item" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Item" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Ítem" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ítem" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Elementti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elementti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Item" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Item" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Item" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Item" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Item" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Item" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Item" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Item" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент" } } } }, - "Item '%@' is not in home '%@'": { - "comment": "Error message when the selected item does not belong to the selected home. First placeholder is the item name, second is the home name.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Das Item „%@“ befindet sich nicht im Zuhause „%@“" + "Item '%@' is not in home '%@'" : { + "comment" : "Error message when the selected item does not belong to the selected home. First placeholder is the item name, second is the home name.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Das Item „%@“ befindet sich nicht im Zuhause „%@“" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Item '%@' is not in home '%@'" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Item '%@' is not in home '%@'" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "El elemento ‘%@’ no está en el hogar ‘%@’" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "El elemento ‘%@’ no está en el hogar ‘%@’" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Elementti ‘%@’ ei ole kodissa ‘%@’" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elementti ‘%@’ ei ole kodissa ‘%@’" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "L'élément '%@' n'est pas dans la maison '%@'" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "L'élément '%@' n'est pas dans la maison '%@'" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "L'elemento '%@' non si trova nella casa '%@'" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "L'elemento '%@' non si trova nella casa '%@'" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Element ‘%@’ er ikke i hjemmet ‘%@’" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Element ‘%@’ er ikke i hjemmet ‘%@’" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Item '%@' staat niet in huis '%@'" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Item '%@' staat niet in huis '%@'" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент «%@» не входит в дом «%@»" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент «%@» не входит в дом «%@»" } } } }, - "Item '%@' not found": { - "comment": "Error message when an item is not found. The argument is the name of the item.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Element '%@' nicht gefunden" + "Item '%@' not found" : { + "comment" : "Error message when an item is not found. The argument is the name of the item.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Element '%@' nicht gefunden" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento ‘%@’ no encontrado" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento ‘%@’ no encontrado" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Elementtiä ‘%@’ ei löydy" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elementtiä ‘%@’ ei löydy" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Élément '%@' introuvable" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément '%@' introuvable" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elemento '%@' non trovato" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento '%@' non trovato" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Element ‘%@’ ikke funnet" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Element ‘%@’ ikke funnet" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Item '%@' niet gevonden" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Item '%@' niet gevonden" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент «%@» не найден" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент «%@» не найден" } } } }, - "Items": { - "comment": "The title of the view that lists available items.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Items" + "Items" : { + "comment" : "The title of the view that lists available items.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Items" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Items" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Items" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Ítems" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ítems" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Elementit" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elementit" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Éléments" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Éléments" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elementi" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elementi" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Elementer" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elementer" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Onderdelen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Onderdelen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элементы" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элементы" } } } }, - "Label": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Label" + "Label" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Label" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Label" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Label" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Etiqueta" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Etiqueta" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Etiketti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Etiketti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Étiquette" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Étiquette" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Etichetta" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Etichetta" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Etikett" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Etikett" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Label" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Label" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Метка" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Метка" } } } }, - "Latitude": { - "comment": "Parameter title for latitude in the Set Location Control Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Breitengrad" + "Latitude" : { + "comment" : "Parameter title for latitude in the Set Location Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Breitengrad" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Latitude" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Latitude" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Latitud" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Latitud" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Leveysaste" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Leveysaste" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Latitude" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Latitude" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Latitudine" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Latitudine" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Breddegrad" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Breddegrad" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Breedtegraad" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Breedtegraad" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Широта" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Широта" } } } }, - "Latitude must be between -90 and 90": { - "comment": "Error message when a latitude value is out of the valid range.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Breitengrad muss zwischen -90 and 90 sein" + "Latitude must be between -90 and 90" : { + "comment" : "Error message when a latitude value is out of the valid range.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Breitengrad muss zwischen -90 and 90 sein" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Latitude must be between -90 and 90" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Latitude must be between -90 and 90" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "La latitud debe estar entre -90 y 90" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "La latitud debe estar entre -90 y 90" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Leveysasteen on oltava välillä −90 ja 90" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Leveysasteen on oltava välillä −90 ja 90" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "La latitude doit être comprise entre -90 et 90" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "La latitude doit être comprise entre -90 et 90" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "La latitudine deve essere compresa tra -90 e 90" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "La latitudine deve essere compresa tra -90 e 90" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Breddegraden må være mellom -90 og 90" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Breddegraden må være mellom -90 og 90" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Breedtegraad moet tussen -90 en 90 liggen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Breedtegraad moet tussen -90 en 90 liggen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Широта должна быть от -90 до 90" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Широта должна быть от -90 до 90" } } } }, - "legal": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Rechtliches" + "legal" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rechtliches" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Legal" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Legal" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Legal" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Legal" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Legal" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Legal" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Légal" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Légal" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Legale" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Legale" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Juridisk" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Juridisk" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Juridisch" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Juridisch" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Legal" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Legal" } } } }, - "Legal": { - "comment": "A link to the app's legal information.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Rechtliches" + "Legal" : { + "comment" : "A link to the app's legal information.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rechtliches" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Legal" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Legal" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Aviso legal" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aviso legal" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Oikeudelliset tiedot" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Oikeudelliset tiedot" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Mentions légales" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mentions légales" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Note legali" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Note legali" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Juridisk" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Juridisk" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Juridische informatie" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Juridische informatie" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Юридическая информация" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Юридическая информация" } } } }, - "Loading Items…": { - "comment": "A message displayed while waiting to load items.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Items werden geladen …" + "Loading Items…" : { + "comment" : "A message displayed while waiting to load items.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Items werden geladen …" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Loading Items…" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loading Items…" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Cargando elementos…" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cargando elementos…" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ladataan elementtejä…" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ladataan elementtejä…" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Chargement des éléments…" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chargement des éléments…" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Caricamento elementi…" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Caricamento elementi…" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Laster elementer…" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Laster elementer…" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Items worden geladen…" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Items worden geladen…" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Загрузка элементов…" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Загрузка элементов…" } } } }, - "Loading sitemap…": { - "comment": "A placeholder text indicating that the sitemap is being loaded.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sitemap wird geladen …" + "Loading sitemap…" : { + "comment" : "A placeholder text indicating that the sitemap is being loaded.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap wird geladen …" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Loading sitemap…" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loading sitemap…" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Cargando mapa del sitio…" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cargando mapa del sitio…" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ladataan sivukarttaa…" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ladataan sivukarttaa…" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Chargement du plan du site…" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chargement du plan du site…" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Caricamento sitemap…" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Caricamento sitemap…" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Laster nettstedskart…" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Laster nettstedskart…" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Sitemap wordt geladen…" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap wordt geladen…" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Загрузка карты сайта…" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Загрузка карты сайта…" } } } }, - "Loading…": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Wird geladen …" + "Loading…" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wird geladen …" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Loading…" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loading…" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Cargando…" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cargando…" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ladataan …" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ladataan …" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Chargement…" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chargement…" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Caricamento …" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Caricamento …" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Laster …" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Laster …" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Laden…" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Laden…" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Загрузка …" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Загрузка …" } } } }, - "Local Network access may be required.": { - "comment": "A warning message that appears when the user's local network access is required to connect to a server.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Möglicherweise ist der Zugriff auf das lokale Netzwerk erforderlich." + "Local Network access may be required." : { + "comment" : "A warning message that appears when the user's local network access is required to connect to a server.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Möglicherweise ist der Zugriff auf das lokale Netzwerk erforderlich." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Es posible que se requiera acceso a la red local." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Es posible que se requiera acceso a la red local." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Lähiverkon käyttöoikeus voi olla tarpeen." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lähiverkon käyttöoikeus voi olla tarpeen." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "L'accès au réseau local peut être requis." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "L'accès au réseau local peut être requis." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Potrebbe essere necessario l'accesso alla rete locale." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Potrebbe essere necessario l'accesso alla rete locale." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Tilgang til lokalt nettverk kan være nødvendig." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tilgang til lokalt nettverk kan være nødvendig." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Toegang tot lokaal netwerk kan vereist zijn." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toegang tot lokaal netwerk kan vereist zijn." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Может потребоваться доступ к локальной сети." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Может потребоваться доступ к локальной сети." } } } }, - "Local Network Access Required": { - "comment": "A title for an alert that warns the user that local network access is required to connect to a local openHAB server.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zugriff auf lokales Netzwerk erforderlich" + "Local Network Access Required" : { + "comment" : "A title for an alert that warns the user that local network access is required to connect to a local openHAB server.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zugriff auf lokales Netzwerk erforderlich" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Se requiere acceso a la red local" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Se requiere acceso a la red local" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Lähiverkon käyttöoikeus vaaditaan" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lähiverkon käyttöoikeus vaaditaan" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Accès au réseau local requis" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Accès au réseau local requis" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Accesso alla rete locale richiesto" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Accesso alla rete locale richiesto" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Tilgang til lokalt nettverk kreves" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tilgang til lokalt nettverk kreves" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Toegang tot lokaal netwerk vereist" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toegang tot lokaal netwerk vereist" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Требуется доступ к локальной сети" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Требуется доступ к локальной сети" } } } }, - "Local server": { - "comment": "Title of the section in the connection settings view that pertains to the local OpenHAB server.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Lokaler Server" + "Local server" : { + "comment" : "Title of the section in the connection settings view that pertains to the local OpenHAB server.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lokaler Server" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Local server" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Local server" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Servidor local" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Servidor local" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Paikallinen palvelin" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Paikallinen palvelin" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Serveur local" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Serveur local" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Server locale" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Server locale" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Lokal server" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lokal server" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Lokale server" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lokale server" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Локальный сервер" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Локальный сервер" } } } }, - "local_url": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Lokale URL" + "local_url" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lokale URL" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Local URL" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Local URL" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "URL local" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL local" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Local URL" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Local URL" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "URL locale" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL locale" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "URL locale" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL locale" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Lokal URL" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lokal URL" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Lokale URL" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lokale URL" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Локальный URL-адрес" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Локальный URL-адрес" } } } }, - "Location": { - "comment": "Name of a marker displayed on a map view.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Standort" + "Location" : { + "comment" : "Name of a marker displayed on a map view.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Standort" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Location" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Location" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Ubicación" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ubicación" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Sijainti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sijainti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Emplacement" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Emplacement" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Posizione" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Posizione" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Plassering" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Plassering" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Locatie" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Locatie" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Местоположение" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Местоположение" } } } }, - "Location Item": { - "comment": "Display name for a location item type.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Standort-Item" + "Location Item" : { + "comment" : "Display name for a location item type.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Standort-Item" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento de ubicación" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento de ubicación" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Sijaintielementti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sijaintielementti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Élément de localisation" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément de localisation" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elemento posizione" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento posizione" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Plasseringselement" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Plasseringselement" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Locatie-item" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Locatie-item" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент местоположения" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент местоположения" } } } }, - "Logs": { - "comment": "A link in the debug settings that navigates to the logs viewer.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Protokolle" + "Logs" : { + "comment" : "A link in the debug settings that navigates to the logs viewer.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Protokolle" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Logs" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Logs" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Registros" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Registros" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Lokit" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lokit" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Historiques" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Historiques" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Log" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Log" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Logger" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Logger" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Logbestanden" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Logbestanden" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Журналы" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Журналы" } } } }, - "Long press the widget to configure which sensors to display": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Widget lange drücken, um die anzuzeigenden Sensoren zu konfigurieren" + "Long press the widget to configure which sensors to display" : { + "comment" : "A description of the action to configure the widget.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Widget lange drücken, um die anzuzeigenden Sensoren zu konfigurieren" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Mantén presionado el widget para configurar qué sensores mostrar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mantén presionado el widget para configurar qué sensores mostrar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Paina widgettiä pitkään määrittääksesi, mitkä anturit näytetään" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Paina widgettiä pitkään määrittääksesi, mitkä anturit näytetään" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Appuyez longuement sur le widget pour configurer les capteurs à afficher" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Appuyez longuement sur le widget pour configurer les capteurs à afficher" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Tieni premuto il widget per configurare quali sensori mostrare" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tieni premuto il widget per configurare quali sensori mostrare" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Trykk og hold på widgeten for å konfigurere hvilke sensorer som skal vises" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trykk og hold på widgeten for å konfigurere hvilke sensorer som skal vises" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Houd de widget ingedrukt om te configureren welke sensoren worden weergegeven" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Houd de widget ingedrukt om te configureren welke sensoren worden weergegeven" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Удерживайте виджет, чтобы настроить, какие датчики отображать" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Удерживайте виджет, чтобы настроить, какие датчики отображать" } } - }, - "comment": "A description of the action to configure the widget.", - "isCommentAutoGenerated": true + } }, - "Long press the widget to configure which switches to control": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Widget lange drücken, um die zu steuernden Schalter zu konfigurieren" + "Long press the widget to configure which switches to control" : { + "comment" : "A description of the action to configure a widget.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Widget lange drücken, um die zu steuernden Schalter zu konfigurieren" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Mantén presionado el widget para configurar qué interruptores controlar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mantén presionado el widget para configurar qué interruptores controlar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Paina widgettiä pitkään määrittääksesi, mitä kytkimiä ohjataan" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Paina widgettiä pitkään määrittääksesi, mitä kytkimiä ohjataan" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Appuyez longuement sur le widget pour configurer les commutateurs à contrôler" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Appuyez longuement sur le widget pour configurer les commutateurs à contrôler" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Tieni premuto il widget per configurare quali interruttori controllare" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tieni premuto il widget per configurare quali interruttori controllare" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Trykk og hold på widgeten for å konfigurere hvilke brytere som skal styres" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trykk og hold på widgeten for å konfigurere hvilke brytere som skal styres" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Houd de widget ingedrukt om te configureren welke schakelaars worden bediend" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Houd de widget ingedrukt om te configureren welke schakelaars worden bediend" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Удерживайте виджет, чтобы настроить, какими переключателями управлять" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Удерживайте виджет, чтобы настроить, какими переключателями управлять" } } - }, - "comment": "A description of the action to configure a widget.", - "isCommentAutoGenerated": true + } }, - "Longitude": { - "comment": "Parameter title for longitude in the Set Location Control Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Längengrad" + "Longitude" : { + "comment" : "Parameter title for longitude in the Set Location Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Längengrad" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Longitude" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Longitude" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Longitud" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Longitud" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Pituusaste" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pituusaste" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Longitude" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Longitude" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Longitudine" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Longitudine" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Lengdegrad" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lengdegrad" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Lengtegraad" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lengtegraad" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Долгота" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Долгота" } } } }, - "Longitude must be between -180 and 180": { - "comment": "Error message when a longitude value is out of the valid range.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Längengrad muss zwischen -180 and 180 sein" + "Longitude must be between -180 and 180" : { + "comment" : "Error message when a longitude value is out of the valid range.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Längengrad muss zwischen -180 and 180 sein" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Longitude must be between -180 and 180" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Longitude must be between -180 and 180" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "La longitud debe estar entre -180 y 180" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "La longitud debe estar entre -180 y 180" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Pituusasteen on oltava välillä −180 ja 180" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pituusasteen on oltava välillä −180 ja 180" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "La longitude doit être comprise entre -180 et 180" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "La longitude doit être comprise entre -180 et 180" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "La longitudine deve essere compresa tra -180 e 180" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "La longitudine deve essere compresa tra -180 e 180" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Lengdegraden må være mellom -180 og 180" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lengdegraden må være mellom -180 og 180" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Lengtegraad moet tussen -180 en 180 liggen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lengtegraad moet tussen -180 en 180 liggen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Долгота должна быть от -180 до 180" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Долгота должна быть от -180 до 180" } } } }, - "Lowers by %@": { - "comment": "A hint that describes how much the value can be decreased by.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Um %@ senken" + "Lowers by %@" : { + "comment" : "A hint that describes how much the value can be decreased by.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Um %@ senken" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Lowers by %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lowers by %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Baja %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Baja %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Laskee %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Laskee %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Abaisse de %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abaisse de %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Abbassa di %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abbassa di %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Senker med %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Senker med %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Verlaagt met %@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verlaagt met %@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Снижает на %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Снижает на %@" } } } }, - "Main": { - "comment": "The header text for the \"Main\" section in the drawer view.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Main" + "Main" : { + "comment" : "The header text for the \"Main\" section in the drawer view.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Main" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Main" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Main" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Principal" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Principal" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Pää" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pää" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Principal" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Principal" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Principale" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Principale" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Hoved" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hoved" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Hoofd" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hoofd" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Главная" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Главная" } } } }, - "mainui_settings": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Main UI-Einstellungen" + "mainui_settings" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Main UI-Einstellungen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Main UI Settings" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Main UI Settings" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Ajustes de la interfaz de usuario principal" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajustes de la interfaz de usuario principal" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Main UI Settings" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Main UI Settings" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Paramètres Main UI" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Paramètres Main UI" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Impostazioni Main UI" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impostazioni Main UI" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Innstillinger for Main UI" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Innstillinger for Main UI" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Main UI Instellingen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Main UI Instellingen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Main UI Settings" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Main UI Settings" } } } }, - "Manage Homes": { - "comment": "The title of a view that allows users to manage their homes.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zuhause verwalten" + "Manage Homes" : { + "comment" : "The title of a view that allows users to manage their homes.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zuhause verwalten" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Manage Homes" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manage Homes" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Gestionar hogares" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gestionar hogares" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Hallitse koteja" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hallitse koteja" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Gérer les maisons" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gérer les maisons" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Gestisci case" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gestisci case" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Administrer hjem" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Administrer hjem" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Huizen beheren" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Huizen beheren" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Управление домами" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Управление домами" } } } }, - "message_not_decoded": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Nachricht konnte nicht dekodiert werden" + "message_not_decoded" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nachricht konnte nicht dekodiert werden" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Message could not be decoded" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Message could not be decoded" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "No se ha podido decodificar el mensaje" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se ha podido decodificar el mensaje" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Message could not be decoded" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Message could not be decoded" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Le message n'a pas pu être décodé" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Le message n'a pas pu être décodé" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Il messaggio non può essere decodificato" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Il messaggio non può essere decodificato" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Melding kunne ikke dekodes" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Melding kunne ikke dekodes" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Bericht kon niet gedecodeerd worden" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bericht kon niet gedecodeerd worden" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Message could not be decoded" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Message could not be decoded" } } } }, - "Movement Interval: %lld s": { - "comment": "A stepper that allows the user to set the interval in seconds between when the screen saver will activate and when it will deactivate after a user is detected moving.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Bewegungsintervall: %lld s" + "Movement Interval: %lld s" : { + "comment" : "A stepper that allows the user to set the interval in seconds between when the screen saver will activate and when it will deactivate after a user is detected moving.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bewegungsintervall: %lld s" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Movement Interval: %lld s" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Movement Interval: %lld s" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Intervalo de movimiento: %lld s" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Intervalo de movimiento: %lld s" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Liikeväli: %lld s" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Liikeväli: %lld s" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Intervalle de déplacement : %lld s" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Intervalle de déplacement : %lld s" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Intervallo di movimento: %lld s" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Intervallo di movimento: %lld s" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Bevegelsesintervall: %lld s" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bevegelsesintervall: %lld s" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Bewegingsinterval: %lld s" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bewegingsinterval: %lld s" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Интервал движения: %lld с" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Интервал движения: %lld с" } } } }, - "Name": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Name" + "Name" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Name" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Name" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Name" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Nombre" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nombre" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Nimi" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nimi" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Nom" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nom" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Nome" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nome" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Navn" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Navn" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Naam" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Naam" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Имя" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Имя" } } } }, - "Name for new home": { - "comment": "A placeholder label for a text field in an alert used to enter the name of a new home.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Name für neues Zuhause" + "Name for new home" : { + "comment" : "A placeholder label for a text field in an alert used to enter the name of a new home.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Name für neues Zuhause" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Name for new home" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Name for new home" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Nombre para el nuevo hogar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nombre para el nuevo hogar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Nimi uudelle kodille" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nimi uudelle kodille" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Nom de la nouvelle maison" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nom de la nouvelle maison" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Nome per la nuova casa" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nome per la nuova casa" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Navn for nytt hjem" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Navn for nytt hjem" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Naam voor nieuw huis" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Naam voor nieuw huis" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Имя для нового дома" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Имя для нового дома" } } } }, - "network_not_available": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Netzwerk ist nicht verfügbar." + "network_not_available" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Netzwerk ist nicht verfügbar." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Network is not available." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Network is not available." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Red no disponible." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Red no disponible." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Network is not available." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Network is not available." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Aucune connexion réseau disponible." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucune connexion réseau disponible." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Rete non disponibile." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rete non disponibile." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Nettverk er ikke tilgjengelig." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nettverk er ikke tilgjengelig." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Netwerk is niet beschikbaar." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Netwerk is niet beschikbaar." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Network is not available." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Network is not available." } } } }, - "New name": { - "comment": "A label for a text field where the user can enter a new name for a home.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Neuer Name" + "New name" : { + "comment" : "A label for a text field where the user can enter a new name for a home.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Neuer Name" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "New name" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "New name" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Nuevo nombre" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nuevo nombre" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Uusi nimi" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uusi nimi" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Nouveau nom" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nouveau nom" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Nuovo nome" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nuovo nome" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Nytt navn" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nytt navn" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Nieuwe naam" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nieuwe naam" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Новое имя" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Новое имя" } } } }, - "Next": { - "comment": "Display name for the next action in the PlayerAction enum.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Nächstes" + "Next" : { + "comment" : "Display name for the next action in the PlayerAction enum.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nächstes" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Next" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Next" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Siguiente" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Siguiente" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Seuraava" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seuraava" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Suivant" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suivant" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Successivo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Successivo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Neste" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Neste" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Volgende" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Volgende" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Следующий" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Следующий" } } } }, - "Next track": { - "comment": "Synonyms for the \"Next\" player action.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Nächster Titel" + "Next track" : { + "comment" : "Synonyms for the \"Next\" player action.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nächster Titel" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Siguiente pista" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Siguiente pista" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Seuraava raita" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seuraava raita" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Piste suivante" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Piste suivante" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Traccia successiva" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Traccia successiva" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Neste spor" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Neste spor" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Volgende nummer" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Volgende nummer" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Следующий трек" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Следующий трек" } } } }, - "No accepted server certificates": { - "comment": "A message displayed when there are no accepted server certificates.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Keine akzeptierten Server-Zertifikate" + "No accepted server certificates" : { + "comment" : "A message displayed when there are no accepted server certificates.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine akzeptierten Server-Zertifikate" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "No accepted server certificates" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No accepted server certificates" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "No hay certificados de servidor aceptados" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No hay certificados de servidor aceptados" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ei hyväksyttyjä palvelinvarmenteita" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ei hyväksyttyjä palvelinvarmenteita" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Aucun certificat serveur accepté" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucun certificat serveur accepté" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Nessun certificato server accettato" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nessun certificato server accettato" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ingen godkjente serversertifikater" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen godkjente serversertifikater" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Geen geaccepteerde servercertificaten" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geen geaccepteerde servercertificaten" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Нет принятых сертификатов сервера" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Нет принятых сертификатов сервера" } } } }, - "No Data": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Keine Daten" + "No Data" : { + "comment" : "A label displayed when a sensor has no data.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine Daten" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Sin datos" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sin datos" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ei tietoja" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ei tietoja" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Aucune donnée" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucune donnée" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Nessun dato" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nessun dato" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ingen data" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen data" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Geen gegevens" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geen gegevens" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Нет данных" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Нет данных" } } - }, - "comment": "A label displayed when a sensor has no data.", - "isCommentAutoGenerated": true + } }, - "No Image URL": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Keine Bild-URL" + "No Image URL" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine Bild-URL" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "No Image URL" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No Image URL" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Sin URL de imagen" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sin URL de imagen" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ei kuvan URL-osoitetta" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ei kuvan URL-osoitetta" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Aucune URL d'image" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucune URL d'image" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Nessun URL immagine" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nessun URL immagine" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ingen bilde-URL" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen bilde-URL" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Geen afbeeldings-URL" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geen afbeeldings-URL" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Нет URL изображения" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Нет URL изображения" } } } }, - "No sitemaps available": { - "comment": "A message indicating that there are no sitemaps available to choose from in the \"Sitemap For Apple Watch\" picker.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Keine Sitemaps verfügbar" + "No sitemaps available" : { + "comment" : "A message indicating that there are no sitemaps available to choose from in the \"Sitemap For Apple Watch\" picker.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine Sitemaps verfügbar" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "No sitemaps available" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No sitemaps available" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "No hay mapas del sitio disponibles" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No hay mapas del sitio disponibles" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ei sivukarttoja saatavilla" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ei sivukarttoja saatavilla" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Aucun plan du site disponible" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucun plan du site disponible" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Nessuna sitemap disponibile" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nessuna sitemap disponibile" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ingen nettstedskart tilgjengelig" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen nettstedskart tilgjengelig" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Geen sitemaps beschikbaar" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geen sitemaps beschikbaar" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Нет доступных карт сайта" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Нет доступных карт сайта" } } } }, - "No State": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Kein Status" + "No State" : { + "comment" : "A label displayed when a device is not available.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kein Status" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Sin estado" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sin estado" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ei tilaa" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ei tilaa" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Aucun état" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucun état" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Nessuno stato" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nessuno stato" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ingen tilstand" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen tilstand" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Geen status" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geen status" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Нет состояния" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Нет состояния" } } - }, - "comment": "A label displayed when a device is not available.", - "isCommentAutoGenerated": true + } }, - "No Video URL": { - "comment": "A message displayed when a video URL is not provided for a video row.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Keine Video-URL" + "No Video URL" : { + "comment" : "A message displayed when a video URL is not provided for a video row.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine Video-URL" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "No Video URL" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No Video URL" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Sin URL de vídeo" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sin URL de vídeo" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ei videon URL-osoitetta" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ei videon URL-osoitetta" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Aucune URL vidéo" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucune URL vidéo" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Nessun URL video" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nessun URL video" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ingen video-URL" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen video-URL" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Geen video-URL" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geen video-URL" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Нет URL видео" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Нет URL видео" } } } }, - "No widgets available.": { - "comment": "A message displayed when a user has no widgets configured.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Keine Widgets verfügbar." + "No widgets available." : { + "comment" : "A message displayed when a user has no widgets configured.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine Widgets verfügbar." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "No widgets available." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No widgets available." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "No hay widgets disponibles." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No hay widgets disponibles." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ei widgettejä saatavilla." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ei widgettejä saatavilla." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Aucun widget disponible." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucun widget disponible." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Nessun widget disponibile." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nessun widget disponibile." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ingen widgeter tilgjengelig." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen widgeter tilgjengelig." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Geen widgets beschikbaar." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geen widgets beschikbaar." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Нет доступных виджетов." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Нет доступных виджетов." } } } }, - "no_active_connection": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Keine aktive Verbindung vorhanden. Bitte Einstellungen prüfen" + "no_active_connection" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine aktive Verbindung vorhanden. Bitte Einstellungen prüfen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "No active connection available. Please check your settings." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No active connection available. Please check your settings." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "No active connection available. Please check your settings." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No active connection available. Please check your settings." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "No active connection available. Please check your settings." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "No active connection available. Please check your settings." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "No active connection available. Please check your settings." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "No active connection available. Please check your settings." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "No active connection available. Please check your settings." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "No active connection available. Please check your settings." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "No active connection available. Please check your settings." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "No active connection available. Please check your settings." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "No active connection available. Please check your settings." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "No active connection available. Please check your settings." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "No active connection available. Please check your settings." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "No active connection available. Please check your settings." } } } }, - "no_connection_will_reconnect": { - "comment": "Title of a popup message displayed when the OpenHAB client is not connected to the server and will automatically try to reconnect.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Keine Verbindung, erneuter Verbindungsversuch" + "no_connection_will_reconnect" : { + "comment" : "Title of a popup message displayed when the OpenHAB client is not connected to the server and will automatically try to reconnect.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine Verbindung, erneuter Verbindungsversuch" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "no_connection_will_reconnect" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "no_connection_will_reconnect" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Sin conexión, reintentando…" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sin conexión, reintentando…" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ei yhteyttä, yritetään uudelleen…" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ei yhteyttä, yritetään uudelleen…" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Pas de connexion, reconnexion en cours" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pas de connexion, reconnexion en cours" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Nessuna connessione, riconnessione in corso" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nessuna connessione, riconnessione in corso" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ingen tilkobling, kobler til igjen…" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen tilkobling, kobler til igjen…" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Geen verbinding, opnieuw verbinden" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geen verbinding, opnieuw verbinden" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Нет подключения, повторное подключение…" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Нет подключения, повторное подключение…" } } } }, - "no_servers_found": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "No servers found" + "no_servers_found" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "No servers found" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "No servers found" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No servers found" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "No servers found" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No servers found" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "No servers found" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "No servers found" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Aucun serveur trouvé" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucun serveur trouvé" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "No servers found" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "No servers found" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "No servers found" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "No servers found" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "No servers found" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "No servers found" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "No servers found" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "No servers found" } } } }, - "notification": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Benachrichtigung" + "None" : { + "comment" : "A label for the \"Sitemap for CarPlay\" picker.", + "isCommentAutoGenerated" : true + }, + "notification" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Benachrichtigung" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Notification" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notification" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Notificación" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notificación" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Notification" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notification" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Notification" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notification" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Notifica" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notifica" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Varsel" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Varsel" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Notificatie" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notificatie" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Notification" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notification" } } } }, - "notifications": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Benachrichtigungen" + "notifications" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Benachrichtigungen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Notifications" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notifications" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Notificaciones" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notificaciones" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Notifications" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notifications" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Notifications" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notifications" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Notifiche" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notifiche" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Varsler" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Varsler" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Notificaties" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notificaties" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Notifications" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notifications" } } } }, - "Notifications": { - "comment": "The title of the notifications view.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Benachrichtigungen" + "Notifications" : { + "comment" : "The title of the notifications view.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Benachrichtigungen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Notifications" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notifications" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Notificaciones" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notificaciones" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ilmoitukset" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ilmoitukset" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Notifications" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notifications" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Notifiche" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notifiche" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Varsler" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Varsler" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Notificaties" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notificaties" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Уведомления" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Уведомления" } } } }, - "Number Item": { - "comment": "Display name for a number item type.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Numerisches-Item" + "Number Item" : { + "comment" : "Display name for a number item type.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Numerisches-Item" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Élément numérique" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento numérico" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elemento numerico" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Numerinen kohde" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Numeriek item" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément numérique" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento numérico" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento numerico" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Numerinen kohde" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Numerisk element" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Numerisk element" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Numeriek item" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Числовой элемент" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Числовой элемент" } } } }, - "Off": { - "comment": "The text \"Off\" displayed in the accessibility value of the toggle.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Aus" + "Off" : { + "comment" : "The text \"Off\" displayed in the accessibility value of the toggle.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aus" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Off" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Off" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Desactivado" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Desactivado" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Pois" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pois" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Désactivé" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Désactivé" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "No" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "No" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Av" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Av" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Uit" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uit" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Выкл" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Выкл" } } } }, - "OFF": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "AUS" + "OFF" : { + "comment" : "A label that shows the state of a switch.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "AUS" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "APAGADO" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "APAGADO" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "POIS" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "POIS" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "ÉTEINT" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "ÉTEINT" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "SPENTO" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "SPENTO" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "AV" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "AV" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "UIT" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "UIT" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "ВЫКЛ" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "ВЫКЛ" } } - }, - "comment": "A label that shows the state of a switch.", - "isCommentAutoGenerated": true + } }, - "Offline": { - "comment": "A label indicating that the device is currently offline.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Offline" + "Offline" : { + "comment" : "A label indicating that the device is currently offline.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Offline" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Offline" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Offline" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Sin conexión" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sin conexión" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Offline" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Offline" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Déconnecté" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Déconnecté" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Non in linea" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Non in linea" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Frakoblet" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Frakoblet" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Offline" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Offline" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Не в сети" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Не в сети" } } } }, - "oh_secret": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "openHAB-Secret" + "oh_secret" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB-Secret" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "openHAB Secret" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Secret" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "código secreto de openHAB" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "código secreto de openHAB" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "openHAB Secret" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Secret" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Phrase secrète openHAB" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Phrase secrète openHAB" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "openHAB Secret" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Secret" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "openHAB Hemmelighet" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Hemmelighet" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "openHAB geheim" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB geheim" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "openHAB Secret" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Secret" } } } }, - "oh_uuid": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "openHAB-UUID" + "oh_uuid" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB-UUID" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "openHAB UUID" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB UUID" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "openHAB UUID" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB UUID" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "openHAB UUID" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB UUID" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "openHAB UUID" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB UUID" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "openHAB UUID" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB UUID" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "openHAB UUID" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB UUID" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "openHAB UUID" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB UUID" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "openHAB UUID" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB UUID" } } } }, - "oh_version": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "openHAB-Version" + "oh_version" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB-Version" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "openHAB Version" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Version" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Versión de openHAB" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Versión de openHAB" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "openHAB Version" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Version" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "openHAB Version" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Version" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Versione openHAB" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Versione openHAB" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "openHAB versjon" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB versjon" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "openHAB Versie" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Versie" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "openHAB Version" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Version" } } } }, - "OK": { - "comment": "The text for an OK button.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "OK" + "OK" : { + "comment" : "The text for an OK button.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "OK" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "OK" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "OK" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Aceptar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aceptar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "OK" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "OK" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "OK" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "OK" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "OK" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "OK" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "OK" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "OK" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "OK" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "OK" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "ОК" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "ОК" } } } }, - "On": { - "comment": "A label indicating that a switch is on.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "An" + "On" : { + "comment" : "A label indicating that a switch is on.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "An" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "On" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "On" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Encendido" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Encendido" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Päällä" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Päällä" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Activé" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activé" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Attivo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Attivo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "På" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "På" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Aan" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aan" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Вкл" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Вкл" } } } }, - "ON": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "EIN" + "ON" : { + "comment" : "A label that shows the state of a switch.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "EIN" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "ENCENDIDO" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "ENCENDIDO" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "PÄÄLLÄ" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "PÄÄLLÄ" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "ALLUMÉ" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "ALLUMÉ" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "ACCESO" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "ACCESO" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "PÅ" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "PÅ" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "AAN" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "AAN" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "ВКЛ" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "ВКЛ" } } - }, - "comment": "A label that shows the state of a switch.", - "isCommentAutoGenerated": true + } }, - "Once": { - "comment": "Button to allow the app to connect to the server only once, even if the certificate is invalid.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Einmal" + "Once" : { + "comment" : "Button to allow the app to connect to the server only once, even if the certificate is invalid.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Einmal" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Once" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Once" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Una vez" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Una vez" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kerran" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kerran" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Une fois" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Une fois" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Una volta" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Una volta" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Én gang" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Én gang" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Eenmalig" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eenmalig" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Один раз" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Один раз" } } } }, - "open": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "offen" + "open" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "offen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "open" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "open" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "abierto" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "abierto" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "open" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "open" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "ouvert" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "ouvert" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "aperto" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "aperto" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "åpen" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "åpen" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "open" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "open" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "open" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "open" } } } }, - "Open": { - "comment": "Displayed name for the 'Open' contact state.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Geöffnet" + "Open" : { + "comment" : "Displayed name for the 'Open' contact state.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geöffnet" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Abierto" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abierto" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Auki" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Auki" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Ouvert" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ouvert" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Aperto" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aperto" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Åpen" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Åpen" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Open" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Open" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Открыто" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Открыто" } } } }, - "Open Settings": { - "comment": "A button that opens the user's system settings.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Einstellungen öffnen" + "Open Settings" : { + "comment" : "A button that opens the user's system settings.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Einstellungen öffnen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Abrir ajustes" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abrir ajustes" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Avaa asetukset" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Avaa asetukset" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Ouvrir les réglages" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ouvrir les réglages" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Apri Impostazioni" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apri Impostazioni" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Åpne innstillinger" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Åpne innstillinger" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Instellingen openen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Instellingen openen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Открыть настройки" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Открыть настройки" } } } }, - "Opened": { - "comment": "A synonym for the \"Open\" contact state.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Geöffnet" + "Opened" : { + "comment" : "A synonym for the \"Open\" contact state.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geöffnet" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Abierto" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abierto" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Avattu" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Avattu" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Ouvert" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ouvert" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Aperto" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aperto" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Åpnet" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Åpnet" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Geopend" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geopend" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Открыт" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Открыт" } } } }, - "openHAB Cloud Service": { - "comment": "A toggle that allows the user to enable or disable notifications from the openHAB Cloud Service.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "openHAB Cloud-Dienst" + "openHAB Cloud Service" : { + "comment" : "A toggle that allows the user to enable or disable notifications from the openHAB Cloud Service.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Cloud-Dienst" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "openHAB Cloud Service" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Cloud Service" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Servicio en la nube openHAB" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Servicio en la nube openHAB" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "openHAB-pilvipalvelu" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB-pilvipalvelu" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Service openHAB Cloud" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Service openHAB Cloud" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Servizio openHAB Cloud" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Servizio openHAB Cloud" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "openHAB-skytjeneste" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB-skytjeneste" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "openHAB Cloud-dienst" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Cloud-dienst" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Облачный сервис openHAB" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Облачный сервис openHAB" } } } }, - "openHAB Sensor": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "openHAB-Sensor" + "openHAB Sensor" : { + "comment" : "Widget title.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB-Sensor" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Sensor de openHAB" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensor de openHAB" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "openHAB-anturi" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB-anturi" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Capteur openHAB" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Capteur openHAB" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Sensore openHAB" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensore openHAB" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "openHAB-sensor" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB-sensor" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "openHAB-sensor" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB-sensor" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Датчик openHAB" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Датчик openHAB" } } - }, - "comment": "Widget title.", - "isCommentAutoGenerated": true + } }, - "openHAB Switch": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "openHAB-Schalter" + "openHAB Switch" : { + "comment" : "Widget name.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB-Schalter" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Interruptor de openHAB" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Interruptor de openHAB" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "openHAB-kytkin" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB-kytkin" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Commutateur openHAB" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Commutateur openHAB" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Interruttore openHAB" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Interruttore openHAB" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "openHAB-bryter" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB-bryter" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "openHAB-schakelaar" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB-schakelaar" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Переключатель openHAB" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Переключатель openHAB" } } - }, - "comment": "Widget name.", - "isCommentAutoGenerated": true + } }, - "openhab_connection": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "openHAB Verbindung" + "openhab_connection" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Verbindung" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "openHAB Connection" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Connection" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "conexión openHAB" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "conexión openHAB" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "openHAB Connection" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Connection" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Connexion openHAB" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connexion openHAB" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Connessione openHAB" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connessione openHAB" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "openHAB Tilkobling" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Tilkobling" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "openHAB Verbinding" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "openHAB Verbinding" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "подключение к OpenHAB" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "подключение к OpenHAB" } } } }, - "password": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Passwort" + "password" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Passwort" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Password" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Password" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Contraseña" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contraseña" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Password" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Password" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Mot de passe" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mot de passe" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Password" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Password" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Passord" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Passord" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Wachtwoord" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wachtwoord" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Password" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Password" } } } }, - "Password": { - "comment": "A label displayed above a text field for entering a password.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Passwort" + "Password" : { + "comment" : "A label displayed above a text field for entering a password.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Passwort" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Password" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Password" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Contraseña" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contraseña" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Salasana" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Salasana" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Mot de passe" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mot de passe" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Password" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Password" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Passord" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Passord" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Wachtwoord" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wachtwoord" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Пароль" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Пароль" } } } }, - "Pause": { - "comment": "Display name for the pause action in the PlayerAction enum.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Pause" + "Pause" : { + "comment" : "Display name for the pause action in the PlayerAction enum.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pause" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Pause" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pause" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Pausa" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pausa" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Tauko" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tauko" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Pause" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pause" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Pausa" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pausa" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Pause" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pause" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Pauzeren" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pauzeren" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Пауза" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Пауза" } } } }, - "Play": { - "comment": "Display name for the play action in the PlayerAction enum.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Abspielen" + "Play" : { + "comment" : "Display name for the play action in the PlayerAction enum.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abspielen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Play" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Play" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Reproducir" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reproducir" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Toista" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toista" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Lire" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lire" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Riproduci" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Riproduci" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Spill av" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Spill av" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Afspelen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Afspelen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Воспроизвести" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Воспроизвести" } } } }, - "Player Action": { - "comment": "Type display name for the PlayerAction enum used in app intents.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Player-Aktion" + "Player Action" : { + "comment" : "Type display name for the PlayerAction enum used in app intents.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Player-Aktion" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Player Action" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Player Action" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Acción del reproductor" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Acción del reproductor" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Soittimen toiminto" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Soittimen toiminto" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Action du lecteur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Action du lecteur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Azione player" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Azione player" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Spillerhandling" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Spillerhandling" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Speleractie" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Speleractie" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Действие плеера" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Действие плеера" } } } }, - "Player Item": { - "comment": "Display name for a player item type.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Player-Item" + "Player Item" : { + "comment" : "Display name for a player item type.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Player-Item" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento reproductor" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento reproductor" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Soitinelementti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Soitinelementti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Élément lecteur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément lecteur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elemento player" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento player" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Spillerelement" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Spillerelement" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Speler-item" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Speler-item" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент плеера" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент плеера" } } } }, - "PNG": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "PNG" + "PNG" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "PNG" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "PNG" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "PNG" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "PNG" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "PNG" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "PNG" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "PNG" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "PNG" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "PNG" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "PNG" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "PNG" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "PNG" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "PNG" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "PNG" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "PNG" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "PNG" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "PNG" } } } }, - "Preferences": { - "comment": "Label for the preferences tab in the watch app.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Einstellungen" + "Preferences" : { + "comment" : "Label for the preferences tab in the watch app.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Einstellungen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Preferences" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Preferences" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Preferencias" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Preferencias" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Asetukset" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Asetukset" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Préférences" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Préférences" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Preferenze" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Preferenze" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Innstillinger" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Innstillinger" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Voorkeuren" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Voorkeuren" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Настройки" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Настройки" } } } }, - "Preview state": { - "comment": "The accessibility label for the picker that allows the user to select and preview a different state of an interactive state token.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Vorschauzustand" + "Preview state" : { + "comment" : "The accessibility label for the picker that allows the user to select and preview a different state of an interactive state token.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vorschauzustand" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Preview state" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Preview state" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Estado de vista previa" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Estado de vista previa" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Esikatselutila" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Esikatselutila" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "État d'aperçu" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "État d'aperçu" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Stato anteprima" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stato anteprima" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Forhåndsvisningstilstand" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Forhåndsvisningstilstand" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Voorbeeldstatus" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Voorbeeldstatus" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Состояние предварительного просмотра" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Состояние предварительного просмотра" } } } }, - "Previous": { - "comment": "Display name for the previous action in the PlayerAction enum.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Vorheriges" + "Previous" : { + "comment" : "Display name for the previous action in the PlayerAction enum.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vorheriges" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Previous" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Previous" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Anterior" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anterior" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Edellinen" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Edellinen" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Précédent" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Précédent" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Precedente" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Precedente" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Forrige" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Forrige" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Vorige" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vorige" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Предыдущий" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Предыдущий" } } } }, - "Previous track": { - "comment": "Display name for the previous action.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Vorheriger Titel" + "Previous track" : { + "comment" : "Display name for the previous action.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vorheriger Titel" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Pista anterior" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pista anterior" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Edellinen raita" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Edellinen raita" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Piste précédente" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Piste précédente" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Traccia precedente" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Traccia precedente" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Forrige spor" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Forrige spor" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Vorig nummer" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vorig nummer" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Предыдущий трек" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Предыдущий трек" } } } }, - "privacy_policy": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Datenschutzerklärung" + "privacy_policy" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datenschutzerklärung" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Privacy Policy" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Privacy Policy" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Política de privacidad" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Política de privacidad" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Privacy Policy" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Privacy Policy" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Politique de Confidentialité" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Politique de Confidentialité" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Politica sulla privacy" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Politica sulla privacy" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Personvernregler" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Personvernregler" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Privacybeleid" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Privacybeleid" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Privacy Policy" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Privacy Policy" } } } }, - "Queued commands: %lld": { - "comment": "A label indicating that there are one or more commands in the queue. The placeholder inside the label is replaced with the actual number of queued commands.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Wartende Befehle: %lld" + "Queued commands: %lld" : { + "comment" : "A label indicating that there are one or more commands in the queue. The placeholder inside the label is replaced with the actual number of queued commands.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wartende Befehle: %lld" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Queued commands: %lld" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Queued commands: %lld" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Comandos en cola: %lld" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Comandos en cola: %lld" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Jonossa olevat komennot: %lld" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jonossa olevat komennot: %lld" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Commandes en attente : %lld" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Commandes en attente : %lld" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Comandi in coda: %lld" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Comandi in coda: %lld" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kommandoer i kø: %lld" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kommandoer i kø: %lld" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Opdrachten in wachtrij: %lld" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opdrachten in wachtrij: %lld" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Команды в очереди: %lld" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Команды в очереди: %lld" } } } }, - "Raises by %@": { - "comment": "A hint that appears when a user hovers over the \"Increase\" button in the setpoint row. The argument is the step size for the current setpoint.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Um %@ erhöhen" + "Raises by %@" : { + "comment" : "A hint that appears when a user hovers over the \"Increase\" button in the setpoint row. The argument is the step size for the current setpoint.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Um %@ erhöhen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Raises by %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Raises by %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Sube %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sube %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Nostaa %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nostaa %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Monte de %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Monte de %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Aumenta di %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aumenta di %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Øker med %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Øker med %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Verhoogt met %@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verhoogt met %@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Повышает на %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Повышает на %@" } } } }, - "real_time_sliders": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Echtzeitschieberegler" + "real_time_sliders" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Echtzeitschieberegler" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Real-time Sliders" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Real-time Sliders" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Deslizadores en tiempo real" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Deslizadores en tiempo real" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Real-time Sliders" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Real-time Sliders" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Curseurs en temps réel" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Curseurs en temps réel" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Cursori In Tempo Reale" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cursori In Tempo Reale" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sanntids Skyveknapper" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sanntids Skyveknapper" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Realtime schuifregelaars" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Realtime schuifregelaars" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Real-time Sliders" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Real-time Sliders" } } } }, - "Real-time Sliders": { - "comment": "A toggle that enables or disables the real-time slider feature.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Echtzeitschieberegler" + "Real-time Sliders" : { + "comment" : "A toggle that enables or disables the real-time slider feature.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Echtzeitschieberegler" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Real-time Sliders" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Real-time Sliders" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Controles deslizantes en tiempo real" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Controles deslizantes en tiempo real" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Reaaliaikaiset liukusäätimet" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reaaliaikaiset liukusäätimet" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Curseurs en temps réel" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Curseurs en temps réel" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Cursori in tempo reale" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cursori in tempo reale" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sanntidsskyvere" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sanntidsskyvere" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Realtime schuifregelaars" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Realtime schuifregelaars" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Слайдеры реального времени" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Слайдеры реального времени" } } } }, - "Relative Date: %@": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Relatives Datum: %@" + "Relative Date: %@" : { + "comment" : "A label describing the relative size of the date text compared to the clock text.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Relatives Datum: %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Fecha relativa: %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Relative Date: %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Suhteellinen päivämäärä: %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fecha relativa: %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Date relative : %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suhteellinen päivämäärä: %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Data relativa: %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Date relative : %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Relativ dato: %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Data relativa: %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Relatieve datum: %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Relativ dato: %@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Относительная дата: %@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Relatieve datum: %@" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Relative Date: %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Относительная дата: %@" } } - }, - "comment": "A label describing the relative size of the date text compared to the clock text." + } }, - "Remote server": { - "comment": "Header text for the remote server section in the connection settings view.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Fernzugriff-Server" + "Remote server" : { + "comment" : "Header text for the remote server section in the connection settings view.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fernzugriff-Server" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Remote server" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Remote server" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Servidor remoto" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Servidor remoto" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Etäpalvelin" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Etäpalvelin" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Serveur distant" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Serveur distant" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Server remoto" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Server remoto" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Fjernserver" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fjernserver" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Externe server" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Externe server" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Удалённый сервер" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Удалённый сервер" } } } }, - "remote_url": { - "comment": "Space constraint string which has only about half the screen width on Apple Watch. Best to try and keep it no longer than the English string.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Fern-URL" + "remote_url" : { + "comment" : "Space constraint string which has only about half the screen width on Apple Watch. Best to try and keep it no longer than the English string.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fern-URL" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Remote URL" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Remote URL" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "URL remota" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL remota" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Remote URL" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Remote URL" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "URL distante" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL distante" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "URL remoto" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL remoto" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ekstern URL" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ekstern URL" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Externe URL" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Externe URL" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Удаленный URL-адрес" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Удаленный URL-адрес" } } } }, - "remote_url_not_configured": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "URL für Fernzugriff ist nicht konfiguriert." + "remote_url_not_configured" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL für Fernzugriff ist nicht konfiguriert." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Remote URL is not configured." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Remote URL is not configured." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "La URL remota no está configurada." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "La URL remota no está configurada." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Remote URL is not configured." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Remote URL is not configured." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "L'URL distante n'est pas configurée." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "L'URL distante n'est pas configurée." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "L'URL remoto non è configurato." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "L'URL remoto non è configurato." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ekstern URL er ikke konfigurert." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ekstern URL er ikke konfigurert." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Externe URL is niet geconfigureerd." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Externe URL is niet geconfigureerd." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Remote URL is not configured." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Remote URL is not configured." } } } }, - "Rename": { - "comment": "A button that renames a home.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Umbenennen" + "Rename" : { + "comment" : "A button that renames a home.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Umbenennen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Rename" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rename" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Renombrar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Renombrar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Nimeä uudelleen" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nimeä uudelleen" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Renommer" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Renommer" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Rinomina" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rinomina" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Gi nytt navn" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gi nytt navn" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Wijzig naam" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wijzig naam" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Переименовать" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Переименовать" } } } }, - "Replay": { - "comment": "A synonym for the rewind action.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Wiederholen" + "Replay" : { + "comment" : "A synonym for the rewind action.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wiederholen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Reproducir de nuevo" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reproducir de nuevo" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Toista uudelleen" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toista uudelleen" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Rejouer" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rejouer" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Riproduci" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Riproduci" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Spill av igjen" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Spill av igjen" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Opnieuw afspelen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opnieuw afspelen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Повторить" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Повторить" } } } }, - "Reset": { - "comment": "Synonyms for the \"Reset\" state of a contact.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zurückgesetzt" + "Reset" : { + "comment" : "Synonyms for the \"Reset\" state of a contact.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zurückgesetzt" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Restablecer" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Restablecer" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Nollattu" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nollattu" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Réinitialisé" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Réinitialisé" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Ripristinato" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ripristinato" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Tilbakestilt" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tilbakestilt" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Teruggezet" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Teruggezet" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Сброшен" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Сброшен" } } } }, - "Restore Brightness: %@": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Helligkeit wiederherstellen: %@" + "Restore Brightness: %@" : { + "comment" : "A label under the slider that shows the current brightness level and allows the user to adjust it. The value shown is the brightness level as a percentage.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Helligkeit wiederherstellen: %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Restaurar brillo: %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Restore Brightness: %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Palauta kirkkaus: %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Restaurar brillo: %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Restaurer la luminosité : %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Palauta kirkkaus: %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Ripristina luminosità: %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Restaurer la luminosité : %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Gjenopprett lysstyrke: %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ripristina luminosità: %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Helderheid herstellen: %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gjenopprett lysstyrke: %@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Восстановить яркость: %@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Helderheid herstellen: %@" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Restore Brightness: %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Восстановить яркость: %@" } } - }, - "comment": "A label under the slider that shows the current brightness level and allows the user to adjust it. The value shown is the brightness level as a percentage." + } }, - "Restore Previous Brightness on Wake": { - "comment": "A toggle that allows the user to restore the brightness level to its previous value when the device wakes up.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Vorherige Helligkeit beim Aufwecken wiederherstellen" + "Restore Previous Brightness on Wake" : { + "comment" : "A toggle that allows the user to restore the brightness level to its previous value when the device wakes up.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vorherige Helligkeit beim Aufwecken wiederherstellen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Restore Previous Brightness on Wake" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Restore Previous Brightness on Wake" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Restaurar brillo anterior al despertar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Restaurar brillo anterior al despertar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Palauta aiempi kirkkaus herätessä" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Palauta aiempi kirkkaus herätessä" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Restaurer la luminosité précédente au réveil" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Restaurer la luminosité précédente au réveil" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Ripristina luminosità precedente alla riattivazione" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ripristina luminosità precedente alla riattivazione" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Gjenopprett tidligere lysstyrke ved vekking" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gjenopprett tidligere lysstyrke ved vekking" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Vorige helderheid herstellen bij activering" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vorige helderheid herstellen bij activering" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Восстановить предыдущую яркость при пробуждении" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Восстановить предыдущую яркость при пробуждении" } } } }, - "Resume": { - "comment": "Action to resume playback.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Fortsetzen" + "Resume" : { + "comment" : "Action to resume playback.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fortsetzen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Reanudar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reanudar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Jatka" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jatka" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Reprendre" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reprendre" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Riprendi" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Riprendi" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Fortsett" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fortsett" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Hervatten" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hervatten" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Возобновить" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Возобновить" } } } }, - "Retrieve the current state of an item": { - "comment": "Description of the Get Item State app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Aktuellen Status eines Items abrufen" + "Retrieve the current state of an item" : { + "comment" : "Description of the Get Item State app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktuellen Status eines Items abrufen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Retrieve the current state of an item" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Retrieve the current state of an item" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Recuperar el estado actual de un elemento" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Recuperar el estado actual de un elemento" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Hae elementin nykyinen tila" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hae elementin nykyinen tila" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Récupérer l'état actuel d'un item" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Récupérer l'état actuel d'un item" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Recupera lo stato attuale di un Item" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Recupera lo stato attuale di un Item" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Hent nåværende tilstand for et Item" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hent nåværende tilstand for et Item" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Haal de huidige status van een item op" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Haal de huidige status van een item op" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Получить текущее состояние элемента" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Получить текущее состояние элемента" } } } }, - "retry": { - "comment": "retry connection", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Erneut versuchen" + "retry" : { + "comment" : "retry connection", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erneut versuchen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Retry" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Retry" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Reintentar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reintentar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Retry" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Retry" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Réessayer" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Réessayer" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Riprova" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Riprova" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Prøv på nytt" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Prøv på nytt" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Opnieuw" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opnieuw" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Retry" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Retry" } } } }, - "Rewind": { - "comment": "Display name for the rewind action in the PlayerAction enum.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zurückspulen" + "Rewind" : { + "comment" : "Display name for the rewind action in the PlayerAction enum.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zurückspulen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Rewind" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rewind" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Rebobinar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rebobinar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kelaa taaksepäin" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kelaa taaksepäin" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Retour arrière" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Retour arrière" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Riavvolgi" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Riavvolgi" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Spol tilbake" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Spol tilbake" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Terugspoelen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terugspoelen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Перемотка назад" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Перемотка назад" } } } }, - "Rewind back": { - "comment": "A synonym for the rewind action.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zurückspulen" + "Rewind back" : { + "comment" : "A synonym for the rewind action.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zurückspulen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Rebobinar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rebobinar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kelaa taaksepäin" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kelaa taaksepäin" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Rembobiner" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rembobiner" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Riavvolgi" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Riavvolgi" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Spol tilbake" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Spol tilbake" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Terugspoelen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terugspoelen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Перемотать" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Перемотать" } } } }, - "running_demo_mode": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Läuft im Demomodus. Überprüfe die Einstellungen, um den Demomodus zu deaktivieren." + "running_demo_mode" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Läuft im Demomodus. Überprüfe die Einstellungen, um den Demomodus zu deaktivieren." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Running in demo mode. Check settings to disable demo mode." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Running in demo mode. Check settings to disable demo mode." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Funccionando en modo demostración. Comprueba la configuración para desactivarlo." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Funccionando en modo demostración. Comprueba la configuración para desactivarlo." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Running in demo mode. Check settings to disable demo mode." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Running in demo mode. Check settings to disable demo mode." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Exécution en mode démo. Vérifiez les paramètres pour désactiver le mode démo." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Exécution en mode démo. Vérifiez les paramètres pour désactiver le mode démo." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Esecuzione in modalità demo. Controlla le impostazioni per disabilitare la modalità demo." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Esecuzione in modalità demo. Controlla le impostazioni per disabilitare la modalità demo." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kjører i demomodus. Sjekk innstillingene for å deaktivere demomodus." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kjører i demomodus. Sjekk innstillingene for å deaktivere demomodus." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Uitvoeren in demo-modus. Controleer instellingen om demomodus uit te schakelen." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uitvoeren in demo-modus. Controleer instellingen om demomodus uit te schakelen." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Running in demo mode. Check settings to disable demo mode." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Running in demo mode. Check settings to disable demo mode." } } } }, - "Save": { - "comment": "The text for a button that saves current settings.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sichern" + "Save" : { + "comment" : "The text for a button that saves current settings.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sichern" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Save" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Save" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Guardar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Guardar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Tallenna" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tallenna" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Enregistrer" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enregistrer" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Salva" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Salva" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Lagre" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lagre" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Bewaar" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bewaar" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Сохранить" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Сохранить" } } } }, - "Screen Saver": { - "comment": "The title of the screen.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Bildschirmschoner" + "Screen Saver" : { + "comment" : "The title of the screen.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bildschirmschoner" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Screen Saver" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Screen Saver" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Salvapantallas" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Salvapantallas" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Näytönsäästäjä" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Näytönsäästäjä" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Économiseur d’écran" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Économiseur d’écran" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Salvaschermo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Salvaschermo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Skjermsparer" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skjermsparer" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Schermbeveiliging" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schermbeveiliging" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Заставка экрана" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Заставка экрана" } } } }, - "Screen Saver Settings": { - "comment": "A link to the settings related to the screen saver.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Bildschirmschoner-Einstellungen" + "Screen Saver Settings" : { + "comment" : "A link to the settings related to the screen saver.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bildschirmschoner-Einstellungen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Screen Saver Settings" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Screen Saver Settings" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Ajustes del salvapantallas" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajustes del salvapantallas" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Näytönsäästäjän asetukset" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Näytönsäästäjän asetukset" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Paramètres de l'économiseur d'écran" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Paramètres de l'économiseur d'écran" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Impostazioni salvaschermo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impostazioni salvaschermo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Skjermsparer-innstillinger" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skjermsparer-innstillinger" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Schermbeveiligingsinstellingen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schermbeveiligingsinstellingen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Настройки заставки" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Настройки заставки" } } } }, - "Search": { - "comment": "A text field for searching through a list of items.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Suchen" + "Search" : { + "comment" : "A text field for searching through a list of items.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suchen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Search" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Search" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Buscar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Buscar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Haku" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Haku" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Rechercher" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rechercher" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Cerca" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cerca" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Søk" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Søk" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Zoek" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zoek" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Поиск" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Поиск" } } } }, - "Search for an item": { - "comment": "Dialog text prompting the user to search for an openHAB item in app intents.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Nach Item suchen" + "Search for an item" : { + "comment" : "Dialog text prompting the user to search for an openHAB item in app intents.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nach Item suchen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Search for an item" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Search for an item" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Buscar un elemento" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Buscar un elemento" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Etsi elementtiä" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Etsi elementtiä" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Rechercher un élément" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rechercher un élément" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Cerca un elemento" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cerca un elemento" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Søk etter et element" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Søk etter et element" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Zoeken naar een item" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zoeken naar een item" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Найти элемент" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Найти элемент" } } } }, - "search_items": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Suche openHAB Items" + "search_items" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suche openHAB Items" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Search openHAB items" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Search openHAB items" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Buscar ítems en openHAB" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Buscar ítems en openHAB" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Search openHAB items" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Search openHAB items" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Rechercher des items openHAB" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rechercher des items openHAB" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Cerca Item openHAB" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cerca Item openHAB" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Søk i openHAB Items" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Søk i openHAB Items" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Zoek openHAB items" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zoek openHAB items" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Search openHAB items" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Search openHAB items" } } } }, - "Select a home for '%@'": { - "comment": "A message to be displayed in a dialog box when the user needs to select a home. The argument is the name of the item that needs a home.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zuhause für '%@' auswählen" + "Select a home for '%@'" : { + "comment" : "A message to be displayed in a dialog box when the user needs to select a home. The argument is the name of the item that needs a home.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zuhause für '%@' auswählen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Seleccionar una casa para '%@'" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seleccionar una casa para '%@'" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Valitse koti kohteelle '%@'" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valitse koti kohteelle '%@'" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Sélectionner un domicile pour '%@'" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sélectionner un domicile pour '%@'" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Seleziona una casa per '%@'" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seleziona una casa per '%@'" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Velg et hjem for '%@'" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Velg et hjem for '%@'" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Selecteer een thuis voor '%@'" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Selecteer een thuis voor '%@'" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Выберите дом для '%@'" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Выберите дом для '%@'" } } } }, - "Select color": { - "comment": "A button that, when pressed, opens a view allowing the user to select a color.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Farbe wählen" + "Select color" : { + "comment" : "A button that, when pressed, opens a view allowing the user to select a color.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Farbe wählen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Select color" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Select color" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Seleccionar color" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seleccionar color" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Valitse väri" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valitse väri" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Sélectionner une couleur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sélectionner une couleur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Seleziona colore" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seleziona colore" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Velg farge" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Velg farge" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Kleur selecteren" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kleur selecteren" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Выбрать цвет" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Выбрать цвет" } } } }, - "select_sitemap": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sitemap auswählen" + "select_sitemap" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap auswählen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Select Sitemap" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Select Sitemap" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Seleccionar mapa web" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seleccionar mapa web" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Select Sitemap" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Select Sitemap" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Sélectionner le Sitemap" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sélectionner le Sitemap" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Seleziona Sitemap" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seleziona Sitemap" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Velg Sitemap" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Velg Sitemap" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Selecteer Sitemap" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Selecteer Sitemap" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Выбрать Sitemap" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Выбрать Sitemap" } } } }, - "Send ${action} to ${itemEntity}": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "${action} an ${itemEntity} senden" + "Send ${action} to ${itemEntity}" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "${action} an ${itemEntity} senden" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Enviar ${action} a ${itemEntity}" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviar ${action} a ${itemEntity}" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Lähetä ${action} kohteeseen ${itemEntity}" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lähetä ${action} kohteeseen ${itemEntity}" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Envoyer ${action} à ${itemEntity}" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Envoyer ${action} à ${itemEntity}" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Invia ${action} a ${itemEntity}" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Invia ${action} a ${itemEntity}" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Send ${action} til ${itemEntity}" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Send ${action} til ${itemEntity}" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "${action} verzenden naar ${itemEntity}" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "${action} verzenden naar ${itemEntity}" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Отправить ${action} в ${itemEntity}" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Отправить ${action} в ${itemEntity}" } } } }, - "Send a player command such as play, pause, next, or previous": { - "comment": "Description of the Set Player Control Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Einen Player-Befehl senden, z. B. Abspielen, Pause, Weiter oder Zurück" + "Send a player command such as play, pause, next, or previous" : { + "comment" : "Description of the Set Player Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Einen Player-Befehl senden, z. B. Abspielen, Pause, Weiter oder Zurück" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Send a player command such as play, pause, next, or previous" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Send a player command such as play, pause, next, or previous" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Enviar un comando al reproductor como reproducir, pausar, siguiente o anterior" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviar un comando al reproductor como reproducir, pausar, siguiente o anterior" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Lähetä soittimelle komento, kuten toista, tauko, seuraava tai edellinen" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lähetä soittimelle komento, kuten toista, tauko, seuraava tai edellinen" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Envoyer une commande au lecteur telle que lire, pause, suivant ou précédent" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Envoyer une commande au lecteur telle que lire, pause, suivant ou précédent" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Invia un comando al player come riproduci, pausa, successivo o precedente" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Invia un comando al player come riproduci, pausa, successivo o precedente" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Send en spillerkommando som spill av, pause, neste eller forrige" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Send en spillerkommando som spill av, pause, neste eller forrige" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Een spelersopdracht verzenden zoals afspelen, pauzeren, volgende of vorige" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Een spelersopdracht verzenden zoals afspelen, pauzeren, volgende of vorige" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Отправить команду плееру: воспроизвести, пауза, следующий или предыдущий" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Отправить команду плееру: воспроизвести, пауза, следующий или предыдущий" } } } }, - "Sending commands: %lld": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Befehle werden gesendet: %lld" + "Sending commands: %lld" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Befehle werden gesendet: %lld" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sending commands: %lld" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sending commands: %lld" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Enviando comandos: %lld" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviando comandos: %lld" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Lähetetään komentoja: %lld" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lähetetään komentoja: %lld" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Envoi de commandes : %lld" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Envoi de commandes : %lld" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Invio comandi: %lld" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Invio comandi: %lld" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sender kommandoer: %lld" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sender kommandoer: %lld" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Opdrachten verzenden: %lld" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opdrachten verzenden: %lld" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Отправка команд: %lld" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Отправка команд: %lld" } } } }, - "Sensor Item": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sensorelement" + "Sensor Item" : { + "comment" : "Label for the sensor item parameter in the sensor widget configuration intent.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensorelement" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento sensor" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento sensor" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Anturikohde" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anturikohde" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Élément capteur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément capteur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elemento sensore" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento sensore" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sensorelement" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensorelement" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Sensoritem" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensoritem" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент датчика" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент датчика" } } - }, - "comment": "Label for the sensor item parameter in the sensor widget configuration intent.", - "isCommentAutoGenerated": true + } }, - "Sensor Item 1": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sensorelement 1" + "Sensor Item 1" : { + "comment" : "The first sensor item to display in the widget.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensorelement 1" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento sensor 1" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento sensor 1" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Anturikohde 1" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anturikohde 1" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Élément capteur 1" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément capteur 1" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elemento sensore 1" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento sensore 1" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sensorelement 1" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensorelement 1" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Sensoritem 1" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensoritem 1" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент датчика 1" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент датчика 1" } } - }, - "comment": "The first sensor item to display in the widget.", - "isCommentAutoGenerated": true + } }, - "Sensor Item 2": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sensorelement 2" + "Sensor Item 2" : { + "comment" : "Label for the second sensor item in the sensor widget configuration intent.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensorelement 2" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento sensor 2" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento sensor 2" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Anturikohde 2" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anturikohde 2" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Élément capteur 2" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément capteur 2" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elemento sensore 2" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento sensore 2" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sensorelement 2" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensorelement 2" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Sensoritem 2" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensoritem 2" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент датчика 2" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент датчика 2" } } - }, - "comment": "Label for the second sensor item in the sensor widget configuration intent.", - "isCommentAutoGenerated": true + } }, - "Sensor Item 3": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sensorelement 3" + "Sensor Item 3" : { + "comment" : "Label for the sensor item 3 parameter in the sensor large widget configuration intent.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensorelement 3" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento sensor 3" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento sensor 3" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Anturikohde 3" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anturikohde 3" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Élément capteur 3" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément capteur 3" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elemento sensore 3" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento sensore 3" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sensorelement 3" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensorelement 3" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Sensoritem 3" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensoritem 3" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент датчика 3" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент датчика 3" } } - }, - "comment": "Label for the sensor item 3 parameter in the sensor large widget configuration intent.", - "isCommentAutoGenerated": true + } }, - "Sensor Item 4": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sensorelement 4" + "Sensor Item 4" : { + "comment" : "Label for the sensor item 4 parameter in the sensor large widget configuration intent.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensorelement 4" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento sensor 4" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento sensor 4" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Anturikohde 4" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anturikohde 4" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Élément capteur 4" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément capteur 4" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elemento sensore 4" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento sensore 4" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sensorelement 4" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensorelement 4" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Sensoritem 4" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensoritem 4" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент датчика 4" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент датчика 4" } } - }, - "comment": "Label for the sensor item 4 parameter in the sensor large widget configuration intent.", - "isCommentAutoGenerated": true + } }, - "Sensor Widget Configuration": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sensor-Widget-Konfiguration" + "Sensor Widget Configuration" : { + "comment" : "Title of the sensor widget configuration intent.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensor-Widget-Konfiguration" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Configuración del widget de sensor" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración del widget de sensor" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Anturiwidgetin määritys" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anturiwidgetin määritys" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Configuration du widget capteur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuration du widget capteur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Configurazione widget sensore" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configurazione widget sensore" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sensor-widgetkonfigurasjon" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensor-widgetkonfigurasjon" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Sensorwidgetconfiguratie" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensorwidgetconfiguratie" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Настройка виджета датчика" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Настройка виджета датчика" } } - }, - "comment": "Title of the sensor widget configuration intent.", - "isCommentAutoGenerated": true + } }, - "Sent %@ to %@": { - "comment": "The result text displayed in the dialog. The argument is the action, and the second argument is the item label.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "%1$@ an %2$@ gesendet" + "Sent %@ to %@" : { + "comment" : "The result text displayed in the dialog. The argument is the action, and the second argument is the item label.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ an %2$@ gesendet" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sent %1$@ to %2$@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sent %1$@ to %2$@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Enviado %1$@ a %2$@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviado %1$@ a %2$@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "%1$@ lähetetty kohteeseen %2$@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ lähetetty kohteeseen %2$@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "%1$@ envoyé à %2$@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ envoyé à %2$@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "%1$@ inviato a %2$@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ inviato a %2$@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "%1$@ sendt til %2$@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ sendt til %2$@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "%1$@ verzonden naar %2$@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ verzonden naar %2$@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "%1$@ отправлено в %2$@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ отправлено в %2$@" } } } }, - "Sent location %lf, %lf to %@": { - "comment": "Dialog shown after setting a location value. First two placeholders are latitude and longitude, third is the item name.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "%lf, %lf wurde an Standort %@ gesendet" + "Sent location %lf, %lf to %@" : { + "comment" : "Dialog shown after setting a location value. First two placeholders are latitude and longitude, third is the item name.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lf, %lf wurde an Standort %@ gesendet" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sent location %lf, %lf to %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sent location %lf, %lf to %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Ubicación %lf, %lf enviada a %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ubicación %lf, %lf enviada a %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Sijainti %lf, %lf lähetetty kohteeseen %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sijainti %lf, %lf lähetetty kohteeseen %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Emplacement %lf, %lf envoyé à %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Emplacement %lf, %lf envoyé à %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Posizione %lf, %lf inviata a %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Posizione %lf, %lf inviata a %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Plassering %lf, %lf sendt til %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Plassering %lf, %lf sendt til %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Locatie %lf, %lf verzonden naar %@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Locatie %lf, %lf verzonden naar %@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Местоположение %lf, %lf отправлено в %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Местоположение %lf, %lf отправлено в %@" } } } }, - "Sent the color value of %@ to %@": { - "comment": "Dialog shown after setting a color value. First placeholder is the color value, second is the item name.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Der Farbwert %@ wurde an %@ gesendet" + "Sent the color value of %@ to %@" : { + "comment" : "Dialog shown after setting a color value. First placeholder is the color value, second is the item name.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Der Farbwert %@ wurde an %@ gesendet" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sent the color value of %@ to %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sent the color value of %@ to %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Enviado el valor de color de %@ a %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviado el valor de color de %@ a %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Väriarvo %@ lähetetty kohteeseen %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Väriarvo %@ lähetetty kohteeseen %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Envoyé la valeur de couleur de %@ à %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Envoyé la valeur de couleur de %@ à %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Inviato il valore di colore di %@ a %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inviato il valore di colore di %@ a %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sendte fargeverdien %@ til %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sendte fargeverdien %@ til %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "De kleurwaarde van %@ is verzonden naar %@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De kleurwaarde van %@ is verzonden naar %@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Значение цвета %@ отправлено в %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Значение цвета %@ отправлено в %@" } } } }, - "Sent the date %@ to %@": { - "comment": "Dialog shown after setting a date/time value. First placeholder is the date string, second is the item name.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Datum %@ auf %@ setzen" + "Sent the date %@ to %@" : { + "comment" : "Dialog shown after setting a date/time value. First placeholder is the date string, second is the item name.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datum %@ auf %@ setzen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sent the date %@ to %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sent the date %@ to %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Fecha %@ enviada a %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fecha %@ enviada a %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Päivämäärä %@ lähetetty kohteeseen %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Päivämäärä %@ lähetetty kohteeseen %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Date %@ envoyée à %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Date %@ envoyée à %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Data %@ inviata a %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Data %@ inviata a %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Dato %@ sendt til %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dato %@ sendt til %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Datum %@ verzonden naar %@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datum %@ verzonden naar %@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Дата %@ отправлена в %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Дата %@ отправлена в %@" } } } }, - "Sent the number %lf to %@": { - "comment": "Dialog shown after setting a number control value. First placeholder is the decimal value, second is the item name.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Die Zahl %lf wurde an %@ gesendet" + "Sent the number %lf to %@" : { + "comment" : "Dialog shown after setting a number control value. First placeholder is the decimal value, second is the item name.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Die Zahl %lf wurde an %@ gesendet" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sent the number %lf to %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sent the number %lf to %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Definir el número %lf a %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Definir el número %lf a %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Luku %lf lähetetty kohteeseen %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Luku %lf lähetetty kohteeseen %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Envoyé le numéro %lf à %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Envoyé le numéro %lf à %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Inviato il numero %lf a %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inviato il numero %lf a %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Send den numeriske verdien %lf til %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Send den numeriske verdien %lf til %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "De waarde %lf is verzonden naar %@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De waarde %lf is verzonden naar %@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Число %lf отправлено в %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Число %lf отправлено в %@" } } } }, - "Sent the string %@ to %@": { - "comment": "Dialog shown after setting a string control value. First placeholder is the string value, second is the item name.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Der String %@ wurde an %@ gesendet" + "Sent the string %@ to %@" : { + "comment" : "Dialog shown after setting a string control value. First placeholder is the string value, second is the item name.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Der String %@ wurde an %@ gesendet" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sent the string %@ to %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sent the string %@ to %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Enviada la cadena %@ a %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviada la cadena %@ a %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Merkkijono %@ lähetetty kohteeseen %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Merkkijono %@ lähetetty kohteeseen %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Envoi de la chaîne %@ à %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Envoi de la chaîne %@ à %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Inviata la stringa %@ a %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inviata la stringa %@ a %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Send strengverdien %@ til %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Send strengverdien %@ til %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "De waarde %@ is verzonden naar %@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De waarde %@ is verzonden naar %@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Строка %@ отправлена в %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Строка %@ отправлена в %@" } } } }, - "Sent the value of %lld to %@": { - "comment": "Dialog shown after setting a dimmer or roller shutter value. First placeholder is the integer value, second is the item name.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Der Wert %lld wurde an %@ gesendet" + "Sent the value of %lld to %@" : { + "comment" : "Dialog shown after setting a dimmer or roller shutter value. First placeholder is the integer value, second is the item name.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Der Wert %lld wurde an %@ gesendet" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sent the value of %lld to %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sent the value of %lld to %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Enviado el valor de %lld a %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviado el valor de %lld a %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Arvo %lld lähetetty kohteeseen %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Arvo %lld lähetetty kohteeseen %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Envoyé la valeur %lld à %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Envoyé la valeur %lld à %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Inviato il valore %lld a %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inviato il valore %lld a %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sendte verdien %lld til %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sendte verdien %lld til %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "De waarde %lld is verzonden naar %@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De waarde %lld is verzonden naar %@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Значение %lld отправлено в %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Значение %lld отправлено в %@" } } } }, - "Set ${itemEntity} to ${latitude}, ${longitude}": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Standort ${itemEntity} auf ${latitude}, ${longitude} setzen" + "Set ${itemEntity} to ${latitude}, ${longitude}" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Standort ${itemEntity} auf ${latitude}, ${longitude} setzen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer ${itemEntity} en ${latitude}, ${longitude}" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer ${itemEntity} en ${latitude}, ${longitude}" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta ${itemEntity} arvoon ${latitude}, ${longitude}" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta ${itemEntity} arvoon ${latitude}, ${longitude}" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir ${itemEntity} à ${latitude}, ${longitude}" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir ${itemEntity} à ${latitude}, ${longitude}" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta ${itemEntity} su ${latitude}, ${longitude}" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta ${itemEntity} su ${latitude}, ${longitude}" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sett ${itemEntity} til ${latitude}, ${longitude}" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sett ${itemEntity} til ${latitude}, ${longitude}" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "${itemEntity} instellen op ${latitude}, ${longitude}" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "${itemEntity} instellen op ${latitude}, ${longitude}" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить ${itemEntity} в ${latitude}, ${longitude}" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить ${itemEntity} в ${latitude}, ${longitude}" } } } }, - "Set ${itemEntity} to ${value}": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "${itemEntity} auf ${value} setzen" + "Set ${itemEntity} to ${value}" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "${itemEntity} auf ${value} setzen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer ${itemEntity} en ${value}" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer ${itemEntity} en ${value}" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta ${itemEntity} arvoon ${value}" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta ${itemEntity} arvoon ${value}" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir ${itemEntity} sur ${value}" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir ${itemEntity} sur ${value}" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta ${itemEntity} su ${value}" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta ${itemEntity} su ${value}" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sett ${itemEntity} til ${value}" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sett ${itemEntity} til ${value}" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "${itemEntity} instellen op ${value}" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "${itemEntity} instellen op ${value}" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить ${itemEntity} в ${value}" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить ${itemEntity} в ${value}" } } } }, - "Set ${itemEntity} to ${value} (HSB)": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "${itemEntity} auf ${value} (HSB) setzen" + "Set ${itemEntity} to ${value} (HSB)" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "${itemEntity} auf ${value} (HSB) setzen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer ${itemEntity} en ${value} (HSB)" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer ${itemEntity} en ${value} (HSB)" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta ${itemEntity} arvoon ${value} (HSB)" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta ${itemEntity} arvoon ${value} (HSB)" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir ${itemEntity} sur ${value} (TSL)" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir ${itemEntity} sur ${value} (TSL)" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta ${itemEntity} su ${value} (HSB)" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta ${itemEntity} su ${value} (HSB)" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sett ${itemEntity} til ${value} (HSB)" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sett ${itemEntity} til ${value} (HSB)" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "${itemEntity} instellen op ${value} (HSB)" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "${itemEntity} instellen op ${value} (HSB)" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить ${itemEntity} в ${value} (HSB)" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить ${itemEntity} в ${value} (HSB)" } } } }, - "Set Active Home": { - "comment": "Title of the intent to set the active home.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Aktives Zuhause setzen" + "Set Active Home" : { + "comment" : "Title of the intent to set the active home.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktives Zuhause setzen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer casa activa" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer casa activa" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta aktiivinen koti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta aktiivinen koti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir le domicile actif" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir le domicile actif" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta casa attiva" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta casa attiva" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi aktivt hjem" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi aktivt hjem" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Actief thuis instellen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actief thuis instellen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить активный дом" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить активный дом" } } } }, - "Set active home to ${home}": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Aktives Zuhause auf ${home} setzen" + "Set active home to ${home}" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktives Zuhause auf ${home} setzen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer casa activa como ${home}" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer casa activa como ${home}" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta aktiiviseksi kodiksi ${home}" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta aktiiviseksi kodiksi ${home}" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir ${home} comme domicile actif" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir ${home} comme domicile actif" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta ${home} come casa attiva" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta ${home} come casa attiva" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi ${home} som aktivt hjem" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi ${home} som aktivt hjem" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "${home} instellen als actief thuis" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "${home} instellen als actief thuis" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить ${home} как активный дом" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить ${home} как активный дом" } } } }, - "Set Color": { - "comment": "Short title for the shortcut to set a color value.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Farbe einstellen" + "Set Color" : { + "comment" : "Short title for the shortcut to set a color value.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Farbe einstellen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer color" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer color" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta väri" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta väri" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir la couleur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la couleur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta il colore" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta il colore" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi farge" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi farge" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Kleur instellen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kleur instellen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить цвет" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить цвет" } } } }, - "Set Color Control Value": { - "comment": "Title of the Set Color Control Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Farbwert festlegen" + "Set Color Control Value" : { + "comment" : "Title of the Set Color Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Farbwert festlegen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set Color Control Value" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set Color Control Value" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Define el valor de control de color" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Define el valor de control de color" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta värisäätimen arvo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta värisäätimen arvo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir la valeur de contrôle de couleur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la valeur de contrôle de couleur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta Valore Controllo Colore" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta Valore Controllo Colore" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi Fargekontrollverdi" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi Fargekontrollverdi" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Kleurwaarde instellen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kleurwaarde instellen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить значение элемента управления цветом" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить значение элемента управления цветом" } } } }, - "Set Contact State": { - "comment": "Short title for the shortcut to set the contact state.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Kontaktstatus setzen" + "Set Contact State" : { + "comment" : "Short title for the shortcut to set the contact state.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontaktstatus setzen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer estado de contacto" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer estado de contacto" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta kontaktin tila" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta kontaktin tila" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir l'état du contact" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir l'état du contact" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta lo stato del contatto" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta lo stato del contatto" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi kontaktstatus" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi kontaktstatus" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Contactstatus instellen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contactstatus instellen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить состояние контакта" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить состояние контакта" } } } }, - "Set Contact State Value": { - "comment": "Title of the Set Contact State Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Wert eines Kontakt=Status festlegen" + "Set Contact State Value" : { + "comment" : "Title of the Set Contact State Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wert eines Kontakt=Status festlegen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set Contact State Value" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set Contact State Value" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer el valor del estado del pulsador" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer el valor del estado del pulsador" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta kontaktin tilanarvo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta kontaktin tilanarvo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir la valeur de l'état du contacteur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la valeur de l'état du contacteur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta lo Stato del Contatto" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta lo Stato del Contatto" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sett Tilstand for Bryter" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sett Tilstand for Bryter" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Stel contact status waarde in" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stel contact status waarde in" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить значение состояния контакта" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить значение состояния контакта" } } } }, - "Set Date & Time": { - "comment": "Short title for the shortcut to set a date and time.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Datum & Uhrzeit setzen" + "Set Date & Time" : { + "comment" : "Short title for the shortcut to set a date and time.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datum & Uhrzeit setzen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer fecha y hora" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer fecha y hora" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta päivämäärä ja aika" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta päivämäärä ja aika" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir la date et l'heure" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la date et l'heure" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta data e ora" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta data e ora" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi dato og klokkeslett" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi dato og klokkeslett" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Datum en tijd instellen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datum en tijd instellen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить дату и время" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить дату и время" } } } }, - "Set DateTime Control Value": { - "comment": "Title of the Set DateTime Control Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Wert eines DateTime-Kontrollelements setzen " + "Set DateTime Control Value" : { + "comment" : "Title of the Set DateTime Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wert eines DateTime-Kontrollelements setzen " } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set DateTime Control Value" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set DateTime Control Value" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer valor del control DateTime" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer valor del control DateTime" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta DateTime-säätimen arvo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta DateTime-säätimen arvo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir la valeur du contrôle DateHeure" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la valeur du contrôle DateHeure" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta valore controllo DateTime" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta valore controllo DateTime" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi DateTime-kontrollverdi" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi DateTime-kontrollverdi" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "DateTime-regelaarswaarde instellen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "DateTime-regelaarswaarde instellen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить значение элемента управления DateTime" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить значение элемента управления DateTime" } } } }, - "Set Dimmer / Roller": { - "comment": "Short title for a shortcut that allows the user to set the value of a dimmer or roller shutter.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Dimmer / Rollo einstellen" + "Set Dimmer / Roller" : { + "comment" : "Short title for a shortcut that allows the user to set the value of a dimmer or roller shutter.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dimmer / Rollo einstellen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Configurar regulador / persiana" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configurar regulador / persiana" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta himmentimen / rullakaihdin arvo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta himmentimen / rullakaihdin arvo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Régler le variateur / volet" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Régler le variateur / volet" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta dimmer / tapparella" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta dimmer / tapparella" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi dimmer / rullegardin" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi dimmer / rullegardin" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Dimmer / rolluik instellen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dimmer / rolluik instellen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить диммер / жалюзи" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить диммер / жалюзи" } } } }, - "Set Dimmer or Roller Shutter Value": { - "comment": "Title of the Set Dimmer or Roller Shutter Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Wert eines Dimmers oder Rollladens festlegen" + "Set Dimmer or Roller Shutter Value" : { + "comment" : "Title of the Set Dimmer or Roller Shutter Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wert eines Dimmers oder Rollladens festlegen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set Dimmer or Roller Shutter Value" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set Dimmer or Roller Shutter Value" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Definir el valor del regulador o persiana" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Definir el valor del regulador o persiana" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta himmentimen tai rullaverkon arvo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta himmentimen tai rullaverkon arvo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Régler la valeur du Dimmer ou du Volet Roulant" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Régler la valeur du Dimmer ou du Volet Roulant" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta il valore di Dimmer o Tapparella" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta il valore di Dimmer o Tapparella" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi Verdi for Dimmer eller Rullegardin" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi Verdi for Dimmer eller Rullegardin" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Stel Dimmer of Rolluik waarde in" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stel Dimmer of Rolluik waarde in" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить значение диммера или рольставни" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить значение диммера или рольставни" } } } }, - "Set Location Control Value": { - "comment": "Title of the Set Location Control Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Standort-Kontrollwert setzen" + "Set Location Control Value" : { + "comment" : "Title of the Set Location Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Standort-Kontrollwert setzen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set Location Control Value" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set Location Control Value" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer valor del control de ubicación" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer valor del control de ubicación" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta sijaintisäätimen arvo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta sijaintisäätimen arvo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir la valeur du contrôle de localisation" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la valeur du contrôle de localisation" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta valore controllo posizione" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta valore controllo posizione" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi plasseringskontrollverdi" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi plasseringskontrollverdi" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Locatieregelaarswaarde instellen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Locatieregelaarswaarde instellen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить значение элемента управления местоположением" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить значение элемента управления местоположением" } } } }, - "Set Number": { - "comment": "Text displayed in a shortcut for setting a number value.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zahl setzen" + "Set Number" : { + "comment" : "Text displayed in a shortcut for setting a number value.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zahl setzen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer número" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer número" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta luku" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta luku" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir un nombre" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir un nombre" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta numero" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta numero" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi tall" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi tall" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Getal instellen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Getal instellen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить число" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить число" } } } }, - "Set Number Control Value": { - "comment": "Title of the Set Number Control Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Wert eines numerischen Kontrollelements setzen" + "Set Number Control Value" : { + "comment" : "Title of the Set Number Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wert eines numerischen Kontrollelements setzen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set Number Control Value" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set Number Control Value" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer valor de control de número" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer valor de control de número" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta numerosäätimen arvo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta numerosäätimen arvo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir la valeur de contrôle du numéro" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la valeur de contrôle du numéro" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta Valore del Numero di Controllo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta Valore del Numero di Controllo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sett Verdi for Numerisk Kontroll" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sett Verdi for Numerisk Kontroll" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Zet Nummer Control Waarde" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zet Nummer Control Waarde" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить числовое значение элемента управления" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить числовое значение элемента управления" } } } }, - "Set Player Control Value": { - "comment": "Title of the Set Player Control Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Player-Kontrollelement setzen" + "Set Player Control Value" : { + "comment" : "Title of the Set Player Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Player-Kontrollelement setzen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set Player Control Value" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set Player Control Value" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer valor del control del reproductor" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer valor del control del reproductor" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta soittimen säätimen arvo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta soittimen säätimen arvo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir la valeur du contrôle du lecteur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la valeur du contrôle du lecteur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta valore controllo player" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta valore controllo player" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi spillerkontrollverdi" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi spillerkontrollverdi" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Spelerregelaarswaarde instellen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Spelerregelaarswaarde instellen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить значение элемента управления плеером" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить значение элемента управления плеером" } } } }, - "Set String Control Value": { - "comment": "Title of the Set String Control Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "String-Kontrollwert setzen" + "Set String Control Value" : { + "comment" : "Title of the Set String Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "String-Kontrollwert setzen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set String Control Value" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set String Control Value" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer el valor de control de cadena" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer el valor de control de cadena" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta merkkijonosäätimen arvo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta merkkijonosäätimen arvo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir la valeur de contrôle de chaîne" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la valeur de contrôle de chaîne" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta Valore della Stringa di Controllo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta Valore della Stringa di Controllo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sett Strengkontroll-Verdi" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sett Strengkontroll-Verdi" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Zet String Control Waarde in" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zet String Control Waarde in" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить строковое значение элемента управления" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить строковое значение элемента управления" } } } }, - "Set Switch": { - "comment": "Short title for a shortcut that allows the user to set a switch.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Schalter setzen" + "Set Switch" : { + "comment" : "Short title for a shortcut that allows the user to set a switch.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schalter setzen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Configurar interruptor" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configurar interruptor" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta kytkin" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta kytkin" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir un interrupteur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir un interrupteur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta interruttore" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta interruttore" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi bryter" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi bryter" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Schakelaar instellen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schakelaar instellen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить переключатель" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить переключатель" } } } }, - "Set Switch State": { - "comment": "Title of the Set Switch State app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Schalterzustand setzen" + "Set Switch State" : { + "comment" : "Title of the Set Switch State app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schalterzustand setzen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set Switch State" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set Switch State" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Definir el estado del interruptor" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Definir el estado del interruptor" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta kytkimen tila" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta kytkimen tila" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir l'état du Switch" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir l'état du Switch" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta stato Interruttore" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta stato Interruttore" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi Brytertilstand" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi Brytertilstand" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Stel schakelstatus in" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stel schakelstatus in" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить состояние переключателя" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить состояние переключателя" } } } }, - "Set Text": { - "comment": "Text displayed in a shortcut for setting a string value.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Text setzen" + "Set Text" : { + "comment" : "Text displayed in a shortcut for setting a string value.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Text setzen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer texto" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer texto" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta teksti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta teksti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir le texte" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir le texte" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta testo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta testo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi tekst" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi tekst" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Tekst instellen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tekst instellen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить текст" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить текст" } } } }, - "Set the color of a color control item": { - "comment": "Description of the Set Color Control Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Farbe eines Kontrollelements festlegen" + "Set the color of a color control item" : { + "comment" : "Description of the Set Color Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Farbe eines Kontrollelements festlegen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set the color of a color control item" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set the color of a color control item" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Definir el color de un ítem de control de color" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Definir el color de un ítem de control de color" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta värielementin väri" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta värielementin väri" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir la couleur d'un élément de contrôle de couleur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la couleur d'un élément de contrôle de couleur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta il colore di un Item di controllo colore" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta il colore di un Item di controllo colore" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi fargen til et fargekontroll-Item" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi fargen til et fargekontroll-Item" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Stel de kleur van een kleurbesturingsitem in" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stel de kleur van een kleurbesturingsitem in" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить цвет элемента управления цветом" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить цвет элемента управления цветом" } } } }, - "Set the date and time of a DateTime control item": { - "comment": "Description of the Set DateTime Control Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Datum und Uhrzeit eines DateTime-Kontrollelements setzen" + "Set the date and time of a DateTime control item" : { + "comment" : "Description of the Set DateTime Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datum und Uhrzeit eines DateTime-Kontrollelements setzen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set the date and time of a DateTime control item" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set the date and time of a DateTime control item" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer la fecha y hora de un elemento de control DateTime" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer la fecha y hora de un elemento de control DateTime" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta DateTime-elementin päivämäärä ja aika" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta DateTime-elementin päivämäärä ja aika" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir la date et l'heure d'un élément de contrôle DateHeure" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la date et l'heure d'un élément de contrôle DateHeure" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta la data e l'ora di un elemento di controllo DateTime" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta la data e l'ora di un elemento di controllo DateTime" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi dato og tid for et DateTime-kontrollelement" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi dato og tid for et DateTime-kontrollelement" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "De datum en tijd instellen van een DateTime-regelaarselement" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De datum en tijd instellen van een DateTime-regelaarselement" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить дату и время элемента DateTime" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить дату и время элемента DateTime" } } } }, - "Set the decimal value of a number control item": { - "comment": "Description of the Set Number Control Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Dezimalwert eines numerischen Kontrollelements setzen" + "Set the decimal value of a number control item" : { + "comment" : "Description of the Set Number Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dezimalwert eines numerischen Kontrollelements setzen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set the decimal value of a number control item" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set the decimal value of a number control item" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer el valor decimal de un ítem de control numérico" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer el valor decimal de un ítem de control numérico" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta numeroelementin desimaaliarvo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta numeroelementin desimaaliarvo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir la valeur décimale d'un item de contrôle de nombre" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la valeur décimale d'un item de contrôle de nombre" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta il valore decimale di un Item di controllo numerico" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta il valore decimale di un Item di controllo numerico" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sett desimalverdien til en numerisk kontroll-Item" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sett desimalverdien til en numerisk kontroll-Item" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Stel de decimale waarde van een nummer control item in" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stel de decimale waarde van een nummer control item in" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить десятичное значение числового элемента" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить десятичное значение числового элемента" } } } }, - "Set the integer value of a dimmer or roller shutter": { - "comment": "Description of the Set Dimmer or Roller Shutter Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Festlegen des Wertes eines Dimmers oder Rollladens" + "Set the integer value of a dimmer or roller shutter" : { + "comment" : "Description of the Set Dimmer or Roller Shutter Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Festlegen des Wertes eines Dimmers oder Rollladens" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set the integer value of a dimmer or roller shutter" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set the integer value of a dimmer or roller shutter" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Define el valor entero de un regulador o persiana" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Define el valor entero de un regulador o persiana" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta himmentimen tai rullaverkon kokonaislukuarvo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta himmentimen tai rullaverkon kokonaislukuarvo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir la valeur entière d'un dimmer ou d'un volet roulant" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la valeur entière d'un dimmer ou d'un volet roulant" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta il valore intero di un dimmer o una tapparella" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta il valore intero di un dimmer o una tapparella" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sett heltallsverdien til en dimmer eller rullegardin" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sett heltallsverdien til en dimmer eller rullegardin" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Zet de integerwaarde van een dimmer of rolluik" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zet de integerwaarde van een dimmer of rolluik" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить целочисленное значение диммера или рольставни" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить целочисленное значение диммера или рольставни" } } } }, - "Set the latitude and longitude of a location control item": { - "comment": "Description of the Set Location Control Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Breitengrad und Längengrad eines Standort-Kontrollelements setzen" + "Set the latitude and longitude of a location control item" : { + "comment" : "Description of the Set Location Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Breitengrad und Längengrad eines Standort-Kontrollelements setzen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set the latitude and longitude of a location control item" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set the latitude and longitude of a location control item" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer la latitud y longitud de un elemento de control de ubicación" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer la latitud y longitud de un elemento de control de ubicación" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta sijaintielementin leveys- ja pituusaste" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta sijaintielementin leveys- ja pituusaste" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir la latitude et la longitude d'un élément de contrôle de localisation" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la latitude et la longitude d'un élément de contrôle de localisation" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta la latitudine e la longitudine di un elemento di controllo posizione" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta la latitudine e la longitudine di un elemento di controllo posizione" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi bredde- og lengdegrad for et plasseringskontrollelement" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi bredde- og lengdegrad for et plasseringskontrollelement" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "De breedte- en lengtegraad instellen van een locatieregelaarselement" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De breedte- en lengtegraad instellen van een locatieregelaarselement" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить широту и долготу элемента местоположения" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить широту и долготу элемента местоположения" } } } }, - "Set the state of ${itemEntity} to ${state}": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Den Status von ${itemEntity} auf ${state} setzen" + "Set the state of ${itemEntity} to ${state}" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Den Status von ${itemEntity} auf ${state} setzen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer el estado de ${itemEntity} en ${state}" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer el estado de ${itemEntity} en ${state}" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta ${itemEntity}-tila arvoon ${state}" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta ${itemEntity}-tila arvoon ${state}" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir l'état de ${itemEntity} sur ${state}" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir l'état de ${itemEntity} sur ${state}" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta lo stato di ${itemEntity} su ${state}" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta lo stato di ${itemEntity} su ${state}" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi status for ${itemEntity} til ${state}" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi status for ${itemEntity} til ${state}" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "De status van ${itemEntity} instellen op ${state}" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De status van ${itemEntity} instellen op ${state}" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить состояние ${itemEntity} в ${state}" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить состояние ${itemEntity} в ${state}" } } } }, - "Set the state of a contact open or closed": { - "comment": "Description of the Set Contact State Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Status eines Kontakts auf offen oder geschlossen setzen" + "Set the state of a contact open or closed" : { + "comment" : "Description of the Set Contact State Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Status eines Kontakts auf offen oder geschlossen setzen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set the state of a contact open or closed" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set the state of a contact open or closed" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer el estado de un interruptor encendido o apagado" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer el estado de un interruptor encendido o apagado" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta kontaktin tilaksi auki tai kiinni" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta kontaktin tilaksi auki tai kiinni" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir l'état d'un contacteur ouvert ou fermé" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir l'état d'un contacteur ouvert ou fermé" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta lo stato di un contatto aperto o chiuso" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta lo stato di un contatto aperto o chiuso" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sett tilstanden til en bryter til åpen eller lukket" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sett tilstanden til en bryter til åpen eller lukket" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Zet de status van een contact open of gesloten" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zet de status van een contact open of gesloten" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить состояние контакта: открыт или закрыт" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить состояние контакта: открыт или закрыт" } } } }, - "Set the state of a switch on or off, or toggle its state": { - "comment": "Description of the Set Switch State app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Switch-Status auf An, Aus oder Umschalten setzen" + "Set the state of a switch on or off, or toggle its state" : { + "comment" : "Description of the Set Switch State app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Switch-Status auf An, Aus oder Umschalten setzen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set the state of a switch on or off, or toggle its state" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set the state of a switch on or off, or toggle its state" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer el estado de un interruptor en encendido o apagado, o alternar su estado" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer el estado de un interruptor en encendido o apagado, o alternar su estado" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta kytkimen tilaksi päällä tai pois tai vaihda sen tilaa" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta kytkimen tilaksi päällä tai pois tai vaihda sen tilaa" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir l'état d'un interrupteur sur activé ou désactivé, ou basculer son état" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir l'état d'un interrupteur sur activé ou désactivé, ou basculer son état" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta lo stato di un interruttore su acceso o spento, o attiva/disattiva il suo stato" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta lo stato di un interruttore su acceso o spento, o attiva/disattiva il suo stato" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi brytertilstanden til på eller av, eller bytt tilstand" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi brytertilstanden til på eller av, eller bytt tilstand" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "De status van een schakelaar op aan of uit zetten, of de status omschakelen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De status van een schakelaar op aan of uit zetten, of de status omschakelen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить состояние переключателя вкл. или выкл., или переключить его состояние" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить состояние переключателя вкл. или выкл., или переключить его состояние" } } } }, - "Set the string of a string control item": { - "comment": "Description of the Set String Control Value app intent.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zeichenkette eines String-Kontrollelements setzen" + "Set the string of a string control item" : { + "comment" : "Description of the Set String Control Value app intent.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zeichenkette eines String-Kontrollelements setzen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set the string of a string control item" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set the string of a string control item" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer la cadena de un ítem controlador de cadena" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer la cadena de un ítem controlador de cadena" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta merkkijonoelementin merkkijono" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta merkkijonoelementin merkkijono" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir la chaîne d'un item de contrôle de chaîne" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la chaîne d'un item de contrôle de chaîne" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta il valore di una item di controllo di tipo stringa" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta il valore di una item di controllo di tipo stringa" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi teksten til et teksterkontroll-Item" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi teksten til et teksterkontroll-Item" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "De tekenreeks van een string controle item instellen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De tekenreeks van een string controle item instellen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить строку строкового элемента" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить строку строкового элемента" } } } }, - "Set value": { - "comment": "A button that sets the value of a text input.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Wert setzen" + "Set value" : { + "comment" : "A button that sets the value of a text input.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wert setzen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set value" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set value" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Establecer valor" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer valor" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Aseta arvo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aseta arvo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Définir la valeur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Définir la valeur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Imposta valore" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imposta valore" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Angi verdi" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angi verdi" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Waarde instellen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Waarde instellen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Установить значение" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установить значение" } } } }, - "settings": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Einstellungen" + "settings" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Einstellungen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Settings" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Settings" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Configuración" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Settings" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Settings" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Paramétrage" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Paramétrage" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Impostazioni" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impostazioni" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Innstillinger" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Innstillinger" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Instellingen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Instellingen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Settings" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Settings" } } } }, - "settings_not_received": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Einstellungen noch nicht vom iPhone empfangen." + "settings_not_received" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Einstellungen noch nicht vom iPhone empfangen." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Settings not yet received from iPhone." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Settings not yet received from iPhone." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Configuración aún no recibida del iPhone." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración aún no recibida del iPhone." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Asetuksia ei ole vielä vastaanotettu iPhonelta." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Asetuksia ei ole vielä vastaanotettu iPhonelta." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Paramètres pas encore reçus de l'iPhone." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Paramètres pas encore reçus de l'iPhone." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Impostazioni non ancora ricevute da iPhone." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impostazioni non ancora ricevute da iPhone." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Innstillinger som ennå ikke er mottatt fra iPhone." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Innstillinger som ennå ikke er mottatt fra iPhone." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Instellingen nog niet ontvangen van iPhone." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Instellingen nog niet ontvangen van iPhone." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Настройки ещё не получены с iPhone." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Настройки ещё не получены с iPhone." } } } }, - "Show Date": { - "comment": "A toggle that enables or disables the display of the date in the screen saver.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Datum anzeigen" + "Show Date" : { + "comment" : "A toggle that enables or disables the display of the date in the screen saver.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datum anzeigen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Show Date" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show Date" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Mostrar fecha" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostrar fecha" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Näytä päivämäärä" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Näytä päivämäärä" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Afficher la date" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Afficher la date" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Mostra data" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostra data" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Vis dato" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vis dato" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Datum weergeven" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datum weergeven" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Показать дату" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Показать дату" } } } }, - "Show Search Field": { - "comment": "A toggle that enables or disables the search field in the app.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Suchfeld anzeigen" + "Show Search Field" : { + "comment" : "A toggle that enables or disables the search field in the app.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suchfeld anzeigen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Show Search Field" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show Search Field" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Mostrar campo de búsqueda" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostrar campo de búsqueda" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Näytä hakukenttä" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Näytä hakukenttä" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Afficher le champ de recherche" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Afficher le champ de recherche" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Mostra campo di ricerca" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostra campo di ricerca" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Vis søkefelt" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vis søkefelt" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Zoekveld weergeven" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zoekveld weergeven" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Показать поле поиска" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Показать поле поиска" } } } }, - "Show Seconds": { - "comment": "A toggle that lets the user enable or disable the display of seconds in the screen saver.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sekunden anzeigen" + "Show Seconds" : { + "comment" : "A toggle that lets the user enable or disable the display of seconds in the screen saver.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sekunden anzeigen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Show Seconds" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show Seconds" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Mostrar segundos" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostrar segundos" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Näytä sekunnit" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Näytä sekunnit" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Afficher les secondes" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Afficher les secondes" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Mostra secondi" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostra secondi" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Vis sekunder" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vis sekunder" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Seconden weergeven" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seconden weergeven" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Показать секунды" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Показать секунды" } } } }, - "Show Time": { - "comment": "A toggle that enables or disables the display of the time.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zeit anzeigen" + "Show Time" : { + "comment" : "A toggle that enables or disables the display of the time.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zeit anzeigen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Show Time" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show Time" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Mostrar hora" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostrar hora" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Näytä aika" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Näytä aika" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Afficher l'heure" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Afficher l'heure" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Mostra ora" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostra ora" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Vis tid" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vis tid" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Tijd weergeven" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tijd weergeven" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Показать время" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Показать время" } } } }, - "show_search_field": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Suchfeld anzeigen" + "show_search_field" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suchfeld anzeigen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Show Search Field" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show Search Field" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Show Search Field" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show Search Field" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Show Search Field" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show Search Field" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Show Search Field" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show Search Field" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Show Search Field" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show Search Field" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Show Search Field" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show Search Field" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Show Search Field" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show Search Field" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Show Search Field" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show Search Field" } } } }, - "Shut": { - "comment": "Displayed title for the \"Shut\" state of a contact.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Geschlossen" + "Shut" : { + "comment" : "Displayed title for the \"Shut\" state of a contact.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geschlossen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Cerrado" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cerrado" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Suljettu" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suljettu" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Fermé" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fermé" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Chiuso" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chiuso" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Lukket" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lukket" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Gesloten" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gesloten" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Закрыт" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Закрыт" } } } }, - "sitemap": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sitemap" + "sitemap" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sitemap" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Mapa web" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mapa web" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Sitemap" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Sitemap" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Sitemap" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sitemap" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Sitemap" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Sitemap" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap" } } } }, - "Sitemap": { - "comment": "Label for the tab item representing the sitemap view.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sitemap" + "Sitemap" : { + "comment" : "Label for the tab item representing the sitemap view.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sitemap" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Mapa del sitio" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mapa del sitio" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Sivukartta" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sivukartta" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Plan du site" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Plan du site" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Sitemap" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Nettstedskart" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nettstedskart" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Sitemap" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Карта сайта" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Карта сайта" } } } }, - "Sitemap Diagnostics Logging": { - "comment": "A toggle that enables or disables logging of sitemap diagnostics.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sitemap-Diagnoseprotokollierung" + "Sitemap Diagnostics Logging" : { + "comment" : "A toggle that enables or disables logging of sitemap diagnostics.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap-Diagnoseprotokollierung" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Registro de diagnóstico del mapa del sitio" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Registro de diagnóstico del mapa del sitio" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Sivukartan diagnostiikkaloki" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sivukartan diagnostiikkaloki" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Journalisation des diagnostics du plan du site" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Journalisation des diagnostics du plan du site" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Registrazione diagnostica sitemap" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Registrazione diagnostica sitemap" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Diagnoslogging for nettstedskart" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Diagnoslogging for nettstedskart" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Sitemap-diagnoselogboek" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap-diagnoselogboek" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Журналирование диагностики карты сайта" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Журналирование диагностики карты сайта" } } } }, - "Sitemap for Apple Watch": { - "comment": "A label for selecting a sitemap to be used with an Apple Watch app.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Apple Watch Sitemap" + "Sitemap for Apple Watch" : { + "comment" : "A label for selecting a sitemap to be used with an Apple Watch app.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple Watch Sitemap" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sitemap for Apple Watch" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap for Apple Watch" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Mapa del sitio para Apple Watch" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mapa del sitio para Apple Watch" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Apple Watch -sivukartta" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple Watch -sivukartta" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Plan du site pour Apple Watch" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Plan du site pour Apple Watch" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Sitemap per Apple Watch" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap per Apple Watch" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Nettstedskart for Apple Watch" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nettstedskart for Apple Watch" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Sitemap voor Apple Watch" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap voor Apple Watch" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Карта сайта для Apple Watch" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Карта сайта для Apple Watch" } } } }, - "sitemap_settings": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sitemap-Einstellungen" + "Sitemap for CarPlay" : { + "comment" : "A label for selecting a sitemap to be used for CarPlay.", + "isCommentAutoGenerated" : true + }, + "sitemap_settings" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap-Einstellungen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sitemap Settings" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap Settings" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Ajustes del esquema" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajustes del esquema" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Sitemap Settings" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap Settings" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Paramètres Sitemap" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Paramètres Sitemap" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Impostazioni Sitemap" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impostazioni Sitemap" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Innstillinger for Sitemap" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Innstillinger for Sitemap" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Sitemap instellingen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap instellingen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Sitemap Settings" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap Settings" } } } }, - "Sitemaps": { - "comment": "A section header that lists the user's available sitemaps.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sitemaps" + "Sitemaps" : { + "comment" : "A section header that lists the user's available sitemaps.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemaps" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sitemaps" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemaps" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Mapas del sitio" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mapas del sitio" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Sivukartat" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sivukartat" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Plans du site" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Plans du site" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Sitemap" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Nettstedskart" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nettstedskart" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Sitemaps" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemaps" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Карты сайта" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Карты сайта" } } } }, - "Size: %llu MB": { - "comment": "A message showing the size of the image cache. The argument is the size of the image cache in MB.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Größe: %llu MB" + "Size: %llu MB" : { + "comment" : "A message showing the size of the image cache. The argument is the size of the image cache in MB.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Größe: %llu MB" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Size: %llu MB" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Size: %llu MB" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Tamaño: %llu MB" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tamaño: %llu MB" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Koko: %llu Mt" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Koko: %llu Mt" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Taille : %llu Mo" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Taille : %llu Mo" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Dimensione: %llu MB" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dimensione: %llu MB" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Størrelse: %llu MB" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Størrelse: %llu MB" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Grootte: %llu MB" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Grootte: %llu MB" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Размер: %llu МБ" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Размер: %llu МБ" } } } }, - "Skip": { - "comment": "A synonym for the action \"Skip\".", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Überspringen" + "Skip" : { + "comment" : "A synonym for the action \"Skip\".", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Überspringen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Saltar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Saltar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ohita" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ohita" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Passer" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Passer" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Salta" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Salta" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Hopp over" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hopp over" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Overslaan" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Overslaan" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Пропустить" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Пропустить" } } } }, - "Skip ahead": { - "comment": "Synonyms for the \"Skip ahead\" action.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Vorwärts überspringen" + "Skip ahead" : { + "comment" : "Synonyms for the \"Skip ahead\" action.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vorwärts überspringen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Saltar adelante" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Saltar adelante" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Hyppää eteenpäin" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hyppää eteenpäin" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Sauter en avant" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sauter en avant" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Salta avanti" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Salta avanti" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Hopp fremover" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hopp fremover" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Vooruitspringen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vooruitspringen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Пропустить вперёд" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Пропустить вперёд" } } } }, - "Skip back": { - "comment": "Synonyms for the previous action.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zurückspringen" + "Skip back" : { + "comment" : "Synonyms for the previous action.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zurückspringen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Retroceder" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Retroceder" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Siirry taaksepäin" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Siirry taaksepäin" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Reculer" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reculer" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Torna indietro" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Torna indietro" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Hopp tilbake" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hopp tilbake" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Terugspoelen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terugspoelen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Перемотать назад" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Перемотать назад" } } } }, - "Skip forward": { - "comment": "A synonym for the \"Next\" action.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Vorwärts springen" + "Skip forward" : { + "comment" : "A synonym for the \"Next\" action.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vorwärts springen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Avanzar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Avanzar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Siirry eteenpäin" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Siirry eteenpäin" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Avancer" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Avancer" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Avanti veloce" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Avanti veloce" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Hopp fremover" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hopp fremover" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Vooruitspoelen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vooruitspoelen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Перемотать вперёд" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Перемотать вперёд" } } } }, - "Soft White": { - "comment": "A color temperature description.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Weiches Weiß" + "Soft White" : { + "comment" : "A color temperature description.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Weiches Weiß" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Soft White" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Soft White" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Blanco suave" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blanco suave" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Pehmeä valkoinen" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pehmeä valkoinen" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Blanc doux" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blanc doux" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Bianco morbido" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bianco morbido" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Myk hvit" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Myk hvit" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Zacht wit" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zacht wit" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Мягкий белый" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Мягкий белый" } } } }, - "Sort sitemaps by": { - "comment": "A label describing the option to sort sitemaps.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sitemaps sortieren nach" + "Sort sitemaps by" : { + "comment" : "A label describing the option to sort sitemaps.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemaps sortieren nach" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sort sitemaps by" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sort sitemaps by" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Ordenar mapas del sitio por" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ordenar mapas del sitio por" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Lajittele sivukartat" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lajittele sivukartat" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Trier les plans du site par" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trier les plans du site par" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Ordina sitemap per" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ordina sitemap per" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sorter nettstedskart etter" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sorter nettstedskart etter" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Sitemaps sorteren op" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemaps sorteren op" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Сортировать карты сайта по" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Сортировать карты сайта по" } } } }, - "sortSitemapsBy": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sitemaps sortieren nach" + "sortSitemapsBy" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemaps sortieren nach" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sort sitemaps by" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sort sitemaps by" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Ordenar mapas del sitio por" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ordenar mapas del sitio por" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Sitemapien järjestys" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemapien järjestys" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Trier les sitemaps par" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trier les sitemaps par" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Ordina le Sitemaps per" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ordina le Sitemaps per" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sorter SiteMaps etter" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sorter SiteMaps etter" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Sorteer sitemaps op" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sorteer sitemaps op" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Sort sitemaps by" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sort sitemaps by" } } } }, - "Speed up": { - "comment": "Synonyms for the \"Fast Forward\" action.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Beschleunigen" + "Speed up" : { + "comment" : "Synonyms for the \"Fast Forward\" action.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Beschleunigen" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Acelerar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Acelerar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Nopeuta" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nopeuta" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Accélérer" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Accélérer" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Accelera" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Accelera" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Øk hastighet" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Øk hastighet" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Versnellen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Versnellen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Ускорить" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ускорить" } } } }, - "SSL error. The connection couldn’t be established securely.": { - "comment": "Error message displayed when a connection to the OpenHAB server fails due to a SSL error.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "SSL-Fehler. Die Verbindung konnte nicht sicher hergestellt werden." + "SSL error. The connection couldn’t be established securely." : { + "comment" : "Error message displayed when a connection to the OpenHAB server fails due to a SSL error.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL-Fehler. Die Verbindung konnte nicht sicher hergestellt werden." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "SSL error. The connection couldn’t be established securely." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL error. The connection couldn’t be established securely." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Error SSL. No se pudo establecer la conexión de forma segura." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error SSL. No se pudo establecer la conexión de forma segura." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "SSL-virhe. Yhteyttä ei voitu muodostaa turvallisesti." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL-virhe. Yhteyttä ei voitu muodostaa turvallisesti." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Erreur SSL. La connexion n'a pas pu être établie de manière sécurisée." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erreur SSL. La connexion n'a pas pu être établie de manière sécurisée." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Errore SSL. Impossibile stabilire la connessione in modo sicuro." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Errore SSL. Impossibile stabilire la connessione in modo sicuro." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "SSL-feil. Tilkoblingen kunne ikke opprettes sikkert." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL-feil. Tilkoblingen kunne ikke opprettes sikkert." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "SSL-fout. De verbinding kon niet veilig worden opgezet." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL-fout. De verbinding kon niet veilig worden opgezet." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Ошибка SSL. Не удалось установить безопасное соединение." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ошибка SSL. Не удалось установить безопасное соединение." } } } }, - "ssl_certificate_error": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "SSL-Zertifikatfehler" + "ssl_certificate_error" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL-Zertifikatfehler" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "SSL Certificate Error" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL Certificate Error" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Error de certificado SSL" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error de certificado SSL" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "SSL Certificate Error" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL Certificate Error" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Erreur de certificat SSL" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erreur de certificat SSL" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Errore certificato SSL" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Errore certificato SSL" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Feil med SSL sertifikat" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Feil med SSL sertifikat" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "SSL-certificaat fout" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL-certificaat fout" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "SSL Certificate Error" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL Certificate Error" } } } }, - "ssl_certificate_invalid": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Das von %1$@ für %2$@ vorgestellte SSL-Zertifikat ist ungültig. Möchtest du fortfahren?" + "ssl_certificate_invalid" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Das von %1$@ für %2$@ vorgestellte SSL-Zertifikat ist ungültig. Möchtest du fortfahren?" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "SSL Certificate presented by %1$@ for %2$@ is invalid. Do you want to proceed?" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL Certificate presented by %1$@ for %2$@ is invalid. Do you want to proceed?" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "El certificado SSL presentado por %1$@ para %2$@ no es válido. ¿Desea continuar?" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "El certificado SSL presentado por %1$@ para %2$@ no es válido. ¿Desea continuar?" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "SSL Certificate presented by %1$@ for %2$@ is invalid. Do you want to proceed?" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL Certificate presented by %1$@ for %2$@ is invalid. Do you want to proceed?" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Le certificat SSL présenté par %1$@ pour %2$@ n’est pas valide. Veux-tu continuer ?" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Le certificat SSL présenté par %1$@ pour %2$@ n’est pas valide. Veux-tu continuer ?" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Il certificato SSL presentato da %1$@ per %2$@ non è valido. Vuoi procedere?" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Il certificato SSL presentato da %1$@ per %2$@ non è valido. Vuoi procedere?" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "SSL-sertifikat presentert av %1$@ for %2$@ er ugyldig. Vil du fortsette?" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL-sertifikat presentert av %1$@ for %2$@ er ugyldig. Vil du fortsette?" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "SSL-certificaat gepresenteerd door %1$@ voor %2$@ is ongeldig. Wilt u toch doorgaan?" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL-certificaat gepresenteerd door %1$@ voor %2$@ is ongeldig. Wilt u toch doorgaan?" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "SSL Certificate presented by %1$@ for %2$@ is invalid. Do you want to proceed?" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL Certificate presented by %1$@ for %2$@ is invalid. Do you want to proceed?" } } } }, - "ssl_certificate_no_match": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Das von %1$@ für %2$@ vorgestellte SSL-Zertifikat stimmt nicht mit dem Datensatz überein. Möchtest du fortfahren?" + "ssl_certificate_no_match" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Das von %1$@ für %2$@ vorgestellte SSL-Zertifikat stimmt nicht mit dem Datensatz überein. Möchtest du fortfahren?" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "SSL Certificate presented by %1$@ for %2$@ doesn't match the record. Do you want to proceed?" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL Certificate presented by %1$@ for %2$@ doesn't match the record. Do you want to proceed?" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "El certificado SSL presentado por %1$@ para %2$@ no coincide con el registro. ¿Desea continuar?" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "El certificado SSL presentado por %1$@ para %2$@ no coincide con el registro. ¿Desea continuar?" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "SSL Certificate presented by %1$@ for %2$@ doesn't match the record. Do you want to proceed?" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL Certificate presented by %1$@ for %2$@ doesn't match the record. Do you want to proceed?" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Le certificat SSL présenté par %1$@ pour %2$@ ne correspond pas à l’enregistrement. Veux-tu continuer ?" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Le certificat SSL présenté par %1$@ pour %2$@ ne correspond pas à l’enregistrement. Veux-tu continuer ?" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Il certificato SSL presentato da %1$@ per %2$@ non corrisponde al record. Vuoi procedere?" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Il certificato SSL presentato da %1$@ per %2$@ non corrisponde al record. Vuoi procedere?" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "SSL-sertifikat presentert av %1$@ for %2$@ samsvarer ikke med det som er lagret fra før av. Vil du fortsette?" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL-sertifikat presentert av %1$@ for %2$@ samsvarer ikke med det som er lagret fra før av. Vil du fortsette?" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "SSL-certificaat gepresenteerd door %1$@ voor %2$@ komt niet overeen met het record. Wilt u toch doorgaan?" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL-certificaat gepresenteerd door %1$@ voor %2$@ komt niet overeen met het record. Wilt u toch doorgaan?" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "SSL Certificate presented by %1$@ for %2$@ doesn't match the record. Do you want to proceed?" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL Certificate presented by %1$@ for %2$@ doesn't match the record. Do you want to proceed?" } } } }, - "ssl_certificate_warning": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "SSL-Zertifikatwarnung" + "ssl_certificate_warning" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL-Zertifikatwarnung" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "SSL Certificate Warning" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL Certificate Warning" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Aviso de certificado SSL" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aviso de certificado SSL" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "SSL Certificate Warning" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL Certificate Warning" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Avertissement de certificat SSL" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Avertissement de certificat SSL" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Avviso Certificato SSL" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Avviso Certificato SSL" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Advarsel for SSL sertifikat" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Advarsel for SSL sertifikat" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "SSL-certificaat waarschuwing" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL-certificaat waarschuwing" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "SSL Certificate Warning" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSL Certificate Warning" } } } }, - "Start": { - "comment": "A synonym for the \"Play\" action.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Start" + "Start" : { + "comment" : "A synonym for the \"Play\" action.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Start" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Iniciar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Iniciar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Käynnistä" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Käynnistä" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Démarrer" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Démarrer" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Avvia" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Avvia" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Start" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Start" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Start" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Start" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Запустить" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Запустить" } } } }, - "State": { - "comment": "A label for a picker that lets the user select a state.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zustand" + "State" : { + "comment" : "A label for a picker that lets the user select a state.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zustand" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "State" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "State" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Provincia" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Provincia" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Tila" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tila" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "État" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "État" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Stato" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stato" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Tilstand" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tilstand" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Status" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Status" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Состояние" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Состояние" } } } }, - "Stop": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Stopp" + "Stop" : { + "comment" : "Synonyms for the pause action.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stopp" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Parar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Parar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Pysäytä" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pysäytä" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Arrêter" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Arrêter" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Ferma" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ferma" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Stopp" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stopp" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Stoppen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stoppen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Остановить" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Остановить" } } - }, - "comment": "Synonyms for the pause action.", - "isCommentAutoGenerated": true + } }, - "String Item": { - "comment": "Display name for a string item type.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "String-Item" + "String Item" : { + "comment" : "Display name for a string item type.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "String-Item" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento de texto" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento de texto" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Merkkijonoelementti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Merkkijonoelementti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Élément texte" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément texte" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elemento stringa" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento stringa" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Tekststrengselement" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tekststrengselement" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Tekst-item" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tekst-item" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Строковый элемент" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Строковый элемент" } } } }, - "Suspend": { - "comment": "Display name for the pause action.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Anhalten" + "Suspend" : { + "comment" : "Display name for the pause action.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anhalten" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Suspender" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suspender" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Keskeytä" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keskeytä" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Suspendre" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suspendre" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Sospendi" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sospendi" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sett på vent" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sett på vent" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Onderbreken" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Onderbreken" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Приостановить" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Приостановить" } } } }, - "SVG": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "SVG" + "SVG" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "SVG" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "SVG" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "SVG" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "SVG" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "SVG" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "SVG" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "SVG" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "SVG" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "SVG" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "SVG" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "SVG" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "SVG" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "SVG" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "SVG" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "SVG" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "SVG" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "SVG" } } } }, - "Switch": { - "comment": "The type of action to perform on a switch.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Schalten" + "Switch" : { + "comment" : "The type of action to perform on a switch.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schalten" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Alternar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alternar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kytke" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kytke" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Commuter" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Commuter" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Cambia" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cambia" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Bytt" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bytt" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Schakelen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schakelen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Переключить" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Переключить" } } } }, - "Switch Action": { - "comment": "Type display name for the SwitchAction enum used in app intents.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Switch-Aktion" + "Switch Action" : { + "comment" : "Type display name for the SwitchAction enum used in app intents.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Switch-Aktion" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Switch Action" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Switch Action" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Acción del interruptor" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Acción del interruptor" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kytkimen toiminto" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kytkimen toiminto" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Action d'interrupteur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Action d'interrupteur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Azione interruttore" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Azione interruttore" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Bryterhandling" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bryterhandling" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Schakelaaractie" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schakelaaractie" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Действие переключателя" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Действие переключателя" } } } }, - "Switch Home": { - "comment": "Shortcut title for switching between homes.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zuhause wechseln" + "Switch Home" : { + "comment" : "Shortcut title for switching between homes.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zuhause wechseln" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Cambiar hogar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cambiar hogar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Vaihda koti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vaihda koti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Changer de maison" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Changer de maison" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Cambia casa" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cambia casa" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Bytt hjem" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bytt hjem" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Thuis wisselen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Thuis wisselen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Переключить дом" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Переключить дом" } } } }, - "Switch Item": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Schalterelement" + "Switch Item" : { + "comment" : "Display name for a switch item type.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schalterelement" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento de interruptor" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento de interruptor" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kytkinkohde" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kytkinkohde" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Élément commutateur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément commutateur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elemento interruttore" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento interruttore" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Brytterelement" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brytterelement" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Schakelaaritem" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schakelaaritem" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент переключателя" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент переключателя" } } - }, - "comment": "Display name for a switch item type.", - "isCommentAutoGenerated": true + } }, - "Switch Item 1": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Schalterelement 1" + "Switch Item 1" : { + "comment" : "Name of the first switch item to be configured in the widget.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schalterelement 1" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento de interruptor 1" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento de interruptor 1" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kytkinkohde 1" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kytkinkohde 1" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Élément commutateur 1" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément commutateur 1" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elemento interruttore 1" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento interruttore 1" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Brytterelement 1" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brytterelement 1" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Schakelaaritem 1" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schakelaaritem 1" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент переключателя 1" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент переключателя 1" } } - }, - "comment": "Name of the first switch item to be configured in the widget.", - "isCommentAutoGenerated": true + } }, - "Switch Item 2": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Schalterelement 2" + "Switch Item 2" : { + "comment" : "Label for the second switch item in the widget configuration intent.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schalterelement 2" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento de interruptor 2" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento de interruptor 2" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kytkinkohde 2" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kytkinkohde 2" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Élément commutateur 2" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément commutateur 2" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elemento interruttore 2" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento interruttore 2" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Brytterelement 2" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brytterelement 2" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Schakelaaritem 2" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schakelaaritem 2" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент переключателя 2" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент переключателя 2" } } - }, - "comment": "Label for the second switch item in the widget configuration intent.", - "isCommentAutoGenerated": true + } }, - "Switch Item 3": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Schalterelement 3" + "Switch Item 3" : { + "comment" : "Label for the third switch item in the widget configuration.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schalterelement 3" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento de interruptor 3" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento de interruptor 3" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kytkinkohde 3" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kytkinkohde 3" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Élément commutateur 3" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément commutateur 3" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elemento interruttore 3" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento interruttore 3" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Brytterelement 3" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brytterelement 3" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Schakelaaritem 3" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schakelaaritem 3" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент переключателя 3" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент переключателя 3" } } - }, - "comment": "Label for the third switch item in the widget configuration.", - "isCommentAutoGenerated": true + } }, - "Switch Item 4": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Schalterelement 4" + "Switch Item 4" : { + "comment" : "Label for the fourth switch item in the widget configuration.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schalterelement 4" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento de interruptor 4" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento de interruptor 4" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kytkinkohde 4" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kytkinkohde 4" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Élément commutateur 4" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément commutateur 4" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elemento interruttore 4" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento interruttore 4" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Brytterelement 4" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brytterelement 4" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Schakelaaritem 4" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schakelaaritem 4" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент переключателя 4" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент переключателя 4" } } - }, - "comment": "Label for the fourth switch item in the widget configuration.", - "isCommentAutoGenerated": true + } }, - "Switch off": { - "comment": "Displayed when the user disables a device.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Abschalten" + "Switch off" : { + "comment" : "Displayed when the user disables a device.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abschalten" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Desactivar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Desactivar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kytke pois" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kytke pois" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Désactiver" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Désactiver" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Disattiva" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Disattiva" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Deaktiver" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Deaktiver" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Uitzetten" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uitzetten" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Отключить" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Отключить" } } } }, - "Switch on": { - "comment": "Switch action to turn a device on.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Anschalten" + "Switch on" : { + "comment" : "Switch action to turn a device on.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anschalten" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Activar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kytke päälle" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kytke päälle" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Activer" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activer" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Attiva" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Attiva" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Aktiver" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktiver" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Aanzetten" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aanzetten" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Включить" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Включить" } } } }, - "Switch or Dimmer Item": { - "comment": "Display representation of the type of an item that can be turned on or off.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Schalter- oder Dimmerelement" + "Switch or Dimmer Item" : { + "comment" : "Display representation of the type of an item that can be turned on or off.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schalter- oder Dimmerelement" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Elemento de interruptor o regulador" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento de interruptor o regulador" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kytkin- tai himmenninkohde" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kytkin- tai himmenninkohde" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Élément commutateur ou variateur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élément commutateur ou variateur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Elemento interruttore o dimmer" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elemento interruttore o dimmer" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Bryter- eller dimmer-element" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bryter- eller dimmer-element" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Schakelaar- of dimmeritem" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schakelaar- of dimmeritem" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Элемент переключателя или диммера" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Элемент переключателя или диммера" } } } }, - "Switch the active home in the openHAB app": { - "comment": "Description of the intent to set the active home.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Aktives Zuhause in der openHAB-App wechseln" + "Switch the active home in the openHAB app" : { + "comment" : "Description of the intent to set the active home.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktives Zuhause in der openHAB-App wechseln" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Cambiar la casa activa en la app openHAB" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cambiar la casa activa en la app openHAB" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Vaihda aktiivinen koti openHAB-sovelluksessa" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vaihda aktiivinen koti openHAB-sovelluksessa" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Changer le domicile actif dans l'application openHAB" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Changer le domicile actif dans l'application openHAB" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Cambia la casa attiva nell'app openHAB" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cambia la casa attiva nell'app openHAB" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Bytt aktivt hjem i openHAB-appen" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bytt aktivt hjem i openHAB-appen" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Actief thuis wijzigen in de openHAB-app" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actief thuis wijzigen in de openHAB-app" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Сменить активный дом в приложении openHAB" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Сменить активный дом в приложении openHAB" } } } }, - "Switch Widget Configuration": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Schalter-Widget-Konfiguration" + "Switch Widget Configuration" : { + "comment" : "Title of the intent to configure a switch widget.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schalter-Widget-Konfiguration" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Configuración del widget de interruptor" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración del widget de interruptor" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kytkinwidgetin määritys" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kytkinwidgetin määritys" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Configuration du widget commutateur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuration du widget commutateur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Configurazione widget interruttore" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configurazione widget interruttore" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Bryter-widgetkonfigurasjon" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bryter-widgetkonfigurasjon" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Schakelaarwidgetconfiguratie" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schakelaarwidgetconfiguratie" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Настройka виджета переключателя" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Настройka виджета переключателя" } } - }, - "comment": "Title of the intent to configure a switch widget.", - "isCommentAutoGenerated": true + } }, - "sync_prefs": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Synchronisationseinstellung" + "sync_prefs" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Synchronisationseinstellung" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sync preferences" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sync preferences" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Preferencias de sincronización" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Preferencias de sincronización" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Sync preferences" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sync preferences" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Synchroniser les préférences" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Synchroniser les préférences" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Sincronizza preferenze" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sincronizza preferenze" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Sync-innstillinger" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sync-innstillinger" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Synchronisatie voorkeuren" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Synchronisatie voorkeuren" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Sync preferences" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sync preferences" } } } }, - "System": { - "comment": "A section header in the drawer view that links to the system settings.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "System" + "System" : { + "comment" : "A section header in the drawer view that links to the system settings.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "System" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "System" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "System" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Sistema" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sistema" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Järjestelmä" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Järjestelmä" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Système" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Système" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Sistema" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sistema" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "System" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "System" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Systeem" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Systeem" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Система" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Система" } } } }, - "Test Connection": { - "comment": "A button label that triggers a test connection action.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Testverbindung" + "Test Connection" : { + "comment" : "A button label that triggers a test connection action.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Testverbindung" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Test Connection" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Test Connection" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Probar conexión" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Probar conexión" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Testaa yhteys" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Testaa yhteys" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Tester la connexion" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tester la connexion" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Testa connessione" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Testa connessione" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Test tilkobling" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Test tilkobling" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Verbinding testen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbinding testen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Проверить соединение" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Проверить соединение" } } } }, - "Test Screen Saver": { - "comment": "A button that can be used to test the functionality of the screen saver.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Bildschirmschoner testen" + "Test Screen Saver" : { + "comment" : "A button that can be used to test the functionality of the screen saver.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bildschirmschoner testen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Test Screen Saver" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Test Screen Saver" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Probar salvapantallas" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Probar salvapantallas" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Testaa näytönsäästäjä" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Testaa näytönsäästäjä" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Tester l'économiseur d'écran" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tester l'économiseur d'écran" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Testa salvaschermo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Testa salvaschermo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Test skjermsparer" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Test skjermsparer" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Schermbeveiliging testen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schermbeveiliging testen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Проверить заставку" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Проверить заставку" } } } }, - "The connection timed out. Try again later.": { - "comment": "Error message displayed when a network request times out.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Die Verbindung ist abgelaufen. Später noch einmal versuchen." + "The connection timed out. Try again later." : { + "comment" : "Error message displayed when a network request times out.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Die Verbindung ist abgelaufen. Später noch einmal versuchen." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "The connection timed out. Try again later." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "The connection timed out. Try again later." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "La conexión ha agotado el tiempo de espera. Inténtelo de nuevo más tarde." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "La conexión ha agotado el tiempo de espera. Inténtelo de nuevo más tarde." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Yhteys aikakatkaistiin. Yritä myöhemmin uudelleen." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Yhteys aikakatkaistiin. Yritä myöhemmin uudelleen." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "La connexion a expiré. Réessayez plus tard." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "La connexion a expiré. Réessayez plus tard." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "La connessione è scaduta. Riprova più tardi." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "La connessione è scaduta. Riprova più tardi." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Tilkoblingen ble tidsavbrutt. Prøv igjen senere." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tilkoblingen ble tidsavbrutt. Prøv igjen senere." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "De verbinding is verlopen. Probeer het later opnieuw." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De verbinding is verlopen. Probeer het later opnieuw." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Время соединения истекло. Повторите попытку позже." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Время соединения истекло. Повторите попытку позже." } } } }, - "The state of %@ is %@": { - "comment": "Dialog shown when retrieving an item state. First placeholder is the item name, second is its state.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Der Status von %@ ist %@" + "The state of %@ is %@" : { + "comment" : "Dialog shown when retrieving an item state. First placeholder is the item name, second is its state.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Der Status von %@ ist %@" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "The state of %@ is %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "The state of %@ is %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "El estado de %@ es %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "El estado de %@ es %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kohteen %@ tila on %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kohteen %@ tila on %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "L'état de %@ est %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "L'état de %@ est %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Lo stato di %@ è %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lo stato di %@ è %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Tilstanden til %@ er %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tilstanden til %@ er %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "De staat van %@ is %@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De staat van %@ is %@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Состояние %@ равно %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Состояние %@ равно %@" } } } }, - "The state of %@ was set to %@": { - "comment": "Dialog shown after setting a contact state. First placeholder is the item name, second is the new state.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Der Status von %@ wurde auf %@ gesetzt" + "The state of %@ was set to %@" : { + "comment" : "Dialog shown after setting a contact state. First placeholder is the item name, second is the new state.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Der Status von %@ wurde auf %@ gesetzt" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "The state of %@ was set to %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "The state of %@ was set to %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "El estado de %@ se ha establecido en %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "El estado de %@ se ha establecido en %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Kohteen %@ tilaksi asetettiin %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kohteen %@ tilaksi asetettiin %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "L'état de %@ a été réglé sur %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "L'état de %@ a été réglé sur %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Lo stato di %@ è stato impostato su %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lo stato di %@ è stato impostato su %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Tilstanden til %@ ble satt til %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tilstanden til %@ ble satt til %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "De status van %@ is ingesteld op %@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De status van %@ is ingesteld op %@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Состояние %@ установлено в %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Состояние %@ установлено в %@" } } } }, - "The URL is invalid. Please check the format (e.g., http://192.168.2.1:8080 or http://[::1]:8080 for IPv6).": { - "comment": "Error message displayed when the URL is invalid.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Die URL ist ungültig. Bitte überprüfe das Format (z.B. http://192.168.2.1:8080 oder http://[::1]:8080 für IPv6)." + "The URL is invalid. Please check the format (e.g., http://192.168.2.1:8080 or http://[::1]:8080 for IPv6)." : { + "comment" : "Error message displayed when the URL is invalid.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Die URL ist ungültig. Bitte überprüfe das Format (z.B. http://192.168.2.1:8080 oder http://[::1]:8080 für IPv6)." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "La URL no es válida. Compruebe el formato (p. ej., http://192.168.2.1:8080 o http://[::1]:8080 para IPv6)." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "La URL no es válida. Compruebe el formato (p. ej., http://192.168.2.1:8080 o http://[::1]:8080 para IPv6)." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "URL on virheellinen. Tarkista muoto (esim. http://192.168.2.1:8080 tai http://[::1]:8080 IPv6:lle)." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL on virheellinen. Tarkista muoto (esim. http://192.168.2.1:8080 tai http://[::1]:8080 IPv6:lle)." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "L'URL est invalide. Veuillez vérifier le format (p. ex., http://192.168.2.1:8080 ou http://[::1]:8080 pour IPv6)." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "L'URL est invalide. Veuillez vérifier le format (p. ex., http://192.168.2.1:8080 ou http://[::1]:8080 pour IPv6)." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "L'URL non è valido. Controlla il formato (es. http://192.168.2.1:8080 o http://[::1]:8080 per IPv6)." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "L'URL non è valido. Controlla il formato (es. http://192.168.2.1:8080 o http://[::1]:8080 per IPv6)." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "URL-en er ugyldig. Sjekk formatet (f.eks. http://192.168.2.1:8080 eller http://[::1]:8080 for IPv6)." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL-en er ugyldig. Sjekk formatet (f.eks. http://192.168.2.1:8080 eller http://[::1]:8080 for IPv6)." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "De URL is ongeldig. Controleer het formaat (bijv. http://192.168.2.1:8080 of http://[::1]:8080 voor IPv6)." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De URL is ongeldig. Controleer het formaat (bijv. http://192.168.2.1:8080 of http://[::1]:8080 voor IPv6)." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "URL недействителен. Проверьте формат (например, http://192.168.2.1:8080 или http://[::1]:8080 для IPv6)." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL недействителен. Проверьте формат (например, http://192.168.2.1:8080 или http://[::1]:8080 для IPv6)." } } } }, - "Tiles": { - "comment": "A section header that lists the user's tiles.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Kacheln" + "Tiles" : { + "comment" : "A section header that lists the user's tiles.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kacheln" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Tiles" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tiles" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Mosaicos" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mosaicos" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ruudut" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ruudut" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Tuiles" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tuiles" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Riquadri" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Riquadri" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Fliser" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fliser" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Tegels" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tegels" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Плитки" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Плитки" } } } }, - "Timing": { - "comment": "The header for the timing section in the screen saver settings view.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Timing" + "Timing" : { + "comment" : "The header for the timing section in the screen saver settings view.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Timing" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Timing" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Timing" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Temporización" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Temporización" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Ajoitus" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajoitus" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Minutage" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Minutage" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Temporizzazione" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Temporizzazione" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Timing" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Timing" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Timing" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Timing" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Синхронизация" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Синхронизация" } } } }, - "To connect to your local openHAB server, please allow Local Network access when prompted. If you previously denied it, enable it in Settings → Privacy & Security → Local Network.": { - "comment": "A message displayed in the alert when the user tries to connect to a local openHAB server.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Um eine Verbindung zu deinem lokalen openHAB-Server herzustellen, erlauben bitte den Zugriff auf das lokale Netzwerk, wenn du dazu aufgefordert wirst. Wenn du es zuvor abgelehnt hast, aktiviere es unter Einstellungen → Datenschutz & Sicherheit → Lokales Netzwerk." + "To connect to your local openHAB server, please allow Local Network access when prompted. If you previously denied it, enable it in Settings → Privacy & Security → Local Network." : { + "comment" : "A message displayed in the alert when the user tries to connect to a local openHAB server.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Um eine Verbindung zu deinem lokalen openHAB-Server herzustellen, erlauben bitte den Zugriff auf das lokale Netzwerk, wenn du dazu aufgefordert wirst. Wenn du es zuvor abgelehnt hast, aktiviere es unter Einstellungen → Datenschutz & Sicherheit → Lokales Netzwerk." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Pour te connecter à ton serveur openHAB local, autorise l’accès au réseau local lorsque tu y es invité. Si tu l’as précédemment refusé, active-le dans Réglages → Confidentialité et sécurité → Réseau local." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Para conectarte a tu servidor openHAB local, permite el acceso a la Red local cuando se te solicite. Si anteriormente lo denegaste, actívalo en Configuración → Privacidad y seguridad → Red local." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Per connetterti al tuo server openHAB locale, consenti l'accesso alla rete locale quando richiesto. Se in precedenza hai negato l'accesso, abilitalo in Impostazioni → Privacy e sicurezza → Rete locale." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Yhdistääksesi paikalliseen openHAB-palvelimeesi, salli lähiverkon käyttö pyydettäessä. Jos aiemmin kieltäydyit, ota se käyttöön kohdasta Asetukset → Yksityisyys ja turvallisuus → Paikallinen verkko." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Om verbinding te maken met uw lokale openHAB-server, sta toegang tot het lokale netwerk toe wanneer hierom wordt gevraagd. Als u dit eerder heeft geweigerd, schakel het in via Instellingen → Privacy en beveiliging → Lokaal netwerk." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pour te connecter à ton serveur openHAB local, autorise l’accès au réseau local lorsque tu y es invité. Si tu l’as précédemment refusé, active-le dans Réglages → Confidentialité et sécurité → Réseau local." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Para conectarte a tu servidor openHAB local, permite el acceso a la Red local cuando se te solicite. Si anteriormente lo denegaste, actívalo en Configuración → Privacidad y seguridad → Red local." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Per connetterti al tuo server openHAB locale, consenti l'accesso alla rete locale quando richiesto. Se in precedenza hai negato l'accesso, abilitalo in Impostazioni → Privacy e sicurezza → Rete locale." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Yhdistääksesi paikalliseen openHAB-palvelimeesi, salli lähiverkon käyttö pyydettäessä. Jos aiemmin kieltäydyit, ota se käyttöön kohdasta Asetukset → Yksityisyys ja turvallisuus → Paikallinen verkko." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "For å koble til den lokale openHAB-serveren din, tillat tilgang til lokalt nettverk når du blir bedt om det. Hvis du tidligere nektet det, aktiver det under Innstillinger → Personvern og sikkerhet → Lokalt nettverk." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "For å koble til den lokale openHAB-serveren din, tillat tilgang til lokalt nettverk når du blir bedt om det. Hvis du tidligere nektet det, aktiver det under Innstillinger → Personvern og sikkerhet → Lokalt nettverk." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Om verbinding te maken met uw lokale openHAB-server, sta toegang tot het lokale netwerk toe wanneer hierom wordt gevraagd. Als u dit eerder heeft geweigerd, schakel het in via Instellingen → Privacy en beveiliging → Lokaal netwerk." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Чтобы подключиться к локальному серверу openHAB, разрешите доступ к локальной сети при появлении запроса. Если вы ранее отказали в доступе, включите его в разделе Настройки → Конфиденциальность и безопасность → Локальная сеть." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Чтобы подключиться к локальному серверу openHAB, разрешите доступ к локальной сети при появлении запроса. Если вы ранее отказали в доступе, включите его в разделе Настройки → Конфиденциальность и безопасность → Локальная сеть." } } } }, - "Toggle": { - "comment": "Display name for the toggle action in the SwitchAction enum.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Umschalten" + "Toggle" : { + "comment" : "Display name for the toggle action in the SwitchAction enum.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Umschalten" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Toggle" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toggle" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Alternar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alternar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Vaihda" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vaihda" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Basculer" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basculer" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Attiva/Disattiva" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Attiva/Disattiva" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Bytt" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bytt" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Omschakelen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Omschakelen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Переключить" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Переключить" } } } }, - "Triggered": { - "comment": "Display name for the contact state \"OPEN\".", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Ausgelöst" + "Triggered" : { + "comment" : "Display name for the contact state \"OPEN\".", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ausgelöst" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Activado" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activado" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Lauennut" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lauennut" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Déclenché" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Déclenché" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Attivato" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Attivato" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Utløst" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Utløst" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Geactiveerd" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geactiveerd" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Сработал" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Сработал" } } } }, - "Turn off": { - "comment": "Turn off action.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Ausschalten" + "Turn off" : { + "comment" : "Turn off action.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ausschalten" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Apagar" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apagar" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Laita pois" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Laita pois" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Éteindre" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Éteindre" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Spegni" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Spegni" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Slå av" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Slå av" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Uitschakelen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uitschakelen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Выключить" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Выключить" } } } }, - "Turn on": { - "comment": "Turn on action.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Einschalten" + "Turn on" : { + "comment" : "Turn on action.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Einschalten" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Encender" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Encender" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Laita päälle" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Laita päälle" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Allumer" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Allumer" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Accendi" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Accendi" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Slå på" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Slå på" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Inschakelen" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inschakelen" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Включить" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Включить" } } } }, - "unable_to_add_certificate": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zertifikat konnte nicht zum Schlüsselbund hinzugefügt werden: %@." + "unable_to_add_certificate" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zertifikat konnte nicht zum Schlüsselbund hinzugefügt werden: %@." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Unable to add certificate to the keychain: %@." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Unable to add certificate to the keychain: %@." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "No se puede agregar el certificado al llavero %@." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se puede agregar el certificado al llavero %@." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Unable to add certificate to the keychain: %@." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Unable to add certificate to the keychain: %@." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Impossible d'ajouter le certificat au trousseau : %@." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossible d'ajouter le certificat au trousseau : %@." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Impossibile aggiungere il certificato al portachiavi: %@." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossibile aggiungere il certificato al portachiavi: %@." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kan ikke legge sertifikatet til i nøkkelkjeden: %@." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kan ikke legge sertifikatet til i nøkkelkjeden: %@." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Kan certificaat niet toevoegen aan de sleutelhanger: %@." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kan certificaat niet toevoegen aan de sleutelhanger: %@." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Unable to add certificate to the keychain: %@." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Unable to add certificate to the keychain: %@." } } } }, - "unable_to_decode_certificate": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zertifikat kann nicht dekodiert werden: %@." + "unable_to_decode_certificate" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zertifikat kann nicht dekodiert werden: %@." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Unable to decode certificate: %@." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Unable to decode certificate: %@." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "No se puede descifrar el certificado: %@." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se puede descifrar el certificado: %@." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Unable to decode certificate: %@." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Unable to decode certificate: %@." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Impossible de décoder le certificat : %@." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossible de décoder le certificat : %@." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Impossibile decodificare il certificato: %@." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossibile decodificare il certificato: %@." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Kan ikke dekode sertifikat: %@." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kan ikke dekode sertifikat: %@." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Kan certificaat niet decoderen: %@." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kan certificaat niet decoderen: %@." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Unable to decode certificate: %@." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Unable to decode certificate: %@." } } } }, - "Unexpected error: %@": { - "comment": "A user-visible description of a `DecodingError` that might occur when testing a network connection.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Unerwarteter Fehler: %@" + "Unexpected error: %@" : { + "comment" : "A user-visible description of a `DecodingError` that might occur when testing a network connection.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Unerwarteter Fehler: %@" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Unexpected error: %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Unexpected error: %@" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Error inesperado: %@" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error inesperado: %@" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Odottamaton virhe: %@" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Odottamaton virhe: %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Erreur inattendue : %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erreur inattendue : %@" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Errore imprevisto: %@" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Errore imprevisto: %@" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Uventet feil: %@" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uventet feil: %@" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Onverwachte fout: %@" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Onverwachte fout: %@" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Непредвиденная ошибка: %@" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Непредвиденная ошибка: %@" } } } }, - "unknown": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "unbekannt" + "unknown" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "unbekannt" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "unknown" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "unknown" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "desconocido" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "desconocido" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "unknown" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "unknown" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "inconnu" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "inconnu" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "sconosciuto" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "sconosciuto" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "ukjent" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "ukjent" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "onbekend" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "onbekend" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "unknown" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "unknown" } } } }, - "Unknown home": { - "comment": "Error message displayed when a home is selected but the home ID is not found in the list of stored homes.", - "isCommentAutoGenerated": true, - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Unbekanntes Zuhause" + "Unknown home" : { + "comment" : "Error message displayed when a home is selected but the home ID is not found in the list of stored homes.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Unbekanntes Zuhause" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Casa desconocida" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Casa desconocida" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Tuntematon koti" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tuntematon koti" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Domicile inconnu" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Domicile inconnu" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Casa sconosciuta" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Casa sconosciuta" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ukjent hjem" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ukjent hjem" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Onbekend thuis" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Onbekend thuis" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Неизвестный дом" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Неизвестный дом" } } } }, - "Updating…": { - "comment": "A text that appears while a user is waiting for the sitemap to update.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Aktualisierung …" + "Updating…" : { + "comment" : "A text that appears while a user is waiting for the sitemap to update.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktualisierung …" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Updating…" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Updating…" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Actualizando…" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actualizando…" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Päivitetään…" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Päivitetään…" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Mise à jour…" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mise à jour…" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Aggiorno …" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aggiorno …" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Oppdaterer…" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Oppdaterer…" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Bijwerken…" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bijwerken…" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Обновление…" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Обновление…" } } } }, - "URL": { - "comment": "A text field for entering the URL of the remote server.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "URL" + "URL" : { + "comment" : "A text field for entering the URL of the remote server.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "URL" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "URL" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "URL" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "URL" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "URL" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "URL" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "URL" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "URL" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL" } } } }, - "uselastpath_settings": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zuletzt besuchten Pfad verwenden?" + "uselastpath_settings" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zuletzt besuchten Pfad verwenden?" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Use last visited path?" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Use last visited path?" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "¿Usar la última ruta visitada?" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Usar la última ruta visitada?" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Käytä edellistä polkua?" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Käytä edellistä polkua?" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Utiliser le chemin de la dernière visite ?" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Utiliser le chemin de la dernière visite ?" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Usare L'Ultimo Percorso Visitato?" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Usare L'Ultimo Percorso Visitato?" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Bruk sist brukte bane?" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bruk sist brukte bane?" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Laatst bezochte pad gebruiken?" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Laatst bezochte pad gebruiken?" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Use last visited path?" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Use last visited path?" } } } }, - "username": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Benutzername" + "username" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Benutzername" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Username" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Username" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Nombre de usario" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nombre de usario" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Username" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Username" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Identifiant" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Identifiant" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Utente" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Utente" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Brukernavn" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brukernavn" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Gebruikersnaam" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gebruikersnaam" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Username" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Username" } } } }, - "Username": { - "comment": "A label displayed above the text field for the username.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Benutzername" + "Username" : { + "comment" : "A label displayed above the text field for the username.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Benutzername" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Username" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Username" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Nombre de usuario" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nombre de usuario" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Käyttäjänimi" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Käyttäjänimi" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Nom d’utilisateur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nom d’utilisateur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Nome utente" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nome utente" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Brukernavn" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brukernavn" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Gebruikersnaam" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gebruikersnaam" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Имя пользователя" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Имя пользователя" } } } }, - "Value": { - "comment": "Parameter title for a value in app intents.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Wert" + "Value" : { + "comment" : "Parameter title for a value in app intents.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wert" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Value" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Value" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Valor" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valor" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Arvo" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Arvo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Valeur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valeur" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Valore" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valore" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Verdi" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verdi" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Waarde" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Waarde" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Значение" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Значение" } } } }, - "version": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Version" + "version" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Version" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Version" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Version" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Versión" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Versión" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Version" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Version" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Version" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Version" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Versione" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Versione" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Versjon" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Versjon" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Versie" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Versie" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Version" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Version" } } } }, - "Very Cool": { - "comment": "A description for a very cool color temperature.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sehr kalt" + "Very Cool" : { + "comment" : "A description for a very cool color temperature.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sehr kalt" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Very Cool" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Very Cool" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Muy frío" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Muy frío" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Hyvin viileä" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hyvin viileä" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Très froid" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Très froid" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Molto freddo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Molto freddo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Svært kjølig" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Svært kjølig" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Zeer koel" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zeer koel" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Очень холодный" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Очень холодный" } } } }, - "Very Warm": { - "comment": "A color temperature range description.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sehr warm" + "Very Warm" : { + "comment" : "A color temperature range description.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sehr warm" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Very Warm" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Very Warm" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Muy cálido" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Muy cálido" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Hyvin lämmin" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hyvin lämmin" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Très chaud" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Très chaud" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Molto caldo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Molto caldo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Svært varm" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Svært varm" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Zeer warm" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zeer warm" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Очень тёплый" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Очень тёплый" } } } }, - "Warm White": { - "comment": "A color temperature range description.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Warmweiß" + "Warm White" : { + "comment" : "A color temperature range description.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Warmweiß" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Warm White" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Warm White" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Blanco cálido" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blanco cálido" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Lämmin valkoinen" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lämmin valkoinen" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Blanc chaud" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blanc chaud" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Bianco caldo" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bianco caldo" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Varm hvit" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Varm hvit" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Warm wit" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Warm wit" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Тёплый белый" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Тёплый белый" } } } }, - "warning": { - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Warnung" + "warning" : { + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Warnung" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Warning" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Warning" } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Aviso" + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aviso" } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Varoitus" + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Varoitus" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Attention" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Attention" } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Attenzione" + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Attenzione" } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Advarsel" + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Advarsel" } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Waarschuwing" + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Waarschuwing" } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Warning" + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Warning" } } } }, - "Warning: Renaming the home might cause external integrations like shortcuts to fail until reconfigured.": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Warnung: Das Umbenennen des Zuhauses kann dazu führen, dass externe Integrationen wie Kurzbefehle bis zur Neukonfiguration fehlschlagen." + "Warning: Renaming the home might cause external integrations like shortcuts to fail until reconfigured." : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Warnung: Das Umbenennen des Zuhauses kann dazu führen, dass externe Integrationen wie Kurzbefehle bis zur Neukonfiguration fehlschlagen." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Warning: Renaming the home might cause external integrations like shortcuts to fail until reconfigured." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Warning: Renaming the home might cause external integrations like shortcuts to fail until reconfigured." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Advertencia: Cambiar el nombre del hogar puede hacer que las integraciones externas, como los atajos, fallen hasta que se reconfiguren." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Advertencia: Cambiar el nombre del hogar puede hacer que las integraciones externas, como los atajos, fallen hasta que se reconfiguren." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Varoitus: Kodin uudelleennimeäminen voi aiheuttaa ulkoisten integraatioiden, kuten pikakomentojen, epäonnistumisen, kunnes ne on uudelleenmääritetty." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Varoitus: Kodin uudelleennimeäminen voi aiheuttaa ulkoisten integraatioiden, kuten pikakomentojen, epäonnistumisen, kunnes ne on uudelleenmääritetty." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Attention : renommer la maison peut entraîner l'échec des intégrations externes telles que les raccourcis jusqu'à leur reconfiguration." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Attention : renommer la maison peut entraîner l'échec des intégrations externes telles que les raccourcis jusqu'à leur reconfiguration." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Attenzione: rinominare la casa potrebbe causare il malfunzionamento delle integrazioni esterne come i comandi rapidi fino alla loro riconfigurazione." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Attenzione: rinominare la casa potrebbe causare il malfunzionamento delle integrazioni esterne come i comandi rapidi fino alla loro riconfigurazione." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Advarsel: Å gi hjemmet nytt navn kan føre til at eksterne integrasjoner som snarveier mislykkes inntil de er rekonfigurert." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Advarsel: Å gi hjemmet nytt navn kan føre til at eksterne integrasjoner som snarveier mislykkes inntil de er rekonfigurert." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Waarschuwing: Het hernoemen van het huis kan ertoe leiden dat externe integraties zoals snelkoppelingen mislukken totdat ze opnieuw zijn geconfigureerd." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Waarschuwing: Het hernoemen van het huis kan ertoe leiden dat externe integraties zoals snelkoppelingen mislukken totdat ze opnieuw zijn geconfigureerd." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Предупреждение: переименование дома может привести к сбою внешних интеграций, таких как ярлыки, до их перенастройки." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Предупреждение: переименование дома может привести к сбою внешних интеграций, таких как ярлыки, до их перенастройки." } } } }, - "You appear to be offline. Check your internet connection.": { - "comment": "Error message displayed when the user is offline.", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Du scheinst offline zu sein. Überprüfe deine Internetverbindung." + "You appear to be offline. Check your internet connection." : { + "comment" : "Error message displayed when the user is offline.", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Du scheinst offline zu sein. Überprüfe deine Internetverbindung." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "You appear to be offline. Check your internet connection." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "You appear to be offline. Check your internet connection." } }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Parece que estás sin conexión. Comprueba tu conexión a internet." + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Parece que estás sin conexión. Comprueba tu conexión a internet." } }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Näyttää siltä, että olet offline. Tarkista internet-yhteytesi." + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Näyttää siltä, että olet offline. Tarkista internet-yhteytesi." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Tu sembles être hors ligne. Vérifie ta connexion Internet." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tu sembles être hors ligne. Vérifie ta connexion Internet." } }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Sembra che tu sia offline. Controlla la connessione a Internet." + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sembra che tu sia offline. Controlla la connessione a Internet." } }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Du ser ut til å være frakoblet. Sjekk internettilkoblingen din." + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Du ser ut til å være frakoblet. Sjekk internettilkoblingen din." } }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "U lijkt offline te zijn. Controleer uw internetverbinding." + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "U lijkt offline te zijn. Controleer uw internetverbinding." } }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Похоже, вы не в сети. Проверьте подключение к интернету." + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Похоже, вы не в сети. Проверьте подключение к интернету." } } } - }, - "carplay_not_configured": { - "comment": "CarPlay root list placeholder shown before a CarPlay favorites page is set up.", - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Keine CarPlay-Favoriten konfiguriert" - } - }, - "en": { - "stringUnit": { - "state": "translated", - "value": "No CarPlay favorites configured" - } - }, - "es": { - "stringUnit": { - "state": "translated", - "value": "No hay favoritos de CarPlay configurados" - } - }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "CarPlay-suosikkeja ei ole määritetty" - } - }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Aucun favori CarPlay configuré" - } - }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Nessun preferito CarPlay configurato" - } - }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Ingen CarPlay-favoritter er konfigurert" - } - }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Geen CarPlay-favorieten geconfigureerd" - } - }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Избранное CarPlay не настроено" - } - } - } - }, - "carplay_not_configured_detail": { - "comment": "CarPlay root list placeholder detail text shown before a CarPlay favorites page is set up.", - "extractionState": "manual", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "CarPlay-Sitemapseite in den Einstellungen einrichten" - } - }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Set up a CarPlay sitemap page in Settings" - } - }, - "es": { - "stringUnit": { - "state": "translated", - "value": "Configura una página de mapa del sitio de CarPlay en Ajustes" - } - }, - "fi": { - "stringUnit": { - "state": "translated", - "value": "Määritä CarPlay-sivustokarttasivu Asetuksissa" - } - }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Configurez une page de plan de site CarPlay dans les réglages" - } - }, - "it": { - "stringUnit": { - "state": "translated", - "value": "Configura una pagina sitemap CarPlay nelle Impostazioni" - } - }, - "nb": { - "stringUnit": { - "state": "translated", - "value": "Konfigurer en CarPlay-nettstedskartside i Innstillinger" - } - }, - "nl": { - "stringUnit": { - "state": "translated", - "value": "Stel een CarPlay-sitemappagina in via Instellingen" - } - }, - "ru": { - "stringUnit": { - "state": "translated", - "value": "Настройте страницу карты сайта CarPlay в Настройках" - } - } - } - }, - "None": { - "comment": "A label for the \"Sitemap for CarPlay\" picker.", - "isCommentAutoGenerated": true - }, - "Sitemap for CarPlay": { - "comment": "A label for selecting a sitemap to be used for CarPlay.", - "isCommentAutoGenerated": true } }, - "version": "1.1" -} + "version" : "1.1" +} \ No newline at end of file From dbf9282f42bb4c919082644ce81b210b7705ac2b Mon Sep 17 00:00:00 2001 From: openhab-bot Date: Mon, 20 Jul 2026 09:22:12 +0000 Subject: [PATCH 88/91] committed version bump: 3.4.14 (308) Signed-off-by: openhab-bot --- CHANGELOG.md | 89 ++++++++++++++++++++++++++++++++++++++++++++++++ Version.xcconfig | 4 +-- 2 files changed, 91 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47c3c8aab..f738d1b28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,95 @@ ## [Unreleased] +- CarPlay placeholder text adjustments (#1303) +- Extend CarPlay icons (#1302) +- committed version bump: 3.4.13 (307) +- feat(carplay): restrict widgets to plain switches and press-release buttons +- committed version bump: 3.4.12 (306) +- fix: widget visibility filtering and test-layer crash fixes +- fix: use CPInformationTemplate for CarPlay not-configured placeholder +- SensorItemRow adjustments for consistent value font sizes (#1300) +- committed version bump: 3.3.9 (305) +- committed version bump: 3.4.11 (303) +- Remove ohMinimumHitTarget() to reinstate consistent sitemap row heights (#1298) +- docs: expand ohMinimumHitTarget doc comment +- singleMappingButton toggle button fix (#1296) +- committed version bump: 3.4.10 (302) +- fix: show inline error for inaccessible images and suppress connectivity alerts +- Minor widget layout adjustments +- fix: keep SSE stream alive across CarPlay page refreshes +- committed version bump: 3.4.9 (300) +- fix: complete missing localizations for 3 strings +- refactor: rename OpenHABIconOverlay to OpenHABIconWatermark and complete translations +- chore: switch main app target to CarPlay entitlements file +- fix: use LocalizedStringResource in AppIntents display representations +- fix: improve CarPlay robustness and localizations +- Widgets layout adjustments (#1292) +- committed version bump: 3.4.8 (298) +- fix: remove 7 stale localization keys with no source references +- fix: remove stale %lld %% localization keys for screensaver percentage labels +- Make Circular Accessory Switch icon state aware (#1291) +- committed version bump: 3.4.7 (297) +- refactor: replace remote icon fetch with SF symbol mapping in CarPlay +- feat: switch CarPlay to CPGridTemplate with openHAB icons +- fix: show placeholder instead of empty CPListTemplate when no compatible widgets +- refactor: migrate CarPlay streaming from long-poll to SSE +- feat: add CarPlay support with sitemap-based favorites scene +- Migrate sitemap page updates from long-polling to SSE (#1168) +- Small Widget background watermark alignment adjustments (#1289) +- fix: show message body below title in notification list +- fix: add Localizable.xcstrings to widget extension target membership +- fix: remove duplicate knownRegions from project.pbxproj +- committed version bump: 3.4.6 (296) +- Allow for two lines label and value for iOS Lock Screen Accessories (#1286) +- chore: apply linter fixes +- feat: add accessory widget previews for Switch and Sensor widgets +- feat: wide-display sitemap grid and full-width media rows +- refactor: remove UIImageView polling timer from MJPEG video stream +- committed version bump: 3.4.5 (294) +- Fix small widget content to be cntered +- committed version bump: 3.4.4 (293) +- committed version bump: 3.4.3 (292) +- fix: sync allowedItemTypes with query and add regression test +- feat: make Dimmer items selectable in Set Switch State intent +- chore: apply linter fixes +- fix: detect GIF images by magic bytes in ImageRowView +- committed version bump: 3.4.2 (290) +- fix: animate GIFs in the Image sitemap widget (#1280) +- Widget adjustments (#1279) +- fix: lock screen widgets now send commands on tap +- fix: notification ping now respects the ringer/silent switch (#1278) +- fix: add OpenHABShortcuts.swift to app target to resolve AppShortcuts.xcstrings phrase warnings +- committed version bump: 3.4.1 (287) +- fix: add provisioning profile for openHABWidgetExtension +- l10n: add Spanish, Finnish, Norwegian, and Russian translations +- l10n: switch German and French translations to informal address (du/tu) +- chore: remove 5 stale localization keys on feature branch +- l10n: add Spanish, Finnish, Norwegian, and Russian translations +- l10n: translate 2 new widget keys into all 9 languages +- chore: update openHABWidgetExtension scheme with askForAppToLaunch +- fix: restore project.pbxproj corrupted by bad 3-way merge of pbxproj +- chore: add shared schemes for openHABWidgetExtension and OpenHABWatchComplicationsExtension +- build: raise macOS minimum to 15 for AsyncSequence.Failure support +- chore: bump openHABTestsSwift deployment target to iOS 18.0 +- Merge develop + fix build warnings; add iOS home screen widgets with item display +- Sensor widget: format state values using stateDescription numberPattern +- Sensor widget: support 1/2/4 items per small/medium/large size +- Make ScrollToTop overlay button conditional (#1231) +- Bump minimum deployment targets to iOS 18 / watchOS 11 +- Use iOS 18 onScrollGeometryChange in SitemapPageView +- Use iOS 17 animation and scroll APIs +- Replace SetLocationValueIntent with SetActiveHomeIntent in AppShortcutsProvider +- Register SetActiveHomeIntent with AppShortcutsProvider +- Linting and AppIntents SSE warnings fixes +- Bump deployment targets to iOS 17.0 / watchOS 10.0 +- Convert AppShortcuts.strings to AppShortcuts.xcstrings String Catalog +- Fix AppIntentsMetadataProcessor synonym warnings in AppEnum cases +- Add AppShortcutsProvider and fix intent dialog gaps +- Upgrade SwiftFormatPlugin 0.58.7→0.61.1, SwiftLintPlugin 0.62.2→0.63.3 (#1225) +- ItemEventStream: backport SSE watchdog and self-contained network monitor +- openHABWidget: add interactive switch and sensor widgets + - feat(carplay): restrict widgets to plain switches and press-release buttons - committed version bump: 3.4.12 (306) - fix: widget visibility filtering and test-layer crash fixes diff --git a/Version.xcconfig b/Version.xcconfig index 2a49e7d01..a89dfac8b 100644 --- a/Version.xcconfig +++ b/Version.xcconfig @@ -6,5 +6,5 @@ // https://developer.apple.com/documentation/xcode/adding-a-build-configuration-file-to-your-project -MARKETING_VERSION = 3.4.13 -CURRENT_PROJECT_VERSION = 307 +MARKETING_VERSION = 3.4.14 +CURRENT_PROJECT_VERSION = 308 From b73c3e17f1fcf8b5fde2b02b2f5de0be922472a3 Mon Sep 17 00:00:00 2001 From: DigiH <17110652+DigiH@users.noreply.github.com> Date: Mon, 20 Jul 2026 18:44:15 +0200 Subject: [PATCH 89/91] Add Garage Door icon to CarPlay icons (#1304) Signed-off-by: DigiH <17110652+DigiH@users.noreply.github.com> --- openHAB/CarPlaySceneDelegate.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openHAB/CarPlaySceneDelegate.swift b/openHAB/CarPlaySceneDelegate.swift index 90320e2f8..9169355c0 100644 --- a/openHAB/CarPlaySceneDelegate.swift +++ b/openHAB/CarPlaySceneDelegate.swift @@ -248,6 +248,8 @@ final class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegat let icon = widget.icon.lowercased() let symbolName: SFSymbol = if icon.contains("power") { isOn ? .powerCircleFill : .powerCircle + } else if icon.contains("garage") { + isOn ? .doorGarageDoubleBayOpen : .doorGarageDoubleBayClosed } else if icon.contains("lamp") || icon.contains("light") || icon.contains("bulb") { isOn ? .lightbulbFill : .lightbulb } else if icon.contains("lock") || icon.contains("security") || icon.contains("alarm") { From 337cac5b345be9a760e67a131424dccddd793506 Mon Sep 17 00:00:00 2001 From: Tim Mueller-Seydlitz Date: Mon, 20 Jul 2026 22:26:26 +0200 Subject: [PATCH 90/91] l10n: translate and review CarPlay strings for all supported languages Adds translations for "None" and "Sitemap for CarPlay" picker labels, and updates carplay_not_configured / carplay_not_configured_detail to match the current English text (Switch items, iOS app settings) across de, es, fi, fr, it, nb, nl, and ru. Signed-off-by: Tim Mueller-Seydlitz --- .../Supporting Files/Localizable.xcstrings | 168 ++++++++++++++---- 1 file changed, 134 insertions(+), 34 deletions(-) diff --git a/openHAB/Supporting Files/Localizable.xcstrings b/openHAB/Supporting Files/Localizable.xcstrings index 28e29a980..eeef03770 100644 --- a/openHAB/Supporting Files/Localizable.xcstrings +++ b/openHAB/Supporting Files/Localizable.xcstrings @@ -2347,8 +2347,8 @@ "localizations" : { "de" : { "stringUnit" : { - "state" : "needs_review", - "value" : "Keine CarPlay-Favoriten konfiguriert" + "state" : "translated", + "value" : "Keine CarPlay-kompatiblen Switch-Elemente konfiguriert." } }, "en" : { @@ -2359,44 +2359,44 @@ }, "es" : { "stringUnit" : { - "state" : "needs_review", - "value" : "No hay favoritos de CarPlay configurados" + "state" : "translated", + "value" : "No hay elementos Switch compatibles con CarPlay configurados." } }, "fi" : { "stringUnit" : { - "state" : "needs_review", - "value" : "CarPlay-suosikkeja ei ole määritetty" + "state" : "translated", + "value" : "CarPlay-yhteensopivia Switch-kohteita ei ole määritetty." } }, "fr" : { "stringUnit" : { - "state" : "needs_review", - "value" : "Aucun favori CarPlay configuré" + "state" : "translated", + "value" : "Aucun élément Switch compatible CarPlay configuré." } }, "it" : { "stringUnit" : { - "state" : "needs_review", - "value" : "Nessun preferito CarPlay configurato" + "state" : "translated", + "value" : "Nessun elemento Switch compatibile con CarPlay configurato." } }, "nb" : { "stringUnit" : { - "state" : "needs_review", - "value" : "Ingen CarPlay-favoritter er konfigurert" + "state" : "translated", + "value" : "Ingen CarPlay-kompatible Switch-elementer er konfigurert." } }, "nl" : { "stringUnit" : { - "state" : "needs_review", - "value" : "Geen CarPlay-favorieten geconfigureerd" + "state" : "translated", + "value" : "Geen CarPlay-compatibele Switch-items geconfigureerd." } }, "ru" : { "stringUnit" : { - "state" : "needs_review", - "value" : "Избранное CarPlay не настроено" + "state" : "translated", + "value" : "Нет настроенных Switch-элементов, совместимых с CarPlay." } } } @@ -2407,8 +2407,8 @@ "localizations" : { "de" : { "stringUnit" : { - "state" : "needs_review", - "value" : "CarPlay-Sitemapseite in den Einstellungen einrichten" + "state" : "translated", + "value" : "CarPlay-Sitemapseite in den iOS-App-Einstellungen einrichten" } }, "en" : { @@ -2419,44 +2419,44 @@ }, "es" : { "stringUnit" : { - "state" : "needs_review", - "value" : "Configura una página de mapa del sitio de CarPlay en Ajustes" + "state" : "translated", + "value" : "Configura una página de sitemap de CarPlay en los ajustes de la app de iOS" } }, "fi" : { "stringUnit" : { - "state" : "needs_review", - "value" : "Määritä CarPlay-sivustokarttasivu Asetuksissa" + "state" : "translated", + "value" : "Määritä CarPlay-sivustokarttasivu iOS-sovelluksen asetuksissa" } }, "fr" : { "stringUnit" : { - "state" : "needs_review", - "value" : "Configurez une page de plan de site CarPlay dans les réglages" + "state" : "translated", + "value" : "Configurez une page de plan de site CarPlay dans les réglages de l'app iOS" } }, "it" : { "stringUnit" : { - "state" : "needs_review", - "value" : "Configura una pagina sitemap CarPlay nelle Impostazioni" + "state" : "translated", + "value" : "Configura una pagina sitemap CarPlay nelle impostazioni dell'app iOS" } }, "nb" : { "stringUnit" : { - "state" : "needs_review", - "value" : "Konfigurer en CarPlay-nettstedskartside i Innstillinger" + "state" : "translated", + "value" : "Konfigurer en CarPlay-nettstedskartside i iOS-app-innstillingene" } }, "nl" : { "stringUnit" : { - "state" : "needs_review", - "value" : "Stel een CarPlay-sitemappagina in via Instellingen" + "state" : "translated", + "value" : "Stel een CarPlay-sitemappagina in via de iOS-app-instellingen" } }, "ru" : { "stringUnit" : { - "state" : "needs_review", - "value" : "Настройте страницу карты сайта CarPlay в Настройках" + "state" : "translated", + "value" : "Настройте страницу карты сайта CarPlay в настройках приложения iOS" } } } @@ -11746,7 +11746,57 @@ }, "None" : { "comment" : "A label for the \"Sitemap for CarPlay\" picker.", - "isCommentAutoGenerated" : true + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ninguna" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ei mitään" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucune" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nessuna" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geen" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Нет" + } + } + } }, "notification" : { "localizations" : { @@ -18686,7 +18736,57 @@ }, "Sitemap for CarPlay" : { "comment" : "A label for selecting a sitemap to be used for CarPlay.", - "isCommentAutoGenerated" : true + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap für CarPlay" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap para CarPlay" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "CarPlay-sivustokartta" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap pour CarPlay" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap per CarPlay" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nettstedskart for CarPlay" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitemap voor CarPlay" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Карта сайта для CarPlay" + } + } + } }, "sitemap_settings" : { "localizations" : { From a947995eca5fb882116a44f606de7519bd5f239b Mon Sep 17 00:00:00 2001 From: openhab-bot Date: Mon, 20 Jul 2026 21:10:48 +0000 Subject: [PATCH 91/91] committed version bump: 3.4.15 (309) Signed-off-by: openhab-bot --- CHANGELOG.md | 92 ++++++++++++++++++++++++++++++++++++++++++++++++ Version.xcconfig | 4 +-- 2 files changed, 94 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f738d1b28..c0fba65e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,98 @@ ## [Unreleased] +- l10n: translate and review CarPlay strings for all supported languages +- Add Garage Door icon to CarPlay icons (#1304) +- committed version bump: 3.4.14 (308) +- CarPlay placeholder text adjustments (#1303) +- Extend CarPlay icons (#1302) +- committed version bump: 3.4.13 (307) +- feat(carplay): restrict widgets to plain switches and press-release buttons +- committed version bump: 3.4.12 (306) +- fix: widget visibility filtering and test-layer crash fixes +- fix: use CPInformationTemplate for CarPlay not-configured placeholder +- SensorItemRow adjustments for consistent value font sizes (#1300) +- committed version bump: 3.3.9 (305) +- committed version bump: 3.4.11 (303) +- Remove ohMinimumHitTarget() to reinstate consistent sitemap row heights (#1298) +- docs: expand ohMinimumHitTarget doc comment +- singleMappingButton toggle button fix (#1296) +- committed version bump: 3.4.10 (302) +- fix: show inline error for inaccessible images and suppress connectivity alerts +- Minor widget layout adjustments +- fix: keep SSE stream alive across CarPlay page refreshes +- committed version bump: 3.4.9 (300) +- fix: complete missing localizations for 3 strings +- refactor: rename OpenHABIconOverlay to OpenHABIconWatermark and complete translations +- chore: switch main app target to CarPlay entitlements file +- fix: use LocalizedStringResource in AppIntents display representations +- fix: improve CarPlay robustness and localizations +- Widgets layout adjustments (#1292) +- committed version bump: 3.4.8 (298) +- fix: remove 7 stale localization keys with no source references +- fix: remove stale %lld %% localization keys for screensaver percentage labels +- Make Circular Accessory Switch icon state aware (#1291) +- committed version bump: 3.4.7 (297) +- refactor: replace remote icon fetch with SF symbol mapping in CarPlay +- feat: switch CarPlay to CPGridTemplate with openHAB icons +- fix: show placeholder instead of empty CPListTemplate when no compatible widgets +- refactor: migrate CarPlay streaming from long-poll to SSE +- feat: add CarPlay support with sitemap-based favorites scene +- Migrate sitemap page updates from long-polling to SSE (#1168) +- Small Widget background watermark alignment adjustments (#1289) +- fix: show message body below title in notification list +- fix: add Localizable.xcstrings to widget extension target membership +- fix: remove duplicate knownRegions from project.pbxproj +- committed version bump: 3.4.6 (296) +- Allow for two lines label and value for iOS Lock Screen Accessories (#1286) +- chore: apply linter fixes +- feat: add accessory widget previews for Switch and Sensor widgets +- feat: wide-display sitemap grid and full-width media rows +- refactor: remove UIImageView polling timer from MJPEG video stream +- committed version bump: 3.4.5 (294) +- Fix small widget content to be cntered +- committed version bump: 3.4.4 (293) +- committed version bump: 3.4.3 (292) +- fix: sync allowedItemTypes with query and add regression test +- feat: make Dimmer items selectable in Set Switch State intent +- chore: apply linter fixes +- fix: detect GIF images by magic bytes in ImageRowView +- committed version bump: 3.4.2 (290) +- fix: animate GIFs in the Image sitemap widget (#1280) +- Widget adjustments (#1279) +- fix: lock screen widgets now send commands on tap +- fix: notification ping now respects the ringer/silent switch (#1278) +- fix: add OpenHABShortcuts.swift to app target to resolve AppShortcuts.xcstrings phrase warnings +- committed version bump: 3.4.1 (287) +- fix: add provisioning profile for openHABWidgetExtension +- l10n: add Spanish, Finnish, Norwegian, and Russian translations +- l10n: switch German and French translations to informal address (du/tu) +- chore: remove 5 stale localization keys on feature branch +- l10n: add Spanish, Finnish, Norwegian, and Russian translations +- l10n: translate 2 new widget keys into all 9 languages +- chore: update openHABWidgetExtension scheme with askForAppToLaunch +- fix: restore project.pbxproj corrupted by bad 3-way merge of pbxproj +- chore: add shared schemes for openHABWidgetExtension and OpenHABWatchComplicationsExtension +- build: raise macOS minimum to 15 for AsyncSequence.Failure support +- chore: bump openHABTestsSwift deployment target to iOS 18.0 +- Merge develop + fix build warnings; add iOS home screen widgets with item display +- Sensor widget: format state values using stateDescription numberPattern +- Sensor widget: support 1/2/4 items per small/medium/large size +- Make ScrollToTop overlay button conditional (#1231) +- Bump minimum deployment targets to iOS 18 / watchOS 11 +- Use iOS 18 onScrollGeometryChange in SitemapPageView +- Use iOS 17 animation and scroll APIs +- Replace SetLocationValueIntent with SetActiveHomeIntent in AppShortcutsProvider +- Register SetActiveHomeIntent with AppShortcutsProvider +- Linting and AppIntents SSE warnings fixes +- Bump deployment targets to iOS 17.0 / watchOS 10.0 +- Convert AppShortcuts.strings to AppShortcuts.xcstrings String Catalog +- Fix AppIntentsMetadataProcessor synonym warnings in AppEnum cases +- Add AppShortcutsProvider and fix intent dialog gaps +- Upgrade SwiftFormatPlugin 0.58.7→0.61.1, SwiftLintPlugin 0.62.2→0.63.3 (#1225) +- ItemEventStream: backport SSE watchdog and self-contained network monitor +- openHABWidget: add interactive switch and sensor widgets + - CarPlay placeholder text adjustments (#1303) - Extend CarPlay icons (#1302) - committed version bump: 3.4.13 (307) diff --git a/Version.xcconfig b/Version.xcconfig index a89dfac8b..ad4f7936f 100644 --- a/Version.xcconfig +++ b/Version.xcconfig @@ -6,5 +6,5 @@ // https://developer.apple.com/documentation/xcode/adding-a-build-configuration-file-to-your-project -MARKETING_VERSION = 3.4.14 -CURRENT_PROJECT_VERSION = 308 +MARKETING_VERSION = 3.4.15 +CURRENT_PROJECT_VERSION = 309