I find all the other answers very helpful. However, I'd like to add a little safeguard here. For once, for the case that the requested language might not be available. And twice, for the case that we have a more complex locale identifier.
Especially in the latter case it's not as straight forward as to simply map the locale to the right lproj folder because they might not be named exactly the same way. Thus, I found it easier to leave this part up to the system. Bundle.preferredLocalizations
does exactly that.
Imagine you request en-GB but your app only supports en-US or just en in this matter. Since there is no en-GB.lproj folder in the project, no bundle will be returned. However, a call to preferredLocalizations
should resolve it to en.lproj. As a side note, the same is mostly true for Chinese as well (zh, zh-Hans, zh-Hant, etc.).
func localizedBundle(locale: String?) -> Bundle {
if let locale {
if let preferredLocale = Bundle.preferredLocalizations(from: Bundle.main.localizations, forPreferences: [locale]).first {
if let path = Bundle.main.path(forResource: preferredLocale, ofType: "lproj") {
if let bundle = Bundle(path: path) {
return bundle
}
}
}
}
return Bundle.main
}
Please be aware that preferredLocalizations
should at least return one value, which might not necessarily match the requested locale. If you want to know if a locale cannot be resolved, you have to create two Locale objects for each, the input locale string and the returned locale string, and then compare the language tags. But this is not part of this answer here.
Call NSLocalizedString with the bundle as follows.
NSLocalizedString("key", bundle: bundle, comment: "")
By the way, if the bundle is the main bundle, then it's equivalent to calling NSLocalizedString with just the key and comment argument, so no worries.
It also makes sense to cache the bundle and not retrieving it each time before calling NSLocalizedString.