diff --git a/Clocker/AppDelegate.swift b/Clocker/AppDelegate.swift index 3dfe9cb..b627833 100644 --- a/Clocker/AppDelegate.swift +++ b/Clocker/AppDelegate.swift @@ -57,6 +57,16 @@ open class AppDelegate: NSObject, NSApplicationDelegate { Fabric.with([Crashlytics.self]) checkIfRunFromApplicationsFolder() #endif + + logCurrentLanguagePreferences() + } + + // Help us priortize our localization efforts + private func logCurrentLanguagePreferences() { + let annotations = [ + "Language": Locale.preferredLanguages.first ?? "en-US", + ] + Logger.log(object: annotations, for: "Locale") } public func applicationDockMenu(_: NSApplication) -> NSMenu? { diff --git a/Clocker/Clocker.xcodeproj/project.pbxproj b/Clocker/Clocker.xcodeproj/project.pbxproj index a39b3d2..ae75cad 100755 --- a/Clocker/Clocker.xcodeproj/project.pbxproj +++ b/Clocker/Clocker.xcodeproj/project.pbxproj @@ -105,6 +105,7 @@ 9A7547E51F184E3F004705EF /* ClockerHelper.app in Login Item Helper */ = {isa = PBXBuildFile; fileRef = 9A7547D01F184DC3004705EF /* ClockerHelper.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 9A7CDC1B22BEC2170035902D /* StartupManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A7CDC1A22BEC2170035902D /* StartupManager.swift */; }; 9A8605AE1BEC148400A810A4 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A8605AD1BEC148400A810A4 /* main.m */; }; + 9A8B256A232EFAD300204CAD /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 9A13BAEC1CA88A76007C6CBE /* Localizable.strings */; }; 9A9E87621C1FEDB500A7A2DF /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A9E87611C1FEDB500A7A2DF /* CFNetwork.framework */; }; 9A9E876A1C1FEDDB00A7A2DF /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A9E87691C1FEDDB00A7A2DF /* SystemConfiguration.framework */; }; 9AB6F1562259CF3900A44663 /* CalendarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AB6F1552259CF3900A44663 /* CalendarViewController.swift */; }; @@ -1038,6 +1039,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9A8B256A232EFAD300204CAD /* Localizable.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Clocker/Clocker/hi.lproj/Localizable.strings b/Clocker/Clocker/hi.lproj/Localizable.strings index a1c72f4..0bf4f18 100644 --- a/Clocker/Clocker/hi.lproj/Localizable.strings +++ b/Clocker/Clocker/hi.lproj/Localizable.strings @@ -6,6 +6,7 @@ */ +"CFBundleDisplayName" = "विश्व का समय"; "Thank you for helping make Clocker even better!" = "Thank you for helping make Clocker even better!"; "iRateMessageTitle" = "Rate %@"; "iRateAppMessage" = "If you enjoy using %@, would you mind taking a moment to rate it? It won’t take more than a minute. Thanks for your support!"; @@ -46,8 +47,8 @@ "Sort by Name" = "नाम द्वारा सॉर्ट करें"; "Sort by Label" = "लेबल द्वारा सॉर्ट करें"; "Search Field Placeholder" = "Enter a city, state or country name"; -"No Timezone Selected Error Message" = "Please select a timezone!"; -"Max Timezones Selected Error Message" = "Maximum 100 timezones allowed!"; -"Max Search Characters Error Message" = "Only 50 characters allowed!"; +"No Timezone Selected" = "Please select a timezone!"; +"Max Timezones Selected" = "Maximum 100 timezones allowed!"; +"Max Search Characters" = "Only 50 characters allowed!"; "Add Button Title" = "ऐड "; "Close Button Title" = "बंद करे"; diff --git a/Clocker/ClockerUITests/AboutUsTests.swift b/Clocker/ClockerUITests/AboutUsTests.swift index d0ba955..6c18478 100644 --- a/Clocker/ClockerUITests/AboutUsTests.swift +++ b/Clocker/ClockerUITests/AboutUsTests.swift @@ -32,7 +32,8 @@ class AboutUsTests: XCTestCase { tapAboutTab() - let expectedVersion = "Clocker 1.6.15 (70)" + let appDisplayName = "CFBundleDisplayName".localizedString() + let expectedVersion = "\(appDisplayName) 1.6.15 (70)" guard let presentVersion = app.windows["Clocker"].staticTexts["ClockerVersion"].value as? String else { XCTFail("Present version not present") return diff --git a/Clocker/ClockerUITests/FloatingWindowTests.swift b/Clocker/ClockerUITests/FloatingWindowTests.swift index d53324e..1309da6 100644 --- a/Clocker/ClockerUITests/FloatingWindowTests.swift +++ b/Clocker/ClockerUITests/FloatingWindowTests.swift @@ -2,6 +2,16 @@ import XCTest +extension String { + func localizedString() -> String { + let bundle = Bundle(for: AboutUsTests.self) + let deviceLanguage = Locale.preferredLanguages.first + let localizationBundle = Bundle(path: bundle.path(forResource: deviceLanguage, + ofType: "lproj")!) + return NSLocalizedString(self, bundle: localizationBundle!, comment: "") + } +} + class FloatingWindowTests: XCTestCase { var app: XCUIApplication! @@ -77,8 +87,11 @@ class FloatingWindowTests: XCTestCase { remindersCheckbox.click() addUIInterruptionMonitor(withDescription: "Reminders Access") { (alert) -> Bool in + print("Interruption Handler called") + print(alert) let alertButton = alert.buttons["OK"] if alertButton.exists { + print("Okay button found") alertButton.tap() return true } @@ -118,7 +131,7 @@ class FloatingWindowTests: XCTestCase { } app.buttons["FloatingPreferences"].click() - app.windows["Clocker"].toolbars.buttons["Preferences"].click() + app.windows["Clocker"].toolbars.buttons["Preferences Tab".localizedString()].click() let menubarDisplayQuery = app.tables.checkBoxes.matching(NSPredicate(format: "value == 1", "")) let menubarDisplayQueryCount = menubarDisplayQuery.count diff --git a/Clocker/ClockerUITests/NetworkDisconnectionTests.swift b/Clocker/ClockerUITests/NetworkDisconnectionTests.swift index 8276c62..cad6153 100644 --- a/Clocker/ClockerUITests/NetworkDisconnectionTests.swift +++ b/Clocker/ClockerUITests/NetworkDisconnectionTests.swift @@ -36,7 +36,7 @@ class NetworkDisconnectionTests: XCTestCase { sleep(1) XCTAssertTrue(app.sheets.staticTexts["ErrorPlaceholder"].exists) - app.sheets.buttons["Close"].click() + app.sheets.buttons["Close Button Title".localizedString()].click() } func testFetchingATimezone() { @@ -66,6 +66,6 @@ class NetworkDisconnectionTests: XCTestCase { sleep(1) XCTAssertTrue(app.sheets.staticTexts["ErrorPlaceholder"].exists) - app.sheets.buttons["Close"].click() + app.sheets.buttons["Close Button Title".localizedString()].click() } } diff --git a/Clocker/ClockerUITests/PreferencesTest.swift b/Clocker/ClockerUITests/PreferencesTest.swift index e7616ea..bc3e71d 100644 --- a/Clocker/ClockerUITests/PreferencesTest.swift +++ b/Clocker/ClockerUITests/PreferencesTest.swift @@ -63,9 +63,9 @@ class PreferencesTest: XCTestCase { addAPlace(place: "San Francisco", to: app) addAPlace(place: "Florida", to: app, shouldSleep: false) // Last elements don't need to sleep - XCTAssertTrue(app.windows["Clocker"].checkBoxes["Sort by Time Difference"].exists) + XCTAssertTrue(app.windows["Clocker"].checkBoxes["Sort by Time Difference".localizedString()].exists) - app.windows["Clocker"].checkBoxes["Sort by Time Difference"].click() + app.windows["Clocker"].checkBoxes["Sort by Time Difference".localizedString()].click() var actualLabels: [String] = [] let newFormattedAddressQuery = app.windows["Clocker"].textFields @@ -78,7 +78,7 @@ class PreferencesTest: XCTestCase { XCTAssertEqual(actualLabels, ["New Zealand", "Florida", "San Francisco"]) - app.windows["Clocker"].checkBoxes["Sort by Time Difference"].click() + app.windows["Clocker"].checkBoxes["Sort by Time Difference".localizedString()].click() var actualReversedLabels: [String] = [] let newReversedQuery = app.windows["Clocker"].textFields @@ -99,9 +99,9 @@ class PreferencesTest: XCTestCase { app.tapMenubarIcon() app.tables["mainTableView"].typeKey(",", modifierFlags: .command) - XCTAssertTrue(app.windows["Clocker"].checkBoxes["Sort by Time Difference"].exists) - XCTAssertTrue(app.windows["Clocker"].checkBoxes["Sort by Label"].exists) - XCTAssertTrue(app.windows["Clocker"].checkBoxes["Sort by Name"].exists) + XCTAssertTrue(app.windows["Clocker"].checkBoxes["Sort by Time Difference".localizedString()].exists) + XCTAssertTrue(app.windows["Clocker"].checkBoxes["Sort by Label".localizedString()].exists) + XCTAssertTrue(app.windows["Clocker"].checkBoxes["Sort by Name".localizedString()].exists) var formattedAddress: [String] = [] @@ -115,8 +115,8 @@ class PreferencesTest: XCTestCase { formattedAddress.sort() - if let value = app.windows["Clocker"].checkBoxes["Sort by Name"].value as? Int, value == 0 { - app.windows["Clocker"].checkBoxes["Sort by Name"].click() + if let value = app.windows["Clocker"].checkBoxes["Sort by Name".localizedString()].value as? Int, value == 0 { + app.windows["Clocker"].checkBoxes["Sort by Name".localizedString()].click() } var newformattedAddress: [String] = [] @@ -132,9 +132,9 @@ class PreferencesTest: XCTestCase { app.windows["Clocker"].checkBoxes["SortButton"].click() - XCTAssertFalse(app.windows["Clocker"].checkBoxes["Sort by Time Difference"].exists) - XCTAssertFalse(app.windows["Clocker"].checkBoxes["Sort by Label"].exists) - XCTAssertFalse(app.windows["Clocker"].checkBoxes["Sort by Name"].exists) + XCTAssertFalse(app.windows["Clocker"].checkBoxes["Sort by Time Difference".localizedString()].exists) + XCTAssertFalse(app.windows["Clocker"].checkBoxes["Sort by Label".localizedString()].exists) + XCTAssertFalse(app.windows["Clocker"].checkBoxes["Sort by Name".localizedString()].exists) } func testSortingCitiesByCustomLabel() { @@ -147,7 +147,7 @@ class PreferencesTest: XCTestCase { addAPlace(place: "Asia/Kolkata", to: app) addAPlace(place: "Anywhere on Earth", to: app, shouldSleep: false) - XCTAssertTrue(app.windows["Clocker"].checkBoxes["Sort by Label"].exists) + XCTAssertTrue(app.windows["Clocker"].checkBoxes["Sort by Label".localizedString()].exists) var expectedLabels: [String] = [] @@ -161,8 +161,8 @@ class PreferencesTest: XCTestCase { expectedLabels.sort() - if let value = app.windows["Clocker"].checkBoxes["Sort by Label"].value as? Int, value == 0 { - app.windows["Clocker"].checkBoxes["Sort by Label"].click() + if let value = app.windows["Clocker"].checkBoxes["Sort by Label".localizedString()].value as? Int, value == 0 { + app.windows["Clocker"].checkBoxes["Sort by Label".localizedString()].click() } var actualLabels: [String] = [] @@ -191,7 +191,7 @@ class PreferencesTest: XCTestCase { addAPlace(place: "Asia/Kolkata", to: app) addAPlace(place: "Anywhere on Earth", to: app, shouldSleep: false) - XCTAssertTrue(app.windows["Clocker"].checkBoxes["Sort by Label"].exists) + XCTAssertTrue(app.windows["Clocker"].checkBoxes["Sort by Label".localizedString()].exists) var expectedLabels: [String] = [] @@ -205,8 +205,8 @@ class PreferencesTest: XCTestCase { expectedLabels.sort() - if let value = app.windows["Clocker"].checkBoxes["Sort by Label"].value as? Int, value == 0 { - app.windows["Clocker"].checkBoxes["Sort by Label"].click() + if let value = app.windows["Clocker"].checkBoxes["Sort by Label".localizedString()].value as? Int, value == 0 { + app.windows["Clocker"].checkBoxes["Sort by Label".localizedString()].click() } var actualLabels: [String] = [] @@ -238,7 +238,7 @@ class PreferencesTest: XCTestCase { sleep(2) - let maxCharacterCountPredicate = NSPredicate(format: "value like %@", "Only 50 characters allowed!") + let maxCharacterCountPredicate = NSPredicate(format: "value like %@", "Max Search Characters".localizedString()) let currentSheets = app.sheets.firstMatch.staticTexts let maxCharacterQuery = currentSheets.matching(maxCharacterCountPredicate) @@ -423,10 +423,24 @@ extension XCTestCase { } func deleteAPlace(place: String, for app: XCUIApplication, shouldSleep: Bool = true) { + let userPrefferedLanguage = Locale.preferredLanguages.first ?? "en-US" + if !userPrefferedLanguage.lowercased().contains("en") { + // We're testing in a different user language. We can't do string matching here. + // Delete the last row + let rowCount = app.tables["TimezoneTableView"].tableRows.count + let rowToDelete = app.tables["TimezoneTableView"].tableRows.element(boundBy: rowCount - 1) + deleteAtRow(rowToDelete, for: app, shouldSleep: shouldSleep) + return + } + let matchPredicate = NSPredicate(format: "value contains %@", place) let row = app.tables["TimezoneTableView"].textFields.matching(matchPredicate).firstMatch - row.click() - row.typeKey(XCUIKeyboardKey.delete, modifierFlags: XCUIElement.KeyModifierFlags()) + deleteAtRow(row, for: app, shouldSleep: shouldSleep) + } + + private func deleteAtRow(_ rowToDelete: XCUIElement, for _: XCUIApplication, shouldSleep: Bool) { + rowToDelete.click() + rowToDelete.typeKey(XCUIKeyboardKey.delete, modifierFlags: XCUIElement.KeyModifierFlags()) if shouldSleep { sleep(2) }