SwiftUI - Localization of a dynamic Text
Asked Answered
A

5

5

I am struggling with the locilization of some of my TextFields. Usually a "normal" localization of a Text() or TextField() works without any problem in my App if the text I want to translate is hardcoded like this:

Text("English Text")

I translate it in my Localizable.strings like this:

"English Text" = "German Text";

Now I want to translate TextFields which are more dynamic, but where I know each possible entry:

                TextField("New note" + (refresh ? "" : " "),text: $newToDo, onCommit: {
                    self.addToDo()
                    self.refresh.toggle()
                })

(The refresh is necessary because of a SwiftUI bug sometimes not showing the placeholder-text again.)

Another example would be:

    func dayWord() -> String {
        let dateFormatter = DateFormatter()
        dateFormatter.timeZone = TimeZone.current
        dateFormatter.locale = Locale(identifier: "de_DE")
        dateFormatter.dateFormat = "EEEE"
        return dateFormatter.string(from: self)
    }

    var day: String {
        return data.date.dateFromMilliseconds().dayWord()
    }

   Text(day.prefix(2))

The Text(day.prefix(2)) has only seven possible states, but I don't know what to write as a key in my Localizable.strings.

Alimentary answered 8/5, 2020 at 16:53 Comment(0)
A
12

Use NSLocalizedString, like

TextField(NSLocalizedString("New note", comment: "") + (refresh ? "" : " "), ...
Aludel answered 8/5, 2020 at 17:4 Comment(0)
W
6

According to SwiftUI convention, the Text() label is automatically localized only when it has a string "literal". When the text is a variable, you need to use LocalizedStringKey(). Other localizable texts inside Button() or TextField() also require LocalizedStringKey(). So you need to change this to:

Text(LocalizedStringKey(String(day.prefix(2))))

This converts day.prefix(2) into a String, because it is actually a substring, and then calls LocalizedStringKey() to make it localizable. In your Localizable.strings file you could add all 7 possibilities.

"Mo" = "Mo";
"Di" = "Tu"; 
//etc.

but why would you? Instead, use:

dateFormatter.locale = Locale(identifier: Locale.preferredLanguages.first)
...
Text(day.prefix(2)) 

to determine the user's current language and display that. Apple returns the text in the proper language, so this text doesn't need to be localized any further.

TextField() does need localization using LocalizedStringKey():

TextField(LocalizedStringKey("New note") + (refresh ? "" : " "),text: $newToDo, onCommit: {
                    self.addToDo()
                    self.refresh.toggle()
                })

As Asperi points out, for "New note" the same can be accomplished using NSLocalizedString(), which might be better depending on how you like to work. The benefits are: easily adding a comment for the translator, and automatic export into the xliff when you choose Editor -> Export for localization…

By contrast, SwiftUI's LocalizedStringKey() requires you to manually add strings to the Localizable.strings file. For your day.prefix(2) example, I think it would make more sense to get the user's preferred language and display the localized date directly.

Wellbeloved answered 12/1, 2021 at 20:52 Comment(0)
V
3

SwiftUI Text will only localize literal strings, strings defined in double quotes. You have to either create localized key or retrieve localized string.

Examples

Simple localization with string literal key.

Text("Label")

If you construct the label, you can use LocalizedStringKey function to localize your computed label.

let key = "Label"
let localizedKey = LocalizedStringKey(key)
Text(localizedKey)

You can also get the localized string using NSLocalizedString.

let localizedString = NSLocalizedString("Label", comment: "")
Text(localizedString)

String Interpolation

It's also possible to have localized strings accept arguments (@, lld, and lf) through String Interpolation. For example you could have the following localizations in your project Localizable.strings file:

"Name %@" = "Name %@";
"Number %lld" = "%lld is the number";

And you can use it like this:

Text("Name \(object)")
Text("Number \(number)")

Xcode Search

If you want to search for calls of Text that aren't done with literal strings in your projects. You can use Xcode Find Regular Expression feature. View > Navigators > Find or Cmd-4 keyboard shortcut. Change the search type just above the search field to Regular Expression.

Use the following regular expression: Text\([^\"]

Vervain answered 26/9, 2022 at 14:20 Comment(2)
I was exactly looking for this. But Text("Label \(object)") doesn't work.Soothsay
Sorry that should be Text("Name \(object)") to match the localization string in Localizable.strings.Vervain
H
0

As @Localizer stated above XCode doesn’t localise non-literal string values.

You can write a String extension to simplify generating a localised string as follows:

extension String {
    func localised() -> LocalizedStringKey {
        return LocalizedStringKey(self)
    }
}

For example, you can use the extension as follows:

var test = "test"       //  localised as "This is a test"

//  blah blah blah

Text(test.localised())

Not as convenient as it should be, but not as bad as it might have been.

Heida answered 9/10 at 7:14 Comment(0)
G
0

You can use String(localized:) initializer:

let str = String(localized: "New note" + (refresh ? "" : " ")) 
TextField(str, ...)
Gladsome answered 10/10 at 1:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.