From 6b91506e97762b0d9a2d5c8a03f70baa0929040f Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Fri, 26 Jun 2026 13:05:59 -0600 Subject: [PATCH 1/2] Localization: document the non-translatable SwiftUI text rule MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `xcstringstool` tags each extracted string with its compile gate (`visibility`); `sync` admits only unconditional strings into the catalog, so `#Preview` / `PreviewProvider` / `#if DEBUG` literals are excluded automatically — no `verbatim:` needed. Document this, and that `Text(verbatim:)` is for non-translatable literals in shipping code (glyphs, brands, pure interpolations). --- docs/localization.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/localization.md b/docs/localization.md index 5024e1755202..c34705a36c99 100644 --- a/docs/localization.md +++ b/docs/localization.md @@ -83,6 +83,24 @@ Use `SharedStrings` (@WordPress/Classes/Utility/SharedStrings.swift) for common let localizedCount = NumberFormatter.localizedString(from: NSNumber(value: count), number: .none) ``` +## Non-localizable text + +A SwiftUI `Text("literal")` uses `LocalizedStringKey`, so in **shipping code** the literal is **automatically extracted for translation**. For strings that must *not* be translated — glyphs and symbols (`Text("·")`), brand names (`Text("WordPress.com")`), and pure interpolations (`Text("@\(username)")`) — use `Text(verbatim:)` so the string is excluded: + +```swift +Text(verbatim: "·") // divider glyph — not translatable +Text(verbatim: "WordPress.com") // brand name — not translatable +``` + +### Previews and `#if DEBUG` are excluded automatically — don't wrap them + +You do **not** need `Text(verbatim:)` for placeholder strings in `#Preview`, `PreviewProvider`, or `#if DEBUG` blocks, and you don't need to flag them in review. The extraction tooling excludes them on its own: + +- `xcstringstool extract` tags every extracted string with the compile gate it sits under — its `visibility`. Shipping code carries none (`visibility: nil`); a preview literal comes out as `visibility: "preview"`, and a `#if DEBUG` literal as `visibility: defined(DEBUG)`. +- `xcstringstool sync` — the step that builds `Localizable.xcstrings` — admits **only** unconditional strings (`visibility: nil`). Anything carrying a `visibility` (preview, `#if DEBUG`, or a combination of the two) is dropped before it can reach the catalog or GlotPress. + +So a plain `Text("Sample Card")` inside a `#Preview` is fine as-is — it is not a localization leak, and wrapping it in `verbatim:` only adds noise. If you spot a translatable literal in a preview during review, you can leave it: `sync` already excludes it. + ## Organization Pattern Organize strings using private enums within each view or view model: From 1567c8939dcf5c31fe2d8ec40f34e61be4881b4f Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Fri, 26 Jun 2026 12:59:36 -0600 Subject: [PATCH 2/2] Localization: wrap non-translatable SwiftUI Text literals (prep for swiftui: true) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Once SwiftUI string extraction is enabled (`swiftui: true` on generate_strings_catalog), `xcstringstool extract --SwiftUI-Text` pulls every shipping `Text("literal")` into the String Catalog for translation — including non-translatable literals that would reach GlotPress as garbage. Wrap those in `Text(verbatim:)`, and fix a few numeric cases to format per the user's locale. No behavior change. - Glyphs/separators, brand names, pure string interpolations, HTTP status codes, and developer/debug-screen text -> `Text(verbatim:)`. - Bare numeric interpolations -> locale formatters: `Text(count, format: .number)`, `Text(Double(progress) / 100, format: .percent)`, and `.formatted()` inside a verbatim wrapper for decorated counts. A bare `Text("\(count)")` ships unlocalized digits (no grouping, no locale numerals). Left translatable on purpose: real UI strings, the `^[%@](inflect:)` string (LocalizedStringKey inflection), and `example.com` (site-address placeholder). Out of scope (separate changes): the unwired Realtime/Insights/Subscribers strings, the `Overriden`->`Overridden` codebase-wide misspelling, the two `"%@ characters"` strings (want proper plurals), and a `TODO:` placeholder in ActivityLogDetailView's share sheet. --- Modules/Sources/JetpackStats/Cards/ChartCard.swift | 2 +- .../Sources/JetpackStats/Charts/PieChartView.swift | 2 +- .../Sources/JetpackStats/Screens/PostStatsView.swift | 2 +- .../JetpackStats/Views/StatsCardTitleView.swift | 4 ++-- .../JetpackStats/Views/TimezoneInfoView.swift | 2 +- .../Views/TopList/Rows/TopListVideoRowView.swift | 2 +- .../JetpackStats/Views/TopList/TopListItemView.swift | 2 +- .../Views/TopList/TopListMetricsView.swift | 2 +- .../UI/Bot Conversations/ConversationBotIntro.swift | 2 +- .../UI/Bot Conversations/ConversationView.swift | 2 +- .../ApplicationTokenListItemView.swift | 2 +- .../Users/Views/DeleteUserConfirmationSheet.swift | 2 +- .../Activity/Details/ActivityLogDetailsView.swift | 2 +- .../Activity/List/ActivityLogRowView.swift | 4 ++-- .../ViewRelated/Activity/List/ActivityLogsMenu.swift | 2 +- .../Activity/List/ActivityTypeSelectionView.swift | 2 +- .../Activity/List/DownloadableBackupSection.swift | 2 +- .../Blog/Site Monitoring/WebServerLogsView.swift | 4 ++-- .../Subscribers/Details/SubscriberDetailsView.swift | 2 +- .../Blog/Subscribers/List/SubscribersMenu.swift | 2 +- .../Developer/StoreSandboxSecretScreen.swift | 2 +- .../Developer/WeeklyRoundupDebugScreen.swift | 12 ++++++------ .../Me/App Settings/DebugFeatureFlagsView.swift | 10 +++++----- .../ContentViews/Components/Text+BoldSubString.swift | 2 +- .../PostSettings/PostDiscussionSettingsView.swift | 2 +- .../Post/PostSettings/PostSettingsView.swift | 2 +- .../Post/PostSettings/PostSlugEditorView.swift | 2 +- .../Views/PostSettingsTruncatedArrayTextView.swift | 2 +- .../Post/PostSettings/Views/PostStatusView.swift | 2 +- .../ViewRelated/Post/Views/PostAuthorPicker.swift | 2 +- .../ViewRelated/Post/Views/ResolveConflictView.swift | 2 +- .../Reader/Detail/Views/ReaderDetailHeaderView.swift | 4 ++-- .../Reader/Headers/ReaderSiteHeaderView.swift | 4 ++-- .../Theme/ReaderDisplaySettingViewController.swift | 6 +++--- .../Reader/User/ReaderUserProfileView.swift | 2 +- .../System/Sidebar/SidebarProfileView.swift | 2 +- .../System/Sidebar/SidebarViewController.swift | 2 +- .../Classes/ViewRelated/Tags/SiteTagsView.swift | 2 +- 38 files changed, 54 insertions(+), 54 deletions(-) diff --git a/Modules/Sources/JetpackStats/Cards/ChartCard.swift b/Modules/Sources/JetpackStats/Cards/ChartCard.swift index e5aa1d6daaee..c06c600595d4 100644 --- a/Modules/Sources/JetpackStats/Cards/ChartCard.swift +++ b/Modules/Sources/JetpackStats/Cards/ChartCard.swift @@ -348,7 +348,7 @@ struct ChartCardHeaderView: View { } } if viewModel.showComparison { - Text("\(viewModel.trend.formattedChange) \(viewModel.trend.iconSign) \(viewModel.trend.formattedPercentage)") + Text(verbatim: "\(viewModel.trend.formattedChange) \(viewModel.trend.iconSign) \(viewModel.trend.formattedPercentage)") .font(.caption.weight(.semibold)) .foregroundColor(viewModel.trend.sentiment.foregroundColor) .contentTransition(.numericText()) diff --git a/Modules/Sources/JetpackStats/Charts/PieChartView.swift b/Modules/Sources/JetpackStats/Charts/PieChartView.swift index e467052dee50..8ae676582c55 100644 --- a/Modules/Sources/JetpackStats/Charts/PieChartView.swift +++ b/Modules/Sources/JetpackStats/Charts/PieChartView.swift @@ -22,7 +22,7 @@ struct PieChartView: View { .cornerRadius(5) .annotation(position: .overlay) { if shouldShowAnnotation(for: segment) { - Text("\(segment.name.capitalized) \((segment.percentage / 100).formatted(.percent.precision(.fractionLength(1))))") + Text(verbatim: "\(segment.name.capitalized) \((segment.percentage / 100).formatted(.percent.precision(.fractionLength(1))))") .font(.caption2) .fontWeight(.medium) .foregroundStyle(.white) diff --git a/Modules/Sources/JetpackStats/Screens/PostStatsView.swift b/Modules/Sources/JetpackStats/Screens/PostStatsView.swift index 9b32ad8f65f3..6bb77bf2cc3e 100644 --- a/Modules/Sources/JetpackStats/Screens/PostStatsView.swift +++ b/Modules/Sources/JetpackStats/Screens/PostStatsView.swift @@ -578,7 +578,7 @@ private struct PostLikesStripView: View { // Show additional count if there are more users if likes.totalCount > maxVisibleAvatars { - Text("+\((likes.totalCount - maxVisibleAvatars).formatted(.number.notation(.compactName)))") + Text(verbatim: "+\((likes.totalCount - maxVisibleAvatars).formatted(.number.notation(.compactName)))") .font(.caption2.weight(.medium)) .foregroundColor(.primary.opacity(0.8)) .padding(.horizontal, 4) diff --git a/Modules/Sources/JetpackStats/Views/StatsCardTitleView.swift b/Modules/Sources/JetpackStats/Views/StatsCardTitleView.swift index f76becd420a2..3870a1227ab6 100644 --- a/Modules/Sources/JetpackStats/Views/StatsCardTitleView.swift +++ b/Modules/Sources/JetpackStats/Views/StatsCardTitleView.swift @@ -26,7 +26,7 @@ struct StatsCardTitleView: View { if showChevron { // Note: had to do that to fix the animation issuse with Menu // hiding the image. - title + Text(" ") + Text(Image(systemName: "chevron.up.chevron.down")) + title + Text(verbatim: " ") + Text(Image(systemName: "chevron.up.chevron.down")) .font(.caption2.weight(.semibold)) .foregroundColor(.secondary) .baselineOffset(1) @@ -59,7 +59,7 @@ struct InlineValuePickerTitle: View { // Note: had to do that to fix the animation issuse with Menu // hiding the image. - title + Text(" ") + Text(Image(systemName: "chevron.up.chevron.down")) + title + Text(verbatim: " ") + Text(Image(systemName: "chevron.up.chevron.down")) .font(.caption2.weight(.semibold)) .foregroundColor(.secondary) .baselineOffset(1) diff --git a/Modules/Sources/JetpackStats/Views/TimezoneInfoView.swift b/Modules/Sources/JetpackStats/Views/TimezoneInfoView.swift index 4b7d5687be8e..a5af4e036711 100644 --- a/Modules/Sources/JetpackStats/Views/TimezoneInfoView.swift +++ b/Modules/Sources/JetpackStats/Views/TimezoneInfoView.swift @@ -38,7 +38,7 @@ struct TimezoneInfoView: View { .font(.headline) .foregroundStyle(.primary) - Text("\(formattedTimeZone) (\(context.formatters.date.formattedTimeOffset))") + Text(verbatim: "\(formattedTimeZone) (\(context.formatters.date.formattedTimeOffset))") .font(.footnote) .foregroundColor(.secondary) diff --git a/Modules/Sources/JetpackStats/Views/TopList/Rows/TopListVideoRowView.swift b/Modules/Sources/JetpackStats/Views/TopList/Rows/TopListVideoRowView.swift index adc3dd041e04..0e68db48ca7b 100644 --- a/Modules/Sources/JetpackStats/Views/TopList/Rows/TopListVideoRowView.swift +++ b/Modules/Sources/JetpackStats/Views/TopList/Rows/TopListVideoRowView.swift @@ -5,7 +5,7 @@ struct TopListVideoRowView: View { var body: some View { VStack(alignment: .leading, spacing: 2) { - (Text(Image(systemName: "play.circle")).font(.footnote) + Text(" ") + Text(item.title)) + (Text(Image(systemName: "play.circle")).font(.footnote) + Text(verbatim: " ") + Text(item.title)) .font(.callout) .foregroundColor(.primary) .lineLimit(1) diff --git a/Modules/Sources/JetpackStats/Views/TopList/TopListItemView.swift b/Modules/Sources/JetpackStats/Views/TopList/TopListItemView.swift index 40db5a665275..3b0fc73920fd 100644 --- a/Modules/Sources/JetpackStats/Views/TopList/TopListItemView.swift +++ b/Modules/Sources/JetpackStats/Views/TopList/TopListItemView.swift @@ -51,7 +51,7 @@ struct TopListItemView: View { var content: some View { HStack(alignment: .center, spacing: 0) { if let index { - Text("\(index + 1)") + Text(index + 1, format: .number) .font(.system(.subheadline, design: .rounded, weight: .medium)) .frame(width: 22, alignment: .center) .lineLimit(1) diff --git a/Modules/Sources/JetpackStats/Views/TopList/TopListMetricsView.swift b/Modules/Sources/JetpackStats/Views/TopList/TopListMetricsView.swift index 8e00cd62eda1..241887bf10d4 100644 --- a/Modules/Sources/JetpackStats/Views/TopList/TopListMetricsView.swift +++ b/Modules/Sources/JetpackStats/Views/TopList/TopListMetricsView.swift @@ -20,7 +20,7 @@ struct TopListMetricsView: View { } if let trend { HStack(spacing: 3) { - Text("\(trend.iconSign) \(trend.formattedPercentage)") + Text(verbatim: "\(trend.iconSign) \(trend.formattedPercentage)") .font(.system(.caption2, design: .rounded, weight: .semibold)).tracking(-0.25) .foregroundColor(trend.sentiment.foregroundColor) .contentTransition(.numericText()) diff --git a/Modules/Sources/Support/UI/Bot Conversations/ConversationBotIntro.swift b/Modules/Sources/Support/UI/Bot Conversations/ConversationBotIntro.swift index b0430d4e5672..8f8c531d348e 100644 --- a/Modules/Sources/Support/UI/Bot Conversations/ConversationBotIntro.swift +++ b/Modules/Sources/Support/UI/Bot Conversations/ConversationBotIntro.swift @@ -17,7 +17,7 @@ struct ConversationBotIntro: View { .font(.title2) .fontWeight(.semibold) - Text("👋") + Text(verbatim: "👋") .font(.title2) } diff --git a/Modules/Sources/Support/UI/Bot Conversations/ConversationView.swift b/Modules/Sources/Support/UI/Bot Conversations/ConversationView.swift index 8caf3365d2cf..145b55672d77 100644 --- a/Modules/Sources/Support/UI/Bot Conversations/ConversationView.swift +++ b/Modules/Sources/Support/UI/Bot Conversations/ConversationView.swift @@ -284,7 +284,7 @@ public struct ConversationView: View { switchToHumanSupport - Text("").padding(.bottom, 4) + Text(verbatim: "").padding(.bottom, 4) .listRowInsets(.zero) .listRowBackground(Color.clear) .listRowSpacing(0) diff --git a/WordPress/Classes/ApplicationToken/ApplicationTokenListItemView.swift b/WordPress/Classes/ApplicationToken/ApplicationTokenListItemView.swift index a7607a5629e6..3804d85e797c 100644 --- a/WordPress/Classes/ApplicationToken/ApplicationTokenListItemView.swift +++ b/WordPress/Classes/ApplicationToken/ApplicationTokenListItemView.swift @@ -20,7 +20,7 @@ public struct ApplicationTokenListItemView: View { .multilineTextAlignment(.leading) .lineLimit(2) .truncationMode(.middle) - Text("\(createdDateText) • \(lastUsedText)") + Text(verbatim: "\(createdDateText) • \(lastUsedText)") .font(.footnote) .foregroundStyle(.secondary) .lineLimit(1) diff --git a/WordPress/Classes/Users/Views/DeleteUserConfirmationSheet.swift b/WordPress/Classes/Users/Views/DeleteUserConfirmationSheet.swift index 15517fd67dc2..0958b4f1cadf 100644 --- a/WordPress/Classes/Users/Views/DeleteUserConfirmationSheet.swift +++ b/WordPress/Classes/Users/Views/DeleteUserConfirmationSheet.swift @@ -19,7 +19,7 @@ struct DeleteUserConfirmationSheet: View { Section { Picker(Strings.attributeContentToUserLabel, selection: $deleteUserViewModel.selectedUser) { ForEach(deleteUserViewModel.otherUsers) { user in - Text("\(user.displayName) (\(user.username))").tag(user) + Text(verbatim: "\(user.displayName) (\(user.username))").tag(user) } } } header: { diff --git a/WordPress/Classes/ViewRelated/Activity/Details/ActivityLogDetailsView.swift b/WordPress/Classes/ViewRelated/Activity/Details/ActivityLogDetailsView.swift index 69358b829281..fc08cb8705e1 100644 --- a/WordPress/Classes/ViewRelated/Activity/Details/ActivityLogDetailsView.swift +++ b/WordPress/Classes/ViewRelated/Activity/Details/ActivityLogDetailsView.swift @@ -145,7 +145,7 @@ private struct ActivityHeaderView: View { ) .fixedSize(horizontal: false, vertical: true) } else { - Text("—") + Text(verbatim: "—") .font(.body) .foregroundColor(.secondary) } diff --git a/WordPress/Classes/ViewRelated/Activity/List/ActivityLogRowView.swift b/WordPress/Classes/ViewRelated/Activity/List/ActivityLogRowView.swift index d4eb66fdb027..5776682eba76 100644 --- a/WordPress/Classes/ViewRelated/Activity/List/ActivityLogRowView.swift +++ b/WordPress/Classes/ViewRelated/Activity/List/ActivityLogRowView.swift @@ -46,7 +46,7 @@ struct ActivityLogRowView: View { .font(.footnote) .foregroundColor(.secondary) if let subtitle = viewModel.actorSubtitle { - Text("·") + Text(verbatim: "·") .font(.footnote) .foregroundColor(.secondary) Text(subtitle) @@ -55,7 +55,7 @@ struct ActivityLogRowView: View { } if let metadata = viewModel.actorMetadata { - Text("·") + Text(verbatim: "·") .font(.footnote) .foregroundColor(.secondary) Text(metadata) diff --git a/WordPress/Classes/ViewRelated/Activity/List/ActivityLogsMenu.swift b/WordPress/Classes/ViewRelated/Activity/List/ActivityLogsMenu.swift index d9b5203edf31..2f1a286fe34d 100644 --- a/WordPress/Classes/ViewRelated/Activity/List/ActivityLogsMenu.swift +++ b/WordPress/Classes/ViewRelated/Activity/List/ActivityLogsMenu.swift @@ -83,7 +83,7 @@ struct ActivityLogsFiltersMenu: View { } label: { Text(Strings.activityTypes) if !viewModel.parameters.activityTypes.isEmpty { - Text("\(viewModel.parameters.activityTypes.count)") + Text(viewModel.parameters.activityTypes.count, format: .number) } Image(systemName: "list.bullet") } diff --git a/WordPress/Classes/ViewRelated/Activity/List/ActivityTypeSelectionView.swift b/WordPress/Classes/ViewRelated/Activity/List/ActivityTypeSelectionView.swift index fa0a787a601c..6b1389af6381 100644 --- a/WordPress/Classes/ViewRelated/Activity/List/ActivityTypeSelectionView.swift +++ b/WordPress/Classes/ViewRelated/Activity/List/ActivityTypeSelectionView.swift @@ -151,7 +151,7 @@ private struct ActivityTypeRow: View { HStack { Text(group.name) Spacer() - Text("\(group.count)") + Text(group.count, format: .number) .foregroundColor(.secondary) } } diff --git a/WordPress/Classes/ViewRelated/Activity/List/DownloadableBackupSection.swift b/WordPress/Classes/ViewRelated/Activity/List/DownloadableBackupSection.swift index 910ad0580c57..611190d4ed77 100644 --- a/WordPress/Classes/ViewRelated/Activity/List/DownloadableBackupSection.swift +++ b/WordPress/Classes/ViewRelated/Activity/List/DownloadableBackupSection.swift @@ -88,7 +88,7 @@ private struct BackupInProgressView: View { .progressViewStyle(.linear) .tint(.accentColor) - Text("\(progress)%") + Text(Double(progress) / 100, format: .percent) .font(.caption) .foregroundStyle(.secondary) .monospacedDigit() diff --git a/WordPress/Classes/ViewRelated/Blog/Site Monitoring/WebServerLogsView.swift b/WordPress/Classes/ViewRelated/Blog/Site Monitoring/WebServerLogsView.swift index ac3dc43f7284..40cdfd2c4341 100644 --- a/WordPress/Classes/ViewRelated/Blog/Site Monitoring/WebServerLogsView.swift +++ b/WordPress/Classes/ViewRelated/Blog/Site Monitoring/WebServerLogsView.swift @@ -90,11 +90,11 @@ struct WebServerLogsView: View { Picker("", selection: $searchCriteria.status) { let cases = [200, 301, 302, 400, 401, 403, 404, 500] ForEach(cases, id: \.self) { - Text("\($0)").tag(Optional.some($0)) + Text(verbatim: "\($0)").tag(Optional.some($0)) } }.pickerStyle(.inline) } label: { - Text("\($0)") + Text(verbatim: "\($0)") } .contentPresentationStyle(.menu) } diff --git a/WordPress/Classes/ViewRelated/Blog/Subscribers/Details/SubscriberDetailsView.swift b/WordPress/Classes/ViewRelated/Blog/Subscribers/Details/SubscriberDetailsView.swift index 5a3e836e02c7..279e2057d87b 100644 --- a/WordPress/Classes/ViewRelated/Blog/Subscribers/Details/SubscriberDetailsView.swift +++ b/WordPress/Classes/ViewRelated/Blog/Subscribers/Details/SubscriberDetailsView.swift @@ -159,7 +159,7 @@ struct SubscriberDetailsView: View { Text(plan.title) .font(.headline) if plan.isGift { - Text("– \(Strings.gift)") + Text(verbatim: "– \(Strings.gift)") .foregroundStyle(.secondary) Spacer() Image(systemName: "gift") diff --git a/WordPress/Classes/ViewRelated/Blog/Subscribers/List/SubscribersMenu.swift b/WordPress/Classes/ViewRelated/Blog/Subscribers/List/SubscribersMenu.swift index a90fc931613b..c1c30e6da1fc 100644 --- a/WordPress/Classes/ViewRelated/Blog/Subscribers/List/SubscribersMenu.swift +++ b/WordPress/Classes/ViewRelated/Blog/Subscribers/List/SubscribersMenu.swift @@ -12,7 +12,7 @@ struct SubscribersFiltersMenu: View { filterByPaymenetType } if let response = viewModel.response, let count = viewModel.makeFormattedSubscribersCount(for: response) { - Text("\(Strings.subscribers) \(count)") + Text(verbatim: "\(Strings.subscribers) \(count)") } } label: { Image(systemName: "line.3.horizontal.decrease") diff --git a/WordPress/Classes/ViewRelated/Developer/StoreSandboxSecretScreen.swift b/WordPress/Classes/ViewRelated/Developer/StoreSandboxSecretScreen.swift index e428db14ff63..2c0062a07d92 100644 --- a/WordPress/Classes/ViewRelated/Developer/StoreSandboxSecretScreen.swift +++ b/WordPress/Classes/ViewRelated/Developer/StoreSandboxSecretScreen.swift @@ -10,7 +10,7 @@ struct StoreSandboxSecretScreen: View { var body: some View { NavigationView { VStack(alignment: .leading) { - Text("Enter the Store Sandbox Cookie Secret:") + Text(verbatim: "Enter the Store Sandbox Cookie Secret:") TextField("Secret", text: $secret) .padding(EdgeInsets(top: 16, leading: 16, bottom: 16, trailing: 16)) .border(Color.black) diff --git a/WordPress/Classes/ViewRelated/Developer/WeeklyRoundupDebugScreen.swift b/WordPress/Classes/ViewRelated/Developer/WeeklyRoundupDebugScreen.swift index 61208517ce69..13683052884c 100644 --- a/WordPress/Classes/ViewRelated/Developer/WeeklyRoundupDebugScreen.swift +++ b/WordPress/Classes/ViewRelated/Developer/WeeklyRoundupDebugScreen.swift @@ -41,7 +41,7 @@ struct WeeklyRoundupDebugScreen: View { VStack(alignment: .center) { if errorScheduling { Group { - Text("Error scheduling Weekly Roundup!") + Text(verbatim: "Error scheduling Weekly Roundup!") .foregroundColor(.red) } @@ -130,7 +130,7 @@ struct WeeklyRoundupDebugScreen: View { .frame(height: settings.spacerHeight) } - Text("The values represent when the dynamic notification is scheduled at the earliest. It can take a lot more time to be sent since iOS basically decides when to deliver it.") + Text(verbatim: "The values represent when the dynamic notification is scheduled at the earliest. It can take a lot more time to be sent since iOS basically decides when to deliver it.") .fixedSize(horizontal: false, vertical: true) .padding(settings.defaultPadding) @@ -153,18 +153,18 @@ struct WeeklyRoundupDebugScreen: View { func scheduleDetailsView() -> some View { guard !running else { - return AnyView(Text("Running...")) + return AnyView(Text(verbatim: "Running...")) } if let scheduledDate = self.scheduledDate { return AnyView(HStack { - Text("Earliest begin date:") + Text(verbatim: "Earliest begin date:") Spacer() - Text("\(scheduledDate.shortStringWithTime())") + Text(verbatim: "\(scheduledDate.shortStringWithTime())") }) } else { return AnyView(HStack { - Text("Not scheduled.") + Text(verbatim: "Not scheduled.") }) } } diff --git a/WordPress/Classes/ViewRelated/Me/App Settings/DebugFeatureFlagsView.swift b/WordPress/Classes/ViewRelated/Me/App Settings/DebugFeatureFlagsView.swift index 839bdaa87ea8..31d3219ed3ab 100644 --- a/WordPress/Classes/ViewRelated/Me/App Settings/DebugFeatureFlagsView.swift +++ b/WordPress/Classes/ViewRelated/Me/App Settings/DebugFeatureFlagsView.swift @@ -15,10 +15,10 @@ struct DebugFeatureFlagsView: View { .navigationBarTitleDisplayMode(.inline) .toolbarTitleMenu { Picker("Filter", selection: $viewModel.filter) { - Text("Feature Flags (All)").tag(DebugFeatureFlagFilter.all) - Text("Remote Feature Flags").tag(DebugFeatureFlagFilter.remote) - Text("Local Feature Flags").tag(DebugFeatureFlagFilter.local) - Text("Overriden Feature Flags").tag(DebugFeatureFlagFilter.overriden) + Text(verbatim: "Feature Flags (All)").tag(DebugFeatureFlagFilter.all) + Text(verbatim: "Remote Feature Flags").tag(DebugFeatureFlagFilter.remote) + Text(verbatim: "Local Feature Flags").tag(DebugFeatureFlagFilter.local) + Text(verbatim: "Overriden Feature Flags").tag(DebugFeatureFlagFilter.overriden) }.pickerStyle(.inline) } .toolbar { @@ -59,7 +59,7 @@ struct DebugFeatureFlagsView: View { VStack(alignment: .leading) { Text(title) if isOverriden { - Text("Overriden") + Text(verbatim: "Overriden") .font(.subheadline) .foregroundColor(.secondary) } diff --git a/WordPress/Classes/ViewRelated/NUX/Controllers/UnifiedPrologue/ContentViews/Components/Text+BoldSubString.swift b/WordPress/Classes/ViewRelated/NUX/Controllers/UnifiedPrologue/ContentViews/Components/Text+BoldSubString.swift index 754c843e309c..523641f441d8 100644 --- a/WordPress/Classes/ViewRelated/NUX/Controllers/UnifiedPrologue/ContentViews/Components/Text+BoldSubString.swift +++ b/WordPress/Classes/ViewRelated/NUX/Controllers/UnifiedPrologue/ContentViews/Components/Text+BoldSubString.swift @@ -16,7 +16,7 @@ extension Text { return } - var text = Text("") + var text = Text(verbatim: "") parts.enumerated().forEach { index, part in let partText = Text(part) diff --git a/WordPress/Classes/ViewRelated/Post/PostSettings/PostDiscussionSettingsView.swift b/WordPress/Classes/ViewRelated/Post/PostSettings/PostDiscussionSettingsView.swift index 2f0f6d6a40c5..327890e51b73 100644 --- a/WordPress/Classes/ViewRelated/Post/PostSettings/PostDiscussionSettingsView.swift +++ b/WordPress/Classes/ViewRelated/Post/PostSettings/PostDiscussionSettingsView.swift @@ -32,7 +32,7 @@ struct PostDiscussionSettingsView: View { .accessibilityIdentifier("post_discussion_allow_pings_toggle") } footer: { Link(destination: Strings.pingbacksLearnMoreURL) { - (Text(Strings.learnMorePingbacksText) + Text(" ") + Text(Image(systemName: "link"))) + (Text(Strings.learnMorePingbacksText) + Text(verbatim: " ") + Text(Image(systemName: "link"))) .font(.footnote) } .accessibilityIdentifier("post_discussion_pingbacks_learn_more_button") diff --git a/WordPress/Classes/ViewRelated/Post/PostSettings/PostSettingsView.swift b/WordPress/Classes/ViewRelated/Post/PostSettings/PostSettingsView.swift index 8babc5965f23..135dc01b0c97 100644 --- a/WordPress/Classes/ViewRelated/Post/PostSettings/PostSettingsView.swift +++ b/WordPress/Classes/ViewRelated/Post/PostSettings/PostSettingsView.swift @@ -529,7 +529,7 @@ private struct PostSettingsAuthorRow: View { .foregroundColor(.secondary) .textSelection(.enabled) } else { - Text("—") + Text(verbatim: "—") .foregroundColor(.secondary) } } diff --git a/WordPress/Classes/ViewRelated/Post/PostSettings/PostSlugEditorView.swift b/WordPress/Classes/ViewRelated/Post/PostSettings/PostSlugEditorView.swift index e7ca20157f93..1a0f6e741d48 100644 --- a/WordPress/Classes/ViewRelated/Post/PostSettings/PostSlugEditorView.swift +++ b/WordPress/Classes/ViewRelated/Post/PostSettings/PostSlugEditorView.swift @@ -75,7 +75,7 @@ struct PostSlugEditorView: View { .foregroundColor(.secondary) Link(destination: URL(string: "https://wordpress.com/support/permalinks-and-slugs/")!) { - (Text(Strings.learnMore) + Text(" ") + Text(Image(systemName: "arrow.up.right.square"))) + (Text(Strings.learnMore) + Text(verbatim: " ") + Text(Image(systemName: "arrow.up.right.square"))) .font(.subheadline) .foregroundColor(.accentColor) } diff --git a/WordPress/Classes/ViewRelated/Post/PostSettings/Views/PostSettingsTruncatedArrayTextView.swift b/WordPress/Classes/ViewRelated/Post/PostSettings/Views/PostSettingsTruncatedArrayTextView.swift index 2fc7cbdecb37..0bf0c230f9f9 100644 --- a/WordPress/Classes/ViewRelated/Post/PostSettings/Views/PostSettingsTruncatedArrayTextView.swift +++ b/WordPress/Classes/ViewRelated/Post/PostSettings/Views/PostSettingsTruncatedArrayTextView.swift @@ -31,7 +31,7 @@ private struct ItemView: View { .font(.subheadline) .foregroundColor(.secondary) if remainingCount > 0 { - Text("(+\(remainingCount))") + Text(verbatim: "(+\(remainingCount.formatted()))") .font(.system(.subheadline, design: .monospaced)) .foregroundColor(.secondary) .tracking(-0.5) diff --git a/WordPress/Classes/ViewRelated/Post/PostSettings/Views/PostStatusView.swift b/WordPress/Classes/ViewRelated/Post/PostSettings/Views/PostStatusView.swift index 28faa804a697..ebb347c9c17a 100644 --- a/WordPress/Classes/ViewRelated/Post/PostSettings/Views/PostStatusView.swift +++ b/WordPress/Classes/ViewRelated/Post/PostSettings/Views/PostStatusView.swift @@ -212,7 +212,7 @@ struct PostSettingsPasswordRow: View { HStack { Text(Strings.passwordLabel) Spacer() - Text("••••••••••••") + Text(verbatim: "••••••••••••") .font(.footnote.monospaced()) .foregroundStyle(.secondary) Image(systemName: "chevron.forward") diff --git a/WordPress/Classes/ViewRelated/Post/Views/PostAuthorPicker.swift b/WordPress/Classes/ViewRelated/Post/Views/PostAuthorPicker.swift index 905750659de4..9c20141965e1 100644 --- a/WordPress/Classes/ViewRelated/Post/Views/PostAuthorPicker.swift +++ b/WordPress/Classes/ViewRelated/Post/Views/PostAuthorPicker.swift @@ -64,7 +64,7 @@ private struct AuthorRow: View { .font(.callout.weight(.medium)) if let username = author.username { - Text("@\(username)") + Text(verbatim: "@\(username)") .font(.footnote) .foregroundColor(.secondary) } diff --git a/WordPress/Classes/ViewRelated/Post/Views/ResolveConflictView.swift b/WordPress/Classes/ViewRelated/Post/Views/ResolveConflictView.swift index e769249b2b3d..1d458474f6e0 100644 --- a/WordPress/Classes/ViewRelated/Post/Views/ResolveConflictView.swift +++ b/WordPress/Classes/ViewRelated/Post/Views/ResolveConflictView.swift @@ -27,7 +27,7 @@ struct ResolveConflictView: View { Section { VStack(alignment: .leading, spacing: 12) { let title = post.latest().titleForDisplay() - Text("\"\(title)\"") + Text(verbatim: "\"\(title)\"") .font(.headline) .lineLimit(2) Text(Strings.description) diff --git a/WordPress/Classes/ViewRelated/Reader/Detail/Views/ReaderDetailHeaderView.swift b/WordPress/Classes/ViewRelated/Reader/Detail/Views/ReaderDetailHeaderView.swift index c7709a3349de..61e7a26d848c 100644 --- a/WordPress/Classes/ViewRelated/Reader/Detail/Views/ReaderDetailHeaderView.swift +++ b/WordPress/Classes/ViewRelated/Reader/Detail/Views/ReaderDetailHeaderView.swift @@ -266,7 +266,7 @@ struct ReaderDetailHeaderView: View { .font(Font(viewModel.displaySetting.font(with: .footnote, weight: .semibold))) .foregroundStyle(Color(primaryTextColor)) if !viewModel.authorName.isEmpty { - Text(" • ") + Text(verbatim: " • ") .font(Font(viewModel.displaySetting.font(with: .footnote))) .foregroundColor(Color(viewModel.displaySetting.color.secondaryForeground)) .layoutPriority(1) @@ -282,7 +282,7 @@ struct ReaderDetailHeaderView: View { .font(Font(viewModel.displaySetting.font(with: .footnote))) .foregroundStyle(Color(primaryTextColor)) if !viewModel.isFollowingSite || !viewModel.isFollowButtonInteractive { - Text(" • ") + Text(verbatim: " • ") .font(Font(viewModel.displaySetting.font(with: .footnote))) .foregroundColor(Color(viewModel.displaySetting.color.secondaryForeground)) .layoutPriority(1) diff --git a/WordPress/Classes/ViewRelated/Reader/Headers/ReaderSiteHeaderView.swift b/WordPress/Classes/ViewRelated/Reader/Headers/ReaderSiteHeaderView.swift index 3c65daa77428..40004dec0af8 100644 --- a/WordPress/Classes/ViewRelated/Reader/Headers/ReaderSiteHeaderView.swift +++ b/WordPress/Classes/ViewRelated/Reader/Headers/ReaderSiteHeaderView.swift @@ -121,14 +121,14 @@ private struct ReaderSiteHeader: View { let countsString = String(format: Constants.countsFormat, viewModel.postCount, viewModel.followerCount) let stringItems = countsString.components(separatedBy: " ") - return stringItems.reduce(Text(""), { + return stringItems.reduce(Text(verbatim: ""), { var text = Text($1) if $1 == viewModel.postCount || $1 == viewModel.followerCount { text = text.font(.subheadline) } else { text = text.font(.subheadline).foregroundColor(.secondary) } - return $0 + text + Text(" ") + return $0 + text + Text(verbatim: " ") }) } diff --git a/WordPress/Classes/ViewRelated/Reader/Theme/ReaderDisplaySettingViewController.swift b/WordPress/Classes/ViewRelated/Reader/Theme/ReaderDisplaySettingViewController.swift index fea0515b9df9..29e89895f0a9 100644 --- a/WordPress/Classes/ViewRelated/Reader/Theme/ReaderDisplaySettingViewController.swift +++ b/WordPress/Classes/ViewRelated/Reader/Theme/ReaderDisplaySettingViewController.swift @@ -360,7 +360,7 @@ extension ReaderDisplaySettingSelectionView { TrackingKeys.valueKey: font.rawValue]) } label: { VStack(spacing: .DS.Padding.half) { - Text("Aa") + Text(verbatim: "Aa") .font(Font(ReaderDisplaySettings.font(with: font, textStyle: .largeTitle)).bold()) .foregroundStyle(Color(.label)) Text(font.rawValue.capitalized) @@ -390,11 +390,11 @@ extension ReaderDisplaySettingSelectionView { step: 1) { Text(Strings.sizeSliderLabel) } minimumValueLabel: { - Text("A") + Text(verbatim: "A") .font(Font(ReaderDisplaySettings.font(with: .sans, size: .extraSmall, textStyle: .body))) .accessibilityHidden(true) } maximumValueLabel: { - Text("A") + Text(verbatim: "A") .font(Font(ReaderDisplaySettings.font(with: .sans, size: .extraExtraExtraLarge, textStyle: .body))) .accessibilityHidden(true) } onEditingChanged: { _ in diff --git a/WordPress/Classes/ViewRelated/Reader/User/ReaderUserProfileView.swift b/WordPress/Classes/ViewRelated/Reader/User/ReaderUserProfileView.swift index 33f870d8cfe1..781d42cea6bf 100644 --- a/WordPress/Classes/ViewRelated/Reader/User/ReaderUserProfileView.swift +++ b/WordPress/Classes/ViewRelated/Reader/User/ReaderUserProfileView.swift @@ -21,7 +21,7 @@ struct ReaderUserProfileView: View { .lineLimit(2) } } else { - Text("–") + Text(verbatim: "–") .foregroundStyle(.secondary) } } diff --git a/WordPress/Classes/ViewRelated/System/Sidebar/SidebarProfileView.swift b/WordPress/Classes/ViewRelated/System/Sidebar/SidebarProfileView.swift index 1d15e51803ea..130fec963940 100644 --- a/WordPress/Classes/ViewRelated/System/Sidebar/SidebarProfileView.swift +++ b/WordPress/Classes/ViewRelated/System/Sidebar/SidebarProfileView.swift @@ -15,7 +15,7 @@ struct SidebarProfileView: View { Text(displayName) .font(.subheadline.weight(.medium)) } - Text("@\(account.username)") + Text(verbatim: "@\(account.username)") .font(.footnote) .foregroundStyle(.secondary) } diff --git a/WordPress/Classes/ViewRelated/System/Sidebar/SidebarViewController.swift b/WordPress/Classes/ViewRelated/System/Sidebar/SidebarViewController.swift index a1a9598cb730..a299ec4231a1 100644 --- a/WordPress/Classes/ViewRelated/System/Sidebar/SidebarViewController.swift +++ b/WordPress/Classes/ViewRelated/System/Sidebar/SidebarViewController.swift @@ -204,7 +204,7 @@ private struct SidebarProfileContainerView: View { VStack(alignment: .leading, spacing: 1) { Text("Sign In") .font(.subheadline.weight(.medium)) - Text("WordPress.com") + Text(verbatim: "WordPress.com") .font(.footnote) .foregroundColor(.secondary) } diff --git a/WordPress/Classes/ViewRelated/Tags/SiteTagsView.swift b/WordPress/Classes/ViewRelated/Tags/SiteTagsView.swift index 59988e7b9bf7..ce82ff1f3389 100644 --- a/WordPress/Classes/ViewRelated/Tags/SiteTagsView.swift +++ b/WordPress/Classes/ViewRelated/Tags/SiteTagsView.swift @@ -170,7 +170,7 @@ private struct TagRowContent: View { // swiftlint:disable:next empty_count if showPostCount, tag.count != 0 { - Text("\(tag.count)") + Text(tag.count, format: .number) .font(.callout) .foregroundColor(.secondary) }