Android: Automatic string extraction

Android features a simple localization mechanism. Strings are separated from layouts (or code) and stored in dedicated resource files. This mechanism works conveniently when a great number of resources are already extracted. However, the process of extracting strings from layouts for new application projects or new screens is quite cumbersome and may take some time for comprehensive layouts.

This article demonstrates an approach to automate the string extraction process.

Motivation

Imagine, you, as developer, are creating a new application or a new screen for an existing application. Maybe you have a nice UI specification from the customer or your UX designer. Thanks to Android Studio’s layout editor you have created several awesome layouts with lots of labels, hints, buttons and so one. Now, there is one last thing you need to do to localize your application: extract all strings from those layouts.

Lint report showing a large amount of hardcoded text occurrences.

Fortunately, there is a shortcut which allows you to manually extract a single string occurrence. You need to select each string one-by-one, use the shortcut and provide a unique key for each value. These are two factors which slows down the process:

  1. select each string one-by-one
  2. manually provide a unique key for each string

As mentioned before, this task may take some time and becomes cumbersome for a larger amount of localizable strings.

Android Studio shortcut to extract a single string resource.

Automation

To reduce the time to extract all hardcoded strings from all layouts of all flavors, we have implemented a custom Gradle plugin:

https://github.com/it-objects/android-string-extractor-plugin

The plugin automatically extracts hardcoded strings from Android layouts. To do so, it uses a convention to automatically generate a key:

<!-- Before, Layout: example.xml -->
<TextView
android:id="@+id/textView"
android:text="Hardcoded value"
/>

<!-- After -->
<TextView
android:id="@+id/textView"
android:text="@string/example_textView_text"
/>

To uniquely identify a string, the plugin takes the layout name, the widget identifier and the attribute. Like showcased above, a hardcoded text of a TextView with the id “textView” within a layout file “example.xml” gets extracted and replaced by “example_textView_text”.

All automatically extracted strings will be written to a dedicated string resource file:

<!-- flavor/res/values/string_layouts.xml -->
<?xml version=”1.0" encoding=”UTF-8" standalone=”no”?>
<!-— This file is auto-generated DO NOT insert new strings here manually!!! -->
<resources>
<string name=”example_textView_text”>Hardcoded value</string>
</resources>

Excursus: Resource names

We’re using this pattern for string resource names for quite a long time now and for different kinds of Android projects. Even before we started to automate the extraction process. We’ve noticed, that for heavily scaling or already large projects, it’s mandatory to have a convention to name string resources.

The aforementioned convention features three major benefits:

  1. Traceability
  2. Reliability
  3. Consistency

Traceability: It allows direct referencing of string resources to screens and elements. This allows to provide a context for translators. For example, “Confirm” could be translated differently in a certain language, depending on the context. By having the layout and the widget of the string coded in its name, the translator has a better idea what should be expressed.

<string name="confirm">Confirm</string>
<string name="hint">Reason</string>
<!-- vs -->
<string name="order_confirmation_btConfirm_text">Confirm</string>
<string name="order_confirmation_etReason_hint">Reason</string>

Reliability: We’ve noticed, that string resources often change over time. What was “Submit” today, becomes “Send” tomorrow and maybe just “OK” the day after tomorrow. By choosing a unique key for each text occurrence, there are no conflicts when changing a particular key’s value. By reusing one key in different layout there is a potential to change something in the wrong place.

Consistency: Conventions are nice, but as long as they are not enforced (e.g. by a custom Lint rule), there is a chance that developers may name things differently or others name changes are not applied to the string resource name. However, when automating the process you’ll get consistency by default.

Usage

The usage is pretty straightforward. The plugin is available via jcenter. You need to add the classpath to your project level build.gradle and apply it in your application level build.gradle:

buildscript {
repositories {
jcenter()
}
dependencies {
...
classpath 'de.ito.gradle.plugin:android-string-extractor:0.1.0'
}
}

apply plugin: 'android-string-extractor'

Last but not least, when necessary, just execute the extractStringsFromLayouts task:

$ ./gradlew extractStringsFromLayouts

Conclusion

By automating the string extraction process we could significantly save time when creating new screens. If you are not restricted in the way you name your string resources, the presented approach may also help you to speed up string extraction.

Please note, this is the first release of the plugin. The feature set is limited and you may encounter bugs when using it (although we have a good test coverage). In case you encounter a bug, we would really appreciate if you file an issue to our issue tracker. Thanks and happy extracting :-)