Get the word "Month", "Day", "Week" localized

How can I can the localized string for the month, day, week units. I'm not speaking about "january", "february"... But the localized word "Month". Is the only solution is to do this by hand with NSLocalizedString?

Accepted Reply

Finally found the solution by myself with:


extension Calendar {

    /// get a calendar configured with the language of the user
    static var localized: Calendar {
        let prefLanguage = Locale.preferredLanguages[0]
        var calendar = Calendar(identifier: .gregorian)
        calendar.locale = Locale(identifier: prefLanguage)
        return calendar
    }
    
     func unitTitle(_ unit: NSCalendar.Unit, value: Int = 1, locale: Locale? = nil) -> String {
        let emptyString = String()
        let date = Date()
        let component = getComponent(from: unit)
        guard let sinceUnits = self.date(byAdding: component, value: value, to: date) else {
            return emptyString
        }

        let timeInterval = sinceUnits.timeIntervalSince(date)
        let formatter = DateComponentsFormatter()

        formatter.calendar = self
        formatter.allowedUnits = [unit]
        formatter.unitsStyle = .full
        guard let string = formatter.string(from: timeInterval) else {
            return emptyString
        }

        return string.replacingOccurrences(of: String(value), with: emptyString).trimmingCharacters(in: .whitespaces).capitalized
    }
    
    // swiftlint:disable:next cyclomatic_complexity
    private func getComponent(from unit: NSCalendar.Unit) -> Component {
        let component: Component

        switch unit {
        case .era:
            component = .era
        case .year:
            component = .year
        case .month:
            component = .month
        case .day:
            component = .day
        case .hour:
            component = .hour
        case .minute:
            component = .minute
        case .second:
            component = .second
        case .weekday:
            component = .weekday
        case .weekdayOrdinal:
            component = .weekdayOrdinal
        case .quarter:
            component = .quarter
        case .weekOfMonth:
            component = .weekOfMonth
        case .weekOfYear:
            component = .weekOfYear
        case .yearForWeekOfYear:
            component = .yearForWeekOfYear
        case .nanosecond:
            component = .nanosecond
        case .calendar:
            component = .calendar
        case .timeZone:
            component = .timeZone
        default:
            component = .calendar
        }
        return component
    }
}

And the use is:

Calendar.localized.unitTitle(.day)
Calendar.localized.unitTitle(.weekofMonth)
Calendcar.localized.unitTitle(.month)
  • This breaks on Sep 1 2022 for some reason:

    po formatter.string(from: 30 * 24 * 3600) // returns "0 months"
Add a Comment

Replies

Where do you get "month" in the date display ?


Could you show an example of how you get the date, how you localize, how you set locale and what you get with "month" inside.


If you write "month" as a string you define yourself, yes, you need NSLocalized.

Finally found the solution by myself with:


extension Calendar {

    /// get a calendar configured with the language of the user
    static var localized: Calendar {
        let prefLanguage = Locale.preferredLanguages[0]
        var calendar = Calendar(identifier: .gregorian)
        calendar.locale = Locale(identifier: prefLanguage)
        return calendar
    }
    
     func unitTitle(_ unit: NSCalendar.Unit, value: Int = 1, locale: Locale? = nil) -> String {
        let emptyString = String()
        let date = Date()
        let component = getComponent(from: unit)
        guard let sinceUnits = self.date(byAdding: component, value: value, to: date) else {
            return emptyString
        }

        let timeInterval = sinceUnits.timeIntervalSince(date)
        let formatter = DateComponentsFormatter()

        formatter.calendar = self
        formatter.allowedUnits = [unit]
        formatter.unitsStyle = .full
        guard let string = formatter.string(from: timeInterval) else {
            return emptyString
        }

        return string.replacingOccurrences(of: String(value), with: emptyString).trimmingCharacters(in: .whitespaces).capitalized
    }
    
    // swiftlint:disable:next cyclomatic_complexity
    private func getComponent(from unit: NSCalendar.Unit) -> Component {
        let component: Component

        switch unit {
        case .era:
            component = .era
        case .year:
            component = .year
        case .month:
            component = .month
        case .day:
            component = .day
        case .hour:
            component = .hour
        case .minute:
            component = .minute
        case .second:
            component = .second
        case .weekday:
            component = .weekday
        case .weekdayOrdinal:
            component = .weekdayOrdinal
        case .quarter:
            component = .quarter
        case .weekOfMonth:
            component = .weekOfMonth
        case .weekOfYear:
            component = .weekOfYear
        case .yearForWeekOfYear:
            component = .yearForWeekOfYear
        case .nanosecond:
            component = .nanosecond
        case .calendar:
            component = .calendar
        case .timeZone:
            component = .timeZone
        default:
            component = .calendar
        }
        return component
    }
}

And the use is:

Calendar.localized.unitTitle(.day)
Calendar.localized.unitTitle(.weekofMonth)
Calendcar.localized.unitTitle(.month)
  • This breaks on Sep 1 2022 for some reason:

    po formatter.string(from: 30 * 24 * 3600) // returns "0 months"
Add a Comment

Just add 3 days to timeInterval if the unit is month to accommodate varying month lengths.

        var timeInterval = sinceUnits.timeIntervalSince(date)
        if unit == .month {
            timeInterval = timeInterval.advanced(by: .days(3))
        }