Swift Package localization always using English Localization

Hey!

I'm developing a Swift Package which has localized strings in a Resource Folder, exactly as described in Apple Developer Documentation.

Since the update to Xcode 12.5, NSLocalizedString always returns the en Localization, eventhough the device language is e.g. German.

My Package.swift has a defaultLocalization: "de", entry,
and my Folder structure looks like this:

image

I access the strings like this:

static let yes = NSLocalizedString("Yes", tableName: nil, bundle: Bundle.module, value: "", comment: "")
print(yes) // prints: Yes

I don't understand what changed so that this isn't working anymore. Apple's Docs above are straightforward and I double checked their and my implementation.

Maybe someone has an idea here.
Cheers!

1 Like

Update:
I stumbled across Swift package manager localization - Stack Overflow.

At the bottom it suggests setting CFBundleAllowMixedLocalizations to YES in the host app's info.plist. This solves the problem, now it returns German strings when I set the schemes language to German.

Very interesting, what has changed here?

1 Like

The quote on StackOverflow came from here on the forums, where the context includes a broader explanation:

You have not described how the host application is set up, so I have to infer some details, but what you describe of Xcode 12.5 sounds like the intended behaviour. It also matches my memory of how it worked before, even going back all the way to before Swift existed. Is it possible something else changed and the attribution of the difference to an Xcode update is mistaken?

@SDGGiesbrecht Thanks for linking the full explanation.

I'm still wondering why it worked before. I distributed this app to others and localization worked for them (that is before I made a build in Xcode 12.5). The host app did not have any special setup, literally the Xcode defaults all the way through, few lines of code, no changes in the Info.plist or anything.

This rises the question of how I should advise people that implement this Swift Package regarding localization:

  1. Will a "default" app like I described above (no special settings) always fallback to English? What determines the default locale the implemented Swift Package will use?
  2. Is it wise to advise the implementing App Developer to always set CFBundleAllowMixedLocalizations to YES in any case?

By default, the application will use the best of its own matches for the user’s localization. Then it will attempt to use that same localization from all of its dependency packages.

So a brand new application from the template with only en resources of its own will also only use en from your package. (Sometimes that is what the client wants.)

If instead the client wants to use de, es, fr or it from your package even in an application that only has en resources of its own, then that client should set CFBundleAllowMixedLocalizations.

This strategy may cause mayhem if individually localized strings are being interpolated into each other across module boundaries. You can wind up with things like “Today is Montag, le 1er décembre.” That caveat is probably why Apple refrained from making it the default. But as long as you steer clear of that, most users will be glad to have the menu bar, file system errors and the like in their own language even though a game script may only be in English. I always use it.

It sounds like that developer finds the CFBundleAllowMixedLocalizations style better too, although it is entirely up to that developer and not something you can control as a package vendor.

That puzzles me too if it really did behave differently before. But until I see a reproducible case, I suspect that the client must have done something else different without realizing it. For example, when they remember it pulling de from your package, it might have been a project that also had de resources of its own, and now they are looking at a different, fresh project that doesn’t have any de resources yet. Or maybe they set en in a scheme for a testing run and what they observe is actually responding to a launch argument override instead of resolving like it would in a real install.

Ultimately, if you are using Foundation’s NSLocalizedString, then it is macOS that is selecting the resources for each bundle according to the various Info.plist files involved. Except what you see in the property lists, nothing is controlled by Xcode or compiled into the application. So the Xcode version cannot make any difference. If something did change, it must have happened with an operating system update instead. (But each style is used by some of the applications I work with every day and my language settings are wild enough that the difference is immediately obvious. With daily evidence of both styles behaving consistently over the course of years, the idea that something changed without my noticing seems fishy to me.)

4 Likes

@SDGGiesbrecht Thanks for the thorough explanation. I will lay out the pros and cons of that setting then as well.

It happened to me as well. It turns out that the problem is my main app had no localization files. Although configured to localize multiple languages. But all the languages had 0 localization files.

I solved this by adding an InfoPlist.strings with CFBundleDisplayName to all languages the app support.

1 Like