In the previous article, we saw why Text, or more accurately LocalizedStringKey, can accept Image and Date, but not Bool or custom Person types. In this next article, let’s look at specific ways to make Text support other types.
Custom interpolation for LocalizedStringKey
If we just want Text to accept true or false directly, we can simply overload Bool with appendInterpolation.
extension LocalizedStringKey.StringInterpolation {
mutating func appendInterpolation(_ value: Bool) {
appendLiteral(value.description)
}
}
Copy the code
This way, we can avoid compilation errors:
Text("3 == 3 is (true)")
Copy the code
For Person, we can also add appendInterpolation to add the Person version of the interpolation method directly to LocalizedStringKey:
extension LocalizedStringKey.StringInterpolation {
mutating func appendInterpolation(_ person: Person, isFriend: Bool) {
appendLiteral(person.title(isFriend: isFriend))
}
}
Copy the code
The above code for LocalizedStringKey. StringInterpolation added Bool and Person’s support, but it actually destroys the localization support. This may not have the desired effect and may even lead to unexpected behavior. Use with caution until fully understood. We’ll talk more about this topic later in this article in the section on localization.
LocalizedStringKey
Find localized values by key
We’ve spent a lot of time just messing around with LocalizedStringKey and its interpolation. In retrospect, it seems that we haven’t paid any attention to what LocalizedStringKey itself is. As its name suggests, LocalizedStringKey is the type SwiftUI uses to find the key in Localization. Strings. Try printing the simplest LocalizedStringKey:
let key1: LocalizedStringKey = "Hello World"
print(key1)
// LocalizedStringKey(
// key: "Hello World",
// hasFormatting: false,
// arguments: []
// )
Copy the code
It looks for the string corresponding to the “Hello World” key. For example, in a localized string file there is a definition like this:
// Localization. Strings "Hello World"=" Hello World";Copy the code
When used, SwiftUI will select the result based on the value of localizedStringKey. key:
Text("Hello World")
Text("Hello World")
.environment(.locale, Locale(identifier: "zh-Hans"))
Copy the code
Interpolate the key of LocalizedStringKey
So now the interesting part is, what’s the key of this LocalizedStringKey?
let name = "onevcat"
let key2: LocalizedStringKey = "I am (name)"
Copy the code
Is it “I am onevcat”? If so, how will the string be localized? If not, what would the key be?
Print it out and see:
print(key2)
// LocalizedStringKey(
// key: "I am %@",
// hasFormatting: true,
// arguments: [
// SwiftUI.LocalizedStringKey.FormatArgument(
// ...storage: Storage.value("onevcat", nil)
// )
// ]
// )
Copy the code
The key is not a fixed “I am onevcat”, but a String formatter: “I am %@”. For readers familiar with String Format, this will be familiar: Name is passed as a variable into String Format, replacing the %@ placeholder for the object. So, when localizing this string, the key we need to specify is “I am %@”. Of course, this LocalizedStringKey could also correspond to any other input:
// Localization. Strings "I am %@"=" I am %@"; Swift Text("I am ("onevcat")") // swift Text("I am ("onevcat")") // Swift Text("I am ("onevcat")") // Swift Text("I am ("onevcat")"Copy the code
For Image interpolation, the situation is similar: the interpolated portion of the Image is converted to %@ to meet the need for localized keys:
let key3: LocalizedStringKey = "Hello (Image(systemName: "globe"))" print(key3) // LocalizedStringKey( // key: "Hello %@", // ... / / / / Localization strings / / "Hello % @" = "Hello, % @"; Text("Hello (Image(systemName: "globe"))") Text("Hello (Image(systemName: "globe"))") .environment(.locale, Locale(identifier: "zh-Hans"))Copy the code
It is worth noting that the formatting symbol for the interpolation of Image is %@, which is the same symbol for the interpolation of String or any other object. That is, the same localized string is found in the following two interpolation methods:
Text("Hello ("onevcat")")
.environment(.locale, Locale(identifier: "zh-Hans"))
Text("Hello (Image(systemName: "globe"))")
.environment(.locale, Locale(identifier: "zh-Hans"))
Copy the code
Other types of interpolation formatting
As you might have guessed, LocalizedStringKey supports other types of formatting in addition to %@. For example, when interpolating ints, the key argument is converted to % LLD; For Double, convert to %lf and so on:
let key4: LocalizedStringKey = "Hello (1))" // LocalizedStringKey(key: "Hello %lld) let key5: LocalizedStringKey = "Hello (1.0))"Copy the code
Using Hello % LLD or Hello %lf will not match the previous Hello %@ in the localization file.
A more reasonable appendInterpolation implementation
Avoid appendLiteral
Now let’s get back to the interpolation of Bool and Person. At the beginning of this article, we added two interpolation methods to make LocalizedStringKey accept the interpolation of Bool and Person:
mutating func appendInterpolation(_ value: Bool) {
appendLiteral(value.description)
}
mutating func appendInterpolation(_ person: Person, isFriend: Bool) {
appendLiteral(person.title(isFriend: isFriend))
}
Copy the code
In both methods we use appendLiteral to add String directly to the key, so we get a full LocalizedStringKey with no arguments, which in most cases is not what we want:
let key6: LocalizedStringKey = "3 == 3 is (true)"
// LocalizedStringKey(key: "3 == 3 is true", ...)
let person = Person(name: "Geralt", place: "Rivia", nickName: "White Wolf")
let key7: LocalizedStringKey = "Hi, (person, isFriend: false)"
// LocalizedStringKey(key: "Hi, Geralt of Rivia", ...)
Copy the code
When implementing the new appendInterpolation, respect the insertion parameters and forward the actual insertion action to the existing appendInterpolation implementation, leaving the LocalizedStringKey type to handle key composition and formatting characters. It would have been more reasonable and generic:
mutating func appendInterpolation(_ value: Bool) {
appendInterpolation(value.description)
}
mutating func appendInterpolation(_ person: Person, isFriend: Bool) {
appendInterpolation(person.title(isFriend: isFriend))
}
let key6: LocalizedStringKey = "3 == 3 is (true)"
// LocalizedStringKey(key: "3 == 3 is %@", ...)
let key7: LocalizedStringKey = "Hi, (person, isFriend: false)"
// LocalizedStringKey(key: "Hi, %@", ...)
Copy the code
Add styles for Text
With interpolation using the LocalizedStringKey parameter and the existing appendInterpolation, you can write some handy methods. For example, we can add a set of string formatting methods to make Text styling easier:
extension LocalizedStringKey.StringInterpolation {
mutating func appendInterpolation(bold value: LocalizedStringKey){
appendInterpolation(Text(value).bold())
}
mutating func appendInterpolation(underline value: LocalizedStringKey){
appendInterpolation(Text(value).underline())
}
mutating func appendInterpolation(italic value: LocalizedStringKey) {
appendInterpolation(Text(value).italic())
}
mutating func appendInterpolation(_ value: LocalizedStringKey, color: Color?) {
appendInterpolation(Text(value).foregroundColor(color))
}
}
Copy the code
Text("A \(bold: "wonderful") serenity \(italic: "has taken") \("possession", color: .red) of my \(underline: "entire soul").")
Copy the code
The following results can be obtained:
The corresponding key is “A % @serenity % @% @of my %@.” . Interpolation is considered a placeholder for parameters. In some cases it might not be the result you want, but the localization of the view string is also an annoying thing in UIKit. Compared to UIKit, SwiftUI’s progress in this area is obvious.
about_FormatSpecifiable
Finally let’s look at the _FormatSpecifiable problem. You may have noticed, the LocalizedStringKey, built. There are two method involves _FormatSpecifiable StringInterpolation:
mutating func appendInterpolation<T>(_ value: T) where T : _FormatSpecifiable
mutating func appendInterpolation<T>(_ value: T, specifier: String) where T : _FormatSpecifiable
Copy the code
Specifies the placeholder format
Part of the basic type in Swift is the _FormatSpecifiable private protocol. This protocol helps LocalizedStringKey select an appropriate placeholder representation when concatenating keys, such as % LLD for Int, % LF for Double, and so on. When we use Int or Double for interpolation, the overloaded method above will be used:
Text("1.5 + 1.5 = (1.5 + 1.5)") LocalizedStringKey = "1.5 + 1.5 = (1.5 + 1.5)" print(key) // 1.5 + 1.5 = %lfCopy the code
The right side of the above Text equals sign will be rendered as %lf:
If you want to keep it to one decimal point, you can just use the version with the specifier parameter. When the key is generated, the specifier is passed in instead of the format that would have been used:
The Text (" 1.5 + 1.5 = (1.5 + 1.5, specifiers: "%. 1 lf") ") / / key: 1.5 + 1.5 = %. 1 lfCopy the code
Implementation for custom types_FormatSpecifiable
_FormatSpecifiable is relatively simple, though it is a private protocol:
protocol _FormatSpecifiable: Equatable {
associatedtype _Arg
var _arg: _Arg { get }
var _specifier: String { get }
}
Copy the code
Let _arg return the actual value to be interpolated, and let _specifier return the placeholder format. Int: _FormatSpecifiable () ¶
extension Int: _FormatSpecifiable {
var _arg: Int { self }
var _specifier: String { "%lld" }
}
Copy the code
For the Person we use multiple times in our example, we can use a similar trick to satisfy _FormatSpecifiable:
extension Person: _FormatSpecifiable {
var _arg: String { "(name) of (place)" }
var _specifier: String { "%@" }
}
Copy the code
This way, even if we don’t add the Person interpolation method to LocalizedStringKey, the compiler will select the _FormatSpecifiable interpolation method for us, adding the Person description to the final key.
conclusion
On the basis of the previous chapter, in this paper:
- We tried to extend it
LocalizedStringKey
The interpolation method allows it to be supportedBool
和Person
. LocalizedStringKey
The main task of interpolation is to automatically generate appropriate localized keys with parameters.- In an extension
LocalizedStringKey
Interpolation should be used whenever possibleappendInterpolation
To avoid parameter swallowing. - The interpolation format is made up of
_FormatSpecifiable
Sure. We can also interpolate by having custom types implement this protocol. - Download all kinds of technical documents of iOS
At this point, we should have an idea of why an Image can be interpolated in a Text, and everything that happens behind it.