I have hundreds of dynamic strings, i.e. one or more variables exist within these string that are dynamically generated in code. For example, this is a string with anything in <> representing a variable whose value is unknown until the code is executed:
<Mostly cloudy> with to <25°> by <2pm>
As these strings will be localised, ideally I’d depend on a .strings file to store them. Here’s the above example if I was to define it there:
/*
One condition throughout an hourly range
e.g. <Mostly cloudy> with <temperatures> <rising> to <25°> by <2pm>.
Parameters:
1- weather
2- measurement point (default=temperature)
3- measurement trajectory (upwards or downwards)
4- measurement value
5- time above value is reached
*/
"hourSeries_const" = "%@ with %@ %@ to %@ by %@.";
Thanks to https://stackoverflow.com/a/56445894/698971, I created a String extension that returns the localised string with its arguments passed in:
/// Fetches a localised String Arguments
///
/// - Parameter arguments: parameters to be added in a string
/// - Returns: localized string
public func localized(with arguments: [CVarArg]) -> String {
return String(format: self.localized, locale: nil, arguments: arguments)
}
And getting the final string for the UI is as simple as calling:
let a = "hourSeries_const".localized(with: ["Mostly cloudy","temperatures","rising","24°","2pm"])
But this isn’t perfect for a couple of reasons. The string in the .strings file won’t be reader-friendly. Comments are essential to understand what each variable represents. And then imagine a situation in which the variable order needs to be rearranged so that the string reads naturally in a language. This needs to be tracked somehow, and then I have to intervene in code too to ensure the order I pass in the arguments is changed accordingly.
The alternative I thought of that may partially address this (but has its own issues – more on that later) is to move the strings into code. For example, have a func:
func hourSeries_const(weather:String, dataPoint:String, valueDirection:String, valueHighlight:String, highlightedValueTime:String) -> String {
return "\(weather) with \(dataPoint) \(valueDirection) to \(valueHighlight) by \(highlightedValueTime)."
}
But with several languages to support, I’ll need to have a switch
to choose between the different languages. This isn’t ideal as I was planning to send each translator their language file(s) to work with, i.e. it should only include their language’s strings. I could work around by adding a selector function that calls the selected language’s function:
func hourSeries_const(...) -> String {
switch language {
case "en": return hourSeries_const_en(...)
case "de": return hourSeries_const_de(...)
}
}
This is not the end of the world, but it does mean every time I add a new language, I’ll need to add a new case for every one of these functions pointing to the language’s corresponding function for the string.
Is there an option that has the convenience of depending on .strings files but that offers the readability of descriptive variable names in strings for translators to work with?