After solving my problem where I needed to pluralize based on second argument/variable, I have also found a solution to your problem without the need to change arguments' order or using proxy rule chaining. Thanks to the other posters for their answers and solutions which led me to experiments and better understanding of .stringsdict
.
I think that my case is close to yours, but is a bit easier and may help you understand better how string format and rules are applied, so let me show starting with my example.
My case
I need to construct a string like 1 of 1 item selected
or 1 of 5 items selected
:
let countWithSelectionFormat = NSLocalizedString("%ld of %ld item(s) selected", comment: "Number of items with selection")
let countString = String.localizedStringWithFormat(countWithSelectionFormat, selectedCount, totalCount)
String format %ld of %ld item(s) selected
is just a placeholder here, an alias. It is ignored in Localizable.strings
, because it has been found first in Localizable.stringsdict
. It is used only as a key to the rules in Localizable.stringsdict
, but is not actually used to construct a string:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>%ld of %ld item(s) selected</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%ld of %#@totalItems@ selected</string>
<key>totalItems</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>ld</string>
<key>one</key>
<string>%ld item</string>
<key>other</key>
<string>%ld items</string>
</dict>
</dict>
</dict>
</plist>
The actual string format is listed in NSStringLocalizedFormatKey
, it is %ld of %#@totalItems@ selected
. Here the first argument selectedCount
is passed through using %ld
(without making it variable and listing rules for it). The second argument totalCount
is parsed with %#@totalItems@
variable and its rules which return 1 item
or, for example, 5 items
, thus constructing correct strings.
If you need to change the order of arguments in output for some languages, you can use %2$#@totalItems@, %1$ld selected
as NSStringLocalizedFormatKey
.
You can also introduce second variable (and rules) if needed: %2$#@totalItems@, %1$#@selectedItems@
.
Your case
My Swift code is essentially same as yours in Objective-C:
let stringFormat = NSLocalizedString("On %@ there are up to %ld sun hours", comment: "")
let string = String.localizedStringWithFormat(stringFormat, dayString, sunHours)
Localizable.stringsdict
:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>On %@ there are up to %ld sun hours</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@day@%#@hours@</string>
<key>day</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>other</key>
<string></string>
</dict>
<key>hours</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>ld</string>
<key>zero</key>
<string>On %1$@ there are no sun hours</string>
<key>one</key>
<string>There is one sun hour on %1$@</string>
<key>other</key>
<string>On %1$@ there are up to %2$ld sun hours</string>
</dict>
</dict>
</dict>
</plist>
In the NSStringLocalizedFormatKey
I use 2 variables: %#@day@%#@hours@
.
The day
variable and rules are used to 'consume' the first argument dayString
, we don't need any output here (fixed at the start of the sentence). I have used format value type d
(as in %d
for an integer) here as we don't need and we can't parse a string to choose an applicable plural rule. And we use only the required other
rule, which returns an empty string. For the same reason there is no space between the day
and hours
variables in the string format.
The hours
variable captures the second argument sunHours
and its rules are in charge of constructing the actual output strings (whole sentences in this case). As I have to reference both arguments from inside the rules of second variable, I use %1$@
and %2$ld
to refer to dayString
and sunHours
arguments respectively. Thus you can also use your variables in any combination and order.
This gives the desired results:
String.localizedStringWithFormat(stringFormat, dayString, 0) // On Monday there are no sun hours
String.localizedStringWithFormat(stringFormat, dayString, 1) // There is one sun hour on Monday
String.localizedStringWithFormat(stringFormat, dayString, 5) // On Monday there are up to 5 sun hours
References:
Both examples were tested with Swift 5, Xcode 10.2.1, macOS 10.14.5, targeting macOS 10.12.