| 1 | import Foundation |
| 2 | import RastrilloNative |
| 3 | |
| 4 | /// The words the kit's sync views say. `.kit` reads the package's own |
| 5 | /// catalog, in the same twelve languages as the apps; an app overrides any |
| 6 | /// of them, for example to count cells instead of documents, by changing a |
| 7 | /// copy. |
| 8 | public struct NativeSyncStrings: Sendable { |
| 9 | public var saving: String |
| 10 | public var retrying: String |
| 11 | /// "Offline · N pending". |
| 12 | public var offline: @Sendable (Int) -> String |
| 13 | /// "Sign in to send N pending". |
| 14 | public var needsSignIn: @Sendable (Int) -> String |
| 15 | public var signIn: String |
| 16 | /// A held entry's words are the app's; this is the fallback. |
| 17 | public var failed: @Sendable (SyncFailure) -> String |
| 18 | /// The banner while the path or server is down and edits are not durable. |
| 19 | public var offlineWaiting: String |
| 20 | /// The banner once edits are kept on this device. |
| 21 | public var offlineDurable: String |
| 22 | /// Reported once after a lost key or corrupt entry removed unsent changes. |
| 23 | public var unsentLost: String |
| 24 | |
| 25 | public init(saving: String, retrying: String, offline: @escaping @Sendable (Int) -> String, |
| 26 | needsSignIn: @escaping @Sendable (Int) -> String, signIn: String, |
| 27 | failed: @escaping @Sendable (SyncFailure) -> String, offlineWaiting: String, |
| 28 | offlineDurable: String, unsentLost: String) { |
| 29 | self.saving = saving; self.retrying = retrying; self.offline = offline; self.needsSignIn = needsSignIn |
| 30 | self.signIn = signIn; self.failed = failed; self.offlineWaiting = offlineWaiting |
| 31 | self.offlineDurable = offlineDurable; self.unsentLost = unsentLost |
| 32 | } |
| 33 | |
| 34 | public static var kit: NativeSyncStrings { kit(localization: Bundle.module.preferredLocalizations.first ?? "en") } |
| 35 | public static func kit(localization: String) -> NativeSyncStrings { |
| 36 | let path = Bundle.module.path(forResource: localization, ofType: "lproj") |
| 37 | ?? Bundle.module.path(forResource: "en", ofType: "lproj")! |
| 38 | let bundle = Bundle(path: path)! |
| 39 | @Sendable func text(_ key: String) -> String { bundle.localizedString(forKey: key, value: nil, table: nil) } |
| 40 | @Sendable func plural(_ key: String, _ count: Int) -> String { |
| 41 | String(format: text(key), locale: Locale(identifier: localization), arguments: [count]) |
| 42 | } |
| 43 | return NativeSyncStrings(saving: text("sync.saving"), retrying: text("sync.retrying"), |
| 44 | offline: { plural("sync.offline", $0) }, needsSignIn: { plural("sync.needsSignIn", $0) }, signIn: text("sync.signIn"), |
| 45 | failed: { reason in |
| 46 | switch reason { |
| 47 | case .cannotSaveOnDevice: return text("sync.failed.cannotSave") |
| 48 | case .tooManyUnsent: return text("sync.failed.tooManyUnsent") |
| 49 | case .held: return text("sync.failed.held") |
| 50 | } |
| 51 | }, offlineWaiting: text("offline.waiting"), offlineDurable: text("offline.durable"), unsentLost: text("sync.unsentLost")) |
| 52 | } |
| 53 | public func text(for status: SyncStatus) -> String? { |
| 54 | switch status { |
| 55 | case .saved: return nil |
| 56 | case .saving: return saving |
| 57 | case .retrying: return retrying |
| 58 | case .offline(let count): return offline(count) |
| 59 | case .needsSignIn(let count): return needsSignIn(count) |
| 60 | case .failed(_, let reason): return failed(reason) |
| 61 | } |
| 62 | } |
| 63 | } |
| 64 | |