LocalizedStringResource's bundle

When I call this in my package:

    print(#bundle) // package bundle

it correctly returns the package' bundle.

However when I call this from my package:

    let x: LocalizedStringResource = "Hello"
    print(x.bundle) // main app bundle

it returns the main app bundle, which makes the subsequent:

String(localized: x)

fail on a missing key.

Is this a bug or a feature?

1 Like

When you construct a LocalizedStringResource from a string literal, it applies the defaults from init(_:table:locale:bundle:comment:), where bundle is set to .main. So I think it's a "feature".

Hmm.. I could use:

    let x: LocalizedStringResource = .init("Hello", bundle: #bundle)

but it's so easy to forget to do so.

Yeah. Since #bundle macro is added. A better idea is we deprecate the old old which use .main as the default value and instead introduce a new API which use #bundle as the default value for Bundle.

And I really do not understand why Foundation introduce a new LocalizedStringResource.BundleDescription type instead of reuse the existing Bundle type.

Yeah prior to the introduction of #bundle - Bundle.main was the best/only option Foundation had for a default value. Now that we have #bundle, it does provide a better default than Bundle.main. I've thought a bit about changing the default for localization APIs in Foundation, however the string literal space that @pyrtsa brought up is the main limitation: there's no way for LocalizedStringResource (or String.LocalizationValue) to have an ExpressibleByStringLiteral conformance that can get the value of #bundle from the caller today (somewhat intentionally because ExpressibleByStringLiteral isn't designed to allow string literals to behave differently depending on the module called from). We don't want to diverge the string literal behavior from the explicit initializer behavior, and I've yet to come up with a better solution for the string literal behavior.

There’s the slightly evil option of walking the call stack and identifying the bundle corresponding to the library that called you.

Theoretically that could work for some of the cases that #bundle handles, but not all of the cases (it'd work when we end up just finding the bundle based on the DSO handle, but it wouldn't work for Swift packages where it comes from Bundle.module or for mergeable libraries where it comes from a special helper class pointer)

1 Like

Understandably this won't work today, but is there a fundamental reason why it can't?

struct LocalizedStringResource: ExpressibleByStringLiteral {
    init(stringLiteral: String, bundle: Bundle = #bundle) {
        self.init(stringLiteral, bundle: bundle)
    }
}