From 5e2fe617807205ddb6ab4558fc7f7be786a1b462 Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Thu, 28 Mar 2024 18:36:21 +0200 Subject: [PATCH 1/8] Create a localized stats percentage formatter with number abbreviation --- .../Stats/Extensions/Double+Stats.swift | 20 +++++++++++++++++++ .../Extensions/NumberFormatter+Stats.swift | 18 +++++++++++++++++ WordPress/WordPress.xcodeproj/project.pbxproj | 20 +++++++++++++------ 3 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 WordPress/Classes/ViewRelated/Stats/Extensions/NumberFormatter+Stats.swift diff --git a/WordPress/Classes/ViewRelated/Stats/Extensions/Double+Stats.swift b/WordPress/Classes/ViewRelated/Stats/Extensions/Double+Stats.swift index 5b1ea82ed58e..eecee0402efd 100644 --- a/WordPress/Classes/ViewRelated/Stats/Extensions/Double+Stats.swift +++ b/WordPress/Classes/ViewRelated/Stats/Extensions/Double+Stats.swift @@ -143,6 +143,14 @@ extension Double { return formattedString } + func abbreviatedPercentageString(forHeroNumber: Bool = false) -> String { + guard let numberWithPercentage = NumberFormatter.statsPercentage.string(from: .init(value: self)) else { + return abbreviatedString(forHeroNumber: forHeroNumber) + } + + return numberWithPercentage.replacingOccurrences(of: "\(self)", with: self.abbreviatedString(forHeroNumber: forHeroNumber)) + } + private func formatWithCommas() -> String { return numberFormatter.string(for: self) ?? "" } @@ -157,16 +165,28 @@ extension NSNumber { func abbreviatedString(forHeroNumber: Bool = false) -> String { return self.doubleValue.abbreviatedString(forHeroNumber: forHeroNumber) } + + func abbreviatedPercentageString(forHeroNumber: Bool = false) -> String { + return self.doubleValue.abbreviatedPercentageString(forHeroNumber: forHeroNumber) + } } extension Float { func abbreviatedString(forHeroNumber: Bool = false) -> String { return Double(self).abbreviatedString(forHeroNumber: forHeroNumber) } + + func abbreviatedPercentageString(forHeroNumber: Bool = false) -> String { + return Double(self).abbreviatedPercentageString(forHeroNumber: forHeroNumber) + } } extension Int { func abbreviatedString(forHeroNumber: Bool = false) -> String { return Double(self).abbreviatedString(forHeroNumber: forHeroNumber) } + + func abbreviatedPercentageString(forHeroNumber: Bool = false) -> String { + return Double(self).abbreviatedPercentageString(forHeroNumber: forHeroNumber) + } } diff --git a/WordPress/Classes/ViewRelated/Stats/Extensions/NumberFormatter+Stats.swift b/WordPress/Classes/ViewRelated/Stats/Extensions/NumberFormatter+Stats.swift new file mode 100644 index 000000000000..956eba6bb627 --- /dev/null +++ b/WordPress/Classes/ViewRelated/Stats/Extensions/NumberFormatter+Stats.swift @@ -0,0 +1,18 @@ +import Foundation + +extension NumberFormatter { + static let statsPercentage: NumberFormatter = { + let formatter = NumberFormatter() + formatter.numberStyle = .percent + formatter.multiplier = 1 + formatter.minimumFractionDigits = 0 + formatter.maximumFractionDigits = 2 + if let preferredLocaleIdentifier = Bundle.main.preferredLocalizations.first { + formatter.locale = Locale(identifier: preferredLocaleIdentifier) + } else { + formatter.locale = Locale.current + } + + return formatter + }() +} diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj index 1f19d0216c0c..8c976e443d6d 100644 --- a/WordPress/WordPress.xcodeproj/project.pbxproj +++ b/WordPress/WordPress.xcodeproj/project.pbxproj @@ -250,6 +250,9 @@ 01E2580B2ACDC72C00F09666 /* PlanWizardContentViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01E2580A2ACDC72C00F09666 /* PlanWizardContentViewModel.swift */; }; 01E2580C2ACDC72C00F09666 /* PlanWizardContentViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01E2580A2ACDC72C00F09666 /* PlanWizardContentViewModel.swift */; }; 01E2580E2ACDC88100F09666 /* PlanWizardContentViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01E2580D2ACDC88100F09666 /* PlanWizardContentViewModelTests.swift */; }; + 01E70EBB2BB5CCCF000BFE45 /* NumberFormatter+Stats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01E70EBA2BB5CCCF000BFE45 /* NumberFormatter+Stats.swift */; }; + 01E70EBC2BB5CCCF000BFE45 /* NumberFormatter+Stats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01E70EBA2BB5CCCF000BFE45 /* NumberFormatter+Stats.swift */; }; + 01E70EBD2BB5D035000BFE45 /* NumberFormatter+Stats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01E70EBA2BB5CCCF000BFE45 /* NumberFormatter+Stats.swift */; }; 01E78D1D296EA54F00FB6863 /* StatsPeriodHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01E78D1C296EA54F00FB6863 /* StatsPeriodHelperTests.swift */; }; 02761EC02270072F009BAF0F /* BlogDetailsViewController+SectionHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02761EBF2270072F009BAF0F /* BlogDetailsViewController+SectionHelpers.swift */; }; 02761EC222700A9C009BAF0F /* BlogDetailsSubsectionToSectionCategoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02761EC122700A9C009BAF0F /* BlogDetailsSubsectionToSectionCategoryTests.swift */; }; @@ -5999,6 +6002,7 @@ 01E258082ACC3AA000F09666 /* iOS17WidgetAPIs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOS17WidgetAPIs.swift; sourceTree = ""; }; 01E2580A2ACDC72C00F09666 /* PlanWizardContentViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlanWizardContentViewModel.swift; sourceTree = ""; }; 01E2580D2ACDC88100F09666 /* PlanWizardContentViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlanWizardContentViewModelTests.swift; sourceTree = ""; }; + 01E70EBA2BB5CCCF000BFE45 /* NumberFormatter+Stats.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NumberFormatter+Stats.swift"; sourceTree = ""; }; 01E78D1C296EA54F00FB6863 /* StatsPeriodHelperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsPeriodHelperTests.swift; sourceTree = ""; }; 02761EBF2270072F009BAF0F /* BlogDetailsViewController+SectionHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BlogDetailsViewController+SectionHelpers.swift"; sourceTree = ""; }; 02761EC122700A9C009BAF0F /* BlogDetailsSubsectionToSectionCategoryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlogDetailsSubsectionToSectionCategoryTests.swift; sourceTree = ""; }; @@ -10970,7 +10974,7 @@ path = Classes; sourceTree = ""; }; - 29B97314FDCFA39411CA2CEA = { + 29B97314FDCFA39411CA2CEA /* CustomTemplate */ = { isa = PBXGroup; children = ( 3F20FDF3276BF21000DA3CAD /* Packages */, @@ -14666,6 +14670,7 @@ 98487E3921EE8FB500352B4E /* UITableViewCell+Stats.swift */, FAB9826D2697038700B172A3 /* StatsViewController+JetpackSettings.swift */, FAB985C02697550C00B172A3 /* NoResultsViewController+StatsModule.swift */, + 01E70EBA2BB5CCCF000BFE45 /* NumberFormatter+Stats.swift */, ); path = Extensions; sourceTree = ""; @@ -19383,7 +19388,7 @@ bg, sk, ); - mainGroup = 29B97314FDCFA39411CA2CEA; + mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */; packageReferences = ( 3FF1442E266F3C2400138163 /* XCRemoteSwiftPackageReference "ScreenObject" */, 3FC2C33B26C4CF0A00C6D98F /* XCRemoteSwiftPackageReference "XCUITestHelpers" */, @@ -20984,11 +20989,11 @@ files = ( ); inputPaths = ( - "$SRCROOT/../Scripts/BuildPhases/CopyGutenbergJS.inputs.xcfilelist", + $SRCROOT/../Scripts/BuildPhases/CopyGutenbergJS.inputs.xcfilelist, ); name = "Copy Gutenberg JS"; outputFileListPaths = ( - "$SRCROOT/../Scripts/BuildPhases/CopyGutenbergJS.outputs.xcfilelist", + $SRCROOT/../Scripts/BuildPhases/CopyGutenbergJS.outputs.xcfilelist, ); outputPaths = ( "", @@ -21183,13 +21188,13 @@ files = ( ); inputFileListPaths = ( - "$SRCROOT/../Scripts/BuildPhases/CopyGutenbergJS.inputs.xcfilelist", + $SRCROOT/../Scripts/BuildPhases/CopyGutenbergJS.inputs.xcfilelist, ); inputPaths = ( ); name = "Copy Gutenberg JS"; outputFileListPaths = ( - "$SRCROOT/../Scripts/BuildPhases/CopyGutenbergJS.outputs.xcfilelist", + $SRCROOT/../Scripts/BuildPhases/CopyGutenbergJS.outputs.xcfilelist, ); outputPaths = ( ); @@ -21273,6 +21278,7 @@ 0107E0D028F97D5000DE87DB /* HomeWidgetThisWeek.swift in Sources */, C9FE384729C2A3D200D39841 /* LockScreenStatsWidgetConfig.swift in Sources */, C9B477BA29CD2FEF008CBF49 /* LockScreenUnconfiguredViewModel.swift in Sources */, + 01E70EBD2BB5D035000BFE45 /* NumberFormatter+Stats.swift in Sources */, 0107E0D128F97D5000DE87DB /* SingleStatView.swift in Sources */, 0188FE4C2AA62F800093EDA5 /* LockScreenTodayLikesCommentsStatWidgetConfig.swift in Sources */, 0107E0D228F97D5000DE87DB /* UnconfiguredView.swift in Sources */, @@ -21989,6 +21995,7 @@ 98B52AE121F7AF4A006FF6B4 /* StatsDataHelper.swift in Sources */, 8BAD272C241FEF3300E9D105 /* PrepublishingViewController.swift in Sources */, 4A1E77C92988997C006281CC /* PublicizeConnection+Creation.swift in Sources */, + 01E70EBB2BB5CCCF000BFE45 /* NumberFormatter+Stats.swift in Sources */, F10D634F26F0B78E00E46CC7 /* Blog+Organization.swift in Sources */, DCCDF75B283BEFEA00AA347E /* SiteStatsInsightsDetailsTableViewController.swift in Sources */, 80A2154029CA68D5002FE8EB /* RemoteFeatureFlag.swift in Sources */, @@ -25359,6 +25366,7 @@ FABB24182602FC2C00C8785C /* WKWebView+UserAgent.swift in Sources */, FABB24192602FC2C00C8785C /* JetpackCapabilitiesService.swift in Sources */, FABB241A2602FC2C00C8785C /* PostToPost30To31.m in Sources */, + 01E70EBC2BB5CCCF000BFE45 /* NumberFormatter+Stats.swift in Sources */, FABB241B2602FC2C00C8785C /* GutenGhostView.swift in Sources */, FABB241C2602FC2C00C8785C /* ModelSettableCell.swift in Sources */, FABB241D2602FC2C00C8785C /* FollowCommentsService.swift in Sources */, From c549c601695a8e2b2b630174627d8c538a80b094 Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Thu, 28 Mar 2024 18:41:59 +0200 Subject: [PATCH 2/8] Refactor OverviewCell difference label to add localized percentage using NumberFormatter --- .../Stats/Period Stats/Overview/OverviewCell.swift | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Stats/Period Stats/Overview/OverviewCell.swift b/WordPress/Classes/ViewRelated/Stats/Period Stats/Overview/OverviewCell.swift index 403a9079b358..67d34a3a61d5 100644 --- a/WordPress/Classes/ViewRelated/Stats/Period Stats/Overview/OverviewCell.swift +++ b/WordPress/Classes/ViewRelated/Stats/Period Stats/Overview/OverviewCell.swift @@ -56,11 +56,16 @@ struct OverviewTabData: FilterTabBarItem, Hashable { } var differenceLabel: String { - let stringFormat = NSLocalizedString("%@%@ (%@%%)", comment: "Difference label for Period Overview stat, indicating change from previous period. Ex: +99.9K (5%)") + let stringFormat = NSLocalizedString( + "stats.overview.differenceLabelWithNumber", + value: "%1$@%2$@ (%3$@)", + comment: "Text for the Stats Traffic Overview stat difference label. Shows the change from the previous period, including the percentage value. E.g.: +12.3K (5%). %1$@ is the placeholder for the change sign ('-', '+', or none). %2$@ is the placeholder for the change numerical value. %3$@ is the placeholder for the change percentage value." + ) + return String.localizedStringWithFormat(stringFormat, difference < 0 ? "" : "+", difference.abbreviatedString(), - differencePercent.abbreviatedString()) + differencePercent.abbreviatedPercentageString()) } var differenceTextColor: UIColor { From 35c31e9d845719298fdfb5d9147efe023610abd1 Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Thu, 28 Mar 2024 18:49:31 +0200 Subject: [PATCH 3/8] Refactor TotalLikes difference label to add localized percentage using NumberFormatter --- .../Stats/Insights/StatsTotalInsightsCell.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Stats/Insights/StatsTotalInsightsCell.swift b/WordPress/Classes/ViewRelated/Stats/Insights/StatsTotalInsightsCell.swift index 7b3ffb3f3bc7..6ea0a5670973 100644 --- a/WordPress/Classes/ViewRelated/Stats/Insights/StatsTotalInsightsCell.swift +++ b/WordPress/Classes/ViewRelated/Stats/Insights/StatsTotalInsightsCell.swift @@ -304,9 +304,9 @@ class StatsTotalInsightsCell: StatsBaseCell { let differenceText: String = { if difference > 0 { - return String(format: TextContent.differenceHigher, differencePrefix, difference.abbreviatedString(), percentage.abbreviatedString()) + return String(format: TextContent.differenceHigher, differencePrefix, difference.abbreviatedString(), percentage.abbreviatedPercentageString()) } else if difference < 0 { - return String(format: TextContent.differenceLower, differencePrefix, difference.abbreviatedString(), percentage.abbreviatedString()) + return String(format: TextContent.differenceLower, differencePrefix, difference.abbreviatedString(), percentage.abbreviatedPercentageString()) } else { return TextContent.differenceSame } @@ -397,11 +397,11 @@ class StatsTotalInsightsCell: StatsBaseCell { private enum TextContent { static let differenceDelimiter = Character("*") - static let differenceHigher = NSLocalizedString("stats.insights.label.totalLikes.higher", - value: "*%@%@ (%@%%)* higher than the previous 7-days", + static let differenceHigher = NSLocalizedString("stats.insights.label.totalLikes.higherNumber", + value: "*%1$@%2$@ (%3$@)* higher than the previous 7-days", comment: "Label shown on some metrics in the Stats Insights section, such as Comments count. The placeholders will be populated with a change and a percentage – e.g. '+17 (40%) higher than the previous 7-days'. The *s mark the numerical values, which will be highlighted differently from the rest of the text.") - static let differenceLower = NSLocalizedString("stats.insights.label.totalLikes.lower", - value: "*%@%@ (%@%%)* lower than the previous 7-days", + static let differenceLower = NSLocalizedString("stats.insights.label.totalLikes.lowerNumber", + value: "*%1$@%2$@ (%3$@)* lower than the previous 7-days", comment: "Label shown on some metrics in the Stats Insights section, such as Comments count. The placeholders will be populated with a change and a percentage – e.g. '-17 (40%) lower than the previous 7-days'. The *s mark the numerical values, which will be highlighted differently from the rest of the text.") static let differenceSame = NSLocalizedString("stats.insights.label.totalLikes.same", value: "The same as the previous 7-days", From 5ae6153a5dfc855493424e10dc55f970760f45d1 Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Thu, 28 Mar 2024 19:01:18 +0200 Subject: [PATCH 4/8] Refactor MostPopularTime difference label to add localized percentage using NumberFormatter --- .../Stats/Insights/SiteStatsInsightsViewModel.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Stats/Insights/SiteStatsInsightsViewModel.swift b/WordPress/Classes/ViewRelated/Stats/Insights/SiteStatsInsightsViewModel.swift index a424cd434668..36fc3b936007 100644 --- a/WordPress/Classes/ViewRelated/Stats/Insights/SiteStatsInsightsViewModel.swift +++ b/WordPress/Classes/ViewRelated/Stats/Insights/SiteStatsInsightsViewModel.swift @@ -452,8 +452,8 @@ private extension SiteStatsInsightsViewModel { static let bestDay = NSLocalizedString("Best Day", comment: "'Best Day' label for Most Popular stat.") static let bestHour = NSLocalizedString("Best Hour", comment: "'Best Hour' label for Most Popular stat.") static let viewPercentage = NSLocalizedString( - "stats.insights.mostPopularCard.viewPercentage", - value: "%d%% of views", + "stats.insights.mostPopularCard.viewsNumber", + value: "%1$@ of views", comment: "Label showing the percentage of views to a user's site which fall on a particular day." ) } @@ -523,8 +523,8 @@ private extension SiteStatsInsightsViewModel { return nil } - let dayPercentage = String(format: MostPopularStats.viewPercentage, mostPopularStats.mostPopularDayOfWeekPercentage) - let hourPercentage = String(format: MostPopularStats.viewPercentage, mostPopularStats.mostPopularHourPercentage) + let dayPercentage = String(format: MostPopularStats.viewPercentage, mostPopularStats.mostPopularDayOfWeekPercentage.abbreviatedPercentageString()) + let hourPercentage = String(format: MostPopularStats.viewPercentage, mostPopularStats.mostPopularHourPercentage.abbreviatedPercentageString()) return StatsMostPopularTimeData(mostPopularDayTitle: MostPopularStats.bestDay, mostPopularTimeTitle: MostPopularStats.bestHour, mostPopularDay: dayString, mostPopularTime: timeString.uppercased(), dayPercentage: dayPercentage, timePercentage: hourPercentage) } From 593935eb1fed008c0807a7804083b99ddb3c1ec1 Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Thu, 28 Mar 2024 19:05:15 +0200 Subject: [PATCH 5/8] Refactor ViewsVisitorsCell difference label to add localized percentage using NumberFormatter --- .../ViewsVisitors/ViewsVisitorsLineChartCell.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Stats/Insights/ViewsVisitors/ViewsVisitorsLineChartCell.swift b/WordPress/Classes/ViewRelated/Stats/Insights/ViewsVisitors/ViewsVisitorsLineChartCell.swift index 1b87cfb7e0d0..3ae03231eb51 100644 --- a/WordPress/Classes/ViewRelated/Stats/Insights/ViewsVisitors/ViewsVisitorsLineChartCell.swift +++ b/WordPress/Classes/ViewRelated/Stats/Insights/ViewsVisitors/ViewsVisitorsLineChartCell.swift @@ -69,15 +69,15 @@ struct StatsSegmentedControlData { if differencePercent != 0 { let stringFormat = NSLocalizedString( - "insights.visitorsLineChartCell.differenceLabelWithPercentage", - value: "%1$@%2$@ (%3$@%%)", - comment: "Text for the Insights Overview stat difference label. Shows the change from the previous period, including the percentage value. E.g.: +12.3K (5%). %1$@ is the placeholder for the change sign ('-', '+', or none). %2$@ is the placeholder for the change numerical value. %3$@ is the placeholder for the change percentage value, excluding the % sign." + "insights.visitorsLineChartCell.differenceLabelWithNumber", + value: "%1$@%2$@ (%3$@)", + comment: "Text for the Insights Overview stat difference label. Shows the change from the previous period, including the percentage value. E.g.: +12.3K (5%). %1$@ is the placeholder for the change sign ('-', '+', or none). %2$@ is the placeholder for the change numerical value. %3$@ is the placeholder for the change percentage value." ) return String.localizedStringWithFormat( stringFormat, plusSign, difference.abbreviatedString(), - differencePercent.abbreviatedString() + differencePercent.abbreviatedPercentageString() ) } else { let stringFormat = NSLocalizedString( From 9898c7cef837ffeb79583096fc874f5167029eba Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Thu, 28 Mar 2024 19:13:03 +0200 Subject: [PATCH 6/8] Remove difference label related code from StatsTrafficBarChartCell --- .../Chart/StatsTrafficBarChartCell.swift | 122 ------------------ 1 file changed, 122 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Stats/Traffic/Chart/StatsTrafficBarChartCell.swift b/WordPress/Classes/ViewRelated/Stats/Traffic/Chart/StatsTrafficBarChartCell.swift index d403870d3ac7..57b65064c4c6 100644 --- a/WordPress/Classes/ViewRelated/Stats/Traffic/Chart/StatsTrafficBarChartCell.swift +++ b/WordPress/Classes/ViewRelated/Stats/Traffic/Chart/StatsTrafficBarChartCell.swift @@ -53,7 +53,6 @@ final class StatsTrafficBarChartCell: UITableViewCell { self.unit = unit self.siteStatsPeriodDelegate = siteStatsPeriodDelegate - updateLabels() updateButtons() updateChartView() } @@ -61,16 +60,10 @@ final class StatsTrafficBarChartCell: UITableViewCell { private extension StatsTrafficBarChartCell { @objc func selectedFilterDidChange(_ filterBar: FilterTabBar) { - updateLabels() updateChartView() siteStatsPeriodDelegate?.barChartTabSelected?(filterBar.selectedIndex) } - func updateLabels() { - let tabData = tabsData[filterTabBar.selectedIndex] - differenceLabel.attributedText = differenceAttributedString(tabData) - } - func updateButtons() { let font = tabsFont(for: tabsData) filterTabBar.tabsFont = font @@ -165,121 +158,6 @@ private extension StatsTrafficBarChartCell { } } -// MARK: - Difference - -private extension StatsTrafficBarChartCell { - enum DifferenceStrings { - static let weekHigher = NSLocalizedString("stats.traffic.label.weekDifference.higher", - value: "%@ higher than the previous 7-days\n", - comment: "Stats views higher than previous 7 days") - static let weekLower = NSLocalizedString("stats.traffic.label.weekDifference.lower", - value: "%@ lower than the previous 7-days\n", - comment: "Stats views lower than previous 7 days") - - static let monthHigher = NSLocalizedString("stats.traffic.label.monthDifference.higher", - value: "%@ higher than the previous month\n", - comment: "Stats views higher than previous month") - static let monthLower = NSLocalizedString("stats.traffic.label.monthDifference.lower", - value: "%@ lower than the previous month\n", - comment: "Stats views lower than previous month") - - static let yearHigher = NSLocalizedString("stats.traffic.label.yearDifference.higher", - value: "%@ higher than the previous year\n", - comment: "Stats views higher than previous year") - static let yearLower = NSLocalizedString("stats.traffic.label.yearDifference.lower", - value: "%@ lower than the previous year\n", - comment: "Stats views lower than previous year") - } - - func differenceAttributedString(_ data: StatsTrafficBarChartTabData) -> NSAttributedString? { - guard let differenceText = differenceText(data) else { - return nil - } - - let defaultAttributes = [NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .footnote), NSAttributedString.Key.foregroundColor: UIColor.DS.Foreground.secondary] - let differenceColor = data.difference > 0 ? UIColor.DS.Foreground.success : UIColor.DS.Foreground.error - let differenceLabel = differenceLabel(data) - let attributedString = NSMutableAttributedString( - string: String(format: differenceText, differenceLabel), - attributes: defaultAttributes - ) - - let str = attributedString.string as NSString - let range = str.range(of: differenceLabel) - - attributedString.addAttributes( - [.foregroundColor: differenceColor, - .font: UIFont.preferredFont(forTextStyle: .footnote) - ], - range: NSRange(location: range.location, length: differenceLabel.count) - ) - - return attributedString - } - - func differenceText(_ data: StatsTrafficBarChartTabData) -> String? { - switch data.period { - case .week: - if data.difference > 0 { - return DifferenceStrings.weekHigher - } else if data.difference < 0 { - return DifferenceStrings.weekLower - } - case .month: - if data.difference > 0 { - return DifferenceStrings.monthHigher - } else if data.difference < 0 { - return DifferenceStrings.monthLower - } - case .year: - if data.difference > 0 { - return DifferenceStrings.yearHigher - } else if data.difference < 0 { - return DifferenceStrings.yearLower - } - default: - return nil - } - - return nil - } - - func differenceLabel(_ data: StatsTrafficBarChartTabData) -> String { - // We want to show something like "+10.2K (+5%)" if we have a percentage difference and "1.2K" if we don't. - // - // Negative cases automatically appear with a negative sign "-10.2K (-5%)" by using `abbreviatedString()`. - // `abbreviatedString()` also handles formatting big numbers, i.e. 10,200 will become 10.2K. - let formatter = NumberFormatter() - formatter.locale = .current - let plusSign = data.difference <= 0 ? "" : "\(formatter.plusSign ?? "")" - - if data.differencePercent != 0 { - let stringFormat = NSLocalizedString( - "stats.traffic.differenceLabelWithPercentage", - value: "%1$@%2$@ (%3$@%%)", - comment: "Text for the Stats Traffic Overview stat difference label. Shows the change from the previous period, including the percentage value. E.g.: +12.3K (5%). %1$@ is the placeholder for the change sign ('-', '+', or none). %2$@ is the placeholder for the change numerical value. %3$@ is the placeholder for the change percentage value, excluding the % sign." - ) - return String.localizedStringWithFormat( - stringFormat, - plusSign, - data.difference.abbreviatedString(), - data.differencePercent.abbreviatedString() - ) - } else { - let stringFormat = NSLocalizedString( - "stats.traffic.differenceLabelWithoutPercentage", - value: "%1$@%2$@", - comment: "Text for the Stats Traffic Overview stat difference label. Shows the change from the previous period. E.g.: +12.3K. %1$@ is the placeholder for the change sign ('-', '+', or none). %2$@ is the placeholder for the change numerical value." - ) - return String.localizedStringWithFormat( - stringFormat, - plusSign, - data.difference.abbreviatedString() - ) - } - } -} - struct StatsTrafficBarChartTabData: FilterTabBarItem, Equatable { var tabTitle: String var tabData: Int From ba5c2dea9a09369bbb304887154eb11dae3a90c1 Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Mon, 1 Apr 2024 10:59:24 +0300 Subject: [PATCH 7/8] Make Views and Visitors chart marker difference label percentage localized --- .../Stats/Insights/ViewsVisitors/ViewsVisitorsChartMarker.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPress/Classes/ViewRelated/Stats/Insights/ViewsVisitors/ViewsVisitorsChartMarker.swift b/WordPress/Classes/ViewRelated/Stats/Insights/ViewsVisitors/ViewsVisitorsChartMarker.swift index e588c5a1af96..412aead1ec58 100644 --- a/WordPress/Classes/ViewRelated/Stats/Insights/ViewsVisitors/ViewsVisitorsChartMarker.swift +++ b/WordPress/Classes/ViewRelated/Stats/Insights/ViewsVisitors/ViewsVisitorsChartMarker.swift @@ -265,7 +265,7 @@ final class ViewsVisitorsChartMarker: MarkerView { .paragraphStyle: paragraphStyle, .foregroundColor: UIColor.white] - let topRowStr = NSMutableAttributedString(string: "\(differenceStr) (\(roundedPercentage)%)\n", attributes: topRowAttributes) + let topRowStr = NSMutableAttributedString(string: "\(differenceStr) (\(roundedPercentage.abbreviatedPercentageString()))\n", attributes: topRowAttributes) let bottomRowStr = NSAttributedString(string: "\(yValue) \(name)", attributes: bottomRowAttributes) topRowStr.append(bottomRowStr) From d8bdb39d13470b92742a50be87e282d9c34bff10 Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Wed, 3 Apr 2024 10:32:39 +0300 Subject: [PATCH 8/8] Remove abbreviation from the percentage string --- .../Stats/Extensions/Double+Stats.swift | 20 ++++++++----------- .../Insights/SiteStatsInsightsViewModel.swift | 4 ++-- .../Insights/StatsTotalInsightsCell.swift | 4 ++-- .../ViewsVisitorsChartMarker.swift | 2 +- .../ViewsVisitorsLineChartCell.swift | 2 +- .../Period Stats/Overview/OverviewCell.swift | 2 +- 6 files changed, 15 insertions(+), 19 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Stats/Extensions/Double+Stats.swift b/WordPress/Classes/ViewRelated/Stats/Extensions/Double+Stats.swift index eecee0402efd..b66ab7fb9f83 100644 --- a/WordPress/Classes/ViewRelated/Stats/Extensions/Double+Stats.swift +++ b/WordPress/Classes/ViewRelated/Stats/Extensions/Double+Stats.swift @@ -143,12 +143,8 @@ extension Double { return formattedString } - func abbreviatedPercentageString(forHeroNumber: Bool = false) -> String { - guard let numberWithPercentage = NumberFormatter.statsPercentage.string(from: .init(value: self)) else { - return abbreviatedString(forHeroNumber: forHeroNumber) - } - - return numberWithPercentage.replacingOccurrences(of: "\(self)", with: self.abbreviatedString(forHeroNumber: forHeroNumber)) + func percentageString() -> String { + return NumberFormatter.statsPercentage.string(from: .init(value: self))! } private func formatWithCommas() -> String { @@ -166,8 +162,8 @@ extension NSNumber { return self.doubleValue.abbreviatedString(forHeroNumber: forHeroNumber) } - func abbreviatedPercentageString(forHeroNumber: Bool = false) -> String { - return self.doubleValue.abbreviatedPercentageString(forHeroNumber: forHeroNumber) + func percentageString() -> String { + return self.doubleValue.percentageString() } } @@ -176,8 +172,8 @@ extension Float { return Double(self).abbreviatedString(forHeroNumber: forHeroNumber) } - func abbreviatedPercentageString(forHeroNumber: Bool = false) -> String { - return Double(self).abbreviatedPercentageString(forHeroNumber: forHeroNumber) + func percentageString(forHeroNumber: Bool = false) -> String { + return Double(self).percentageString() } } @@ -186,7 +182,7 @@ extension Int { return Double(self).abbreviatedString(forHeroNumber: forHeroNumber) } - func abbreviatedPercentageString(forHeroNumber: Bool = false) -> String { - return Double(self).abbreviatedPercentageString(forHeroNumber: forHeroNumber) + func percentageString(forHeroNumber: Bool = false) -> String { + return Double(self).percentageString() } } diff --git a/WordPress/Classes/ViewRelated/Stats/Insights/SiteStatsInsightsViewModel.swift b/WordPress/Classes/ViewRelated/Stats/Insights/SiteStatsInsightsViewModel.swift index 477cefee503e..144dfdb4435d 100644 --- a/WordPress/Classes/ViewRelated/Stats/Insights/SiteStatsInsightsViewModel.swift +++ b/WordPress/Classes/ViewRelated/Stats/Insights/SiteStatsInsightsViewModel.swift @@ -523,8 +523,8 @@ private extension SiteStatsInsightsViewModel { return nil } - let dayPercentage = String(format: MostPopularStats.viewPercentage, mostPopularStats.mostPopularDayOfWeekPercentage.abbreviatedPercentageString()) - let hourPercentage = String(format: MostPopularStats.viewPercentage, mostPopularStats.mostPopularHourPercentage.abbreviatedPercentageString()) + let dayPercentage = String(format: MostPopularStats.viewPercentage, mostPopularStats.mostPopularDayOfWeekPercentage.percentageString()) + let hourPercentage = String(format: MostPopularStats.viewPercentage, mostPopularStats.mostPopularHourPercentage.percentageString()) return StatsMostPopularTimeData(mostPopularDayTitle: MostPopularStats.bestDay, mostPopularTimeTitle: MostPopularStats.bestHour, mostPopularDay: dayString, mostPopularTime: timeString.uppercased(), dayPercentage: dayPercentage, timePercentage: hourPercentage) } diff --git a/WordPress/Classes/ViewRelated/Stats/Insights/StatsTotalInsightsCell.swift b/WordPress/Classes/ViewRelated/Stats/Insights/StatsTotalInsightsCell.swift index 6ea0a5670973..299386b84797 100644 --- a/WordPress/Classes/ViewRelated/Stats/Insights/StatsTotalInsightsCell.swift +++ b/WordPress/Classes/ViewRelated/Stats/Insights/StatsTotalInsightsCell.swift @@ -304,9 +304,9 @@ class StatsTotalInsightsCell: StatsBaseCell { let differenceText: String = { if difference > 0 { - return String(format: TextContent.differenceHigher, differencePrefix, difference.abbreviatedString(), percentage.abbreviatedPercentageString()) + return String(format: TextContent.differenceHigher, differencePrefix, difference.abbreviatedString(), percentage.percentageString()) } else if difference < 0 { - return String(format: TextContent.differenceLower, differencePrefix, difference.abbreviatedString(), percentage.abbreviatedPercentageString()) + return String(format: TextContent.differenceLower, differencePrefix, difference.abbreviatedString(), percentage.percentageString()) } else { return TextContent.differenceSame } diff --git a/WordPress/Classes/ViewRelated/Stats/Insights/ViewsVisitors/ViewsVisitorsChartMarker.swift b/WordPress/Classes/ViewRelated/Stats/Insights/ViewsVisitors/ViewsVisitorsChartMarker.swift index 412aead1ec58..54f265c35786 100644 --- a/WordPress/Classes/ViewRelated/Stats/Insights/ViewsVisitors/ViewsVisitorsChartMarker.swift +++ b/WordPress/Classes/ViewRelated/Stats/Insights/ViewsVisitors/ViewsVisitorsChartMarker.swift @@ -265,7 +265,7 @@ final class ViewsVisitorsChartMarker: MarkerView { .paragraphStyle: paragraphStyle, .foregroundColor: UIColor.white] - let topRowStr = NSMutableAttributedString(string: "\(differenceStr) (\(roundedPercentage.abbreviatedPercentageString()))\n", attributes: topRowAttributes) + let topRowStr = NSMutableAttributedString(string: "\(differenceStr) (\(roundedPercentage.percentageString()))\n", attributes: topRowAttributes) let bottomRowStr = NSAttributedString(string: "\(yValue) \(name)", attributes: bottomRowAttributes) topRowStr.append(bottomRowStr) diff --git a/WordPress/Classes/ViewRelated/Stats/Insights/ViewsVisitors/ViewsVisitorsLineChartCell.swift b/WordPress/Classes/ViewRelated/Stats/Insights/ViewsVisitors/ViewsVisitorsLineChartCell.swift index 3ae03231eb51..a345b42db4fd 100644 --- a/WordPress/Classes/ViewRelated/Stats/Insights/ViewsVisitors/ViewsVisitorsLineChartCell.swift +++ b/WordPress/Classes/ViewRelated/Stats/Insights/ViewsVisitors/ViewsVisitorsLineChartCell.swift @@ -77,7 +77,7 @@ struct StatsSegmentedControlData { stringFormat, plusSign, difference.abbreviatedString(), - differencePercent.abbreviatedPercentageString() + differencePercent.percentageString() ) } else { let stringFormat = NSLocalizedString( diff --git a/WordPress/Classes/ViewRelated/Stats/Period Stats/Overview/OverviewCell.swift b/WordPress/Classes/ViewRelated/Stats/Period Stats/Overview/OverviewCell.swift index 67d34a3a61d5..6cea18a78e6a 100644 --- a/WordPress/Classes/ViewRelated/Stats/Period Stats/Overview/OverviewCell.swift +++ b/WordPress/Classes/ViewRelated/Stats/Period Stats/Overview/OverviewCell.swift @@ -65,7 +65,7 @@ struct OverviewTabData: FilterTabBarItem, Hashable { return String.localizedStringWithFormat(stringFormat, difference < 0 ? "" : "+", difference.abbreviatedString(), - differencePercent.abbreviatedPercentageString()) + differencePercent.percentageString()) } var differenceTextColor: UIColor {