summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAndrej Mihajlov <and@mullvad.net>2021-07-22 13:08:18 +0200
committerAndrej Mihajlov <and@mullvad.net>2021-07-22 13:08:18 +0200
commit43a5f956fbc8fc137367fb6676c7c34d3e8f93cf (patch)
tree4b51efff61dff08363bca633d932dc1d5f88aad8
parentfc34d04f9b7ca18267230be55267f04c82c9c96a (diff)
parent97496dd3732958f4c9c9e3599ccf37c8da91f21c (diff)
downloadmullvadvpn-43a5f956fbc8fc137367fb6676c7c34d3e8f93cf.tar.xz
mullvadvpn-43a5f956fbc8fc137367fb6676c7c34d3e8f93cf.zip
Merge branch 'localize-various-errors'
-rw-r--r--ios/MullvadVPN.xcodeproj/project.pbxproj108
-rw-r--r--ios/MullvadVPN/AccountInputGroupView.swift7
-rw-r--r--ios/MullvadVPN/AppDelegate.swift23
-rw-r--r--ios/MullvadVPN/CustomDateComponentsFormatting.swift7
-rw-r--r--ios/MullvadVPN/DisplayChainedError.swift282
-rw-r--r--ios/MullvadVPN/HeaderBarView.swift7
-rw-r--r--ios/MullvadVPN/MullvadRest.swift21
-rw-r--r--ios/MullvadVPN/UIBarButtonItem+KeyboardNavigation.swift14
-rw-r--r--ios/MullvadVPN/en.lproj/AccountInput.strings2
-rw-r--r--ios/MullvadVPN/en.lproj/AppDelegate.strings8
-rw-r--r--ios/MullvadVPN/en.lproj/AppStorePaymentManager.strings17
-rw-r--r--ios/MullvadVPN/en.lproj/CustomDateComponentsFormatting.strings2
-rw-r--r--ios/MullvadVPN/en.lproj/HeaderBar.strings2
-rw-r--r--ios/MullvadVPN/en.lproj/KeyboardNavigation.strings5
-rw-r--r--ios/MullvadVPN/en.lproj/MullvadRest.strings23
-rw-r--r--ios/MullvadVPN/en.lproj/StoreKitErrors.strings14
-rw-r--r--ios/MullvadVPN/en.lproj/TunnelManager.strings53
17 files changed, 545 insertions, 50 deletions
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj
index e3c651d6c4..9f9274c748 100644
--- a/ios/MullvadVPN.xcodeproj/project.pbxproj
+++ b/ios/MullvadVPN.xcodeproj/project.pbxproj
@@ -227,9 +227,18 @@
58F558E32695D1D800F630D0 /* Preferences.strings in Resources */ = {isa = PBXBuildFile; fileRef = 58F558E12695D1D800F630D0 /* Preferences.strings */; };
58F558E62695D1F200F630D0 /* ProblemReport.strings in Resources */ = {isa = PBXBuildFile; fileRef = 58F558E42695D1F200F630D0 /* ProblemReport.strings */; };
58F558E92695D20F00F630D0 /* SelectLocation.strings in Resources */ = {isa = PBXBuildFile; fileRef = 58F558E72695D20F00F630D0 /* SelectLocation.strings */; };
+ 58F558EC2695D26A00F630D0 /* MullvadRest.strings in Resources */ = {isa = PBXBuildFile; fileRef = 58F558EA2695D26A00F630D0 /* MullvadRest.strings */; };
58F558EF2695D50D00F630D0 /* ProblemReportReview.strings in Resources */ = {isa = PBXBuildFile; fileRef = 58F558ED2695D50D00F630D0 /* ProblemReportReview.strings */; };
+ 58F558F92696EB1C00F630D0 /* StoreKitErrors.strings in Resources */ = {isa = PBXBuildFile; fileRef = 58F558F32696EB1C00F630D0 /* StoreKitErrors.strings */; };
+ 58F558FA2696EB1C00F630D0 /* TunnelManager.strings in Resources */ = {isa = PBXBuildFile; fileRef = 58F558F52696EB1C00F630D0 /* TunnelManager.strings */; };
+ 58F558FB2696EB1C00F630D0 /* AppStorePaymentManager.strings in Resources */ = {isa = PBXBuildFile; fileRef = 58F558F72696EB1C00F630D0 /* AppStorePaymentManager.strings */; };
58F5590E2697002100F630D0 /* Main.strings in Resources */ = {isa = PBXBuildFile; fileRef = 58F559052697002000F630D0 /* Main.strings */; };
58F5590F2697002100F630D0 /* ConnectionPanel.strings in Resources */ = {isa = PBXBuildFile; fileRef = 58F559072697002100F630D0 /* ConnectionPanel.strings */; };
+ 58F558FE2696F09100F630D0 /* KeyboardNavigation.strings in Resources */ = {isa = PBXBuildFile; fileRef = 58F558FC2696F09100F630D0 /* KeyboardNavigation.strings */; };
+ 58F5590B2697002100F630D0 /* CustomDateComponentsFormatting.strings in Resources */ = {isa = PBXBuildFile; fileRef = 58F558FF2697002000F630D0 /* CustomDateComponentsFormatting.strings */; };
+ 58F5590C2697002100F630D0 /* AppDelegate.strings in Resources */ = {isa = PBXBuildFile; fileRef = 58F559012697002000F630D0 /* AppDelegate.strings */; };
+ 58F5590D2697002100F630D0 /* AccountInput.strings in Resources */ = {isa = PBXBuildFile; fileRef = 58F559032697002000F630D0 /* AccountInput.strings */; };
+ 58F559102697002100F630D0 /* HeaderBar.strings in Resources */ = {isa = PBXBuildFile; fileRef = 58F559092697002100F630D0 /* HeaderBar.strings */; };
58F61F4F2692F21C00DCFC2B /* WireguardKeys.strings in Resources */ = {isa = PBXBuildFile; fileRef = 58F61F4D2692F21C00DCFC2B /* WireguardKeys.strings */; };
58F7CA882692E34000FC59FD /* WireguardKeysContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F7CA872692E34000FC59FD /* WireguardKeysContentView.swift */; };
58F840AF2464382C0044E708 /* KeychainItemRevision.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F840AE2464382C0044E708 /* KeychainItemRevision.swift */; };
@@ -451,9 +460,18 @@
58F558E22695D1D800F630D0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Preferences.strings; sourceTree = "<group>"; };
58F558E52695D1F200F630D0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/ProblemReport.strings; sourceTree = "<group>"; };
58F558E82695D20F00F630D0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/SelectLocation.strings; sourceTree = "<group>"; };
+ 58F558EB2695D26A00F630D0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/MullvadRest.strings; sourceTree = "<group>"; };
58F558EE2695D50D00F630D0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/ProblemReportReview.strings; sourceTree = "<group>"; };
+ 58F558F42696EB1C00F630D0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/StoreKitErrors.strings; sourceTree = "<group>"; };
+ 58F558F62696EB1C00F630D0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/TunnelManager.strings; sourceTree = "<group>"; };
+ 58F558F82696EB1C00F630D0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/AppStorePaymentManager.strings; sourceTree = "<group>"; };
58F559062697002000F630D0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Main.strings; sourceTree = "<group>"; };
58F559082697002100F630D0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/ConnectionPanel.strings; sourceTree = "<group>"; };
+ 58F558FD2696F09100F630D0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/KeyboardNavigation.strings; sourceTree = "<group>"; };
+ 58F559002697002000F630D0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/CustomDateComponentsFormatting.strings; sourceTree = "<group>"; };
+ 58F559022697002000F630D0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/AppDelegate.strings; sourceTree = "<group>"; };
+ 58F559042697002000F630D0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/AccountInput.strings; sourceTree = "<group>"; };
+ 58F5590A2697002100F630D0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/HeaderBar.strings; sourceTree = "<group>"; };
58F61F4E2692F21C00DCFC2B /* en */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/WireguardKeys.strings; sourceTree = "<group>"; };
58F7CA872692E34000FC59FD /* WireguardKeysContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WireguardKeysContentView.swift; sourceTree = "<group>"; };
58F840AE2464382C0044E708 /* KeychainItemRevision.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainItemRevision.swift; sourceTree = "<group>"; };
@@ -559,17 +577,26 @@
isa = PBXGroup;
children = (
581FC4F82695ACE100AA97BA /* Account.strings */,
+ 58F559032697002000F630D0 /* AccountInput.strings */,
+ 58F559012697002000F630D0 /* AppDelegate.strings */,
+ 58F558F72696EB1C00F630D0 /* AppStorePaymentManager.strings */,
582CFEE526945FC30072883A /* AppStoreSubscriptions.strings */,
58F559072697002100F630D0 /* ConnectionPanel.strings */,
58F558DB2695B85E00F630D0 /* Consent.strings */,
+ 58F558FF2697002000F630D0 /* CustomDateComponentsFormatting.strings */,
+ 58F559092697002100F630D0 /* HeaderBar.strings */,
+ 58F558FC2696F09100F630D0 /* KeyboardNavigation.strings */,
587B7543266922BF00DEF7E9 /* Localizable.strings */,
58F558DE2695BD3E00F630D0 /* Login.strings */,
58F559052697002000F630D0 /* Main.strings */,
+ 58F558EA2695D26A00F630D0 /* MullvadRest.strings */,
58F558E12695D1D800F630D0 /* Preferences.strings */,
58F558E42695D1F200F630D0 /* ProblemReport.strings */,
58F558ED2695D50D00F630D0 /* ProblemReportReview.strings */,
58F558E72695D20F00F630D0 /* SelectLocation.strings */,
582CFEE8269463B80072883A /* Settings.strings */,
+ 58F558F32696EB1C00F630D0 /* StoreKitErrors.strings */,
+ 58F558F52696EB1C00F630D0 /* TunnelManager.strings */,
58F61F4D2692F21C00DCFC2B /* WireguardKeys.strings */,
);
name = Localizations;
@@ -978,8 +1005,10 @@
58F3C0A624A50157003E76BE /* relays.json in Resources */,
58F558DD2695B85E00F630D0 /* Consent.strings in Resources */,
58727283265D173C00F315B2 /* LaunchScreen.storyboard in Resources */,
+ 58F558FB2696EB1C00F630D0 /* AppStorePaymentManager.strings in Resources */,
586ADD4723FC13F400CE9E87 /* countries.geo.json in Resources */,
58F5590F2697002100F630D0 /* ConnectionPanel.strings in Resources */,
+ 58F558FA2696EB1C00F630D0 /* TunnelManager.strings in Resources */,
58F558E02695BD3E00F630D0 /* Login.strings in Resources */,
58CE5E6B224146210008646E /* Assets.xcassets in Resources */,
58F558E92695D20F00F630D0 /* SelectLocation.strings in Resources */,
@@ -989,10 +1018,17 @@
58F558EF2695D50D00F630D0 /* ProblemReportReview.strings in Resources */,
584789B8264D4A2A000E45FB /* old_le_root_cert.cer in Resources */,
58F558E62695D1F200F630D0 /* ProblemReport.strings in Resources */,
+ 58F5590D2697002100F630D0 /* AccountInput.strings in Resources */,
+ 58F559102697002100F630D0 /* HeaderBar.strings in Resources */,
+ 58F558F92696EB1C00F630D0 /* StoreKitErrors.strings in Resources */,
584789BE264D4A2A000E45FB /* new_le_root_cert.cer in Resources */,
58F61F4F2692F21C00DCFC2B /* WireguardKeys.strings in Resources */,
+ 58F5590B2697002100F630D0 /* CustomDateComponentsFormatting.strings in Resources */,
58F5590E2697002100F630D0 /* Main.strings in Resources */,
+ 58F558FE2696F09100F630D0 /* KeyboardNavigation.strings in Resources */,
+ 58F5590C2697002100F630D0 /* AppDelegate.strings in Resources */,
581FC4FA2695ACE100AA97BA /* Account.strings in Resources */,
+ 58F558EC2695D26A00F630D0 /* MullvadRest.strings in Resources */,
582CFEEA269463B80072883A /* Settings.strings in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -1361,6 +1397,14 @@
name = SelectLocation.strings;
sourceTree = "<group>";
};
+ 58F558EA2695D26A00F630D0 /* MullvadRest.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 58F558EB2695D26A00F630D0 /* en */,
+ );
+ name = MullvadRest.strings;
+ sourceTree = "<group>";
+ };
58F558ED2695D50D00F630D0 /* ProblemReportReview.strings */ = {
isa = PBXVariantGroup;
children = (
@@ -1369,6 +1413,30 @@
name = ProblemReportReview.strings;
sourceTree = "<group>";
};
+ 58F558F32696EB1C00F630D0 /* StoreKitErrors.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 58F558F42696EB1C00F630D0 /* en */,
+ );
+ name = StoreKitErrors.strings;
+ sourceTree = "<group>";
+ };
+ 58F558F52696EB1C00F630D0 /* TunnelManager.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 58F558F62696EB1C00F630D0 /* en */,
+ );
+ name = TunnelManager.strings;
+ sourceTree = "<group>";
+ };
+ 58F558F72696EB1C00F630D0 /* AppStorePaymentManager.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 58F558F82696EB1C00F630D0 /* en */,
+ );
+ name = AppStorePaymentManager.strings;
+ sourceTree = "<group>";
+ };
58F559052697002000F630D0 /* Main.strings */ = {
isa = PBXVariantGroup;
children = (
@@ -1383,6 +1451,46 @@
58F559082697002100F630D0 /* en */,
);
name = ConnectionPanel.strings;
+ sourceTree = "<group>";
+ };
+ 58F558FC2696F09100F630D0 /* KeyboardNavigation.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 58F558FD2696F09100F630D0 /* en */,
+ );
+ name = KeyboardNavigation.strings;
+ sourceTree = "<group>";
+ };
+ 58F558FF2697002000F630D0 /* CustomDateComponentsFormatting.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 58F559002697002000F630D0 /* en */,
+ );
+ name = CustomDateComponentsFormatting.strings;
+ sourceTree = "<group>";
+ };
+ 58F559012697002000F630D0 /* AppDelegate.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 58F559022697002000F630D0 /* en */,
+ );
+ name = AppDelegate.strings;
+ sourceTree = "<group>";
+ };
+ 58F559032697002000F630D0 /* AccountInput.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 58F559042697002000F630D0 /* en */,
+ );
+ name = AccountInput.strings;
+ sourceTree = "<group>";
+ };
+ 58F559092697002100F630D0 /* HeaderBar.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 58F5590A2697002100F630D0 /* en */,
+ );
+ name = HeaderBar.strings;
sourceTree = "<group>";
};
58F61F4D2692F21C00DCFC2B /* WireguardKeys.strings */ = {
diff --git a/ios/MullvadVPN/AccountInputGroupView.swift b/ios/MullvadVPN/AccountInputGroupView.swift
index b67a752407..9ca166d383 100644
--- a/ios/MullvadVPN/AccountInputGroupView.swift
+++ b/ios/MullvadVPN/AccountInputGroupView.swift
@@ -20,7 +20,12 @@ class AccountInputGroupView: UIView {
let button = UIButton(type: .custom)
button.translatesAutoresizingMaskIntoConstraints = false
button.setImage(UIImage(named: "IconArrow"), for: .normal)
- button.accessibilityLabel = NSLocalizedString("ACCOUNT_INPUT_LOGIN_BUTTON_ACCESSIBILITY_LABEL", comment: "")
+ button.accessibilityLabel = NSLocalizedString(
+ "ACCOUNT_INPUT_LOGIN_BUTTON_ACCESSIBILITY_LABEL",
+ tableName: "AccountInput",
+ value: "Log in",
+ comment: "Accessibility label for submit button in account token input."
+ )
return button
}()
diff --git a/ios/MullvadVPN/AppDelegate.swift b/ios/MullvadVPN/AppDelegate.swift
index 4f7990d636..c240b4ddf0 100644
--- a/ios/MullvadVPN/AppDelegate.swift
+++ b/ios/MullvadVPN/AppDelegate.swift
@@ -544,7 +544,12 @@ extension AppDelegate: ConnectViewControllerDelegate {
case .failure(let error):
self.logger?.error(chainedError: error, message: "Failed to start the VPN tunnel")
- self.presentTunnelError(error, alertTitle: NSLocalizedString("Failed to start the VPN tunnel", comment: ""))
+ self.presentTunnelError(error, alertTitle: NSLocalizedString(
+ "START_VPN_TUNNEL_ERROR_ALERT_TITLE",
+ tableName: "AppDelegate",
+ value: "Failed to start the VPN tunnel",
+ comment: ""
+ ))
}
}
}
@@ -558,7 +563,12 @@ extension AppDelegate: ConnectViewControllerDelegate {
case .failure(let error):
self.logger?.error(chainedError: error, message: "Failed to stop the VPN tunnel")
- self.presentTunnelError(error, alertTitle: NSLocalizedString("Failed to stop the VPN tunnel", comment: ""))
+ self.presentTunnelError(error, alertTitle: NSLocalizedString(
+ "STOP_VPN_TUNNEL_ERROR_ALERT_TITLE",
+ tableName: "AppDelegate",
+ value: "Failed to stop the VPN tunnel",
+ comment: ""
+ ))
}
}
}
@@ -576,7 +586,14 @@ extension AppDelegate: ConnectViewControllerDelegate {
preferredStyle: .alert
)
alertController.addAction(
- UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .cancel)
+ UIAlertAction(
+ title: NSLocalizedString(
+ "TUNNEL_ERROR_ALERT_OK_BUTTON",
+ tableName: "AppDelegate",
+ comment: "Dismiss button in tunnel error alert."
+ ),
+ style: .cancel
+ )
)
self.alertPresenter.enqueue(alertController, presentingController: self.rootContainer!)
diff --git a/ios/MullvadVPN/CustomDateComponentsFormatting.swift b/ios/MullvadVPN/CustomDateComponentsFormatting.swift
index 7ef731a999..d9c12dabff 100644
--- a/ios/MullvadVPN/CustomDateComponentsFormatting.swift
+++ b/ios/MullvadVPN/CustomDateComponentsFormatting.swift
@@ -42,7 +42,12 @@ extension CustomDateComponentsFormatting {
let seconds = dateComponents.second ?? 0
if days == 0 && hours == 0 && minutes == 0 && seconds < 60 {
- return NSLocalizedString("Less than a minute", comment: "")
+ return NSLocalizedString(
+ "LESS_THAN_ONE_MINUTE",
+ tableName: "CustomDateComponentsFormatting",
+ value: "Less than a minute",
+ comment: "Phrase used for less than 1 minute duration."
+ )
} else if days == 0 && hours == 23 && minutes >= 30 {
return formatter.string(from: DateComponents(calendar: calendar, day: 1))
} else if days >= 1 && days <= 90 {
diff --git a/ios/MullvadVPN/DisplayChainedError.swift b/ios/MullvadVPN/DisplayChainedError.swift
index c27658b6ce..aeb10a80ef 100644
--- a/ios/MullvadVPN/DisplayChainedError.swift
+++ b/ios/MullvadVPN/DisplayChainedError.swift
@@ -18,7 +18,12 @@ extension RestError: DisplayChainedError {
switch self {
case .network(let urlError):
return String(
- format: NSLocalizedString("Network error: %@", comment: ""),
+ format: NSLocalizedString(
+ "NETWORK_ERROR",
+ tableName: "MullvadRest",
+ value: "Network error: %@",
+ comment: "Network error. Use %@ placeholder to place localized failure description."
+ ),
urlError.localizedDescription
)
case .server(let serverError):
@@ -26,16 +31,36 @@ extension RestError: DisplayChainedError {
return knownErrorDescription
} else {
return String(
- format: NSLocalizedString("Server error: %@", comment: ""),
+ format: NSLocalizedString(
+ "SERVER_ERROR",
+ tableName: "MullvadRest",
+ value: "Server error: %@",
+ comment: "Server error. Use %@ placeholder to place localized failure description."
+ ),
serverError.error ?? "(empty)"
)
}
case .encodePayload:
- return NSLocalizedString("Server request encoding error", comment: "")
+ return NSLocalizedString(
+ "SERVER_REQUEST_ENCODING_ERROR",
+ tableName: "MullvadRest",
+ value: "Server request encoding error",
+ comment: "Failure to encode the server request."
+ )
case .decodeSuccessResponse:
- return NSLocalizedString("Server success response decoding error", comment: "")
+ return NSLocalizedString(
+ "SERVER_SUCCESS_RESPONSE_DECODING_ERROR",
+ tableName: "MullvadRest",
+ value: "Server success response decoding error",
+ comment: "Failure to decode the server success response."
+ )
case .decodeErrorResponse:
- return NSLocalizedString("Server error response decoding error", comment: "")
+ return NSLocalizedString(
+ "SERVER_FAILURE_RESPONSE_DECODING_ERROR",
+ tableName: "MullvadRest",
+ value: "Server error response decoding error",
+ comment: "Failure to decode the server failure response."
+ )
}
}
}
@@ -44,59 +69,165 @@ extension TunnelManager.Error: DisplayChainedError {
var errorChainDescription: String? {
switch self {
case .loadAllVPNConfigurations(let systemError):
- return String(format: NSLocalizedString("Failed to load system VPN configurations: %@", comment: ""), systemError.localizedDescription)
+ return String(
+ format: NSLocalizedString(
+ "LOAD_ALL_VPN_CONFIGURATIONS_ERROR",
+ tableName: "TunnelManager",
+ value: "Failed to load system VPN configurations: %@",
+ comment: ""
+ ),
+ systemError.localizedDescription
+ )
case .reloadVPNConfiguration(let systemError):
- return String(format: NSLocalizedString("Failed to reload a VPN configuration: %@", comment: ""), systemError.localizedDescription)
+ return String(
+ format: NSLocalizedString(
+ "RELOAD_VPN_CONFIGURATIONS_ERROR",
+ tableName: "TunnelManager",
+ value: "Failed to reload a VPN configuration: %@",
+ comment: ""
+ ),
+ systemError.localizedDescription
+ )
case .saveVPNConfiguration(let systemError):
- return String(format: NSLocalizedString("Failed to save a VPN tunnel configuration: %@", comment: ""), systemError.localizedDescription)
+ return String(
+ format: NSLocalizedString(
+ "SAVE_VPN_CONFIGURATION_ERROR",
+ tableName: "TunnelManager",
+ value: "Failed to save a VPN tunnel configuration: %@",
+ comment: ""
+ ),
+ systemError.localizedDescription
+ )
case .obtainPersistentKeychainReference(_):
- return NSLocalizedString("Failed to obtain the persistent keychain reference for the VPN configuration", comment: "")
+ return NSLocalizedString(
+ "OBTAIN_PERSISTENT_KEYCHAIN_REFERENCE_ERROR",
+ tableName: "TunnelManager",
+ value: "Failed to obtain the persistent keychain reference for the VPN configuration",
+ comment: ""
+ )
case .startVPNTunnel(let systemError):
- return String(format: NSLocalizedString("System error when starting the VPN tunnel: %@", comment: ""), systemError.localizedDescription)
+ return String(
+ format: NSLocalizedString(
+ "START_VPN_TUNNEL_ERROR",
+ tableName: "TunnelManager",
+ value: "System error when starting the VPN tunnel: %@",
+ comment: ""
+ ),
+ systemError.localizedDescription
+ )
case .removeVPNConfiguration(let systemError):
- return String(format: NSLocalizedString("Failed to remove the system VPN configuration: %@", comment: ""), systemError.localizedDescription)
+ return String(
+ format: NSLocalizedString(
+ "REMOVE_VPN_CONFIGURATION_ERROR",
+ tableName: "TunnelManager",
+ value: "Failed to remove the system VPN configuration: %@",
+ comment: ""
+ ),
+ systemError.localizedDescription
+ )
case .removeInconsistentVPNConfiguration(let systemError):
- return String(format: NSLocalizedString("Failed to remove the outdated system VPN configuration: %@", comment: ""), systemError.localizedDescription)
+ return String(
+ format: NSLocalizedString(
+ "REMOVE_INCONSISTENT_VPN_CONFIGURATION",
+ tableName: "TunnelManager",
+ value: "Failed to remove the outdated system VPN configuration: %@",
+ comment: ""
+ ),
+ systemError.localizedDescription
+ )
case .readTunnelSettings(_):
- return NSLocalizedString("Failed to read tunnel settings", comment: "")
+ return NSLocalizedString(
+ "READ_TUNNEL_SETTINGS_ERROR",
+ tableName: "TunnelManager",
+ value: "Failed to read tunnel settings",
+ comment: ""
+ )
case .addTunnelSettings(_):
- return NSLocalizedString("Failed to add tunnel settings", comment: "")
+ return NSLocalizedString(
+ "ADD_TUNNEL_SETTINGS_ERROR",
+ tableName: "TunnelManager",
+ value: "Failed to add tunnel settings",
+ comment: ""
+ )
case .updateTunnelSettings(_):
- return NSLocalizedString("Failed to update tunnel settings", comment: "")
+ return NSLocalizedString(
+ "UPDATE_TUNNEL_SETTINGS_ERROR",
+ tableName: "TunnelManager",
+ value: "Failed to update tunnel settings",
+ comment: ""
+ )
case .removeTunnelSettings(_):
- return NSLocalizedString("Failed to remove tunnel settings", comment: "")
+ return NSLocalizedString(
+ "REMOVE_TUNNEL_SETTINGS_ERROR",
+ tableName: "TunnelManager",
+ value: "Failed to remove tunnel settings",
+ comment: ""
+ )
case .migrateTunnelSettings(_):
- return NSLocalizedString("Failed to migrate tunnel settings", comment: "")
+ return NSLocalizedString(
+ "MIGRATE_TUNNEL_SETTINGS_ERROR",
+ tableName: "TunnelManager",
+ value: "Failed to migrate tunnel settings",
+ comment: ""
+ )
case .pushWireguardKey(let restError):
let reason = restError.errorChainDescription ?? ""
- var message = String(format: NSLocalizedString("Failed to send the WireGuard key to server: %@", comment: ""), reason)
+ var message = String(
+ format: NSLocalizedString(
+ "PUSH_WIREGUARD_KEY_ERROR",
+ tableName: "TunnelManager",
+ value: "Failed to send the WireGuard key to server: %@",
+ comment: ""
+ ),
+ reason
+ )
if case .server(.keyLimitReached) = restError {
+ // TODO: maybe use `restError.recoverySuggestion` instead?
message.append("\n\n")
- message.append(NSLocalizedString("Remove unused WireGuard keys and try again", comment: ""))
+ message.append(NSLocalizedString(
+ "PUSH_WIREGUARD_KEY_RECOVERY_SUGGESTION",
+ tableName: "TunnelManager",
+ value: "Remove unused WireGuard keys and try again",
+ comment: ""
+ ))
}
return message
case .replaceWireguardKey(let restError):
let reason = restError.errorChainDescription ?? ""
- var message = String(format: NSLocalizedString("Failed to replace the WireGuard key on server: %@", comment: ""), reason)
+ var message = String(
+ format: NSLocalizedString(
+ "REPLACE_WIREGUARD_KEY_ERROR",
+ tableName: "TunnelManager",
+ value: "Failed to replace the WireGuard key on server: %@",
+ comment: ""
+ ),
+ reason
+ )
if case .server(.keyLimitReached) = restError {
+ // TODO: maybe use `restError.recoverySuggestion` instead?
message.append("\n\n")
- message.append(NSLocalizedString("Remove unused WireGuard keys and try again", comment: ""))
+ message.append(NSLocalizedString(
+ "REPLACE_WIREGUARD_KEY_RECOVERY_SUGGESTION",
+ tableName: "TunnelManager",
+ value: "Remove unused WireGuard keys and try again",
+ comment: "")
+ )
}
return message
@@ -108,10 +239,23 @@ extension TunnelManager.Error: DisplayChainedError {
case .verifyWireguardKey(let restError):
let reason = restError.errorChainDescription ?? ""
- return String(format: NSLocalizedString("Failed to verify the WireGuard key on server: %@", comment: ""), reason)
+ return String(
+ format: NSLocalizedString(
+ "VERIFY_WIREGUARD_KEY_ERROR",
+ tableName: "TunnelManager",
+ value: "Failed to verify the WireGuard key on server: %@",
+ comment: ""
+ ),
+ reason
+ )
case .missingAccount:
- return NSLocalizedString("Internal error: missing account", comment: "")
+ return NSLocalizedString(
+ "MISSING_ACCOUNT_INTERNAL_ERROR",
+ tableName: "TunnelManager",
+ value: "Internal error: missing account",
+ comment: ""
+ )
}
}
}
@@ -132,15 +276,40 @@ extension SKError: LocalizedError {
public var errorDescription: String? {
switch self.code {
case .unknown:
- return NSLocalizedString("Unknown error", comment: "")
+ return NSLocalizedString(
+ "UNKNOWN_ERROR",
+ tableName: "StoreKitErrors",
+ value: "Unknown error",
+ comment: ""
+ )
case .clientInvalid:
- return NSLocalizedString("Client is not allowed to issue the request", comment: "")
+ return NSLocalizedString(
+ "CLIENT_INVALID",
+ tableName: "StoreKitErrors",
+ value: "Client is not allowed to issue the request",
+ comment: ""
+ )
case .paymentCancelled:
- return NSLocalizedString("User cancelled the request", comment: "")
+ return NSLocalizedString(
+ "PAYMENT_CANCELLED",
+ tableName: "StoreKitErrors",
+ value: "User cancelled the request",
+ comment: ""
+ )
case .paymentInvalid:
- return NSLocalizedString("Invalid purchase identifier", comment: "")
+ return NSLocalizedString(
+ "PAYMENT_INVALID",
+ tableName: "StoreKitErrors",
+ value: "Invalid purchase identifier",
+ comment: ""
+ )
case .paymentNotAllowed:
- return NSLocalizedString("This device is not allowed to make the payment", comment: "")
+ return NSLocalizedString(
+ "PAYMENT_NOT_ALLOWED",
+ tableName: "StoreKitErrors",
+ value: "This device is not allowed to make the payment",
+ comment: ""
+ )
default:
return self.localizedDescription
}
@@ -151,29 +320,64 @@ extension AppStorePaymentManager.Error: DisplayChainedError {
var errorChainDescription: String? {
switch self {
case .noAccountSet:
- return NSLocalizedString("Internal error: account is not set", comment: "")
+ return NSLocalizedString(
+ "NO_ACCOUNT_SET_ERROR",
+ tableName: "AppStorePaymentManager",
+ value: "Internal error: account is not set",
+ comment: ""
+ )
case .readReceipt(let readReceiptError):
switch readReceiptError {
case .refresh(let storeError):
let skErrorMessage = (storeError as? SKError)?.errorDescription ?? storeError.localizedDescription
- return String(format: NSLocalizedString("Cannot refresh the AppStore receipt: %@", comment: ""), skErrorMessage)
+ return String(
+ format: NSLocalizedString(
+ "REFRESH_RECEIPT_ERROR",
+ tableName: "AppStorePaymentManager",
+ value: "Cannot refresh the AppStore receipt: %@",
+ comment: ""
+ ),
+ skErrorMessage
+ )
case .io(let ioError):
- return String(format: NSLocalizedString("Cannot read the AppStore receipt from disk: %@", comment: ""), ioError.localizedDescription)
+ return String(
+ format: NSLocalizedString(
+ "READ_RECEIPT_ERROR",
+ tableName: "AppStorePaymentManager",
+ value: "Cannot read the AppStore receipt from disk: %@",
+ comment: ""
+ ),
+ ioError.localizedDescription
+ )
case .doesNotExist:
- return NSLocalizedString("AppStore receipt is not found on disk.", comment: "")
+ return NSLocalizedString(
+ "RECEIPT_NOT_FOUND_ERROR",
+ tableName: "AppStorePaymentManager",
+ value: "AppStore receipt is not found on disk.",
+ comment: ""
+ )
}
case .sendReceipt(let restError):
let reason = restError.errorChainDescription ?? ""
- let format = NSLocalizedString(#"""
-Failed to send the receipt to server: %@
-
-Please retry by using the "Restore purchases" button.
-"""#, comment: "")
-
- return String(format: format, reason)
+ let errorFormat = NSLocalizedString(
+ "SEND_RECEIPT_ERROR",
+ tableName: "AppStorePaymentManager",
+ value: "Failed to send the receipt to server: %@",
+ comment: ""
+ )
+ let recoverySuggestion = NSLocalizedString(
+ "SEND_RECEIPT_RECOVERY_SUGGESTION",
+ tableName: "AppStorePaymentManager",
+ value: "Please retry by using the \"Restore purchases\" button.",
+ comment: ""
+ )
+ var errorString = String(format: errorFormat, reason)
+ errorString.append("\n\n")
+ errorString.append(recoverySuggestion)
+ return errorString
case .storePayment(let storeError):
return (storeError as? SKError)?.errorDescription ?? storeError.localizedDescription
diff --git a/ios/MullvadVPN/HeaderBarView.swift b/ios/MullvadVPN/HeaderBarView.swift
index 0912e5c107..4dd1d68ce5 100644
--- a/ios/MullvadVPN/HeaderBarView.swift
+++ b/ios/MullvadVPN/HeaderBarView.swift
@@ -33,7 +33,12 @@ class HeaderBarView: UIView {
settingsButton.setImage(UIImage(named: "IconSettings"), for: .normal)
settingsButton.translatesAutoresizingMaskIntoConstraints = false
settingsButton.accessibilityIdentifier = "SettingsButton"
- settingsButton.accessibilityLabel = NSLocalizedString("HEADER_BAR_SETTINGS_BUTTON_ACCESSIBILITY_LABEL", comment: "")
+ settingsButton.accessibilityLabel = NSLocalizedString(
+ "HEADER_BAR_SETTINGS_BUTTON_ACCESSIBILITY_LABEL",
+ tableName: "HeaderBar",
+ value: "Settings",
+ comment: ""
+ )
return settingsButton
}
diff --git a/ios/MullvadVPN/MullvadRest.swift b/ios/MullvadVPN/MullvadRest.swift
index 2fc99b4d64..0e7abdc239 100644
--- a/ios/MullvadVPN/MullvadRest.swift
+++ b/ios/MullvadVPN/MullvadRest.swift
@@ -69,9 +69,19 @@ struct ServerErrorResponse: LocalizedError, Decodable, Equatable {
var errorDescription: String? {
switch code {
case Code.keyLimitReached.rawValue:
- return NSLocalizedString("Too many WireGuard keys in use.", comment: "")
+ return NSLocalizedString(
+ "KEY_LIMIT_REACHED_ERROR_DESCRIPTION",
+ tableName: "MullvadRest",
+ value: "Too many WireGuard keys in use.",
+ comment: ""
+ )
case Code.invalidAccount.rawValue:
- return NSLocalizedString("Invalid account.", comment: "")
+ return NSLocalizedString(
+ "INVALID_ACCOUNT_ERROR_DESCRIPTION",
+ tableName: "MullvadRest",
+ value: "Invalid account.",
+ comment: ""
+ )
default:
return nil
}
@@ -80,7 +90,12 @@ struct ServerErrorResponse: LocalizedError, Decodable, Equatable {
var recoverySuggestion: String? {
switch code {
case Code.keyLimitReached.rawValue:
- return NSLocalizedString("Please visit the website to revoke a key before login is possible.", comment: "")
+ return NSLocalizedString(
+ "KEY_LIMIT_REACHED_ERROR_RECOVERY_SUGGESTION",
+ tableName: "MullvadRest",
+ value: "Please visit the website to revoke a key before login is possible.",
+ comment: ""
+ )
default:
return nil
}
diff --git a/ios/MullvadVPN/UIBarButtonItem+KeyboardNavigation.swift b/ios/MullvadVPN/UIBarButtonItem+KeyboardNavigation.swift
index 85d2d258a6..ee81308440 100644
--- a/ios/MullvadVPN/UIBarButtonItem+KeyboardNavigation.swift
+++ b/ios/MullvadVPN/UIBarButtonItem+KeyboardNavigation.swift
@@ -16,9 +16,19 @@ extension UIBarButtonItem {
fileprivate var localizedTitle: String {
switch self {
case .previous:
- return NSLocalizedString("Previous", comment: "Keyboard navigation toolbar")
+ return NSLocalizedString(
+ "PREVIOUS_BUTTON_TITLE",
+ tableName: "KeyboardNavigation",
+ value: "Previous",
+ comment: "Previous button"
+ )
case .next:
- return NSLocalizedString("Next", comment: "Keyboard navigation toolbar")
+ return NSLocalizedString(
+ "NEXT_BUTTON_TITLE",
+ tableName: "KeyboardNavigation",
+ value: "Next",
+ comment: "Next button"
+ )
}
}
diff --git a/ios/MullvadVPN/en.lproj/AccountInput.strings b/ios/MullvadVPN/en.lproj/AccountInput.strings
new file mode 100644
index 0000000000..51d09cc7e4
--- /dev/null
+++ b/ios/MullvadVPN/en.lproj/AccountInput.strings
@@ -0,0 +1,2 @@
+/* Accessibility label for submit button in account token input. */
+"ACCOUNT_INPUT_LOGIN_BUTTON_ACCESSIBILITY_LABEL" = "Log in";
diff --git a/ios/MullvadVPN/en.lproj/AppDelegate.strings b/ios/MullvadVPN/en.lproj/AppDelegate.strings
new file mode 100644
index 0000000000..4b22937c5a
--- /dev/null
+++ b/ios/MullvadVPN/en.lproj/AppDelegate.strings
@@ -0,0 +1,8 @@
+/* No comment provided by engineer. */
+"START_VPN_TUNNEL_ERROR_ALERT_TITLE" = "Failed to start the VPN tunnel";
+
+/* No comment provided by engineer. */
+"STOP_VPN_TUNNEL_ERROR_ALERT_TITLE" = "Failed to stop the VPN tunnel";
+
+/* Dismiss button in tunnel error alert. */
+"TUNNEL_ERROR_ALERT_OK_BUTTON" = "OK";
diff --git a/ios/MullvadVPN/en.lproj/AppStorePaymentManager.strings b/ios/MullvadVPN/en.lproj/AppStorePaymentManager.strings
new file mode 100644
index 0000000000..54c9c348c4
--- /dev/null
+++ b/ios/MullvadVPN/en.lproj/AppStorePaymentManager.strings
@@ -0,0 +1,17 @@
+/* No comment provided by engineer. */
+"NO_ACCOUNT_SET_ERROR" = "Internal error: account is not set";
+
+/* No comment provided by engineer. */
+"READ_RECEIPT_ERROR" = "Cannot read the AppStore receipt from disk: %@";
+
+/* No comment provided by engineer. */
+"RECEIPT_NOT_FOUND_ERROR" = "AppStore receipt is not found on disk.";
+
+/* No comment provided by engineer. */
+"REFRESH_RECEIPT_ERROR" = "Cannot refresh the AppStore receipt: %@";
+
+/* No comment provided by engineer. */
+"SEND_RECEIPT_ERROR" = "Failed to send the receipt to server: %@";
+
+/* No comment provided by engineer. */
+"SEND_RECEIPT_RECOVERY_SUGGESTION" = "Please retry by using the \"Restore purchases\" button.";
diff --git a/ios/MullvadVPN/en.lproj/CustomDateComponentsFormatting.strings b/ios/MullvadVPN/en.lproj/CustomDateComponentsFormatting.strings
new file mode 100644
index 0000000000..8f931d4968
--- /dev/null
+++ b/ios/MullvadVPN/en.lproj/CustomDateComponentsFormatting.strings
@@ -0,0 +1,2 @@
+/* Phrase used for less than 1 minute duration. */
+"LESS_THAN_ONE_MINUTE" = "Less than a minute";
diff --git a/ios/MullvadVPN/en.lproj/HeaderBar.strings b/ios/MullvadVPN/en.lproj/HeaderBar.strings
new file mode 100644
index 0000000000..01b3aea007
--- /dev/null
+++ b/ios/MullvadVPN/en.lproj/HeaderBar.strings
@@ -0,0 +1,2 @@
+/* No comment provided by engineer. */
+"HEADER_BAR_SETTINGS_BUTTON_ACCESSIBILITY_LABEL" = "Settings";
diff --git a/ios/MullvadVPN/en.lproj/KeyboardNavigation.strings b/ios/MullvadVPN/en.lproj/KeyboardNavigation.strings
new file mode 100644
index 0000000000..753599eec9
--- /dev/null
+++ b/ios/MullvadVPN/en.lproj/KeyboardNavigation.strings
@@ -0,0 +1,5 @@
+/* Next button */
+"NEXT_BUTTON_TITLE" = "Next";
+
+/* Previous button */
+"PREVIOUS_BUTTON_TITLE" = "Previous";
diff --git a/ios/MullvadVPN/en.lproj/MullvadRest.strings b/ios/MullvadVPN/en.lproj/MullvadRest.strings
new file mode 100644
index 0000000000..f5a83c67a3
--- /dev/null
+++ b/ios/MullvadVPN/en.lproj/MullvadRest.strings
@@ -0,0 +1,23 @@
+/* No comment provided by engineer. */
+"INVALID_ACCOUNT_ERROR_DESCRIPTION" = "Invalid account.";
+
+/* No comment provided by engineer. */
+"KEY_LIMIT_REACHED_ERROR_DESCRIPTION" = "Too many WireGuard keys in use.";
+
+/* No comment provided by engineer. */
+"KEY_LIMIT_REACHED_ERROR_RECOVERY_SUGGESTION" = "Please visit the website to revoke a key before login is possible.";
+
+/* Network error. Use %@ placeholder to place localized failure description. */
+"NETWORK_ERROR" = "Network error: %@";
+
+/* Server error. Use %@ placeholder to place localized failure description. */
+"SERVER_ERROR" = "Server error: %@";
+
+/* Failure to decode the server failure response. */
+"SERVER_FAILURE_RESPONSE_DECODING_ERROR" = "Server error response decoding error";
+
+/* Failure to encode the server request. */
+"SERVER_REQUEST_ENCODING_ERROR" = "Server request encoding error";
+
+/* Failure to decode the server success response. */
+"SERVER_SUCCESS_RESPONSE_DECODING_ERROR" = "Server success response decoding error";
diff --git a/ios/MullvadVPN/en.lproj/StoreKitErrors.strings b/ios/MullvadVPN/en.lproj/StoreKitErrors.strings
new file mode 100644
index 0000000000..99ac40881a
--- /dev/null
+++ b/ios/MullvadVPN/en.lproj/StoreKitErrors.strings
@@ -0,0 +1,14 @@
+/* No comment provided by engineer. */
+"CLIENT_INVALID" = "Client is not allowed to issue the request";
+
+/* No comment provided by engineer. */
+"PAYMENT_CANCELLED" = "User cancelled the request";
+
+/* No comment provided by engineer. */
+"PAYMENT_INVALID" = "Invalid purchase identifier";
+
+/* No comment provided by engineer. */
+"PAYMENT_NOT_ALLOWED" = "This device is not allowed to make the payment";
+
+/* No comment provided by engineer. */
+"UNKNOWN_ERROR" = "Unknown error";
diff --git a/ios/MullvadVPN/en.lproj/TunnelManager.strings b/ios/MullvadVPN/en.lproj/TunnelManager.strings
new file mode 100644
index 0000000000..93ce286a33
--- /dev/null
+++ b/ios/MullvadVPN/en.lproj/TunnelManager.strings
@@ -0,0 +1,53 @@
+/* No comment provided by engineer. */
+"ADD_TUNNEL_SETTINGS_ERROR" = "Failed to add tunnel settings";
+
+/* No comment provided by engineer. */
+"LOAD_ALL_VPN_CONFIGURATIONS_ERROR" = "Failed to load system VPN configurations: %@";
+
+/* No comment provided by engineer. */
+"MIGRATE_TUNNEL_SETTINGS_ERROR" = "Failed to migrate tunnel settings";
+
+/* No comment provided by engineer. */
+"MISSING_ACCOUNT_INTERNAL_ERROR" = "Internal error: missing account";
+
+/* No comment provided by engineer. */
+"OBTAIN_PERSISTENT_KEYCHAIN_REFERENCE_ERROR" = "Failed to obtain the persistent keychain reference for the VPN configuration";
+
+/* No comment provided by engineer. */
+"PUSH_WIREGUARD_KEY_ERROR" = "Failed to send the WireGuard key to server: %@";
+
+/* No comment provided by engineer. */
+"PUSH_WIREGUARD_KEY_RECOVERY_SUGGESTION" = "Remove unused WireGuard keys and try again";
+
+/* No comment provided by engineer. */
+"READ_TUNNEL_SETTINGS_ERROR" = "Failed to read tunnel settings";
+
+/* No comment provided by engineer. */
+"RELOAD_VPN_CONFIGURATIONS_ERROR" = "Failed to reload a VPN configuration: %@";
+
+/* No comment provided by engineer. */
+"REMOVE_INCONSISTENT_VPN_CONFIGURATION" = "Failed to remove the outdated system VPN configuration: %@";
+
+/* No comment provided by engineer. */
+"REMOVE_TUNNEL_SETTINGS_ERROR" = "Failed to remove tunnel settings";
+
+/* No comment provided by engineer. */
+"REMOVE_VPN_CONFIGURATION_ERROR" = "Failed to remove the system VPN configuration: %@";
+
+/* No comment provided by engineer. */
+"REPLACE_WIREGUARD_KEY_ERROR" = "Failed to replace the WireGuard key on server: %@";
+
+/* No comment provided by engineer. */
+"REPLACE_WIREGUARD_KEY_RECOVERY_SUGGESTION" = "Remove unused WireGuard keys and try again";
+
+/* No comment provided by engineer. */
+"SAVE_VPN_CONFIGURATION_ERROR" = "Failed to save a VPN tunnel configuration: %@";
+
+/* No comment provided by engineer. */
+"START_VPN_TUNNEL_ERROR" = "System error when starting the VPN tunnel: %@";
+
+/* No comment provided by engineer. */
+"UPDATE_TUNNEL_SETTINGS_ERROR" = "Failed to update tunnel settings";
+
+/* No comment provided by engineer. */
+"VERIFY_WIREGUARD_KEY_ERROR" = "Failed to verify the WireGuard key on server: %@";