Supporting Multi-Language in React Native Applications

Lee young-jun
5 min readAug 13, 2023

--

A few months ago, olulo UX Team made the decision to offer an English version of KICKGOING.

This decision was taken in response to the demand foreigners who want to use personal mobility out of central city. And the CX Team has also been requested by foreign customers to provide support in English.

Localization

What is Localization?

Localization involves adapting product content to match the language preferences of its intended audience.

When I was developing iOS Apps, I created Localization.strings file for the feature. On the left side is the string key, and on the right side is the translated string.

To get translated string I used localizedString function or NSLocalizedString class.

bundle.localizedString(forKey: "Korean", value: defaultText);

NSLocalizedString("Korean", comment: "")

If the string key contains spaces, it needs to be wrapped in quotation marks.

React-Native

In React Native, there isn’t a built-in feature for localization.

As a result, we need to use open-source libraries. Two popular options are react-native-localize and react-native-i18n. Therefore I chose react-native-localize because the number “18” sounds like a bad term in Korean culture and it is more complex.

To retrieve the current language code, I obtained the primary locale from the react-native-localize.

import localize from 'react-native-localize'
...
localize?.getLocales()?.[0]?.languageCode

Wait.. what is ‘locale’?

A locale refers to the combination of a user’s preferred language and region settings. It influences keyboard layouts and the display of text accordingly.

I can see the Language and region information in the Settings app like this: My primary language is Korean, so the languageCode will be set as ko-KR.

According to OS Version, Device provide Preffered Language to the specific app. Users can set a different language for the entire OS language.

Strings

The traditional method of providing translated strings is using files like Localizable.strings. However, I wanted to explore a new solution because I disliked long names like ‘MY_DETAIL_SCREEN_SUBVIEW_NAME_INPUT_PLACEHOLDER’.

and also, the fact that they are in uppercase!!

I planned two rules for structuring localization.

Plan 1

First plan is to define all strings in same file.

export const authNumberScreenStrings = {
title: {
ko: '인증번호를 입력해주세요.',
en: 'Enter verification code',
},
}

Plan 2

Another plan is to create a string file each language.

export const Koreans = {
title: '인증번호를 입력해주세요.',
}

export const Englishes = {
title: 'Enter verification code',
}

We chose second rule by discussing with App developers.

Getting Strings

To retrieve localized strings in screens or views as desired,

import { Koreans as ko } from './ko'
import { Englishes as en } from './en'

export const authNumberScreenStrings = (): typeof ko => LanguageService.select({ ko, en })

I developed a function similar to Platform.select.

Platform.select({
ios: { zIndex: val },
android: { elevation: val },
})

You might wonder

why ‘authNumberScreenStrings’ is a function?

This is because the app retains the constant value even if it's relaunched by changing the language.

LanguageService.select

select: <T>(
specifics: { [platform in LanguageCode]?: T } | { [platform in LanguageCode]: T } // & { default: T }
): T => {
const lang = currentLanguage()
const selectedValue = specifics[lang]
if (selectedValue) return selectedValue

return selectedValue ?? specifics.en // default
}

Usage

Using the defined strings, I can localize texts and images as shown below.
I retrieved strings for the screen or view.

const strings = Strings.screen.authNumber

And extracted title from strings.

<Text style={styles.header}>{strings().title}</Text>

I am also able to retrieve image sources using useImage like this.

const images = useImages(images => images.popups.bike.firstRent())

Hook

In functional components, I aimed to create a new hook that would automatically change the strings when the language is switched.

const strings = useStrings(strings => strings.screen.authNumber())

To create such a hook, I needed to create a hook that monitors the current language. The useLanguage hook retrieves the language from Redux, although I won't explain the selector in this article.

export const useLanguage = (): LanguageCode | undefined => { language witt redux useSelector })

The useStrings hook emits new strings based on the monitored language.

export const useStrings = <TSelectedStrings>(
selector: (strings: IStrings) => TSelectedStrings,
useShallowEqual = false
): TSelectedStrings => {
const language = useLanguage()
const [selectedStrings, setSelectedStrings] = useState<TSelectedStrings>(selector(Strings))

useEffect(() => {
if (!language) return
const newSelectedStrings = selector(Strings)

if (useShallowEqual && shallowEqual(selectedStrings, newSelectedStrings)) return

setSelectedStrings(newSelectedStrings)
}, [language])

return selectedStrings
}

Format

Some strings need to include formatting.

export const Koreans = {
...,
welcome: '%s님, 반가워요! 👋',
}

export const Englishes = {
...,
welcome: '%s,\ngood to see you again! 👋',
}

sprint-js

owever, React-Native doesn’t inherently support formatting. To address this, I installed the sprint-js library and added an extension method to the String class.

String.prototype.formatted = function (...args: any[]) {
return sprintf(this.valueOf(), ...args)
}

Usage

By utilizing the newly created formatted method, I can easily format strings as needed.

ToastMessage.show({
message: strings().welcome.formatted(this.props.identifiedName),
...
})

References

--

--

No responses yet