Search code examples
javaandroidlocalizationlocale

Managing dynamic localisation resources


For the sake of this question, imagine my application helps users to practice foreign languages.

They press a button to start the Text to Speech introduction, which speaks:

<string name="repeat_after_me" translatable="true">Repeat after me</string>

This string will be localised in the normal way, fetching the string from the appropriate res/values-lang/strings.xml file according to the device Locale.

After the introduction, the app needs to speak any one of a random number of strings, in the language/locale of which they are currently wishing to learn. Herein lies the problem.

Assuming the Text to Speech starts from a simple method such as:

private void startLearning(Locale learningLocale)

And the pseudo code of:

TTS.speak(getString(R.string.repeat_after_me)

followed by:

TTS.speak(getRandomLearningString(learningLocale))

Where:

String getRandomLearningString(Locale learningLocale) {
// return a random string here
}

The above is where I'm stuck on how to best reference the xml resource, that contains the 'string-array' of the language the user is learning (in order to pick one at random).

<string-array name="en_EN" translatable="false">
    <item>"Where is the nearest hospital?"</item>
    <item>"What's the time please?"</item>
    <item>"Only if you promise to wear protection and we have a safe word"</item>
</string-array>

Assuming I have a large number of strings for each language and I support a vast number of languages, the question:

How should I store these strings to keep them manageable and readable in development? How should I 'dynamically' reference them from a method?

To clarify - the main problem is not only how I resolve:

getStringArray(R.array.(variableLocale);

But also how/where I store these string arrays so that the implementation is scalable and organised.

I thank you in advance.

Edit - The actually Text to Speech implementation of switching languages is not a problem, I have that covered.


Solution

  • Scalebale Solution

    If you want to keep this scaleble, you need to save your strings in a form which supports random access without loading everything into memory. So, a plain file (which strings.xml is essentially) won't do the job.

    I recommend you check if you can accomplish what you want with an SQLite Database.

    This would result in something like:

    SELECT text FROM table WHERE locale = yourlocale ORDER BY RANDOM() LIMIT 1

    (see Select random row from an sqlite table).


    This solution requires quite a lot of work to create the needed database, so for known small situations, use the solution below.

    Limited solution

    If you know you won't have too many entries I would recommend to use plain textfiles (one per language). They are easiest to manage.

    You can either save them as raw resource or in the assets folder. Both are relatively easy to read into a String. Then you just need to call String.split("\n") and have an array from which you can select one at random.

    Alternatively, you can put the strings in a string-array in each localized strings.xml and load the wanted array using resources like this:

    Resources standardResources = context.getResources();
    AssetManager assets = standardResources.getAssets();
    DisplayMetrics metrics = standardResources.getDisplayMetrics();
    Configuration config = new Configuration(standardResources.getConfiguration());
    config.locale = yourLocale;
    Resources resources = new Resources(assets, metrics, config);
    

    (see : Load language specific string from resource?)

    As noted in the sources comments this seems to override the resources returned from context.getResources(), maybe you have to reset to the previous locale afterwards.

    Starting from Jellybean there is also context.createConfigurationContext, which doesn't seem to have this problem.

    In all cases it might be a good idea to cache the array if you need to select entries repeatedly.


    Note: This solution doesn't scale well, because the whole array has to be loaded into memory just to select one entry. So large collections might exceed your heap or at least use a lot of memory.