Firebase Crashlytics & Android Localization — Quality & Reach Guide
Building a production Android app requires two critical pillars: stability and user accessibility. Firebase Crashlytics monitors crashes in real-time, while Android localization (i18n) ensures your app speaks your users' language—literally.
Let's integrate both into a professional-grade app.
Part 1: Firebase Crashlytics Setup
Step 1: Add Firebase BOM and Dependencies
In your module-level build.gradle.kts:
dependencies {
// Firebase BOM (Bill of Materials)
implementation(platform("com.google.firebase:firebase-bom:33.1.0"))
implementation("com.google.firebase:firebase-crashlytics-ktx")
implementation("com.google.firebase:firebase-analytics-ktx")
}
The BOM ensures all Firebase libraries are compatible versions—no version mismatch headaches.
Step 2: Configure google-services.json
- Download
google-services.jsonfrom Firebase Console - Place it in
app/directory (notapp/src/) - Add to
build.gradle.kts(project level):
plugins {
id("com.google.gms.google-services") version "4.4.1" apply false
}
- Apply plugin in
build.gradle.kts(app level):
plugins {
id("com.android.application")
id("com.google.gms.google-services")
}
Step 3: Record Exceptions Manually
In your crash-prone code:
val crashlytics = FirebaseCrashlytics.getInstance()
try {
riskyOperation()
} catch (e: Exception) {
crashlytics.recordException(e)
// Don't crash the app, just log it
}
Step 4: Add Custom Keys & Logs
Attach context to crashes:
val crashlytics = FirebaseCrashlytics.getInstance()
// User identification
crashlytics.setUserId("user_${userId}")
// Custom key-value pairs
crashlytics.setCustomKey("subscription_tier", "premium")
crashlytics.setCustomKey("screen_name", "PaymentScreen")
// Breadcrumb logs (last 100 logged)
crashlytics.log("User initiated purchase flow")
crashlytics.log("API response: ${response.code()}")
Step 5: Disable in Debug Builds
Avoid test crashes polluting production metrics:
if (!BuildConfig.DEBUG) {
FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(true)
} else {
FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(false)
}
Or configure in AndroidManifest.xml:
<meta-data
android:name="firebase_crashlytics_collection_enabled"
android:value="false" />
Step 6: Target Crash-Free Rate
Monitor dashboard metrics:
- Target: 99.5%+ crash-free rate
- Alert threshold: If crashes exceed 0.5% of sessions, investigate immediately
- Release process: Only deploy if crash-free rate stays above 98% for 24h
Part 2: Android Localization (i18n)
Step 1: Create Locale-Specific Strings
Create resource directories:
res/
├── values/
│ └── strings.xml (English - default)
├── values-es/
│ └── strings.xml (Spanish)
├── values-ja/
│ └── strings.xml (Japanese)
├── values-fr/
│ └── strings.xml (French)
└── values-pt-rBR/
└── strings.xml (Brazilian Portuguese)
res/values/strings.xml (English):
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">MyApp</string>
<string name="welcome_message">Welcome to %1$s</string>
<string name="buy_button">Purchase Now</string>
<string name="currency">USD</string>
</resources>
res/values-es/strings.xml (Spanish):
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">MiApp</string>
<string name="welcome_message">Bienvenido a %1$s</string>
<string name="buy_button">Comprar Ahora</string>
<string name="currency">EUR</string>
</resources>
res/values-ja/strings.xml (Japanese):
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">マイアプリ</string>
<string name="welcome_message">%1$sへようこそ</string>
<string name="buy_button">今すぐ購入</string>
<string name="currency">JPY</string>
</resources>
Step 2: Use stringResource in Compose
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
@Composable
fun WelcomeScreen() {
val appName = stringResource(R.string.app_name)
val message = stringResource(R.string.welcome_message, appName)
Text(text = message)
}
For legacy XML layouts:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/welcome_message" />
Step 3: Handle Plurals
Different languages have different plural rules. Polish has 3! 🇵🇱
res/values/strings.xml:
<plurals name="item_count">
<item quantity="zero">No items</item>
<item quantity="one">1 item</item>
<item quantity="other">%d items</item>
</plurals>
res/values-ja/strings.xml:
<plurals name="item_count">
<item quantity="other">%d件のアイテム</item>
</plurals>
Usage in Compose:
val context = LocalContext.current
val itemCount = 5
val text = context.resources.getQuantityString(
R.plurals.item_count,
itemCount,
itemCount
)
Text(text)
Step 4: RTL Support (Arabic, Hebrew, etc.)
Enable RTL layouts:
<!-- AndroidManifest.xml -->
<application
android:supportsRtl="true"
...>
</application>
Use start/end instead of left/right:
<!-- Wrong ❌ -->
<TextView
android:layout_marginLeft="16dp"
android:drawableLeft="@drawable/ic_email" />
<!-- Correct ✅ -->
<TextView
android:layout_marginStart="16dp"
android:drawableStart="@drawable/ic_email" />
In Compose:
Row(
modifier = Modifier
.padding(start = 16.dp, end = 16.dp)
) {
Icon(painter = painterResource(R.drawable.ic_email), contentDescription = null)
Text(text = stringResource(R.string.email_label))
}
Step 5: Per-App Language API (Android 13+)
Let users change language without system-wide change:
import androidx.appcompat.app.AppCompatDelegate
import java.util.Locale
fun setAppLanguage(languageCode: String) {
val locale = Locale(languageCode)
AppCompatDelegate.setApplicationLocales(
LocaleListCompat.create(locale)
)
}
Call it from Settings:
Button(onClick = { setAppLanguage("ja") }) {
Text("日本語に変更")
}
Add dependency:
implementation("androidx.appcompat:appcompat:1.6.1")
Integration: Crash Reporting + Localization
Monitor localization crashes:
try {
val text = stringResource(R.string.missing_key)
Text(text)
} catch (e: Exception) {
FirebaseCrashlytics.getInstance().apply {
setCustomKey("crash_type", "localization_missing_string")
setCustomKey("locale", Locale.getDefault().language)
recordException(e)
}
Text("Unable to load translation")
}
This catches missing translations in specific locales before they hit production.
Checklist for Launch
- [ ] Firebase Crashlytics enabled in release builds only
- [ ] Custom keys attached to critical screens
- [ ] Crash-free rate baseline established (target 99.5%+)
- [ ] All UI strings in
strings.xml(no hardcoded text) - [ ] RTL support enabled
- [ ] At least 3 languages (English + 2 target markets)
- [ ] Plurals tested in target languages
- [ ] Per-App Language API working (Android 13+)
- [ ] Localization crashes monitored via Crashlytics
8 Android App Templates → https://myougatheaxo.gumroad.com
Need battle-tested Kotlin templates? Check out our Android App Bundle—8 production-ready templates with Crashlytics, i18n, and best practices pre-configured. Save weeks of setup.
Top comments (0)