Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Here is a quick demo:

demo

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.

I also can confirm, that build contains localization (inside testLocalization.app in lib bundle):

testLocalization.app

Here is a link for project.

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

like image 623
hbk Avatar asked Nov 07 '22 02:11

hbk


1 Answers

I found the reason for this issue - the app itself require localization and setting it in project settings it's 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>

from old outdated doc:

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.”

As usually, Apple didn't mention this in any kind of doc related to SP...

----- update

The proper solutions is "setting CFBundleAllowMixedLocalizations to YES in the application’s Info.plist is the proper solution" - from SDGGiesbrecht

like image 121
hbk Avatar answered Nov 15 '22 16:11

hbk