From ad2ecaefc078cc7a09d09609bd972b281ccdec3c Mon Sep 17 00:00:00 2001 From: jahorton Date: Thu, 30 Jul 2020 10:29:43 +0700 Subject: [PATCH] feat(ios/engine): completes settings views' i18n --- .../Classes/KeyboardMenuView.swift | 3 +- .../Classes/KeymanWebViewController.swift | 2 +- .../KeyboardSwitcherViewController.swift | 2 +- .../LanguageLMDetailViewController.swift | 11 ++-- .../LexicalModelInfoViewController.swift | 13 ++--- .../LexicalModelPickerViewController.swift | 17 +++--- .../ResourceInfoViewController.swift | 15 +++--- .../InstalledLanguagesViewController.swift | 8 ++- .../LanguageSettingsViewController.swift | 34 +++++------- .../KeymanEngine/en.lproj/Localizable.strings | Bin 7920 -> 13512 bytes .../en.lproj/Localizable.stringsdict | 50 ++++++++++++++++++ 11 files changed, 103 insertions(+), 52 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/KeyboardMenuView.swift b/ios/engine/KMEI/KeymanEngine/Classes/KeyboardMenuView.swift index f5089a472b..94238dba5b 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/KeyboardMenuView.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/KeyboardMenuView.swift @@ -45,7 +45,8 @@ class KeyboardMenuView: UIView, UITableViewDelegate, UITableViewDataSource, UIGe titleCloseButton = closeButtonTitle } else { let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String ?? "Keyman" - titleCloseButton = "Close \(appName)" + let format = NSLocalizedString("keyboard-menu-exit", bundle: engineBundle, comment: "") + titleCloseButton = String.localizedStringWithFormat(format, appName) } if let keyboardList = keyboardList { diff --git a/ios/engine/KMEI/KeymanEngine/Classes/KeymanWebViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/KeymanWebViewController.swift index 070fc6138a..55326c6ab5 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/KeymanWebViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/KeymanWebViewController.swift @@ -10,7 +10,7 @@ import UIKit import WebKit import AudioToolbox -private let keyboardChangeHelpText = "Tap here to change keyboard" +private let keyboardChangeHelpText = NSLocalizedString("keyboard-help-change", bundle: engineBundle, comment: "") private let subKeyColor = Colors.popupKey private let subKeyColorHighlighted = Colors.popupKeyHighlighted diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/KeyboardSwitcherViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/KeyboardSwitcherViewController.swift index 1f5526fe71..5dd0d103cf 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/KeyboardSwitcherViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/KeyboardSwitcherViewController.swift @@ -19,7 +19,7 @@ class KeyboardSwitcherViewController: UITableViewController, UIAlertViewDelegate override func viewDidLoad() { super.viewDidLoad() - title = "Keyboards" + title = NSLocalizedString("menu-picker-title", bundle: engineBundle, comment: "") self.accessoryType = .none diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageLMDetailViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageLMDetailViewController.swift index c5cea614e1..6c03696a45 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageLMDetailViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageLMDetailViewController.swift @@ -125,14 +125,15 @@ class LanguageLMDetailViewController: UITableViewController, UIAlertViewDelegate } else { isUpdate = true } - - let alertController = UIAlertController(title: "\(language.name): \(lexicalModel.name)", - message: "Would you like to download this dictionary?", + + let format = NSLocalizedString("menu-lexical-model-install-title", bundle: engineBundle, comment: "") + let alertController = UIAlertController(title: String.localizedStringWithFormat(format, language.name, lexicalModel.name), + message: NSLocalizedString("menu-lexical-model-install-message", bundle: engineBundle, comment: ""), preferredStyle: UIAlertController.Style.alert) - alertController.addAction(UIAlertAction(title: "Cancel", + alertController.addAction(UIAlertAction(title: NSLocalizedString("command-cancel", bundle: engineBundle, comment: ""), style: UIAlertAction.Style.cancel, handler: nil)) - alertController.addAction(UIAlertAction(title: "Download", + alertController.addAction(UIAlertAction(title: NSLocalizedString("command-install", bundle: engineBundle, comment: ""), style: UIAlertAction.Style.default, handler: {_ in self.downloadHandler(lexicalModelIndex)} )) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelInfoViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelInfoViewController.swift index 9b542ee982..86c5774c74 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelInfoViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelInfoViewController.swift @@ -24,18 +24,18 @@ class LexicalModelInfoViewController: UITableViewController, UIAlertViewDelegate infoArray = [[String: String]]() infoArray.append([ - "title": "Dictionary version", + "title": NSLocalizedString("info-label-version-lexical-model", bundle: engineBundle, comment: ""), "subtitle": lexicalModelVersion ]) if !isCustomLexicalModel { infoArray.append([ - "title": "Help link", + "title": NSLocalizedString("info-command-help", bundle: engineBundle, comment: ""), "subtitle": "" ]) } infoArray.append([ - "title": "Uninstall dictionary", + "title": NSLocalizedString("command-uninstall-lexical-model", bundle: engineBundle, comment: ""), "subtitle": "" ]) } @@ -112,12 +112,13 @@ class LexicalModelInfoViewController: UITableViewController, UIAlertViewDelegate } private func showDeleteLexicalModel() { - let alertController = UIAlertController(title: title ?? "", message: "Would you like to delete this dictionary?", + let helpText = NSLocalizedString("command-uninstall-lexical-model-confirm", bundle: engineBundle, comment: "") + let alertController = UIAlertController(title: title ?? "", message: helpText, preferredStyle: UIAlertController.Style.alert) - alertController.addAction(UIAlertAction(title: "Cancel", + alertController.addAction(UIAlertAction(title: NSLocalizedString("command-cancel", bundle: engineBundle, comment: ""), style: UIAlertAction.Style.cancel, handler: nil)) - alertController.addAction(UIAlertAction(title: "Delete", + alertController.addAction(UIAlertAction(title: NSLocalizedString("command-uninstall", bundle: engineBundle, comment: ""), style: UIAlertAction.Style.default, handler: deleteHandler)) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelPickerViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelPickerViewController.swift index 66841a5b5d..a57fa75609 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelPickerViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelPickerViewController.swift @@ -33,8 +33,9 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega override func viewDidLoad() { super.viewDidLoad() - - title = "\(language.name) Dictionaries" + + let format = NSLocalizedString("menu-lexical-model-title", bundle: engineBundle, comment: "") + title = String.localizedStringWithFormat(format, language.name) if Manager.shared.canAddNewLexicalModels { let addButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(self.addClicked)) @@ -203,12 +204,12 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega item.isEnabled = true } - let title: String = "Dictionary Download Error" + let title: String = NSLocalizedString("notification-download-failure-lexical-model", bundle: engineBundle, comment: "") navigationController?.setToolbarHidden(true, animated: true) let alertController = UIAlertController(title: title, message: notification.error.localizedDescription, preferredStyle: UIAlertController.Style.alert) - alertController.addAction(UIAlertAction(title: "OK", + alertController.addAction(UIAlertAction(title: NSLocalizedString("command-ok", bundle: engineBundle, comment: ""), style: UIAlertAction.Style.cancel, handler: { _ in self.navigationController?.popToRootViewController(animated: true) @@ -276,7 +277,7 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega private func setIsDoneButtonEnabled(_ value: Bool) { _isDoneButtonEnabled = value if _isDoneButtonEnabled { - let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, + let doneButton = UIBarButtonItem(title: NSLocalizedString("command-done", bundle: engineBundle, comment: ""), style: .plain, target: self, action: #selector(self.doneClicked)) navigationItem.leftBarButtonItem = doneButton } else { @@ -325,13 +326,13 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega } func noModelsAvailable(cause: String = "nil") { - let msg = "No dictionaries available" - let logMsg = "No lexical models available for language \(language.id) (\(cause))" + let msg = NSLocalizedString("menu-lexical-model-none-message", bundle: engineBundle, comment: "") + let logMsg = "No lexical models available for language \(language.id): (\(cause))" log.info(logMsg) let alertController = UIAlertController(title: title, message: msg, preferredStyle: UIAlertController.Style.alert) - alertController.addAction(UIAlertAction(title: "OK", + alertController.addAction(UIAlertAction(title: NSLocalizedString("command-ok", bundle: engineBundle, comment: ""), style: UIAlertAction.Style.default, handler: { _ in self.navigationController?.popViewController(animated: true) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/ResourceInfoViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/ResourceInfoViewController.swift index fe30761380..a36cd52e4a 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/ResourceInfoViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/ResourceInfoViewController.swift @@ -48,22 +48,20 @@ class ResourceInfoViewController: UIViewController, UIAlertViewDelegate, UITable override func viewDidLoad() { super.viewDidLoad() - let bundle = Bundle(for: Manager.self) - infoArray = [[String: String]]() infoArray.append([ - "title": NSLocalizedString("Keyboard version", bundle: bundle, comment: "As seen on the keyboard's information page"), + "title": NSLocalizedString("info-label-version-keyboard", bundle: engineBundle, comment: ""), "subtitle": resource.version ]) if !isCustomKeyboard { infoArray.append([ - "title": NSLocalizedString("Help link", bundle: bundle, comment: "Links to the help page of the keyboard"), + "title": NSLocalizedString("info-command-help", bundle: engineBundle, comment: ""), "subtitle": "" ]) } infoArray.append([ - "title": NSLocalizedString("Uninstall keyboard", bundle: bundle, comment: "Removes the keyboard from the application"), + "title": NSLocalizedString("command-uninstall-keyboard", bundle: engineBundle, comment: ""), "subtitle": "" ]) @@ -197,12 +195,13 @@ class ResourceInfoViewController: UIViewController, UIAlertViewDelegate, UITable } private func showDeleteKeyboard() { - let alertController = UIAlertController(title: title ?? "", message: "Would you like to delete this keyboard?", + let uninstallHelp = NSLocalizedString("command-uninstall-keyboard-confirm", bundle: engineBundle, comment: "") + let alertController = UIAlertController(title: title ?? "", message: uninstallHelp, preferredStyle: UIAlertController.Style.alert) - alertController.addAction(UIAlertAction(title: "Cancel", + alertController.addAction(UIAlertAction(title: NSLocalizedString("command-cancel", bundle: engineBundle, comment: ""), style: UIAlertAction.Style.cancel, handler: nil)) - alertController.addAction(UIAlertAction(title: "Delete", + alertController.addAction(UIAlertAction(title: NSLocalizedString("command-uninstall", bundle: engineBundle, comment: ""), style: UIAlertAction.Style.default, handler: deleteHandler)) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift index 5e5f3c4b26..dff455c2c1 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift @@ -360,9 +360,13 @@ public class InstalledLanguagesViewController: UITableViewController, UIAlertVie private func batchUpdateCompleted(results: BatchUpdateCompletedNotification) { if let toolbar = navigationController?.toolbar as? ResourceDownloadStatusToolbar { if results.failures.count == 0 { - toolbar.displayStatus("\(results.successes.count) update(s) successfully downloaded!", withIndicator: false, duration: 3.0) + let formatString = NSLocalizedString("notification-update-success", bundle: engineBundle, comment: "") + toolbar.displayStatus(String.localizedStringWithFormat(formatString, results.successes.count), withIndicator: false, duration: 3.0) } else { - toolbar.displayStatus("Updates complete: \(results.successes.count) successful, \(results.failures.count) failed", withIndicator: false, duration: 3.0) + // Needs to be a short string on iPhones. + // Possible TODO: condition on "is an iPad", provide more info on iPads? + let formatString = NSLocalizedString("notification-update-failed", bundle: engineBundle, comment: "") + toolbar.displayStatus(String.localizedStringWithFormat(formatString, results.failures.count), withIndicator: false, duration: 3.0) } } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift index 90c3988c96..fa4eceaa45 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift @@ -35,7 +35,8 @@ class LanguageSettingsViewController: UITableViewController { override func viewDidLoad() { super.viewDidLoad() - title = "\(language.name) Settings" + let titleFormat = NSLocalizedString("menu-langsettings-title", bundle: engineBundle, comment: "") + title = String.localizedStringWithFormat(titleFormat, language.name) log.info("viewDidLoad: LanguageSettingsViewController title: \(title ?? "")") if Manager.shared.canAddNewKeyboards { @@ -191,9 +192,11 @@ class LanguageSettingsViewController: UITableViewController { var title: String switch (section) { case 0: - title = "Keyboards" + // We do NOT use a general-use "keyboards" entry because different languages have different + // pluralization rules, and it's a major assumption to assume which is preferred for general use. + title = NSLocalizedString("menu-langsettings-section-keyboards", bundle: engineBundle, comment: "") case 1: - title = "Language settings" + title = NSLocalizedString("menu-langsettings-section-settings", bundle: engineBundle, comment: "") default: title = "unknown header" } @@ -233,30 +236,21 @@ class LanguageSettingsViewController: UITableViewController { cell.accessoryType = .none switch indexPath.row { case 0: - cell.textLabel?.text = "Enable predictions" + cell.textLabel?.text = NSLocalizedString("menu-langsettings-toggle-predict", bundle: engineBundle, comment: "") case 1: doCorrectionsLabel = cell.textLabel - cell.textLabel?.text = "Enable corrections" + cell.textLabel?.text = NSLocalizedString("menu-langsettings-toggle-correct", bundle: engineBundle, comment: "") cell.textLabel?.isEnabled = !(doCorrectionsSwitch?.isHidden ?? false) case 2: - cell.textLabel?.text = "Dictionaries" + cell.textLabel?.text = NSLocalizedString("menu-langsettings-label-lexical-models", bundle: engineBundle, comment: "") cell.accessoryType = .disclosureIndicator - if let modelCt = language.lexicalModels?.count { - switch modelCt { - case 0: - cell.detailTextLabel?.text = "no dictionaries installed" - case 1: - cell.detailTextLabel?.text = "\(language.lexicalModels![0].name)" - default: - cell.detailTextLabel?.text = "\(modelCt) dictionaries installed" - } + let modelCt = language.lexicalModels?.count ?? 0 + if modelCt != 1 { + let formatString = NSLocalizedString("menu-langsettings-lexical-model-count", bundle: engineBundle, comment: "") + cell.detailTextLabel?.text = String.localizedStringWithFormat(formatString, modelCt) } else { - cell.detailTextLabel?.text = "no dictionaries installed" + cell.detailTextLabel?.text = "\(language.lexicalModels![0].name)" } - case 3: // future - cell.textLabel?.text = "Manage dictionary" - cell.accessoryType = .disclosureIndicator - cell.isUserInteractionEnabled = false default: cell.textLabel?.text = "error" diff --git a/ios/engine/KMEI/KeymanEngine/en.lproj/Localizable.strings b/ios/engine/KMEI/KeymanEngine/en.lproj/Localizable.strings index 13680b668726c765374b9bd9e22ff0fc4e41d651..77948665801d87ab0deea306d72f138e6af90d97 100644 GIT binary patch literal 13512 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 diff --git a/ios/engine/KMEI/KeymanEngine/en.lproj/Localizable.stringsdict b/ios/engine/KMEI/KeymanEngine/en.lproj/Localizable.stringsdict index 6fcaab931c..17aa9c21d2 100644 --- a/ios/engine/KMEI/KeymanEngine/en.lproj/Localizable.stringsdict +++ b/ios/engine/KMEI/KeymanEngine/en.lproj/Localizable.stringsdict @@ -2,6 +2,56 @@ + menu-langsettings-lexical-model-count + + NSStringLocalizedFormatKey + %#@count@ + count + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + u + zero + No dictionaries installed + one + %u dictionary installed + other + %u dictionaries installed + + + notification-update-failed + + NSStringLocalizedFormatKey + %#@count@ + count + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + u + one + %u update failed + other + %u updates failed + + + notification-update-success + + NSStringLocalizedFormatKey + %#@count@ + count + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + u + one + %u update successful + other + %u updates successful + + settings-keyboards-installed-count NSStringLocalizedFormatKey