facebook / react-native

A framework for building native applications using React
https://reactnative.dev
MIT License
118.08k stars 24.19k forks source link

I18nManager lacks force LTR API #33423

Closed DrRefactor closed 1 month ago

DrRefactor commented 2 years ago

Description

When OS language is set to RTL, apps are rendered in RTL by default.

Looking at sources of I18nManager: https://github.com/facebook/react-native/blob/8bd3edec88148d0ab1f225d2119435681fbbba33/ReactAndroid/src/main/java/com/facebook/react/modules/i18nmanager/I18nManagerModule.java (and corresponding RCTI18nUtil.m for iOS), I see that forceRTL(false) does not force LTR, but stops forcing RTL.

This can be worked around using:

function forceLayout(rtl: boolean) {
  I18nManager.allowRTL(rtl);
  I18nManager.forceRTL(rtl);
}

However, having proper API would be consistent with handling opposite case (LTR OS, forcing RTL).

Version

0.67.0

Output of npx react-native info

not related, all RN versions affected

Steps to reproduce

Using Android OS.

  1. Set OS language to hebrew
  2. Open app
  3. Try forcing LTR layout

Snack, code example, screenshot, or link to a repository

No response

HagarAyad commented 2 years ago

Try using this package https://www.npmjs.com/package/react-native-restart when the app opens , check if is RTL call function forceLayout(false)

function forceLayout(rtl: boolean) {
  I18nManager.allowRTL(rtl);
  I18nManager.forceRTL(rtl);
  RNRestart.Restart();
}

I hope this will fix your problem

DrRefactor commented 2 years ago

Hi, thanks for your help.

Let me rephrase a bit though. It's not that I cannot achieve LTR layout at all. I mean - I18nManager lacks such API, and using allowRTL() seems hacky.

abumalick commented 2 years ago

Try using this package npmjs.com/package/react-native-restart when the app opens , check if is RTL call function forceLayout(false)

function forceLayout(rtl: boolean) {
  I18nManager.allowRTL(rtl);
  I18nManager.forceRTL(rtl);
  RNRestart.Restart();
}

I hope this will fix your problem

Thank you it works with your solution. It is strange because it was working using forceRTL only in previous versions of react native. I just updated to react-native 0.68.2

vladyslavNiemtsev commented 1 year ago

@abumalick I think it's because this library doesn't have support for new architecture in React Native

meypod commented 1 year ago

the workaround does not work (I'm on RN 0.72.3) it works if the app is reloaded in debug mode, but it does not seem to persist between restarts

Edit: there was a bug in my own code. I was setting allowRTL on native side (for some other reason I don't remember)

omaranwaar commented 1 year ago

the workaround does not work (I'm on RN 0.72.3) it works if the app is reloaded in debug mode, but it does not seem to persist between restarts

Edit: there was a bug in my own code. I was setting allowRTL on native side (for some other reason I don't remember)

I'm on RN 0.72.3 as well and set the sharedI18nUtilInstance.forceRTL(this,true); in mainApplication.java, it works as expected and change the layout to RTL by default, but when I try to change the language to english and change the layout to LTR it just updates the language to english but layout stays the same RTL. @meypod

saadi-ninjasCode commented 1 year ago

the workaround does not work (I'm on RN 0.72.3) it works if the app is reloaded in debug mode, but it does not seem to persist between restarts Edit: there was a bug in my own code. I was setting allowRTL on native side (for some other reason I don't remember)

I'm on RN 0.72.3 as well and set the sharedI18nUtilInstance.forceRTL(this,true); in mainApplication.java, it works as expected and change the layout to RTL by default, but when I try to change the language to english and change the layout to LTR it just updates the language to english but layout stays the same RTL. @meypod

@meypod ,@omaranwaar you need to restart the app for this (Changin fo Layout). You can use the react-native-restart package for this purpose.

omaranwaar commented 1 year ago

@meypod ,@omaranwaar you need to restart the app for this (Changin fo Layout). You can use the react-native-restart package for this purpose.

Thank you very much @saadi-ninjasCode for your concern but I am restarting the app as well, working 100% on iOS, but it is not changing the layout in android if I add sharedI18nUtilInstance.forceRTL(this,true); in mainApplication.java.

saadi-ninjasCode commented 1 year ago

@meypod ,@omaranwaar you need to restart the app for this (Changin fo Layout). You can use the react-native-restart package for this purpose.

Thank you very much @saadi-ninjasCode for your concern but I am restarting the app as well, working 100% on iOS, but it is not changing the layout in android if I add sharedI18nUtilInstance.forceRTL(this,true); in mainApplication.java.

@omaranwaar , I remember I faced this issue and removed the sharedI18nUtilInstance.forceRTL(this,true); in mainApplication.java. While I add android:supportsRtl="true" in AndroidManifest.xml Then I use I18nManager.forceRTL & I18nManager.allowRTL function on React Native side.

with sharedI18nUtilInstance.forceRTL(this,true); app doesn't change the layout from RTL to LTR because of true param.

One more thing, (Specially for @DrRefactor ) In AndroidManifest.xml , add |layoutDirection|locale option in android:configChanges. When you change the system language your text orientation will change too. For example, if you are in English mode and you switch the mobile system language to any RTL language. This option will change the Text orientation to RTL.

I hope this will sort out your & @DrRefactor 's problem.

omaranwaar commented 1 year ago

@saadi-ninjasCode thanks your response but I did remove the sharedI18nUtilInstance.forceRTL(this,true); from mainApplication.java but it doesn't work. I need the RTL by default and it doesn't do that if I remove this. I need to restart the app again to change the layout then

USB18 commented 11 months ago

@omaranwaar facing the same issue but I am restarting the app is iRtl is false the first time rendered but in some devices, it's going in a loop.

const activeLanguage = await getStorage('active_lan');
            const defaultLanguage = "ar";
            const languageToUse = hasValue(activeLanguage) ? activeLanguage : defaultLanguage;
            await i18n.changeLanguage(languageToUse)
            if (languageToUse == "ar") {
                NativeModules.RTLManager.setRTL(true);
                !I18nManager.isRTL && restart_app()
            } else if (languageToUse == "en") {)
                NativeModules.RTLManager.setRTL(false);
                I18nManager.isRTL && restart_app()
            }

Have you found any success?

GaryGiebler commented 10 months ago

In my app, we don't know which language a user prefers until they login the first time. Is there a way to forceUpdate the components without restarting the app? We hate to have them restart the app and have to login again immediately after logging in for the first time. (bad user experience)

omaranwaar commented 7 months ago

@omaranwaar facing the same issue but I am restarting the app is iRtl is false the first time rendered but in some devices, it's going in a loop. Have you found any success?

@USB18 I am using this function to restart and It is not going into loop. It just restarts the android app once on first installed app only.

const appInitialize = async () => {
  const storedLanguage = await AsyncStorage.getItem('appLanguage');
  if (storedLanguage === null && Platform.OS === 'android' && !I18nManager.isRTL) {
    I18nManager.allowRTL(true);
    I18nManager.forceRTL(true);
    await AsyncStorage.setItem('appLanguage', 'ar');
    RNRestart.Restart();
  }
};
monkeedev commented 7 months ago

Faced the same issue today. Try to add I18nUtil.getInstance().allowRTL(this, false); in your project's MainApplication.java as it is mentioned down below:

@Override
public void onCreate() {
    super.onCreate();
    // here
    I18nUtil.getInstance().allowRTL(this, false);

    // other code
}
react-native-bot commented 1 month ago

This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.

react-native-bot commented 1 month ago

This issue was closed because it has been stalled for 7 days with no activity.