Internationalising a mobile app is two jobs, and only one of them is in your code.
One job is inside the project: reading the phone's language, swapping the strings your screens render, formatting dates and money, and surviving a language that reads right to left. The other is inside App Store Connect and Play Console: the title, the description, the keywords and the screenshots a stranger reads before they install anything. Doing one does not do the other, and Apple says so in its own help. Languages on the App Information page are part of your app's metadata, and that differs from adding languages to your app when you localise the binary.
If you were handed a React Native and Expo project rather than typing it, the app half has already been half decided for you by whatever is in package.json, which is the normal position after building React Native apps without coding. The store half has not been started at all. Every key name, field name and rule below comes from Expo's, Apple's and Google's own current documentation, read on 17 October 2026.
Start hereThe short version
Translate the app in your repository. Localise the listing in a console.
Inside the app, the cost of a translation depends on where the string lives. Copy your screens render is JavaScript, so a new language file is a JavaScript change and can travel as one of your over the air updates. The app name, the permission prompts and the list of locales the operating system believes you support are native values. Expo writes them during prebuild, which makes them a new build, a new TestFlight upload and a new Play release.
The store listing is neither of those. It is typed into a web form and it ships on the store's schedule, not yours. There is no build and no bundle, but there are rules you do not control: Apple lets you manage languages only while the app status is editable, and Google will show machine translations of your listing to users whether or not you added any. Treat them as two projects with two owners and the whole thing gets simpler.
What does internationalising the app itself involve?
Three pieces, and they are easy to confuse. The first is finding out what the phone is set to. That is expo-localization, and it is the only part Expo actually ships. Its getLocales and useLocales return a list guaranteed to hold at least one locale, and each entry carries languageTag, languageCode, regionCode, textDirection, measurementSystem, currencyCode, the decimal and digit grouping separators and the temperature unit. One detail saves a bug report later: on iOS the results stay the same for as long as the app is running, while on Android a user can change locale settings without restarting, so Expo tells you to read the values again whenever the app comes back to the foreground.
The second piece is the translation library, and Expo deliberately does not choose it for you. Its guide uses i18n-js in the examples and names Lingui, fbtee, React i18next and Intlayer as alternatives. If someone else generated this project, that decision has already been made and it is sitting in package.json. Look there before you install a second one, because two i18n libraries in one app is a week nobody enjoys. For dates, numbers, currency, units and plurals, Expo is blunt: do not localise those by hand, use the standard Intl APIs, which work on all platforms if the app runs on Hermes. The catch it also prints is that Intl will not tell you the device locale, so expo-localization stays in the project either way.
The third piece is native, and it is the one people discover from a one-star review. The name under your app icon and the sentence in a permission prompt are not in your translation files. Expo handles them through the locales map in the app config, which points a language code at a JSON file. That file holds an ios object, where you set CFBundleDisplayName and any usage description keys such as NSContactsUsageDescription, and an android object with app_name. For iOS you must set ios.infoPlist.CFBundleAllowMixedLocalizations to true before the list does anything. During prebuild Expo writes those values into InfoPlist.strings for the matching locale. Three more options live on the expo-localization config plugin: supportsRTL, which defaults to true, forcesRTL, which defaults to false, and supportedLocales, which is what enables per-app language selection in the phone's own settings. Expo says these set properties that require a new native build, so none of them is a JavaScript change.
Two of these details moved recently, which matters if you are copying an example you found. The iOS-only Localizable.strings option inside a locale file arrived in SDK 55, and the right to left default changed in SDK 58. Check your project against the Expo SDK release notes before you assume a snippet applies to you.
Expo, localization guide, modified 7 September 2026, read 17 October 2026
Sort it first
Which job is this, and does it need a new build?
Tick what you want to appear in another language. Each one belongs to either the app or the store listing, never both, and the two have different costs.
Nothing ticked. Most people who say they want the app in Spanish mean three of these at once and have budgeted for one.
What breaks when the language reads right to left?
Arabic, Hebrew, Urdu and Persian do not just need different words. They mirror the layout. Expo says right to left support is enabled by default, and that on earlier versions it was enabled by default everywhere except Expo Go, where it was disabled. That single difference explains a lot of confusion: a screen can look correct in Expo Go and wrong in a real build, or the reverse, with nothing in your code having changed. If you are testing RTL, test it in a build.
On iOS there is a condition people miss. The device language has to be one of the app's supported locales for the layout to flip at all. Set an iPhone to Arabic and open an app that never declared Arabic, and you get Arabic text in a left to right layout, which looks like your code is broken when it is your app config that is incomplete. React Native's own I18nManager reference describes the same gate from the other side: on iOS isRTL is true only when the user's preferred language is an RTL language and the application-defined localizations include it, and on Android only when the manifest declares supportsRTL on the application element.
I18nManager is also the runtime switch, and it is a worse tool than it looks. allowRTL and forceRTL both take effect on the next application start rather than immediately, and both persist across restarts. React Native's own documentation says to avoid forcing RTL in production precisely because it needs a full restart, and that forceRTL is meant for development and testing. In Expo Go none of it holds, because Expo Go resets RTL preferences when it opens the launcher or an individual project. So a language switcher inside your app that flips direction without a restart is not a small feature. Expo's guide points you at wrapping the navigator and passing the direction down instead.
The layout work itself is mechanical and tedious. Use start and end in your styles rather than left and right, and the framework mirrors them for you. The exception is the one that leaves most of the damage: textAlign does not accept start or end, so you have to set left or right explicitly and therefore conditionally. Budget a pass over every screen in the project, and remember that if someone else generated those screens they almost certainly used left and right, because that is what reads naturally when you are writing English.
Expo, localization guide, right to left section, read 17 October 2026
What does localising the store listing involve instead?
Typing, in a web form, with no reference to your code at all. Apple draws the line itself: languages on the App Information page in App Store Connect are part of your app's metadata, and that differs from adding languages to your app when you localise the binary. You can ship an app that speaks eleven languages and sell it with an English-only product page, or publish a product page in eleven languages for an app that only speaks English. Both are common and both are mistakes, but they are separate mistakes.
The path in App Store Connect is the primary language pop-up menu at the top right of your app. Scroll to the Not Localized section, hold the pointer over the language and locale you want, and click the add button. Two constraints bite before you start. You can manage languages only while the app status is editable, so this is not something to attempt mid-review. And the required role is Account Holder, Admin, App Manager or Marketing, which is worth checking early, because the person who wrote the German copy is often not the person with a role that can paste it in.
Where a field lives decides how far it travels. App name and privacy policy URL sit under App Information and are shared across platforms, so a localization added from a platform version is automatically added to the shared metadata and to your other platforms. Description and keywords are per platform version. When you add a language, Apple copies the primary language's screenshots and properties into it, with description and keywords left out, which means a new language starts with the right pictures and empty prose. Removing a language is not neutral either: the countries or regions that displayed app information in it revert to what you entered for the primary language. Apple lists 50 languages and locales for metadata, and says the language a given customer sees depends on the App Store language for their location, their device's language settings, the languages you added and your primary language.
Choose the primary language before you launch, because changing it afterwards is the single most expensive move here. Apple requires that the language was already added to a previous version, that App Review has approved it, and that you have already uploaded and had approved screenshots in it for every supported platform, at sizes matching your current primary language. With a custom product page configured you need screenshots in that language for each of those too. All of this lands in the same pass as publishing to the App Store, so decide it there rather than a year later.
App Store Connect Help, localize app information, read 17 October 2026
What do the stores translate for you, and what do they miss?
Google does more than Apple, and one of the things it does will not help you. Play Console Help, in its article on translating and localising your app, read 17 October 2026, gives three routes for the listing. You add your own translations from the Store listing page through Manage translations, Select languages, then Add or remove languages and Apply and Save. You order paid human translation from a third-party vendor, which Google prices from about 0.07 US dollars a word with delivery inside seven days, and you deal with that vendor directly. Or you take free machine translation of your store listing and in-app products, available in Arabic, French for France, German, Indonesian, Italian, Japanese, Portuguese for Brazil, Spanish for Latin America, Spanish for Spain and Thai. Google's own warning on that last one is that these are machine translations and are not reviewed or approved by humans.
Do nothing and Google still acts. Its help says that if you add no translations, automated translations of your store listing will be available to Google Play users, who see an option to view the default language instead, and that those automated translations are not available in Armenian, Raeto-romance, Tagalog and Zulu. There is a trap attached: add translated text without localised graphics and your app's graphic assets show from the default language. That is how a Japanese listing ends up carrying English screenshots, and nobody tells you, because from Google's side nothing is missing.
Now the part that matters most if a generator produced your project. Play will also translate your app's own strings. Its help says app strings are automatically translated using Gemini models at no cost, that the string table is built from the strings.xml file in each bundle you upload to a draft release, and that preview is available once you have an uploaded bundle in a draft release. In a React Native and Expo project, strings.xml holds the native application name and whatever native libraries contributed. Your screens are JavaScript. Their text is not in that file and never will be. So the free Gemini service has almost nothing to work on here, and the one string it can reach is the app name, which Expo already handles through the locales map. Three further conditions are worth knowing before anyone turns it on: existing translations for the languages you pick get overridden, switching it on applies to current and future bundles, and if you select right to left languages Google says your app has to be RTL-ready already.
Apple's side is quieter. Its two localization help pages, App Store localizations and Localize app information, cover metadata only, and neither offers to translate anything for you or mentions a translation service. Read that as what those two pages say rather than as proof that nothing exists anywhere in App Store Connect: I checked the localization pages, not every page in the help. Either way, plan on supplying your own Apple copy, and plan on reading whatever Google generates before you let it represent you.
App Store Connect Help, localize app information, read 17 October 2026
The same word, two jobs, two owners
| The question | Translating the app | Localising the store listing | Same answer for both |
|---|---|---|---|
| Where the work happens | Your repository, in JavaScript and in the app config | App Store Connect and Play Console, typed into a form | No |
| Needs a new native build | Only when you add a locale or change an RTL option | Never | No |
| Can ship over the air | Yes, if the change is only in the JavaScript | Not applicable, it is not in the app | No |
| The store does some of it for you | Play translates strings.xml with Gemini, free | Play auto-translates the listing, Apple's localization pages do not | No |
| Blocked by app status or review | No | Yes, languages are editable only in some app statuses | No |
| A wrong choice is expensive to undo | A dropped locale is a rebuild | A changed primary language needs prior App Review approval | Yes |
Where this lands if a builder generated your app
Newly writes a real React Native and Expo project that you own, so every key name on this page is a key name in your repository. You can open the app config, read the locales map, see which i18n library is in package.json and change any of it, by hand or by asking for it. Two-way GitHub sync means a translator can work in the repository rather than in a spreadsheet you later paste from. It is $25 a month, with no free tier.
The split in this page is also a split in how changes reach people. New copy in an existing language is JavaScript, so it can ride an update. A new locale is native, so it is a new build: Newly uploads iOS builds to TestFlight, publishes to Google Play internal testing, and can produce a standalone Android APK. Plan language launches around builds rather than around sprints, and you will stop being surprised by a translation that is correct in the repository and absent on the phone.
The honest limit is the store half. No generator fills in your product page, and on iOS nobody else can press submit for you. Apple's guideline on apps built from a template or an app generation service requires the provider of the app's content to submit, which is you, so the 50 metadata localizations, the screenshots per language and the choice of primary language are yours to do. The good news is that this half needs no build, no tooling and no engineer. It needs someone who can write in the language you are selling into.
Questions people ask about internationalising a mobile app
Two jobs that share a name. The first is inside the project: reading the phone's language, swapping the strings your screens render, formatting dates and money, and surviving a language that reads right to left. The second is inside App Store Connect and Play Console: the title, the description, the keywords and the screenshots someone reads before they install. Neither job does the other. Apple states it plainly in its own help: languages on the App Information page are part of your app's metadata, and that differs from adding languages to your app when you localise the binary.
Pick the language, then do both halves on purpose
Write the app half into the next build and the listing half into the next submission. Two lists, two owners, two dates. That is the whole method, and it is the only part of this that people get wrong.
Start building