iOS user with language of "en", no region code, translation lookup fails

65 Views Asked by At

We have a bug reported where a user has a device with an en language and nil region code, and thus all NSLocalizedString lookups in are failing, meaning our string key is what is rendered onscreen. Thus, if we had this in our en.lproj/Localizable.strings file:

"some_key" = "Some string.";

It would render some_key instead of Some string. in our UI.

First question: how do I replicate this scenario locally? This question on Stack seems to almost describe the issue, but does not describe how one enters this state.

Second question: why would iOS not fall back to English in the event the region code was nil?

1

There are 1 best solutions below

2
Neklas On

Second question: why would iOS not fall back to English in the event the region code was nil?

The cause can be "There is no base development language that is enabled". Or it is iOS logic.

Here is my solution for Localization. I just want to share with you an alternative solution if it can help you to solve the issue.

public enum AppResourceLang: String {
    case en
    case vi
}

public class AppResManager {
    public static let shared = AppResManager()
    public var lang = "en"
    var textBundle: Bundle!
    
    public var mainBundle: Bundle!
    
    private init() {

        let mainBundleId = Bundle.main.bundleIdentifier!
        // we use mainBundle's bundleIdentifier because normally your running target will contains "lproj" in Copy Bundle Resource
        // If your text/image is located on different project/target or framework, you need to enter that target's bundleId.
    
        mainBundle = Bundle(identifier: mainBundle)!
        let path = mainBundle.path(forResource: lang, ofType: "lproj")
        textBundle = Bundle(path: path!)
    }
    
    public func changeLang(code: String) {
        if let path = mainBundle.path(forResource: code, ofType: "lproj") {
            lang = code; textBundle = Bundle(path: path)
        } else {
            // fallback to English
            lang = "en"
            let path = mainBundle.path(forResource: lang, ofType: "lproj")
            textBundle = Bundle(path: path!)
        }
    }
}

Then we can use above textBundle like below:

public extension String {    
    var localText: String {
        guard let bundle = AppResManager.shared.textBundle else { return self }
        return NSLocalizedString(self, bundle: bundle, comment: "")
    }
    
    var lTextUpcased: String {
        guard let bundle = AppResManager.shared.textBundle else { return self.uppercased() }
        return NSLocalizedString(self, bundle: bundle, comment: "").uppercased()
    }
}

Here is my AppResource (like a framework). We can see I have Localizable.strings and it is localized for EN, VI. Here is my AppResource (like a framework)

Here is the real file/folder structure, you can see if you check on English in Localization for *.string, *.storyboard,... file. It will be cloned and saved in this folder (ex: en.lproj). You can base on this to point to the corresponding resource file.

enter image description here

Then, how to use my above codes. It is just a way to allow you to completely control the Localization.

    public enum AppLanguage: String {
        case en
        case vi
    }

    // MARK: - Device info
    public static func getDeviceLang() -> AppLanguage {
        guard let languageCode = Locale.current.languageCode else { return .en }
        let appLang = AppLanguage(rawValue: languageCode.lowercased()) ?? .en
        return appLang
    }

    // Then this will switch the language of your resource based on device language.
    AppResManager.shared.changeLang(code: YourGlobalClass.getDeviceLang().rawValue)

    // Then use the above string extension to load the corresponding text
    // Anywhere in your project.
    let text = "your_key".localText