Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to do an app with truly multilingual strings?

what should be the best approach to make strings for different languages? I have this problem, I am trying to display strings such as 'month', 'months', 'year', 'years'. Currently I am working on 3 languages I know: spanish, english and polish. For english and spanish this is straight forward. But for instance, in polish 'years' can become 'lata' (after numbers 2 - 4) or 'lat' (after numbers from 5). I was thinking on adding an extra string for this, and making it empty in the other languages. However this made me think about the other languages I don't know, which might have even more differences. Which should be the best approach in this case, if I am considering adding more languages in the future?

like image 628
Jan S. Avatar asked Jun 28 '10 21:06

Jan S.


2 Answers

Sounds like you want a ChoiceFormat, or at least using one through a MessageFormat:

public static void main(String... args) {
    String[] formats = { 
        // NOTE - In a real app, you'd fetch the format strings from a language,
        // file, not hard-code them in your program. Obviously.
        "{0,number} {0,choice,0#years|1#year|1<years}", // english
        "{0,number} {0,choice,0#años|1#año|1<años}", // spanish
        "{0,number} {0,choice,1#[fewer than 2]|2#lata|4<lat}", // polish
        "{0,number} år", // swedish - singular and plural forms look the same!
    };
    int[] years = {0, 1, 2, 3, 4, 5, 6};
    for (int year : years) {
        for (String format : formats) {
            System.out.println(MessageFormat.format(format, year));
        }
        System.out.println();
    }
}

In your program, you would of course get the format string from your string file.

like image 95
gustafc Avatar answered Oct 04 '22 21:10

gustafc


Thanks to the answers I have got, I am writing 2 Android based solutions. The first one I used was having plurals. At first glance checking the Plurals docs/examples, you could think that there is a quantity="few" (for 2-4 plurals) which checking at the sources only works for locale 'cs'. For the rest of locales, only "one" and "other" are working. So in your strings.xml files:

<plurals name ="years">
    <item quantity="one">1 year</item>
    <item quantity="other"><xliff:g id="number">%d</xliff:g> years</item>
</plurals>

So for polish I would have:

<plurals name ="years">
    <item quantity="one">1 rok</item>
    <item quantity="other"><xliff:g id="number">%d</xliff:g> lat</item>
</plurals>

Then I would have on my code:

int n = getYears(...);
if (Locale.getDefault().getLanguage().equalsIgnoreCase("pl") && n >= 2 && n <= 4) {
   return getString(R.string.years_pl, n);
} else {
   return getResources().getQuantityString(R.plurals.years, n, n);
}

And in my strings.xml file for the polish locale I would add the missing string:

<string name="years_pl"><xliff:g id="number">%d</xliff:g> lata</string>

My second solution has the plurals element for english, spanish, and other languages that don't have too many plural changes. Then for the rest of the languages that have this kind of changes I would use ChoiceFormat. So in my code:

...
private static final int LANG_PL = 0;
// add more languages here if needed
...
String formats[] = {
"{0,number} {0,choice,1#" + getString(R.string.year_1) + "|2#" + getString(R.string.years_2_4) +  "|4<" + getString(R.string.years_lots) +"}", // polish
// more to come
};
...
// Here I would add more for certain languages
    if (Locale.getDefault().getLanguage().equalsIgnoreCase("pl")) {
       return MessageFormat.format(formats[LANG_PL], n);
    } else {
       return getResources().getQuantityString(R.plurals.years, n, n);
    }

I don't know if these ways are the best way, but for the moment, or until Google makes something better, this works for me.

like image 36
Jan S. Avatar answered Oct 04 '22 22:10

Jan S.