create-expo-stack icon indicating copy to clipboard operation
create-expo-stack copied to clipboard

i18n error on expo app web

Open BrazilianJoe opened this issue 8 months ago • 2 comments

Hello there!

I digged a bit into the code.

When starting up a fresh project using: npx create-expo-stack@latest poster --tamagui --expo-router --supabase --i18next

On languageDetector.ts, these lines returns an error when testing or trying to build for production Expo web:

const locales = Localization.getLocales();
const firstLanguageCode = locales[0].languageCode ?? 'en';

Localization.getLocales() returns an empty array []. This is not in accordance to the documentation. As a result, the next line causes an error when testing Expo web.

https://docs.expo.dev/versions/latest/sdk/localization/#localizationgetlocales List of user's locales, returned as an array of objects of type Locale. Guaranteed to contain at least 1 element.

Image

Digging deeper:

In the module expo-localization/ExpoLocalization.ts, getLocales() start with:

  getLocales(): Locale[] {
    const locales = getNavigatorLocales();

getNavigatorLocales() is defined on the same file:

const getNavigatorLocales = () => {
  return Platform.isDOMAvailable ? navigator.languages || [navigator.language] : [];
};

On expo-modules-core/src/Platform.ts, Platform defines the isDOMAvailable property:

isDOMAvailable,
 /**
  * Denotes if the current environment can attach event listeners
  * to the window. This will return false in native React
  * runtimes and Node.js.
  */

It reads the defaults from environment/browser.ts:

// In standard node environments there is no DOM API
export const isDOMAvailable = false;
export const canUseEventListeners = false;
export const canUseViewport = false;

This is causing the inconsistent behavior on web, which is not in accordance to the documentation.

I worked around it with the following steps:

  1. install i18next-browser-languagedetector (official i18n package)
  2. create file core/i18n/languageDetectorWeb.ts with this code:
import LanguageDetector from 'i18next-browser-languagedetector';

export const languageDetectorWeb = new LanguageDetector();
  1. changed core/i18n/init.ts to read:
...
import { languageDetectorWeb } from './languageDetectorWeb'; //added new language detector just for the web
...
 return i18n
    .use(Platform.OS === 'web' ? languageDetectorWeb : languageDetector) // use one or the other language detector depending on platform
...

Not sure if it's the ideal solution, as I am working around something that's plainly not working as the documentation says.

But since I created the app using create-expo-stack, I figured you may want to implement this to have a more feature-complete solution out-of-the-box.

BrazilianJoe avatar Mar 03 '25 10:03 BrazilianJoe

will inspect the issue here as I happen to be considering another tool for internationalization but I would not expect a fix in the coming couple weeks as I have other things that are of higher priority atm

danstepanov avatar Mar 29 '25 01:03 danstepanov

I happen to be considering another tool for internationalization

Im curious, what other tool would that be? 😄

ottob avatar May 17 '25 19:05 ottob