Swift package manager localization

I'm wondering, how localization works with the swift package. I saw the WWDC20/10169 video - Swift packages: Resources and localization . I'm trying to do the same things within my own project.

If I do same configuration (as on WWDC video) and add .environment(\.locale, Locale(identifier: "es")) to View preview, it works (but only when test inside sp).

The problems start when I try to use localized resources inside the actual app. The process is very simple - I just use a view with a localized string from the package inside the app. For test purposes I launch my app with a specific locale (scheme settings) - the result, I always get en translation.

My manifest:

let package = Package(
  name: "MyLibrary",
  defaultLocalization: "en",
  platforms: [
    .iOS(.v14),
  ],
  products: [
    .library(
      name: "MyLibrary",
      targets: ["MyLibrary"]),
  ],
  dependencies: [
  ],
  targets: [
    .target(
      name: "MyLibrary",
      dependencies: [],
      path: "Sources"
    ),
    .testTarget(
      name: "MyLibraryTests",
      dependencies: ["MyLibrary"]),
  ]
)

Structure of the package:

Structure of the package

resource_bundle_accessor is generated fine - so I have an access to Bundle.module , and indeed I can list all localizations as expected - en and es .

I also added supported languages into the project itself.
Supported languages

For generating localized string I want to use SwiftGen:

extension L10n {
  private static func tr(_ table: String, _ key: String, _ args: CVarArg...) -> String {
    let format = BundleToken.bundle.localizedString(forKey: key, value: nil, table: table)
    return String(format: format, locale: Locale.current, arguments: args)
  }
}

// swiftlint:disable convenience_type
private final class BundleToken {
  static let bundle: Bundle = {
    #if SWIFT_PACKAGE
    return Bundle.module
    #else
    return Bundle(for: BundleToken.self)
    #endif
  }()
}
// swiftlint:enable convenience_type

Alternatively, I tested

let text = NSLocalizedString("welcome", tableName: "Localizable", bundle: .module, value: "", comment: "")

or similar from Bundle - Bundle.module.localizedString method and from SwiftUI Text("welcome", bundle: .module) .

The result the same - it won't change the language - always I get dev language - en .

Here is a link for project.

So my question - what I'm doing incorrectly? Where is the error?

Having exactly the same issue. Read from Apple dev forums that the app using the framework should have the same localisations. My framework has three languages supported so I guess should add the same three localisations to the app using the framework?

As I show on screenshot - I already added same localization as one used in SP...

Ok so that won’t help either. Saves me trying that. Maybe time to ask this at Apple dev forum.

I found the reason for this issue - @andreas66 u was right - the app itself require localization, but the way I did is not enough, instead, we also should add CFBundleLocalizations in info plist with required localizations as an Array of strings. Even if in official doc says that is's support only few localizations, we can add additional one there (using same locale code as lproj folders) and everything will works.

<key>CFBundleLocalizations</key>
<array>
	<string>en</string>
	<string>es</string>
	<string>uk-UA</string>
</array>

An application can notify the system that it supports additional localizations through its information property list (Info.plist) file. To specify localizations not included in your bundle’s .lproj directories, add the CFBundleLocalizations key to this file. The value for the key is an array of strings, each of which contains an ISO language designator as described in “Language and Locale Designations.”

from old outdated doc...

4 Likes

It sounds like the real issue might be you are expecting libraries to use additional localizations beyond those supported by the main application bundle. If so, setting CFBundleAllowMixedLocalizations to YES in the application’s Info.plist is the proper solution.


To explain the nature of the workaround you found:

The localizations supported by a particular bundle are the union of the lproj folders, the CFBundleLocalizations you found, and CFBundleDevelopmentRegion. (The relevant bundle in your case is the main bundle of the top‐level application.) If a module includes no localized resource files, then Xcode will not bother to create an empty lproj. Empty directories are somewhat brittle anyway. That is likely why you needed to add to CFBundleLocalizations even though you had already told Xcode which localizations you cared about.

But none of that matters if you turn on CFBundleAllowMixedLocalizations—which ought to have been the default in my opinion. Then the main bundle does not restrain other bundles’ selection of localizations.

9 Likes

@SDGGiesbrecht many thanks for detailed explanation of the nature for this issue.