Swift 5开发的iOS应用内切换语言(本地化)实现方案咨询
Absolutely, in-app language switching is totally supported for Swift 5 iOS apps—and I’ve got a battle-tested, optimal approach to share with you, based on production best practices:
iOS defaults to loading localized resources based on the user’s system language settings. To switch languages within your app, you’ll need to manually manage the user’s language preference and force the app to load resources from your chosen language files instead of relying on the system default.
1. Create a Singleton Language Manager to Store Preferences
First, we’ll build a shared manager to handle saving/retrieving the user’s selected language. This keeps the logic centralized and easy to access from any screen.
enum AppLanguage: String { case english = "en" case simplifiedChinese = "zh-Hans" // Add more languages here as needed (e.g., .spanish = "es") } class LanguageManager { static let shared = LanguageManager() private let userDefaultsKey = "AppSelectedLanguage" // Get the current active language (falls back to system default if no preference is saved) var currentLanguage: AppLanguage { guard let savedLangCode = UserDefaults.standard.string(forKey: userDefaultsKey), let language = AppLanguage(rawValue: savedLangCode) else { // Fall back to system's preferred language, or default to English let systemLangCode = Locale.preferredLanguages.first ?? "en" return systemLangCode.starts(with: "zh") ? .simplifiedChinese : .english } return language } // Set a new language and notify the app to refresh UI func setLanguage(_ language: AppLanguage) { UserDefaults.standard.set(language.rawValue, forKey: userDefaultsKey) // Post a notification so all screens know to update their UI NotificationCenter.default.post(name: NSNotification.Name("AppLanguageChanged"), object: nil) } }
2. Build a Custom Localization String Loader
The default NSLocalizedString uses the system language, so we’ll create a custom extension to load strings from the language file matching our manager’s current selection:
extension String { func localized() -> String { let targetLangCode = LanguageManager.shared.currentLanguage.rawValue // Get the bundle for the target language guard let langBundlePath = Bundle.main.path(forResource: targetLangCode, ofType: "lproj"), let langBundle = Bundle(path: langBundlePath) else { // Fall back to system default localization if the target bundle doesn't exist return NSLocalizedString(self, comment: "") } return NSLocalizedString(self, bundle: langBundle, comment: "") } } // Usage example: // let greeting = "hello_world".localized()
3. Update Storyboard/XIB Content to Follow Custom Language
Storyboard elements won’t automatically pick up your custom language—you have two solid options here:
Option A: Manual UI Refresh in View Controllers
In each view controller’s viewWillAppear (or a base view controller that all screens inherit from), update your UI elements using the localized() method:
override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) self.title = "home_screen_title".localized() self.loginButton.setTitle("login_button_label".localized(), for: .normal) // Repeat for all text-based elements }
Option B: Custom Localized UI Components (Cleaner for Storyboards)
Create IBDesignable subclasses for UI elements like buttons, labels, etc., so you can set a localization key directly in Storyboard:
@IBDesignable class LocalizedLabel: UILabel { @IBInspectable var localizationKey: String = "" { didSet { text = localizationKey.localized() } } } @IBDesignable class LocalizedButton: UIButton { @IBInspectable var localizationKey: String = "" { didSet { setTitle(localizationKey.localized(), for: .normal) } } }
Then, in Storyboard, change your label/button’s class to LocalizedLabel/LocalizedButton and enter the localization key in the Attributes Inspector—no manual code needed for each element!
4. Handle Language Switch Trigger & UI Refresh
When the user taps your language switch button, update the preference and refresh the app’s UI. Two approaches here:
Approach 1: Refresh UI via Notifications
Call the language manager’s setLanguage method, then have each view controller listen for the AppLanguageChanged notification to update their UI:
// In your language switch button's action: @IBAction func switchToChineseTapped(_ sender: UIButton) { LanguageManager.shared.setLanguage(.simplifiedChinese) } // In each view controller (or base view controller): override func viewDidLoad() { super.viewDidLoad() NotificationCenter.default.addObserver(self, selector: #selector(refreshLocalizedContent), name: NSNotification.Name("AppLanguageChanged"), object: nil) } @objc private func refreshLocalizedContent() { self.title = "home_screen_title".localized() self.loginButton.setTitle("login_button_label".localized(), for: .normal) } deinit { NotificationCenter.default.removeObserver(self) }
Approach 2: Restart the Root View Controller (More Thorough)
For a clean, full refresh (great if you have complex UI or nested navigation), replace the app’s root view controller:
@IBAction func switchToEnglishTapped(_ sender: UIButton) { LanguageManager.shared.setLanguage(.english) // Get the app's main window (adjust for SceneDelegate if using iOS 13+) guard let window = UIApplication.shared.windows.first else { return } let newRootVC = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController()! window.rootViewController = newRootVC window.makeKeyAndVisible() } // If using SceneDelegate (iOS 13+): // guard let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene, // let window = scene.windows.first else { return }
5. Handle Edge Cases
- Localized Images: If you have language-specific images, place them in the corresponding
.lprojfolders, then use a custom image loader:func localizedImage(named name: String) -> UIImage? { let targetLangCode = LanguageManager.shared.currentLanguage.rawValue guard let langBundlePath = Bundle.main.path(forResource: targetLangCode, ofType: "lproj"), let langBundle = Bundle(path: langBundlePath) else { return UIImage(named: name) } return UIImage(named: name, in: langBundle, compatibleWith: nil) } - System UI Elements: Things like navigation bar back buttons default to system language. To fix this, set the back button title manually in your base view controller using a localized string.
- Use standard ISO language codes (e.g.,
zh-Hansfor simplified Chinese,enfor English) to match system language file naming conventions. - Test thoroughly to ensure all localization keys are present in every language’s
.stringsfile. - If you’re using SwiftUI, the approach is similar—use the
LanguageManagerto observe language changes and refresh views via@ObservedObjector@EnvironmentObject.
内容的提问来源于stack exchange,提问作者New iOS Dev

