25

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.

Community
  • 1
  • 1
SalmaGh
  • 441
  • 1
  • 5
  • 11
  • 2
    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 Feb 02 '16 at 10:48
  • @A-Live OK, Thank you – SalmaGh Feb 02 '16 at 10:59

10 Answers10

46

in Swift 4 try this,

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

//FOR LEFT TO RIGHT
 UIView.appearance().semanticContentAttribute = .forceLeftToRight
Berlin
  • 2,115
  • 2
  • 16
  • 28
13

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
            }
        }
    }
}
Shahzaib Maqbool
  • 1,479
  • 16
  • 25
9

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.

SalmaGh
  • 441
  • 1
  • 5
  • 11
  • 3
    this will flip text and images too then it's will destroy app design – Fadi Abuzant Aug 24 '16 at 07:22
  • @FadiAbuzant Yes i wanted my app design to be from right to left, i mean all images, labels, textFields.... etc to be in the right side in view while my view objects was in the left side. I was converting my app from English to Arabic "multi language app", so instead of build a new views with right to left design i used this way. – SalmaGh Aug 25 '16 at 12:21
  • 7
    Dude... @SalmaGh your serious? – SwingerDinger Feb 21 '18 at 14:33
  • @FadiAbuzant this answer was made when my early iOS Career this is the noob way to do it. what i done is flip whole view. and manually flip view item back to normal using the same code. it was a hard way to do it. but it solve my problem. – Muhammad Asyraf Aug 15 '18 at 05:00
  • I found that if you are making apps using arabic my as well set multilanguage app instead. and used iOS built in feature which will flip the view correctly if you change device language to Arabic. if not. the UI would stay in English LTR design. – Muhammad Asyraf Aug 15 '18 at 05:03
  • Funny Confusion between client and user is. they want this feature to be flip and user can change to another language in App. while the developer has not set up a storyboard for multi language. why not make the app multi-language in the first place and follow device language. it would be absurd if the user using english as device language and want to used arabic in the app. ;p – Muhammad Asyraf Aug 15 '18 at 05:05
3

Swift 5 deprecated use of

UIView.appearance().semanticContentAttribute = .forceRightToLeft

Use this instead

self.view.semanticContentAttribute = .forceRightToLeft

Update after UIView.semanticContentAttribute depreciated.

Mayank Verma
  • 108
  • 1
  • 8
  • `UIView.semanticContentAttribute` is not valid code. – ryanholden8 Dec 31 '20 at 21:22
  • 1
    @ryanholden8 can you tell me why? I mean I read it and it says **A semantic description of the view’s contents, used to determine whether the view should be flipped when switching between left-to-right and right-to-left layouts.** – Mubin Mall Dec 14 '22 at 09:57
2

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.

Muhammad Asyraf
  • 1,748
  • 15
  • 20
1

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.

SwiftArchitect
  • 47,376
  • 28
  • 140
  • 179
1

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.

IOSPercy
  • 39
  • 5
  • Setting semanticContentAttribute on the appearance() proxy is not supported. You're going to run into many other issues and bugs since the app still believes it's running in a language that isn't the one you're overriding. Adding a language switcher to your app is only going to make it more confusing; users expect their apps to follow the language that their device is set to. – wakachamo Sep 22 '17 at 06:18
1

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)
Mohamad Kaakati
  • 392
  • 3
  • 11
  • I followed this and whats written on your website, but the moment the view loads, it crashes the entire app with Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value – Zyfe3r Jul 08 '21 at 06:20
  • @Zyfe3r Do you have the Localization files? make sure you follow the steps carefully, I use this code on daily basis, if you could share more context of your problem so I can help you. – Mohamad Kaakati Jul 08 '21 at 09:20
  • Yes i do. But it doesn't even go that far, it crashes right on loading the first view ! – Zyfe3r Jul 08 '21 at 09:38
  • 1
    @MohamadKaakati, your answer w.r.t to SceneDelegate helped me. Thanks :) – Programming Learner Jul 12 '21 at 12:04
0

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.

wakachamo
  • 1,723
  • 1
  • 12
  • 18
0

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
          }