Independent Language Settings in SwiftUI (Without Changing iPhone System Language)
2024-11-22iosswiftui

Independent Language Settings in SwiftUI (Without Changing iPhone System Language)

Many iOS apps rely on the device's system language.
However, sometimes users want to choose a different language inside the app without changing their entire iPhone language.

For example:

  • iPhone language: English
  • App language: Arabic

In this article, I will show a simple way to implement independent language settings in SwiftUI using a LanguageManager.

The idea is:

  1. Store the selected language in UserDefaults
  2. Manage the language using a centralized manager
  3. Inject the manager into the SwiftUI environment
  4. Update Locale and LayoutDirection dynamically

1. Inject the Language Manager into the App Environment

First, create the manager and inject it into the environment so it can be accessed anywhere in the app.

@Bindable private var languageManager = LanguageManager()

var body: some Scene {
    WindowGroup {
        MainView()
            .environment(languageManager)
            .environment(\.locale, languageManager.locale)
            .environment(\.layoutDirection, languageManager.layoutDirection)
    }
}

Now every view can access the language manager using:

@Environment(LanguageManager.self) private var languageManager

2. Create the LanguageManager

The LanguageManager is responsible for:

  • storing the current language
  • updating Locale
  • updating LayoutDirection (important for RTL languages like Arabic)
@MainActor
@Observable
final class LanguageManager {

    var locale: Locale
    var layoutDirection: LayoutDirection

    private(set) var currentLanguage: LanguageType {
        get { GetCurrentLanguage.value }
        set {
            GetCurrentLanguage.value = newValue
            updateState(for: newValue)
        }
    }

    init() {
        let localeString = GetCurrentLanguage.value.rawValue
        locale = Locale(identifier: localeString)
        layoutDirection = LanguageType(rawValue: localeString) == .arabic
            ? .rightToLeft
            : .leftToRight
    }

    func setLanguage(_ language: LanguageType) {
        guard currentLanguage != language else { return }
        currentLanguage = language
    }

    private func updateState(for language: LanguageType) {
        locale = Locale(identifier: language.rawValue)
        layoutDirection = language == .arabic
            ? .rightToLeft
            : .leftToRight
    }
}

Key points:

  • @Observable allows SwiftUI to automatically update views
  • Locale controls localization
  • LayoutDirection ensures proper RTL support

3. Persist Language Using UserDefaults

Next, we store the selected language so it persists between app launches.

struct GetCurrentLanguage {
    private init() {}

    static var value: LanguageType {
        get {
            let localeString = UserDefaults.standard.string(forKey: "AppLanguage") ?? "en"
            return LanguageType(rawValue: localeString) ?? .english
        }
        set {
            UserDefaults.standard.set(newValue.rawValue, forKey: "AppLanguage")
        }
    }
}

This ensures that when the app restarts, it remembers the user's language preference.


4. Define Supported Languages

Create an enum to represent the supported languages.

enum LanguageType: String {
    case english = "en"
    case arabic = "ar"
}

You can easily add more languages later.

Example:

case indonesian = "id"
case french = "fr"

5. Access the Language Manager in Any View

Now any view can access and change the language.

@Environment(LanguageManager.self) private var languageManager

Example usage:

Button("Switch to Arabic") {
    languageManager.setLanguage(.arabic)
}

When the language changes:

  • Locale updates
  • LayoutDirection updates
  • SwiftUI views automatically refresh

6. Supporting Right-to-Left Languages

Languages like Arabic require RTL layout direction.

This is why the manager also controls:

LayoutDirection.rightToLeft

Without this, the UI layout would remain left-to-right and look incorrect.


Why This Approach Works Well

Advantages of this pattern:

✅ Users can change language inside the app
✅ No need to change iPhone system language
✅ Works naturally with SwiftUI
✅ Supports RTL languages
✅ Centralized language management


Final Thoughts

Handling localization in SwiftUI becomes much easier when using a dedicated language manager.
This pattern keeps your code clean and allows the app to support dynamic language switching.

If you're building international apps, this approach gives you full control over localization behavior while keeping SwiftUI reactive and simple.