From 08357c263aec5ae38f5de5f9f21ce485eca47b48 Mon Sep 17 00:00:00 2001 From: Paul B Date: Sun, 16 May 2021 20:54:48 +0200 Subject: [PATCH] Feature/settings page (#45) * feat: SettingsVC, PrivacyURL * chore: package.resolved * WIP link missing files; l10n. * Vertical gear pos. * Add last reference. * Fix alignment warning. * Change button color. * Prepare version cell for migration to core module. * Cosmetics and logic change * SwiftLint and cell indicator * opensource libs and font size * Add separator. * Change color. * Reorder. * Outsource AppVersionCell. * Revert context file. Co-authored-by: Paul Ballmann Co-authored-by: Yannick Spreen --- DGCAWallet.xcodeproj/project.pbxproj | 36 ++- .../Base.lproj/CertificateViewer.storyboard | 273 ++++++++++++++++++ .../Storyboards/Base.lproj/Main.storyboard | 30 +- .../Base.lproj/Settings.storyboard | 176 +++++++++++ .../en.lproj/Localizable.strings | 1 + DGCAWallet/ViewControllers/List.swift | 31 +- DGCAWallet/ViewControllers/Settings.swift | 87 ++++++ .../en.xcloc/Localized Contents/en.xliff | 74 +++++ .../Base.lproj}/CertificateViewer.storyboard | 8 +- .../Storyboards/Base.lproj/Main.storyboard | 19 +- .../Base.lproj/Settings.storyboard | 125 ++++++++ .../en.lproj/Localizable.strings | 1 + 12 files changed, 836 insertions(+), 25 deletions(-) create mode 100644 DGCAWallet/Storyboards/Base.lproj/CertificateViewer.storyboard create mode 100644 DGCAWallet/Storyboards/Base.lproj/Settings.storyboard create mode 100644 DGCAWallet/ViewControllers/Settings.swift rename {DGCAWallet/Storyboards => Localization/DGCAWallet/en.xcloc/Source Contents/DGCAWallet/Storyboards/Base.lproj}/CertificateViewer.storyboard (98%) create mode 100644 Localization/DGCAWallet/en.xcloc/Source Contents/DGCAWallet/Storyboards/Base.lproj/Settings.storyboard diff --git a/DGCAWallet.xcodeproj/project.pbxproj b/DGCAWallet.xcodeproj/project.pbxproj index bd31e35..5ceb742 100644 --- a/DGCAWallet.xcodeproj/project.pbxproj +++ b/DGCAWallet.xcodeproj/project.pbxproj @@ -8,7 +8,6 @@ /* Begin PBXBuildFile section */ CE13CF00262DCC180070C80E /* FloatingPanel in Frameworks */ = {isa = PBXBuildFile; productRef = CE13CEFF262DCC180070C80E /* FloatingPanel */; }; - CE13CF05262DCDCD0070C80E /* CertificateViewer.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CE13CF04262DCDCD0070C80E /* CertificateViewer.storyboard */; }; CE13CF0A262DCDDA0070C80E /* CertificateViewer.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE13CF09262DCDDA0070C80E /* CertificateViewer.swift */; }; CE13CF0F262DD0D80070C80E /* FullFloatingPanelLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE13CF0E262DD0D80070C80E /* FullFloatingPanelLayout.swift */; }; CE13CF23262DDF810070C80E /* RoundedButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE13CF22262DDF810070C80E /* RoundedButton.swift */; }; @@ -16,6 +15,9 @@ CE1F155C2639F9E700736D48 /* SwiftyJSON in Frameworks */ = {isa = PBXBuildFile; productRef = CE1F155B2639F9E700736D48 /* SwiftyJSON */; }; CE260F56263DD1720083A200 /* WalletCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE260F55263DD1720083A200 /* WalletCell.swift */; }; CE37B643263867D700DEE13D /* SecureBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE37B642263867D700DEE13D /* SecureBackground.swift */; }; + CE4BD254264FF28900689FD6 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE4BD253264FF28900689FD6 /* Settings.swift */; }; + CE4BD255264FF39400689FD6 /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CE4BD257264FF39400689FD6 /* Settings.storyboard */; }; + CE4BD258264FF39D00689FD6 /* CertificateViewer.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CE4BD25A264FF39D00689FD6 /* CertificateViewer.storyboard */; }; CE56B5052639F48E00FB31B1 /* SwiftDGC in Frameworks */ = {isa = PBXBuildFile; productRef = CE56B5042639F48E00FB31B1 /* SwiftDGC */; }; CE56BC07264068110044FD3F /* GatewayConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE56BC06264068110044FD3F /* GatewayConnection.swift */; }; CE6D4A44264835F100A5D33D /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = CE6D4A46264835F100A5D33D /* Localizable.strings */; }; @@ -63,13 +65,15 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - CE13CF04262DCDCD0070C80E /* CertificateViewer.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = CertificateViewer.storyboard; sourceTree = ""; }; CE13CF09262DCDDA0070C80E /* CertificateViewer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CertificateViewer.swift; sourceTree = ""; }; CE13CF0E262DD0D80070C80E /* FullFloatingPanelLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullFloatingPanelLayout.swift; sourceTree = ""; }; CE13CF22262DDF810070C80E /* RoundedButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedButton.swift; sourceTree = ""; }; CE1D1EF5263597A2004C8919 /* LocalData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalData.swift; sourceTree = ""; }; CE260F55263DD1720083A200 /* WalletCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletCell.swift; sourceTree = ""; }; CE37B642263867D700DEE13D /* SecureBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackground.swift; sourceTree = ""; }; + CE4BD253264FF28900689FD6 /* Settings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = ""; }; + CE4BD256264FF39400689FD6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Settings.storyboard; sourceTree = ""; }; + CE4BD259264FF39D00689FD6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/CertificateViewer.storyboard; sourceTree = ""; }; CE56BC06264068110044FD3F /* GatewayConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GatewayConnection.swift; sourceTree = ""; }; CE6D4A45264835F100A5D33D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; CE8096D8263B07BB00A65AD6 /* UIColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIColor.swift; sourceTree = ""; }; @@ -93,7 +97,7 @@ CEA6D70D261F8D2900715333 /* DGCAWalletUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DGCAWalletUITests.swift; sourceTree = ""; }; CEA6D70F261F8D2900715333 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; CEA6E560264058B50066DC8E /* UIViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewController.swift; sourceTree = ""; }; - CEC7FEDE264C5A41005561BA /* context.jsonc */ = {isa = PBXFileReference; lastKnownFileType = file; path = context.jsonc; sourceTree = SOURCE_ROOT; }; + CEC7FEDE264C5A41005561BA /* context.jsonc */ = {isa = PBXFileReference; lastKnownFileType = text; path = context.jsonc; sourceTree = SOURCE_ROOT; }; CED2725F26398683003D47A9 /* UIFont.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIFont.swift; sourceTree = ""; }; CED949C9263B50CE00883558 /* List.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = List.swift; sourceTree = ""; }; CEDABD3F263C5FF4007A9B97 /* CertTable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CertTable.swift; sourceTree = ""; }; @@ -139,7 +143,8 @@ children = ( CEA6D6F6261F8D2900715333 /* LaunchScreen.storyboard */, CEA6D6F1261F8D2700715333 /* Main.storyboard */, - CE13CF04262DCDCD0070C80E /* CertificateViewer.storyboard */, + CE4BD25A264FF39D00689FD6 /* CertificateViewer.storyboard */, + CE4BD257264FF39400689FD6 /* Settings.storyboard */, ); path = Storyboards; sourceTree = ""; @@ -174,6 +179,7 @@ CE891304263581D900CB92AF /* Home.swift */, CEDABD48263C70EF007A9B97 /* CertPages.swift */, CEDABD3F263C5FF4007A9B97 /* CertTable.swift */, + CE4BD253264FF28900689FD6 /* Settings.swift */, ); path = ViewControllers; sourceTree = ""; @@ -399,9 +405,10 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - CE13CF05262DCDCD0070C80E /* CertificateViewer.storyboard in Resources */, + CE4BD258264FF39D00689FD6 /* CertificateViewer.storyboard in Resources */, CE6D4A44264835F100A5D33D /* Localizable.strings in Resources */, CEA6D6F8261F8D2900715333 /* LaunchScreen.storyboard in Resources */, + CE4BD255264FF39400689FD6 /* Settings.storyboard in Resources */, CEC7FEDF264C5A41005561BA /* context.jsonc in Resources */, CE81533A263FF7EC0030D777 /* README.md in Resources */, CEA6D6F5261F8D2900715333 /* Assets.xcassets in Resources */, @@ -458,6 +465,7 @@ CEA1556B262F784E0024B7AC /* SelfSizedTableView.swift in Sources */, CE8096D9263B07BB00A65AD6 /* UIColor.swift in Sources */, CEDABD49263C70EF007A9B97 /* CertPages.swift in Sources */, + CE4BD254264FF28900689FD6 /* Settings.swift in Sources */, CE1D1EF6263597A2004C8919 /* LocalData.swift in Sources */, CED2726026398683003D47A9 /* UIFont.swift in Sources */, CEE9DA55263C7D4000A31532 /* CertCode.swift in Sources */, @@ -508,6 +516,22 @@ /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ + CE4BD257264FF39400689FD6 /* Settings.storyboard */ = { + isa = PBXVariantGroup; + children = ( + CE4BD256264FF39400689FD6 /* Base */, + ); + name = Settings.storyboard; + sourceTree = ""; + }; + CE4BD25A264FF39D00689FD6 /* CertificateViewer.storyboard */ = { + isa = PBXVariantGroup; + children = ( + CE4BD259264FF39D00689FD6 /* Base */, + ); + name = CertificateViewer.storyboard; + sourceTree = ""; + }; CE6D4A46264835F100A5D33D /* Localizable.strings */ = { isa = PBXVariantGroup; children = ( @@ -657,7 +681,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = KH99XNF745; + DEVELOPMENT_TEAM = 9TD26M5RG8; INFOPLIST_FILE = DGCAWallet/SupportingFiles/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.1; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/DGCAWallet/Storyboards/Base.lproj/CertificateViewer.storyboard b/DGCAWallet/Storyboards/Base.lproj/CertificateViewer.storyboard new file mode 100644 index 0000000..46fc745 --- /dev/null +++ b/DGCAWallet/Storyboards/Base.lproj/CertificateViewer.storyboard @@ -0,0 +1,273 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DGCAWallet/Storyboards/Base.lproj/Main.storyboard b/DGCAWallet/Storyboards/Base.lproj/Main.storyboard index ca60d24..6ea75f0 100644 --- a/DGCAWallet/Storyboards/Base.lproj/Main.storyboard +++ b/DGCAWallet/Storyboards/Base.lproj/Main.storyboard @@ -53,7 +53,7 @@ - + @@ -126,16 +126,16 @@ - + + + + - + + @@ -252,11 +265,12 @@ - + + diff --git a/DGCAWallet/Storyboards/Base.lproj/Settings.storyboard b/DGCAWallet/Storyboards/Base.lproj/Settings.storyboard new file mode 100644 index 0000000..9956cac --- /dev/null +++ b/DGCAWallet/Storyboards/Base.lproj/Settings.storyboard @@ -0,0 +1,176 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DGCAWallet/SupportingFiles/en.lproj/Localizable.strings b/DGCAWallet/SupportingFiles/en.lproj/Localizable.strings index a57196d..672dbc2 100644 --- a/DGCAWallet/SupportingFiles/en.lproj/Localizable.strings +++ b/DGCAWallet/SupportingFiles/en.lproj/Localizable.strings @@ -43,3 +43,4 @@ "auth.later" = "Try Later"; "cert.delete.title" = "Delete Certificate"; "cert.delete.body" = "Are you sure you want to delete this certificate? This action cannot be undone."; +"app-version" = "App Version %@"; diff --git a/DGCAWallet/ViewControllers/List.swift b/DGCAWallet/ViewControllers/List.swift index 47c2db9..e0bd17d 100644 --- a/DGCAWallet/ViewControllers/List.swift +++ b/DGCAWallet/ViewControllers/List.swift @@ -53,6 +53,27 @@ class ListVC: UIViewController { performSegue(withIdentifier: "scanner", sender: self) } + @IBAction func settingsTapped(_ sender: UIButton) { + guard let settingsVC = UIStoryboard(name: "Settings", bundle: nil).instantiateInitialViewController(), + let viewer = settingsVC as? SettingsVC else { + return + } + showFloatingPanel(for: viewer) + } + + func showFloatingPanel(for controller: UIViewController) { + let fpc = FloatingPanelController() + fpc.set(contentViewController: controller) + fpc.isRemovalInteractionEnabled = true + fpc.layout = FullFloatingPanelLayout() + fpc.surfaceView.layer.cornerRadius = 24.0 + fpc.surfaceView.clipsToBounds = true + fpc.delegate = self + presentingViewer = controller + + present(fpc, animated: true, completion: nil) + } + @IBOutlet weak var table: UITableView! @IBOutlet weak var emptyView: UIView! @@ -67,7 +88,7 @@ class ListVC: UIViewController { presentingViewer?.dismiss(animated: true, completion: nil) } - var presentingViewer: CertificateViewerVC? + var presentingViewer: UIViewController? var newHCertScanned: HCert? func presentViewer(for certificate: HCert, with tan: String? = nil, isSaved: Bool = true) { @@ -191,13 +212,13 @@ extension ListVC: FloatingPanelControllerDelegate { let threshold: CGFloat = 5.0 switch fpc.layout.position { case .top: - return (velocity.dy <= -threshold) + return (velocity.dy <= -threshold) case .left: - return (velocity.dx <= -threshold) + return (velocity.dx <= -threshold) case .bottom: - return (velocity.dy >= threshold) + return (velocity.dy >= threshold) case .right: - return (velocity.dx >= threshold) + return (velocity.dx >= threshold) } } } diff --git a/DGCAWallet/ViewControllers/Settings.swift b/DGCAWallet/ViewControllers/Settings.swift new file mode 100644 index 0000000..3ca2d1f --- /dev/null +++ b/DGCAWallet/ViewControllers/Settings.swift @@ -0,0 +1,87 @@ +// +/*- + * ---license-start + * eu-digital-green-certificates / dgca-wallet-app-ios + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ +// +// Settings.swift +// DGCAWallet +// +// Created by Paul Ballmann on 14.05.21. +// + +import Foundation +import UIKit +import FloatingPanel +import SwiftDGC + +class SettingsVC: UINavigationController { + + override func viewDidLoad() { + super.viewDidLoad() + + additionalSafeAreaInsets.top = 16.0 + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + + } +} + +class SettingsTableVC: UITableViewController { + + @IBAction + func cancelButton() { + dismiss(animated: true, completion: nil) + } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + switch indexPath.row { + case 0: + openPrivacyDoc() + case 1: + openEuCertDoc() + case 2: + openGitHubSource() + default: + return + } + } + + func openPrivacyDoc() { + let link = LocalData.sharedInstance.versionedConfig["privacyUrl"].string ?? "" + openUrl(link) + } + + func openEuCertDoc() { + let link = "https://ec.europa.eu/health/ehealth/covid-19_en" + openUrl(link) + } + + func openGitHubSource() { + let link = "https://github.com/eu-digital-green-certificates" + openUrl(link) + } + + func openUrl(_ string: String!) { + if let url = URL(string: string) { + UIApplication.shared.open(url) + } + } +} diff --git a/Localization/DGCAWallet/en.xcloc/Localized Contents/en.xliff b/Localization/DGCAWallet/en.xcloc/Localized Contents/en.xliff index 58d223f..ebf7c77 100644 --- a/Localization/DGCAWallet/en.xcloc/Localized Contents/en.xliff +++ b/Localization/DGCAWallet/en.xcloc/Localized Contents/en.xliff @@ -1,5 +1,42 @@ + +
+ +
+ + + TAN: GIWHX3 + TAN: GIWHX3 + Class = "UILabel"; text = "TAN: GIWHX3"; ObjectID = "BmW-aV-sN6"; + + + Max Mustermann + Max Mustermann + Class = "UILabel"; text = "Max Mustermann"; ObjectID = "DVW-2y-e4I"; + + + Cancel + Cancel + Class = "UIButton"; normalTitle = "Cancel"; ObjectID = "YeB-JS-Q1Q"; + + + Done + Done + Class = "UIButton"; normalTitle = "Done"; ObjectID = "a9u-Ia-KEX"; + + + Identification Number + Identification Number + Class = "UILabel"; text = "Identification Number"; ObjectID = "bxm-hI-f1t"; + + + DE4382974329 + DE4382974329 + Class = "UILabel"; text = "DE4382974329"; ObjectID = "gn8-3g-ntq"; + + +
@@ -59,6 +96,38 @@ + +
+ +
+ + + Privacy Information + Privacy Information + Class = "UIButton"; normalTitle = "Privacy Information"; ObjectID = "OQB-kV-Lgy"; + + + app-version + app-version + Class = "UILabel"; text = "app-version"; ObjectID = "Rdn-TW-8aO"; + + + Links + Links + Class = "UITableViewSection"; headerTitle = "Links"; ObjectID = "fbO-s8-6gg"; + + + Cancel + Cancel + Class = "UIBarButtonItem"; title = "Cancel"; ObjectID = "nbb-SQ-pYn"; + + + Settings + Settings + Class = "UINavigationItem"; title = "Settings"; ObjectID = "nqQ-GZ-nBR"; + + +
@@ -91,6 +160,11 @@
+ + App Version %@ + App Version %@ + + Confirm Identity Confirm Identity diff --git a/DGCAWallet/Storyboards/CertificateViewer.storyboard b/Localization/DGCAWallet/en.xcloc/Source Contents/DGCAWallet/Storyboards/Base.lproj/CertificateViewer.storyboard similarity index 98% rename from DGCAWallet/Storyboards/CertificateViewer.storyboard rename to Localization/DGCAWallet/en.xcloc/Source Contents/DGCAWallet/Storyboards/Base.lproj/CertificateViewer.storyboard index 51906e3..b58c943 100644 --- a/DGCAWallet/Storyboards/CertificateViewer.storyboard +++ b/Localization/DGCAWallet/en.xcloc/Source Contents/DGCAWallet/Storyboards/Base.lproj/CertificateViewer.storyboard @@ -26,7 +26,7 @@ + + + - + + @@ -257,6 +271,7 @@ + diff --git a/Localization/DGCAWallet/en.xcloc/Source Contents/DGCAWallet/Storyboards/Base.lproj/Settings.storyboard b/Localization/DGCAWallet/en.xcloc/Source Contents/DGCAWallet/Storyboards/Base.lproj/Settings.storyboard new file mode 100644 index 0000000..e8b2a12 --- /dev/null +++ b/Localization/DGCAWallet/en.xcloc/Source Contents/DGCAWallet/Storyboards/Base.lproj/Settings.storyboard @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Localization/DGCAWallet/en.xcloc/Source Contents/DGCAWallet/SupportingFiles/en.lproj/Localizable.strings b/Localization/DGCAWallet/en.xcloc/Source Contents/DGCAWallet/SupportingFiles/en.lproj/Localizable.strings index a57196d..672dbc2 100644 --- a/Localization/DGCAWallet/en.xcloc/Source Contents/DGCAWallet/SupportingFiles/en.lproj/Localizable.strings +++ b/Localization/DGCAWallet/en.xcloc/Source Contents/DGCAWallet/SupportingFiles/en.lproj/Localizable.strings @@ -43,3 +43,4 @@ "auth.later" = "Try Later"; "cert.delete.title" = "Delete Certificate"; "cert.delete.body" = "Are you sure you want to delete this certificate? This action cannot be undone."; +"app-version" = "App Version %@";