NSLocalizedString vs swift

is there an equivalent to this Obj-C fragment? i can bridge to Obj-C specifically for that, but would really love to stay in pure swift if there is a way:

NSString* key = ....;
NSString* string = NSLocalizedString(key, ""); // or NSBundle.localizedStringForKey
BOOL stringIsAbsent = string == key; // pointer check

NSLocalizedString(_:tableName:bundle:value:comment:) | Apple Developer Documentation

Just pass a value that you are sure will never be a valid localized string then check if the result is equal to it.

sure...

any other, more robust way i can use in swift without jumping to Objective-C?

This doesn't even work in Objective-C. There are a number of reasons NSLocalizedString could return a string that is pointer-equal to key even though it got it out of the NSBundle, the most likely being tagged pointer strings.

But stepping back, what are you going to do with the answer? If it's to track down strings that are missing localizations, there is the built-in NSShowNonLocalizedStrings default you can use instead. Or is it something else?

1 Like

hmm, can you provide a reproducible test for this?
sorry to post obj-c code here, but this doesn't reproduce the problem you are talking about:

import "ViewController.h"

/*
 Localizable.strings:
 ------------
 "a" = "a";
 "key1" = "key1";
 "key2" = "else";
 */

void foo() {
    NSString* keyA = @"a"; // key in strings table: "a" = "a";
    NSString* strA = NSLocalizedString(keyA, @"");
    NSString* key1 = @"key1"; // key in strings table: "key1" = "key1";
    NSString* str1 = NSLocalizedString(key1, @"");
    NSString* key2 = @"key2"; // key in strings table: "key2" = "else";
    NSString* str2 = NSLocalizedString(key2, @"");
    NSString* key3 = @"key3"; // key not in strings table
    NSString* str3 = NSLocalizedString(key3, @"");
    
    assert(keyA != strA);
    assert(key1 != str1);
    assert(key2 != str2);
    assert(key3 == str3);

    NSLog(@"done\n");
}

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    foo();
}
@end

thanks for this hint. i want something like this, but a stricter version. is it possible to put a runtime break on "ERROR: xxx not found in table Localizable of bundle CFBundle"?

The behavior isn't contractual; even if NSLocalizedString is returning distinct objects today, it could stop doing so in iOS 20 or whatever. So you shouldn't rely on it.

Not sure, but that's more of an Apple frameworks question than a Swift question. I think your best bet really is what @Cyberbeni already said, though: if you pass an empty string as the fallback value, or something like "MISSING LOCALIZATION", you can check against it by value. (It may also be worth filing a bug against Apple to add a variation of the API that returns an Optional instead.)

i have this feeling that by iOS 20 time either i could be dead, or objective-c could be dead, or NSLocalizedString could be dead. if neither of the latter two things happens - apple should probably keep the current (unwritten) behaviour to not break too many existing (by the time) apps.

actually thanks! this comment somehow triggered my mind and finally brought me to what i wanted. here you are, a fully robust swift native way to check localized string absence:

    let s1 = NSLocalizedString("key", value: "value1", comment: "")
    let s2 = NSLocalizedString("key", value: "value2", comment: "")
    let isStringMissing = s1 != s2

thanks a bunch!
Mike

1 Like