mirror of
https://github.com/aphrody-labs/mullvadvpn-app.git
synced 2026-10-13 22:10:39 +00:00
Extract strings to translate
This commit is contained in:
1 parent
40db8f24fc
commit
1926fe29fb
148 files changed
+3991
-2666
No files matched your search
File diff suppressed because it is too large.
Load diff
@@ -1,7 +0,0 @@
|
||||
/*
|
||||
Localizable.strings
|
||||
MullvadVPN
|
||||
|
||||
Created by Mojgan on 2025-07-16.
|
||||
Copyright © 2025 Mullvad VPN AB. All rights reserved.
|
||||
*/
|
||||
@@ -1,7 +0,0 @@
|
||||
/*
|
||||
Localizable.strings
|
||||
MullvadVPN
|
||||
|
||||
Created by Mojgan on 2025-07-16.
|
||||
Copyright © 2025 Mullvad VPN AB. All rights reserved.
|
||||
*/
|
||||
@@ -1,7 +0,0 @@
|
||||
/*
|
||||
Localizable.strings
|
||||
MullvadVPN
|
||||
|
||||
Created by Mojgan on 2025-07-16.
|
||||
Copyright © 2025 Mullvad VPN AB. All rights reserved.
|
||||
*/
|
||||
@@ -1,7 +0,0 @@
|
||||
/*
|
||||
Localizable.strings
|
||||
MullvadVPN
|
||||
|
||||
Created by Mojgan on 2025-07-16.
|
||||
Copyright © 2025 Mullvad VPN AB. All rights reserved.
|
||||
*/
|
||||
@@ -1,7 +0,0 @@
|
||||
/*
|
||||
Localizable.strings
|
||||
MullvadVPN
|
||||
|
||||
Created by Mojgan on 2025-07-16.
|
||||
Copyright © 2025 Mullvad VPN AB. All rights reserved.
|
||||
*/
|
||||
@@ -1,7 +0,0 @@
|
||||
/*
|
||||
Localizable.strings
|
||||
MullvadVPN
|
||||
|
||||
Created by Mojgan on 2025-07-16.
|
||||
Copyright © 2025 Mullvad VPN AB. All rights reserved.
|
||||
*/
|
||||
@@ -1,7 +0,0 @@
|
||||
/*
|
||||
Localizable.strings
|
||||
MullvadVPN
|
||||
|
||||
Created by Mojgan on 2025-07-16.
|
||||
Copyright © 2025 Mullvad VPN AB. All rights reserved.
|
||||
*/
|
||||
@@ -1,7 +0,0 @@
|
||||
/*
|
||||
Localizable.strings
|
||||
MullvadVPN
|
||||
|
||||
Created by Mojgan on 2025-07-16.
|
||||
Copyright © 2025 Mullvad VPN AB. All rights reserved.
|
||||
*/
|
||||
@@ -1,7 +0,0 @@
|
||||
/*
|
||||
Localizable.strings
|
||||
MullvadVPN
|
||||
|
||||
Created by Mojgan on 2025-07-16.
|
||||
Copyright © 2025 Mullvad VPN AB. All rights reserved.
|
||||
*/
|
||||
@@ -1,7 +0,0 @@
|
||||
/*
|
||||
Localizable.strings
|
||||
MullvadVPN
|
||||
|
||||
Created by Mojgan on 2025-07-16.
|
||||
Copyright © 2025 Mullvad VPN AB. All rights reserved.
|
||||
*/
|
||||
@@ -1,7 +0,0 @@
|
||||
/*
|
||||
Localizable.strings
|
||||
MullvadVPN
|
||||
|
||||
Created by Mojgan on 2025-07-16.
|
||||
Copyright © 2025 Mullvad VPN AB. All rights reserved.
|
||||
*/
|
||||
@@ -1,7 +0,0 @@
|
||||
/*
|
||||
Localizable.strings
|
||||
MullvadVPN
|
||||
|
||||
Created by Mojgan on 2025-07-16.
|
||||
Copyright © 2025 Mullvad VPN AB. All rights reserved.
|
||||
*/
|
||||
@@ -1,7 +0,0 @@
|
||||
/*
|
||||
Localizable.strings
|
||||
MullvadVPN
|
||||
|
||||
Created by Mojgan on 2025-07-16.
|
||||
Copyright © 2025 Mullvad VPN AB. All rights reserved.
|
||||
*/
|
||||
@@ -1,7 +0,0 @@
|
||||
/*
|
||||
Localizable.strings
|
||||
MullvadVPN
|
||||
|
||||
Created by Mojgan on 2025-07-16.
|
||||
Copyright © 2025 Mullvad VPN AB. All rights reserved.
|
||||
*/
|
||||
@@ -1,7 +0,0 @@
|
||||
/*
|
||||
Localizable.strings
|
||||
MullvadVPN
|
||||
|
||||
Created by Mojgan on 2025-07-16.
|
||||
Copyright © 2025 Mullvad VPN AB. All rights reserved.
|
||||
*/
|
||||
@@ -1,7 +0,0 @@
|
||||
/*
|
||||
Localizable.strings
|
||||
MullvadVPN
|
||||
|
||||
Created by Mojgan on 2025-07-16.
|
||||
Copyright © 2025 Mullvad VPN AB. All rights reserved.
|
||||
*/
|
||||
@@ -1,7 +0,0 @@
|
||||
/*
|
||||
Localizable.strings
|
||||
MullvadVPN
|
||||
|
||||
Created by Mojgan on 2025-07-16.
|
||||
Copyright © 2025 Mullvad VPN AB. All rights reserved.
|
||||
*/
|
||||
@@ -1,7 +0,0 @@
|
||||
/*
|
||||
Localizable.strings
|
||||
MullvadVPN
|
||||
|
||||
Created by Mojgan on 2025-07-16.
|
||||
Copyright © 2025 Mullvad VPN AB. All rights reserved.
|
||||
*/
|
||||
@@ -1,7 +0,0 @@
|
||||
/*
|
||||
Localizable.strings
|
||||
MullvadVPN
|
||||
|
||||
Created by Mojgan on 2025-07-16.
|
||||
Copyright © 2025 Mullvad VPN AB. All rights reserved.
|
||||
*/
|
||||
@@ -1,7 +0,0 @@
|
||||
/*
|
||||
Localizable.strings
|
||||
MullvadVPN
|
||||
|
||||
Created by Mojgan on 2025-07-16.
|
||||
Copyright © 2025 Mullvad VPN AB. All rights reserved.
|
||||
*/
|
||||
@@ -18,20 +18,10 @@ public enum CustomRelayListError: LocalizedError, Hashable {
|
||||
public var errorDescription: String? {
|
||||
switch self {
|
||||
case .duplicateName:
|
||||
NSLocalizedString(
|
||||
"DUPLICATE_CUSTOM_LISTS_ERROR",
|
||||
tableName: "CustomLists",
|
||||
value: "A custom list with this name exists, please choose a unique name.",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("A custom list with this name exists, please choose a unique name.", comment: "")
|
||||
case .nameTooLong:
|
||||
String(
|
||||
format: NSLocalizedString(
|
||||
"CUSTOM_LIST_NAME_TOO_LONG_ERROR",
|
||||
tableName: "CustomLists",
|
||||
value: "Name should be no longer than %i characters.",
|
||||
comment: ""
|
||||
),
|
||||
format: NSLocalizedString("Name should be no longer than %i characters.", comment: ""),
|
||||
NameInputFormatter.maxLength
|
||||
)
|
||||
}
|
||||
|
||||
@@ -80,12 +80,7 @@ public enum WireGuardObfuscationUdpOverTcpPort: Codable, Equatable, CustomString
|
||||
public var description: String {
|
||||
switch self {
|
||||
case .automatic:
|
||||
NSLocalizedString(
|
||||
"WIREGUARD_OBFUSCATION_UDP_TCP_PORT_AUTOMATIC",
|
||||
tableName: "VPNSettings",
|
||||
value: "Automatic",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Automatic", comment: "")
|
||||
case .port80:
|
||||
"80"
|
||||
case .port5001:
|
||||
@@ -110,12 +105,7 @@ public enum WireGuardObfuscationShadowsocksPort: Codable, Equatable, CustomStrin
|
||||
public var description: String {
|
||||
switch self {
|
||||
case .automatic:
|
||||
NSLocalizedString(
|
||||
"WIREGUARD_OBFUSCATION_SHADOWSOCKS_PORT_AUTOMATIC",
|
||||
tableName: "VPNSettings",
|
||||
value: "Automatic",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Automatic", comment: "")
|
||||
case let .custom(port):
|
||||
String(port)
|
||||
}
|
||||
|
||||
@@ -48,9 +48,9 @@ public extension AccessMethodKind {
|
||||
case .direct, .bridges, .encryptedDNS:
|
||||
""
|
||||
case .shadowsocks:
|
||||
NSLocalizedString("SHADOWSOCKS", tableName: "APIAccess", value: "Shadowsocks", comment: "")
|
||||
NSLocalizedString("Shadowsocks", comment: "")
|
||||
case .socks5:
|
||||
NSLocalizedString("SOCKS_V5", tableName: "APIAccess", value: "Socks5", comment: "").uppercased()
|
||||
NSLocalizedString("Socks5", comment: "").uppercased()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -961,6 +961,7 @@
|
||||
F041BE532C9878B60083EC28 /* ConnectionConfigurationBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F041BE522C9878B60083EC28 /* ConnectionConfigurationBuilder.swift */; };
|
||||
F04413612BA45CD70018A6EE /* CustomListLocationNodeBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F04413602BA45CD70018A6EE /* CustomListLocationNodeBuilder.swift */; };
|
||||
F04413622BA45CE30018A6EE /* CustomListLocationNodeBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F04413602BA45CD70018A6EE /* CustomListLocationNodeBuilder.swift */; };
|
||||
F04789ED2E3A4FEB0011E3A5 /* ApplicationLanguage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F04789EB2E3A4FEB0011E3A5 /* ApplicationLanguage.swift */; };
|
||||
F048BFA22D31843000251CB9 /* ChangeLogModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F048BFA12D31842B00251CB9 /* ChangeLogModel.swift */; };
|
||||
F04AF92D2C466013004A8314 /* EphemeralPeerNegotiationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = F04AF92C2C466013004A8314 /* EphemeralPeerNegotiationState.swift */; };
|
||||
F04FBE612A8379EE009278D7 /* AppPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = F04FBE602A8379EE009278D7 /* AppPreferences.swift */; };
|
||||
@@ -980,16 +981,6 @@
|
||||
F05769BB2C6661EE00D9778B /* TunnelSettingsStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = F05769BA2C6661EE00D9778B /* TunnelSettingsStrategy.swift */; };
|
||||
F05919752C45194B00C301F3 /* EphemeralPeerKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = F05919742C45194B00C301F3 /* EphemeralPeerKey.swift */; };
|
||||
F05919802C45515200C301F3 /* EphemeralPeerExchangeActor.swift in Sources */ = {isa = PBXBuildFile; fileRef = A948809A2BC9308D0090A44C /* EphemeralPeerExchangeActor.swift */; };
|
||||
F05DCE9E2E38C563009A9B85 /* AppLanguage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F05DCE872E38C563009A9B85 /* AppLanguage.swift */; };
|
||||
F05DCE9F2E38C563009A9B85 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F05DCE9C2E38C563009A9B85 /* Localizable.strings */; };
|
||||
F05DCEA02E38C5F1009A9B85 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F05DCE9C2E38C563009A9B85 /* Localizable.strings */; };
|
||||
F05DCEA12E38C5F1009A9B85 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F05DCE9C2E38C563009A9B85 /* Localizable.strings */; };
|
||||
F05DCEA22E38C5F1009A9B85 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F05DCE9C2E38C563009A9B85 /* Localizable.strings */; };
|
||||
F05DCEA32E38C5F1009A9B85 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F05DCE9C2E38C563009A9B85 /* Localizable.strings */; };
|
||||
F05DCEA42E38C5F1009A9B85 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F05DCE9C2E38C563009A9B85 /* Localizable.strings */; };
|
||||
F05DCEA52E38C5F1009A9B85 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F05DCE9C2E38C563009A9B85 /* Localizable.strings */; };
|
||||
F05DCEA62E38C5F1009A9B85 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F05DCE9C2E38C563009A9B85 /* Localizable.strings */; };
|
||||
F05DCEA72E38C5F1009A9B85 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F05DCE9C2E38C563009A9B85 /* Localizable.strings */; };
|
||||
F05F39972B21C735006E60A7 /* RelayCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5820675A26E6576800655B05 /* RelayCache.swift */; };
|
||||
F05F39982B21C73C006E60A7 /* CachedRelays.swift in Sources */ = {isa = PBXBuildFile; fileRef = 585DA87626B024A600B8C587 /* CachedRelays.swift */; };
|
||||
F06045E62B231EB700B2D37A /* URLSessionTransport.swift in Sources */ = {isa = PBXBuildFile; fileRef = F06045E52B231EB700B2D37A /* URLSessionTransport.swift */; };
|
||||
@@ -1069,6 +1060,7 @@
|
||||
F0B894F32BF7526700817A42 /* RelaySelector+Wireguard.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0B894F22BF7526700817A42 /* RelaySelector+Wireguard.swift */; };
|
||||
F0B894F52BF7528700817A42 /* RelaySelector+Shadowsocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0B894F42BF7528700817A42 /* RelaySelector+Shadowsocks.swift */; };
|
||||
F0BE65372B9F136A005CC385 /* LocationSectionHeaderFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0BE65362B9F136A005CC385 /* LocationSectionHeaderFooterView.swift */; };
|
||||
F0C1374F2E45EF1F006307BF /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = F0C1374E2E45EF1F006307BF /* Localizable.xcstrings */; };
|
||||
F0C13FE42C64F7CB00BD087D /* DAITASettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0C13FE32C64F7CB00BD087D /* DAITASettings.swift */; };
|
||||
F0C13FE62C64FB3400BD087D /* TunnelSettingsV6.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0C13FE52C64FB3400BD087D /* TunnelSettingsV6.swift */; };
|
||||
F0C2AEFD2A0BB5CC00986207 /* NotificationProviderIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0C2AEFC2A0BB5CC00986207 /* NotificationProviderIdentifier.swift */; };
|
||||
@@ -2431,6 +2423,7 @@
|
||||
F041BE4E2C983C2B0083EC28 /* DAITASettingsPromptItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DAITASettingsPromptItem.swift; sourceTree = "<group>"; };
|
||||
F041BE522C9878B60083EC28 /* ConnectionConfigurationBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionConfigurationBuilder.swift; sourceTree = "<group>"; };
|
||||
F04413602BA45CD70018A6EE /* CustomListLocationNodeBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomListLocationNodeBuilder.swift; sourceTree = "<group>"; };
|
||||
F04789EB2E3A4FEB0011E3A5 /* ApplicationLanguage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationLanguage.swift; sourceTree = "<group>"; };
|
||||
F048BFA12D31842B00251CB9 /* ChangeLogModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChangeLogModel.swift; sourceTree = "<group>"; };
|
||||
F04AF92C2C466013004A8314 /* EphemeralPeerNegotiationState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EphemeralPeerNegotiationState.swift; sourceTree = "<group>"; };
|
||||
F04DD3D72C130DF600E03E28 /* TunnelSettingsManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TunnelSettingsManager.swift; sourceTree = "<group>"; };
|
||||
@@ -2452,27 +2445,6 @@
|
||||
F05919782C45402E00C301F3 /* SingleHopEphemeralPeerExchanger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleHopEphemeralPeerExchanger.swift; sourceTree = "<group>"; };
|
||||
F059197C2C454C9200C301F3 /* MultiHopEphemeralPeerExchanger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiHopEphemeralPeerExchanger.swift; sourceTree = "<group>"; };
|
||||
F059197E2C454CE000C301F3 /* EphemeralPeerExchangingProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EphemeralPeerExchangingProtocol.swift; sourceTree = "<group>"; };
|
||||
F05DCE872E38C563009A9B85 /* AppLanguage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLanguage.swift; sourceTree = "<group>"; };
|
||||
F05DCE882E38C563009A9B85 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
F05DCE892E38C563009A9B85 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
F05DCE8A2E38C563009A9B85 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
F05DCE8B2E38C563009A9B85 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
F05DCE8C2E38C563009A9B85 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
F05DCE8D2E38C563009A9B85 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
F05DCE8E2E38C563009A9B85 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
F05DCE8F2E38C563009A9B85 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
F05DCE902E38C563009A9B85 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
F05DCE912E38C563009A9B85 /* my */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = my; path = my.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
F05DCE922E38C563009A9B85 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
F05DCE932E38C563009A9B85 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
F05DCE942E38C563009A9B85 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
F05DCE952E38C563009A9B85 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||
F05DCE962E38C563009A9B85 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
F05DCE972E38C563009A9B85 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
F05DCE982E38C563009A9B85 /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
F05DCE992E38C563009A9B85 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
F05DCE9A2E38C563009A9B85 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||
F05DCE9B2E38C563009A9B85 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||
F06045E52B231EB700B2D37A /* URLSessionTransport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLSessionTransport.swift; sourceTree = "<group>"; };
|
||||
F06045E92B23217E00B2D37A /* ShadowsocksTransport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShadowsocksTransport.swift; sourceTree = "<group>"; };
|
||||
F06045EB2B2322A500B2D37A /* Jittered.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Jittered.swift; sourceTree = "<group>"; };
|
||||
@@ -2526,6 +2498,7 @@
|
||||
F0B894F22BF7526700817A42 /* RelaySelector+Wireguard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RelaySelector+Wireguard.swift"; sourceTree = "<group>"; };
|
||||
F0B894F42BF7528700817A42 /* RelaySelector+Shadowsocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RelaySelector+Shadowsocks.swift"; sourceTree = "<group>"; };
|
||||
F0BE65362B9F136A005CC385 /* LocationSectionHeaderFooterView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationSectionHeaderFooterView.swift; sourceTree = "<group>"; };
|
||||
F0C1374E2E45EF1F006307BF /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = "<group>"; };
|
||||
F0C13FE32C64F7CB00BD087D /* DAITASettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DAITASettings.swift; sourceTree = "<group>"; };
|
||||
F0C13FE52C64FB3400BD087D /* TunnelSettingsV6.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelSettingsV6.swift; sourceTree = "<group>"; };
|
||||
F0C2AEFC2A0BB5CC00986207 /* NotificationProviderIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationProviderIdentifier.swift; sourceTree = "<group>"; };
|
||||
@@ -2788,11 +2761,11 @@
|
||||
06799ABD28F98E1D00ACD94E /* MullvadREST */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7A2C0E872D82E450003D8048 /* MullvadAPI */,
|
||||
F06045F02B2324DA00B2D37A /* ApiHandlers */,
|
||||
062B45A228FD4C0F00746E77 /* Assets */,
|
||||
7AD63A422CDA661B00445268 /* Extensions */,
|
||||
582FFA82290A84E700895745 /* Info.plist */,
|
||||
7A2C0E872D82E450003D8048 /* MullvadAPI */,
|
||||
06799ABE28F98E1D00ACD94E /* MullvadREST.h */,
|
||||
F0DC779F2B2222D20087F09D /* Relay */,
|
||||
06FAE67B28F83CA50033DD93 /* REST.swift */,
|
||||
@@ -3717,6 +3690,7 @@
|
||||
589A454A28DDF59B00565204 /* Shared */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F04789EB2E3A4FEB0011E3A5 /* ApplicationLanguage.swift */,
|
||||
58BFA5CB22A7CE1F00A6173D /* ApplicationConfiguration.swift */,
|
||||
58C76A072A33850E00100D75 /* ApplicationTarget.swift */,
|
||||
F0F1EF8C2BE8FF0A00CED01D /* LaunchArguments.swift */,
|
||||
@@ -3949,7 +3923,6 @@
|
||||
58F3C0A824A50C0E003E76BE /* Assets */,
|
||||
58ECD29023F178FD004298B6 /* Configurations */,
|
||||
584F991F2902CBDD001F858D /* Frameworks */,
|
||||
F05DCE9D2E38C563009A9B85 /* Localizations */,
|
||||
01EF6F2D2B6A51B100125696 /* mullvad-api.h */,
|
||||
8556EB512B9A1C6900D26DD4 /* MullvadApi.swift */,
|
||||
58D223F4294C8FF00029F5F8 /* MullvadLogging */,
|
||||
@@ -4228,6 +4201,7 @@
|
||||
children = (
|
||||
5859A55429CD9DD800F66591 /* changes.txt */,
|
||||
587DCCEE287D84A500CE821E /* countries.geo.json */,
|
||||
F0C1374E2E45EF1F006307BF /* Localizable.xcstrings */,
|
||||
);
|
||||
path = Assets;
|
||||
sourceTree = "<group>";
|
||||
@@ -4691,15 +4665,6 @@
|
||||
path = PostQuantum;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F05DCE9D2E38C563009A9B85 /* Localizations */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F05DCE872E38C563009A9B85 /* AppLanguage.swift */,
|
||||
F05DCE9C2E38C563009A9B85 /* Localizable.strings */,
|
||||
);
|
||||
path = Localizations;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F06045F02B2324DA00B2D37A /* ApiHandlers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -5616,9 +5581,9 @@
|
||||
"zh-Hant",
|
||||
ru,
|
||||
pl,
|
||||
tr,
|
||||
fi,
|
||||
my,
|
||||
tr,
|
||||
);
|
||||
mainGroup = 58CE5E57224146200008646E;
|
||||
packageReferences = (
|
||||
@@ -5658,7 +5623,6 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
062B45A328FD4CA700746E77 /* le_root_cert.cer in Resources */,
|
||||
F05DCEA22E38C5F1009A9B85 /* Localizable.strings in Resources */,
|
||||
7A95B67B2D5F758300687524 /* relays.json in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -5681,7 +5645,6 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F05DCEA52E38C5F1009A9B85 /* Localizable.strings in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -5699,9 +5662,9 @@
|
||||
A9BA08312BA32FA9005A7A2D /* PrivacyInfo.xcprivacy in Resources */,
|
||||
7A83C3FF2A55B72E00DFB83A /* MullvadVPNApp.xctestplan in Resources */,
|
||||
58727283265D173C00F315B2 /* LaunchScreen.storyboard in Resources */,
|
||||
F05DCE9F2E38C563009A9B85 /* Localizable.strings in Resources */,
|
||||
5859A55529CD9DD900F66591 /* changes.txt in Resources */,
|
||||
587DCCEF287D84A500CE821E /* countries.geo.json in Resources */,
|
||||
F0C1374F2E45EF1F006307BF /* Localizable.xcstrings in Resources */,
|
||||
58CE5E6B224146210008646E /* Assets.xcassets in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -5711,7 +5674,6 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A9BA08322BA32FB6005A7A2D /* PrivacyInfo.xcprivacy in Resources */,
|
||||
F05DCEA02E38C5F1009A9B85 /* Localizable.strings in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -5719,7 +5681,6 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F05DCEA12E38C5F1009A9B85 /* Localizable.strings in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -5727,7 +5688,6 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F05DCEA32E38C5F1009A9B85 /* Localizable.strings in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -5735,7 +5695,6 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F05DCEA42E38C5F1009A9B85 /* Localizable.strings in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -5750,7 +5709,6 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F05DCEA62E38C5F1009A9B85 /* Localizable.strings in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -5786,7 +5744,6 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F05DCEA72E38C5F1009A9B85 /* Localizable.strings in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -6410,6 +6367,7 @@
|
||||
7A8A190A2CE5FFE9000BCB5B /* SettingsDAITAView.swift in Sources */,
|
||||
F0E8E4C92A604E7400ED26A3 /* AccountDeletionInteractor.swift in Sources */,
|
||||
449E9A6D2D283A2500F8574A /* ConnectionViewComponentPreview.swift in Sources */,
|
||||
F04789ED2E3A4FEB0011E3A5 /* ApplicationLanguage.swift in Sources */,
|
||||
7A5869952B32E9C700640D27 /* LinkButton.swift in Sources */,
|
||||
F09A297D2A9F8A9B00EA3B6F /* RedeemVoucherContentView.swift in Sources */,
|
||||
5803B4B02940A47300C23744 /* TunnelConfiguration.swift in Sources */,
|
||||
@@ -6524,7 +6482,6 @@
|
||||
F91CCBFA2DFAC8ED007F1925 /* DeviceListView.swift in Sources */,
|
||||
7A9CCCBF2A96302800DD6A34 /* SettingsCoordinator.swift in Sources */,
|
||||
58F70FE52AEA707800E6890E /* StoreTransactionLog.swift in Sources */,
|
||||
F05DCE9E2E38C563009A9B85 /* AppLanguage.swift in Sources */,
|
||||
F9394EF02DC0B58D009595EA /* MullvadListNavigationItemView.swift in Sources */,
|
||||
582AE3102440A6CA00E6733A /* InputTextFormatter.swift in Sources */,
|
||||
7A6F2FAD2AFD3DA7006D0856 /* CustomDNSViewController.swift in Sources */,
|
||||
@@ -7330,36 +7287,6 @@
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
F05DCE9C2E38C563009A9B85 /* Localizable.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
F05DCE882E38C563009A9B85 /* da */,
|
||||
F05DCE892E38C563009A9B85 /* de */,
|
||||
F05DCE8A2E38C563009A9B85 /* en */,
|
||||
F05DCE8B2E38C563009A9B85 /* es */,
|
||||
F05DCE8C2E38C563009A9B85 /* fi */,
|
||||
F05DCE8D2E38C563009A9B85 /* fr */,
|
||||
F05DCE8E2E38C563009A9B85 /* it */,
|
||||
F05DCE8F2E38C563009A9B85 /* ja */,
|
||||
F05DCE902E38C563009A9B85 /* ko */,
|
||||
F05DCE912E38C563009A9B85 /* my */,
|
||||
F05DCE922E38C563009A9B85 /* nb */,
|
||||
F05DCE932E38C563009A9B85 /* nl */,
|
||||
F05DCE942E38C563009A9B85 /* pl */,
|
||||
F05DCE952E38C563009A9B85 /* pt-PT */,
|
||||
F05DCE962E38C563009A9B85 /* ru */,
|
||||
F05DCE972E38C563009A9B85 /* sv */,
|
||||
F05DCE982E38C563009A9B85 /* th */,
|
||||
F05DCE992E38C563009A9B85 /* tr */,
|
||||
F05DCE9A2E38C563009A9B85 /* zh-Hans */,
|
||||
F05DCE9B2E38C563009A9B85 /* zh-Hant */,
|
||||
);
|
||||
name = Localizable.strings;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
06799AD428F98E1D00ACD94E /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
@@ -7771,6 +7698,7 @@
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_PRECOMPILE_BRIDGING_HEADER = NO;
|
||||
SWIFT_VERSION = 6.0;
|
||||
@@ -7832,6 +7760,7 @@
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
SWIFT_PRECOMPILE_BRIDGING_HEADER = NO;
|
||||
SWIFT_VERSION = 6.0;
|
||||
@@ -8483,6 +8412,7 @@
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_PRECOMPILE_BRIDGING_HEADER = NO;
|
||||
SWIFT_VERSION = 6.0;
|
||||
@@ -9338,6 +9268,7 @@
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
SWIFT_PRECOMPILE_BRIDGING_HEADER = NO;
|
||||
SWIFT_VERSION = 6.0;
|
||||
|
||||
@@ -28,11 +28,7 @@ extension CustomDateComponentsFormatting {
|
||||
let dateComponents = calendar.dateComponents([.year, .day], from: start, to: max(start, end))
|
||||
|
||||
guard !isLessThanOneDay(dateComponents: dateComponents) else {
|
||||
return NSLocalizedString(
|
||||
"CUSTOM_DATE_COMPONENTS_FORMATTING_LESS_THAN_ONE_DAY",
|
||||
value: "Less than a day",
|
||||
comment: ""
|
||||
)
|
||||
return NSLocalizedString("Less than a day", comment: "")
|
||||
}
|
||||
|
||||
let formatter = DateComponentsFormatter()
|
||||
|
||||
@@ -64,12 +64,7 @@ class HeaderBarView: UIView {
|
||||
let accountButton: UIButton = {
|
||||
let button = makeHeaderBarButton(with: UIImage.Buttons.account)
|
||||
button.setAccessibilityIdentifier(.accountButton)
|
||||
button.accessibilityLabel = NSLocalizedString(
|
||||
"HEADER_BAR_ACCOUNT_BUTTON_ACCESSIBILITY_LABEL",
|
||||
tableName: "HeaderBar",
|
||||
value: "Account",
|
||||
comment: ""
|
||||
)
|
||||
button.accessibilityLabel = NSLocalizedString("Account", comment: "")
|
||||
button.heightAnchor.constraint(equalToConstant: UIMetrics.Button.barButtonSize).isActive = true
|
||||
button.widthAnchor.constraint(equalTo: button.heightAnchor, multiplier: 1).isActive = true
|
||||
return button
|
||||
@@ -78,12 +73,7 @@ class HeaderBarView: UIView {
|
||||
let settingsButton: UIButton = {
|
||||
let button = makeHeaderBarButton(with: UIImage.Buttons.settings)
|
||||
button.setAccessibilityIdentifier(.settingsButton)
|
||||
button.accessibilityLabel = NSLocalizedString(
|
||||
"HEADER_BAR_SETTINGS_BUTTON_ACCESSIBILITY_LABEL",
|
||||
tableName: "HeaderBar",
|
||||
value: "Settings",
|
||||
comment: ""
|
||||
)
|
||||
button.accessibilityLabel = NSLocalizedString("Settings", comment: "")
|
||||
button.heightAnchor.constraint(equalToConstant: UIMetrics.Button.barButtonSize).isActive = true
|
||||
button.widthAnchor.constraint(equalTo: button.heightAnchor, multiplier: 1).isActive = true
|
||||
return button
|
||||
@@ -123,12 +113,7 @@ class HeaderBarView: UIView {
|
||||
private var timeLeft: Date? {
|
||||
didSet {
|
||||
if let timeLeft {
|
||||
let formattedTimeLeft = NSLocalizedString(
|
||||
"TIME_LEFT_HEADER_VIEW",
|
||||
tableName: "Account",
|
||||
value: "Time left: %@",
|
||||
comment: ""
|
||||
)
|
||||
let formattedTimeLeft = NSLocalizedString("Time left: %@", comment: "")
|
||||
timeLeftLabel.text = String(
|
||||
format: formattedTimeLeft,
|
||||
CustomDateComponentsFormatting.localizedString(
|
||||
@@ -146,12 +131,7 @@ class HeaderBarView: UIView {
|
||||
private var deviceName: String? {
|
||||
didSet {
|
||||
if let deviceName {
|
||||
let formattedDeviceName = NSLocalizedString(
|
||||
"DEVICE_NAME_HEADER_VIEW",
|
||||
tableName: "Account",
|
||||
value: "Device name: %@",
|
||||
comment: ""
|
||||
)
|
||||
let formattedDeviceName = NSLocalizedString("Device name: %@", comment: "")
|
||||
deviceNameLabel.text = String(format: formattedDeviceName, deviceName)
|
||||
} else {
|
||||
deviceNameLabel.text = ""
|
||||
|
||||
@@ -146,12 +146,7 @@ final class AccountCoordinator: Coordinator, Presentable, Presenting, @unchecked
|
||||
}
|
||||
)
|
||||
)
|
||||
controller.title = NSLocalizedString(
|
||||
"MANAGE_DEVICES_TITLE",
|
||||
tableName: "DeviceManagement",
|
||||
value: "Manage devices",
|
||||
comment: ""
|
||||
)
|
||||
controller.title = NSLocalizedString("Manage devices", comment: "")
|
||||
let doneButton = UIBarButtonItem(
|
||||
systemItem: .done,
|
||||
primaryAction: UIAction(handler: { _ in
|
||||
@@ -172,12 +167,7 @@ final class AccountCoordinator: Coordinator, Presentable, Presenting, @unchecked
|
||||
message: message,
|
||||
buttons: [
|
||||
AlertAction(
|
||||
title: NSLocalizedString(
|
||||
"ERROR_ALERT_OK_ACTION",
|
||||
tableName: "Common",
|
||||
value: "Got it!",
|
||||
comment: ""
|
||||
),
|
||||
title: NSLocalizedString("Got it!", comment: ""),
|
||||
style: .default
|
||||
),
|
||||
]
|
||||
@@ -249,9 +239,7 @@ final class AccountCoordinator: Coordinator, Presentable, Presenting, @unchecked
|
||||
|
||||
private func showRestorePurchasesInfo() {
|
||||
let message = NSLocalizedString(
|
||||
"RESTORE_PURCHASES_DIALOG_MESSAGE",
|
||||
tableName: "Account",
|
||||
value: """
|
||||
"""
|
||||
You can use the "restore purchases" function to check for any in-app payments \
|
||||
made via Apple services. If there is a payment that has not been credited, it will \
|
||||
add the time to the currently logged in Mullvad account.
|
||||
@@ -262,20 +250,10 @@ final class AccountCoordinator: Coordinator, Presentable, Presenting, @unchecked
|
||||
let presentation = AlertPresentation(
|
||||
id: "account-device-info-alert",
|
||||
icon: .info,
|
||||
title: NSLocalizedString(
|
||||
"RESTORE_PURCHASES_DIALOG_TITLE",
|
||||
tableName: "Account",
|
||||
value: "If you haven’t received additional VPN time after purchasing",
|
||||
comment: ""
|
||||
),
|
||||
title: NSLocalizedString("If you haven’t received additional VPN time after purchasing", comment: ""),
|
||||
message: message,
|
||||
buttons: [AlertAction(
|
||||
title: NSLocalizedString(
|
||||
"RESTORE_PURCHASES_DIALOG_OK_ACTION",
|
||||
tableName: "Account",
|
||||
value: "Got it!",
|
||||
comment: ""
|
||||
),
|
||||
title: NSLocalizedString("Got it!", comment: ""),
|
||||
style: .default
|
||||
)]
|
||||
)
|
||||
@@ -286,9 +264,6 @@ final class AccountCoordinator: Coordinator, Presentable, Presenting, @unchecked
|
||||
|
||||
func showFailToFetchProducts() {
|
||||
let message = NSLocalizedString(
|
||||
"WELCOME_FAILED_TO_FETCH_PRODUCTS_DIALOG",
|
||||
tableName: "Welcome",
|
||||
value:
|
||||
"""
|
||||
Failed to connect to App store, please try again later.
|
||||
""",
|
||||
@@ -301,12 +276,7 @@ final class AccountCoordinator: Coordinator, Presentable, Presenting, @unchecked
|
||||
message: message,
|
||||
buttons: [
|
||||
AlertAction(
|
||||
title: NSLocalizedString(
|
||||
"WELCOME_FAILED_TO_FETCH_PRODUCTS_OK_ACTION",
|
||||
tableName: "Welcome",
|
||||
value: "Got it!",
|
||||
comment: ""
|
||||
),
|
||||
title: NSLocalizedString("Got it!", comment: ""),
|
||||
style: .default
|
||||
),
|
||||
]
|
||||
|
||||
@@ -34,22 +34,12 @@ final class ChangeLogCoordinator: Coordinator, Presentable, SettingsChildCoordin
|
||||
func start(animated: Bool) {
|
||||
let changeLogViewController = UIHostingController(rootView: ChangeLogView(viewModel: viewModel))
|
||||
changeLogViewController.view.setAccessibilityIdentifier(.changeLogAlert)
|
||||
changeLogViewController.navigationItem.title = NSLocalizedString(
|
||||
"whats_new_title",
|
||||
tableName: "Changelog",
|
||||
value: "What's new",
|
||||
comment: ""
|
||||
)
|
||||
changeLogViewController.navigationItem.title = NSLocalizedString("What's new", comment: "")
|
||||
|
||||
switch route {
|
||||
case .changelog:
|
||||
let barButtonItem = UIBarButtonItem(
|
||||
title: NSLocalizedString(
|
||||
"CHANGELOG_NAVIGATION_DONE_BUTTON",
|
||||
tableName: "Changelog",
|
||||
value: "Done",
|
||||
comment: ""
|
||||
),
|
||||
title: NSLocalizedString("Done", comment: ""),
|
||||
primaryAction: UIAction { [weak self] _ in
|
||||
guard let self else { return }
|
||||
didFinish?(self)
|
||||
|
||||
@@ -43,19 +43,9 @@ class AddCustomListCoordinator: Coordinator, Presentable, Presenting {
|
||||
)
|
||||
controller.delegate = self
|
||||
|
||||
controller.navigationItem.title = NSLocalizedString(
|
||||
"CUSTOM_LISTS_NAVIGATION_EDIT_TITLE",
|
||||
tableName: "CustomLists",
|
||||
value: "New custom list",
|
||||
comment: ""
|
||||
)
|
||||
controller.navigationItem.title = NSLocalizedString("New custom list", comment: "")
|
||||
|
||||
controller.saveBarButton.title = NSLocalizedString(
|
||||
"CUSTOM_LISTS_NAVIGATION_CREATE_BUTTON",
|
||||
tableName: "CustomLists",
|
||||
value: "Create",
|
||||
comment: ""
|
||||
)
|
||||
controller.saveBarButton.title = NSLocalizedString("Create", comment: "")
|
||||
|
||||
controller.navigationItem.leftBarButtonItem = UIBarButtonItem(
|
||||
systemItem: .cancel,
|
||||
|
||||
@@ -40,12 +40,7 @@ class AddLocationsCoordinator: Coordinator, Presentable, Presenting {
|
||||
)
|
||||
controller.delegate = self
|
||||
|
||||
controller.navigationItem.title = NSLocalizedString(
|
||||
"ADD_LOCATIONS_NAVIGATION_TITLE",
|
||||
tableName: "AddLocations",
|
||||
value: "Add locations",
|
||||
comment: ""
|
||||
)
|
||||
controller.navigationItem.title = NSLocalizedString("Add locations", comment: "")
|
||||
|
||||
navigationController.pushViewController(controller, animated: true)
|
||||
}
|
||||
|
||||
@@ -40,13 +40,13 @@ enum CustomListItemIdentifier: Hashable, CaseIterable {
|
||||
var text: String? {
|
||||
switch self {
|
||||
case .name:
|
||||
NSLocalizedString("NAME", tableName: "CustomLists", value: "Name", comment: "")
|
||||
NSLocalizedString("Name", comment: "")
|
||||
case .addLocations:
|
||||
NSLocalizedString("ADD", tableName: "CustomLists", value: "Add locations", comment: "")
|
||||
NSLocalizedString("Add locations", comment: "")
|
||||
case .editLocations:
|
||||
NSLocalizedString("EDIT", tableName: "CustomLists", value: "Edit locations", comment: "")
|
||||
NSLocalizedString("Edit locations", comment: "")
|
||||
case .deleteList:
|
||||
NSLocalizedString("Delete", tableName: "CustomLists", value: "Delete list", comment: "")
|
||||
NSLocalizedString("Delete list", comment: "")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,12 +47,7 @@ class CustomListViewController: UIViewController {
|
||||
|
||||
lazy var saveBarButton: UIBarButtonItem = {
|
||||
let barButtonItem = UIBarButtonItem(
|
||||
title: NSLocalizedString(
|
||||
"CUSTOM_LIST_NAVIGATION_SAVE_BUTTON",
|
||||
tableName: "CustomLists",
|
||||
value: "Save",
|
||||
comment: ""
|
||||
),
|
||||
title: NSLocalizedString("Save", comment: ""),
|
||||
primaryAction: UIAction { [weak self] _ in
|
||||
self?.onSave()
|
||||
}
|
||||
@@ -163,12 +158,7 @@ class CustomListViewController: UIViewController {
|
||||
|
||||
private func onDelete() {
|
||||
let message = NSMutableAttributedString(
|
||||
markdownString: NSLocalizedString(
|
||||
"CUSTOM_LISTS_DELETE_PROMPT",
|
||||
tableName: "CustomLists",
|
||||
value: "Do you want to delete the list **\(subject.value.name)**?",
|
||||
comment: ""
|
||||
),
|
||||
markdownString: NSLocalizedString("Do you want to delete the list **\(subject.value.name)**?", comment: ""),
|
||||
options: MarkdownStylingOptions(font: .preferredFont(forTextStyle: .body))
|
||||
)
|
||||
|
||||
@@ -178,12 +168,7 @@ class CustomListViewController: UIViewController {
|
||||
attributedMessage: message,
|
||||
buttons: [
|
||||
AlertAction(
|
||||
title: NSLocalizedString(
|
||||
"CUSTOM_LISTS_DELETE_BUTTON",
|
||||
tableName: "CustomLists",
|
||||
value: "Delete list",
|
||||
comment: ""
|
||||
),
|
||||
title: NSLocalizedString("Delete list", comment: ""),
|
||||
style: .destructive,
|
||||
accessibilityId: .confirmDeleteCustomListButton,
|
||||
handler: {
|
||||
@@ -192,12 +177,7 @@ class CustomListViewController: UIViewController {
|
||||
}
|
||||
),
|
||||
AlertAction(
|
||||
title: NSLocalizedString(
|
||||
"CUSTOM_LISTS_CANCEL_BUTTON",
|
||||
tableName: "CustomLists",
|
||||
value: "Cancel",
|
||||
comment: ""
|
||||
),
|
||||
title: NSLocalizedString("Cancel", comment: ""),
|
||||
style: .default,
|
||||
accessibilityId: .cancelDeleteCustomListButton
|
||||
),
|
||||
|
||||
@@ -58,12 +58,7 @@ class EditCustomListCoordinator: Coordinator, Presentable, Presenting {
|
||||
)
|
||||
controller.delegate = self
|
||||
|
||||
controller.navigationItem.title = NSLocalizedString(
|
||||
"CUSTOM_LIST_NAVIGATION_TITLE",
|
||||
tableName: "CustomLists",
|
||||
value: subject.value.name,
|
||||
comment: ""
|
||||
)
|
||||
controller.navigationItem.title = subject.value.name
|
||||
|
||||
navigationController.interactivePopGestureRecognizer?.delegate = self
|
||||
navigationController.pushViewController(controller, animated: true)
|
||||
@@ -97,12 +92,7 @@ class EditCustomListCoordinator: Coordinator, Presentable, Presenting {
|
||||
|
||||
private func presentUnsavedChangesDialog() {
|
||||
let message = NSMutableAttributedString(
|
||||
markdownString: NSLocalizedString(
|
||||
"CUSTOM_LISTS_UNSAVED_CHANGES_PROMPT",
|
||||
tableName: "CustomLists",
|
||||
value: "You have unsaved changes.",
|
||||
comment: ""
|
||||
),
|
||||
markdownString: NSLocalizedString("You have unsaved changes.", comment: ""),
|
||||
options: MarkdownStylingOptions(font: .preferredFont(forTextStyle: .body))
|
||||
)
|
||||
|
||||
@@ -112,24 +102,14 @@ class EditCustomListCoordinator: Coordinator, Presentable, Presenting {
|
||||
attributedMessage: message,
|
||||
buttons: [
|
||||
AlertAction(
|
||||
title: NSLocalizedString(
|
||||
"CUSTOM_LISTS_DISCARD_CHANGES_BUTTON",
|
||||
tableName: "CustomLists",
|
||||
value: "Discard changes",
|
||||
comment: ""
|
||||
),
|
||||
title: NSLocalizedString("Discard changes", comment: ""),
|
||||
style: .destructive,
|
||||
handler: {
|
||||
self.didCancel?(self)
|
||||
}
|
||||
),
|
||||
AlertAction(
|
||||
title: NSLocalizedString(
|
||||
"CUSTOM_LISTS_BACK_TO_EDITING_BUTTON",
|
||||
tableName: "CustomLists",
|
||||
value: "Back to editing",
|
||||
comment: ""
|
||||
),
|
||||
title: NSLocalizedString("Back to editing", comment: ""),
|
||||
style: .default
|
||||
),
|
||||
]
|
||||
|
||||
@@ -41,12 +41,7 @@ class EditLocationsCoordinator: Coordinator, Presentable, Presenting {
|
||||
)
|
||||
controller.delegate = self
|
||||
|
||||
controller.navigationItem.title = NSLocalizedString(
|
||||
"EDIT_LOCATIONS_NAVIGATION_TITLE",
|
||||
tableName: "EditLocations",
|
||||
value: "Edit locations",
|
||||
comment: ""
|
||||
)
|
||||
controller.navigationItem.title = NSLocalizedString("Edit locations", comment: "")
|
||||
navigationController.pushViewController(controller, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,12 +92,7 @@ class ListCustomListViewController: UIViewController {
|
||||
}
|
||||
|
||||
private func configureNavigationItem() {
|
||||
navigationItem.title = NSLocalizedString(
|
||||
"LIST_CUSTOM_LIST_NAVIGATION_TITLE",
|
||||
tableName: "CustomList",
|
||||
value: "Edit custom list",
|
||||
comment: ""
|
||||
)
|
||||
navigationItem.title = NSLocalizedString("Edit custom list", comment: "")
|
||||
|
||||
navigationItem.rightBarButtonItem = UIBarButtonItem(
|
||||
systemItem: .done,
|
||||
|
||||
@@ -181,9 +181,7 @@ extension LocationCoordinator: @preconcurrency LocationViewControllerWrapperDele
|
||||
|
||||
func navigateToCustomLists(nodes: [LocationNode]) {
|
||||
let actionSheet = UIAlertController(
|
||||
title: NSLocalizedString(
|
||||
"ACTION_SHEET_TITLE", tableName: "CustomLists", value: "Custom lists", comment: ""
|
||||
),
|
||||
title: NSLocalizedString("Custom lists", comment: ""),
|
||||
message: nil,
|
||||
preferredStyle: UIDevice.current.userInterfaceIdiom == .pad ? .alert : .actionSheet
|
||||
)
|
||||
@@ -191,9 +189,7 @@ extension LocationCoordinator: @preconcurrency LocationViewControllerWrapperDele
|
||||
actionSheet.view.tintColor = .AlertController.tintColor
|
||||
|
||||
let addCustomListAction = UIAlertAction(
|
||||
title: NSLocalizedString(
|
||||
"ACTION_SHEET_ADD_LIST_BUTTON", tableName: "CustomLists", value: "Add new list", comment: ""
|
||||
),
|
||||
title: NSLocalizedString("Add new list", comment: ""),
|
||||
style: .default,
|
||||
handler: { [weak self] _ in
|
||||
self?.showAddCustomList(nodes: nodes)
|
||||
@@ -203,9 +199,7 @@ extension LocationCoordinator: @preconcurrency LocationViewControllerWrapperDele
|
||||
actionSheet.addAction(addCustomListAction)
|
||||
|
||||
let editAction = UIAlertAction(
|
||||
title: NSLocalizedString(
|
||||
"ACTION_SHEET_EDIT_LISTS_BUTTON", tableName: "CustomLists", value: "Edit lists", comment: ""
|
||||
),
|
||||
title: NSLocalizedString("Edit lists", comment: ""),
|
||||
style: .default,
|
||||
handler: { [weak self] _ in
|
||||
self?.showEditCustomLists(nodes: nodes)
|
||||
@@ -216,12 +210,7 @@ extension LocationCoordinator: @preconcurrency LocationViewControllerWrapperDele
|
||||
actionSheet.addAction(editAction)
|
||||
|
||||
actionSheet.addAction(UIAlertAction(
|
||||
title: NSLocalizedString(
|
||||
"CUSTOM_LIST_ACTION_SHEET_CANCEL_BUTTON",
|
||||
tableName: "CustomLists",
|
||||
value: "Cancel",
|
||||
comment: ""
|
||||
),
|
||||
title: NSLocalizedString("Cancel", comment: ""),
|
||||
style: .cancel
|
||||
))
|
||||
|
||||
|
||||
@@ -126,12 +126,7 @@ final class LoginCoordinator: Coordinator, Presenting {
|
||||
message: errorDescription,
|
||||
buttons: [
|
||||
AlertAction(
|
||||
title: NSLocalizedString(
|
||||
"ERROR_ALERT_OK_ACTION",
|
||||
tableName: "DeviceManagement",
|
||||
value: "Got it!",
|
||||
comment: ""
|
||||
),
|
||||
title: NSLocalizedString("Got it!", comment: ""),
|
||||
style: .default
|
||||
),
|
||||
]
|
||||
|
||||
@@ -65,20 +65,10 @@ extension ProfileVoucherCoordinator: @preconcurrency AddCreditSucceededViewContr
|
||||
}
|
||||
|
||||
func header(in controller: AddCreditSucceededViewController) -> String {
|
||||
NSLocalizedString(
|
||||
"REDEEM_VOUCHER_SUCCESS_TITLE",
|
||||
tableName: "ProfileRedeemVoucher",
|
||||
value: "Voucher was successfully redeemed.",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Voucher was successfully redeemed.", comment: "")
|
||||
}
|
||||
|
||||
func titleForAction(in controller: AddCreditSucceededViewController) -> String {
|
||||
NSLocalizedString(
|
||||
"REDEEM_VOUCHER_DISMISS_BUTTON",
|
||||
tableName: "ProfileRedeemVoucher",
|
||||
value: "Got it!",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Got it!", comment: "")
|
||||
}
|
||||
}
|
||||
+3
-18
@@ -52,26 +52,11 @@ class AddAccessMethodCoordinator: Coordinator, Presentable, Presenting {
|
||||
}
|
||||
|
||||
private func setUpControllerNavigationItem(_ controller: MethodSettingsViewController) {
|
||||
controller.navigationItem.prompt = NSLocalizedString(
|
||||
"METHOD_SETTINGS_NAVIGATION_ADD_PROMPT",
|
||||
tableName: "APIAccess",
|
||||
value: "The app will test the method before saving.",
|
||||
comment: ""
|
||||
)
|
||||
controller.navigationItem.prompt = NSLocalizedString("The app will test the method before saving.", comment: "")
|
||||
|
||||
controller.navigationItem.title = NSLocalizedString(
|
||||
"METHOD_SETTINGS_NAVIGATION_ADD_TITLE",
|
||||
tableName: "APIAccess",
|
||||
value: "Add access method",
|
||||
comment: ""
|
||||
)
|
||||
controller.navigationItem.title = NSLocalizedString("Add access method", comment: "")
|
||||
|
||||
controller.saveBarButton.title = NSLocalizedString(
|
||||
"METHOD_SETTINGS_NAVIGATION_ADD_BUTTON",
|
||||
tableName: "APIAccess",
|
||||
value: "Add",
|
||||
comment: ""
|
||||
)
|
||||
controller.saveBarButton.title = NSLocalizedString("Add", comment: "")
|
||||
|
||||
controller.navigationItem.leftBarButtonItem = UIBarButtonItem(
|
||||
systemItem: .cancel,
|
||||
|
||||
+2
-12
@@ -28,19 +28,9 @@ extension TextCellContentConfiguration {
|
||||
var localizedDescription: String {
|
||||
switch self {
|
||||
case .required:
|
||||
NSLocalizedString(
|
||||
"REQUIRED_PLACEHOLDER",
|
||||
tableName: "APIAccess",
|
||||
value: "Required",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Required", comment: "")
|
||||
case .optional:
|
||||
NSLocalizedString(
|
||||
"OPTIONAL_PLACEHOLDER",
|
||||
tableName: "APIAccess",
|
||||
value: "Optional",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Optional", comment: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -34,13 +34,13 @@ enum ShadowsocksItemIdentifier: Hashable, CaseIterable {
|
||||
var text: String {
|
||||
switch self {
|
||||
case .server:
|
||||
NSLocalizedString("SHADOWSOCKS_SERVER", tableName: "APIAccess", value: "Server", comment: "")
|
||||
NSLocalizedString("Server", comment: "")
|
||||
case .port:
|
||||
NSLocalizedString("SHADOWSOCKS_PORT", tableName: "APIAccess", value: "Port", comment: "")
|
||||
NSLocalizedString("Port", comment: "")
|
||||
case .password:
|
||||
NSLocalizedString("SHADOWSOCKS_PASSWORD", tableName: "APIAccess", value: "Password", comment: "")
|
||||
NSLocalizedString("Password", comment: "")
|
||||
case .cipher:
|
||||
NSLocalizedString("SHADOWSOCKS_CIPHER", tableName: "APIAccess", value: "Cipher", comment: "")
|
||||
NSLocalizedString("Cipher", comment: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,15 +49,15 @@ enum SocksItemIdentifier: Hashable, CaseIterable {
|
||||
var text: String {
|
||||
switch self {
|
||||
case .server:
|
||||
NSLocalizedString("SOCKS_SERVER", tableName: "APIAccess", value: "Server", comment: "")
|
||||
NSLocalizedString("Server", comment: "")
|
||||
case .port:
|
||||
NSLocalizedString("SOCKS_PORT", tableName: "APIAccess", value: "Port", comment: "")
|
||||
NSLocalizedString("Port", comment: "")
|
||||
case .authentication:
|
||||
NSLocalizedString("SOCKS_AUTHENTICATION", tableName: "APIAccess", value: "Authentication", comment: "")
|
||||
NSLocalizedString("Authentication", comment: "")
|
||||
case .username:
|
||||
NSLocalizedString("SOCKS_USERNAME", tableName: "APIAccess", value: "Username", comment: "")
|
||||
NSLocalizedString("Username", comment: "")
|
||||
case .password:
|
||||
NSLocalizedString("SOCKS_PASSWORD", tableName: "APIAccess", value: "Password", comment: "")
|
||||
NSLocalizedString("Password", comment: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-18
@@ -74,26 +74,11 @@ extension EditAccessMethodCoordinator: @preconcurrency EditAccessMethodViewContr
|
||||
alertPresenter: AlertPresenter(context: self)
|
||||
)
|
||||
|
||||
controller.navigationItem.prompt = NSLocalizedString(
|
||||
"METHOD_SETTINGS_NAVIGATION_ADD_PROMPT",
|
||||
tableName: "APIAccess",
|
||||
value: "The app will test the method before saving.",
|
||||
comment: ""
|
||||
)
|
||||
controller.navigationItem.prompt = NSLocalizedString("The app will test the method before saving.", comment: "")
|
||||
|
||||
controller.navigationItem.title = NSLocalizedString(
|
||||
"METHOD_SETTINGS_NAVIGATION_ADD_TITLE",
|
||||
tableName: "APIAccess",
|
||||
value: "Method settings",
|
||||
comment: ""
|
||||
)
|
||||
controller.navigationItem.title = NSLocalizedString("Method settings", comment: "")
|
||||
|
||||
controller.saveBarButton.title = NSLocalizedString(
|
||||
"METHOD_SETTINGS_NAVIGATION_ADD_BUTTON",
|
||||
tableName: "APIAccess",
|
||||
value: "Save",
|
||||
comment: ""
|
||||
)
|
||||
controller.saveBarButton.title = NSLocalizedString("Save", comment: "")
|
||||
|
||||
controller.delegate = self
|
||||
|
||||
|
||||
+5
-5
@@ -54,17 +54,17 @@ enum EditAccessMethodItemIdentifier: Hashable {
|
||||
var text: String? {
|
||||
switch self {
|
||||
case .enableMethod:
|
||||
NSLocalizedString("ENABLE_METHOD", tableName: "APIAccess", value: "Enable method", comment: "")
|
||||
NSLocalizedString("Enable method", comment: "")
|
||||
case .methodSettings:
|
||||
NSLocalizedString("METHOD_SETTINGS", tableName: "APIAccess", value: "Method settings", comment: "")
|
||||
NSLocalizedString("Method settings", comment: "")
|
||||
case .testMethod:
|
||||
NSLocalizedString("TEST_METHOD", tableName: "APIAccess", value: "Test method", comment: "")
|
||||
NSLocalizedString("Test method", comment: "")
|
||||
case .cancelTest:
|
||||
NSLocalizedString("CANCEL_TEST", tableName: "APIAccess", value: "Cancel", comment: "")
|
||||
NSLocalizedString("Cancel", comment: "")
|
||||
case .testingStatus:
|
||||
nil
|
||||
case .deleteMethod:
|
||||
NSLocalizedString("DELETE_METHOD", tableName: "APIAccess", value: "Delete method", comment: "")
|
||||
NSLocalizedString("Delete method", comment: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-12
@@ -20,19 +20,9 @@ enum EditAccessMethodSectionIdentifier: Hashable {
|
||||
var sectionFooter: String? {
|
||||
switch self {
|
||||
case .testMethod:
|
||||
NSLocalizedString(
|
||||
"TEST_METHOD_FOOTER",
|
||||
tableName: "APIAccess",
|
||||
value: "Performs a connection test to a Mullvad API server via this access method.",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Performs a connection test to a Mullvad API server via this access method.", comment: "")
|
||||
case .enableMethod:
|
||||
NSLocalizedString(
|
||||
"METHOD_FOOTER",
|
||||
tableName: "APIAccess",
|
||||
value: "At least one method needs to be enabled.",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("At least one method needs to be enabled.", comment: "")
|
||||
case .methodSettings, .cancelTest, .testingStatus, .deleteMethod:
|
||||
nil
|
||||
}
|
||||
|
||||
+4
-24
@@ -379,31 +379,16 @@ extension EditAccessMethodViewController: UITableViewDelegate {
|
||||
|
||||
private func onDelete() {
|
||||
let methodName = subject.value.name.isEmpty
|
||||
? NSLocalizedString(
|
||||
"METHOD_SETTINGS_SAVE_PROMPT",
|
||||
tableName: "APIAccess",
|
||||
value: "method?",
|
||||
comment: ""
|
||||
)
|
||||
? NSLocalizedString("method", comment: "")
|
||||
: subject.value.name
|
||||
|
||||
let presentation = AlertPresentation(
|
||||
id: "api-access-methods-delete-method-alert",
|
||||
icon: .alert,
|
||||
message: NSLocalizedString(
|
||||
"METHOD_SETTINGS_DELETE_PROMPT",
|
||||
tableName: "APIAccess",
|
||||
value: "Delete \(methodName)?",
|
||||
comment: ""
|
||||
),
|
||||
message: NSLocalizedString("Delete \(methodName)?", comment: ""),
|
||||
buttons: [
|
||||
AlertAction(
|
||||
title: NSLocalizedString(
|
||||
"METHOD_SETTINGS_DELETE_BUTTON",
|
||||
tableName: "APIAccess",
|
||||
value: "Delete",
|
||||
comment: ""
|
||||
),
|
||||
title: NSLocalizedString("Delete", comment: ""),
|
||||
style: .destructive,
|
||||
accessibilityId: .accessMethodConfirmDeleteButton,
|
||||
handler: { [weak self] in
|
||||
@@ -413,12 +398,7 @@ extension EditAccessMethodViewController: UITableViewDelegate {
|
||||
}
|
||||
),
|
||||
AlertAction(
|
||||
title: NSLocalizedString(
|
||||
"METHOD_SETTINGS_CANCEL_BUTTON",
|
||||
tableName: "APIAccess",
|
||||
value: "Cancel",
|
||||
comment: ""
|
||||
),
|
||||
title: NSLocalizedString("Cancel", comment: ""),
|
||||
style: .default
|
||||
),
|
||||
]
|
||||
|
||||
+1
-6
@@ -178,12 +178,7 @@ class MethodSettingsCellConfiguration {
|
||||
var contentConfiguration = MethodTestingStatusCellContentConfiguration()
|
||||
contentConfiguration.status = viewStatus
|
||||
contentConfiguration.detailText = viewStatus == .reachable
|
||||
? NSLocalizedString(
|
||||
"METHOD_SETTINGS_SAVING_CHANGES",
|
||||
tableName: "APIAccess",
|
||||
value: "Saving changes...",
|
||||
comment: ""
|
||||
)
|
||||
? NSLocalizedString("Saving changes...", comment: "")
|
||||
: nil
|
||||
|
||||
cell.contentConfiguration = contentConfiguration
|
||||
|
||||
+3
-3
@@ -71,13 +71,13 @@ enum MethodSettingsItemIdentifier: Hashable {
|
||||
var text: String? {
|
||||
switch self {
|
||||
case .name:
|
||||
NSLocalizedString("NAME", tableName: "APIAccess", value: "Name", comment: "")
|
||||
NSLocalizedString("Name", comment: "")
|
||||
case .protocol:
|
||||
NSLocalizedString("TYPE", tableName: "APIAccess", value: "Type", comment: "")
|
||||
NSLocalizedString("Type", comment: "")
|
||||
case .proxyConfiguration, .validationError:
|
||||
nil
|
||||
case .cancelTest:
|
||||
NSLocalizedString("CANCEL_TEST", tableName: "APIAccess", value: "Cancel", comment: "")
|
||||
NSLocalizedString("Cancel", comment: "")
|
||||
case .testingStatus:
|
||||
nil
|
||||
}
|
||||
|
||||
+1
-6
@@ -21,12 +21,7 @@ enum MethodSettingsSectionIdentifier: Hashable {
|
||||
case .name, .protocol, .validationError, .testingStatus, .cancelTest:
|
||||
nil
|
||||
case .proxyConfiguration:
|
||||
NSLocalizedString(
|
||||
"HOST_CONFIG_SECTION_TITLE",
|
||||
tableName: "APIAccess",
|
||||
value: "Server details",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Server details", comment: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-19
@@ -46,7 +46,7 @@ class MethodSettingsViewController: UITableViewController {
|
||||
|
||||
lazy var saveBarButton: UIBarButtonItem = {
|
||||
let barButtonItem = UIBarButtonItem(
|
||||
title: NSLocalizedString("SAVE_NAVIGATION_BUTTON", tableName: "APIAccess", value: "Save", comment: ""),
|
||||
title: NSLocalizedString("Save", comment: ""),
|
||||
primaryAction: UIAction { [weak self] _ in
|
||||
self?.onTest()
|
||||
}
|
||||
@@ -314,20 +314,10 @@ class MethodSettingsViewController: UITableViewController {
|
||||
id: "api-access-methods-testing-status-failed-alert",
|
||||
accessibilityIdentifier: .accessMethodUnreachableAlert,
|
||||
icon: .warning,
|
||||
message: NSLocalizedString(
|
||||
"METHOD_SETTINGS_SAVE_PROMPT",
|
||||
tableName: "APIAccess",
|
||||
value: "API could not be reached, save anyway?",
|
||||
comment: ""
|
||||
),
|
||||
message: NSLocalizedString("API could not be reached, save anyway?", comment: ""),
|
||||
buttons: [
|
||||
AlertAction(
|
||||
title: NSLocalizedString(
|
||||
"METHOD_SETTINGS_SAVE_BUTTON",
|
||||
tableName: "APIAccess",
|
||||
value: "Save anyway",
|
||||
comment: ""
|
||||
),
|
||||
title: NSLocalizedString("Save anyway", comment: ""),
|
||||
style: .default,
|
||||
accessibilityId: .accessMethodUnreachableSaveButton,
|
||||
handler: { [weak self] in
|
||||
@@ -335,12 +325,7 @@ class MethodSettingsViewController: UITableViewController {
|
||||
}
|
||||
),
|
||||
AlertAction(
|
||||
title: NSLocalizedString(
|
||||
"METHOD_SETTINGS_BACK_BUTTON",
|
||||
tableName: "APIAccess",
|
||||
value: "Back to editing",
|
||||
comment: ""
|
||||
),
|
||||
title: NSLocalizedString("Back to editing", comment: ""),
|
||||
style: .default,
|
||||
accessibilityId: .accessMethodUnreachableBackButton
|
||||
),
|
||||
|
||||
+3
-3
@@ -45,11 +45,11 @@ extension MethodTestingStatusCellContentConfiguration.Status {
|
||||
var text: String {
|
||||
switch self {
|
||||
case .unreachable:
|
||||
NSLocalizedString("API_UNREACHABLE", tableName: "APIAccess", value: "API unreachable", comment: "")
|
||||
NSLocalizedString("API unreachable", comment: "")
|
||||
case .reachable:
|
||||
NSLocalizedString("API_REACHABLE", tableName: "APIAccess", value: "API reachable", comment: "")
|
||||
NSLocalizedString("API reachable", comment: "")
|
||||
case .testing:
|
||||
NSLocalizedString("API_TESTING_INPROGRESS", tableName: "APIAccess", value: "Testing...", comment: "")
|
||||
NSLocalizedString("Testing...", comment: "")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-24
@@ -37,12 +37,7 @@ class ListAccessMethodCoordinator: Coordinator, Presenting, SettingsChildCoordin
|
||||
), delegate: self)
|
||||
)
|
||||
let host = UIHostingController(rootView: view)
|
||||
host.title = NSLocalizedString(
|
||||
"NAVIGATION_TITLE",
|
||||
tableName: "Settings",
|
||||
value: "API access",
|
||||
comment: ""
|
||||
)
|
||||
host.title = NSLocalizedString("API access", comment: "")
|
||||
host.view.setAccessibilityIdentifier(.apiAccessView)
|
||||
|
||||
navigationController.pushViewController(host, animated: animated)
|
||||
@@ -87,41 +82,28 @@ class ListAccessMethodCoordinator: Coordinator, Presenting, SettingsChildCoordin
|
||||
}
|
||||
|
||||
private func about() {
|
||||
let header = NSLocalizedString(
|
||||
"ABOUT_API_ACCESS_HEADER",
|
||||
tableName: "APIAccess",
|
||||
value: "API access",
|
||||
comment: ""
|
||||
)
|
||||
let header = NSLocalizedString("API access", comment: "")
|
||||
let preamble = NSLocalizedString(
|
||||
"ABOUT_API_ACCESS_PREAMBLE",
|
||||
tableName: "APIAccess",
|
||||
value: "Manage default and setup custom methods to access the Mullvad API.",
|
||||
"Manage default and setup custom methods to access the Mullvad API.",
|
||||
comment: ""
|
||||
)
|
||||
let body = [
|
||||
NSLocalizedString(
|
||||
"ABOUT_API_ACCESS_BODY_1",
|
||||
tableName: "APIAccess",
|
||||
value: """
|
||||
"""
|
||||
The app needs to communicate with a Mullvad API server to log you in, fetch server lists, \
|
||||
and other critical operations.
|
||||
""",
|
||||
comment: ""
|
||||
),
|
||||
NSLocalizedString(
|
||||
"ABOUT_API_ACCESS_BODY_2",
|
||||
tableName: "APIAccess",
|
||||
value: """
|
||||
"""
|
||||
On some networks, where various types of censorship are being used, the API servers might \
|
||||
not be directly reachable.
|
||||
""",
|
||||
comment: ""
|
||||
),
|
||||
NSLocalizedString(
|
||||
"ABOUT_API_ACCESS_BODY_3",
|
||||
tableName: "APIAccess",
|
||||
value: """
|
||||
"""
|
||||
This feature allows you to circumvent that censorship by adding custom ways to access the \
|
||||
API via proxies and similar methods.
|
||||
""",
|
||||
|
||||
@@ -29,17 +29,10 @@ struct ListAccessMethodView<ViewModel>: View where ViewModel: ListAccessViewMode
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
let text = NSLocalizedString(
|
||||
"ACCESS_METHOD_HEADER_BODY",
|
||||
tableName: "APIAccess",
|
||||
value: "Manage default and setup custom methods to access the Mullvad API. ",
|
||||
comment: ""
|
||||
)
|
||||
let about = NSLocalizedString(
|
||||
"ACCESS_METHOD_HEADER_BODY",
|
||||
tableName: "APIAccess",
|
||||
value: "About API access…",
|
||||
"Manage default and setup custom methods to access the Mullvad API. ",
|
||||
comment: ""
|
||||
)
|
||||
let about = NSLocalizedString("About API access…", comment: "")
|
||||
|
||||
MullvadList(viewModel.items, header: {
|
||||
MullvadInfoHeaderView(
|
||||
@@ -68,20 +61,10 @@ struct ListAccessMethodView<ViewModel>: View where ViewModel: ListAccessViewMode
|
||||
nil
|
||||
}
|
||||
let state = viewModel.itemInUse?.id == item.id
|
||||
? NSLocalizedString(
|
||||
"LIST_ACCESS_METHODS_IN_USE_ITEM",
|
||||
tableName: "APIAccess",
|
||||
value: "In use",
|
||||
comment: ""
|
||||
)
|
||||
? NSLocalizedString("In use", comment: "")
|
||||
: (
|
||||
!item.isEnabled
|
||||
? NSLocalizedString(
|
||||
"LIST_ACCESS_METHODS_DISABLED",
|
||||
tableName: "APIAccess",
|
||||
value: "Disabled",
|
||||
comment: ""
|
||||
)
|
||||
? NSLocalizedString("Disabled", comment: "")
|
||||
: nil
|
||||
)
|
||||
MullvadListNavigationItemView(
|
||||
|
||||
+5
-30
@@ -16,12 +16,7 @@ struct AccessMethodValidationError: LocalizedError, Equatable {
|
||||
|
||||
var errorDescription: String? {
|
||||
if fieldErrors.count > 1 {
|
||||
NSLocalizedString(
|
||||
"VALIDATION_ERRORS_MULTIPLE",
|
||||
tableName: "APIAccess",
|
||||
value: "Multiple validation errors occurred.",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Multiple validation errors occurred.", comment: "")
|
||||
} else {
|
||||
fieldErrors.first?.localizedDescription
|
||||
}
|
||||
@@ -75,34 +70,14 @@ struct AccessMethodFieldValidationError: LocalizedError, Equatable {
|
||||
var errorDescription: String? {
|
||||
switch kind {
|
||||
case .emptyValue:
|
||||
NSLocalizedString(
|
||||
"VALIDATION_ERRORS_EMPTY_FIELD",
|
||||
tableName: "APIAccess",
|
||||
value: "\(field) cannot be empty.",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("\(field) cannot be empty.", comment: "")
|
||||
case .invalidIPAddress:
|
||||
NSLocalizedString(
|
||||
"VALIDATION_ERRORS_INVALD ADDRESS",
|
||||
tableName: "APIAccess",
|
||||
value: "Please enter a valid IPv4 or IPv6 address.",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Please enter a valid IPv4 or IPv6 address.", comment: "")
|
||||
case .invalidPort:
|
||||
NSLocalizedString(
|
||||
"VALIDATION_ERRORS_INVALID_PORT",
|
||||
tableName: "APIAccess",
|
||||
value: "Please enter a valid port.",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Please enter a valid port.", comment: "")
|
||||
case .nameTooLong:
|
||||
String(
|
||||
format: NSLocalizedString(
|
||||
"VALIDATION_ERRORS_NAME_TOO_LONG",
|
||||
tableName: "APIAccess",
|
||||
value: "Name should be no longer than %i characters.",
|
||||
comment: ""
|
||||
),
|
||||
format: NSLocalizedString("Name should be no longer than %i characters.", comment: ""),
|
||||
NameInputFormatter.maxLength
|
||||
)
|
||||
}
|
||||
|
||||
@@ -81,48 +81,24 @@ extension AccessMethodViewModel {
|
||||
switch id {
|
||||
case AccessMethodRepository.directId:
|
||||
InfoHeaderConfig(
|
||||
body: NSLocalizedString(
|
||||
"DIRECT_ACCESS_METHOD_HEADER_BODY",
|
||||
tableName: "APIAccess",
|
||||
value: "The app communicates with a Mullvad API server directly.",
|
||||
comment: ""
|
||||
),
|
||||
link: NSLocalizedString(
|
||||
"DIRECT_ACCESS_METHOD_HEADER_LINK",
|
||||
tableName: "APIAccess",
|
||||
value: "About Direct method...",
|
||||
comment: ""
|
||||
)
|
||||
body: NSLocalizedString("The app communicates with a Mullvad API server directly.", comment: ""),
|
||||
link: NSLocalizedString("About Direct method...", comment: "")
|
||||
)
|
||||
case AccessMethodRepository.bridgeId:
|
||||
InfoHeaderConfig(
|
||||
body: NSLocalizedString(
|
||||
"BRIDGES_ACCESS_METHOD_HEADER_BODY",
|
||||
tableName: "APIAccess",
|
||||
value: "The app communicates with a Mullvad API server via a Mullvad bridge server.",
|
||||
"The app communicates with a Mullvad API server via a Mullvad bridge server.",
|
||||
comment: ""
|
||||
),
|
||||
link: NSLocalizedString(
|
||||
"BRIDGES_ACCESS_METHOD_HEADER_LINK",
|
||||
tableName: "APIAccess",
|
||||
value: "About Mullvad bridges method...",
|
||||
comment: ""
|
||||
)
|
||||
link: NSLocalizedString("About Mullvad bridges method...", comment: "")
|
||||
)
|
||||
case AccessMethodRepository.encryptedDNSId:
|
||||
InfoHeaderConfig(
|
||||
body: NSLocalizedString(
|
||||
"ENCRYPTED_DNS_ACCESS_METHOD_HEADER_BODY",
|
||||
tableName: "APIAccess",
|
||||
value: "The app communicates with a Mullvad API server via a proxy address.",
|
||||
"The app communicates with a Mullvad API server via a proxy address.",
|
||||
comment: ""
|
||||
),
|
||||
link: NSLocalizedString(
|
||||
"ENCRYPTED_DNS_ACCESS_METHOD_HEADER_LINK",
|
||||
tableName: "APIAccess",
|
||||
value: "About Encrypted DNS proxy method...",
|
||||
comment: ""
|
||||
)
|
||||
link: NSLocalizedString("About Encrypted DNS proxy method...", comment: "")
|
||||
)
|
||||
default:
|
||||
nil
|
||||
@@ -133,87 +109,50 @@ extension AccessMethodViewModel {
|
||||
switch id {
|
||||
case AccessMethodRepository.directId:
|
||||
InfoModalConfig(
|
||||
header: NSLocalizedString(
|
||||
"DIRECT_ACCESS_METHOD_MODAL_HEADER",
|
||||
tableName: "APIAccess",
|
||||
value: "Direct",
|
||||
comment: ""
|
||||
),
|
||||
preamble: NSLocalizedString(
|
||||
"DIRECT_ACCESS_METHOD_MODAL_PREAMBLE",
|
||||
tableName: "APIAccess",
|
||||
value: "The app communicates with a Mullvad API server directly.",
|
||||
comment: ""
|
||||
),
|
||||
header: NSLocalizedString("Direct", comment: ""),
|
||||
preamble: NSLocalizedString("The app communicates with a Mullvad API server directly.", comment: ""),
|
||||
body: [
|
||||
NSLocalizedString(
|
||||
"DIRECT_ACCESS_METHOD_MODAL_BODY_PART_1",
|
||||
tableName: "APIAccess",
|
||||
value: """
|
||||
"""
|
||||
With the "Direct" method, the app communicates with a Mullvad API server \
|
||||
directly without any intermediate proxies.
|
||||
""",
|
||||
comment: ""
|
||||
),
|
||||
NSLocalizedString(
|
||||
"DIRECT_ACCESS_METHOD_MODAL_BODY_PART_2",
|
||||
tableName: "APIAccess",
|
||||
value: "This can be useful when you are not affected by censorship.",
|
||||
comment: ""
|
||||
),
|
||||
NSLocalizedString("This can be useful when you are not affected by censorship.", comment: ""),
|
||||
]
|
||||
)
|
||||
case AccessMethodRepository.bridgeId:
|
||||
InfoModalConfig(
|
||||
header: NSLocalizedString(
|
||||
"BRIDGES_ACCESS_METHOD_MODAL_HEADER",
|
||||
tableName: "APIAccess",
|
||||
value: "Mullvad bridges",
|
||||
comment: ""
|
||||
),
|
||||
header: NSLocalizedString("Mullvad bridges", comment: ""),
|
||||
preamble: NSLocalizedString(
|
||||
"BRIDGES_ACCESS_METHOD_MODAL_PREAMBLE",
|
||||
tableName: "APIAccess",
|
||||
value: "The app communicates with a Mullvad API server via a Mullvad bridge server.",
|
||||
"The app communicates with a Mullvad API server via a Mullvad bridge server.",
|
||||
comment: ""
|
||||
),
|
||||
body: [
|
||||
NSLocalizedString(
|
||||
"BRIDGES_ACCESS_METHOD_MODAL_BODY_PART_1",
|
||||
tableName: "APIAccess",
|
||||
value: """
|
||||
"""
|
||||
With the "Mullvad bridges" method, the app communicates with a Mullvad API server via a \
|
||||
Mullvad bridge server. It does this by sending the traffic obfuscated by Shadowsocks.
|
||||
""",
|
||||
comment: ""
|
||||
),
|
||||
NSLocalizedString(
|
||||
"BRIDGES_ACCESS_METHOD_MODAL_BODY_PART_2",
|
||||
tableName: "APIAccess",
|
||||
value: "This can be useful if the API is censored but Mullvad’s bridge servers are not.",
|
||||
"This can be useful if the API is censored but Mullvad’s bridge servers are not.",
|
||||
comment: ""
|
||||
),
|
||||
]
|
||||
)
|
||||
case AccessMethodRepository.encryptedDNSId:
|
||||
InfoModalConfig(
|
||||
header: NSLocalizedString(
|
||||
"ENCRYPTED_DNS_ACCESS_METHOD_MODAL_HEADER",
|
||||
tableName: "APIAccess",
|
||||
value: "Encrypted DNS proxy",
|
||||
comment: ""
|
||||
),
|
||||
header: NSLocalizedString("Encrypted DNS proxy", comment: ""),
|
||||
preamble: NSLocalizedString(
|
||||
"ENCRYPTED_DNS_ACCESS_METHOD_MODAL_PREAMBLE",
|
||||
tableName: "APIAccess",
|
||||
value: "The app communicates with a Mullvad API server via a proxy address.",
|
||||
"The app communicates with a Mullvad API server via a proxy address.",
|
||||
comment: ""
|
||||
),
|
||||
body: [
|
||||
NSLocalizedString(
|
||||
"ENCRYPTED_DNS_ACCESS_METHOD_MODAL_BODY_PART_1",
|
||||
tableName: "APIAccess",
|
||||
value: """
|
||||
"""
|
||||
With the "Encrypted DNS proxy" method, the app will communicate with our \
|
||||
Mullvad API through a proxy address.
|
||||
It does this by retrieving an address from a DNS over HTTPS (DoH) server and \
|
||||
@@ -222,9 +161,7 @@ extension AccessMethodViewModel {
|
||||
comment: ""
|
||||
),
|
||||
NSLocalizedString(
|
||||
"ENCRYPTED_DNS_ACCESS_METHOD_MODAL_BODY_PART_2",
|
||||
tableName: "APIAccess",
|
||||
value: """
|
||||
"""
|
||||
If you are not connected to our VPN, then the Encrypted DNS proxy will use your own non-VPN IP \
|
||||
when connecting.
|
||||
The DoH servers are hosted by one of the following providers: Quad9 or Cloudflare.
|
||||
|
||||
+1
-6
@@ -27,12 +27,7 @@ struct AccessMethodProtocolPicker {
|
||||
let controller = ListItemPickerViewController(dataSource: dataSource, selectedItemID: currentValue)
|
||||
controller.view.setAccessibilityIdentifier(.accessMethodProtocolPickerView)
|
||||
|
||||
controller.navigationItem.title = NSLocalizedString(
|
||||
"SELECT_PROTOCOL_NAV_TITLE",
|
||||
tableName: "APIAccess",
|
||||
value: "Type",
|
||||
comment: ""
|
||||
)
|
||||
controller.navigationItem.title = NSLocalizedString("Type", comment: "")
|
||||
|
||||
controller.onSelect = { selectedItem in
|
||||
navigationController.popViewController(animated: true)
|
||||
|
||||
+1
-6
@@ -26,12 +26,7 @@ struct ShadowsocksCipherPicker {
|
||||
let dataSource = ShadowsocksCipherPickerDataSource()
|
||||
let controller = ListItemPickerViewController(dataSource: dataSource, selectedItemID: currentValue)
|
||||
|
||||
controller.navigationItem.title = NSLocalizedString(
|
||||
"SELECT_SHADOWSOCKS_CIPHER_NAV_TITLE",
|
||||
tableName: "APIAccess",
|
||||
value: "Cipher",
|
||||
comment: ""
|
||||
)
|
||||
controller.navigationItem.title = NSLocalizedString("Cipher", comment: "")
|
||||
|
||||
controller.onSelect = { selectedItem in
|
||||
navigationController.popViewController(animated: true)
|
||||
|
||||
@@ -15,14 +15,9 @@ struct DAITAMultihopNotice: View {
|
||||
.resizable()
|
||||
.frame(width: 18, height: 18)
|
||||
.foregroundStyle(Color(.primaryTextColor).opacity(0.6))
|
||||
Text(NSLocalizedString(
|
||||
"SETTINGS_DAITA_MULTIHOP_ENABLED",
|
||||
tableName: "Settings",
|
||||
value: "Multihop is being used to enable DAITA for your selected location.",
|
||||
comment: ""
|
||||
))
|
||||
.font(.mullvadTinySemiBold)
|
||||
.foregroundColor(Color(.primaryTextColor).opacity(0.6))
|
||||
Text(NSLocalizedString("Multihop is being used to enable DAITA for your selected location.", comment: ""))
|
||||
.font(.mullvadTinySemiBold)
|
||||
.foregroundColor(Color(.primaryTextColor).opacity(0.6))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,12 +51,7 @@ class DAITASettingsCoordinator: Coordinator, SettingsChildCoordinator, Presentab
|
||||
}
|
||||
|
||||
let host = UIHostingController(rootView: view)
|
||||
host.title = NSLocalizedString(
|
||||
"NAVIGATION_TITLE_DAITA",
|
||||
tableName: "Settings",
|
||||
value: "DAITA",
|
||||
comment: ""
|
||||
)
|
||||
host.title = NSLocalizedString("DAITA", comment: "")
|
||||
host.view.setAccessibilityIdentifier(.daitaView)
|
||||
customiseNavigation(on: host)
|
||||
|
||||
@@ -88,31 +83,16 @@ class DAITASettingsCoordinator: Coordinator, SettingsChildCoordinator, Presentab
|
||||
id: "settings-daita-prompt",
|
||||
accessibilityIdentifier: .daitaPromptAlert,
|
||||
icon: .warning,
|
||||
message: NSLocalizedString(
|
||||
"SETTINGS_DAITA_ENABLE_TEXT",
|
||||
tableName: "DAITA",
|
||||
value: item.description,
|
||||
comment: ""
|
||||
),
|
||||
message: item.description,
|
||||
buttons: [
|
||||
AlertAction(
|
||||
title: String(format: NSLocalizedString(
|
||||
"SETTINGS_DAITA_ENABLE_OK_ACTION",
|
||||
tableName: "DAITA",
|
||||
value: "Enable \"%@\"",
|
||||
comment: ""
|
||||
), item.title),
|
||||
title: String(format: NSLocalizedString("Enable \"%@\"", comment: ""), item.title),
|
||||
style: .default,
|
||||
accessibilityId: .daitaConfirmAlertEnableButton,
|
||||
handler: { onSave() }
|
||||
),
|
||||
AlertAction(
|
||||
title: NSLocalizedString(
|
||||
"SETTINGS_DAITA_ENABLE_CANCEL_ACTION",
|
||||
tableName: "DAITA",
|
||||
value: "Cancel",
|
||||
comment: ""
|
||||
),
|
||||
title: NSLocalizedString("Cancel", comment: ""),
|
||||
style: .default,
|
||||
handler: { onDiscard() }
|
||||
),
|
||||
|
||||
@@ -21,9 +21,9 @@ enum DAITASettingsPromptItem: CustomStringConvertible {
|
||||
case let .daitaSettingIncompatibleWithSinglehop(setting), let .daitaSettingIncompatibleWithMultihop(setting):
|
||||
switch setting {
|
||||
case .daita:
|
||||
"DAITA"
|
||||
NSLocalizedString("DAITA", comment: "")
|
||||
case .directOnly:
|
||||
"Direct only"
|
||||
NSLocalizedString("Direct only", comment: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,15 +31,19 @@ enum DAITASettingsPromptItem: CustomStringConvertible {
|
||||
var description: String {
|
||||
switch self {
|
||||
case .daitaSettingIncompatibleWithSinglehop:
|
||||
"""
|
||||
NSLocalizedString("""
|
||||
DAITA isn't available at the currently selected location. After enabling, please go to \
|
||||
the "Select location" view and select a location that supports DAITA.
|
||||
"""
|
||||
""", comment: "")
|
||||
|
||||
case .daitaSettingIncompatibleWithMultihop:
|
||||
"""
|
||||
DAITA isn't available on the current entry server. After enabling, please go to the \
|
||||
"Select location" view and select an entry location that supports DAITA.
|
||||
"""
|
||||
NSLocalizedString(
|
||||
"""
|
||||
DAITA isn't available on the current entry server. After enabling, please go to the \
|
||||
"Select location" view and select an entry location that supports DAITA.
|
||||
""",
|
||||
comment: ""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,33 +31,21 @@ struct SettingsDAITAView<ViewModel>: View where ViewModel: TunnelSettingsObserva
|
||||
GroupedRowView {
|
||||
SwitchRowView(
|
||||
isOn: daitaIsEnabled,
|
||||
text: NSLocalizedString(
|
||||
"SETTINGS_SWITCH_DAITA_ENABLE",
|
||||
tableName: "Settings",
|
||||
value: "Enable",
|
||||
comment: ""
|
||||
),
|
||||
text: NSLocalizedString("Enable", comment: ""),
|
||||
accessibilityId: .daitaSwitch
|
||||
)
|
||||
RowSeparator()
|
||||
SwitchRowView(
|
||||
isOn: directOnlyIsEnabled,
|
||||
disabled: !daitaIsEnabled.wrappedValue,
|
||||
text: NSLocalizedString(
|
||||
"SETTINGS_SWITCH_DAITA_DIRECT_ONLY",
|
||||
tableName: "Settings",
|
||||
value: "Direct only",
|
||||
comment: ""
|
||||
),
|
||||
text: NSLocalizedString("Direct only", comment: ""),
|
||||
accessibilityId: .daitaDirectOnlySwitch
|
||||
)
|
||||
}
|
||||
|
||||
SettingsRowViewFooter(
|
||||
text: NSLocalizedString(
|
||||
"SETTINGS_SWITCH_DAITA_ENABLE",
|
||||
tableName: "Settings",
|
||||
value: """
|
||||
"""
|
||||
By enabling "Direct only" you will have to manually select a server that \
|
||||
is DAITA-enabled. Multihop won't automatically be used to enable DAITA with \
|
||||
any server.
|
||||
@@ -116,9 +104,7 @@ extension SettingsDAITAView {
|
||||
pages: [
|
||||
SettingsInfoViewModelPage(
|
||||
body: NSLocalizedString(
|
||||
"SETTINGS_INFO_DAITA_PAGE_1",
|
||||
tableName: "Settings",
|
||||
value: """
|
||||
"""
|
||||
**Attention: This increases network traffic and will also negatively affect speed, latency, \
|
||||
and battery usage. Use with caution on limited plans.**
|
||||
|
||||
@@ -134,9 +120,7 @@ extension SettingsDAITAView {
|
||||
),
|
||||
SettingsInfoViewModelPage(
|
||||
body: NSLocalizedString(
|
||||
"SETTINGS_INFO_DAITA_PAGE_2",
|
||||
tableName: "Settings",
|
||||
value: """
|
||||
"""
|
||||
If an observer monitors these data packets, DAITA makes it significantly \
|
||||
harder for them to identify which websites you are visiting or with whom \
|
||||
you are communicating.
|
||||
|
||||
@@ -68,35 +68,24 @@ extension IPOverrideCoordinator: @preconcurrency IPOverrideViewControllerDelegat
|
||||
}
|
||||
|
||||
func presentAbout() {
|
||||
let header = NSLocalizedString(
|
||||
"IP_OVERRIDE_HEADER",
|
||||
tableName: "IPOverride",
|
||||
value: "Server IP override",
|
||||
comment: ""
|
||||
)
|
||||
let header = NSLocalizedString("Server IP override", comment: "")
|
||||
let body = [
|
||||
NSLocalizedString(
|
||||
"IP_OVERRIDE_BODY_1",
|
||||
tableName: "IPOverride",
|
||||
value: """
|
||||
"""
|
||||
On some networks, where various types of censorship are being used, our server IP addresses are \
|
||||
sometimes blocked.
|
||||
""",
|
||||
comment: ""
|
||||
),
|
||||
NSLocalizedString(
|
||||
"IP_OVERRIDE_BODY_2",
|
||||
tableName: "IPOverride",
|
||||
value: """
|
||||
"""
|
||||
To circumvent this you can import a file or a text, provided by our support team, \
|
||||
with new IP addresses that override the default addresses of the servers in the Select location view.
|
||||
""",
|
||||
comment: ""
|
||||
),
|
||||
NSLocalizedString(
|
||||
"IP_OVERRIDE_BODY_3",
|
||||
tableName: "IPOverride",
|
||||
value: """
|
||||
"""
|
||||
If you are having issues connecting to VPN servers, please contact support.
|
||||
""",
|
||||
comment: ""
|
||||
|
||||
@@ -26,26 +26,11 @@ enum IPOverrideStatus: Equatable, CustomStringConvertible {
|
||||
var title: String {
|
||||
switch self {
|
||||
case .active:
|
||||
NSLocalizedString(
|
||||
"IP_OVERRIDE_STATUS_TITLE_ACTIVE",
|
||||
tableName: "IPOverride",
|
||||
value: "Overrides active",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Overrides active", comment: "")
|
||||
case .noImports, .importFailed:
|
||||
NSLocalizedString(
|
||||
"IP_OVERRIDE_STATUS_TITLE_NO_IMPORTS",
|
||||
tableName: "IPOverride",
|
||||
value: "No overrides imported",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("No overrides imported", comment: "")
|
||||
case .importSuccessful:
|
||||
NSLocalizedString(
|
||||
"IP_OVERRIDE_STATUS_TITLE_IMPORT_SUCCESSFUL",
|
||||
tableName: "IPOverride",
|
||||
value: "Import successful",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Import successful", comment: "")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,19 +58,15 @@ enum IPOverrideStatus: Equatable, CustomStringConvertible {
|
||||
case .active, .noImports:
|
||||
""
|
||||
case let .importFailed(context):
|
||||
String(format: NSLocalizedString(
|
||||
"IP_OVERRIDE_STATUS_DESCRIPTION_INACTIVE",
|
||||
tableName: "IPOverride",
|
||||
value: "Import %@ was unsuccessful, please try again.",
|
||||
comment: ""
|
||||
), context.description)
|
||||
String(
|
||||
format: NSLocalizedString("Import %@ was unsuccessful, please try again.", comment: ""),
|
||||
context.description
|
||||
)
|
||||
case let .importSuccessful(context):
|
||||
String(format: NSLocalizedString(
|
||||
"IP_OVERRIDE_STATUS_DESCRIPTION_INACTIVE",
|
||||
tableName: "IPOverride",
|
||||
value: "Import %@ was successful, overrides are now active.",
|
||||
comment: ""
|
||||
), context.description)
|
||||
String(
|
||||
format: NSLocalizedString("Import %@ was successful, overrides are now active.", comment: ""),
|
||||
context.description
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,12 +14,7 @@ class IPOverrideTextViewController: UIViewController {
|
||||
|
||||
private lazy var importButton: UIBarButtonItem = {
|
||||
return UIBarButtonItem(
|
||||
title: NSLocalizedString(
|
||||
"IMPORT_TEXT_IMPORT_BUTTON",
|
||||
tableName: "IPOverride",
|
||||
value: "Import",
|
||||
comment: ""
|
||||
),
|
||||
title: NSLocalizedString("Import", comment: ""),
|
||||
primaryAction: UIAction(handler: { [weak self] _ in
|
||||
self?.interactor.import(text: self?.textView.text ?? "")
|
||||
self?.dismiss(animated: true)
|
||||
@@ -42,12 +37,7 @@ class IPOverrideTextViewController: UIViewController {
|
||||
|
||||
view.backgroundColor = .secondaryColor
|
||||
|
||||
navigationItem.title = NSLocalizedString(
|
||||
"IMPORT_TEXT_NAVIGATION_TITLE",
|
||||
tableName: "IPOverride",
|
||||
value: "Import via text",
|
||||
comment: ""
|
||||
)
|
||||
navigationItem.title = NSLocalizedString("Import via text", comment: "")
|
||||
|
||||
navigationItem.leftBarButtonItem = UIBarButtonItem(
|
||||
systemItem: .cancel,
|
||||
|
||||
@@ -27,12 +27,7 @@ class IPOverrideViewController: UIViewController {
|
||||
private lazy var clearButton: AppButton = {
|
||||
let button = AppButton(style: .danger)
|
||||
button.addTarget(self, action: #selector(didTapClearButton), for: .touchUpInside)
|
||||
button.setTitle(NSLocalizedString(
|
||||
"IP_OVERRIDE_CLEAR_BUTTON",
|
||||
tableName: "IPOverride",
|
||||
value: "Clear all overrides",
|
||||
comment: ""
|
||||
), for: .normal)
|
||||
button.setTitle(NSLocalizedString("Clear all overrides", comment: ""), for: .normal)
|
||||
return button
|
||||
}()
|
||||
|
||||
@@ -65,12 +60,7 @@ class IPOverrideViewController: UIViewController {
|
||||
}
|
||||
|
||||
private func configureNavigation() {
|
||||
title = NSLocalizedString(
|
||||
"IP_OVERRIDE_HEADER",
|
||||
tableName: "IPOverride",
|
||||
value: "Server IP override",
|
||||
comment: ""
|
||||
)
|
||||
title = NSLocalizedString("Server IP override", comment: "")
|
||||
}
|
||||
|
||||
private func addScrollView() {
|
||||
@@ -112,17 +102,10 @@ class IPOverrideViewController: UIViewController {
|
||||
|
||||
private func addHeaderView() {
|
||||
let body = NSLocalizedString(
|
||||
"IP_OVERRIDE_HEADER_BODY",
|
||||
tableName: "IPOverride",
|
||||
value: "Import files or text with the new IP addresses for the servers in the Select location view.",
|
||||
comment: ""
|
||||
)
|
||||
let link = NSLocalizedString(
|
||||
"IP_OVERRIDE_HEADER_LINK",
|
||||
tableName: "IPOverride",
|
||||
value: "About Server IP override...",
|
||||
"Import files or text with the new IP addresses for the servers in the Select location view.",
|
||||
comment: ""
|
||||
)
|
||||
let link = NSLocalizedString("About Server IP override...", comment: "")
|
||||
|
||||
let headerView = InfoHeaderView(config: InfoHeaderConfig(body: body, link: link))
|
||||
|
||||
@@ -136,22 +119,12 @@ class IPOverrideViewController: UIViewController {
|
||||
private func addImportButtons() {
|
||||
let importTextButton = AppButton(style: .default)
|
||||
importTextButton.addTarget(self, action: #selector(didTapImportTextButton), for: .touchUpInside)
|
||||
importTextButton.setTitle(NSLocalizedString(
|
||||
"IP_OVERRIDE_IMPORT_TEXT_BUTTON",
|
||||
tableName: "IPOverride",
|
||||
value: "Import via text",
|
||||
comment: ""
|
||||
), for: .normal)
|
||||
importTextButton.setTitle(NSLocalizedString("Import via text", comment: ""), for: .normal)
|
||||
importTextButton.titleLabel?.textAlignment = .center
|
||||
|
||||
let importFileButton = AppButton(style: .default)
|
||||
importFileButton.addTarget(self, action: #selector(didTapImportFileButton), for: .touchUpInside)
|
||||
importFileButton.setTitle(NSLocalizedString(
|
||||
"IP_OVERRIDE_IMPORT_FILE_BUTTON",
|
||||
tableName: "IPOverride",
|
||||
value: "Import file",
|
||||
comment: ""
|
||||
), for: .normal)
|
||||
importFileButton.setTitle(NSLocalizedString("Import file", comment: ""), for: .normal)
|
||||
importFileButton.titleLabel?.textAlignment = .center
|
||||
let stackView = UIStackView(arrangedSubviews: [importTextButton, importFileButton])
|
||||
stackView.distribution = .fillEqually
|
||||
@@ -168,16 +141,9 @@ class IPOverrideViewController: UIViewController {
|
||||
let presentation = AlertPresentation(
|
||||
id: "ip-override-clear-alert",
|
||||
icon: .alert,
|
||||
title: NSLocalizedString(
|
||||
"IP_OVERRIDE_CLEAR_DIALOG_TITLE",
|
||||
tableName: "IPOverride",
|
||||
value: "Clear all overrides?",
|
||||
comment: ""
|
||||
),
|
||||
title: NSLocalizedString("Clear all overrides?", comment: ""),
|
||||
message: NSLocalizedString(
|
||||
"IP_OVERRIDE_CLEAR_DIALOG_MESSAGE",
|
||||
tableName: "IPOverride",
|
||||
value: """
|
||||
"""
|
||||
Clearing the imported overrides changes the server IPs, in the Select location view, \
|
||||
back to default.
|
||||
""",
|
||||
@@ -185,24 +151,14 @@ class IPOverrideViewController: UIViewController {
|
||||
),
|
||||
buttons: [
|
||||
AlertAction(
|
||||
title: NSLocalizedString(
|
||||
"IP_OVERRIDE_CLEAR_DIALOG_CLEAR_BUTTON",
|
||||
tableName: "IPOverride",
|
||||
value: "Clear",
|
||||
comment: ""
|
||||
),
|
||||
title: NSLocalizedString("Clear", comment: ""),
|
||||
style: .destructive,
|
||||
handler: { [weak self] in
|
||||
self?.interactor.deleteAllOverrides()
|
||||
}
|
||||
),
|
||||
AlertAction(
|
||||
title: NSLocalizedString(
|
||||
"IP_OVERRIDE_CLEAR_DIALOG_CANCEL_BUTTON",
|
||||
tableName: "IPOverride",
|
||||
value: "Cancel",
|
||||
comment: ""
|
||||
),
|
||||
title: NSLocalizedString("Cancel", comment: ""),
|
||||
style: .default
|
||||
),
|
||||
]
|
||||
|
||||
@@ -39,12 +39,7 @@ class MultihopSettingsCoordinator: Coordinator, SettingsChildCoordinator, Presen
|
||||
let view = SettingsMultihopView(tunnelViewModel: self.viewModel)
|
||||
|
||||
let host = UIHostingController(rootView: view)
|
||||
host.title = NSLocalizedString(
|
||||
"NAVIGATION_TITLE_MULTIHOP",
|
||||
tableName: "Settings",
|
||||
value: "Multihop",
|
||||
comment: ""
|
||||
)
|
||||
host.title = NSLocalizedString("Multihop", comment: "")
|
||||
host.view.setAccessibilityIdentifier(.multihopView)
|
||||
customiseNavigation(on: host)
|
||||
|
||||
|
||||
@@ -19,12 +19,7 @@ struct SettingsMultihopView<ViewModel>: View where ViewModel: TunnelSettingsObse
|
||||
|
||||
SwitchRowView(
|
||||
isOn: $tunnelViewModel.value.isEnabled,
|
||||
text: NSLocalizedString(
|
||||
"SETTINGS_SWITCH_MULTIHOP",
|
||||
tableName: "Settings",
|
||||
value: "Enable",
|
||||
comment: ""
|
||||
),
|
||||
text: NSLocalizedString("Enable", comment: ""),
|
||||
accessibilityId: .multihopSwitch
|
||||
)
|
||||
.padding(.leading, UIMetrics.contentInsets.left)
|
||||
@@ -44,9 +39,7 @@ extension SettingsMultihopView {
|
||||
pages: [
|
||||
SettingsInfoViewModelPage(
|
||||
body: NSLocalizedString(
|
||||
"SETTINGS_INFO_MULTIHOP",
|
||||
tableName: "Settings",
|
||||
value: """
|
||||
"""
|
||||
Multihop routes your traffic into one WireGuard server and out another, making it \
|
||||
harder to trace. This results in increased latency but increases anonymity online.
|
||||
""",
|
||||
|
||||
@@ -114,12 +114,7 @@ struct SettingsViewControllerFactory {
|
||||
let view = SettingsMultihopView(tunnelViewModel: viewModel)
|
||||
|
||||
let host = UIHostingController(rootView: view)
|
||||
host.title = NSLocalizedString(
|
||||
"NAVIGATION_TITLE_MULTIHOP",
|
||||
tableName: "Settings",
|
||||
value: "Multihop",
|
||||
comment: ""
|
||||
)
|
||||
host.title = NSLocalizedString("Multihop", comment: "")
|
||||
host.view.setAccessibilityIdentifier(.multihopView)
|
||||
|
||||
return .viewController(host)
|
||||
|
||||
@@ -134,9 +134,7 @@ struct SettingsInfoView: View {
|
||||
pages: [
|
||||
SettingsInfoViewModelPage(
|
||||
body: NSLocalizedString(
|
||||
"SETTINGS_INFO_DAITA_PAGE_1",
|
||||
tableName: "Settings",
|
||||
value: """
|
||||
"""
|
||||
**Attention: This increases network traffic and will also negatively affect speed, latency, \
|
||||
and battery usage. Use with caution on limited plans.**
|
||||
|
||||
@@ -152,9 +150,7 @@ struct SettingsInfoView: View {
|
||||
),
|
||||
SettingsInfoViewModelPage(
|
||||
body: NSLocalizedString(
|
||||
"SETTINGS_INFO_DAITA_PAGE_2",
|
||||
tableName: "Settings",
|
||||
value: """
|
||||
"""
|
||||
If an observer monitors these data packets, DAITA makes it significantly \
|
||||
harder for them to identify which websites you are visiting or with whom \
|
||||
you are communicating.
|
||||
|
||||
@@ -93,31 +93,19 @@ class TunnelCoordinator: Coordinator, Presenting {
|
||||
id: "main-cancel-tunnel-alert",
|
||||
icon: .alert,
|
||||
message: NSLocalizedString(
|
||||
"CANCEL_TUNNEL_ALERT_MESSAGE",
|
||||
tableName: "Main",
|
||||
value: "If you disconnect now, you won’t be able to secure your connection until the device is online.",
|
||||
"If you disconnect now, you won’t be able to secure your connection until the device is online.",
|
||||
comment: ""
|
||||
),
|
||||
buttons: [
|
||||
AlertAction(
|
||||
title: NSLocalizedString(
|
||||
"CANCEL_TUNNEL_ALERT_DISCONNECT_ACTION",
|
||||
tableName: "Main",
|
||||
value: "Disconnect",
|
||||
comment: ""
|
||||
),
|
||||
title: NSLocalizedString("Disconnect", comment: ""),
|
||||
style: .destructive,
|
||||
handler: { [weak self] in
|
||||
self?.tunnelManager.stopTunnel()
|
||||
}
|
||||
),
|
||||
AlertAction(
|
||||
title: NSLocalizedString(
|
||||
"CANCEL_TUNNEL_ALERT_CANCEL_ACTION",
|
||||
tableName: "Main",
|
||||
value: "Cancel",
|
||||
comment: ""
|
||||
),
|
||||
title: NSLocalizedString("Cancel", comment: ""),
|
||||
style: .default
|
||||
),
|
||||
]
|
||||
|
||||
@@ -83,9 +83,6 @@ final class WelcomeCoordinator: Coordinator, Poppable, Presenting {
|
||||
extension WelcomeCoordinator: @preconcurrency WelcomeViewControllerDelegate {
|
||||
func didRequestToShowFailToFetchProducts(controller: WelcomeViewController) {
|
||||
let message = NSLocalizedString(
|
||||
"WELCOME_FAILED_TO_FETCH_PRODUCTS_DIALOG",
|
||||
tableName: "Welcome",
|
||||
value:
|
||||
"""
|
||||
Failed to connect to App store, please try again later.
|
||||
""",
|
||||
@@ -98,12 +95,7 @@ extension WelcomeCoordinator: @preconcurrency WelcomeViewControllerDelegate {
|
||||
message: message,
|
||||
buttons: [
|
||||
AlertAction(
|
||||
title: NSLocalizedString(
|
||||
"WELCOME_FAILED_TO_FETCH_PRODUCTS_OK_ACTION",
|
||||
tableName: "Welcome",
|
||||
value: "Got it!",
|
||||
comment: ""
|
||||
),
|
||||
title: NSLocalizedString("Got it!", comment: ""),
|
||||
style: .default
|
||||
),
|
||||
]
|
||||
@@ -115,9 +107,6 @@ extension WelcomeCoordinator: @preconcurrency WelcomeViewControllerDelegate {
|
||||
|
||||
func didRequestToShowInfo(controller: WelcomeViewController) {
|
||||
let message = NSLocalizedString(
|
||||
"WELCOME_DEVICE_CONCEPT_TEXT_DIALOG",
|
||||
tableName: "Welcome",
|
||||
value:
|
||||
"""
|
||||
This is the name assigned to the device. Each device logged in on a \
|
||||
Mullvad account gets a unique name that helps \
|
||||
@@ -135,12 +124,7 @@ extension WelcomeCoordinator: @preconcurrency WelcomeViewControllerDelegate {
|
||||
message: message,
|
||||
buttons: [
|
||||
AlertAction(
|
||||
title: NSLocalizedString(
|
||||
"WELCOME_DEVICE_NAME_DIALOG_OK_ACTION",
|
||||
tableName: "Welcome",
|
||||
value: "Got it!",
|
||||
comment: ""
|
||||
),
|
||||
title: NSLocalizedString("Got it!", comment: ""),
|
||||
style: .default
|
||||
),
|
||||
]
|
||||
|
||||
@@ -17,19 +17,9 @@ extension REST.CreateApplePaymentResponse {
|
||||
func alertTitle(context: Context) -> String {
|
||||
switch context {
|
||||
case .purchase:
|
||||
return NSLocalizedString(
|
||||
"TIME_ADDED_ALERT_SUCCESS_TITLE",
|
||||
tableName: "REST",
|
||||
value: "Thanks for your purchase",
|
||||
comment: ""
|
||||
)
|
||||
return NSLocalizedString("Thanks for your purchase", comment: "")
|
||||
case .restoration:
|
||||
return NSLocalizedString(
|
||||
"RESTORE_PURCHASES_ALERT_TITLE",
|
||||
tableName: "REST",
|
||||
value: "Restore purchases",
|
||||
comment: ""
|
||||
)
|
||||
return NSLocalizedString("Restore purchases", comment: "")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,31 +27,19 @@ extension REST.CreateApplePaymentResponse {
|
||||
switch context {
|
||||
case .purchase:
|
||||
return String(
|
||||
format: NSLocalizedString(
|
||||
"TIME_ADDED_ALERT_SUCCESS_MESSAGE",
|
||||
tableName: "REST",
|
||||
value: "%@ have been added to your account",
|
||||
comment: ""
|
||||
),
|
||||
format: NSLocalizedString("%@ have been added to your account", comment: ""),
|
||||
formattedTimeAdded ?? ""
|
||||
)
|
||||
case .restoration:
|
||||
switch self {
|
||||
case .noTimeAdded:
|
||||
return NSLocalizedString(
|
||||
"RESTORE_PURCHASES_ALERT_NO_TIME_ADDED_MESSAGE",
|
||||
tableName: "REST",
|
||||
value: "Your previous purchases have already been added to this account.",
|
||||
"Your previous purchases have already been added to this account.",
|
||||
comment: ""
|
||||
)
|
||||
case .timeAdded:
|
||||
return String(
|
||||
format: NSLocalizedString(
|
||||
"RESTORE_PURCHASES_ALERT_TIME_ADDED_MESSAGE",
|
||||
tableName: "REST",
|
||||
value: "%@ have been added to your account",
|
||||
comment: ""
|
||||
),
|
||||
format: NSLocalizedString("%@ have been added to your account", comment: ""),
|
||||
formattedTimeAdded ?? ""
|
||||
)
|
||||
}
|
||||
@@ -73,19 +51,9 @@ extension REST.CreateApplePaymentResponse.Context {
|
||||
var errorTitle: String {
|
||||
switch self {
|
||||
case .purchase:
|
||||
return NSLocalizedString(
|
||||
"CANNOT_COMPLETE_PURCHASE_ALERT_TITLE",
|
||||
tableName: "Payment",
|
||||
value: "Cannot complete the purchase",
|
||||
comment: ""
|
||||
)
|
||||
return NSLocalizedString("Cannot complete the purchase", comment: "")
|
||||
case .restoration:
|
||||
return NSLocalizedString(
|
||||
"RESTORE_PURCHASES_FAILURE_ALERT_TITLE",
|
||||
tableName: "Payment",
|
||||
value: "Cannot restore purchases",
|
||||
comment: ""
|
||||
)
|
||||
return NSLocalizedString("Cannot restore purchases", comment: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,78 +15,38 @@ extension REST.Error: MullvadTypes.DisplayError {
|
||||
switch self {
|
||||
case let .network(urlError):
|
||||
return String(
|
||||
format: NSLocalizedString(
|
||||
"NETWORK_ERROR",
|
||||
tableName: "REST",
|
||||
value: "Network error: %@",
|
||||
comment: ""
|
||||
),
|
||||
format: NSLocalizedString("Network error: %@", comment: ""),
|
||||
urlError.localizedDescription
|
||||
)
|
||||
|
||||
case let .unhandledResponse(statusCode, serverResponse):
|
||||
guard let serverResponse else {
|
||||
return String(format: NSLocalizedString(
|
||||
"UNEXPECTED_RESPONSE",
|
||||
tableName: "REST",
|
||||
value: "Unexpected server response: %d",
|
||||
comment: ""
|
||||
), statusCode)
|
||||
return String(format: NSLocalizedString("Unexpected server response: %d", comment: ""), statusCode)
|
||||
}
|
||||
|
||||
switch serverResponse.code {
|
||||
case .invalidAccount:
|
||||
return NSLocalizedString(
|
||||
"INVALID_ACCOUNT_ERROR",
|
||||
tableName: "REST",
|
||||
value: "Invalid account",
|
||||
comment: ""
|
||||
)
|
||||
return NSLocalizedString("Invalid account", comment: "")
|
||||
|
||||
case .maxDevicesReached:
|
||||
return NSLocalizedString(
|
||||
"MAX_DEVICES_REACHED_ERROR",
|
||||
tableName: "REST",
|
||||
value: "Too many devices registered with account",
|
||||
comment: ""
|
||||
)
|
||||
return NSLocalizedString("Too many devices registered with account", comment: "")
|
||||
|
||||
case .serviceUnavailable:
|
||||
return NSLocalizedString(
|
||||
"SERVICE_UNAVAILABLE",
|
||||
tableName: "REST",
|
||||
value: "We are having some issues, please try again later",
|
||||
comment: ""
|
||||
)
|
||||
return NSLocalizedString("We are having some issues, please try again later", comment: "")
|
||||
|
||||
case .tooManyRequests:
|
||||
return NSLocalizedString(
|
||||
"TOO_MANY_REQUESTS",
|
||||
tableName: "REST",
|
||||
value: "We are having some issues, please try again later",
|
||||
comment: ""
|
||||
)
|
||||
return NSLocalizedString("We are having some issues, please try again later", comment: "")
|
||||
|
||||
default:
|
||||
return String(
|
||||
format: NSLocalizedString(
|
||||
"SERVER_ERROR",
|
||||
tableName: "REST",
|
||||
value: "Unexpected server response: %1$@ (HTTP status: %2$d)",
|
||||
comment: ""
|
||||
),
|
||||
format: NSLocalizedString("Unexpected server response: %1$@ (HTTP status: %2$d)", comment: ""),
|
||||
serverResponse.code.rawValue,
|
||||
statusCode
|
||||
)
|
||||
}
|
||||
|
||||
default:
|
||||
return NSLocalizedString(
|
||||
"INTERNAL_ERROR",
|
||||
tableName: "REST",
|
||||
value: "Internal error.",
|
||||
comment: ""
|
||||
)
|
||||
return NSLocalizedString("Internal error.", comment: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,40 +12,15 @@ extension SKError: Foundation.LocalizedError {
|
||||
public var errorDescription: String? {
|
||||
switch code {
|
||||
case .unknown:
|
||||
return NSLocalizedString(
|
||||
"UNKNOWN_ERROR",
|
||||
tableName: "StoreKitErrors",
|
||||
value: "Unknown error.",
|
||||
comment: ""
|
||||
)
|
||||
return NSLocalizedString("Unknown error.", comment: "")
|
||||
case .clientInvalid:
|
||||
return NSLocalizedString(
|
||||
"CLIENT_INVALID",
|
||||
tableName: "StoreKitErrors",
|
||||
value: "Client is not allowed to issue the request.",
|
||||
comment: ""
|
||||
)
|
||||
return NSLocalizedString("Client is not allowed to issue the request.", comment: "")
|
||||
case .paymentCancelled:
|
||||
return NSLocalizedString(
|
||||
"PAYMENT_CANCELLED",
|
||||
tableName: "StoreKitErrors",
|
||||
value: "The payment request was cancelled.",
|
||||
comment: ""
|
||||
)
|
||||
return NSLocalizedString("The payment request was cancelled.", comment: "")
|
||||
case .paymentInvalid:
|
||||
return NSLocalizedString(
|
||||
"PAYMENT_INVALID",
|
||||
tableName: "StoreKitErrors",
|
||||
value: "Invalid purchase identifier.",
|
||||
comment: ""
|
||||
)
|
||||
return NSLocalizedString("Invalid purchase identifier.", comment: "")
|
||||
case .paymentNotAllowed:
|
||||
return NSLocalizedString(
|
||||
"PAYMENT_NOT_ALLOWED",
|
||||
tableName: "StoreKitErrors",
|
||||
value: "This device is not allowed to make the payment.",
|
||||
comment: ""
|
||||
)
|
||||
return NSLocalizedString("This device is not allowed to make the payment.", comment: "")
|
||||
default:
|
||||
return localizedDescription
|
||||
}
|
||||
|
||||
@@ -14,64 +14,32 @@ extension StorePaymentManagerError: DisplayError {
|
||||
var displayErrorDescription: String? {
|
||||
switch self {
|
||||
case .noAccountSet:
|
||||
return NSLocalizedString(
|
||||
"INTERNAL_ERROR",
|
||||
tableName: "StorePaymentManager",
|
||||
value: "Internal error.",
|
||||
comment: ""
|
||||
)
|
||||
return NSLocalizedString("Internal error.", comment: "")
|
||||
|
||||
case let .validateAccount(error):
|
||||
let reason = (error as? DisplayError)?.displayErrorDescription ?? ""
|
||||
|
||||
return String(
|
||||
format: NSLocalizedString(
|
||||
"VALIDATE_ACCOUNT_ERROR",
|
||||
tableName: "StorePaymentManager",
|
||||
value: "Failed to validate account number: %@",
|
||||
comment: ""
|
||||
), reason
|
||||
format: NSLocalizedString("Failed to validate account number: %@", comment: ""), reason
|
||||
)
|
||||
|
||||
case let .readReceipt(readReceiptError):
|
||||
if readReceiptError is StoreReceiptNotFound {
|
||||
return NSLocalizedString(
|
||||
"RECEIPT_NOT_FOUND_ERROR",
|
||||
tableName: "StorePaymentManager",
|
||||
value: "AppStore receipt is not found on disk.",
|
||||
comment: ""
|
||||
)
|
||||
return NSLocalizedString("AppStore receipt is not found on disk.", comment: "")
|
||||
} else if let storeError = readReceiptError as? SKError {
|
||||
return String(
|
||||
format: NSLocalizedString(
|
||||
"REFRESH_RECEIPT_ERROR",
|
||||
tableName: "StorePaymentManager",
|
||||
value: "Cannot refresh the AppStore receipt: %@",
|
||||
comment: ""
|
||||
),
|
||||
format: NSLocalizedString("Cannot refresh the AppStore receipt: %@", comment: ""),
|
||||
storeError.localizedDescription
|
||||
)
|
||||
} else {
|
||||
return NSLocalizedString(
|
||||
"READ_RECEIPT_ERROR",
|
||||
tableName: "StorePaymentManager",
|
||||
value: "Cannot read the AppStore receipt from disk",
|
||||
comment: ""
|
||||
)
|
||||
return NSLocalizedString("Cannot read the AppStore receipt from disk", comment: "")
|
||||
}
|
||||
|
||||
case let .sendReceipt(error):
|
||||
let reason = (error as? DisplayError)?.displayErrorDescription ?? ""
|
||||
let errorFormat = NSLocalizedString(
|
||||
"SEND_RECEIPT_ERROR",
|
||||
tableName: "StorePaymentManager",
|
||||
value: "Failed to send the receipt to server: %@",
|
||||
comment: ""
|
||||
)
|
||||
let errorFormat = NSLocalizedString("Failed to send the receipt to server: %@", comment: "")
|
||||
let recoverySuggestion = NSLocalizedString(
|
||||
"SEND_RECEIPT_RECOVERY_SUGGESTION",
|
||||
tableName: "StorePaymentManager",
|
||||
value: "Please retry by using the \"Restore purchases\" button.",
|
||||
"Please retry by using the \"Restore purchases\" button.",
|
||||
comment: ""
|
||||
)
|
||||
var errorString = String(format: errorFormat, reason)
|
||||
|
||||
@@ -15,19 +15,9 @@ extension UIBarButtonItem {
|
||||
fileprivate var localizedTitle: String {
|
||||
switch self {
|
||||
case .previous:
|
||||
return NSLocalizedString(
|
||||
"PREVIOUS_BUTTON_TITLE",
|
||||
tableName: "KeyboardNavigation",
|
||||
value: "Previous",
|
||||
comment: "Previous button"
|
||||
)
|
||||
return NSLocalizedString("Previous", comment: "")
|
||||
case .next:
|
||||
return NSLocalizedString(
|
||||
"NEXT_BUTTON_TITLE",
|
||||
tableName: "KeyboardNavigation",
|
||||
value: "Next",
|
||||
comment: "Next button"
|
||||
)
|
||||
return NSLocalizedString("Next", comment: "")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-9
@@ -55,9 +55,8 @@ final class AccountExpiryInAppNotificationProvider: NotificationProvider, InAppN
|
||||
style: .warning,
|
||||
title: durationText,
|
||||
body: NSAttributedString(string: NSLocalizedString(
|
||||
"ACCOUNT_EXPIRY_IN_APP_NOTIFICATION_BODY",
|
||||
value: "You can add more time via the account view or website to continue using the VPN.",
|
||||
comment: "Title for in-app notification, displayed within the last X days until account expiry."
|
||||
"You can add more time via the account view or website to continue using the VPN.",
|
||||
comment: ""
|
||||
))
|
||||
)
|
||||
}
|
||||
@@ -116,11 +115,6 @@ extension AccountExpiryInAppNotificationProvider {
|
||||
)
|
||||
else { return nil }
|
||||
|
||||
return String(format: NSLocalizedString(
|
||||
"ACCOUNT_EXPIRY_IN_APP_NOTIFICATION_TITLE",
|
||||
tableName: "AccountExpiry",
|
||||
value: "%@ left on this account",
|
||||
comment: "Message for in-app notification, displayed within the last X days until account expiry."
|
||||
), duration).uppercased()
|
||||
return String(format: NSLocalizedString("%@ left on this account", comment: ""), duration).uppercased()
|
||||
}
|
||||
}
|
||||
+10
-26
@@ -130,18 +130,8 @@ final class AccountExpirySystemNotificationProvider: NotificationProvider, Syste
|
||||
extension AccountExpirySystemNotificationProvider {
|
||||
private var formattedRemainingDurationTitle: String {
|
||||
accountHasExpired
|
||||
? NSLocalizedString(
|
||||
"ACCOUNT_EXPIRY_SYSTEM_NOTIFICATION_TITLE",
|
||||
tableName: "AccountExpiry",
|
||||
value: "Account credit has expired",
|
||||
comment: "Title for system account expiry notification, fired on account expiry."
|
||||
)
|
||||
: NSLocalizedString(
|
||||
"ACCOUNT_EXPIRY_SYSTEM_NOTIFICATION_TITLE",
|
||||
tableName: "AccountExpiry",
|
||||
value: "Account credit expires soon",
|
||||
comment: "Title for system account expiry notification, fired X days prior to account expiry."
|
||||
)
|
||||
? NSLocalizedString("Account credit has expired", comment: "")
|
||||
: NSLocalizedString("Account credit expires soon", comment: "")
|
||||
}
|
||||
|
||||
private var formattedRemainingDurationBody: String? {
|
||||
@@ -159,22 +149,18 @@ extension AccountExpirySystemNotificationProvider {
|
||||
|
||||
private var expiredText: String {
|
||||
NSLocalizedString(
|
||||
"ACCOUNT_EXPIRY_SYSTEM_NOTIFICATION_BODY",
|
||||
tableName: "AccountExpiry",
|
||||
value: """
|
||||
"""
|
||||
Blocking internet: Your time on this account has expired. To continue using the internet, \
|
||||
please add more time or disconnect the VPN.
|
||||
""",
|
||||
comment: "Message for in-app notification, displayed on account expiry while connected to VPN."
|
||||
comment: ""
|
||||
)
|
||||
}
|
||||
|
||||
private var singleDayText: String {
|
||||
NSLocalizedString(
|
||||
"ACCOUNT_EXPIRY_SYSTEM_NOTIFICATION_BODY",
|
||||
tableName: "AccountExpiry",
|
||||
value: "You have one day left on this account. Please add more time to continue using the VPN.",
|
||||
comment: "Message for in-app notification, displayed within the last 1 day until account expiry."
|
||||
"You have one day left on this account. Please add more time to continue using the VPN.",
|
||||
comment: ""
|
||||
)
|
||||
}
|
||||
|
||||
@@ -189,11 +175,9 @@ extension AccountExpirySystemNotificationProvider {
|
||||
)
|
||||
else { return nil }
|
||||
|
||||
return String(format: NSLocalizedString(
|
||||
"ACCOUNT_EXPIRY_SYSTEM_NOTIFICATION_BODY",
|
||||
tableName: "AccountExpiry",
|
||||
value: "You have %@ left on this account.",
|
||||
comment: "Message for in-app notification, displayed within the last X days until account expiry."
|
||||
), duration.lowercased())
|
||||
return String(
|
||||
format: NSLocalizedString("You have %@ left on this account.", comment: ""),
|
||||
duration.lowercased()
|
||||
)
|
||||
}
|
||||
}
|
||||
+2
-10
@@ -42,11 +42,7 @@ class LatestChangesNotificationProvider: NotificationProvider, InAppNotification
|
||||
return InAppNotificationDescriptor(
|
||||
identifier: identifier,
|
||||
style: .success,
|
||||
title: NSLocalizedString(
|
||||
"LATEST_CHANGES_IN_APP_NOTIFICATION_TITLE",
|
||||
value: "NEW VERSION INSTALLED",
|
||||
comment: ""
|
||||
),
|
||||
title: NSLocalizedString("NEW VERSION INSTALLED", comment: ""),
|
||||
body: createNotificationBody(),
|
||||
button: createCloseButtonAction(),
|
||||
tapAction: createTapAction()
|
||||
@@ -55,11 +51,7 @@ class LatestChangesNotificationProvider: NotificationProvider, InAppNotification
|
||||
|
||||
private func createNotificationBody() -> NSAttributedString {
|
||||
NSAttributedString(
|
||||
markdownString: NSLocalizedString(
|
||||
"LATEST_CHANGES_IN_APP_NOTIFICATION_BODY",
|
||||
value: "**Tap here** to see what’s new.",
|
||||
comment: ""
|
||||
),
|
||||
markdownString: NSLocalizedString("**Tap here** to see what’s new.", comment: ""),
|
||||
options: MarkdownStylingOptions(
|
||||
font: .preferredFont(forTextStyle: .body)
|
||||
)
|
||||
|
||||
+2
-7
@@ -26,8 +26,7 @@ final class NewDeviceNotificationProvider: NotificationProvider,
|
||||
|
||||
private var attributedBody: NSAttributedString {
|
||||
let formattedString = NSLocalizedString(
|
||||
"ACCOUNT_CREATION_INAPP_NOTIFICATION_BODY",
|
||||
value: "Welcome, this device is now called **%@**. For more details see the info button in Account.",
|
||||
"Welcome, this device is now called **%@**. For more details see the info button in Account.",
|
||||
comment: ""
|
||||
)
|
||||
let deviceName = storedDeviceData?.capitalizedName ?? ""
|
||||
@@ -50,11 +49,7 @@ final class NewDeviceNotificationProvider: NotificationProvider,
|
||||
return InAppNotificationDescriptor(
|
||||
identifier: identifier,
|
||||
style: .success,
|
||||
title: NSLocalizedString(
|
||||
"ACCOUNT_CREATION_INAPP_NOTIFICATION_TITLE",
|
||||
value: "NEW DEVICE CREATED",
|
||||
comment: ""
|
||||
),
|
||||
title: NSLocalizedString("NEW DEVICE CREATED", comment: ""),
|
||||
body: attributedBody,
|
||||
button: InAppNotificationAction(
|
||||
image: UIImage.Buttons.closeSmall,
|
||||
|
||||
+23
-54
@@ -155,11 +155,7 @@ final class TunnelStatusNotificationProvider: NotificationProvider, InAppNotific
|
||||
return InAppNotificationDescriptor(
|
||||
identifier: identifier,
|
||||
style: .error,
|
||||
title: NSLocalizedString(
|
||||
"TUNNEL_BLOCKED_INAPP_NOTIFICATION_TITLE",
|
||||
value: "BLOCKING INTERNET",
|
||||
comment: ""
|
||||
),
|
||||
title: NSLocalizedString("BLOCKING INTERNET", comment: ""),
|
||||
body: createNotificationBody(localizedReasonForBlockedStateError(packetTunnelError)),
|
||||
tapAction: tapAction
|
||||
)
|
||||
@@ -167,11 +163,7 @@ final class TunnelStatusNotificationProvider: NotificationProvider, InAppNotific
|
||||
|
||||
private func createNotificationBody(_ string: String) -> NSAttributedString {
|
||||
NSAttributedString(
|
||||
markdownString: NSLocalizedString(
|
||||
"LATEST_CHANGES_IN_APP_NOTIFICATION_BODY",
|
||||
value: string,
|
||||
comment: ""
|
||||
),
|
||||
markdownString: string,
|
||||
options: MarkdownStylingOptions(font: UIFont.preferredFont(forTextStyle: .body)),
|
||||
applyEffect: { markdownType, _ in
|
||||
guard case .bold = markdownType else { return [:] }
|
||||
@@ -185,20 +177,12 @@ final class TunnelStatusNotificationProvider: NotificationProvider, InAppNotific
|
||||
|
||||
if let startError = error as? StartTunnelError {
|
||||
body = String(
|
||||
format: NSLocalizedString(
|
||||
"START_TUNNEL_ERROR_INAPP_NOTIFICATION_BODY",
|
||||
value: "Failed to start the tunnel: %@.",
|
||||
comment: ""
|
||||
),
|
||||
format: NSLocalizedString("Failed to start the tunnel: %@.", comment: ""),
|
||||
startError.underlyingError?.localizedDescription ?? ""
|
||||
)
|
||||
} else if let stopError = error as? StopTunnelError {
|
||||
body = String(
|
||||
format: NSLocalizedString(
|
||||
"STOP_TUNNEL_ERROR_INAPP_NOTIFICATION_BODY",
|
||||
value: "Failed to stop the tunnel: %@.",
|
||||
comment: ""
|
||||
),
|
||||
format: NSLocalizedString("Failed to stop the tunnel: %@.", comment: ""),
|
||||
stopError.underlyingError?.localizedDescription ?? ""
|
||||
)
|
||||
} else {
|
||||
@@ -208,11 +192,7 @@ final class TunnelStatusNotificationProvider: NotificationProvider, InAppNotific
|
||||
return InAppNotificationDescriptor(
|
||||
identifier: identifier,
|
||||
style: .error,
|
||||
title: NSLocalizedString(
|
||||
"TUNNEL_MANAGER_ERROR_INAPP_NOTIFICATION_TITLE",
|
||||
value: "TUNNEL ERROR",
|
||||
comment: ""
|
||||
),
|
||||
title: NSLocalizedString("TUNNEL ERROR", comment: ""),
|
||||
body: .init(string: body)
|
||||
)
|
||||
}
|
||||
@@ -221,15 +201,10 @@ final class TunnelStatusNotificationProvider: NotificationProvider, InAppNotific
|
||||
InAppNotificationDescriptor(
|
||||
identifier: identifier,
|
||||
style: .warning,
|
||||
title: NSLocalizedString(
|
||||
"TUNNEL_NO_CONNECTIVITY_INAPP_NOTIFICATION_TITLE",
|
||||
value: "NETWORK ISSUES",
|
||||
comment: ""
|
||||
),
|
||||
title: NSLocalizedString("NETWORK ISSUES", comment: ""),
|
||||
body: .init(
|
||||
string: NSLocalizedString(
|
||||
"TUNNEL_NO_CONNECTIVITY_INAPP_NOTIFICATION_BODY",
|
||||
value: """
|
||||
"""
|
||||
Your device is offline. The tunnel will automatically connect once \
|
||||
your device is back online.
|
||||
""",
|
||||
@@ -243,15 +218,10 @@ final class TunnelStatusNotificationProvider: NotificationProvider, InAppNotific
|
||||
InAppNotificationDescriptor(
|
||||
identifier: identifier,
|
||||
style: .warning,
|
||||
title: NSLocalizedString(
|
||||
"TUNNEL_NO_NETWORK_INAPP_NOTIFICATION_TITLE",
|
||||
value: "NETWORK ISSUES",
|
||||
comment: ""
|
||||
),
|
||||
title: NSLocalizedString("NETWORK ISSUES", comment: ""),
|
||||
body: .init(
|
||||
string: NSLocalizedString(
|
||||
"TUNNEL_NO_NETWORK_INAPP_NOTIFICATION_BODY",
|
||||
value: """
|
||||
"""
|
||||
Your device is offline. Try connecting again when the device \
|
||||
has access to Internet.
|
||||
""",
|
||||
@@ -262,34 +232,33 @@ final class TunnelStatusNotificationProvider: NotificationProvider, InAppNotific
|
||||
}
|
||||
|
||||
private func localizedReasonForBlockedStateError(_ error: BlockedStateReason) -> String {
|
||||
let errorString: String
|
||||
|
||||
let errorStringKey: String
|
||||
switch error {
|
||||
case .outdatedSchema:
|
||||
errorString = "Unable to start tunnel connection after update. Please disconnect and reconnect."
|
||||
errorStringKey = "ERROR_OUTDATED_SCHEMA"
|
||||
case .noRelaysSatisfyingFilterConstraints:
|
||||
errorString = "No servers match your location filter. Try changing filter settings."
|
||||
errorStringKey = "ERROR_NO_RELAYS_SATISFYING_FILTER_CONSTRAINTS"
|
||||
case .multihopEntryEqualsExit:
|
||||
errorString = "The entry and exit servers cannot be the same. Try changing one to a new server or location."
|
||||
errorStringKey = "ERROR_MULTIHOP_ENTRY_EQUALS_EXIT"
|
||||
case .noRelaysSatisfyingDaitaConstraints:
|
||||
errorString = "No DAITA compatible servers match your location settings. Try changing location."
|
||||
errorStringKey = "ERROR_NO_RELAYS_SATISFYING_DAITA_CONSTRAINTS"
|
||||
case .noRelaysSatisfyingConstraints:
|
||||
errorString = "No servers match your settings, try changing server or other settings."
|
||||
errorStringKey = "ERROR_NO_RELAYS_SATISFYING_CONSTRAINTS"
|
||||
case .noRelaysSatisfyingPortConstraints:
|
||||
errorString = "The selected WireGuard port is not supported, please change it under **VPN settings**."
|
||||
errorStringKey = "ERROR_NO_RELAYS_SATISFYING_PORT_CONSTRAINTS"
|
||||
case .invalidAccount:
|
||||
errorString = "You are logged in with an invalid account number. Please log out and try another one."
|
||||
errorStringKey = "ERROR_INVALID_ACCOUNT"
|
||||
case .deviceLoggedOut:
|
||||
errorString = "Unable to authenticate account. Please log out and log back in."
|
||||
errorStringKey = "ERROR_DEVICE_LOGGED_OUT"
|
||||
default:
|
||||
errorString = "Unable to start tunnel connection. Please send a problem report."
|
||||
errorStringKey = "ERROR_DEFAULT"
|
||||
}
|
||||
|
||||
return NSLocalizedString(
|
||||
"BLOCKED_STATE_ERROR_TITLE",
|
||||
tableName: "Main",
|
||||
value: errorString,
|
||||
let errorString = NSLocalizedString(
|
||||
errorStringKey,
|
||||
comment: ""
|
||||
)
|
||||
|
||||
return errorString
|
||||
}
|
||||
}
|
||||
@@ -208,16 +208,11 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate, @preconcurrency Setting
|
||||
|
||||
let presentation = AlertPresentation(
|
||||
id: "settings-migration-error-alert",
|
||||
title: NSLocalizedString(
|
||||
"ALERT_TITLE",
|
||||
tableName: "SettingsMigrationUI",
|
||||
value: "Settings migration error",
|
||||
comment: ""
|
||||
),
|
||||
title: NSLocalizedString("Settings migration error", comment: ""),
|
||||
message: Self.migrationErrorReason(error),
|
||||
buttons: [
|
||||
AlertAction(
|
||||
title: NSLocalizedString("Got it!", tableName: "SettingsMigrationUI", comment: ""),
|
||||
title: NSLocalizedString("Got it!", comment: ""),
|
||||
style: .default,
|
||||
handler: {
|
||||
completionHandler()
|
||||
@@ -233,9 +228,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate, @preconcurrency Setting
|
||||
private static func migrationErrorReason(_ error: Error) -> String {
|
||||
if error is UnsupportedSettingsVersionError {
|
||||
return NSLocalizedString(
|
||||
"NEWER_STORED_SETTINGS_ERROR",
|
||||
tableName: "SettingsMigrationUI",
|
||||
value: """
|
||||
"""
|
||||
The version of settings stored on device is unrecognized.\
|
||||
Settings will be reset to defaults and the device will be logged out.
|
||||
""",
|
||||
@@ -243,9 +236,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate, @preconcurrency Setting
|
||||
)
|
||||
} else {
|
||||
return NSLocalizedString(
|
||||
"INTERNAL_ERROR",
|
||||
tableName: "SettingsMigrationUI",
|
||||
value: """
|
||||
"""
|
||||
Internal error occurred. Settings will be reset to defaults and device logged out.
|
||||
""",
|
||||
comment: ""
|
||||
|
||||
@@ -17,19 +17,9 @@ enum StoreSubscription: String, CaseIterable {
|
||||
var localizedTitle: String {
|
||||
switch self {
|
||||
case .thirtyDays:
|
||||
return NSLocalizedString(
|
||||
"STORE_SUBSCRIPTION_TITLE_ADD_30_DAYS",
|
||||
tableName: "StoreSubscriptions",
|
||||
value: "Add 30 days",
|
||||
comment: ""
|
||||
)
|
||||
return NSLocalizedString("Add 30 days", comment: "")
|
||||
case .ninetyDays:
|
||||
return NSLocalizedString(
|
||||
"STORE_SUBSCRIPTION_TITLE_ADD_90_DAYS",
|
||||
tableName: "StoreSubscriptions",
|
||||
value: "Add 90 days",
|
||||
comment: ""
|
||||
)
|
||||
return NSLocalizedString("Add 90 days", comment: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,23 +11,13 @@ import MullvadTypes
|
||||
|
||||
struct UnsetTunnelError: LocalizedError {
|
||||
var errorDescription: String? {
|
||||
NSLocalizedString(
|
||||
"UNSET_TUNNEL_ERROR",
|
||||
tableName: "TunnelManager",
|
||||
value: "Tunnel is unset.",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Tunnel is unset.", comment: "")
|
||||
}
|
||||
}
|
||||
|
||||
struct InvalidDeviceStateError: LocalizedError {
|
||||
var errorDescription: String? {
|
||||
NSLocalizedString(
|
||||
"INVALID_DEVICE_STATE_ERROR",
|
||||
tableName: "TunnelManager",
|
||||
value: "Invalid device state.",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Invalid device state.", comment: "")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,12 +25,7 @@ struct StartTunnelError: LocalizedError, WrappingError {
|
||||
private let _underlyingError: Error
|
||||
|
||||
var errorDescription: String? {
|
||||
NSLocalizedString(
|
||||
"START_TUNNEL_ERROR",
|
||||
tableName: "TunnelManager",
|
||||
value: "Failed to start the tunnel.",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Failed to start the tunnel.", comment: "")
|
||||
}
|
||||
|
||||
var underlyingError: Error? {
|
||||
@@ -56,12 +41,7 @@ struct StopTunnelError: LocalizedError, WrappingError {
|
||||
private let _underlyingError: Error
|
||||
|
||||
var errorDescription: String? {
|
||||
NSLocalizedString(
|
||||
"STOP_TUNNEL_ERROR",
|
||||
tableName: "TunnelManager",
|
||||
value: "Failed to stop the tunnel.",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Failed to stop the tunnel.", comment: "")
|
||||
}
|
||||
|
||||
var underlyingError: Error? {
|
||||
|
||||
@@ -38,130 +38,55 @@ extension TunnelState {
|
||||
switch self {
|
||||
case let .connecting(_, isPostQuantum, _), let .reconnecting(_, isPostQuantum, _):
|
||||
if isPostQuantum {
|
||||
NSLocalizedString(
|
||||
"TUNNEL_STATE_PQ_CONNECTING",
|
||||
tableName: "Main",
|
||||
value: "Creating quantum secure connection",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Creating quantum secure connection", comment: "")
|
||||
} else {
|
||||
NSLocalizedString(
|
||||
"TUNNEL_STATE_CONNECTING",
|
||||
tableName: "Main",
|
||||
value: "Creating secure connection",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Creating secure connection", comment: "")
|
||||
}
|
||||
|
||||
case let .negotiatingEphemeralPeer(_, _, isPostQuantum, _):
|
||||
if isPostQuantum {
|
||||
NSLocalizedString(
|
||||
"TUNNEL_STATE_NEGOTIATING_KEY",
|
||||
tableName: "Main",
|
||||
value: "Creating quantum secure connection",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Creating quantum secure connection", comment: "")
|
||||
} else {
|
||||
NSLocalizedString(
|
||||
"TUNNEL_STATE_CONNECTING",
|
||||
tableName: "Main",
|
||||
value: "Creating secure connection",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Creating secure connection", comment: "")
|
||||
}
|
||||
|
||||
case let .connected(_, isPostQuantum, _):
|
||||
if isPostQuantum {
|
||||
NSLocalizedString(
|
||||
"TUNNEL_STATE_PQ_CONNECTED",
|
||||
tableName: "Main",
|
||||
value: "Quantum secure connection",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Quantum secure connection", comment: "")
|
||||
} else {
|
||||
NSLocalizedString(
|
||||
"TUNNEL_STATE_CONNECTED",
|
||||
tableName: "Main",
|
||||
value: "Connected",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Connected", comment: "")
|
||||
}
|
||||
|
||||
case .disconnecting(.nothing):
|
||||
NSLocalizedString(
|
||||
"TUNNEL_STATE_DISCONNECTING",
|
||||
tableName: "Main",
|
||||
value: "Disconnecting",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Disconnecting", comment: "")
|
||||
|
||||
case .disconnecting(.reconnect), .pendingReconnect:
|
||||
NSLocalizedString(
|
||||
"TUNNEL_STATE_PENDING_RECONNECT",
|
||||
tableName: "Main",
|
||||
value: "Reconnecting",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Reconnecting", comment: "")
|
||||
|
||||
case .disconnected:
|
||||
NSLocalizedString(
|
||||
"TUNNEL_STATE_DISCONNECTED",
|
||||
tableName: "Main",
|
||||
value: "Unsecured connection",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Unsecured connection", comment: "")
|
||||
|
||||
case .waitingForConnectivity(.noConnection), .error:
|
||||
NSLocalizedString(
|
||||
"TUNNEL_STATE_WAITING_FOR_CONNECTIVITY",
|
||||
tableName: "Main",
|
||||
value: "Blocked connection",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Blocked connection", comment: "")
|
||||
|
||||
case .waitingForConnectivity(.noNetwork):
|
||||
NSLocalizedString(
|
||||
"TUNNEL_STATE_NO_NETWORK",
|
||||
tableName: "Main",
|
||||
value: "No network",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("No network", comment: "")
|
||||
}
|
||||
}
|
||||
|
||||
var localizedTitleForSelectLocationButton: String {
|
||||
switch self {
|
||||
case .disconnecting(.reconnect), .pendingReconnect:
|
||||
NSLocalizedString(
|
||||
"SWITCH_LOCATION_BUTTON_TITLE",
|
||||
tableName: "Main",
|
||||
value: "Select location",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Select location", comment: "")
|
||||
|
||||
case .disconnected, .disconnecting(.nothing):
|
||||
NSLocalizedString(
|
||||
"SELECT_LOCATION_BUTTON_TITLE",
|
||||
tableName: "Main",
|
||||
value: "Select location",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Select location", comment: "")
|
||||
|
||||
case .connecting, .connected, .reconnecting, .waitingForConnectivity, .error:
|
||||
NSLocalizedString(
|
||||
"SWITCH_LOCATION_BUTTON_TITLE",
|
||||
tableName: "Main",
|
||||
value: "Switch location",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Switch location", comment: "")
|
||||
|
||||
case .negotiatingEphemeralPeer:
|
||||
NSLocalizedString(
|
||||
"SWITCH_LOCATION_BUTTON_TITLE",
|
||||
tableName: "Main",
|
||||
value: "Switch location",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Switch location", comment: "")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,79 +101,38 @@ extension TunnelState {
|
||||
case let .connected(tunnelInfo, isPostQuantum, _):
|
||||
if isPostQuantum {
|
||||
String(
|
||||
format: NSLocalizedString(
|
||||
"TUNNEL_STATE_PQ_CONNECTED_ACCESSIBILITY_LABEL",
|
||||
tableName: "Main",
|
||||
value: "Quantum secure connection. Connected to %@, %@",
|
||||
comment: ""
|
||||
),
|
||||
format: NSLocalizedString("Quantum secure connection. Connected to %@, %@", comment: ""),
|
||||
tunnelInfo.exit.location.city,
|
||||
tunnelInfo.exit.location.country
|
||||
)
|
||||
} else {
|
||||
String(
|
||||
format: NSLocalizedString(
|
||||
"TUNNEL_STATE_CONNECTED_ACCESSIBILITY_LABEL",
|
||||
tableName: "Main",
|
||||
value: "Secure connection. Connected to %@, %@",
|
||||
comment: ""
|
||||
),
|
||||
format: NSLocalizedString("Secure connection. Connected to %@, %@", comment: ""),
|
||||
tunnelInfo.exit.location.city,
|
||||
tunnelInfo.exit.location.country
|
||||
)
|
||||
}
|
||||
|
||||
case .disconnected:
|
||||
NSLocalizedString(
|
||||
"TUNNEL_STATE_DISCONNECTED_ACCESSIBILITY_LABEL",
|
||||
tableName: "Main",
|
||||
value: "Unsecured connection",
|
||||
comment: ""
|
||||
)
|
||||
|
||||
NSLocalizedString("Unsecured connection", comment: "")
|
||||
case let .reconnecting(tunnelInfo, _, _):
|
||||
String(
|
||||
format: NSLocalizedString(
|
||||
"TUNNEL_STATE_RECONNECTING_ACCESSIBILITY_LABEL",
|
||||
tableName: "Main",
|
||||
value: "Reconnecting to %@, %@",
|
||||
comment: ""
|
||||
),
|
||||
format: NSLocalizedString("Reconnecting to %@, %@", comment: ""),
|
||||
tunnelInfo.exit.location.city,
|
||||
tunnelInfo.exit.location.country
|
||||
)
|
||||
|
||||
case .waitingForConnectivity(.noConnection), .error:
|
||||
NSLocalizedString(
|
||||
"TUNNEL_STATE_WAITING_FOR_CONNECTIVITY_ACCESSIBILITY_LABEL",
|
||||
tableName: "Main",
|
||||
value: "Blocked connection",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Blocked connection", comment: "")
|
||||
|
||||
case .waitingForConnectivity(.noNetwork):
|
||||
NSLocalizedString(
|
||||
"TUNNEL_STATE_NO_NETWORK_ACCESSIBILITY_LABEL",
|
||||
tableName: "Main",
|
||||
value: "No network",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("No network", comment: "")
|
||||
|
||||
case .disconnecting(.nothing):
|
||||
NSLocalizedString(
|
||||
"TUNNEL_STATE_DISCONNECTING_ACCESSIBILITY_LABEL",
|
||||
tableName: "Main",
|
||||
value: "Disconnecting",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Disconnecting", comment: "")
|
||||
|
||||
case .disconnecting(.reconnect), .pendingReconnect:
|
||||
NSLocalizedString(
|
||||
"TUNNEL_STATE_PENDING_RECONNECT_ACCESSIBILITY_LABEL",
|
||||
tableName: "Main",
|
||||
value: "Reconnecting",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Reconnecting", comment: "")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,16 +168,12 @@ extension TunnelState {
|
||||
|
||||
return if let entryName {
|
||||
String(format: NSLocalizedString(
|
||||
"CONNECT_PANEL_TITLE",
|
||||
tableName: "Main",
|
||||
value: "%@ via %@\(usingDaita ? " using DAITA" : "")",
|
||||
"%@ via %@\(usingDaita ? " using DAITA" : "")",
|
||||
comment: ""
|
||||
), exitName, entryName)
|
||||
} else {
|
||||
String(format: NSLocalizedString(
|
||||
"CONNECT_PANEL_TITLE",
|
||||
tableName: "Main",
|
||||
value: "%@\(usingDaita ? " using DAITA" : "")",
|
||||
"%@\(usingDaita ? " using DAITA" : "")",
|
||||
comment: ""
|
||||
), exitName)
|
||||
}
|
||||
@@ -301,19 +181,9 @@ extension TunnelState {
|
||||
|
||||
func secureConnectionLabel(isPostQuantum: Bool) -> String {
|
||||
if isPostQuantum {
|
||||
NSLocalizedString(
|
||||
"TUNNEL_STATE_PQ_CONNECTING_ACCESSIBILITY_LABEL",
|
||||
tableName: "Main",
|
||||
value: "Creating quantum secure connection",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Creating quantum secure connection", comment: "")
|
||||
} else {
|
||||
NSLocalizedString(
|
||||
"TUNNEL_STATE_CONNECTING_ACCESSIBILITY_LABEL",
|
||||
tableName: "Main",
|
||||
value: "Creating secure connection",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Creating secure connection", comment: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,70 +12,40 @@ class AccountContentView: UIView {
|
||||
let purchaseButton: InAppPurchaseButton = {
|
||||
let button = InAppPurchaseButton()
|
||||
button.setAccessibilityIdentifier(.purchaseButton)
|
||||
button.setTitle(NSLocalizedString(
|
||||
"ADD_TIME_BUTTON_TITLE",
|
||||
tableName: "Account",
|
||||
value: "Add time",
|
||||
comment: ""
|
||||
), for: .normal)
|
||||
button.setTitle(NSLocalizedString("Add time", comment: ""), for: .normal)
|
||||
return button
|
||||
}()
|
||||
|
||||
let storeKit2PurchaseButton: AppButton = {
|
||||
let button = AppButton(style: .success)
|
||||
button.setTitle(NSLocalizedString(
|
||||
"BUY_SUBSCRIPTION_STOREKIT_2",
|
||||
tableName: "Account",
|
||||
value: "Make a purchase with StoreKit2",
|
||||
comment: ""
|
||||
), for: .normal)
|
||||
button.setTitle(NSLocalizedString("Make a purchase with StoreKit2", comment: ""), for: .normal)
|
||||
return button
|
||||
}()
|
||||
|
||||
let storeKit2RefundButton: AppButton = {
|
||||
let button = AppButton(style: .success)
|
||||
button.setTitle(NSLocalizedString(
|
||||
"BUY_SUBSCRIPTION_STOREKIT_2",
|
||||
tableName: "Account",
|
||||
value: "Refund last purchase with StoreKit2",
|
||||
comment: ""
|
||||
), for: .normal)
|
||||
button.setTitle(NSLocalizedString("Refund last purchase with StoreKit2", comment: ""), for: .normal)
|
||||
return button
|
||||
}()
|
||||
|
||||
let redeemVoucherButton: AppButton = {
|
||||
let button = AppButton(style: .success)
|
||||
button.setAccessibilityIdentifier(.redeemVoucherButton)
|
||||
button.setTitle(NSLocalizedString(
|
||||
"REDEEM_VOUCHER_BUTTON_TITLE",
|
||||
tableName: "Account",
|
||||
value: "Redeem voucher",
|
||||
comment: ""
|
||||
), for: .normal)
|
||||
button.setTitle(NSLocalizedString("Redeem voucher", comment: ""), for: .normal)
|
||||
return button
|
||||
}()
|
||||
|
||||
let logoutButton: AppButton = {
|
||||
let button = AppButton(style: .danger)
|
||||
button.setAccessibilityIdentifier(.logoutButton)
|
||||
button.setTitle(NSLocalizedString(
|
||||
"LOGOUT_BUTTON_TITLE",
|
||||
tableName: "Account",
|
||||
value: "Log out",
|
||||
comment: ""
|
||||
), for: .normal)
|
||||
button.setTitle(NSLocalizedString("Log out", comment: ""), for: .normal)
|
||||
return button
|
||||
}()
|
||||
|
||||
let deleteButton: AppButton = {
|
||||
let button = AppButton(style: .danger)
|
||||
button.setAccessibilityIdentifier(.deleteButton)
|
||||
button.setTitle(NSLocalizedString(
|
||||
"DELETE_BUTTON_TITLE",
|
||||
tableName: "Account",
|
||||
value: "Delete account",
|
||||
comment: ""
|
||||
), for: .normal)
|
||||
button.setTitle(NSLocalizedString("Delete account", comment: ""), for: .normal)
|
||||
return button
|
||||
}()
|
||||
|
||||
|
||||
@@ -21,12 +21,7 @@ class AccountDeviceRow: UIView {
|
||||
|
||||
private let titleLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.text = NSLocalizedString(
|
||||
"DEVICE_NAME",
|
||||
tableName: "Account",
|
||||
value: "Device name",
|
||||
comment: ""
|
||||
)
|
||||
label.text = NSLocalizedString("Device name", comment: "")
|
||||
label.font = .mullvadTiny
|
||||
label.adjustsFontForContentSizeCategory = true
|
||||
label.textColor = UIColor(white: 1.0, alpha: 0.6)
|
||||
@@ -54,12 +49,7 @@ class AccountDeviceRow: UIView {
|
||||
.foregroundColor: UIColor.primaryTextColor,
|
||||
.underlineStyle: NSUnderlineStyle.single.rawValue,
|
||||
]
|
||||
let title = NSLocalizedString(
|
||||
"DEVICE_MANAGEMENT",
|
||||
tableName: "Account",
|
||||
value: "Manage devices",
|
||||
comment: ""
|
||||
)
|
||||
let title = NSLocalizedString("Manage devices", comment: "")
|
||||
button.attributedText = NSMutableAttributedString(
|
||||
string: title,
|
||||
attributes: attributes
|
||||
|
||||
@@ -15,12 +15,7 @@ class AccountExpiryRow: UIView {
|
||||
let expiry = value
|
||||
|
||||
if let expiry, expiry <= Date() {
|
||||
let localizedString = NSLocalizedString(
|
||||
"ACCOUNT_OUT_OF_TIME_LABEL",
|
||||
tableName: "Account",
|
||||
value: "OUT OF TIME",
|
||||
comment: ""
|
||||
)
|
||||
let localizedString = NSLocalizedString("OUT OF TIME", comment: "")
|
||||
|
||||
valueLabel.text = localizedString
|
||||
accessibilityValue = localizedString
|
||||
@@ -46,12 +41,7 @@ class AccountExpiryRow: UIView {
|
||||
private let textLabel: UILabel = {
|
||||
let textLabel = UILabel()
|
||||
textLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
textLabel.text = NSLocalizedString(
|
||||
"ACCOUNT_EXPIRY_LABEL",
|
||||
tableName: "Account",
|
||||
value: "Paid until",
|
||||
comment: ""
|
||||
)
|
||||
textLabel.text = NSLocalizedString("Paid until", comment: "")
|
||||
textLabel.font = .mullvadTiny
|
||||
textLabel.numberOfLines = 0
|
||||
textLabel.adjustsFontForContentSizeCategory = true
|
||||
|
||||
@@ -26,12 +26,7 @@ class AccountNumberRow: UIView {
|
||||
|
||||
private let titleLabel: UILabel = {
|
||||
let textLabel = UILabel()
|
||||
textLabel.text = NSLocalizedString(
|
||||
"ACCOUNT_TOKEN_LABEL",
|
||||
tableName: "Account",
|
||||
value: "Account number",
|
||||
comment: ""
|
||||
)
|
||||
textLabel.text = NSLocalizedString("Account number", comment: "")
|
||||
textLabel.font = .mullvadTiny
|
||||
textLabel.adjustsFontForContentSizeCategory = true
|
||||
textLabel.textColor = UIColor(white: 1.0, alpha: 0.6)
|
||||
@@ -168,12 +163,7 @@ class AccountNumberRow: UIView {
|
||||
|
||||
if isObscured {
|
||||
return NSAttributedString(
|
||||
string: NSLocalizedString(
|
||||
"ACCOUNT_ACCESSIBILITY_OBSCURED",
|
||||
tableName: "Account",
|
||||
value: "Obscured",
|
||||
comment: ""
|
||||
)
|
||||
string: NSLocalizedString("Obscured", comment: "")
|
||||
)
|
||||
} else {
|
||||
return NSAttributedString(
|
||||
@@ -193,12 +183,7 @@ class AccountNumberRow: UIView {
|
||||
selector: #selector(didTapShowHideAccount)
|
||||
),
|
||||
UIAccessibilityCustomAction(
|
||||
name: NSLocalizedString(
|
||||
"ACCOUNT_ACCESSIBILITY_COPY_TO_PASTEBOARD",
|
||||
tableName: "Account",
|
||||
value: "Copy to pasteboard",
|
||||
comment: ""
|
||||
),
|
||||
name: NSLocalizedString("Copy to pasteboard", comment: ""),
|
||||
target: self,
|
||||
selector: #selector(didTapCopyAccountNumber)
|
||||
),
|
||||
@@ -207,19 +192,9 @@ class AccountNumberRow: UIView {
|
||||
|
||||
private var showHideAccessibilityActionName: String {
|
||||
if isObscured {
|
||||
return NSLocalizedString(
|
||||
"ACCOUNT_ACCESSIBILITY_SHOW_ACCOUNT_NUMBER",
|
||||
tableName: "Account",
|
||||
value: "Show account number",
|
||||
comment: ""
|
||||
)
|
||||
return NSLocalizedString("Show account number", comment: "")
|
||||
} else {
|
||||
return NSLocalizedString(
|
||||
"ACCOUNT_ACCESSIBILITY_HIDE_ACCOUNT_NUMBER",
|
||||
tableName: "Account",
|
||||
value: "Hide account number",
|
||||
comment: ""
|
||||
)
|
||||
return NSLocalizedString("Hide account number", comment: "")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,12 +64,7 @@ class AccountViewController: UIViewController, @unchecked Sendable {
|
||||
super.viewDidLoad()
|
||||
view.backgroundColor = .secondaryColor
|
||||
|
||||
navigationItem.title = NSLocalizedString(
|
||||
"NAVIGATION_TITLE",
|
||||
tableName: "Account",
|
||||
value: "Account",
|
||||
comment: ""
|
||||
)
|
||||
navigationItem.title = NSLocalizedString("Account", comment: "")
|
||||
|
||||
navigationItem.rightBarButtonItem = UIBarButtonItem(
|
||||
barButtonSystemItem: .done,
|
||||
|
||||
@@ -16,18 +16,8 @@ struct PaymentAlertPresenter {
|
||||
func showAlertForRefund(completion: (@MainActor @Sendable () -> Void)? = nil) {
|
||||
let presentation = AlertPresentation(
|
||||
id: "payment-refund-alert",
|
||||
title: NSLocalizedString(
|
||||
"PAYMENT_REFUND_ALERT_TITLE",
|
||||
tableName: "Payment",
|
||||
value: "Refund successful",
|
||||
comment: ""
|
||||
),
|
||||
message: NSLocalizedString(
|
||||
"PAYMENT_REFUND_ALERT_MESSAGE",
|
||||
tableName: "Payment",
|
||||
value: "Your purchase was successfully refunded.",
|
||||
comment: ""
|
||||
),
|
||||
title: NSLocalizedString("Refund successful", comment: ""),
|
||||
message: NSLocalizedString("Your purchase was successfully refunded.", comment: ""),
|
||||
buttons: [
|
||||
AlertAction(
|
||||
title: okButtonTextForKey("PAYMENT_REFUND_ALERT_OK_ACTION"),
|
||||
@@ -121,11 +111,6 @@ struct PaymentAlertPresenter {
|
||||
}
|
||||
|
||||
private func okButtonTextForKey(_ key: String) -> String {
|
||||
NSLocalizedString(
|
||||
key,
|
||||
tableName: "Payment",
|
||||
value: "Got it!",
|
||||
comment: ""
|
||||
)
|
||||
NSLocalizedString("Got it!", comment: "")
|
||||
}
|
||||
}
|
||||
@@ -64,12 +64,7 @@ class RestorePurchasesView: UIView {
|
||||
}
|
||||
|
||||
private func makeAttributedString() -> NSAttributedString {
|
||||
let text = NSLocalizedString(
|
||||
"RESTORE_PURCHASES_BUTTON_TITLE",
|
||||
tableName: "Account",
|
||||
value: "Restore purchases",
|
||||
comment: ""
|
||||
)
|
||||
let text = NSLocalizedString("Restore purchases", comment: "")
|
||||
|
||||
return NSAttributedString(string: text, attributes: [
|
||||
.font: UIFont.mullvadMini,
|
||||
|
||||
@@ -39,12 +39,7 @@ class AccountDeletionContentView: UIView {
|
||||
label.adjustsFontForContentSizeCategory = true
|
||||
label.lineBreakMode = .byWordWrapping
|
||||
label.textColor = .white
|
||||
label.text = NSLocalizedString(
|
||||
"ACCOUNT_DELETION_PAGE_TITLE",
|
||||
tableName: "Account",
|
||||
value: "Account deletion",
|
||||
comment: ""
|
||||
)
|
||||
label.text = NSLocalizedString("Account deletion", comment: "")
|
||||
return label
|
||||
}()
|
||||
|
||||
@@ -68,9 +63,7 @@ class AccountDeletionContentView: UIView {
|
||||
label.lineBreakMode = .byWordWrapping
|
||||
label.textColor = .white
|
||||
label.text = NSLocalizedString(
|
||||
"TIP_TEXT",
|
||||
tableName: "Account",
|
||||
value: """
|
||||
"""
|
||||
This logs out all devices using this account and all \
|
||||
VPN access will be denied even if there is time left on the account. \
|
||||
Enter the last 4 digits of the account number and hit "Delete account" \
|
||||
@@ -106,24 +99,14 @@ class AccountDeletionContentView: UIView {
|
||||
private let deleteButton: AppButton = {
|
||||
let button = AppButton(style: .danger)
|
||||
button.setAccessibilityIdentifier(.deleteButton)
|
||||
button.setTitle(NSLocalizedString(
|
||||
"DELETE_ACCOUNT_BUTTON_TITLE",
|
||||
tableName: "Account",
|
||||
value: "Delete Account",
|
||||
comment: ""
|
||||
), for: .normal)
|
||||
button.setTitle(NSLocalizedString("Delete Account", comment: ""), for: .normal)
|
||||
return button
|
||||
}()
|
||||
|
||||
private let cancelButton: AppButton = {
|
||||
let button = AppButton(style: .default)
|
||||
button.setAccessibilityIdentifier(.cancelButton)
|
||||
button.setTitle(NSLocalizedString(
|
||||
"CANCEL_BUTTON_TITLE",
|
||||
tableName: "Account",
|
||||
value: "Cancel",
|
||||
comment: ""
|
||||
), for: .normal)
|
||||
button.setTitle(NSLocalizedString("Cancel", comment: ""), for: .normal)
|
||||
return button
|
||||
}()
|
||||
|
||||
@@ -219,12 +202,7 @@ class AccountDeletionContentView: UIView {
|
||||
case let .failure(error):
|
||||
return error.localizedDescription
|
||||
case .loading:
|
||||
return NSLocalizedString(
|
||||
"DELETE_ACCOUNT_STATUS_WAITING",
|
||||
tableName: "Account",
|
||||
value: "Deleting account...",
|
||||
comment: ""
|
||||
)
|
||||
return NSLocalizedString("Deleting account...", comment: "")
|
||||
default: return ""
|
||||
}
|
||||
}
|
||||
@@ -324,9 +302,7 @@ class AccountDeletionContentView: UIView {
|
||||
private func updateData() {
|
||||
viewModel.flatMap { viewModel in
|
||||
let text = NSLocalizedString(
|
||||
"BODY_LABEL_TEXT",
|
||||
tableName: "Account",
|
||||
value: """
|
||||
"""
|
||||
Are you sure you want to delete account **\(viewModel.accountNumber)**?
|
||||
""",
|
||||
comment: ""
|
||||
|
||||
@@ -16,12 +16,7 @@ enum AccountDeletionError: LocalizedError {
|
||||
var errorDescription: String? {
|
||||
switch self {
|
||||
case .invalidInput:
|
||||
return NSLocalizedString(
|
||||
"INVALID_ACCOUNT_NUMBER",
|
||||
tableName: "Account",
|
||||
value: "Last four digits of the account number are incorrect",
|
||||
comment: ""
|
||||
)
|
||||
return NSLocalizedString("Last four digits of the account number are incorrect", comment: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-21
@@ -21,12 +21,7 @@ class SetupAccountCompletedContentView: UIView {
|
||||
label.adjustsFontForContentSizeCategory = true
|
||||
label.lineBreakMode = .byWordWrapping
|
||||
label.numberOfLines = .zero
|
||||
label.text = NSLocalizedString(
|
||||
"CREATED_ACCOUNT_CONFIRMATION_PAGE_TITLE",
|
||||
tableName: "CreatedAccountConfirmation",
|
||||
value: "You’re all set!!",
|
||||
comment: ""
|
||||
)
|
||||
label.text = NSLocalizedString("You’re all set!!", comment: "")
|
||||
return label
|
||||
}()
|
||||
|
||||
@@ -34,9 +29,7 @@ class SetupAccountCompletedContentView: UIView {
|
||||
let label = UILabel()
|
||||
|
||||
let message = NSMutableAttributedString(string: NSLocalizedString(
|
||||
"CREATED_ACCOUNT_CONFIRMATION_PAGE_BODY",
|
||||
tableName: "CreatedAccountConfirmation",
|
||||
value: """
|
||||
"""
|
||||
Go ahead and start using the app to begin reclaiming your online privacy.
|
||||
To continue your journey as a privacy ninja, \
|
||||
visit our website to pick up other privacy-friendly habits and tools.
|
||||
@@ -57,12 +50,7 @@ class SetupAccountCompletedContentView: UIView {
|
||||
private let privacyButton: AppButton = {
|
||||
let button = AppButton(style: .success)
|
||||
button.setAccessibilityIdentifier(.learnAboutPrivacyButton)
|
||||
let localizedString = NSLocalizedString(
|
||||
"LEARN_ABOUT_PRIVACY_BUTTON",
|
||||
tableName: "CreatedAccountConfirmation",
|
||||
value: "Learn about privacy",
|
||||
comment: ""
|
||||
)
|
||||
let localizedString = NSLocalizedString("Learn about privacy", comment: "")
|
||||
button.setTitle(localizedString, for: .normal)
|
||||
button.setImage(UIImage(named: "IconExtlink")?.imageFlippedForRightToLeftLayoutDirection(), for: .normal)
|
||||
return button
|
||||
@@ -71,12 +59,7 @@ class SetupAccountCompletedContentView: UIView {
|
||||
private let startButton: AppButton = {
|
||||
let button = AppButton(style: .success)
|
||||
button.setAccessibilityIdentifier(.startUsingTheAppButton)
|
||||
button.setTitle(NSLocalizedString(
|
||||
"START_USING_THE_APP_BUTTON",
|
||||
tableName: "CreatedAccountConfirmation",
|
||||
value: "Start using the app",
|
||||
comment: ""
|
||||
), for: .normal)
|
||||
button.setTitle(NSLocalizedString("Start using the app", comment: ""), for: .normal)
|
||||
return button
|
||||
}()
|
||||
|
||||
|
||||
@@ -29,12 +29,7 @@ final class WelcomeContentView: UIView, Sendable {
|
||||
label.adjustsFontForContentSizeCategory = true
|
||||
label.lineBreakMode = .byWordWrapping
|
||||
label.numberOfLines = .zero
|
||||
label.text = NSLocalizedString(
|
||||
"WELCOME_PAGE_TITLE",
|
||||
tableName: "Welcome",
|
||||
value: "Congrats!",
|
||||
comment: ""
|
||||
)
|
||||
label.text = NSLocalizedString("Congrats!", comment: "")
|
||||
return label
|
||||
}()
|
||||
|
||||
@@ -45,12 +40,7 @@ final class WelcomeContentView: UIView, Sendable {
|
||||
label.adjustsFontForContentSizeCategory = true
|
||||
label.lineBreakMode = .byWordWrapping
|
||||
label.numberOfLines = .zero
|
||||
label.text = NSLocalizedString(
|
||||
"WELCOME_PAGE_SUBTITLE",
|
||||
tableName: "Welcome",
|
||||
value: "Here’s your account number. Save it!",
|
||||
comment: ""
|
||||
)
|
||||
label.text = NSLocalizedString("Here’s your account number. Save it!", comment: "")
|
||||
return label
|
||||
}()
|
||||
|
||||
@@ -107,9 +97,7 @@ final class WelcomeContentView: UIView, Sendable {
|
||||
label.lineBreakMode = .byWordWrapping
|
||||
label.lineBreakStrategy = []
|
||||
label.text = NSLocalizedString(
|
||||
"WELCOME_PAGE_DESCRIPTION",
|
||||
tableName: "Welcome",
|
||||
value: """
|
||||
"""
|
||||
To start using the app, you first need to \
|
||||
add time to your account. Either buy credit \
|
||||
on our website or redeem a voucher.
|
||||
@@ -122,12 +110,7 @@ final class WelcomeContentView: UIView, Sendable {
|
||||
private let purchaseButton: InAppPurchaseButton = {
|
||||
let button = InAppPurchaseButton()
|
||||
button.setAccessibilityIdentifier(.purchaseButton)
|
||||
let localizedString = NSLocalizedString(
|
||||
"ADD_TIME_BUTTON",
|
||||
tableName: "Welcome",
|
||||
value: "Add time",
|
||||
comment: ""
|
||||
)
|
||||
let localizedString = NSLocalizedString("Add time", comment: "")
|
||||
button.setTitle(localizedString, for: .normal)
|
||||
return button
|
||||
}()
|
||||
@@ -171,12 +154,10 @@ final class WelcomeContentView: UIView, Sendable {
|
||||
var viewModel: WelcomeViewModel? {
|
||||
didSet {
|
||||
accountNumberLabel.text = viewModel?.accountNumber
|
||||
deviceNameLabel.text = String(format: NSLocalizedString(
|
||||
"DEVICE_NAME_TEXT",
|
||||
tableName: "Welcome",
|
||||
value: "Device name: %@",
|
||||
comment: ""
|
||||
), viewModel?.deviceName ?? "")
|
||||
deviceNameLabel.text = String(
|
||||
format: NSLocalizedString("Device name: %@", comment: ""),
|
||||
viewModel?.deviceName ?? ""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -101,12 +101,7 @@ class InAppPurchaseViewController: UIViewController, StorePaymentObserver {
|
||||
}
|
||||
|
||||
func showPurchaseOptions(for products: [SKProduct]) {
|
||||
let localizedString = NSLocalizedString(
|
||||
"ADD_TIME_BUTTON",
|
||||
tableName: "Welcome",
|
||||
value: "Add Time",
|
||||
comment: ""
|
||||
)
|
||||
let localizedString = NSLocalizedString("Add Time", comment: "")
|
||||
let sheetController = UIAlertController(
|
||||
title: localizedString,
|
||||
message: nil,
|
||||
@@ -127,12 +122,7 @@ class InAppPurchaseViewController: UIViewController, StorePaymentObserver {
|
||||
sheetController.addAction(action)
|
||||
}
|
||||
|
||||
let cancelAction = UIAlertAction(title: NSLocalizedString(
|
||||
"SHEET_CANCEL_BUTTON",
|
||||
tableName: "ActionSheet",
|
||||
value: "Cancel",
|
||||
comment: ""
|
||||
), style: .cancel) { _ in
|
||||
let cancelAction = UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel) { _ in
|
||||
self.didFinish?()
|
||||
}
|
||||
cancelAction.accessibilityIdentifier = "actoin-sheet-cancel-button"
|
||||
|
||||
@@ -25,12 +25,7 @@ final class AccountInputGroupView: UIView {
|
||||
button.setImage(UIImage.Buttons.rightArrow, for: .normal)
|
||||
button.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
|
||||
button.setAccessibilityIdentifier(.loginTextFieldButton)
|
||||
button.accessibilityLabel = NSLocalizedString(
|
||||
"ACCOUNT_INPUT_LOGIN_BUTTON_ACCESSIBILITY_LABEL",
|
||||
tableName: "AccountInput",
|
||||
value: "Log in",
|
||||
comment: ""
|
||||
)
|
||||
button.accessibilityLabel = NSLocalizedString("Log in", comment: "")
|
||||
return button
|
||||
}()
|
||||
|
||||
@@ -97,12 +92,7 @@ final class AccountInputGroupView: UIView {
|
||||
button.translatesAutoresizingMaskIntoConstraints = false
|
||||
button.contentHorizontalAlignment = .leading
|
||||
button.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
||||
button.accessibilityLabel = NSLocalizedString(
|
||||
"LAST_USED_ACCOUNT_ACCESSIBILITY_LABEL",
|
||||
tableName: "AccountInput",
|
||||
value: "Last used account",
|
||||
comment: ""
|
||||
)
|
||||
button.accessibilityLabel = NSLocalizedString("Last used account", comment: "")
|
||||
button.configuration?.contentInsets = UIMetrics.textFieldMargins.toDirectionalInsets
|
||||
button.configuration?.title = " "
|
||||
button.configuration?
|
||||
@@ -120,12 +110,7 @@ final class AccountInputGroupView: UIView {
|
||||
let button = CustomButton()
|
||||
button.translatesAutoresizingMaskIntoConstraints = false
|
||||
button.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
|
||||
button.accessibilityLabel = NSLocalizedString(
|
||||
"REMOVE_LAST_USED_ACCOUNT_ACCESSIBILITY_LABEL",
|
||||
tableName: "AccountInput",
|
||||
value: "Remove last used account",
|
||||
comment: ""
|
||||
)
|
||||
button.accessibilityLabel = NSLocalizedString("Remove last used account", comment: "")
|
||||
button.configuration?.image = UIImage.Buttons.closeSmall.withTintColor(.primaryColor)
|
||||
button.configuration?.title = " "
|
||||
return button
|
||||
|
||||
@@ -78,12 +78,7 @@ class LoginContentView: UIView {
|
||||
textLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
textLabel.adjustsFontForContentSizeCategory = true
|
||||
textLabel.numberOfLines = 0
|
||||
textLabel.text = NSLocalizedString(
|
||||
"CREATE_BUTTON_HEADER_LABEL",
|
||||
tableName: "Login",
|
||||
value: "Don’t have an account number?",
|
||||
comment: ""
|
||||
)
|
||||
textLabel.text = NSLocalizedString("Don’t have an account number?", comment: "")
|
||||
textLabel.numberOfLines = 0
|
||||
return textLabel
|
||||
}()
|
||||
@@ -92,12 +87,7 @@ class LoginContentView: UIView {
|
||||
let button = AppButton(style: .default)
|
||||
button.setAccessibilityIdentifier(.createAccountButton)
|
||||
button.translatesAutoresizingMaskIntoConstraints = false
|
||||
button.setTitle(NSLocalizedString(
|
||||
"CREATE_ACCOUNT_BUTTON_LABEL",
|
||||
tableName: "Login",
|
||||
value: "Create account",
|
||||
comment: ""
|
||||
), for: .normal)
|
||||
button.setTitle(NSLocalizedString("Create account", comment: ""), for: .normal)
|
||||
return button
|
||||
}()
|
||||
|
||||
|
||||
Loaded 100 of 148 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user