How to Set Localised Direction Within Application(Rtl If User Select Arabic, Ltr Is Selected Language Is English)

How Can I set Localised Direction within application?(RTL if user select arabic, LTR is selected language is English)

Bottom line you can change the language from inside the app. But, an
extra work is needed. I am listing a background on limitations for each iOS version and then providing the solution. because while explaining the solution you need to understand the reason behind it.

Update (iOS 9+)

Solution Key: semanticContentAttribute

Some says that this way is not official and may cause efficiency problems.

UI direction can be forced now without closing the app:

UIView.appearance().semanticContentAttribute = .forceRightToLeft

Note, bars in iOS 9 can't be forced to RTL (Navigation and TabBar). While it get forced with iOS 10.

The only remaining thing that can't be forced RTL in iOS 10 is the default back button.

Changing app the language along with forcing the layout doesn't
automatically forces the system to use the localized string file
associated with the language.

Below iOS 9

Short answer:

Changing the app language will not force the layout direction according to the selected language without an app restart.

Apple's guide line:

Apple doesn't provide that because they don't prefere to allow the user
to change language from inside the app. Apple pushes the developers to
use the device language. and not to change it from inside the app at all.

Workarwound:

Some apps that supports Arabic language notes the user, in the
settings page where the user can change the language, that the layout will not take effect without an app restart. store
link for sample app

Example code to change app language to arabic:

[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:@"ar", @"en", nil] forKey:@"AppleLanguages"];

That won't take effect without a restart


How to change app language without an app restart

Solution is to provide your own localization solution:

  1. Use Base Internationalization but don't localize storyboards.
  2. Create one strings file and localise it to all languages and base for (english).
  3. Place the change language feature in a ViewController that starts before any other ViewController even before your main one. OR use an unwindSegue to go back to the root view controller after changing the language.
  4. Set strings for your views in viewDidLoad method for all view controllers.

Don't satisfy points 5 and 6 if you are not supporting below iOS 9


  1. Set the constraints for your views in viewDidLoad method for all view controllers.

  2. When you set the constraints use right/left according to the language selected instead of leading/trailing.


Language class sample (Swift):
Initialise an object of this class in your singlton class.

class LanguageDetails: NSObject {

var language: String!
var bundle: Bundle!
let LANGUAGE_KEY: String = "LANGUAGE_KEY"

override init() {
super.init()

// user preferred languages. this is the default app language(device language - first launch app language)!
language = Bundle.main.preferredLocalizations[0]

// the language stored in UserDefaults have the priority over the device language.
language = Common.valueForKey(key: LANGUAGE_KEY, default_value: language as AnyObject) as! String

// init the bundle object that contains the localization files based on language
bundle = Bundle(path: Bundle.main.path(forResource: language == "ar" ? language : "Base", ofType: "lproj")!)

// bars direction
if isArabic() {
UIView.appearance().semanticContentAttribute = .forceRightToLeft
} else {
UIView.appearance().semanticContentAttribute = .forceLeftToRight
}
}

// check if current language is arabic
func isArabic () -> Bool {
return language == "ar"
}

// returns app language direction.
func rtl () -> Bool {
return Locale.characterDirection(forLanguage: language) == Locale.LanguageDirection.rightToLeft
}

// switches language. if its ar change it to en and vise-versa
func changeLanguage()
{
var changeTo: String
// check current language to switch to the other.
if language == "ar" {
changeTo = "en"
} else {
changeTo = "ar"
}

// change language
changeLanguageTo(lang: changeTo)

Log.info("Language changed to: \(language)")
}

// change language to a specfic one.
func changeLanguageTo(lang: String) {
language = lang

// set language to user defaults
Common.setValue(value: language as AnyObject, key: LANGUAGE_KEY)

// set prefered languages for the app.
UserDefaults.standard.set([lang], forKey: "AppleLanguages")
UserDefaults.standard.synchronize()

// re-set the bundle object based on the new langauge
bundle = Bundle(path: Bundle.main.path(forResource: language == "ar" ? language : "Base", ofType: "lproj")!)

// app direction
if isArabic() {
UIView.appearance().semanticContentAttribute = .forceRightToLeft
} else {
UIView.appearance().semanticContentAttribute = .forceLeftToRight
}

Log.info("Language changed to: \(language)")
}

// get local string
func getLocale() -> NSLocale {
if rtl() {
return NSLocale(localeIdentifier: "ar_JO")
} else {
return NSLocale(localeIdentifier: "en_US")
}
}

// get localized string based on app langauge.
func LocalString(key: String) -> String {
let localizedString: String? = NSLocalizedString(key, bundle: bundle, value: key, comment: "")
// Log.ver("Localized string '\(localizedString ?? "not found")' for key '\(key)'")
return localizedString ?? key
}

// get localized string for specific language
func LocalString(key: String, lan: String) -> String {
let bundl:Bundle! = Bundle(path: Bundle.main.path(forResource: lan == "ar" ? lan : "Base", ofType: "lproj")!)
return NSLocalizedString(key, bundle: bundl, value: key, comment: "")
}
}

Language class sample (Objective-c):
Swift sample provides full solution. sorry you need to convert it yourself.

@implementation LanguageDetails

@synthesize language;
@synthesize bundle;

#define LANGUAGE_KEY @"LANGUAGE_KEY"

// language var is also the strings file name ar or en

- (id)init
{
if (self = [super init]) {

language = [[[NSBundle mainBundle] preferredLocalizations] objectAtIndex:0];

if (![language isEqualToString:@"ar"])
language = @"en";

language = [Common valueForKey:LANGUAGE_KEY defaultValue:language];

bundle = [NSBundle mainBundle];
}
return self;
}

- (BOOL)rtl {
return [NSLocale characterDirectionForLanguage:language] == NSLocaleLanguageDirectionRightToLeft;
}

- (void)changeLanguage
{
if ([language isEqualToString:@"ar"])
language = @"en";
else
language = @"ar";

[Common setValue:language forKey:LANGUAGE_KEY];
}

- (void)changeLanguageTo:(NSString *)lang
{
language = lang;

[Common setValue:language forKey:LANGUAGE_KEY];
}

- (NSString *) LocalString:(NSString *)key {
return NSLocalizedStringFromTableInBundle(key, language, bundle, nil);
}

@end

JSF render dir=rtl when language is arabic

The client's Accept-Language is indirectly available via UIViewRoot#getLocale(). The UIViewRoot is in turn in EL available as #{view}. So, this should do:

<h:inputText ... dir="#{view.locale.language eq 'ar' ? 'rtl' : 'ltr'}" />

Note that the dir is also supported on all other components and HTML elements, such as <h:form> and even <html>.

<html ... dir="#{view.locale.language eq 'ar' ? 'rtl' : 'ltr'}">

It would be applied on all its children unless overridden by a differently set dir. That would save you from repeating the very same attribute over all child components/elements.

Also note that the JSF will only accept locales which are explicitly registered in <locale-config> in faces-config.xml. So if you don't already have ar in there, then the above wouldn't work irrespective of Accept-Language header.

<application>
<locale-config>
<default-locale>en</default-locale>
<supported-locale>ar</supported-locale>
...
</locale-config>
</application>

Move if necessary the logic to a managed bean so that you can end up like below:

<h:inputText ... dir="#{localeManager.dir}" />

See also:

  • Localization in JSF, how to remember selected locale per session instead of per request/view

How to force Respect Language Direction from RTL to LTR and vice versa

Eventually, I have managed to solve this issue.

I couldn't find a way to force the change the RTL - LTR auto layout constraints, so i have decided to duplicate 2 additional storyboards for each language direction.
So, actually my app now contains 3 storyboards - Main.storyboard, StoryboardRTL.storyboard and StoryboardLTR.storyboard.

Main handles changes in language direction when it is performed from the settings on the iDevice, and RTL / LTR is has it's own orientation, to support changing the app language from inside the app.

When the user selects to change to an RTL language, I set the selected language in the UserDefaults:

    [[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:@"he", nil] forKey:@"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] synchronize];

and right after i call a method i have written in the AppDelegate, which changes the storyboard according to the language layout.

- (void)reloadStoryboard
{
UIStoryboard *storyboard;

switch ([AALanguageManager getCurrentLanguage]) {

case CurrentLanguageRTL:
storyboard = [UIStoryboard storyboardWithName:@"StoryboardRTL" bundle:[NSBundle mainBundle]];
break;
default:
storyboard = [UIStoryboard storyboardWithName:@"StoryboardLTR" bundle:[NSBundle mainBundle]];
break;
}

self.window.rootViewController = [storyboard instantiateInitialViewController];
[self.window makeKeyAndVisible];
}

This looks to the user as if the app restarted, and display the proper storyboard with the RTL / LTR layout.
When the user restarts the app again, the selected language is already set, and the main storyboard is displayed, with all the correct layout according to selected language.

Changing to right to left RTL programmatically

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.



Related Topics



Leave a reply



Submit