New iOS String Initializer can't get correct localized String for specific locale

I tried to build LocalizedKeyString using String's new Initializer.

String(localized: "hello",locale: locale)

When I change the language setting of the device, everything works as expected.

However, when I try to get the string in a specific language using the specified Locale, I can only ever get the string in .current.

String(localized: "hello",locale: Locale(identifier: "zh-cn"))

    func getString(locale:Locale) -> String{

         String(localized: "hello",locale: locale)

    }

If you change the display language of the project (or the language setting of the device), the text in Text is always displayed correctly. Text(getString(locale:.current)) However, the code in onAppear print(getString(locale:Locale(identifier: "zh-cn"))) It only displays the same content as Text, but not the specified Chinese.

Is it my fault that the new Initializer The understanding is not correct, or the bug of String

init(localized keyAndValue: String.LocalizationValue, table: String? = nil, bundle: Bundle? = nil, locale: Locale = .current, comment: StaticString? = nil)

FB number: FB9675845

  • Thanks a lot for filing a feedback and attaching a demo project! Your feedback has been relayed

Add a Comment

Accepted Reply

Hi,

The locale parameter of String(localized:locale:) helps specifying a locale for interpolated values in the string.

For instance, this would display a date in the English (UK) format:

String(localized: "Next Meeting: \(date, format: Date.FormatStyle(date: .numeric))", locale: Locale(identifier: "en_GB"))
// Localizable string is:    Next Meeting: %@
// Returns:                  Next Meeting: 25/12/2020

 

Any reason why you'd need to load strings in a specific locale instead of the current user’s locale? Users can change their device language, but also for each app.

  • Thanks for your reply, it helped me to figure out the use of locale, it seems I had a problem understanding locale in String before. I used the new Formatter API ParseableFormatStyle and tried to let it be used to specify the text in the output by the following code

    let colorString = UIColorFormatStyle().mark().locale(Locale(identifier: "zh-cn")).format(UIColor.blue)

    Looks like I need to tweak the string handling a bit.

    https://www.fatbobman.com/posts/newFormatter/

  • Where is the documentation for String(localized:? One month after iOS 15 has been released, the official documentation still declares No overview available and treats it as beta.

Add a Comment

Replies

You could try to specify the Bundle:

extension String {
    func localized(for lanCode: String) -> String {
        guard
            let bundlePath = Bundle.main.path(forResource: lanCode, ofType: "lproj"),
            let bundle = Bundle(path: bundlePath)
        else { return "" }
        
        return NSLocalizedString(
            self,
            bundle: bundle,
            value: " ",
            comment: ""
        )
    }
}

And call:

"hello".localized(for: "cn")

CRedit: https://stackoverflow.com/questions/28634428/ios-get-localized-version-of-a-string-for-a-specific-language

  • I wouldn’t recommend wrapping String(localized:) calls because it removes comments for translators (super useful to get the context like (to) Book vs (a) Book), and Xcode › Products › Export Localizations wouldn’t be able to add all declared strings automatically to .strings files for you.

Add a Comment

Hi,

The locale parameter of String(localized:locale:) helps specifying a locale for interpolated values in the string.

For instance, this would display a date in the English (UK) format:

String(localized: "Next Meeting: \(date, format: Date.FormatStyle(date: .numeric))", locale: Locale(identifier: "en_GB"))
// Localizable string is:    Next Meeting: %@
// Returns:                  Next Meeting: 25/12/2020

 

Any reason why you'd need to load strings in a specific locale instead of the current user’s locale? Users can change their device language, but also for each app.

  • Thanks for your reply, it helped me to figure out the use of locale, it seems I had a problem understanding locale in String before. I used the new Formatter API ParseableFormatStyle and tried to let it be used to specify the text in the output by the following code

    let colorString = UIColorFormatStyle().mark().locale(Locale(identifier: "zh-cn")).format(UIColor.blue)

    Looks like I need to tweak the string handling a bit.

    https://www.fatbobman.com/posts/newFormatter/

  • Where is the documentation for String(localized:? One month after iOS 15 has been released, the official documentation still declares No overview available and treats it as beta.

Add a Comment

treats it as beta.

You’re seeing the beta tag because this API is also new in macOS 12 and that system is still in the process of being released; it’ll go away when that’s fully complete.

still declares No overview available

Indeed. I encourage you to file a bug about that.

Please post your bug number, just for the record.

Do you have a question about its behaviour? It seems pretty straightforward to me but perhaps I’ve missed some subtlety?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

  • After the previous explanation, I have no more questions about the current behavior of this method. locale is used here to interpolate values for strings as a guide.

    Since I've been hoping Apple would provide an API to get the localizedString for a specific locale, for now I can only try to get it via Bundle directly.

Add a Comment

As of macOS 14 and iOS 17 String(localized:defaultValue:table:bundle:locale:comment:) still has no documentation and the original poster's assumption that specifying a locale would retrieve the string localized for the locale specified seems, to me at least, the most plausible assumption of how this API works.

Additionally, I do have a use-case where being able to retrieve particular strings, including complicated interpolated ones with pluralization, for a locale other than the user's current one easily would be beneficial:

I'm building a simple language learning app in which the user can switch the language being studied. The languages being studied are independent of the UI locale. Accordingly I keep the localized UI strings and the content strings in a separate tables. The UI is localized via SwiftUI and occasionally simple String(localized:) calls. I was hoping I could get at the language learning content with the String(localized:defaultValue:table:bundle:locale:comment:) initializer - but no.

In the past I have implemented my own content localization in bigger apps that needed this kind of separation but this seems excessive here.

Maybe the ship on this API has sailed. If not I would be willing to go into detail and file a bug.

Did you find a solution for how to force return a string localized for a specific language regardless to the system language when you use String Catalog ? check out my question here: https://stackoverflow.com/questions/77544177/xcode-15s-string-catalog-always-return-system-language-translations?noredirect=1#comment136706101_77544177

somebody in this thread is asking for the case in which we need to ignore system language and localize strings for a specific language, we offer the user to choose a language from a list of languages in the app's settings page while their own iPhone's system language is English.

Use case: I'm using the following String extension to change the language/locale in the Swift UI environment, I then pass the locale through to the function to get a localised string. I've made a little Swift UI preview container with an editor panel so we can toggle values in the environment (dark mode, layout direction, locale/language etc) on previews and also within a playbook app - without needing to select a drop-down in the Xcode scheme editor and restarting the app, or needing to change the device settings (although we have to do that when running on iOS 15.)

    func localized(_ locale: Locale, bundle: Bundle = .module) -> String {
        if #available(iOS 16, *) {
            String(
                localized: LocalizedStringResource(
                    String.LocalizationValue(self),
                    locale: locale,
                    bundle: .atURL(bundle.bundleURL)
                )
            )
        } else {
            String(localized: String.LocalizationValue(self), bundle: bundle)
        }
    }