damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

commit 9e9077bab518d73a3cdf9df6c32bb6aca834fa55
parent 7242c0bca32fad0282820854203d8446945a6333
Author: Terry Yiu <963907+tyiu@users.noreply.github.com>
Date:   Sat, 31 Dec 2022 16:27:17 -0500

Internationalize all bundled user-facing strings

Enables localization to non-English locales in the future

Diffstat:
Mdamus.xcodeproj/project.pbxproj | 15+++++++++++++++
Mdamus/Models/Wallet.swift | 24++++++++++++------------
Mdamus/Views/CreateAccountView.swift | 16++++++++--------
Mdamus/Views/EditMetadataView.swift | 6+++---
Mdamus/Views/EventView.swift | 18++++++------------
Mdamus/Views/PostView.swift | 2+-
Mdamus/Views/ProfileView.swift | 8++++----
Mdamus/Views/SaveKeysView.swift | 2+-
Mdamus/Views/SetupView.swift | 2+-
Adamus/en.lproj/Localizable.stringsdict | 42++++++++++++++++++++++++++++++++++++++++++
10 files changed, 93 insertions(+), 42 deletions(-)

diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 3169CAE6294E69C000EE4006 /* EmptyTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3169CAE5294E69C000EE4006 /* EmptyTimelineView.swift */; }; 3169CAED294FCCFC00EE4006 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3169CAEC294FCCFC00EE4006 /* Constants.swift */; }; 31D2E847295218AF006D67F8 /* Shimmer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31D2E846295218AF006D67F8 /* Shimmer.swift */; }; + 3A4325A82961E11400BFCD9D /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 3A4325AA2961E11400BFCD9D /* Localizable.stringsdict */; }; 3ACBCB78295FE5C70037388A /* TimeAgoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ACBCB77295FE5C70037388A /* TimeAgoTests.swift */; }; 4C06670128FC7C5900038D2A /* RelayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C06670028FC7C5900038D2A /* RelayView.swift */; }; 4C06670428FC7EC500038D2A /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 4C06670328FC7EC500038D2A /* Kingfisher */; }; @@ -164,6 +165,7 @@ 3169CAE5294E69C000EE4006 /* EmptyTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyTimelineView.swift; sourceTree = "<group>"; }; 3169CAEC294FCCFC00EE4006 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Constants.swift; path = damus/Util/Constants.swift; sourceTree = SOURCE_ROOT; }; 31D2E846295218AF006D67F8 /* Shimmer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shimmer.swift; sourceTree = "<group>"; }; + 3A4325A92961E11400BFCD9D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Localizable.stringsdict; sourceTree = "<group>"; }; 3ACBCB77295FE5C70037388A /* TimeAgoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeAgoTests.swift; sourceTree = "<group>"; }; 4C06670028FC7C5900038D2A /* RelayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayView.swift; sourceTree = "<group>"; }; 4C06670528FCB08600038D2A /* ImageCarousel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCarousel.swift; sourceTree = "<group>"; }; @@ -593,6 +595,7 @@ 4CE6DEE827F7A08100C66700 /* ContentView.swift */, 4CE6DEEA27F7A08200C66700 /* Assets.xcassets */, 4CE6DEEC27F7A08200C66700 /* Preview Content */, + 3A4325AA2961E11400BFCD9D /* Localizable.stringsdict */, ); path = damus; sourceTree = "<group>"; @@ -755,6 +758,7 @@ files = ( 4CE6DEEE27F7A08200C66700 /* Preview Assets.xcassets in Resources */, 4CE6DEEB27F7A08200C66700 /* Assets.xcassets in Resources */, + 3A4325A82961E11400BFCD9D /* Localizable.stringsdict in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -938,6 +942,17 @@ }; /* End PBXTargetDependency section */ +/* Begin PBXVariantGroup section */ + 3A4325AA2961E11400BFCD9D /* Localizable.stringsdict */ = { + isa = PBXVariantGroup; + children = ( + 3A4325A92961E11400BFCD9D /* en */, + ); + name = Localizable.stringsdict; + sourceTree = "<group>"; + }; +/* End PBXVariantGroup section */ + /* Begin XCBuildConfiguration section */ 4CE6DF0527F7A08200C66700 /* Debug */ = { isa = XCBuildConfiguration; diff --git a/damus/Models/Wallet.swift b/damus/Models/Wallet.swift @@ -37,39 +37,39 @@ enum Wallet: String, CaseIterable, Identifiable { var model: Model { switch self { case .system_default_wallet: - return .init(index: -1, tag: "systemdefaultwallet", displayName: "Local default", + return .init(index: -1, tag: "systemdefaultwallet", displayName: NSLocalizedString("Local default", comment: "Dropdown option label for system default for Lightning wallet."), link: "lightning:", appStoreLink: "lightning:", image: "") case .strike: - return .init(index: 0, tag: "strike", displayName: "Strike", link: "strike:", + return .init(index: 0, tag: "strike", displayName: NSLocalizedString("Strike", comment: "Dropdown option label for Lightning wallet, Strike."), link: "strike:", appStoreLink: "https://apps.apple.com/us/app/strike-bitcoin-payments/id1488724463", image: "strike") case .cashapp: - return .init(index: 1, tag: "cashapp", displayName: "Cash App", link: "squarecash://", + return .init(index: 1, tag: "cashapp", displayName: NSLocalizedString("Cash App", comment: "Dropdown option label for Lightning wallet, Cash App."), link: "squarecash://", appStoreLink: "https://apps.apple.com/us/app/cash-app/id711923939", image: "cashapp") case .muun: - return .init(index: 2, tag: "muun", displayName: "Muun", link: "muun:", appStoreLink: "https://apps.apple.com/us/app/muun-wallet/id1482037683", image: "muun") + return .init(index: 2, tag: "muun", displayName: NSLocalizedString("Muun", comment: "Dropdown option label for Lightning wallet, Muun."), link: "muun:", appStoreLink: "https://apps.apple.com/us/app/muun-wallet/id1482037683", image: "muun") case .bluewallet: - return .init(index: 3, tag: "bluewallet", displayName: "Blue Wallet", link: "bluewallet:lightning:", + return .init(index: 3, tag: "bluewallet", displayName: NSLocalizedString("Blue Wallet", comment: "Dropdown option label for Lightning wallet, Blue Wallet."), link: "bluewallet:lightning:", appStoreLink: "https://apps.apple.com/us/app/bluewallet-bitcoin-wallet/id1376878040", image: "bluewallet") case .walletofsatoshi: - return .init(index: 4, tag: "walletofsatoshi", displayName: "Wallet Of Satoshi", link: "walletofsatoshi:lightning:", + return .init(index: 4, tag: "walletofsatoshi", displayName: NSLocalizedString("Wallet Of Satoshi", comment: "Dropdown option label for Lightning wallet, Wallet Of Satoshi."), link: "walletofsatoshi:lightning:", appStoreLink: "https://apps.apple.com/us/app/wallet-of-satoshi/id1438599608", image: "walletofsatoshi") case .zebedee: - return .init(index: 5, tag: "zebedee", displayName: "Zebedee", link: "zebedee:lightning:", + return .init(index: 5, tag: "zebedee", displayName: NSLocalizedString("Zebedee", comment: "Dropdown option label for Lightning wallet, Zebedee."), link: "zebedee:lightning:", appStoreLink: "https://apps.apple.com/us/app/zebedee-wallet/id1484394401", image: "zebedee") case .zeusln: - return .init(index: 6, tag: "zeusln", displayName: "Zeus LN", link: "zeusln:lightning:", + return .init(index: 6, tag: "zeusln", displayName: NSLocalizedString("Zeus LN", comment: "Dropdown option label for Lightning wallet, Zeus LN."), link: "zeusln:lightning:", appStoreLink: "https://apps.apple.com/us/app/zeus-ln/id1456038895", image: "zeusln") case .lnlink: - return .init(index: 7, tag: "lnlink", displayName: "LNLink", link: "lnlink:lightning:", + return .init(index: 7, tag: "lnlink", displayName: NSLocalizedString("LNLink", comment: "Dropdown option label for Lightning wallet, LNLink."), link: "lnlink:lightning:", appStoreLink: "https://testflight.apple.com/join/aNY4yuuZ", image: "lnlink") case .phoenix: - return .init(index: 8, tag: "phoenix", displayName: "Phoenix", link: "phoenix://", + return .init(index: 8, tag: "phoenix", displayName: NSLocalizedString("Phoenix", comment: "Dropdown option label for Lightning wallet, Phoenix."), link: "phoenix://", appStoreLink: "https://apps.apple.com/us/app/phoenix-wallet/id1544097028", image: "phoenix") case .breez: - return .init(index: 9, tag: "breez", displayName: "Breez", link: "breez:", + return .init(index: 9, tag: "breez", displayName: NSLocalizedString("Breez", comment: "Dropdown option label for Lightning wallet, Breez."), link: "breez:", appStoreLink: "https://apps.apple.com/us/app/breez-lightning-client-pos/id1463604142", image: "breez") case .bitcoinbeach: - return .init(index: 10, tag: "bitcoinbeach", displayName: "Bitcoin Beach", link: "bitcoinbeach://", + return .init(index: 10, tag: "bitcoinbeach", displayName: NSLocalizedString("Bitcoin Beach", comment: "Dropdown option label for Lightning wallet, Bitcoin Beach."), link: "bitcoinbeach://", appStoreLink: "https://apps.apple.com/sv/app/bitcoin-beach-wallet/id1531383905", image: "bbw") } } diff --git a/damus/Views/CreateAccountView.swift b/damus/Views/CreateAccountView.swift @@ -40,26 +40,26 @@ struct CreateAccountView: View { } VStack { SignupForm { - FormLabel("Username") + FormLabel(NSLocalizedString("Username", comment: "Label to prompt username entry.")) HStack(spacing: 0.0) { Text("@") .foregroundColor(.white) .padding(.leading, -25.0) - FormTextInput("satoshi", text: $account.nick_name) + FormTextInput(NSLocalizedString("satoshi", comment: "Example username of Bitcoin creator(s), Satoshi Nakamoto."), text: $account.nick_name) .autocorrectionDisabled(true) .textInputAutocapitalization(.never) } - FormLabel("Display Name", optional: true) - FormTextInput("Satoshi Nakamoto", text: $account.real_name) + FormLabel(NSLocalizedString("Display Name", comment: "Label to prompt display name entry."), optional: true) + FormTextInput(NSLocalizedString("Satoshi Nakamoto", comment: "Name of Bitcoin creator(s)."), text: $account.real_name) .textInputAutocapitalization(.words) - FormLabel("About", optional: true) - FormTextInput("Creator(s) of Bitcoin. Absolute legend.", text: $account.about) + FormLabel(NSLocalizedString("About", comment: "Label to prompt for about text entry for user to describe about themself."), optional: true) + FormTextInput(NSLocalizedString("Creator(s) of Bitcoin. Absolute legend.", comment: "Example description about Bitcoin creator(s), Satoshi Nakamoto."), text: $account.about) - FormLabel("Account ID") + FormLabel(NSLocalizedString("Account ID", comment: "Label to indicate the public ID of the account.")) .onTapGesture { regen_key() } @@ -75,7 +75,7 @@ struct CreateAccountView: View { NavigationLink(destination: SaveKeysView(account: account), isActive: $is_done) { EmptyView() } - DamusWhiteButton("Create") { + DamusWhiteButton(NSLocalizedString("Create", comment: "Button to create account.")) { self.is_done = true } .padding() diff --git a/damus/Views/EditMetadataView.swift b/damus/Views/EditMetadataView.swift @@ -142,7 +142,7 @@ struct EditMetadataView: View { } Section("About Me") { - let placeholder = "Absolute Boss" + let placeholder = NSLocalizedString("Absolute Boss", comment: "Placeholder text for About Me description.") ZStack(alignment: .topLeading) { TextEditor(text: $about) .textInputAutocapitalization(.sentences) @@ -169,9 +169,9 @@ struct EditMetadataView: View { Text("NIP-05 Verification") }, footer: { if let parts = nip05_parts { - Text("'\(parts.username)' at '\(parts.host)' will be used for verification") + Text(String.localizedStringWithFormat("'%@' at '%@' will be used for verification", parts.username, parts.host)) } else { - Text("'\(nip05)' is an invalid nip05 identifier. It should look like an email.") + Text(String.localizedStringWithFormat("'%@' is an invalid nip05 identifier. It should look like an email.", nip05)) } }) diff --git a/damus/Views/EventView.swift b/damus/Views/EventView.swift @@ -401,7 +401,7 @@ func reply_desc(profiles: Profiles, event: NostrEvent) -> String { let n = desc.others if desc.pubkeys.count == 0 { - return "Reply to self" + return NSLocalizedString("Reply to self", comment: "Label to indicate that the user is replying to themself.") } let names: [String] = pubkeys.map { @@ -411,20 +411,14 @@ func reply_desc(profiles: Profiles, event: NostrEvent) -> String { if names.count == 2 { if n > 2 { - let and_other = reply_others_desc(n: n, n_pubkeys: pubkeys.count) - return "Replying to \(names[0]), \(names[1])\(and_other)" + let othersCount = n - pubkeys.count + return String(format: NSLocalizedString("replying_to_two_and_others", comment: "Label to indicate that the user is replying to 2 users and others."), othersCount, names[0], names[1]) } - return "Replying to \(names[0]) & \(names[1])" + return String.localizedStringWithFormat("Replying to %@ & %@", names[0], names[1]) } - let and_other = reply_others_desc(n: n, n_pubkeys: pubkeys.count) - return "Replying to \(names[0])\(and_other)" -} - -func reply_others_desc(n: Int, n_pubkeys: Int) -> String { - let other = n - n_pubkeys - let plural = other == 1 ? "" : "s" - return n > 1 ? " & \(other) other\(plural)" : "" + let othersCount = n - pubkeys.count + return String(format: NSLocalizedString("replying_to_one_and_others", comment: "Label to indicate that the user is replying to 1 user and others."), othersCount, names[0]) } diff --git a/damus/Views/PostView.swift b/damus/Views/PostView.swift @@ -12,7 +12,7 @@ enum NostrPostResult { case cancel } -let POST_PLACEHOLDER = "Type your post here..." +let POST_PLACEHOLDER = NSLocalizedString("Type your post here...", comment: "Text box prompt to ask user to type their post.") struct PostView: View { @State var post: String = "" diff --git a/damus/Views/ProfileView.swift b/damus/Views/ProfileView.swift @@ -22,13 +22,13 @@ enum FollowState { func follow_btn_txt(_ fs: FollowState) -> String { switch fs { case .follows: - return "Unfollow" + return NSLocalizedString("Unfollow", comment: "Button to unfollow a user.") case .following: - return "Following..." + return NSLocalizedString("Following...", comment: "Label to indicate that the user is in the process of following another user.") case .unfollowing: - return "Unfollowing..." + return NSLocalizedString("Unfollowing...", comment: "Label to indicate that the user is in the process of unfollowing another user.") case .unfollows: - return "Follow" + return NSLocalizedString("Follow", comment: "Button to follow a user.") } } diff --git a/damus/Views/SaveKeysView.swift b/damus/Views/SaveKeysView.swift @@ -67,7 +67,7 @@ struct SaveKeysView: View { complete_account_creation(account) } } else { - DamusWhiteButton("Let's go!") { + DamusWhiteButton(NSLocalizedString("Let's go!", comment: "Button to complete account creation and start using the app.")) { complete_account_creation(account) } } diff --git a/damus/Views/SetupView.swift b/damus/Views/SetupView.swift @@ -60,7 +60,7 @@ struct SetupView: View { CarouselView() - DamusWhiteButton("Create Account") { + DamusWhiteButton(NSLocalizedString("Create Account", comment: "Button to create an account.")) { self.state = .create_account } diff --git a/damus/en.lproj/Localizable.stringsdict b/damus/en.lproj/Localizable.stringsdict @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>replying_to_one_and_others</key> + <dict> + <key>NSStringLocalizedFormatKey</key> + <string>Replying to %2$@%#@others@</string> + <key>others</key> + <dict> + <key>NSStringFormatSpecTypeKey</key> + <string>NSStringPluralRuleType</string> + <key>NSStringFormatValueTypeKey</key> + <string>d</string> + <key>zero</key> + <string></string> + <key>one</key> + <string> &amp; 1 other</string> + <key>other</key> + <string> &amp; %d others</string> + </dict> + </dict> + <key>replying_to_two_and_others</key> + <dict> + <key>NSStringLocalizedFormatKey</key> + <string>Replying to %2$@, %3$@%#@others@</string> + <key>others</key> + <dict> + <key>NSStringFormatSpecTypeKey</key> + <string>NSStringPluralRuleType</string> + <key>NSStringFormatValueTypeKey</key> + <string>d</string> + <key>zero</key> + <string></string> + <key>one</key> + <string> &amp; 1 other</string> + <key>other</key> + <string> &amp; %d others</string> + </dict> + </dict> +</dict> +</plist>