iOS

【Forwarded】How stringsdict and N

2019-04-18  本文已影响0人  胡萝卜须摇头玩
Original url: How stringsdict and NSLocalizedString Works

When I wrote everything about iOS localization, I covered on plural support, but was brief.

This post I will explain the use of the powerful .stringsdict with a simple use, explaining the basics, then an advanced use. Lastly I will try to explain some magic under the hood.

Simple Case

It is easier to use an example and explain.

<?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>%d file(s) are selected</key>
    <dict>
        <key>NSStringLocalizedFormatKey</key>
        <string>%#@[email protected] selected</string>
        <key>num_files_are</key>
        <dict>
            <key>NSStringFormatSpecTypeKey</key>
            <string>NSStringPluralRuleType</string>
            <key>NSStringFormatValueTypeKey</key>
            <string>d</string>
            <key>zero</key>
            <string>No file is</string>
            <key>one</key>
            <string>A file is</string>
            <key>other</key>
            <string>%d files are</string>
        </dict>
    </dict>
</dict>
</plist>

The code to use:

let numberOfFiles = 1
let format = NSLocalizedString("%d file(s) are selected", comment: "")
let localizedString = String(format: format, numberOfFiles)

There are 2 distinct calls here:

  1. You need to get the “format” using the macro NSLocalizedString. In the code above, the variable format will be %#@[[email protected]](/cdn-cgi/l/email-protection) selected. Note: This is not yet localized, unlike the usual localization!
  2. Init String with this format and the actual arguments, and you will get the localized string.

Basics of the XML:

[图片上传失败...(image-620bea-1555568209760)]

Advanced Case

I like the example used in objc.io:

<?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>scope.%lu out of %lu runs</key>
    <dict>
        <key>NSStringLocalizedFormatKey</key>
        <string>%1$#@[email protected]</string>
        <key>lu_completed_runs</key>
        <dict>
            <key>NSStringFormatSpecTypeKey</key>
            <string>NSStringPluralRuleType</string>
            <key>NSStringFormatValueTypeKey</key>
            <string>lu</string>
            <key>zero</key>
            <string>No runs completed yet</string>
            <key>one</key>
            <string>One %2$#@[email protected]</string>
            <key>other</key>
            <string>%lu %2$#@[email protected]</string>
        </dict>
        <key>lu_total_runs</key>
        <dict>
            <key>NSStringFormatSpecTypeKey</key>
            <string>NSStringPluralRuleType</string>
            <key>NSStringFormatValueTypeKey</key>
            <string>lu</string>
            <key>one</key>
            <string>run completed</string>
            <key>other</key>
            <string>of %lu runs completed</string>
        </dict>
    </dict>
</dict>
</plist>

The localized string requires 2 arguments:

let completedRuns = 1
let totalRuns = 2
let format = NSLocalizedString("scope.%lu out of %lu runs", comment: "")
let str = String(format: format, completedRuns, totalRuns)

Let’s look at the output first.

Completed runs    Total Runs    Output
------------------------------------------------------------------
0                 0+            No runs completed yet
1                 1             One run completed
1                 2+            One of x runs completed
2+                2+            x of y runs completed

It is an advanced use because the output depends on both the arguments.

The biggest difference is that this time, there are 2 variables, each with their set of rules, and one variable refer to another!

[图片上传失败...(image-7c7029-1555568209760)]

There is some recursive lookup going on :)

The Puzzling String

Let’s look at the simple case again to explain something strange that goes on.

let format = NSLocalizedString("%d file(s) are selected", comment: "")
// format is "%#@[email protected] selected"
let str = String(format: format, 1)
// str is "1 file is selected"

If you have been using String(format:_:), you know it can format the string and replace with the arguments.

But it is hard to digest for the format %#@[[email protected]](/cdn-cgi/l/email-protection) selected, a mere string.. How does a mere string have access to the rules in stringsdict?

The string %#@[[email protected]](/cdn-cgi/l/email-protection) selected is not a mere string. It knows the rules, for a particular locale.

Try this and it will NOT work:

// let format = NSLocalizedString("%d file(s) are selected", comment: "")
let format = "%#@[email protected] selected"
let str = String(format: format, 1)

This proves that the macro NSLocalizedString, which return a String via Bundle.localizedString, is not a pure string.

I am puzzled and I hope someone can explain.

I can see NSString use CFStringCreateWithFormatAndArguments. In CFString, it mentions stringsDictConfig. It seems like the system refer to this dictionary to look up the rules and format the final string.

上一篇 下一篇

猜你喜欢

热点阅读