iosswiftsdklocalizationframeworks

How to Allow Localization Overrides in an iOS Framework?


I’ve created a framework in Swift and provided localization for it. However, I want the framework’s users to be able to override the localization strings at runtime. Here’s what I’ve done so far:

1.  Inside the framework, I created a LocalizationBundle variable and set its default value to the framework’s own bundle identifier.
2.  When the framework is initialized, if this value is overridden (i.e., set to the main app’s bundle), the localization is fetched from there and it works fine.

Here’s the code I implemented:

public class SDK {

    private var localizationBundle: Bundle!

    public func configure(bundle: Bundle = .init(identifier: "com.test.SDK")!) {
        self.bundle = bundle
    }

    public func getBundle() -> Bundle {
        return localizationBundle
    }
}


extension String {
    func localized(in bundle: Bundle = SDK.shared.getBundle(), withComment comment: String? = nil) -> String {
        return NSLocalizedString(self, tableName: nil, bundle: bundle, value: "", comment: comment ?? "")
    }
}

This seems to work, but I’m not entirely sure if this is the best or most common approach. I have a few questions:

  1. Is this the correct way to provide localization overrides in a framework?
  2. What if the user of the framework is using a string catalog instead of a traditional Localizable.strings file? How should I handle that case?
  3. Would it be better to import the strings directly from a JSON file instead of using legacy .strings files for localization?
  4. Has anyone dealt with similar cases or have experience with localization in SDKs/frameworks? Any best practices or common patterns would be greatly appreciated!

Thanks in advance for your insights!


Solution

  • For your framework localization override, here's the simple solution:

    public class LocalizationManager {
        static var bundle: Bundle = Bundle(for: LocalizationManager.self)
        
        public static func setCustomBundle(_ newBundle: Bundle) {
            bundle = newBundle
        }
        
        public static func localizedString(for key: String) -> String {
            return NSLocalizedString(key, bundle: bundle, comment: "")
        }
    }
    

    Then use it like:

    // In framework
    let text = LocalizationManager.localizedString(for: "hello_world")
    
    // In app
    LocalizationManager.setCustomBundle(Bundle.main)
    

    This handles both .strings files and String Catalogs, and keeps things simple and maintainable.