Tikhonov Aleksandr
Tikhonov Aleksandr

Reputation: 14319

How to change localization on the fly SwiftUI

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

Answers (4)

Abedalkareem Omreyh
Abedalkareem Omreyh

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

Tikhonov Aleksandr
Tikhonov Aleksandr

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

Christos Koninis
Christos Koninis

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

Scandi
Scandi

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

Related Questions