Reputation: 14319
How can I change the localization of the App on the fly in SwiftUI?
I think we can use below code, but we should find SwiftUI approach.
func localized(_ lang:String) -> String {
let path = Bundle.main.path(forResource: lang, ofType: "lproj")
let bundle = Bundle(path: path!)
return NSLocalizedString(self, tableName: nil, bundle: bundle!, value: "", comment: "")
}}
Upvotes: 9
Views: 11360
Reputation: 2329
You can use LanguageManager-SwiftUI
It's easy to use and you can get your app ready in a few steps.
1- Add a Localizable.strings
file to your project.
2- Wrap your main view with LanguageManagerView
and pass the default language that your app will run for the first time.
@main
struct ExampleApp: App {
var body: some Scene {
WindowGroup {
// The default language when the app starts for the first time.
// it can be the `deviceLanguage`, `ar`, `en`, or any language.
LanguageManagerView(.deviceLanguage) {
AppView()
.transition(.slide) // The animation that will be happening when the language change.
}
}
}
}
3 - To change the language you should change the selectedLanguage
as below.
struct LanguageView: View {
// MARK: - Properties
@EnvironmentObject var languageSettings: LanguageSettings
// MARK: - body
var body: some View {
VStack {
Text("Select a language")
.padding()
Button("Arabic") {
withAnimation {
languageSettings.selectedLanguage = .ar
}
}
.padding()
Button("English") {
withAnimation {
languageSettings.selectedLanguage = .en
}
}
}
}
}
For more details you can read the read me section for the library.
Upvotes: 0
Reputation: 14319
First of all, we should create 2 Localizable.strings
files, in my case it's en and ru.
And we should store our current language object, I choose store it property in UserSettings
like ObservableObject
, We can also use @AppStorage
when initializing the lang
variable to keep the chosen language
class UserSettings: ObservableObject {
@Published var lang: String = "ru"
var bundle: Bundle? {
let b = Bundle.main.path(forResource: lang, ofType: "lproj")!
return Bundle(path: b)
}
}
We can use this settings object like .environmentObject()
in SceneDelegate.swift
, so every view in hierarchy will be updated.
var settings = UserSettings()
// ...
= UIHostingController(rootView: contentView.environmentObject(settings))
In View
we can get desirable behaviour, note we should specify bundle
in Text
initialization.
struct ContentView: View {
@EnvironmentObject var settings: UserSettings
var body: some View {
VStack {
Text("App", bundle: settings.bundle)
}
}
}
And now I can change the language on someplace in the app, here is just sample loop example, you can call this function changeLanguage()
in the of scene(_ scene:, willConnectTo:, options connectionOptions:)
and every 3 seconds language will be changed.
func changeLanguage() {
print(#function)
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
self.settings.lang = self.settings.lang == "ru" ? "en" : "ru"
self.changeLanguage()
}
}
Upvotes: 8
Reputation: 1678
There is no general way to solve this(localization on the fly). This works only for the strings that your code displays.
Localization is tricky, it is more than strings, you have localized image assets, xibs (e.g. UIViewRepresentable), system images & system text, assets from 3 party libraries.
When starting to write a new app the first thing you care to localize is the text, but if you build your app with these "localization on the fly" solutions latter you might not be able to use it for all the assets that your app needs to localize.
Upvotes: 1
Reputation: 63
@Tikhonov Alexander Does this mean that the bundle identifier must be specified in every Text element? Is there an easy way to set this for all Text elements or is there another way to change the Locale identifiert on the fly? Currently I am testing other languages with the preview by changing the Locale setting.
static var previews: some View {
TestView()
.environment(\.locale, Locale(identifier: "ru"))
}
Upvotes: 5