diff --git a/Authenticator/UI/AccountDetailsView.swift b/Authenticator/UI/AccountDetailsView.swift index 938b4110..72d38c35 100644 --- a/Authenticator/UI/AccountDetailsView.swift +++ b/Authenticator/UI/AccountDetailsView.swift @@ -110,7 +110,7 @@ struct AccountDetailsView: View { } } .accessibilityElement() - .accessibilityHint("Dismiss details view") + .accessibilityHint(String(localized: "Dismiss details view", comment: "Accessibility hint")) .accessibilityAction { self.data = nil } @@ -148,7 +148,7 @@ struct AccountDetailsView: View { account.calculate() } .accessibilityAddTraits(.isButton) - .accessibilityLabel(account.state == .expired ? "Code expired" : account.formattedCode ?? "Code not calculated") + .accessibilityLabel(account.state == .expired ? String(localized: "Code expired", comment: "Accessibility label") : account.formattedCode ?? String(localized: "Code not calculated", comment: "Accessibility label")) ZStack { if let otp = data.account.formattedCode { @@ -212,20 +212,20 @@ struct AccountDetailsView: View { } DetachedMenu(menuActions: [ - DetachedMenuAction(style: .default, isEnabled: account.enableRefresh, title: "Calculate", systemImage: "arrow.clockwise", action: { + DetachedMenuAction(style: .default, isEnabled: account.enableRefresh, title: String(localized: "Calculate", comment: "Menu"), systemImage: "arrow.clockwise", action: { self.account.calculate() }), - DetachedMenuAction(style: .default, isEnabled: account.state != .expired && account.otp != nil, title: "Copy", systemImage: "square.and.arrow.up", action: { + DetachedMenuAction(style: .default, isEnabled: account.state != .expired && account.otp != nil, title: String(localized: "Copy", comment: "Menu"), systemImage: "square.and.arrow.up", action: { guard let otp = account.otp?.code else { return } toastPresenter.copyToClipboard(otp) }), - DetachedMenuAction(style: .default, isEnabled: true, title: account.isPinned ? "Unpin" : "Pin", systemImage: "pin", action: { + DetachedMenuAction(style: .default, isEnabled: true, title: account.isPinned ? String(localized: "Unpin", comment: "Menu") : String(localized: "Pin", comment: "Menu"), systemImage: "pin", action: { account.isPinned.toggle() }), - account.keyVersion >= YKFVersion(string: "5.3.0") ? DetachedMenuAction(style: .default, isEnabled: true, title: "Rename", systemImage: "square.and.pencil", action: { + account.keyVersion >= YKFVersion(string: "5.3.0") ? DetachedMenuAction(style: .default, isEnabled: true, title: String(localized: "Rename", comment: "Menu"), systemImage: "square.and.pencil", action: { showEditing.toggle() }) : nil, - DetachedMenuAction(style: .destructive, isEnabled: true, title: "Delete", systemImage: "trash", action: { + DetachedMenuAction(style: .destructive, isEnabled: true, title: String(localized: "Delete", comment: "Menu"), systemImage: "trash", action: { showDeleteConfirmation = true }) ].compactMap { $0 } ) diff --git a/Authenticator/UI/AccountRowView.swift b/Authenticator/UI/AccountRowView.swift index fafa3b59..62861c8e 100644 --- a/Authenticator/UI/AccountRowView.swift +++ b/Authenticator/UI/AccountRowView.swift @@ -126,7 +126,7 @@ struct AccountRowView: View { .stroke(pillColor, lineWidth: 1) } .accessibilityElement() - .accessibilityLabel(account.state == .expired ? "Code expired" : account.formattedCode ?? "Code not calculated") + .accessibilityLabel(account.state == .expired ? String(localized: "Code expired", comment: "Accessibility label") : account.formattedCode ?? String(localized: "Code not calculated", comment: "Accessibility label")) .opacity(pillOpacity) .scaleEffect(pillScaling) } diff --git a/Authenticator/UI/ErrorAlertView.swift b/Authenticator/UI/ErrorAlertView.swift index 7448bca3..a81e5b63 100644 --- a/Authenticator/UI/ErrorAlertView.swift +++ b/Authenticator/UI/ErrorAlertView.swift @@ -17,7 +17,7 @@ import SwiftUI extension View { - func errorAlert(error: Binding, buttonTitle: String = "OK", handler: (() -> Void)? = nil) -> some View { + func errorAlert(error: Binding, buttonTitle: String = String(localized: "OK", comment:"OK button in error alert."), handler: (() -> Void)? = nil) -> some View { let localizedAlertError = LocalizedAlertError(error: error.wrappedValue) return alert(isPresented: .constant(localizedAlertError != nil), error: localizedAlertError) { _ in Button(buttonTitle) { diff --git a/Authenticator/UI/ListStatusView.swift b/Authenticator/UI/ListStatusView.swift index fad72688..02d78c0a 100644 --- a/Authenticator/UI/ListStatusView.swift +++ b/Authenticator/UI/ListStatusView.swift @@ -46,7 +46,7 @@ struct ListStatusView: View { .frame(height: height - 100) .listRowSeparator(.hidden) .sheet(isPresented: $showWhatsNew) { - VersionHistoryView(title: "What's new in\nYubico Authenticator") + VersionHistoryView(title: String(localized: "What's new in\nYubico Authenticator", comment: "Version history title")) } } } @@ -54,11 +54,11 @@ struct ListStatusView: View { struct WhatsNewView: View { var text: AttributedString { - var see = AttributedString("See ") + var see = AttributedString(localized: "See ", comment: "Substring in \"See what's new in this version\"") see.foregroundColor = .secondaryLabel - var whatsNew = AttributedString("what's new") + var whatsNew = AttributedString(localized: "what's new", comment: "Substring in \"See what's new in this version\"") whatsNew.foregroundColor = Color(.yubiBlue) - var inThisVersion = AttributedString(" in this version") + var inThisVersion = AttributedString(localized: " in this version", comment: "Substring in \"See what's new in this version\"") inThisVersion.foregroundColor = .secondaryLabel return see + whatsNew + inThisVersion } diff --git a/Authenticator/UI/MainView.swift b/Authenticator/UI/MainView.swift index 4268722f..2e708a3a 100644 --- a/Authenticator/UI/MainView.swift +++ b/Authenticator/UI/MainView.swift @@ -38,9 +38,9 @@ struct MainView: View { var insertYubiKeyMessage = { if YubiKitDeviceCapabilities.supportsISO7816NFCTags { - "Insert YubiKey \(!UIAccessibility.isVoiceOverRunning ? "or pull down to activate NFC" : "or scan a NFC YubiKey")" + String(localized: "Insert YubiKey") + "\(!UIAccessibility.isVoiceOverRunning ? String(localized: "or pull down to activate NFC") : String(localized: "or scan a NFC YubiKey"))" } else { - "Insert YubiKey" + String(localized: "Insert YubiKey") } }() @@ -61,7 +61,7 @@ struct MainView: View { AccountRowView(account: account, showAccountDetails: $showAccountDetails) } } else { - ListStatusView(image: Image(systemName: "person.crop.circle.badge.questionmark"), message: "No matching accounts on YubiKey", height: reader.size.height) + ListStatusView(image: Image(systemName: "person.crop.circle.badge.questionmark"), message: String(localized: "No matching accounts on YubiKey"), height: reader.size.height) } } else if model.pinnedAccounts.count > 0 { Section(header: Text("Pinned").frame(maxWidth: .infinity, alignment: .leading).font(.title3.bold()).foregroundColor(Color("ListSectionHeaderColor"))) { @@ -87,12 +87,12 @@ struct MainView: View { AccountRowView(account: account, showAccountDetails: $showAccountDetails) } } else { - ListStatusView(image: Image(systemName: "person.crop.circle"), message: "No accounts on YubiKey", height: reader.size.height) + ListStatusView(image: Image(systemName: "person.crop.circle"), message: String(localized: "No accounts on YubiKey"), height: reader.size.height) } } } .accessibilityHidden(showAccountDetails != nil) - .searchable(text: $searchText, prompt: "Search") + .searchable(text: $searchText, prompt: String(localized: "Search")) .autocorrectionDisabled(true) .keyboardType(.asciiCapable) .listStyle(.inset) @@ -138,7 +138,7 @@ struct MainView: View { } } } - .navigationTitle(model.accountsLoaded ? "Accounts" : "") + .navigationTitle(model.accountsLoaded ? String(localized: "Accounts", comment: "Navigation title in main view.") : "") } .accessibilityHidden(showAccountDetails != nil) .overlay { @@ -159,24 +159,24 @@ struct MainView: View { DisableOTPView() } - .alert("Enter password", isPresented: $model.presentPasswordEntry) { - SecureField("Password", text: $password) - Button("Cancel", role: .cancel) { password = "" } - Button("Ok") { + .alert(String(localized: "Enter password", comment: "Password alert"), isPresented: $model.presentPasswordEntry) { + SecureField(String(localized: "Password", comment: "Password alert"), text: $password) + Button(String(localized: "Cancel", comment: "Password alert"), role: .cancel) { password = "" } + Button(String(localized: "Ok", comment: "Password alert")) { model.password.send(password) password = "" } } message: { Text(model.passwordEntryMessage) } - .alertOrConfirmationDialog("Save password?", isPresented: $model.presentPasswordSaveType) { - Button("Save password") { model.passwordSaveType.send(.some(.save)) } + .alertOrConfirmationDialog(String(localized: "Save password?", comment: "Save password alert"), isPresented: $model.presentPasswordSaveType) { + Button("Save password", comment: "Save password alert.") { model.passwordSaveType.send(.some(.save)) } let authenticationType = PasswordPreferences.evaluatedAuthenticationType() if authenticationType != .none { Button("Save and protect with \(authenticationType.title)") { model.passwordSaveType.send(.some(.lock)) } } - Button("Never for this YubiKey") { model.passwordSaveType.send(.some(.never)) } - Button("Not now" , role: .cancel) { model.passwordSaveType.send(nil) } + Button("Never for this YubiKey", comment: "Save password alert.") { model.passwordSaveType.send(.some(.never)) } + Button("Not now", comment: "Save passsword alert" , role: .cancel) { model.passwordSaveType.send(nil) } } .errorAlert(error: $model.sessionError) .errorAlert(error: $model.connectionError) { model.start() } diff --git a/LocalizationScreenshots/1-start.jpeg b/LocalizationScreenshots/1-start.jpeg new file mode 100644 index 00000000..afa31a32 Binary files /dev/null and b/LocalizationScreenshots/1-start.jpeg differ diff --git a/LocalizationScreenshots/10-add-account-manual.jpeg b/LocalizationScreenshots/10-add-account-manual.jpeg new file mode 100644 index 00000000..eb15e35b Binary files /dev/null and b/LocalizationScreenshots/10-add-account-manual.jpeg differ diff --git a/LocalizationScreenshots/11-add-account-missing-secret.jpeg b/LocalizationScreenshots/11-add-account-missing-secret.jpeg new file mode 100644 index 00000000..e6dccc05 Binary files /dev/null and b/LocalizationScreenshots/11-add-account-missing-secret.jpeg differ diff --git a/LocalizationScreenshots/12-add-account-missing-name.jpeg b/LocalizationScreenshots/12-add-account-missing-name.jpeg new file mode 100644 index 00000000..bf2b86b9 Binary files /dev/null and b/LocalizationScreenshots/12-add-account-missing-name.jpeg differ diff --git a/LocalizationScreenshots/13-configuration.jpeg b/LocalizationScreenshots/13-configuration.jpeg new file mode 100644 index 00000000..7b33edf9 Binary files /dev/null and b/LocalizationScreenshots/13-configuration.jpeg differ diff --git a/LocalizationScreenshots/14-configuration-toggle-otp.jpeg b/LocalizationScreenshots/14-configuration-toggle-otp.jpeg new file mode 100644 index 00000000..d951cb38 Binary files /dev/null and b/LocalizationScreenshots/14-configuration-toggle-otp.jpeg differ diff --git a/LocalizationScreenshots/15-configuration-passwords-and-reset-1.jpeg b/LocalizationScreenshots/15-configuration-passwords-and-reset-1.jpeg new file mode 100644 index 00000000..87631655 Binary files /dev/null and b/LocalizationScreenshots/15-configuration-passwords-and-reset-1.jpeg differ diff --git a/LocalizationScreenshots/16-configuration-passwords-and-reset-2.jpeg b/LocalizationScreenshots/16-configuration-passwords-and-reset-2.jpeg new file mode 100644 index 00000000..37e4d318 Binary files /dev/null and b/LocalizationScreenshots/16-configuration-passwords-and-reset-2.jpeg differ diff --git a/LocalizationScreenshots/17-configuration-set-password.jpeg b/LocalizationScreenshots/17-configuration-set-password.jpeg new file mode 100644 index 00000000..8f62eef1 Binary files /dev/null and b/LocalizationScreenshots/17-configuration-set-password.jpeg differ diff --git a/LocalizationScreenshots/18-configuration-clear-passwords.jpeg b/LocalizationScreenshots/18-configuration-clear-passwords.jpeg new file mode 100644 index 00000000..5321263d Binary files /dev/null and b/LocalizationScreenshots/18-configuration-clear-passwords.jpeg differ diff --git a/LocalizationScreenshots/19-configuration-nfc-settings.jpeg b/LocalizationScreenshots/19-configuration-nfc-settings.jpeg new file mode 100644 index 00000000..ea9f8406 Binary files /dev/null and b/LocalizationScreenshots/19-configuration-nfc-settings.jpeg differ diff --git a/LocalizationScreenshots/2-start-ready-to-scan.jpeg b/LocalizationScreenshots/2-start-ready-to-scan.jpeg new file mode 100644 index 00000000..1a9a99cc Binary files /dev/null and b/LocalizationScreenshots/2-start-ready-to-scan.jpeg differ diff --git a/LocalizationScreenshots/20-configuration-reset.jpeg b/LocalizationScreenshots/20-configuration-reset.jpeg new file mode 100644 index 00000000..b9649bd2 Binary files /dev/null and b/LocalizationScreenshots/20-configuration-reset.jpeg differ diff --git a/LocalizationScreenshots/21-configuration-smarrtcard.jpeg b/LocalizationScreenshots/21-configuration-smarrtcard.jpeg new file mode 100644 index 00000000..73b99544 Binary files /dev/null and b/LocalizationScreenshots/21-configuration-smarrtcard.jpeg differ diff --git a/LocalizationScreenshots/22-configuration-smartcard-enabled.jpeg b/LocalizationScreenshots/22-configuration-smartcard-enabled.jpeg new file mode 100644 index 00000000..1a334344 Binary files /dev/null and b/LocalizationScreenshots/22-configuration-smartcard-enabled.jpeg differ diff --git a/LocalizationScreenshots/23-configuration-smartcard-help.jpeg b/LocalizationScreenshots/23-configuration-smartcard-help.jpeg new file mode 100644 index 00000000..6bab15d9 Binary files /dev/null and b/LocalizationScreenshots/23-configuration-smartcard-help.jpeg differ diff --git a/LocalizationScreenshots/24-about.jpeg b/LocalizationScreenshots/24-about.jpeg new file mode 100644 index 00000000..734bf7d7 Binary files /dev/null and b/LocalizationScreenshots/24-about.jpeg differ diff --git a/LocalizationScreenshots/25-about-tutorial-1.jpeg b/LocalizationScreenshots/25-about-tutorial-1.jpeg new file mode 100644 index 00000000..25300c52 Binary files /dev/null and b/LocalizationScreenshots/25-about-tutorial-1.jpeg differ diff --git a/LocalizationScreenshots/26-about-tutorial-2.jpeg b/LocalizationScreenshots/26-about-tutorial-2.jpeg new file mode 100644 index 00000000..eb347bfa Binary files /dev/null and b/LocalizationScreenshots/26-about-tutorial-2.jpeg differ diff --git a/LocalizationScreenshots/27-about-tutorial-3.jpeg b/LocalizationScreenshots/27-about-tutorial-3.jpeg new file mode 100644 index 00000000..16bca8fd Binary files /dev/null and b/LocalizationScreenshots/27-about-tutorial-3.jpeg differ diff --git a/LocalizationScreenshots/28-about-tutorial-4.jpeg b/LocalizationScreenshots/28-about-tutorial-4.jpeg new file mode 100644 index 00000000..068cd447 Binary files /dev/null and b/LocalizationScreenshots/28-about-tutorial-4.jpeg differ diff --git a/LocalizationScreenshots/29-about-version-history.jpeg b/LocalizationScreenshots/29-about-version-history.jpeg new file mode 100644 index 00000000..3fee5225 Binary files /dev/null and b/LocalizationScreenshots/29-about-version-history.jpeg differ diff --git a/LocalizationScreenshots/3-start-successfully-read.jpeg b/LocalizationScreenshots/3-start-successfully-read.jpeg new file mode 100644 index 00000000..60556060 Binary files /dev/null and b/LocalizationScreenshots/3-start-successfully-read.jpeg differ diff --git a/LocalizationScreenshots/30-disable-otp.png b/LocalizationScreenshots/30-disable-otp.png new file mode 100644 index 00000000..bbf25158 Binary files /dev/null and b/LocalizationScreenshots/30-disable-otp.png differ diff --git a/LocalizationScreenshots/31-smartcard-extension-notification.png b/LocalizationScreenshots/31-smartcard-extension-notification.png new file mode 100644 index 00000000..be377410 Binary files /dev/null and b/LocalizationScreenshots/31-smartcard-extension-notification.png differ diff --git a/LocalizationScreenshots/32-smartcard-extension-unlock.jpeg b/LocalizationScreenshots/32-smartcard-extension-unlock.jpeg new file mode 100644 index 00000000..a725e0c3 Binary files /dev/null and b/LocalizationScreenshots/32-smartcard-extension-unlock.jpeg differ diff --git a/LocalizationScreenshots/33-smartcard-extension-done.png b/LocalizationScreenshots/33-smartcard-extension-done.png new file mode 100644 index 00000000..ec9178d4 Binary files /dev/null and b/LocalizationScreenshots/33-smartcard-extension-done.png differ diff --git a/LocalizationScreenshots/4-start-list.jpeg b/LocalizationScreenshots/4-start-list.jpeg new file mode 100644 index 00000000..2f0b6806 Binary files /dev/null and b/LocalizationScreenshots/4-start-list.jpeg differ diff --git a/LocalizationScreenshots/5-details.jpeg b/LocalizationScreenshots/5-details.jpeg new file mode 100644 index 00000000..8cc11645 Binary files /dev/null and b/LocalizationScreenshots/5-details.jpeg differ diff --git a/LocalizationScreenshots/6-rename.jpeg b/LocalizationScreenshots/6-rename.jpeg new file mode 100644 index 00000000..79779fa5 Binary files /dev/null and b/LocalizationScreenshots/6-rename.jpeg differ diff --git a/LocalizationScreenshots/7-delete-alert.jpeg b/LocalizationScreenshots/7-delete-alert.jpeg new file mode 100644 index 00000000..091486fc Binary files /dev/null and b/LocalizationScreenshots/7-delete-alert.jpeg differ diff --git a/LocalizationScreenshots/8-start-menu.jpeg b/LocalizationScreenshots/8-start-menu.jpeg new file mode 100644 index 00000000..a34a55de Binary files /dev/null and b/LocalizationScreenshots/8-start-menu.jpeg differ diff --git a/LocalizationScreenshots/9-add-account-qr-code.jpeg b/LocalizationScreenshots/9-add-account-qr-code.jpeg new file mode 100644 index 00000000..ab422e9f Binary files /dev/null and b/LocalizationScreenshots/9-add-account-qr-code.jpeg differ