The supported answer is not to set it in code. Since iOS 13 users can choose a language for each app in Settings, and iOS relaunches the app in that language. A language option inside your app should send the user there:
if let url = URL(string: UIApplication.openSettingsURLString) {
await UIApplication.shared.open(url)
}
The language row appears on your app's Settings page when the app ships more than one localization and the user has more than one preferred language in Language & Region. Apple's WWDC24 session "Build multilingual-ready apps" adds that UIPrefersShowingLanguageSettings set to YES in Info.plist shows the row even for users with a single language. Apple's guidance from WWDC19 is explicit: do not set the application language manually in code, because the system handles language and font fallbacks. To read the language the app is actually running in, use Bundle.main.preferredLocalizations.first, not Locale.preferredLanguages.
Two workarounds exist when that is not enough.
The AppleLanguages user default is the list the system reads to decide the app language. Writing to it affects the next launch, not the running process:
UserDefaults.standard.set(["fr"], forKey: "AppleLanguages")
// takes effect after the app is relaunched
Apple documents this key only as a testing launch argument (-AppleLanguages "(fr)" in the scheme editor), so treat it as undocumented behavior that may change.
Switching without a restart means bypassing the system: load the strings of one .lproj folder and re-render your views yourself.
let path = Bundle.main.path(forResource: "fr", ofType: "lproj")!
let french = Bundle(path: path)!
let title = french.localizedString(forKey: "settings_title", value: nil, table: nil)
Everything that does not go through that bundle keeps the launch language: system alerts and share sheets, formatters, and String(localized:), whose locale parameter only changes number formatting, not which translation is loaded.
In SwiftUI, .environment(\.locale, Locale(identifier: "fr")) on a root view makes Text and the other views that take a LocalizedStringKey resolve French strings in that view tree. It is the mechanism Apple documents for previewing localizations, and it works at runtime for the views below the modifier, with the same limits as the bundle approach for everything else.
Published Oct 9, 2026 · Markdown version