Xcode + NSLocalizedString + String Catalogs + Macros

For quite a while, I have been using a somewhat simple-minded technique to provide semi-automatic localizations for strings used within a few of my apps.

Here is the gist:

  • Create an enumeration with RawValue of String that conforms to LocalizedRawRepresentable.
  • Use this enum to associate a "key" with a string (default localization)
  • Run a script that parses all Swift code looking for the enumerations that conform to LocalizedRawRepresentable and create/update a Localized.strings file.

There were some minor issues, as it isn't easy to parse a complex Swift set of sources, but it got the job done.

Simple example:

enum L: String, LocalizedRawRepresentable {
    case fileNotFound = "File not found"
}

// [ ... ]

if !FileManager.default.fileExists(at path: String) {
    print(L.fileNotFound.localized)
}

Well, we now have Xcode 15, and we have some new features:

  • Macros - these things can parse Swift sources with excruciating detail
  • A new ***** called String Catalogs
  • Xcode supports these String Catalogs by automatically detecting localizations and creating, updating, and deleting entries into Strings Catalog automatically.

So, it was time to update my simplistic LocalizedRawRepresentable and update it to support the String Catalogs!

So, with my contrived example above, I spent quite a lot of time reading up and experimenting with the new Swift macros and created a macro that will make localizations much easier (so I thought...):

@LocalizedStrings()
enum L {
    private enum Strings: String {
        case fileNotFound = "File not found"
    }
}

The macro takes this modified enumeration and expands it to the following code:

@LocalizedStrings()
enum L {
    private enum Strings: String {
        case fileNotFound = "File not found"
    }

    static let fileNotFound = NSLocalizedString("fileNotFound", tableName: nil, bundle: .main, value: "File not found")
}
extension L: LocalizedStrings { }

What I expected:

Xcode would pick up the NSLocalizedStrings() generated code, and insert it into the strings catalog, and all of my work is done... no scripts needed!

What I got...

Xcode did not detect the generated code, and nothing was added, modified, or deleted from the strings catalog.

So, I have a few questions:

  • Is my code deficient in some way? Is there something I need to add to my generated code that would let Xcode know there are localizations to be detected?
  • Is this an intentional limitation of Xcode's auto-detection of localizations for string catalogs to ignore generated code in its detection? (I'd hate to think I went through all this work for Xcode to simply ignore what I've done...!)
  • Is this an accidental omission of Xcode that may be "fixed" in a future release?

Right now, I can use the expanded macro to cut/paste the localization keys and values into the strings catalog, but I hoped that Swift macros plus Xcode auto-detection of localizations would have made this process nearly automatic.

Does anybody have any suggestions?

  • Created a Feedback for this: FB13662883

Add a Comment

Replies

Hello and thanks for posting on the forums!

NSLocalizedString does not support being hidden behind macros. This does work for String(localized:), LocalizedStringResource, and similar Swift APIs, but note that this is only fully supported in Xcode when the full string key is passed to the macro as a string literal at the macro call site.