Maintaining Strings for Translation

When introducing translations for your app, a process which often runs in parallel with on-going development, you might start to hit issues with conflicts: renamed resource IDs, removed resource IDs, added resources, and modified resources. It shouldn’t be like that; there has to be a better way!


person struggles to open a cabinet without dropping all the things

Below is a workflow we’ve started to use at Novoda – most readers will already do the first step, it’s included for completeness. Time will tell if it’s any good, but seems to work so far!

Example


screenshot showing app with a register button

The first screen in our app (so good) is a landing page, where the only thing visible is a button that says “Register”.

res/layouts/activity_landing.xml:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent">

  <Button
    android:id="@+id/landing_button_register"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="Register" />
</FrameLayout>

To aid in localisation, extract “Register” into a strings resource file, and update the layout to reference this string resource.

res/values/strings.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="register">Register</string>
</resources>

res/layouts/activity_landing.xml:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent">

  <Button
    android:id="@+id/landing_button_register"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/register" />
</FrameLayout>

IDs must be stable: never modify or delete existing string resources from res/values/strings.xml – this is to ensure that we’re on the same page re: the IDs that the translation process knows about (whether this is people or software).

Sometimes designs change. Refactoring is good. Refactoring is life. I don’t want to feel like I’m stuck with the ID or name I chose before just because it was the first one that popped into my head at the time – it makes sense that you should update these references as you go along so I’ve opted for the following pattern when I extract string resources.

I create a strings resource file for every screen/modular component. In this case, I’ll create one for the landing page, with a string alias for the button text, and then update the layout to use this reference instead.

res/values/strings-landing.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <string name="landing_button_register">@string/register</string>
</resources>

res/layouts/activity_landing.xml:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent">

  <Button
    android:id="@+id/landing_button_register"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/landing_button_register" />
</FrameLayout>

I can change the ID of @string/landing_button_register as much as I want; the translation process only knows about @string/register which shouldn’t change.

Rules

  1. Don’t modify or delete existing string resources from res/values/strings.xml. Only add new resources to that file.
  2. When you name resources in that file, be descriptive about the text itself, not where it’s going to be used. Notice I used @string/register and made no reference to the fact it’s text for a button, nor that it’s used on the landing page. I can use reference that resource anywhere.
  3. Okay, not anywhere. Don’t reference resources in res/values/strings.xml in your layouts/code directly – use an alias which is in a different file.

More thoughts!

I’ve used this for a while, and seems to work ok for the most part. There’s a few notes I’d mention though.

Choosing your IDs based on the content of the text is all well and good, until the text is really long, like a paragraph – you don’t want to match your ID to the content like we did above, word-for-word. In that case, I’d still try to describe what the text is about, e.g. @string/app_description, which doesn’t leak context about where the text is used, so it’s still recyclable.

The second part of this problem is when you add a sentence or two in the middle of this long string – your ID is still relevant but existing translations for that ID aren’t accurate anymore, so you need a new translation. When that happens, I append a number to the ID (creating a new resource, see rule 1!) @string/app_description_2. The good thing is that if you’re using aliases, you’ll never see this in your layouts directly.

One point about using the extra string resource files that I like is that your layouts are just about the layout – when you need to modify the copy for your app, you just modify the string resources, and don’t have to look through layouts.


Thanks Paul for helping me break down this process so a shorter post could be written!

Leave a Reply

Your email address will not be published. Required fields are marked *

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>