Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue with findBestAvailableLanguage #57

Closed
vikrantnegi opened this issue Jul 4, 2019 · 9 comments
Closed

Issue with findBestAvailableLanguage #57

vikrantnegi opened this issue Jul 4, 2019 · 9 comments

Comments

@vikrantnegi
Copy link

Bug

findBestAvailableLanguage is not falling to fallback language i.e. English when I change the language to Spanish.

Steps To Reproduce

Just like the example app, I'm using three languages English, French, and Arabic.

  1. When I change the language to French it shows the French Text
  2. Now, when I change the language to Spanish which is not supported and should fallback to English as a fallback, it remains French.

Environment info

"i18n-js": "^3.3.0",
"lodash.memoize": "^4.1.2",
"react": "16.8.3",
"react-native": "0.59.9",
"react-native-localize": "^1.1.3"

Describe what you expected to happen:

  1. When I change the language to Spanish it should fallback to English language

Reproducible sample code

Find the repo here

@zoontek
Copy link
Owner

zoontek commented Jul 4, 2019

@vikrantnegi Thanks for the repo, it helps a lot!
I just give it a try, and it worked as planned on my device (android 9). Which OS / version do you target?

Also, please note that the goal of findBestAvailableLanguage is to returns the best available translation here.

Given the following configs and English, French, and Arabic available translations:

👉 It will returns French, because Spanish translation isn't available, but your user actually prefer French over English.

👉 It will returns English because it's the first available translation in the list.

👉 It will returns undefined (so the retained value will be your fallback), because none of the user preferred languages are availables in your translations.

If you only want to check the first language in the list, a solution could be to use:

function checkFirstLanguageOrFallback(
  languageTags: string[],
): { languageTag: string, isRTL: boolean } | void {
  const { languageTag, languageCode, scriptCode, isRTL } = getLocales()[0];

  // languageTag format: en-US, zh-Hans-TW, etc.
  // (language code + script code if it exists + country code)
  if (languageTags.includes(languageTag)) {
    return { languageTag, isRTL };
  }

  // partialCode format: en, zh-Hans, etc.
  // (language code + script code if it exists)
  const partialCode = languageCode + (scriptCode ? `-${scriptCode}` : "");

  if (languageTags.includes(partialCode)) {
    return { languageTag: partialCode, isRTL };
  }

  // languageCode format: en, zh, etc.
  if (languageTags.includes(languageCode)) {
    return { languageTag: languageCode, isRTL };
  }
}

But I will not recommand it, it's not how iOS and Android pick native app translations.

@vikrantnegi
Copy link
Author

@zoontek Hey thanks for the detailed explanation. Now I understood how findBestAvailableLanguage is working. I didn't know that it is actually using the preference order of the phone.

I'm using the iOS simulator and when I changed the preference order I can get the English to be shown.

Simulator Screen Shot - iPhone X - 2019-07-04 at 18 26 36

I think this should be mentioned somewhere in the docs so others don't get confused.

@zoontek
Copy link
Owner

zoontek commented Jul 4, 2019

@vikrantnegi That's a good idea. I updated the README to add a warning about it: https://github.com/react-native-community/react-native-localize/tree/1.1.4#findbestavailablelanguage

@zoontek zoontek closed this as completed Jul 4, 2019
@likern
Copy link

likern commented Jun 3, 2020

@vikrantnegi Thanks for the repo, it helps a lot!
I just give it a try, and it worked as planned on my device (android 9). Which OS / version do you target?

Also, please note that the goal of findBestAvailableLanguage is to returns the best available translation here.

Given the following configs and English, French, and Arabic available translations:

It will returns French, because Spanish translation isn't available, but your user actually prefer French over English.

It will returns English because it's the first available translation in the list.

It will returns undefined (so the retained value will be your fallback), because none of the user preferred languages are availables in your translations.

If you only want to check the first language in the list, a solution could be to use:

function checkFirstLanguageOrFallback(
  languageTags: string[],
): { languageTag: string, isRTL: boolean } | void {
  const { languageTag, languageCode, scriptCode, isRTL } = getLocales()[0];

  // languageTag format: en-US, zh-Hans-TW, etc.
  // (language code + script code if it exists + country code)
  if (languageTags.includes(languageTag)) {
    return { languageTag, isRTL };
  }

  // partialCode format: en, zh-Hans, etc.
  // (language code + script code if it exists)
  const partialCode = languageCode + (scriptCode ? `-${scriptCode}` : "");

  if (languageTags.includes(partialCode)) {
    return { languageTag: partialCode, isRTL };
  }

  // languageCode format: en, zh, etc.
  if (languageTags.includes(languageCode)) {
    return { languageTag: languageCode, isRTL };
  }
}

But I will not recommand it, it's not how iOS and Android pick native app translations.

If I pass "en-US" to findBestAvailableLanguage() and system language is "en-GB" I would expect to return "en-US" as best available language, not undefined. Definitely better to provide same language, but different country then fallback to undefined which will lead to default language.

@HugoGresse
Copy link

HugoGresse commented Nov 9, 2020

I agree with @likern, this make more sens to fallback to a closer language than the app default one :/

@zoontek
Copy link
Owner

zoontek commented Nov 9, 2020

@HugoGresse I personally don't think it makes sense.

findBestAvailableLanguage will currently check (in order) for:

  1. exact language code + exact script code + exact region code
  2. exact language code + exact region code
  3. exact language code

What you want to do here is switching to en-US when the user uses en-GB instead of undefined (the general fallback). Which means you want en-US to be the fallback in case en is the language code.

const translations = {
 "en": () => require("../locales/en-US.json"), // en-US is the default "en" file
 "en-FR": () => require("../locales/en-FR.json"), // let's suppose we have a "en-FR" variant
};

const fallback = { languageTag: "fr", isRTL: false };

const { languageTag, isRTL } =
  RNLocalize.findBestAvailableLanguage(Object.keys(translations)) ?? fallback;
  • If the user has its phone configured to en-FR, it will return en-FR
  • If the user has its phone configured to en-US, it will return en (which correspond to en-US)
  • If the user has its phone configured to en-GB, en-BE or else, it will return en (which correspond to en-US)
  • If the user has its phone configured to ar, it will return fr (general fallback)

The issue here is the function input, not the output.

@HugoGresse
Copy link

HugoGresse commented Nov 9, 2020

Thanks for your comment @zoontek
I have a case that I don't get:

  1. My device in configured as: fr-FR, fr-BE, fr-US, en-US
  2. My app available lang are: fr-CA, en

I will assume that's because none of the "fr-XX" are available, the fr will be picked. But the result is "en" behing picked.
So if it's the input the issue, I would assume that I should have fr rather than fr-CA and split it only when the fr-FR shows up? But in that case, fr-BE will not have any french?

Is it a PEBKAC issue or?

EDIT yeah, probably a PEBKAC, I think the thing is that the fallback is dynamic, no the i18n-js fallback lang right?

@zoontek
Copy link
Owner

zoontek commented Nov 9, 2020

@HugoGresse If I understand it correctly, you (sort of) have:

const translations = {
 "fr-CA": () => require("../locales/fr-CA.json"),
 "en": () => require("../locales/en.json"),
};

const fallback = { languageTag: "en", isRTL: false }; // here is your fallback in case findBestAvailableLanguage returns undefined

const { languageTag, isRTL } =
  RNLocalize.findBestAvailableLanguage(Object.keys(translations)) ?? fallback;
  • If you want to load fr-CA even if your phone is configured with fr-FR, change the translation key to fr.
  • If you don't want it, just keep it as it (in some languages, regional dialects might be really confusing, there is more than just the en-US & en-GB case).

By making findBestAvailableLanguage picking fr-CA automatically, having this choice of using a fallback for the language code or not doing it become impossible.

EDIT: i18n-js is the lib used in the examples, but not tied to the library at all. I personally use https://github.com/koala-interactive/frenchkiss.js 🙂

@zoontek
Copy link
Owner

zoontek commented Nov 9, 2020

I will assume that's because none of the "fr-XX" are available, the fr will be picked. But the result is "en" behing picked.
So if it's the input the issue, I would assume that I should have fr rather than fr-CA and split it only when the fr-FR shows up? But in that case, fr-BE will not have any french?

If the translation key is fr, fr-BE will fallback to fr.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants