From 3391e229306aaf5a80403a86f4e5df2c9d2bd408 Mon Sep 17 00:00:00 2001 From: Rinat Enikeev Date: Tue, 19 Dec 2023 22:25:57 +0200 Subject: [PATCH] NotificationService localization fixes (#1804) Implements localization for notifications except ovement and offline --- .../Sources/NotificationService.swift | 62 +++++++------------ .../NotificationService/target.yml | 26 +++++++- .../View/UI/TagChartsViewController.swift | 2 +- .../OffsetCorrectionAppleViewController.swift | 4 +- .../View/UI/TagSettingsViewController.swift | 52 +++++++++++----- .../HumidityUnit+Localization.swift | 3 +- .../Structs/ExportHeadersProvider.swift | 8 +-- .../RuuviStation/Sources/Station.entitlements | 8 +-- .../Templates/strings-swift5.stencil | 19 ++++-- .../RuuviLocalSettingsUserDefaults.swift | 4 +- 10 files changed, 110 insertions(+), 78 deletions(-) diff --git a/Apps/RuuviStation/NotificationService/Sources/NotificationService.swift b/Apps/RuuviStation/NotificationService/Sources/NotificationService.swift index 537d14b17..aac58fb0c 100644 --- a/Apps/RuuviStation/NotificationService/Sources/NotificationService.swift +++ b/Apps/RuuviStation/NotificationService/Sources/NotificationService.swift @@ -1,3 +1,4 @@ +import RuuviLocalization import UserNotifications class NotificationService: UNNotificationServiceExtension { @@ -106,62 +107,45 @@ extension NotificationService { return "" } - var format = "" + let languageUDKey = "SettingsUserDegaults.languageUDKey" + let locale: Locale + if let languageCode = notificationServiceAppGroup?.string(forKey: languageUDKey) { + locale = Locale(identifier: languageCode) + } else { + locale = .current + } + switch triggerType { case .under: switch alertType { case .temperature: - format = "alert_notification_temperature_low_threshold" + return RuuviLocalization.alertNotificationTemperatureLowThreshold(threshold, locale) case .humidity: - format = "alert_notification_humidity_low_threshold" + return RuuviLocalization.alertNotificationHumidityLowThreshold(threshold, locale) case .pressure: - format = "alert_notification_pressure_low_threshold" + return RuuviLocalization.alertNotificationPressureLowThreshold(threshold, locale) case .signal: - format = "alert_notification_rssi_low_threshold" + return RuuviLocalization.alertNotificationRssiLowThreshold(threshold, locale) case .movement: - let format = "LocalNotificationsManager.DidMove.title" - return localized(value: format) - default: - break + return RuuviLocalization.LocalNotificationsManager.DidMove.title // TODO: @rinat localize + case .offline: + return "" // TODO: @rinat obtain spec } case .over: switch alertType { case .temperature: - format = "alert_notification_temperature_high_threshold" + return RuuviLocalization.alertNotificationTemperatureHighThreshold(threshold, locale) case .humidity: - format = "alert_notification_humidity_high_threshold" + return RuuviLocalization.alertNotificationHumidityHighThreshold(threshold, locale) case .pressure: - format = "alert_notification_pressure_high_threshold" + return RuuviLocalization.alertNotificationPressureHighThreshold(threshold, locale) case .signal: - format = "alert_notification_rssi_high_threshold" + return RuuviLocalization.alertNotificationRssiHighThreshold(threshold, locale) case .movement: - let format = "LocalNotificationsManager.DidMove.title" - return localized(value: format) - default: - break + return RuuviLocalization.LocalNotificationsManager.DidMove.title // TODO: @rinat localize + case .offline: + return "" // TODO: @rinat obtain spec } } - - return String(format: localized(value: format), threshold) - } - - private func localized(value: String) -> String { - let languageUDKey = "SettingsUserDegaults.languageUDKey" - guard let languageCode = notificationServiceAppGroup?.string(forKey: languageUDKey), - let bundle = Bundle.main.path( - forResource: languageCode, - ofType: "lproj" - ), - let languageBundle = Bundle(path: bundle) - else { - return NSLocalizedString(value, comment: value) - } - - return NSLocalizedString( - value, - tableName: nil, - bundle: languageBundle, - comment: value - ) } } diff --git a/Apps/RuuviStation/NotificationService/target.yml b/Apps/RuuviStation/NotificationService/target.yml index 78a600716..6bdf91c5b 100644 --- a/Apps/RuuviStation/NotificationService/target.yml +++ b/Apps/RuuviStation/NotificationService/target.yml @@ -36,6 +36,30 @@ targets: excludes: - "*.entitlements" - Info.plist + - path: ../Widgets/Sources resources: - path: ../Sources/Resources/Strings/ - - path: ../Sources/Resources/Sounds/ \ No newline at end of file + - path: ../Sources/Resources/Sounds/ + dependencies: + - package: Swinject + - package: BTKit + - package: Future + - package: GRDB + - package: Humidity + - package: KeychainAccess + - target: RuuviUser + embed: true + - target: RuuviCloud + embed: true + - target: RuuviOntology + embed: true + - target: RuuviPool + embed: true + - target: RuuviLocal + embed: true + - target: RuuviPersistence + embed: true + - target: RuuviContext + embed: true + - target: RuuviLocalization + embed: true \ No newline at end of file diff --git a/Apps/RuuviStation/Sources/Classes/Presentation/Modules/Dashboard/Charts/View/UI/TagChartsViewController.swift b/Apps/RuuviStation/Sources/Classes/Presentation/Modules/Dashboard/Charts/View/UI/TagChartsViewController.swift index b53c01d7b..28646ec8d 100644 --- a/Apps/RuuviStation/Sources/Classes/Presentation/Modules/Dashboard/Charts/View/UI/TagChartsViewController.swift +++ b/Apps/RuuviStation/Sources/Classes/Presentation/Modules/Dashboard/Charts/View/UI/TagChartsViewController.swift @@ -798,7 +798,7 @@ extension TagChartsViewController: TagChartsViewInput { syncStatusLabel.text = RuuviLocalization.TagCharts.Status.serving case let .reading(points): let format = RuuviLocalization.readingHistoryX - syncStatusLabel.text = format(Float(points)) + syncStatusLabel.text = format(Float(points), Locale.current) case .disconnecting: syncStatusLabel.text = RuuviLocalization.TagCharts.Status.disconnecting case .success: diff --git a/Apps/RuuviStation/Sources/Classes/Presentation/Modules/TagSettings/Submodules/OffsetCorrection/View/Apple/OffsetCorrectionAppleViewController.swift b/Apps/RuuviStation/Sources/Classes/Presentation/Modules/TagSettings/Submodules/OffsetCorrection/View/Apple/OffsetCorrectionAppleViewController.swift index a09096e17..f891261fa 100644 --- a/Apps/RuuviStation/Sources/Classes/Presentation/Modules/TagSettings/Submodules/OffsetCorrection/View/Apple/OffsetCorrectionAppleViewController.swift +++ b/Apps/RuuviStation/Sources/Classes/Presentation/Modules/TagSettings/Submodules/OffsetCorrection/View/Apple/OffsetCorrectionAppleViewController.swift @@ -195,11 +195,11 @@ extension OffsetCorrectionAppleViewController: OffsetCorrectionViewInput { case .pressure: let format = RuuviLocalization.OffsetCorrection.Dialog.Calibration.enterPressure let unit = viewModel.pressureUnit.value ?? .hectopascals - message = format(unit.symbol) + message = format(unit.symbol, Locale.current) default: let format = RuuviLocalization.OffsetCorrection.Dialog.Calibration.enterTemperature let unit = viewModel.temperatureUnit.value ?? .celsius - message = format(unit.symbol) + message = format(unit.symbol, Locale.current) } let controller = UIAlertController(title: title, message: message, preferredStyle: .alert) diff --git a/Apps/RuuviStation/Sources/Classes/Presentation/Modules/TagSettings/View/UI/TagSettingsViewController.swift b/Apps/RuuviStation/Sources/Classes/Presentation/Modules/TagSettings/View/UI/TagSettingsViewController.swift index ebe619d50..3041a7c3d 100644 --- a/Apps/RuuviStation/Sources/Classes/Presentation/Modules/TagSettings/View/UI/TagSettingsViewController.swift +++ b/Apps/RuuviStation/Sources/Classes/Presentation/Modules/TagSettings/View/UI/TagSettingsViewController.swift @@ -178,7 +178,8 @@ class TagSettingsViewController: UIViewController { private lazy var temperatureAlertSection: TagSettingsSection? = { let sectionTitle = temperatureAlertFormat( - viewModel?.temperatureUnit.value?.symbol ?? RuuviLocalization.na + viewModel?.temperatureUnit.value?.symbol ?? RuuviLocalization.na, + Locale.current ) let section = TagSettingsSection( identifier: .alertTemperature, @@ -203,7 +204,7 @@ class TagSettingsViewController: UIViewController { private lazy var humidityAlertSection: TagSettingsSection? = { let symbol = HumidityUnit.percent.symbol - let sectionTitle = airHumidityAlertFormat(symbol) + let sectionTitle = airHumidityAlertFormat(symbol, Locale.current) let section = TagSettingsSection( identifier: .alertHumidity, title: sectionTitle, @@ -227,7 +228,7 @@ class TagSettingsViewController: UIViewController { private lazy var pressureAlertSection: TagSettingsSection? = { let sectionTitle = pressureAlertFormat( - viewModel?.pressureUnit.value?.symbol ?? RuuviLocalization.na + viewModel?.pressureUnit.value?.symbol ?? RuuviLocalization.na, Locale.current ) let section = TagSettingsSection( identifier: .alertPressure, @@ -941,7 +942,7 @@ extension TagSettingsViewController { tableView.bind(viewModel.temperatureUnit) { [weak self] _, value in guard let sSelf = self else { return } sSelf.temperatureAlertSection?.title = sSelf.temperatureAlertFormat( - value?.symbol ?? RuuviLocalization.na + value?.symbol ?? RuuviLocalization.na, Locale.current ) } @@ -999,7 +1000,7 @@ extension TagSettingsViewController { header, unit in guard let sSelf = self else { return } let sectionTitle = sSelf.temperatureAlertFormat( - unit?.symbol ?? RuuviLocalization.na + unit?.symbol ?? RuuviLocalization.na, Locale.current ) header.setTitle(with: sectionTitle) } @@ -1119,7 +1120,7 @@ extension TagSettingsViewController { tableView.bind(viewModel.pressureUnit) { [weak self] _, value in guard let sSelf = self else { return } sSelf.pressureAlertSection?.title = sSelf.pressureAlertFormat( - value?.symbol ?? RuuviLocalization.na + value?.symbol ?? RuuviLocalization.na, Locale.current ) } @@ -1179,7 +1180,7 @@ extension TagSettingsViewController { [weak self] header, unit in guard let sSelf = self else { return } let sectionTitle = sSelf.pressureAlertFormat( - unit?.symbol ?? RuuviLocalization.na + unit?.symbol ?? RuuviLocalization.na, Locale.current ) header.setTitle(with: sectionTitle) } @@ -1876,13 +1877,27 @@ extension TagSettingsViewController { guard isViewLoaded else { return nil } let format = RuuviLocalization.TagSettings.Alerts.Temperature.description if let min, let max { - return attributedString(from: format(Float(min), Float(max))) + return attributedString(from: format(Float(min), Float(max), Locale.current)) } if let tu = viewModel?.temperatureUnit.value?.unitTemperature, let l = viewModel?.temperatureLowerBound.value?.converted(to: tu), let u = viewModel?.temperatureUpperBound.value?.converted(to: tu) { - return attributedString(from: format(Float(l.value.round(to: 2)), Float(u.value.round(to: 2)))) + return attributedString( + from: format( + Float( + l.value.round( + to: 2 + ) + ), + Float( + u.value.round( + to: 2 + ) + ), + Locale.current + ) + ) } else { return nil } @@ -1932,13 +1947,14 @@ extension TagSettingsViewController { guard isViewLoaded else { return nil } let format = RuuviLocalization.TagSettings.Alerts.Temperature.description if let min, let max { - return attributedString(from: format(Float(min), Float(max))) + return attributedString(from: format(Float(min), Float(max), Locale.current)) } if let l = viewModel?.relativeHumidityLowerBound.value, let u = viewModel?.relativeHumidityUpperBound.value { let message = format( Float(l.round(to: 2)), - Float(u.round(to: 2)) + Float(u.round(to: 2)), + Locale.current ) return attributedString(from: message) } else { @@ -1984,7 +2000,7 @@ extension TagSettingsViewController { if let minValue, let maxValue { return attributedString( - from: format(Float(minValue), Float(maxValue)) + from: format(Float(minValue), Float(maxValue), Locale.current) ) } @@ -2001,7 +2017,8 @@ extension TagSettingsViewController { ) let message = format( Float(l.round(to: 2)), - Float(u.round(to: 2)) + Float(u.round(to: 2)), + Locale.current ) return attributedString(from: message) } else { @@ -2063,7 +2080,7 @@ extension TagSettingsViewController { if let min, let max { return attributedString( - from: format(Float(min), Float(max)) + from: format(Float(min), Float(max), Locale.current) ) } @@ -2071,7 +2088,8 @@ extension TagSettingsViewController { let upper = viewModel?.signalUpperBound.value { let message = format( Float(lower), - Float(upper) + Float(upper), + Locale.current ) return attributedString(from: message) } else { @@ -3740,7 +3758,7 @@ extension TagSettingsViewController { alertTextField.delegate = self let format = RuuviLocalization.TagSettings.AlertSettings.Dialog.min alertTextField.placeholder = format( - Float(minimumBound) + Float(minimumBound), Locale.current ) alertTextField.keyboardType = .decimalPad alertMinRangeTextField = alertTextField @@ -3757,7 +3775,7 @@ extension TagSettingsViewController { alertTextField.delegate = self let format = RuuviLocalization.TagSettings.AlertSettings.Dialog.max alertTextField.placeholder = format( - Float(maximumBound) + Float(maximumBound), Locale.current ) alertTextField.keyboardType = .decimalPad alertMaxRangeTextField = alertTextField diff --git a/Apps/RuuviStation/Sources/Extensions/HumidityUnit+Localization.swift b/Apps/RuuviStation/Sources/Extensions/HumidityUnit+Localization.swift index 922004d74..5e0c978fa 100644 --- a/Apps/RuuviStation/Sources/Extensions/HumidityUnit+Localization.swift +++ b/Apps/RuuviStation/Sources/Extensions/HumidityUnit+Localization.swift @@ -7,8 +7,7 @@ extension HumidityUnit: SelectionItemProtocol { switch self { case .percent: { _ in RuuviLocalization.HumidityUnit.Percent.title } case .gm3: { _ in RuuviLocalization.HumidityUnit.Gm3.title } - case .dew: - RuuviLocalization.HumidityUnit.Dew.title + case .dew: { param in RuuviLocalization.HumidityUnit.Dew.title(param, Locale.current) } } } diff --git a/Apps/RuuviStation/Sources/Extensions/Structs/ExportHeadersProvider.swift b/Apps/RuuviStation/Sources/Extensions/Structs/ExportHeadersProvider.swift index 0af7acf2b..92240a85e 100644 --- a/Apps/RuuviStation/Sources/Extensions/Structs/ExportHeadersProvider.swift +++ b/Apps/RuuviStation/Sources/Extensions/Structs/ExportHeadersProvider.swift @@ -9,11 +9,11 @@ struct ExportHeadersProvider: RuuviServiceExportHeaders { let humidityFormat = RuuviLocalization.ExportService.humidity return [ RuuviLocalization.ExportService.date, - tempFormat(units.temperatureUnit.symbol), + tempFormat(units.temperatureUnit.symbol, Locale.current), units.humidityUnit == .dew - ? humidityFormat(units.temperatureUnit.symbol) - : humidityFormat(units.humidityUnit.symbol), - pressureFormat(units.pressureUnit.symbol), + ? humidityFormat(units.temperatureUnit.symbol, Locale.current) + : humidityFormat(units.humidityUnit.symbol, Locale.current), + pressureFormat(units.pressureUnit.symbol, Locale.current), "RSSI" + " (\(RuuviLocalization.dBm))", RuuviLocalization.ExportService.accelerationX + " (\(RuuviLocalization.g))", RuuviLocalization.ExportService.accelerationY + " (\(RuuviLocalization.g))", diff --git a/Apps/RuuviStation/Sources/Station.entitlements b/Apps/RuuviStation/Sources/Station.entitlements index 8e9efd5cd..6b8b1a52d 100644 --- a/Apps/RuuviStation/Sources/Station.entitlements +++ b/Apps/RuuviStation/Sources/Station.entitlements @@ -9,12 +9,12 @@ applinks:network.ruuvi.com applinks:station.ruuvi.com + com.apple.developer.nfc.readersession.formats + + TAG + com.apple.security.app-sandbox - com.apple.developer.nfc.readersession.formats - - TAG - com.apple.security.application-groups group.com.ruuvi.station.pnservice diff --git a/Common/RuuviLocalization/Templates/strings-swift5.stencil b/Common/RuuviLocalization/Templates/strings-swift5.stencil index 14f387f37..0a54436cd 100644 --- a/Common/RuuviLocalization/Templates/strings-swift5.stencil +++ b/Common/RuuviLocalization/Templates/strings-swift5.stencil @@ -40,8 +40,8 @@ import Foundation {% endif %} {% set translation string.translation|replace:'"','\"'|replace:' ','\t' %} {% if string.types %} - {{accessModifier}} static func {{string.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}({% call parametersBlock string.types %}) -> String { - {{enumName}}.tr("{{table}}", "{{string.key}}", {%+ call argumentsBlock string.types %}, fallback: "{{translation}}") + {{accessModifier}} static func {{string.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}({% call parametersBlock string.types %}, _ locale: Locale = Locale.current) -> String { + {{enumName}}.tr("{{table}}", "{{string.key}}", {%+ call argumentsBlock string.types %}, fallback: "{{translation}}", locale: locale) } {% elif param.lookupFunction %} {{accessModifier}} static var {{string.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}: String { return {{enumName}}.tr("{{table}}", "{{string.key}}", fallback: "{{translation}}") } @@ -75,13 +75,22 @@ import Foundation // MARK: - Implementation Details extension {{enumName}} { - private static func tr(_ table: String, _ key: String, _ args: CVarArg..., fallback value: String) -> String { + private static func tr(_ table: String, _ key: String, _ args: CVarArg..., fallback value: String, locale: Locale = Locale.current) -> String { {% if param.lookupFunction %} let format = {{ param.lookupFunction }}(key, table, value) {% else %} - let format = {{param.bundle|default:"BundleToken.bundle"}}.localizedString(forKey: key, value: value, table: table) + let format: String + if let bundle = BundleToken.bundle.path( + forResource: locale.identifier, + ofType: "lproj" + ), let languageBundle = Bundle(path: bundle) { + format = languageBundle.localizedString(forKey: key, value: value, table: table) + } else { + format = {{param.bundle|default:"BundleToken.bundle"}}.localizedString(forKey: key, value: value, table: table) + } + {% endif %} - return String(format: format, locale: Locale.current, arguments: args) + return String(format: format, locale: locale, arguments: args) } } {% if not param.bundle and not param.lookupFunction %} diff --git a/Packages/RuuviLocal/Sources/RuuviLocalUserDefaults/RuuviLocalSettingsUserDefaults.swift b/Packages/RuuviLocal/Sources/RuuviLocalUserDefaults/RuuviLocalSettingsUserDefaults.swift index 351d5df97..e66d292d4 100644 --- a/Packages/RuuviLocal/Sources/RuuviLocalUserDefaults/RuuviLocalSettingsUserDefaults.swift +++ b/Packages/RuuviLocal/Sources/RuuviLocalUserDefaults/RuuviLocalSettingsUserDefaults.swift @@ -42,10 +42,8 @@ final class RuuviLocalSettingsUserDefaults: RuuviLocalSettings { private let notificationServiceAppGroup = UserDefaults(suiteName: "group.com.ruuvi.station.pnservice") var language: Language { get { - if let savedCode = UserDefaults.standard.string(forKey: languageUDKey) { + if let savedCode = notificationServiceAppGroup?.string(forKey: languageUDKey) { Language(rawValue: savedCode) ?? .english - } else if let regionCode = Locale.current.languageCode { - Language(rawValue: regionCode) ?? .english } else { .english }