# Localized SwiftUI Views in a Swift Package

A strings file contains the translations of localized user-facing strings for one language with optional comments. The syntax for each string in a strings file is a key-value pair in which `key` is the identifier for looking up the `value` that contains the translation.

```swift
/* A friendly greeting. */
"Hello, World!" = "Hallo, Welt!";
```

Initializers for several SwiftUI types – such as [`Text`](), [`Toggle`](https://developer.apple.com/documentation/swiftui/toggle), [`Picker`](https://developer.apple.com/documentation/swiftui/picker) and others – implicitly lookup a localized string when you provide a string literal.

```swift
Text("Hello, World!") // you might expect that it will show "Hallo, Welt!"
```

This implicit lookup won't work if you use those SwiftUI types in a Swift package !

If you initialize a SwiftUI `Text` view with a string literal, the view uses the [`init(_:tableName:bundle:comment:)`](https://developer.apple.com/documentation/swiftui/text/init(_:tablename:bundle:comment:)) initializer, which interprets the string as a localization key and searches for the key in the table you specify or in the default table if you don’t specify one. More importantly it will use `Bundle.main` if you don't specify one.

```swift
Text("Hello, World!") // Shows "Hello, World!" as it searches the default table in the main bundle.
```

When building your Swift package, Xcode treats each target as a Swift module. If a target includes resources, Xcode creates a resource bundle and an internal static extension on [`Bundle`](https://developer.apple.com/documentation/foundation/bundle) to access each module. You have to use this extension `Bundle.module`  to locate package resources.

```swift
Text("Hello, World!", bundle: .module) // Shows "Hallo, Welt!" :)
```

Not all SwiftUI types have such flexible initializers.  [`Button`](https://developer.apple.com/documentation/swiftui/button) view has an initializer expecting a `LocalizedStringKey`and will lookup the text in the `Localizable.strings` file through `Bundle.main` without the option to specify a different bundle. 

Then you have to use other initializers (if possible)

```swift
Button(action: { print("Label shows 'Hallo, Welt!'") }, label: {
  Text("Hello, World!", bundle: .module)
})
```

or you lookup the localized text directly

```swift
Bundle.module.localizedString(forKey: "Hello, World!", value: nil, table: nil) // returns "Hallo, Welt!"
```

The statement takes a lot of space which can be changed by introducing an extension on `Bundle`

```swift
extension Bundle {
  func localizedString(forKey key: String) -> String {
    self.localizedString(forKey: key, value: nil, table: nil)
  }
}
```

and an extension on `String`

```swift
extension String {
    var localizedString: String {
        Bundle.module.localizedString(forKey: self)
    }
}
```

to get a localized string concisely.

```swift
Button("Hello, World!".localizedString) { print("Label shows 'Hallo, Welt!'") }
```

An extra tip for people who toy with the idea to generate a binary framework (xcframework) from a Swift Package: It is necessary to abstract the bundle access as Xcode won't create the internal static extension  `Bundle.module` for XCFrameworks. You can do this with the following code snippet

```swift
import Foundation

class BundleLocator {}

extension Bundle {
    static var myModule: Bundle {
        #if SWIFT_PACKAGE
            return Bundle.module
        #else
            return Bundle(for: BundleLocator.self)
        #endif
    }
  
    func localizedString(forKey key: String) -> String {
      self.localizedString(forKey: key, value: nil, table: nil)
  }
}

extension String {
    var localizedString: String {
        Bundle.myModule.localizedString(forKey: self)
    }
}
```


