Check existence of localized string in .strings resource file, fall back to default

1k views Asked by At

I want to find the appropriate localized string based on some runtime variable and fall back to a default string:

// localizable.strings
"com.myapp.text1"         = "The default text 1";
"com.myapp.text1#SPECIAL" = "The special text";
"com.myapp.text2"         = "The default text 2";
// my code

let key1     = "com.myapp.text1"
let key2     = "com.myapp.text2"
let modifier = "#SPECIAL"
print( NSLocalizedString(key1 + modifier
                        , value:  NSLocalizedString(key1, comment: "")
                        , comment: "") )
// > "The special text 1"
print( NSLocalizedString(key2 + modifier
                        , value: NSLocalizedString(key2, comment: "") # the default to fall back to
                        , comment: "") )
// > "The default text 2"

Nice, that's what I want, try a special variant, fall back to the default.

However, if the option NSShowNonLocalizedStrings in the user defaults is set to true, it fails:
For non-localised strings, an upper-case version of the key will be returned, ignoring the default value. Also an error message is printed in the console (documentation).
So it appears that my solution is working against intended way of using NSLocalizedString.

UserDefaults.standard.set(true, forKey: "NSShowNonLocalizedStrings") # could also be passed as launch option or set via terminal
print( NSLocalizedString(key2 + modifier
                        , value: NSLocalizedString(key2, comment: "")
                        , comment: "") )
// > ERROR: com.myapp.text2#MODIFIER not found [...]
// > "COM.MYAPP.TEXT2"

I could work around this by testing for the uppercased version etc. but this would just be a hack that masks the actual issue.
What I would probably need is a test if (bundle.hasLocalizationFor(key2 + modifier)... but to implement such a method I would have to re-implement processing of the strings files including parsing and caching. And that feels wrong in itself.

Question:

Is there some method I am missing to achieve what I am looking for?
Or is the whole idea of special/fallback localization just wrong for the platform?

1

There are 1 answers

0
de. On

Thanks to comments by Martin R, I was able to get a reasonably working solution:

static let cache = NSCache<NSString, NSDictionary>()
private func strings(for tableName: String) -> [String: String] {

    guard let strings = cache.object(forKey: tableName as NSString) else {

        guard let path = Bundle.main.path(forResource: tableName, ofType: "strings")
            , let dict = NSDictionary(contentsOfFile: path) as? [String: String] else {

                return [:]
        }
        cache.setObject(dict as NSDictionary, forKey: tableName as NSString)
        return dict
    }
    return strings as! [String: String]
}

func localizedString(_ key: String) -> String {
    let specificKey = key + "#SPECIAL"
    let tableName = "TableName"
    let bundle = Bundle.main

    return self.strings(for: tableName)[specificKey]
    ?? NSLocalizedString(key
                         , tableName: tableName
                         , bundle: bundle
                         , comment: "")
}