15
votes

I'm working on an App which supports two languages: English and Arabic. When the user changes the language to Arabic, I need my app to make the text right-to-left (RTL).

I searched for this issue, and I found this answer about supporting RTL.

I would like to do apply the the above answer but in a programmatically way, because I need to change from LTR to RTL while the App is running.

11
iOS doesn't support such changes at runtime (and I don't know an OS that would let you do that), so you'll have to implement your own layouts everywhere and use left-right instead of start-end to do that. In the future you might be able to switch locale at the Settings app without restarting the apps, it might be even possible to do now, anyway that is done not programmatically. - A-Live
@A-Live OK, Thank you - SalmaGh

11 Answers

35
votes

in Swift 4 try this,

//FOR RIGHT TO LEFT
 UIView.appearance().semanticContentAttribute = .forceRightToLeft

//FOR LEFT TO RIGHT
 UIView.appearance().semanticContentAttribute = .forceLeftToRight
10
votes

I changed my app to right to left RTL by using this :

self.transform = CGAffineTransformMakeScale(-1.0, 1.0)

This flipped the view, then i flipped the objects in the view like this:

label.transform = CGAffineTransformMakeScale(-1.0, 1.0)
textField.transform = CGAffineTransformMakeScale(-1.0, 1.0)
image.transform = CGAffineTransformMakeScale(-1.0, 1.0)

This made my views RTL instead of changing all constraints or rebuild the app as RTL.

10
votes

Use this line of code it will change layout without closing application. From right to left

UIView.appearance().semanticContentAttribute = .forceRightToLeft

And for Left to Right Flip

UIView.appearance().semanticContentAttribute = .forceLeftToRight

and if you want to change textfield layout or text change then use this code because i faced this issue . textfield's texts was not changning layout. check this code to change layout of textfield text

extension UITextField {
open override func awakeFromNib() {
        super.awakeFromNib()
        if UserDefaults.languageCode == "ar" {
            if textAlignment == .natural {
                self.textAlignment = .right
            }
        }
    }
}
2
votes

03/05/2018 : Since @SalmaGh have answers his owned Question.
But the solution would make your UI/UX mess up badly.
I would give a suggestion on how i would solve this issue.

This answer was refer from multiple answer who would deserve to be upvoted instead of downvote.

Reason: Adding a language switcher to your app is only going to make it more confusing

This solution is required since some of arabic language such as mention by @Afshin M. Khiabani are done in App Manually instead of Library/Framework/Device Default. this gives developer more control over their apps. however this method are not prefer.

WARNING: This will mess up Horizantal Scrollview with Subviews

But if you must force LTL view to be RTL. suggest to used IOSPercy and Jaydip answer. but please do make a flagging for checking. such as let isArabic = false or used user default.


First enter code inside AppDelegate didFinishLaunchingWithOptions
why should we put this checking in the AppDelegate is because UIView on AppDelegate would be the superview to all view controller.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
     self.ChangeLayout()
    return true
}

func ChangeLayout(){
    var languageCode = ""
    if let value = UserDefaults.standard.string(forKey: "Applanguage") {
        languageCode = value
    }else{
        languageCode = ""
    }
    if(languageCode == "ar"){
        UIView.appearance().semanticContentAttribute = .forceRightToLeft
    }else{
        UIView.appearance().semanticContentAttribute = .forceLeftToRight
    }
}

This code will automatically Load the apps in RTL mode if Applanguage save "ar" however this will only happen once when you reopen the apps.


But you can access the function by calling the AppDelegate Function Like so:

        let appDelegate: AppDelegate? = UIApplication.shared.delegate as? AppDelegate
        appDelegate?.ChangeLayout()


Then Call TopMost ViewController such as LoadingScreen or something to make the Reload flow.

1
votes

Since it is your own application, I'm going to assume that you have control over it's UI elements.

If so, not only can you provide NSLayoutConstraint which satisfy your requirements, you can also specify alignment for text and buttons representation: NSTextAlignment.

Since you cannot control System Settings programmatically, keep in mind that your app will no longer take advantage of natural constraints, such as leading or trailing.

1
votes

Use Following lines in method didFinishLaunchingWithOptions when You want Arabic language layout, For SWIFT

UIView.appearance().semanticContentAttribute = .forceRightToLeft

For Objective C

[UIView appearance].semanticContentAttribute = UISemanticContentAttributeForceRightToLeft;

and reload the app using

[self application:app didFinishLaunchingWithOptions:nil];

in some other method in appDelegate. Call that method where you are changing app language.

Use vice versa for english localisation of app.

1
votes

Swift 5 deprecated use of

UIView.appearance().semanticContentAttribute = .forceRightToLeft

Use this instead

self.view.semanticContentAttribute = .forceLeftToRight

Update after UIView.semanticContentAttribute depreciated.

1
votes

Swift 5 with Scene Delegate

I created a simple singleton class that would switch the language on the fly using the Localizable.strings file, also it will replace the rootViewController for the keyWindow and acts as an AppRestart, for detailed implementation I wrote an Article You can read it here

//
//  AppLanguage.swift
//  Created by Mohamad Kaakati on 30/05/2021.
//

import UIKit

enum Languages {
    case english, arabic, french
}

class AppLanguage {
    
    static let shared = AppLanguage()
    private init() { }
    
    var bundle: Bundle?
    
    func set(index: Languages) {
        switch index {
        case .english:
            // For English Language set LTR
            print("Set language to LTR")
            selectBundleForResource(forResource: "en", isRTL: false)
        case .arabic:
            // For RTL Language set RTL
            print("Set language to RTL.")
            selectBundleForResource(forResource: "ar", isRTL: true)
        default:
            // For French Language set LTR
            selectBundleForResource(forResource: "fr", isRTL: false)
        }
        
    }
    
    private func selectBundleForResource(forResource: String!, isRTL: Bool) {
        guard let path = Bundle.main.path(forResource: forResource, ofType: "lproj") else {
            return
        }
        self.bundle = Bundle.init(path: path)
        switchViewControllers(isRTL: isRTL)
    }
    
    private func setKeyWindowFromAppDelegate(isRTL: Bool) {
        UIView.appearance().semanticContentAttribute = isRTL ? .forceRightToLeft : .forceLeftToRight
        let appDelegate = UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate
        let homeViewController = UIViewController() // Replace with your root view controller.
        appDelegate?.window?.rootViewController = homeViewController
    }
    
    private func switchViewControllers(isRTL rtl : Bool){
        if rtl {
            setKeyWindowFromAppDelegate(isRTL: true)
        } else {
            setKeyWindowFromAppDelegate(isRTL: false)
        }
    }
}

public extension String {
    /// Return Localized String
    var localizedString : String {
        get {
            return self.toLocal()
        }
    }
}

private extension String {
    func toLocal() -> String {
        if AppLanguage.shared.bundle != nil {
            return NSLocalizedString(self, tableName: "Localizable", bundle: AppLanguage.shared.bundle!, value: "", comment: "")
        }
        return self
    }
}

You can Set the Language using the following method:

AppLanguage.shared.set(index: .arabic)
0
votes

Do you mind if I ask why you need this?

I would highly recommend thinking twice before doing this, mostly because it provides a confusing user experience. Users expect their apps to follow the system language, and therefore not have any method of choosing the language inside the app.

iOS makes it very easy for you to do it the right way; if you use Auto Layout, build your custom controls upon UIKit API and use NSLocalizedString, everything will do the right thing at runtime for your users.

0
votes

swift 5

justify and Right or Left Alignment Together on NSAttributed string

let paragraphStyle = NSMutableParagraphStyle()
     if(arabic){
              paragraphStyle.alignment = .justified
              paragraphStyle.baseWritingDirection = .rightToLeft
              
          }else{
              paragraphStyle.alignment = .justified
              paragraphStyle.baseWritingDirection = .leftToRight
          }
-1
votes

//worked

UIView.appearance().semanticContentAttribute = .forceRightToLeft

 
 UIView.appearance().semanticContentAttribute = .forceLeftToRight