swiftuichartsplottablelocalizedstringkey

How to localize the values for the x- and y-axis in SwiftUI-charts?


I have an app localizable in 2 languages. In SwiftUI-charts I get errors using LocalizedStringKey. Using String in stead of LocalizedStringKey works, but then the x- and y-axis values are not localized. Is there a way to localize these axis-values? Do I have to use LocalizedStringKey, if so how can I make this plottable?

My shortend code:

import SwiftUI
import Charts

enum PickerQuestions: LocalizedStringKey, CaseIterable { //with String the charts draws but no localization
    case Q1  = "first question"
    case Q2  = "second question"
    case Q3  = "third question"
}

enum PickerCategory: LocalizedStringKey, Codable, CaseIterable { //idem
    case picker1 = "not"
    case picker2 = "a little"
    case picker3 = "medium"
    case picker4 = "very"
}

class PickerDataForBarChart: Identifiable {
    let id = UUID()
    var question: PickerQuestions
    var category: PickerCategory
    var percentage: Double
    
    init(question: PickerQuestions, category: PickerCategory, percentage: Double) {
        self.question = question
        self.category = category
        self.percentage = percentage
    }
}

//stubdata
var chartDataStub: [PickerDataForBarChart] = [
    .init(question: .Q1, category: .picker1, percentage: 50),
    .init(question: .Q2, category: .picker2, percentage: 75),
    .init(question: .Q3, category: .picker1, percentage: 25),
]

//the barchart
Chart(chartDataStub) {spec in
    BarMark(
        x: .value("Number", spec.percentage),
        y: .value("Question", spec.question.rawValue)  //error: Initializer 'init(x:y:width:height:stacking:)' requires that 'LocalizedStringKey' conform to 'Plottable'
    )
    .foregroundStyle(by: .value("Category", spec.category.rawValue)) //error: Instance method 'foregroundStyle(by:)' requires that 'LocalizedStringKey' conform to 'Plottable'
}
.chartForegroundStyleScale(         //foreGroundStyle-array and scale-array have to be exactly the same!
    domain: [PickerCategory.picker1.rawValue, PickerCategory.picker2.rawValue, PickerCategory.picker3.rawValue, PickerCategory.picker4.rawValue],                   //error: Referencing instance method 'chartForegroundStyleScale(domain:range:type:)' on 'Array' requires that 'LocalizedStringKey' conform to 'Plottable'
    range: [Color.blue, Color.red, Color.yellow, Color.gray]
)
.frame(height: 350)

PS I use a workaround by checking for the device language setting and conditionally using a string-array for each language, but there must be a better way (I hope). Someone an idea?


Solution

  • You could change your enums to have raw type String. Then add a computed property that returns the localized version of the raw value as per the answer to How to change LocalizedStringKey to String in SwiftUI.

    For example, in the case of the enum PickerQuestions:

    enum PickerQuestions: String, CaseIterable {
        case Q1  = "first question"
        case Q2  = "second question"
        case Q3  = "third question"
    
        var localized: String {
            let localizedKey = String.LocalizationValue(stringLiteral: rawValue)
            return String(localized: localizedKey)
        }
    }
    

    You can then pass .localized to the chart components, instead of .rawValue:

    BarMark(
        x: .value("Number", spec.percentage),
        y: .value("Question", spec.question.localized)
    )
    .foregroundStyle(by: .value("Category", spec.category.localized))
    

    With these translations:

    "first question" = "Question One";
    "second question" = "Question Two";
    "third question" = "Question Three";
    "not" = "Nope";
    "a little" = "Possibly";
    "medium" = "Maybe, maybe not";
    "very" = "Yes";
    

    ...it works like this:

    Screenshot