MyFamilyTracker
Real-time family location sharing — Firebase Realtime DB for sub-second propagation, WorkManager + ForegroundService for OS-compliant background collection, geofencing via Google Maps API.
Localization is one of the highest-ROI features for solo developers. Adding support for 5-10 languages can double your potential market. Here's how to implement localization correctly in an Android app with Compose.
On this page
My apps see 2-3x more installs from non-English markets after adding localization. For a solo developer, it's one of the most efficient growth levers — you're not building new features, you're opening existing ones to a larger audience.
Here's how to do it right.
All user-facing text belongs in
res/values/strings.xml<!-- res/values/strings.xml — English (default) -->
<resources>
<string name="app_name">TaskFlow</string>
<string name="add_task">Add Task</string>
<string name="task_completed">Task completed</string>
<string name="delete_task_confirmation">Delete this task? This cannot be undone.</string>
<!-- Plurals -->
<plurals name="task_count">
<item quantity="one">%d task</item>
<item quantity="other">%d tasks</item>
</plurals>
</resources>
<!-- res/values-es/strings.xml — Spanish -->
<resources>
<string name="app_name">TaskFlow</string>
<string name="add_task">Agregar tarea</string>
<string name="task_completed">Tarea completada</string>
<string name="delete_task_confirmation">¿Eliminar esta tarea? Esto no se puede deshacer.</string>
</resources>// String resource
Text(text = stringResource(R.string.add_task))
// String with format argument
Text(text = stringResource(R.string.welcome_user, userName))
// Plural
Text(
text = pluralStringResource(R.plurals.task_count, taskCount, taskCount)
)
// Annotation strings (rich text)
val annotatedString = buildAnnotatedString {
append(stringResource(R.string.terms_prefix))
withStyle(SpanStyle(color = MaterialTheme.colorScheme.primary)) {
append(stringResource(R.string.terms_link_text))
}
}For global distribution, prioritize by potential market × effort:
| Language | Market Potential | Code |
|---|---|---|
| Spanish | Very High | es |
| Portuguese (Brazilian) | High | pt-rBR |
| German | High | de |
| French | High | fr |
| Japanese | High | ja |
| Korean | Medium-High | ko |
| Hindi | High (growing) | hi |
| Arabic | High (RTL challenge) | ar |
English + these 7-8 languages covers 70%+ of Android users worldwide.
Arabic and Hebrew are right-to-left. Android handles most layout mirroring automatically, but you need to use the right attributes:
// Use start/end instead of left/right
modifier = Modifier.padding(start = 16.dp, end = 8.dp) // Not padding(left, right)
// Row with gravity — use Arrangement.Start not Arrangement.Left
Row(horizontalArrangement = Arrangement.Start) { ... }<!-- In Manifest -->
<application android:supportsRtl="true">Test RTL with: Settings → Developer Options → Force RTL layout direction.
Google Translate can produce a rough initial translation. For professional apps, have a native speaker review the output — machine translation often produces technically correct but unnatural text.
For validation without hiring translators, post in Reddit communities for each language (r/es, r/de, r/france) asking for translation feedback. Many users are happy to help if you ask nicely.
Android Studio has a built-in translation editor:
Open any
strings.xmlDifferent locales need different resources beyond strings:
Date and time formats:
DateTimeFormatter.ofLocalizedDate()Number formats:
NumberFormat.getInstance()Currency:
NumberFormat.getCurrencyInstance()formattedPriceImages: Some regions have different cultural norms for imagery. Put locale-specific images in
res/drawable-es/res/drawable-ja/// Current locale
val locale = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
context.resources.configuration.locales[0]
} else {
@Suppress("DEPRECATION")
context.resources.configuration.locale
}
val languageCode = locale.language // "en", "es", "ja"
val countryCode = locale.country // "US", "MX", "JP"For most apps, you don't need to detect the locale — Android applies the right strings.xml automatically based on the device language setting.
Android 13 introduced per-app language preferences. Users can set a different language for your app than their system language:
// Check the current app language
val currentLocale = AppCompatDelegate.getApplicationLocales().get(0)
// Change the app language
AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags("es"))Implement an in-app language picker in Settings if you want to support this explicitly. Otherwise, Android 13+ users can change the app language from system Settings → Apps → Your App → Language.
Don't forget to localize the Play Store listing itself:
A Spanish user finding your app via a Spanish search sees Spanish listing text — and converts much better than seeing English.
# Change device locale via ADB
adb shell settings put system language es
adb shell settings put system user_locales es-ES
# Restart the launcher for changes to take effect
adb shell am broadcast -a android.intent.action.LOCALE_CHANGEDOr change in Settings → System → Language.
Walk through your app's main flows in each supported language. Look for:
strings.xmlsupportsRtl="true"start/endleft/rightSudarshan Chaudhari
AI Systems Builder / Product Engineer
Bangkok, Thailand
Solo Android developer with 13+ years in QA, building Android apps, AI automation systems, and developer tools at SudarshanTechLabs.
Related Posts
Related Apps
Real-time family location sharing — Firebase Realtime DB for sub-second propagation, WorkManager + ForegroundService for OS-compliant background collection, geofencing via Google Maps API.
Building something? Available for Android dev and QA consulting.
Work with meComments — powered by Giscus
Real-time family location sharing — Firebase Realtime DB for sub-second propagation, WorkManager + ForegroundService for OS-compliant background collection, geofencing via Google Maps API.
ReadPrivate dream journal — structured entry capture, pattern tagging, and optional Claude-powered insight generation. All data stays on-device by default.
ReadWorkout tracker — exercise logging with set/rep/weight history, goal progression, and local Room DB persistence. No account, no cloud sync required.
Read