如何在Swift 3中基于Base国际化实现iOS App运行时即时切换语言
Hey there, I’ve tackled this exact runtime language switching issue before—getting it to work without restarting the app using Base localization. Here’s a solid, step-by-step solution that works seamlessly for both iPhone and iPad:
Core Idea
Apple’s default Bundle doesn’t refresh localization resources while the app is running, so we’ll build a custom Bundle subclass to load strings based on the user’s selected language, plus a notification system to trigger UI updates across the entire app.
1. Create a Custom Localization Bundle
This subclass overrides how the app fetches localized strings, pulling from the user’s chosen language bundle instead of the system default:
import UIKit class LocalizationBundle: Bundle { override func localizedString(forKey key: String, value: String?, table tableName: String?) -> String { // Fetch the saved language code from UserDefaults guard let langCode = UserDefaults.standard.string(forKey: "AppLanguage"), let langBundlePath = Bundle.main.path(forResource: langCode, ofType: "lproj"), let langBundle = Bundle(path: langBundlePath) else { // Fallback to system default if no custom language is set return super.localizedString(forKey: key, value: value, table: tableName) } return langBundle.localizedString(forKey: key, value: value, table: tableName) } }
Then, replace the main bundle’s class in your AppDelegate so the app uses our custom bundle everywhere:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Set default language if none is saved (use system language or fallback to English) if UserDefaults.standard.string(forKey: "AppLanguage") == nil { let systemDefaultLang = Locale.preferredLanguages.first ?? "en" UserDefaults.standard.set(systemDefaultLang, forKey: "AppLanguage") } // Swap the main bundle's class to our custom LocalizationBundle object_setClass(Bundle.main, LocalizationBundle.self) return true }
2. Add Language Switching Logic
Create a function to handle user language selection—this saves the choice to UserDefaults and triggers a full UI refresh:
func switchAppLanguage(to langCode: String) { // Save the selected language code (e.g., "en", "zh-Hans") UserDefaults.standard.set(langCode, forKey: "AppLanguage") UserDefaults.standard.synchronize() // Notify all screens to update their UI NotificationCenter.default.post(name: NSNotification.Name(rawValue: "AppLanguageChanged"), object: nil) // Refresh the root view controller to apply changes to storyboard-based UI if let window = UIApplication.shared.keyWindow { window.rootViewController = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController() window.makeKeyAndVisible() } }
Call this from your settings screen when the user selects a language:
// Example: Switch to English switchAppLanguage(to: "en") // Example: Switch to Simplified Chinese switchAppLanguage(to: "zh-Hans")
3. Update UI Elements on Language Change
All view controllers and custom UI components need to listen for the language change notification and refresh their localized text:
For View Controllers:
import UIKit class HomeViewController: UIViewController { @IBOutlet weak var submitButton: UIButton! override func viewDidLoad() { super.viewDidLoad() // Set initial localized text updateLocalizedUI() // Listen for language change notifications NotificationCenter.default.addObserver(self, selector: #selector(updateLocalizedUI), name: NSNotification.Name(rawValue: "AppLanguageChanged"), object: nil) } @objc private func updateLocalizedUI() { self.title = NSLocalizedString("Home_Screen_Title", comment: "Home screen title") submitButton.setTitle(NSLocalizedString("Submit_Button_Label", comment: "Submit button text"), for: .normal) // Add all other UI elements that need localization here } deinit { // Clean up the observer to avoid memory leaks NotificationCenter.default.removeObserver(self) } }
For Custom UI Components:
If you have custom buttons/labels, add the observer in their awakeFromNib method:
class CustomLocalizedButton: UIButton { override func awakeFromNib() { super.awakeFromNib() NotificationCenter.default.addObserver(self, selector: #selector(refreshText), name: NSNotification.Name(rawValue: "AppLanguageChanged"), object: nil) refreshText() } @objc private func refreshText() { // Use a User Defined Runtime Attribute to set the localization key in Interface Builder if let key = self.value(forKey: "localizationKey") as? String { self.setTitle(NSLocalizedString(key, comment: ""), for: .normal) } } deinit { NotificationCenter.default.removeObserver(self) } }
4. Handle Storyboard/XIB Localization
Since Storyboard/XIB elements don’t auto-refresh at runtime, use one of these fixes:
- Set all localized text via code (as shown above), or
- Rely on the root view controller refresh we added in
switchAppLanguageto reload storyboard content.
Key Notes
- Ensure your localization folders are named correctly (e.g.,
en.lproj,zh-Hans.lproj) and contain aLocalizable.stringsfile with your key-value pairs. - For system controls like back buttons, manually set their title in
viewWillAppear:override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) navigationItem.backBarButtonItem = UIBarButtonItem(title: NSLocalizedString("Back_Button", comment: ""), style: .plain, target: nil, action: nil) }
内容的提问来源于stack exchange,提问作者Rushikesh

