Skip to content

Commit

Permalink
Stats: Localize percentage strings (#22918)
Browse files Browse the repository at this point in the history
  • Loading branch information
staskus authored Apr 3, 2024
2 parents 573ced7 + d8bdb39 commit a80e96e
Show file tree
Hide file tree
Showing 9 changed files with 64 additions and 139 deletions.
16 changes: 16 additions & 0 deletions WordPress/Classes/ViewRelated/Stats/Extensions/Double+Stats.swift
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,10 @@ extension Double {
return formattedString
}

func percentageString() -> String {
return NumberFormatter.statsPercentage.string(from: .init(value: self))!
}

private func formatWithCommas() -> String {
return numberFormatter.string(for: self) ?? ""
}
Expand All @@ -157,16 +161,28 @@ extension NSNumber {
func abbreviatedString(forHeroNumber: Bool = false) -> String {
return self.doubleValue.abbreviatedString(forHeroNumber: forHeroNumber)
}

func percentageString() -> String {
return self.doubleValue.percentageString()
}
}

extension Float {
func abbreviatedString(forHeroNumber: Bool = false) -> String {
return Double(self).abbreviatedString(forHeroNumber: forHeroNumber)
}

func percentageString(forHeroNumber: Bool = false) -> String {
return Double(self).percentageString()
}
}

extension Int {
func abbreviatedString(forHeroNumber: Bool = false) -> String {
return Double(self).abbreviatedString(forHeroNumber: forHeroNumber)
}

func percentageString(forHeroNumber: Bool = false) -> String {
return Double(self).percentageString()
}
}
Original file line number Diff line number Diff line change
@@ -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
}()
}
Original file line number Diff line number Diff line change
Expand Up @@ -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."
)
}
Expand Down Expand Up @@ -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.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)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.percentageString())
} else if difference < 0 {
return String(format: TextContent.differenceLower, differencePrefix, difference.abbreviatedString(), percentage.abbreviatedString())
return String(format: TextContent.differenceLower, differencePrefix, difference.abbreviatedString(), percentage.percentageString())
} else {
return TextContent.differenceSame
}
Expand Down Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.percentageString()))\n", attributes: topRowAttributes)
let bottomRowStr = NSAttributedString(string: "\(yValue) \(name)", attributes: bottomRowAttributes)

topRowStr.append(bottomRowStr)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.percentageString()
)
} else {
let stringFormat = NSLocalizedString(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.percentageString())
}

var differenceTextColor: UIColor {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,24 +53,17 @@ final class StatsTrafficBarChartCell: UITableViewCell {
self.unit = unit
self.siteStatsPeriodDelegate = siteStatsPeriodDelegate

updateLabels()
updateButtons()
updateChartView()
}
}

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
Expand Down Expand Up @@ -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
Expand Down
Loading

0 comments on commit a80e96e

Please sign in to comment.