How to Localize Your Android App with AI
Set up values/ folders, translate strings.xml with AI, switch languages in Kotlin, then upload screenshots so PTC (Private Translation Cloud) reviews the running Android app. By the end of this guide you will have a multilingual Android app ready to ship, with every target language verified by visual review before release.
This guide assumes you have an existing Android project in Android Studio. If you are starting fresh, scaffold a new project with the Empty Activity template first.
How Android resolves localized resources from values-{locale}/ folders
Android organizes localized resources in res/values-{locale}/ folders inside app/src/main/. The default folder is values/, which holds the source-language strings. Each target language gets its own values-{locale}/ folder.
app/src/main/res/
values/strings.xml # source language (typically English) - REQUIRED
values-es/strings.xml # Spanish
values-fr/strings.xml # French
values-de/strings.xml # German
values-ja/strings.xml # Japanese
values-zh-rCN/strings.xml # Simplified Chinese (region-specific)
Always include the default values/ folder without a language code. That is your fallback. Android picks the right folder at runtime based on the user's device locale. If the user's language is Spanish but you do not have values-es/, Android falls back to values/. The fallback is silent. A partially translated app shows English mixed with Spanish, with no error.
The locale codes follow BCP 47. es for Spanish, pt-rBR for Brazilian Portuguese (note the r prefix for the region), zh-rCN for Simplified Chinese.
Move every user-facing string into strings.xml
Always use string resources stored in strings.xml. Never hardcode text directly in Kotlin/Java code or in an android:text= layout attribute. Hardcoded text cannot be translated.
Move every user-facing string into res/values/strings.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">My Notes</string>
<string name="welcome_message">Welcome, %1$s!</string>
<string name="notes_count_zero">No notes yet</string>
<plurals name="notes_count">
<item quantity="one">%d note</item>
<item quantity="other">%d notes</item>
</plurals>
<string-array name="categories">
<item>Personal</item>
<item>Work</item>
<item>Travel</item>
</string-array>
</resources>
Reference each string in Kotlin via the generated R.string.* ID:
val appName = stringResource(R.string.app_name)
val welcome = stringResource(R.string.welcome_message, userName)
val counts = pluralStringResource(R.plurals.notes_count, notes.size, notes.size)
Key conventions PTC respects:
- Positional placeholders. Use
%1$s,%2$d,%3$finstead of%s,%d. Some languages need to reorder the arguments. Positional placeholders make that possible. PTC recognizes and preserves them automatically. <plurals>for countable nouns. Do not writeif (count == 1) "1 note" else "$count notes"in code. Use<plurals>withone/other/few/manyquantities. PTC generates the right plural categories per target language (1 form for Japanese, 2 for English, 4 for Polish, 6 for Arabic).<string-array>for static lists. PTC translates each item while preserving the array structure.- Escape apostrophes (
\') and quotes (\") to avoid XML parsing errors.
Translate strings.xml with PTC in 5 steps
- Start a PTC project in your dashboard. Upload your
values/strings.xml, set your output paths, and choose target languages. PTC supports 40+. During the free trial, you can select any two. PTC uses the source language to pick the right MT engine and to skip strings where source equals target. - Review the auto-generated app description. PTC creates a short description of your Android app from the uploaded file. Edit it to better describe your app and audience. PTC uses this context to match the right tone and formality for each language.
- Optionally upload existing translations. If you already have translated
values-*/strings.xmlfiles, upload them so PTC can match your existing style. Otherwise, translate from scratch. - Translate and review. PTC respects every
name=key (your keys never change), every positional placeholder, and generates the correct plural categories per target. If a translation exceeds your length limits, PTC highlights it so nothing slips through. Edit any string in place or ask PTC to retranslate with the constraint in mind. - Download the translated
strings.xmlper language as a ZIP. Each file is structurally identical to your source. Same keys, same arrays, same plural tags. Translated values.
Drop the translated strings.xml files into values-{locale}/
Drop each translated file into the matching values-{locale}/ folder:
app/src/main/res/values-es/strings.xml # from PTC
app/src/main/res/values-fr/strings.xml # from PTC
app/src/main/res/values-de/strings.xml # from PTC
Rebuild the project. Android picks up the new resources automatically. To verify, change your emulator or device language and relaunch the app. The UI should appear in the new language.
Add an in-app language switcher with AppCompatDelegate.setApplicationLocales()
Many apps need an in-app language switcher so users can override the device locale. As of Android 13 (API 33), the canonical approach uses AppCompatDelegate.setApplicationLocales():
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.os.LocaleListCompat
fun setAppLanguage(languageTag: String) {
val locales = LocaleListCompat.forLanguageTags(languageTag)
AppCompatDelegate.setApplicationLocales(locales)
}
// Usage in a settings screen
binding.spanishButton.setOnClickListener {
setAppLanguage("es")
}
binding.frenchButton.setOnClickListener {
setAppLanguage("fr")
}
To declare which languages your app supports (so the system locale picker shows them), add a locales_config.xml:
<!-- res/xml/locales_config.xml -->
<?xml version="1.0" encoding="utf-8"?>
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
<locale android:name="en"/>
<locale android:name="es"/>
<locale android:name="fr"/>
<locale android:name="de"/>
<locale android:name="ja"/>
</locale-config>
And reference it in AndroidManifest.xml:
<application
android:localeConfig="@xml/locales_config"
... >
For apps targeting Android 12 or lower, the older approach is to save the user's choice in SharedPreferences and apply it on every app start:
fun setLocale(context: Context, languageCode: String) {
// Save the user's choice
val prefs = context.getSharedPreferences("Settings", Context.MODE_PRIVATE)
prefs.edit().putString("app_language", languageCode).apply()
// Apply the new locale
val locale = Locale(languageCode)
Locale.setDefault(locale)
val config = Configuration()
config.setLocale(locale)
context.resources.updateConfiguration(config, context.resources.displayMetrics)
// Restart the activity so changes take effect
if (context is Activity) {
context.recreate()
}
}
Then load the saved language on every app launch:
override fun onCreate() {
super.onCreate()
val prefs = getSharedPreferences("Settings", Context.MODE_PRIVATE)
val languageCode = prefs.getString("app_language", "en") ?: "en"
val locale = Locale(languageCode)
Locale.setDefault(locale)
val config = Configuration()
config.setLocale(locale)
resources.updateConfiguration(config, resources.displayMetrics)
}
This persists between sessions and works correctly after you publish to Google Play.
Use start/end (not left/right) for right-to-left languages
For Arabic, Hebrew, Persian, and Urdu, use start and end instead of left and right in your layouts so Android mirrors the layout automatically:
<!-- Instead of this -->
<TextView android:layout_marginLeft="16dp" />
<!-- Use this -->
<TextView android:layout_marginStart="16dp" />
In Jetpack Compose, use Arrangement.Start instead of Arrangement.Absolute.Left. Enable “Force RTL layout direction” in Developer Options to test mirroring without changing the device language.
Automate Android translation on every release with Git or the PTC API
For active projects, manually re-running translation per release does not scale. PTC gives you two ways to automate.
Git integration (recommended). Go to Settings > Merge Requests in the PTC dashboard and click Add Git Integration to connect your GitHub, GitLab, or Bitbucket repository. Provide an access token with read and write permissions and choose the branches PTC should monitor. From there, PTC detects changes, translates new or updated strings, and opens a merge request for your review.
API integration. Go to Settings > Manage API Tokens and create an access token. Then use the PTC API inside your existing CI:
# .github/workflows/translate.yml
name: PTC translate
on:
push:
branches: [main]
paths:
- 'app/src/main/res/values/strings.xml'
jobs:
translate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Trigger PTC translation
run: |
curl -X POST https://api.ptc.wpml.org/v1/projects/${{ secrets.PTC_PROJECT_ID }}/sync \
-H "Authorization: Bearer ${{ secrets.PTC_API_KEY }}"
Both Git and API integration are Pro features that become available when you activate Pay-As-You-Go. See the PTC API reference for endpoints, webhooks, and the full integration model.
Visual translation review of your translated Android app - ship without manual QA per language
After PTC translates your strings.xml, the work is not done. A translated label may overflow a button in German. “Submit” may be translated as a noun in French when the UI needed a verb. A hardcoded English string someone forgot to wrap in getString(R.string.welcome) will be glaringly untranslated in the running Android app. Catching these traditionally means a manual QA pass in every target language. That is the slowest and most expensive stage of mobile localization.
PTC's AI Visual QA replaces that pass. For native Android apps (which do not run in a browser, so the extension flow does not apply), use screenshot upload. Capture the running app's critical screens in each target language (settings, main flow, edge cases) and upload them to PTC. PTC's vision AI inspects every screen and reports back:
- Issues PTC can fix in
strings.xml. PTC re-translates the verb sense, picks a shorter synonym that fits the button width, regenerates a layout-aware plural. PTC opens a PR with the fixes. It needs no work from you. - Issues that live in your Kotlin code. Missing
getString()wrapper, hardcoded strings, sentence concatenation that should useString.format()with placeholders. PTC generates a ready-to-paste prompt for Cursor or Claude Code. Your dev team wraps the strings in seconds.
The outcome: you ship a verified, multilingual Android app per release. You do not get a translated strings.xml with a manual QA tail.
Translate your Play Store listing, release notes, and push notifications
Your Play Store listing description, what's-new release notes, and push notification copy live outside strings.xml. PTC's Paste to Translate handles that copy in the same project. Paste the source text in the PTC dashboard, choose target languages, get back translations that use the same glossary and brand voice as your in-app strings. Then in Google Play Console, go to Store Presence > Main Store Listing > Translations, add the translated short and long descriptions, and save.
Translate in-app user content with the PTC API
In-app chat, social posts, and user-generated reviews need translation as content arrives. The PTC REST API translates this content on demand with Bearer-token authentication, using the same glossary and brand voice as your strings.xml translations.
Test your localized Android app on every release
Change your device language in Settings, or use Android Studio's emulator language picker. For each language, check for text that is too long for UI elements, missing translations (silent fallback to default), incorrect date/number/currency formatting, and layout issues with longer text. For RTL languages, enable “Force RTL layout direction” in Developer Options.
Android translation pricing after the free trial
Pay-As-You-Go, no subscription. You only pay for what you translate. The first 500 words every month are free, and the rate decreases as you translate more. Use the pricing calculator for an estimate.
Now translate your real Android app
Start your free 30-day trial - 20,000 words into 2 languages, no credit card. Upload your strings.xml, translate it in minutes, then upload screenshots and let PTC verify the running app.
Related:
- Translate iOS and Android apps with AI - service overview for cross-platform teams.
- How to localize your iOS app - the equivalent guide for Xcode and SwiftUI.
- PTC API reference - REST endpoints for CI integration.