Ray
Ray

Reputation: 9674

Force RTL on LTR devices

The title says it all. I found a couple of ways to set the application's default layout to RTL using Facebook's I18nManager, but it does that only on second app launch.

code example: I18nManager.forceRTL(true)

I do not want to give users the ability to change languages because the application itself is in Arabic. I've searched everywhere but all talk about how to support RTL and not actually use it as the default layout.

Is there a way to achieve this with I18nManager or do I have to do a couple of changes to my native code?

Upvotes: 16

Views: 8711

Answers (5)

grreeenn
grreeenn

Reputation: 2555

2024: Kotlin & Expo

Kotlin

the answers by @ataravati and @MageNative are both correct, but they're in Java - while the modern android apps are written in Kotlin. So do add the RTL support in the manifest, and then, in your MainActivity.kt, do:

// import these in the imports block
import android.content.res.Configuration
import java.util.Locale

// and then, inside the class
override fun onCreate(savedInstanceState: Bundle?) {
  // ...[some generated code]

  val locale = Locale("he_IL")
  Locale.setDefault(locale)
  val config = Configuration()
  config.setLocale(locale)
  resources.updateConfiguration(config, resources.displayMetrics)
  
  // this is autogenerated too, needs to be the last row of the function AFAIK
  super.onCreate(null)
}

Expo

The latest React Native team recommendation is to use frameworks, and Expo is the only one out there. The way you change native code with Expo is by using plugins and mods (see docs). So here's the expo plugin that will implement the above changes to your built native code every time the app is built from scratch (android folder doesn't exist):

const { withMainActivity, withAndroidManifest } = require('expo/config-plugins');

function indent(text, spaces = 4) {
    return text
        .split('\n')
        .map((line) => ' '.repeat(spaces) + line)
        .join('\n');
}

function updateMainActivity(fileContent) {
    const importStatement = 'import android.content.res.Configuration\nimport java.util.Locale\n';
    const rtlSupportCode = [
        indent('val locale = Locale("he_IL")'),
        indent('Locale.setDefault(locale)'),
        indent('val config = Configuration()'),
        indent('config.setLocale(locale)'),
        indent('resources.updateConfiguration(config, resources.displayMetrics)'),
    ];

    const updatedContent = fileContent.split('\n');

    // Find the index of the class declaration
    const classIndex = updatedContent.findIndex((line) => line.includes('class MainActivity'));

    // Insert the import statement before the class declaration
    updatedContent.splice(classIndex, 0, importStatement);

    // Find the index of the onCreate method
    const onCreateIndex = updatedContent.findIndex((line) => line.includes('override fun onCreate'));

    // Find the index of the super.onCreate call within the onCreate method
    const superOnCreateIndex = updatedContent.findIndex(
        (line, index) => index > onCreateIndex && line.includes('super.onCreate'),
    );

    // Insert the RTL support code before the super.onCreate call
    updatedContent.splice(superOnCreateIndex, 0, ...rtlSupportCode);

    return updatedContent.join('\n');
}

const withRtlAndroidMainActivity = (config) => {
    return withMainActivity(config, async (config) => {
        config.modResults.contents = updateMainActivity(config.modResults.contents);
        return config;
    });
};

const withRtlAndroidManifest = (config) => {
    return withAndroidManifest(config, async (config) => {
        manifest = config.modResults.manifest;
        manifest.application[0].$['android:supportsRtl'] = true;
        return config;
    });
};

module.exports = [withRtlAndroidMainActivity, withRtlAndroidManifest];

and then use plugin chaining to apply the plugins. Voila :)

Upvotes: 1

blueware
blueware

Reputation: 5381

You have to do this in two places:

First: In your Manifest's application tag, add support for RTL like this:

<application
    ...
    android:supportsRtl="true"
    ..>
</application>

Second one: Add this as first line of your onCreate() method of your activity:

getWindow().getDecorView().setLayoutDirection(View.LAYOUT_DIRECTION_RTL);

If you have a base activity, you can do the same and it will be applied to other child activities.

Upvotes: 0

ataravati
ataravati

Reputation: 9155

MageNative's answer is correct. The only thing is setting the locale like that is deprecated. You need to use the setLocale() method, like this:

Locale locale = new Locale("fa_IR"); // This is for Persian (Iran)
Locale.setDefault(locale);
Configuration config = new Configuration();
config.setLocale(locale);
getApplicationContext().getResources().updateConfiguration(config, getApplicationContext().getResources().getDisplayMetrics());

You'll need to import java.util.Locale and android.content.res.Configuration.

Upvotes: 4

MageNative
MageNative

Reputation: 702

First, add RTL support in the manifest like this:

<application
    android:name=".application.SampleApplication"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
</application>

Then, on your launcher activity add the following code in onCreate().

Locale locale = new Locale("ar");
Locale.setDefault(locale);
Configuration config = new Configuration();
config.locale = locale; 
getApplicationContext().getResources().updateConfiguration(config, getApplicationContext().getResources().getDisplayMetrics());

Upvotes: 9

Parazok
Parazok

Reputation: 359

add this line of code to the most top of the onCreate method (before super and setContentView) of all of your activities:

getWindow().getDecorView().setLayoutDirection(View.LAYOUT_DIRECTION_RTL);

and sure that you have supportRtl equal to True in manifest.

Upvotes: 14

Related Questions