Translation and localization is a vital part of any growing app. It is, however, time-consuming—often being carried out in disjointed phases and as such it’s all too often left until the last minute. If you’re not managing your String resources with this fact in mind, you could find yourself with delays in the development (and ultimately the release) of your application.
At Novoda, we’ve slowly converged onto a common approach to managing our String resources that acknowledges this real-world issue and allows us to be flexible with when and how translations are supplied. We’ve found it very helpful for accommodating most translation efforts and have been using it on all projects, even if we don’t believe an application will be translated right now.
The approach itself is fairly simple and consists of 3 rules.
- For all user-facing text use String resources (in both XML layouts or in code as appropriate).
- Never use String resources that contain String literals in your code/XML layouts directly
- Use resource aliases to provide one level of abstraction between user-facing text and IDs
Let’s illustrate this with a concrete example. Here’s a simple Textview label displaying “Hello world” that’s being shown in an Activity named ‘Initial’:
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/initial_welcome_message" />
@string/initial_welcome_message resource would not be the String literal “Hello world”. Instead, it would be a reference to another resource in a the strings file named after the screen/domain (in this case, res/strings-initial.xml, after our Activity).
@string/initial_welcome_message resource then references the
@string/hello_world String resource, which is inside the file holding all translated strings. It’s this resource that holds actual text (in this case, “Hello World”) and has literal naming (“hello_world”): res/strings-localised.xml
<string name="hello_world">Hello world</string>
This layer of indirection allows you to change the referenced resource ID that
initial_welcome_message points to as you refactor your code. You therefore wouldn’t need to alter the
hello_world String, which it may have been sent for translation. These IDs would remain stable and so prevent conflicts when the translation is completed and the resulting String need to be merged back in.
Once translation has started, the process would be fairly simple:
- Don’t delete or rename IDs/values in strings-localised.xml; instead add new ones as necessary
- Only clean up (unused) String resources when it’s safe (if it ever is safe, checking the process of the translation team)
And that’s it. This approach allows for easy replacement as translated text becomes available without any major refactoring of XML layouts or code, meaning your development process is a lot safer.
I don’t only like it because it makes string translation easier, I like it for the separation of concerns and the use of domain concepts to segregate the string resources into different files, allowing faster development and code maintenance when adding/changing features. Translations is a cheeky bonus, nice!
(For a little more detail, and some more concrete examples, here’s a longer description of this process.)