mirror of
https://github.com/passepartoutvpn/passepartout-apple.git
synced 2025-02-16 04:42:11 +00:00
Share common protocols across localized strings (#324)
This commit is contained in:
parent
bf70c7c59a
commit
98e5e4cdde
@ -29,7 +29,6 @@
|
||||
0E0F4C5C29C76B790022E884 /* SceneDelegate+Shortcuts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0F4C5B29C76B790022E884 /* SceneDelegate+Shortcuts.swift */; };
|
||||
0E0F4C6429C84B5A0022E884 /* LockableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0F4C6329C84B5A0022E884 /* LockableView.swift */; };
|
||||
0E0F4C6629C84CF60022E884 /* LogoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0F4C6529C84CF60022E884 /* LogoView.swift */; };
|
||||
0E12BC8F27F62C8600B2F912 /* Validators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E12BC8E27F62C8500B2F912 /* Validators.swift */; };
|
||||
0E1AD5CC2A2682DA002AE6E6 /* AppError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1AD5CB2A2682DA002AE6E6 /* AppError.swift */; };
|
||||
0E1AD5CE2A268645002AE6E6 /* Errors+L10n.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1AD5CD2A268645002AE6E6 /* Errors+L10n.swift */; };
|
||||
0E1B5F5C29C506AD00FE7D18 /* ProfileView+Diagnostics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1B5F5B29C506AC00FE7D18 /* ProfileView+Diagnostics.swift */; };
|
||||
@ -317,7 +316,6 @@
|
||||
0E0F4C5B29C76B790022E884 /* SceneDelegate+Shortcuts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SceneDelegate+Shortcuts.swift"; sourceTree = "<group>"; };
|
||||
0E0F4C6329C84B5A0022E884 /* LockableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockableView.swift; sourceTree = "<group>"; };
|
||||
0E0F4C6529C84CF60022E884 /* LogoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogoView.swift; sourceTree = "<group>"; };
|
||||
0E12BC8E27F62C8500B2F912 /* Validators.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Validators.swift; sourceTree = "<group>"; };
|
||||
0E1AD5CB2A2682DA002AE6E6 /* AppError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppError.swift; sourceTree = "<group>"; };
|
||||
0E1AD5CD2A268645002AE6E6 /* Errors+L10n.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Errors+L10n.swift"; sourceTree = "<group>"; };
|
||||
0E1B5F5B29C506AC00FE7D18 /* ProfileView+Diagnostics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileView+Diagnostics.swift"; sourceTree = "<group>"; };
|
||||
@ -612,7 +610,6 @@
|
||||
0E2C172A27CB63F9007E8488 /* Reviewer.swift */,
|
||||
0ED89C1427DE0A0C008B36D6 /* Shortcut.swift */,
|
||||
0E5349BD27C16A4500C71BB3 /* StyledPicker.swift */,
|
||||
0E12BC8E27F62C8500B2F912 /* Validators.swift */,
|
||||
);
|
||||
path = Reusable;
|
||||
sourceTree = "<group>";
|
||||
@ -1509,7 +1506,6 @@
|
||||
0E2A8D4927ADF87F00207D04 /* PassepartoutApp.swift in Sources */,
|
||||
0EBC075527EBC83800208AD9 /* MailComposerView.swift in Sources */,
|
||||
0EF0FAF727DD159C007EB181 /* IntentDispatcher.swift in Sources */,
|
||||
0E12BC8F27F62C8600B2F912 /* Validators.swift in Sources */,
|
||||
0E0838FD2877334300A34EC0 /* DefaultLightProviderManager.swift in Sources */,
|
||||
0E0F4C6629C84CF60022E884 /* LogoView.swift in Sources */,
|
||||
0E039279281890B100827C10 /* AddHostView.swift in Sources */,
|
||||
|
@ -48,7 +48,7 @@ final class IntentDispatcher {
|
||||
intent.profileId = header.id.uuidString
|
||||
intent.providerFullName = providerFullName
|
||||
intent.serverId = server.id
|
||||
intent.serverName = server.localizedLongDescription(withCategory: false)
|
||||
intent.serverName = server.localizedDescription(style: .longWithCategory(withCategory: false))
|
||||
return intent
|
||||
}
|
||||
|
||||
|
@ -60,7 +60,7 @@ extension ProviderLocation: Comparable {
|
||||
}
|
||||
|
||||
public static func < (lhs: Self, rhs: Self) -> Bool {
|
||||
lhs.localizedCountry < rhs.localizedCountry
|
||||
lhs.localizedDescription(style: .country) < rhs.localizedDescription(style: .country)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,8 +26,19 @@
|
||||
import Foundation
|
||||
import PassepartoutLibrary
|
||||
|
||||
extension ObservableVPNState {
|
||||
func localizedStatusDescription(isActiveProfile: Bool, withErrors: Bool, dataCountIfAvailable: Bool) -> String {
|
||||
extension ObservableVPNState: StyledLocalizableEntity {
|
||||
public enum Style {
|
||||
case status(isActiveProfile: Bool, withErrors: Bool, dataCountIfAvailable: Bool)
|
||||
}
|
||||
|
||||
public func localizedDescription(style: Style) -> String {
|
||||
switch style {
|
||||
case .status(let isActiveProfile, let withErrors, let dataCountIfAvailable):
|
||||
return statusDescription(isActiveProfile: isActiveProfile, withErrors: withErrors, dataCountIfAvailable: dataCountIfAvailable)
|
||||
}
|
||||
}
|
||||
|
||||
private func statusDescription(isActiveProfile: Bool, withErrors: Bool, dataCountIfAvailable: Bool) -> String {
|
||||
guard isActiveProfile && isEnabled else {
|
||||
return L10n.Tunnelkit.Vpn.disabled
|
||||
}
|
||||
@ -65,8 +76,8 @@ extension Profile.WireGuardSettings {
|
||||
}
|
||||
}
|
||||
|
||||
extension Network.Choice {
|
||||
var localizedDescription: String {
|
||||
extension Network.Choice: LocalizableEntity {
|
||||
public var localizedDescription: String {
|
||||
switch self {
|
||||
case .automatic:
|
||||
return L10n.Global.Strings.automatic
|
||||
@ -77,8 +88,8 @@ extension Network.Choice {
|
||||
}
|
||||
}
|
||||
|
||||
extension Network.DNSSettings.ConfigurationType {
|
||||
var localizedDescription: String {
|
||||
extension Network.DNSSettings.ConfigurationType: LocalizableEntity {
|
||||
public var localizedDescription: String {
|
||||
switch self {
|
||||
case .plain:
|
||||
return Unlocalized.DNS.plain
|
||||
@ -95,8 +106,8 @@ extension Network.DNSSettings.ConfigurationType {
|
||||
}
|
||||
}
|
||||
|
||||
extension Network.ProxySettings.ConfigurationType {
|
||||
var localizedDescription: String {
|
||||
extension Network.ProxySettings.ConfigurationType: LocalizableEntity {
|
||||
public var localizedDescription: String {
|
||||
switch self {
|
||||
case .manual:
|
||||
return L10n.Global.Strings.manual
|
||||
@ -109,3 +120,38 @@ extension Network.ProxySettings.ConfigurationType {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Profile.Account.AuthenticationMethod: LocalizableEntity {
|
||||
public var localizedDescription: String {
|
||||
switch self {
|
||||
case .persistent:
|
||||
return L10n.Account.Items.AuthenticationMethod.persistent
|
||||
|
||||
case .interactive:
|
||||
return L10n.Account.Items.AuthenticationMethod.interactive
|
||||
|
||||
case .totp:
|
||||
return Unlocalized.Other.totp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Int: StyledLocalizableEntity {
|
||||
public enum Style {
|
||||
case mtu
|
||||
}
|
||||
|
||||
public func localizedDescription(style: Style) -> String {
|
||||
switch style {
|
||||
case .mtu:
|
||||
return mtuDescription
|
||||
}
|
||||
}
|
||||
|
||||
private var mtuDescription: String {
|
||||
guard self != 0 else {
|
||||
return L10n.Global.Strings.default
|
||||
}
|
||||
return description
|
||||
}
|
||||
}
|
||||
|
@ -27,20 +27,20 @@ import Foundation
|
||||
import PassepartoutLibrary
|
||||
import TunnelKitOpenVPN
|
||||
|
||||
extension OpenVPN.Cipher {
|
||||
var localizedDescription: String {
|
||||
extension OpenVPN.Cipher: LocalizableEntity {
|
||||
public var localizedDescription: String {
|
||||
description
|
||||
}
|
||||
}
|
||||
|
||||
extension OpenVPN.Digest {
|
||||
var localizedDescription: String {
|
||||
extension OpenVPN.Digest: LocalizableEntity {
|
||||
public var localizedDescription: String {
|
||||
description
|
||||
}
|
||||
}
|
||||
|
||||
extension OpenVPN.CompressionFraming {
|
||||
var localizedDescription: String {
|
||||
extension OpenVPN.CompressionFraming: LocalizableEntity {
|
||||
public var localizedDescription: String {
|
||||
switch self {
|
||||
case .disabled:
|
||||
return L10n.Global.Strings.disabled
|
||||
@ -54,8 +54,8 @@ extension OpenVPN.CompressionFraming {
|
||||
}
|
||||
}
|
||||
|
||||
extension OpenVPN.CompressionAlgorithm {
|
||||
var localizedDescription: String {
|
||||
extension OpenVPN.CompressionAlgorithm: LocalizableEntity {
|
||||
public var localizedDescription: String {
|
||||
let V = L10n.Endpoint.Advanced.Openvpn.Items.self
|
||||
switch self {
|
||||
case .disabled:
|
||||
@ -70,24 +70,24 @@ extension OpenVPN.CompressionAlgorithm {
|
||||
}
|
||||
}
|
||||
|
||||
extension Optional where Wrapped == OpenVPN.TLSWrap {
|
||||
var localizedDescription: String {
|
||||
guard let strategy = self?.strategy else {
|
||||
return L10n.Global.Strings.disabled
|
||||
}
|
||||
let V = L10n.Endpoint.Advanced.Openvpn.Items.self
|
||||
switch strategy {
|
||||
case .auth:
|
||||
return V.TlsWrapping.Value.auth
|
||||
extension OpenVPN.XORMethod: StyledLocalizableEntity {
|
||||
public enum Style {
|
||||
case short
|
||||
|
||||
case .crypt:
|
||||
return V.TlsWrapping.Value.crypt
|
||||
case long
|
||||
}
|
||||
|
||||
public func localizedDescription(style: Style) -> String {
|
||||
switch style {
|
||||
case .short:
|
||||
return shortDescription
|
||||
|
||||
case .long:
|
||||
return longDescription
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension OpenVPN.XORMethod {
|
||||
var localizedDescription: String {
|
||||
private var shortDescription: String {
|
||||
switch self {
|
||||
case .xormask:
|
||||
return Unlocalized.OpenVPN.XOR.xormask.rawValue
|
||||
@ -103,52 +103,22 @@ extension OpenVPN.XORMethod {
|
||||
}
|
||||
}
|
||||
|
||||
var localizedLongDescription: String {
|
||||
private var longDescription: String {
|
||||
switch self {
|
||||
case .xormask(let mask):
|
||||
return "\(localizedDescription) \(mask.toHex())"
|
||||
return "\(shortDescription) \(mask.toHex())"
|
||||
|
||||
case .obfuscate(let mask):
|
||||
return "\(localizedDescription) \(mask.toHex())"
|
||||
return "\(shortDescription) \(mask.toHex())"
|
||||
|
||||
default:
|
||||
return localizedDescription
|
||||
return shortDescription
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Optional where Wrapped == Bool {
|
||||
var localizedDescriptionAsEKU: String {
|
||||
let V = L10n.Global.Strings.self
|
||||
return (self ?? false) ? V.enabled : V.disabled
|
||||
}
|
||||
}
|
||||
|
||||
extension TimeInterval {
|
||||
var localizedDescriptionAsRenegotiatesAfter: String {
|
||||
let V = L10n.Endpoint.Advanced.Openvpn.Items.self
|
||||
if self > 0 {
|
||||
return V.RenegotiationSeconds.Value.after(TimeInterval(self).localizedDescription)
|
||||
} else {
|
||||
return L10n.Global.Strings.disabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Bool {
|
||||
var localizedDescriptionAsRandomizeEndpoint: String {
|
||||
let V = L10n.Global.Strings.self
|
||||
return self ? V.enabled : V.disabled
|
||||
}
|
||||
|
||||
var localizedDescriptionAsRandomizeHostnames: String {
|
||||
let V = L10n.Global.Strings.self
|
||||
return self ? V.enabled : V.disabled
|
||||
}
|
||||
}
|
||||
|
||||
extension OpenVPN.PullMask {
|
||||
var localizedDescription: String {
|
||||
extension OpenVPN.PullMask: LocalizableEntity {
|
||||
public var localizedDescription: String {
|
||||
switch self {
|
||||
case .routes:
|
||||
return L10n.Endpoint.Advanced.Openvpn.Items.Route.caption
|
||||
@ -162,6 +132,111 @@ extension OpenVPN.PullMask {
|
||||
}
|
||||
}
|
||||
|
||||
extension OpenVPN.ConfigurationBuilder: StyledLocalizableEntity {
|
||||
public enum Style {
|
||||
case tlsWrap
|
||||
|
||||
case eku
|
||||
}
|
||||
|
||||
public func localizedDescription(style: Style) -> String {
|
||||
switch style {
|
||||
case .tlsWrap:
|
||||
return tlsWrap.tlsWrapDescription
|
||||
|
||||
case .eku:
|
||||
return checksEKU.ekuDescription
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension OpenVPN.Configuration: StyledOptionalLocalizableEntity {
|
||||
public enum OptionalStyle {
|
||||
case keepAlive
|
||||
|
||||
case renegotiatesAfter
|
||||
|
||||
case randomizeEndpoint
|
||||
|
||||
case randomizeHostnames
|
||||
}
|
||||
|
||||
public func localizedDescription(optionalStyle: OptionalStyle) -> String? {
|
||||
switch optionalStyle {
|
||||
case .keepAlive:
|
||||
return keepAliveInterval?.keepAliveDescription
|
||||
|
||||
case .renegotiatesAfter:
|
||||
return renegotiatesAfter?.renegotiatesAfterDescription
|
||||
|
||||
case .randomizeEndpoint:
|
||||
return randomizeEndpoint?.randomizeEndpointDescription
|
||||
|
||||
case .randomizeHostnames:
|
||||
return randomizeHostnames?.randomizeHostnamesDescription
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension Optional where Wrapped == OpenVPN.TLSWrap {
|
||||
var tlsWrapDescription: String {
|
||||
guard let strategy = self?.strategy else {
|
||||
return L10n.Global.Strings.disabled
|
||||
}
|
||||
let V = L10n.Endpoint.Advanced.Openvpn.Items.self
|
||||
switch strategy {
|
||||
case .auth:
|
||||
return V.TlsWrapping.Value.auth
|
||||
|
||||
case .crypt:
|
||||
return V.TlsWrapping.Value.crypt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension TimeInterval {
|
||||
var keepAliveDescription: String {
|
||||
let V = L10n.Endpoint.Advanced.Openvpn.Items.self
|
||||
if self > 0 {
|
||||
return V.KeepAlive.Value.seconds(Int(self))
|
||||
} else {
|
||||
return L10n.Global.Strings.disabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension Optional where Wrapped == Bool {
|
||||
var ekuDescription: String {
|
||||
let V = L10n.Global.Strings.self
|
||||
return (self ?? false) ? V.enabled : V.disabled
|
||||
}
|
||||
}
|
||||
|
||||
private extension TimeInterval {
|
||||
var renegotiatesAfterDescription: String {
|
||||
let V = L10n.Endpoint.Advanced.Openvpn.Items.self
|
||||
if self > 0 {
|
||||
return V.RenegotiationSeconds.Value.after(TimeInterval(self).localizedDescription)
|
||||
} else {
|
||||
return L10n.Global.Strings.disabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension Bool {
|
||||
var randomizeEndpointDescription: String {
|
||||
let V = L10n.Global.Strings.self
|
||||
return self ? V.enabled : V.disabled
|
||||
}
|
||||
|
||||
var randomizeHostnamesDescription: String {
|
||||
let V = L10n.Global.Strings.self
|
||||
return self ? V.enabled : V.disabled
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Errors
|
||||
|
||||
extension TunnelKitOpenVPNError: LocalizedError {
|
||||
public var errorDescription: String? {
|
||||
let V = L10n.Tunnelkit.Errors.Vpn.self
|
||||
|
@ -26,15 +26,31 @@
|
||||
import Foundation
|
||||
import PassepartoutLibrary
|
||||
|
||||
extension ProviderManager {
|
||||
func localizedPreset(forProfile profile: Profile) -> String? {
|
||||
extension ProviderManager: StyledOptionalLocalizableEntity {
|
||||
public enum OptionalStyle {
|
||||
case preset(profile: Profile)
|
||||
|
||||
case infrastructureUpdate(profile: Profile)
|
||||
}
|
||||
|
||||
public func localizedDescription(optionalStyle: OptionalStyle) -> String? {
|
||||
switch optionalStyle {
|
||||
case .preset(let profile):
|
||||
return presetDescription(forProfile: profile)
|
||||
|
||||
case .infrastructureUpdate(let profile):
|
||||
return infrastructureUpdateDescription(forProfile: profile)
|
||||
}
|
||||
}
|
||||
|
||||
private func presetDescription(forProfile profile: Profile) -> String? {
|
||||
guard let server = profile.providerServer(self) else {
|
||||
return nil
|
||||
}
|
||||
return profile.providerPreset(server)?.localizedDescription
|
||||
}
|
||||
|
||||
func localizedInfrastructureUpdate(forProfile profile: Profile) -> String? {
|
||||
private func infrastructureUpdateDescription(forProfile profile: Profile) -> String? {
|
||||
guard let providerName = profile.header.providerName else {
|
||||
return nil
|
||||
}
|
||||
@ -42,8 +58,19 @@ extension ProviderManager {
|
||||
}
|
||||
}
|
||||
|
||||
extension ProviderMetadata {
|
||||
var localizedGuidanceString: String? {
|
||||
extension ProviderMetadata: StyledOptionalLocalizableEntity {
|
||||
public enum OptionalStyle {
|
||||
case guidance
|
||||
}
|
||||
|
||||
public func localizedDescription(optionalStyle: OptionalStyle) -> String? {
|
||||
switch optionalStyle {
|
||||
case .guidance:
|
||||
return guidanceString
|
||||
}
|
||||
}
|
||||
|
||||
private var guidanceString: String? {
|
||||
let prefix = "account.sections.guidance.footer.infrastructure"
|
||||
let key = "\(prefix).\(name)"
|
||||
var format = NSLocalizedString(key, bundle: .main, comment: "")
|
||||
@ -59,26 +86,92 @@ extension ProviderMetadata {
|
||||
}
|
||||
}
|
||||
|
||||
extension ProviderLocation {
|
||||
var localizedCountry: String {
|
||||
extension ProviderLocation: StyledLocalizableEntity {
|
||||
public enum Style {
|
||||
case country
|
||||
}
|
||||
|
||||
public func localizedDescription(style: Style) -> String {
|
||||
switch style {
|
||||
case .country:
|
||||
return countryDescription
|
||||
}
|
||||
}
|
||||
|
||||
private var countryDescription: String {
|
||||
countryCode.localizedAsCountryCode
|
||||
}
|
||||
}
|
||||
|
||||
extension ProviderServer {
|
||||
var localizedCountry: String {
|
||||
extension ProviderServer: StyledLocalizableEntity {
|
||||
public enum Style {
|
||||
case country
|
||||
|
||||
case countryWithCategory(withCategory: Bool)
|
||||
|
||||
case shortWithDefault
|
||||
|
||||
case longWithCategory(withCategory: Bool)
|
||||
}
|
||||
|
||||
public func localizedDescription(style: Style) -> String {
|
||||
switch style {
|
||||
case .country:
|
||||
return countryDescription
|
||||
|
||||
case .countryWithCategory(let withCategory):
|
||||
return countryDescription(withCategory: withCategory)
|
||||
|
||||
case .shortWithDefault:
|
||||
return shortDescriptionWithDefault
|
||||
|
||||
case .longWithCategory(let withCategory):
|
||||
return longDescription(withCategory: withCategory)
|
||||
}
|
||||
}
|
||||
|
||||
private var countryDescription: String {
|
||||
countryCode.localizedAsCountryCode
|
||||
}
|
||||
|
||||
func localizedCountry(withCategory: Bool) -> String {
|
||||
let desc = localizedCountry
|
||||
private func countryDescription(withCategory: Bool) -> String {
|
||||
let desc = countryDescription
|
||||
if withCategory, !categoryName.isEmpty {
|
||||
return "\(categoryName.uppercased()): \(desc)"
|
||||
}
|
||||
return desc
|
||||
}
|
||||
|
||||
var localizedShortDescription: String? {
|
||||
private var shortDescriptionWithDefault: String {
|
||||
shortDescription ?? "\(L10n.Global.Strings.default) [\(apiId)]"
|
||||
}
|
||||
|
||||
private func longDescription(withCategory: Bool) -> String {
|
||||
var comps: [String] = [countryDescription]
|
||||
shortDescription.map {
|
||||
comps.append($0)
|
||||
}
|
||||
let desc = comps.joined(separator: ", ")
|
||||
if withCategory, !categoryName.isEmpty {
|
||||
return "\(categoryName.uppercased()): \(desc)"
|
||||
}
|
||||
return desc
|
||||
}
|
||||
}
|
||||
|
||||
extension ProviderServer: StyledOptionalLocalizableEntity {
|
||||
public enum OptionalStyle {
|
||||
case short
|
||||
}
|
||||
|
||||
public func localizedDescription(optionalStyle: OptionalStyle) -> String? {
|
||||
switch optionalStyle {
|
||||
case .short:
|
||||
return shortDescription
|
||||
}
|
||||
}
|
||||
|
||||
private var shortDescription: String? {
|
||||
var comps = localizedName.map { [$0] } ?? []
|
||||
if let serverIndex = serverIndex {
|
||||
comps.append("#\(serverIndex)")
|
||||
@ -96,26 +189,10 @@ extension ProviderServer {
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
var localizedShortDescriptionWithDefault: String {
|
||||
localizedShortDescription ?? "\(L10n.Global.Strings.default) [\(apiId)]"
|
||||
}
|
||||
|
||||
func localizedLongDescription(withCategory: Bool) -> String {
|
||||
var comps: [String] = [localizedCountry]
|
||||
localizedShortDescription.map {
|
||||
comps.append($0)
|
||||
}
|
||||
let desc = comps.joined(separator: ", ")
|
||||
if withCategory, !categoryName.isEmpty {
|
||||
return "\(categoryName.uppercased()): \(desc)"
|
||||
}
|
||||
return desc
|
||||
}
|
||||
}
|
||||
|
||||
extension ProviderServer.Preset {
|
||||
var localizedDescription: String {
|
||||
extension ProviderServer.Preset: LocalizableEntity {
|
||||
public var localizedDescription: String {
|
||||
name
|
||||
}
|
||||
}
|
||||
|
@ -30,8 +30,8 @@ import TunnelKitManager
|
||||
import TunnelKitOpenVPN
|
||||
import TunnelKitWireGuard
|
||||
|
||||
extension VPNStatus {
|
||||
var localizedDescription: String {
|
||||
extension VPNStatus: LocalizableEntity {
|
||||
public var localizedDescription: String {
|
||||
switch self {
|
||||
case .connecting:
|
||||
return L10n.Tunnelkit.Vpn.connecting
|
||||
@ -48,62 +48,74 @@ extension VPNStatus {
|
||||
}
|
||||
}
|
||||
|
||||
extension DataCount {
|
||||
var localizedDescription: String {
|
||||
extension DataCount: LocalizableEntity {
|
||||
public var localizedDescription: String {
|
||||
let down = received.descriptionAsDataUnit
|
||||
let up = sent.descriptionAsDataUnit
|
||||
return "↓\(down) ↑\(up)"
|
||||
}
|
||||
}
|
||||
|
||||
extension Int {
|
||||
var localizedDescriptionAsMTU: String {
|
||||
guard self != 0 else {
|
||||
return L10n.Global.Strings.default
|
||||
}
|
||||
return description
|
||||
}
|
||||
}
|
||||
extension IPv4Settings: StyledLocalizableEntity {
|
||||
public enum Style {
|
||||
case address
|
||||
|
||||
extension TimeInterval {
|
||||
var localizedDescriptionAsKeepAlive: String {
|
||||
let V = L10n.Endpoint.Advanced.Openvpn.Items.self
|
||||
if self > 0 {
|
||||
return V.KeepAlive.Value.seconds(Int(self))
|
||||
} else {
|
||||
return L10n.Global.Strings.disabled
|
||||
case defaultGateway
|
||||
}
|
||||
|
||||
public func localizedDescription(style: Style) -> String {
|
||||
switch style {
|
||||
case .address:
|
||||
return addressDescription
|
||||
|
||||
case .defaultGateway:
|
||||
return defaultGatewayDescription
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension IPv4Settings {
|
||||
var localizedAddress: String {
|
||||
private var addressDescription: String {
|
||||
"\(address)/\(addressMask)"
|
||||
}
|
||||
|
||||
var localizedDefaultGateway: String {
|
||||
private var defaultGatewayDescription: String {
|
||||
defaultGateway
|
||||
}
|
||||
}
|
||||
|
||||
extension IPv6Settings {
|
||||
var localizedAddress: String {
|
||||
extension IPv6Settings: StyledLocalizableEntity {
|
||||
public enum Style {
|
||||
case address
|
||||
|
||||
case defaultGateway
|
||||
}
|
||||
|
||||
public func localizedDescription(style: Style) -> String {
|
||||
switch style {
|
||||
case .address:
|
||||
return addressDescription
|
||||
|
||||
case .defaultGateway:
|
||||
return defaultGatewayDescription
|
||||
}
|
||||
}
|
||||
|
||||
private var addressDescription: String {
|
||||
"\(address)/\(addressPrefixLength)"
|
||||
}
|
||||
|
||||
var localizedDefaultGateway: String {
|
||||
private var defaultGatewayDescription: String {
|
||||
defaultGateway
|
||||
}
|
||||
}
|
||||
|
||||
extension IPv4Settings.Route {
|
||||
var localizedDescription: String {
|
||||
extension IPv4Settings.Route: LocalizableEntity {
|
||||
public var localizedDescription: String {
|
||||
"\(destination)/\(mask) -> \(gateway ?? "*")"
|
||||
}
|
||||
}
|
||||
|
||||
extension IPv6Settings.Route {
|
||||
var localizedDescription: String {
|
||||
extension IPv6Settings.Route: LocalizableEntity {
|
||||
public var localizedDescription: String {
|
||||
"\(destination)/\(prefixLength) -> \(gateway ?? "*")"
|
||||
}
|
||||
}
|
||||
|
@ -24,8 +24,36 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import PassepartoutLibrary
|
||||
import TunnelKitWireGuard
|
||||
|
||||
extension WireGuard.ConfigurationBuilder: StyledOptionalLocalizableEntity {
|
||||
public enum OptionalStyle {
|
||||
case keepAlive(peerIndex: Int)
|
||||
}
|
||||
|
||||
public func localizedDescription(optionalStyle: OptionalStyle) -> String? {
|
||||
switch optionalStyle {
|
||||
case .keepAlive(let peerIndex):
|
||||
return keepAlive(ofPeer: peerIndex)?.keepAliveDescription
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension UInt16 {
|
||||
var keepAliveDescription: String {
|
||||
// FIXME: l10n, move from OpenVPN to shared
|
||||
let V = L10n.Endpoint.Advanced.Openvpn.Items.self
|
||||
if self > 0 {
|
||||
return V.KeepAlive.Value.seconds(Int(self))
|
||||
} else {
|
||||
return L10n.Global.Strings.disabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Errors
|
||||
|
||||
extension TunnelKitWireGuardError: LocalizedError {
|
||||
public var errorDescription: String? {
|
||||
let V = L10n.Tunnelkit.Errors.Vpn.self
|
||||
|
@ -50,7 +50,7 @@ final class DefaultLightProviderLocation: LightProviderLocation {
|
||||
let servers: [LightProviderServer]
|
||||
|
||||
init(_ location: ProviderLocation) {
|
||||
description = location.localizedCountry
|
||||
description = location.localizedDescription(style: .country)
|
||||
id = location.id
|
||||
countryCode = location.countryCode
|
||||
servers = location.servers?
|
||||
@ -71,8 +71,8 @@ final class DefaultLightProviderServer: LightProviderServer {
|
||||
let serverId: String
|
||||
|
||||
init(_ server: ProviderServer) {
|
||||
description = server.localizedShortDescriptionWithDefault
|
||||
longDescription = server.localizedLongDescription(withCategory: false)
|
||||
description = server.localizedDescription(style: .shortWithDefault)
|
||||
longDescription = server.localizedDescription(style: .longWithCategory(withCategory: false))
|
||||
categoryName = server.categoryName
|
||||
locationId = server.locationId
|
||||
serverId = server.id
|
||||
|
@ -89,7 +89,7 @@ struct AccountView: View {
|
||||
.withLeadingText(L10n.Account.Items.Seed.caption)
|
||||
}
|
||||
} footer: {
|
||||
metadata?.localizedGuidanceString.map {
|
||||
metadata?.localizedDescription(optionalStyle: .guidance).map {
|
||||
Text($0)
|
||||
}
|
||||
}
|
||||
@ -133,21 +133,6 @@ private extension AccountView {
|
||||
}
|
||||
}
|
||||
|
||||
private extension Profile.Account.AuthenticationMethod {
|
||||
var localizedDescription: String {
|
||||
switch self {
|
||||
case .persistent:
|
||||
return L10n.Account.Items.AuthenticationMethod.persistent
|
||||
|
||||
case .interactive:
|
||||
return L10n.Account.Items.AuthenticationMethod.interactive
|
||||
|
||||
case .totp:
|
||||
return Unlocalized.Other.totp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
private extension AccountView {
|
||||
|
@ -87,11 +87,11 @@ private extension EndpointAdvancedView.OpenVPNView {
|
||||
if let settings = builder.ipv4 {
|
||||
themeLongContentLinkDefault(
|
||||
L10n.Global.Strings.address,
|
||||
content: .constant(settings.localizedAddress)
|
||||
content: .constant(settings.localizedDescription(style: .address))
|
||||
)
|
||||
themeLongContentLinkDefault(
|
||||
L10n.NetworkSettings.Gateway.title,
|
||||
content: .constant(settings.localizedDefaultGateway)
|
||||
content: .constant(settings.localizedDescription(style: .defaultGateway))
|
||||
)
|
||||
}
|
||||
builder.routes4.map { routes in
|
||||
@ -112,11 +112,11 @@ private extension EndpointAdvancedView.OpenVPNView {
|
||||
if let settings = builder.ipv6 {
|
||||
themeLongContentLinkDefault(
|
||||
L10n.Global.Strings.address,
|
||||
content: .constant(settings.localizedAddress)
|
||||
content: .constant(settings.localizedDescription(style: .address))
|
||||
)
|
||||
themeLongContentLinkDefault(
|
||||
L10n.NetworkSettings.Gateway.title,
|
||||
content: .constant(settings.localizedDefaultGateway)
|
||||
content: .constant(settings.localizedDescription(style: .defaultGateway))
|
||||
)
|
||||
}
|
||||
builder.routes6.map { routes in
|
||||
@ -137,17 +137,17 @@ private extension EndpointAdvancedView.OpenVPNView {
|
||||
Section {
|
||||
settings.cipher.map {
|
||||
Text(L10n.Endpoint.Advanced.Openvpn.Items.Cipher.caption)
|
||||
.withTrailingText($0.localizedDescription)
|
||||
.withTrailingText($0)
|
||||
}
|
||||
settings.digest.map {
|
||||
Text(L10n.Endpoint.Advanced.Openvpn.Items.Digest.caption)
|
||||
.withTrailingText($0.localizedDescription)
|
||||
.withTrailingText($0)
|
||||
}
|
||||
if let xor = settings.xor {
|
||||
themeLongContentLink(
|
||||
Unlocalized.VPN.xor,
|
||||
content: .constant(xor.localizedLongDescription),
|
||||
withPreview: xor.localizedDescription
|
||||
content: .constant(xor.longDescription),
|
||||
withPreview: xor.shortDescription
|
||||
)
|
||||
} else {
|
||||
Text(Unlocalized.VPN.xor)
|
||||
@ -176,8 +176,8 @@ private extension EndpointAdvancedView.OpenVPNView {
|
||||
if let xor = builder.xorMethod {
|
||||
themeLongContentLink(
|
||||
Unlocalized.VPN.xor,
|
||||
content: .constant(xor.localizedLongDescription),
|
||||
withPreview: xor.localizedDescription
|
||||
content: .constant(xor.localizedDescription(style: .long)),
|
||||
withPreview: xor.localizedDescription(style: .short)
|
||||
)
|
||||
} else {
|
||||
Text(Unlocalized.VPN.xor)
|
||||
@ -193,11 +193,11 @@ private extension EndpointAdvancedView.OpenVPNView {
|
||||
Section {
|
||||
settings.framing.map {
|
||||
Text(L10n.Endpoint.Advanced.Openvpn.Items.CompressionFraming.caption)
|
||||
.withTrailingText($0.localizedDescription)
|
||||
.withTrailingText($0)
|
||||
}
|
||||
settings.algorithm.map {
|
||||
Text(L10n.Endpoint.Advanced.Openvpn.Items.CompressionAlgorithm.caption)
|
||||
.withTrailingText($0.localizedDescription)
|
||||
.withTrailingText($0)
|
||||
}
|
||||
} header: {
|
||||
Text(L10n.Endpoint.Advanced.Openvpn.Sections.Compression.header)
|
||||
@ -246,11 +246,11 @@ private extension EndpointAdvancedView.OpenVPNView {
|
||||
Section {
|
||||
settings.proxy.map {
|
||||
Text(L10n.Global.Strings.address)
|
||||
.withTrailingText($0.rawValue, copyOnTap: true)
|
||||
.withTrailingText($0, copyOnTap: true)
|
||||
}
|
||||
settings.pac.map {
|
||||
Text(Unlocalized.Network.proxyAutoConfiguration)
|
||||
.withTrailingText($0.absoluteString, copyOnTap: true)
|
||||
.withTrailingText($0, copyOnTap: true)
|
||||
}
|
||||
ForEach(settings.bypass, id: \.self) {
|
||||
Text(L10n.NetworkSettings.Items.ProxyBypass.caption)
|
||||
@ -286,11 +286,11 @@ private extension EndpointAdvancedView.OpenVPNView {
|
||||
themeLongContentLink(
|
||||
L10n.Endpoint.Advanced.Openvpn.Items.TlsWrapping.caption,
|
||||
content: .constant(wrap.key.hexString),
|
||||
withPreview: builder.tlsWrap.localizedDescription
|
||||
withPreview: builder.localizedDescription(style: .tlsWrap)
|
||||
)
|
||||
}
|
||||
Text(L10n.Endpoint.Advanced.Openvpn.Items.Eku.caption)
|
||||
.withTrailingText(builder.checksEKU.localizedDescriptionAsEKU)
|
||||
.withTrailingText(builder.localizedDescription(style: .eku))
|
||||
} header: {
|
||||
Text(Unlocalized.Network.tls)
|
||||
}
|
||||
@ -301,19 +301,19 @@ private extension EndpointAdvancedView.OpenVPNView {
|
||||
Section {
|
||||
settings.keepAlive.map {
|
||||
Text(L10n.Global.Strings.keepalive)
|
||||
.withTrailingText($0.localizedDescriptionAsKeepAlive)
|
||||
.withTrailingText($0)
|
||||
}
|
||||
settings.reneg.map {
|
||||
Text(L10n.Endpoint.Advanced.Openvpn.Items.RenegotiationSeconds.caption)
|
||||
.withTrailingText($0.localizedDescriptionAsRenegotiatesAfter)
|
||||
.withTrailingText($0)
|
||||
}
|
||||
settings.randomizeEndpoint.map {
|
||||
Text(L10n.Endpoint.Advanced.Openvpn.Items.RandomEndpoint.caption)
|
||||
.withTrailingText($0.localizedDescriptionAsRandomizeEndpoint)
|
||||
.withTrailingText($0)
|
||||
}
|
||||
settings.randomizeHostnames.map {
|
||||
Text(L10n.Endpoint.Advanced.Openvpn.Items.RandomHostname.caption)
|
||||
.withTrailingText($0.localizedDescriptionAsRandomizeHostnames)
|
||||
.withTrailingText($0)
|
||||
}
|
||||
} header: {
|
||||
Text(L10n.Endpoint.Advanced.Openvpn.Sections.Other.header)
|
||||
@ -324,17 +324,17 @@ private extension EndpointAdvancedView.OpenVPNView {
|
||||
|
||||
private extension OpenVPN.Configuration {
|
||||
struct CommunicationOptions {
|
||||
let cipher: OpenVPN.Cipher?
|
||||
let cipher: String?
|
||||
|
||||
let digest: OpenVPN.Digest?
|
||||
let digest: String?
|
||||
|
||||
let xor: OpenVPN.XORMethod?
|
||||
let xor: (shortDescription: String, longDescription: String)?
|
||||
}
|
||||
|
||||
struct CompressionOptions {
|
||||
let framing: OpenVPN.CompressionFraming?
|
||||
let framing: String?
|
||||
|
||||
let algorithm: OpenVPN.CompressionAlgorithm?
|
||||
let algorithm: String?
|
||||
}
|
||||
|
||||
struct DNSOptions {
|
||||
@ -344,35 +344,44 @@ private extension OpenVPN.Configuration {
|
||||
}
|
||||
|
||||
struct ProxyOptions {
|
||||
let proxy: Proxy?
|
||||
let proxy: String?
|
||||
|
||||
let pac: URL?
|
||||
let pac: String?
|
||||
|
||||
let bypass: [String]
|
||||
}
|
||||
|
||||
struct OtherOptions {
|
||||
let keepAlive: TimeInterval?
|
||||
let keepAlive: String?
|
||||
|
||||
let reneg: TimeInterval?
|
||||
let reneg: String?
|
||||
|
||||
let randomizeEndpoint: Bool?
|
||||
let randomizeEndpoint: String?
|
||||
|
||||
let randomizeHostnames: Bool?
|
||||
let randomizeHostnames: String?
|
||||
}
|
||||
|
||||
var communicationSettings: CommunicationOptions? {
|
||||
guard cipher != nil || digest != nil || xorMethod != nil else {
|
||||
return nil
|
||||
}
|
||||
return .init(cipher: cipher, digest: digest, xor: xorMethod)
|
||||
return .init(
|
||||
cipher: cipher?.localizedDescription,
|
||||
digest: digest?.localizedDescription,
|
||||
xor: xorMethod.map {
|
||||
($0.localizedDescription(style: .short), $0.localizedDescription(style: .long))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
var compressionSettings: CompressionOptions? {
|
||||
guard compressionFraming != nil || compressionAlgorithm != nil else {
|
||||
return nil
|
||||
}
|
||||
return .init(framing: compressionFraming, algorithm: compressionAlgorithm)
|
||||
return .init(
|
||||
framing: compressionFraming?.localizedDescription,
|
||||
algorithm: compressionAlgorithm?.localizedDescription
|
||||
)
|
||||
}
|
||||
|
||||
var dnsSettings: DNSOptions? {
|
||||
@ -388,8 +397,8 @@ private extension OpenVPN.Configuration {
|
||||
return nil
|
||||
}
|
||||
return .init(
|
||||
proxy: httpsProxy ?? httpProxy,
|
||||
pac: proxyAutoConfigurationURL,
|
||||
proxy: (httpsProxy ?? httpProxy)?.rawValue,
|
||||
pac: proxyAutoConfigurationURL?.absoluteString,
|
||||
bypass: proxyBypassDomains ?? []
|
||||
)
|
||||
}
|
||||
@ -400,10 +409,10 @@ private extension OpenVPN.Configuration {
|
||||
return nil
|
||||
}
|
||||
return .init(
|
||||
keepAlive: keepAliveInterval,
|
||||
reneg: renegotiatesAfter,
|
||||
randomizeEndpoint: randomizeEndpoint,
|
||||
randomizeHostnames: randomizeHostnames
|
||||
keepAlive: localizedDescription(optionalStyle: .keepAlive),
|
||||
reneg: localizedDescription(optionalStyle: .renegotiatesAfter),
|
||||
randomizeEndpoint: localizedDescription(optionalStyle: .randomizeEndpoint),
|
||||
randomizeHostnames: localizedDescription(optionalStyle: .randomizeHostnames)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ private extension EndpointAdvancedView.WireGuardView {
|
||||
builder.mtu.map { mtu in
|
||||
Section {
|
||||
Text(Unlocalized.Network.mtu)
|
||||
.withTrailingText(Int(mtu).localizedDescriptionAsMTU)
|
||||
.withTrailingText(Int(mtu).localizedDescription(style: .mtu))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -125,9 +125,9 @@ private extension EndpointView.WireGuardView {
|
||||
Text(L10n.Endpoint.Wireguard.Items.AllowedIp.caption)
|
||||
.withTrailingText($0)
|
||||
}
|
||||
builder.keepAlive(ofPeer: peerIndex).map {
|
||||
builder.localizedDescription(optionalStyle: .keepAlive(peerIndex: peerIndex)).map {
|
||||
Text(L10n.Global.Strings.keepalive)
|
||||
.withTrailingText(TimeInterval($0).localizedDescriptionAsKeepAlive)
|
||||
.withTrailingText($0)
|
||||
}
|
||||
} header: {
|
||||
Text(L10n.Endpoint.Wireguard.Items.Peer.caption)
|
||||
|
@ -257,7 +257,7 @@ private extension NetworkSettingsView {
|
||||
L10n.Global.Strings.bytes,
|
||||
selection: $settings.mtu.mtuBytes,
|
||||
values: Network.MTUSettings.availableBytes,
|
||||
description: \.localizedDescriptionAsMTU
|
||||
description: { $0.localizedDescription(style: .mtu) }
|
||||
)
|
||||
}
|
||||
} header: {
|
||||
|
@ -130,9 +130,9 @@ private extension ProfileView.ProviderSection {
|
||||
return nil
|
||||
}
|
||||
if currentProfile.value.providerRandomizesServer ?? false {
|
||||
return server.localizedCountry(withCategory: true)
|
||||
return server.localizedDescription(style: .countryWithCategory(withCategory: true))
|
||||
} else {
|
||||
return server.localizedLongDescription(withCategory: true)
|
||||
return server.localizedDescription(style: .longWithCategory(withCategory: true))
|
||||
}
|
||||
}
|
||||
|
||||
@ -144,11 +144,11 @@ private extension ProfileView.ProviderSection {
|
||||
}
|
||||
|
||||
var currentProviderPreset: String? {
|
||||
providerManager.localizedPreset(forProfile: profile)
|
||||
providerManager.localizedDescription(optionalStyle: .preset(profile: profile))
|
||||
}
|
||||
|
||||
var lastInfrastructureUpdate: String? {
|
||||
providerManager.localizedInfrastructureUpdate(forProfile: profile)
|
||||
providerManager.localizedDescription(optionalStyle: .infrastructureUpdate(profile: profile))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,13 +101,15 @@ extension ProviderLocationView {
|
||||
HStack {
|
||||
themeAssetsCountryImage(location.countryCode).asAssetImage
|
||||
VStack {
|
||||
if let singleServer = location.onlyServer, singleServer.localizedShortDescription != nil {
|
||||
Text(location.localizedCountry)
|
||||
if let singleServer = location.onlyServer,
|
||||
let shortServerDescription = singleServer.localizedDescription(optionalStyle: .short) {
|
||||
|
||||
Text(location.localizedDescription(style: .country))
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
Text(singleServer.localizedShortDescription ?? "")
|
||||
Text(shortServerDescription)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
} else {
|
||||
Text(location.localizedCountry)
|
||||
Text(location.localizedDescription(style: .country))
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
|
||||
}
|
||||
}.withTrailingCheckmark(when: location.id == selectedLocationId)
|
||||
@ -132,7 +134,7 @@ extension ProviderLocationView {
|
||||
ScrollViewReader { scrollProxy in
|
||||
List {
|
||||
ForEach(servers) { server in
|
||||
Button(server.localizedShortDescriptionWithDefault) {
|
||||
Button(server.localizedDescription(style: .shortWithDefault)) {
|
||||
selectedServer = server
|
||||
}.withTrailingCheckmark(when: server.id == selectedServer?.id)
|
||||
}
|
||||
@ -211,7 +213,7 @@ private extension ProviderLocationView {
|
||||
ServerListView(
|
||||
location: location,
|
||||
selectedServer: $selectedServer
|
||||
).navigationTitle(location.localizedCountry)
|
||||
).navigationTitle(location.localizedDescription(style: .country))
|
||||
}, label: {
|
||||
LocationRow(
|
||||
location: location,
|
||||
|
@ -45,10 +45,10 @@ struct VPNStatusText: View {
|
||||
|
||||
private extension VPNStatusText {
|
||||
var statusText: String {
|
||||
currentVPNState.localizedStatusDescription(
|
||||
currentVPNState.localizedDescription(style: .status(
|
||||
isActiveProfile: isActiveProfile,
|
||||
withErrors: true,
|
||||
dataCountIfAvailable: true
|
||||
)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,42 @@
|
||||
//
|
||||
// LocalizableEntity.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 7/4/23.
|
||||
// Copyright (c) 2023 Davide De Rosa. All rights reserved.
|
||||
//
|
||||
// https://github.com/passepartoutvpn
|
||||
//
|
||||
// This file is part of Passepartout.
|
||||
//
|
||||
// Passepartout is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Passepartout is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol LocalizableEntity {
|
||||
var localizedDescription: String { get }
|
||||
}
|
||||
|
||||
public protocol StyledLocalizableEntity {
|
||||
associatedtype Style
|
||||
|
||||
func localizedDescription(style: Style) -> String
|
||||
}
|
||||
|
||||
public protocol StyledOptionalLocalizableEntity {
|
||||
associatedtype OptionalStyle
|
||||
|
||||
func localizedDescription(optionalStyle: OptionalStyle) -> String?
|
||||
}
|
@ -25,8 +25,8 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
struct Validators {
|
||||
enum ValidationError: Error {
|
||||
public struct Validators {
|
||||
public enum ValidationError: Error {
|
||||
case notSet
|
||||
|
||||
case empty
|
||||
@ -44,20 +44,20 @@ struct Validators {
|
||||
|
||||
private static let rxWildcardDomainName = NSRegularExpression("^((?!-)[A-Za-z0-9-]{1,63}(?<!-)\\.|\\*\\.)+([A-Za-z]{2,}|\\*)$")
|
||||
|
||||
static func notNil(_ string: String?) throws {
|
||||
public static func notNil(_ string: String?) throws {
|
||||
guard string != nil else {
|
||||
throw ValidationError.notSet
|
||||
}
|
||||
}
|
||||
|
||||
static func notEmpty(_ string: String) throws {
|
||||
public static func notEmpty(_ string: String) throws {
|
||||
let valid = string.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !valid.isEmpty else {
|
||||
throw ValidationError.empty
|
||||
}
|
||||
}
|
||||
|
||||
static func ipAddress(_ string: String) throws {
|
||||
public static func ipAddress(_ string: String) throws {
|
||||
var sin = sockaddr_in()
|
||||
var sin6 = sockaddr_in6()
|
||||
|
||||
@ -68,25 +68,25 @@ struct Validators {
|
||||
}
|
||||
}
|
||||
|
||||
static func domainName(_ string: String) throws {
|
||||
public static func domainName(_ string: String) throws {
|
||||
guard rxDomainName.numberOfMatches(in: string, options: [], range: .init(location: 0, length: string.count)) > 0 else {
|
||||
throw ValidationError.domainName
|
||||
}
|
||||
}
|
||||
|
||||
static func wildcardDomainName(_ string: String) throws {
|
||||
public static func wildcardDomainName(_ string: String) throws {
|
||||
guard rxWildcardDomainName.numberOfMatches(in: string, options: [], range: .init(location: 0, length: string.count)) > 0 else {
|
||||
throw ValidationError.wildcardDomainName
|
||||
}
|
||||
}
|
||||
|
||||
static func url(_ string: String) throws {
|
||||
public static func url(_ string: String) throws {
|
||||
guard URL(string: string) != nil else {
|
||||
throw ValidationError.url
|
||||
}
|
||||
}
|
||||
|
||||
static func dnsOverTLSServerName(_ string: String) throws {
|
||||
public static func dnsOverTLSServerName(_ string: String) throws {
|
||||
do {
|
||||
try domainName(string)
|
||||
} catch {
|
Loading…
Reference in New Issue
Block a user