Mobile App Localization for Android Developers

Want to expand your Android app’s global reach? Localization doesn’t require manual string copying. With Humanicer’s API, automate translations whilst maintaining perfect Android resource file formatting.

Why Android Localization Matters

42% of Play Store revenue comes from non-English markets (Statista 2024). Proper localization goes beyond translation—it adapts layouts, formats, and cultural references. Manually managing strings.xml files across multiple values-xx directories is error-prone. Humanicer solves this by automating translations whilst preserving XML structure.

Localizing strings.xml

Android’s primary localization file requires careful XML management. Here’s how to translate strings.xml efficiently:

Key Tip: To maintain context with translations:

  1. Use xliff:g tags for better translation context
  2. Preserve CDATA sections and HTML formatting
  3. Handle all plural forms (zero/one/few/many/other)
  4. Include region variants (e.g., values-es-rMX)
curl -X POST "https://humanicer.com/v1/localize" \
--header 'Content-Type: application/json' \
--header 'X-API-Key: your_api_key' \
--data '{
    "text": "<resources>\n    <string name=\"price\">Price: <xliff:g id=\"price\">%1$.2f</xliff:g></string>\n    <plurals name=\"items_count\">\n        <item quantity=\"zero\">No items</item>\n        <item quantity=\"one\">One item</item>\n        <item quantity=\"few\">%d items (few)</item>\n        <item quantity=\"many\">%d items (many)</item>\n        <item quantity=\"other\">%d items</item>\n    </plurals>\n    <string name=\"font_family\">sans-serif</string>\n</resources>",
    "target_platform": "mobile",
    "target_language": "de"
}'

The response will look like this:

{
    "processing_time_ms":6877,
    "remaining_uses":39,
    "reset_time":"2025-05-01T00:00:00Z",
    "result":{
        "character_count":409,
        "language":"de",
        "localized_text":"<resources> <string name=\"price\">Preis: <xliff:g id=\"price\">%1$.2f</xliff:g> €</string> <plurals name=\"items_count\"> <item quantity=\"zero\">Keine Artikel</item> <item quantity=\"one\">Ein Artikel</item> <item quantity=\"few\">%d Artikel (wenige)</item> <item quantity=\"many\">%d Artikel (viele)</item> <item quantity=\"other\">%d Artikel</item> </plurals> <string name=\"font_family\">sans-serif</string> </resources>",
        "original_text":"<resources>\n    <string name=\"price\">Price: <xliff:g id=\"price\">%1$.2f</xliff:g></string>\n    <plurals name=\"items_count\">\n        <item quantity=\"zero\">No items</item>\n        <item quantity=\"one\">One item</item>\n        <item quantity=\"few\">%d items (few)</item>\n        <item quantity=\"many\">%d items (many)</item>\n        <item quantity=\"other\">%d items</item>\n    </plurals>\n    <string name=\"font_family\">sans-serif</string>\n</resources>",
        "platform":"mobile",
        "platform_metrics":{
            "compliance_checks":true,
            "platform_fit_score":0.65,
            "readability_score":-1.2599310344827583
            },
        "word_count":29
    }
}%

Gradle Automation

task localizeStrings(type: Exec) {
    inputs.file 'app/src/main/res/values/strings.xml'
    outputs.dir 'app/src/main/res/values-*/'
    
    commandLine 'python3', 'scripts/localize_android.py',
        '-i', inputs.files.singleFile.path,
        '-o', 'app/src/main/res/values-{lang}-r{region}/strings.xml',
        '-l', 'es-MX,fr-CA,de-DE,ja-JP,zh-CN',
        '-k', project.properties['humanicerApiKey']
}

preBuild.dependsOn localizeStrings

Localization Python Script

Equivalent Android version of the iOS Python script:

import os
import requests
from pathlib import Path

def translate_android_xml(content, target_lang):
    try:
        headers = {'X-API-Key': os.getenv('HUMANICER_API_KEY')}
        data = {
            "text": content,
            "target_platform": "mobile",
            "target_language": target_lang
        }
        response = requests.post(API_URL, json=data, headers=headers, timeout=30)
        response.raise_for_status()
        result = response.json()
        if not result['result']['platform_metrics']['compliance_checks']:
            raise ValueError("Translation failed platform compliance checks")
        return result['result']['localized_text']
    except Exception as e:
        print(f"Error translating to {target_lang}: {e}")
        return None

def generate_localized_files(source_xml, language_map):
    for lang, regions in language_map.items():
        for region in regions:
            lang_code = f"{lang}-r{region}" if region else lang
            localized_content = translate_android_xml(source_xml, lang_code)
            if localized_content:
                output_dir = Path(f"app/src/main/res/values-{lang_code}")
                output_dir.mkdir(exist_ok=True)
                (output_dir / "strings.xml").write_text(localized_content)
                print(f"Generated {lang_code} localization")

if __name__ == "__main__":
    language_map = {
        'es': ['MX', 'ES', ''],
        'fr': ['CA', 'FR', ''],
        'de': ['DE', '']
    }
    generate_localized_files(
        Path("app/src/main/res/values/strings.xml").read_text(),
        language_map
    )

Best Practices

Testing Localization:

@RunWith(AndroidJUnit4::class)
class LocalizationTest {
    
    @Test
    @Config(qualifiers = "es-rMX")
    fun testSpanishMexicoLocalization() {
        val context = ApplicationProvider.getApplicationContext<Context>()
        assertEquals("Mi App", context.getString(R.string.app_name))
    }

    @Test
    @Config(qualifiers = "ar")
    fun testRtlLayout() {
        val context = ApplicationProvider.getApplicationContext<Context>()
        assertEquals(View.LAYOUT_DIRECTION_RTL, 
            View(context).layoutDirection)
    }
}

Dynamic Language Switching:

fun Context.setAppLocale(language: String, region: String? = null): Context {
    val locale = when {
        region != null -> Locale(language, region)
        else -> Locale(language)
    }
    
    Locale.setDefault(locale)
    val config = Configuration(resources.configuration).apply {
        setLocale(locale)
        setLayoutDirection(locale)
    }
    
    return createConfigurationContext(config).also {
        resources.updateConfiguration(config, resources.displayMetrics)
    }
}

val newContext = context.setAppLocale("es", "MX")

Conclusion

Mastering Android app localization helps tap into global markets whilst maintaining code quality. By automating strings.xml translations with Humanicer’s API, developers can:

Start localizing your Android app efficiently today with the Humanicer Localization API and discover why top Play Store apps trust automated XML localization.