UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

getting localization translations in code

Forums > SwiftUI

I'm learning about localization using a Strings file. I'm past the basics and am now learning about using keys that contain argument placeholders. I want the argument values to be translated too. For example, for English I have

"Annie %@ %@" = "The %@ will come out %@.";

and for French I have:

"Annie %@ %@" = "Le %@ sortira %@.";
"sun" = "soleil";
"tomorrow" = "demain";

In my code I want to translate the keys "sun" and "tomorrow" and use the results to pass for the "Annie" translation like this:

let sun = String(localized: "sun")
let tomorrow = String(localized: "tomorrow")
Text("Annie \(sun) \(tomorrow)")

I don't get the strings I expect from String(localized:). Is there another way that I can get the translated value of a key in the current locale?

2      

Interestingly Text("sun") performs the correct translation based on the current locale, but String(localized: "sun") just returns "sun" regardless of the locale.

2      

I've narrowed this down a bit. I can use either of these approaches to get a translation in code:

let translation = Bundle.main.localizedString(
    forKey: key,
    value: key, // translation defaults to the key
    table: nil // defaults to "Localizable" which uses the file Localizable.strings
)

// This is a shortcut for the above where table is nil
// which causes it to use `Localizable` for the table.
let translation = NSLocalizedString(key, comment: "")

These get a translation based on the locale selected in the Settings app. However, neither of these honor the locale specified with the environment view modifier like this:

.environment(\.locale, .init(identifier: locale))

The same issue exists with retrieving localized images from an "Image Set". I wonder if this is considered a bug in SwiftUI.

2      

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Okay, from my understanding

Text("Annie \(sun) \(tomorrow)")

matches "Annie %@ %@"

but isn't translated in the process.

Personally I use an extension for String:

/**
A NSLocalizedString for this String where a key was hopefully added to Localizable.strings file.
*/
var localized: String {
  return NSLocalizedString(self, comment: "")
}

func localized(_ arguments: CVarArg...) -> String {
  return String(format: self.localized, arguments: arguments)
}

Then try

Text("Annie \(sun.localized) \(tomorrow.localized)")

2      

@Hatsushira I tried your suggestions. I added this:

extension String {
    var localized: String {
        return NSLocalizedString(self, comment: "")
    }

    func localized(_ arguments: CVarArg...) -> String {
        return String(format: localized, arguments: arguments)
    }
}

and I changed my code to this where the comments indicate the output when the locale is "fr-FR":

Text("sun") // soleil
Text("tomorrow") // demain
Text("Annie \("sun".localized) \("tomorrow".localized)") // Le sun sortira tomorrow.

I get the correct translations from Text("sun") and Text("tomorow"), but not for the Annie text. I expected to get "Le soleil sortina demain."

Also see how I'm using "sun".localized instead of sun.localized because I don't think I need to have a sun variable.

Do you see anything I'm doing wrong?

2      

As "Annie %@ %@" is the complete String what about

Text("Annie \(sun) \(tomorrow)".localized)

I don't know how the localization internally works. So I'm just guessing here.

After reading your initial post again I'm guessing you don't have sun and tomorrow in your English strings file. You should have all keys in all translation files. Perhaps this could be the problem. So add "sun" = "sun" and "tomorrow"="tomorrow" in your English strings file.

I don't know if you know about "Edit Scheme > Options" and check the "Show non-localized strings" box. Then in your debug sessions all missing strings are shown in capital letters.

2      

@Hatsushira You said "You should have all keys in all translation files." I feel 99% certain this is not true. It's my understanding that when a translation is missing it just uses the key. It does that for me. That's a big benefit of making the keys be the English text you want to see. But I did try adding the following in the English translations and it didn't help.

"sun" = "sun";
"tomorrow" = "tomorrow";

I was not aware of the "Show non-localized strings" option. Thanks for telling me about that! Interestingly that causes lots of errors to appear in the debug console because it attempts to translate every string passed to the Text view even if you don't intend for it to be translated.

Going back to my earlier message, the key thing to notice is that Text("sun") is correctly translated in the all the languages I am supporting, but Text("Annie \("sun".localized) \("tomorrow".localized)") is not. It does translate parts of the target text such as "The" and "will come out", but not "sun" and "tomorrow". So it seems to me the issue is that "sun".localized does not honor the value of the environment variable locale. It seems to only consider the default language of the device.

2      

Do your translations work on a real device? Perhaps this is one of the quirks in the Simulator/Preview and we try to fix something we're just not able to.

I can guarantee you that .localized works because I use it in my apps all the time.

2      

@Hatsushira I get the same results on my iPhone.

.localized does work for me, but it only considers the top language in the list inside Settings app under General ... Language & Region ... PREFERRED LANGUAGES. It is not affected by changing the value of the locale environment variable like this:

        .environment(\.locale, .init(identifier: locale))

I have a segmented Picker in my app that changes the value of locale.

But changing the locale environment variable does affect many other translations such as those performed automatically by the Text view.

In my app I want to allow the user to select a language without changing their preferred language in the Settings app. So the issue I'm facing is that .localized doesn't give me different results when I change the locale environment variable.

2      

Okay, what I understand is that you want to change the locale dynamically inside a view and use the environment modifier for it.

It could be this simply doesn't work. From the documentation

The environment(::) modifier affects the given view, as well as that view’s descendant views. It has no effect outside the view hierarchy on which you call it.

I don't read from this that it should not but I really don't know.

2      

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.