6

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?

Jan S.
  • 10,328
  • 3
  • 31
  • 36
  • just for you to notice i hope you are working with the manifest string file , to your question : you are refering to situation were on run time you choose your lang ? if that so i think reflection using localizion will help you without even asking your user... – yoav.str Jun 28 '10 at 21:48
  • Yes, I am working with the strings file. However I don't know if I can achieve what I want in a clean way using the strings file. Yes it is about showing certain messages at runtime, more specifically is about handling plurals, which in certain languages can have more than 1 way to write them. – Jan S. Jun 29 '10 at 09:29

4 Answers4

3

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.

gustafc
  • 28,465
  • 7
  • 73
  • 99
  • Thanks for the example, at first I was looking a more android based solution (e.g. using only the xml to have the strings), so to tell you the truth I thought that it wasn't possible to integrate the Android strings system with ChoiceFormat, but you just gave me a great idea on how to mix both. BTW this is exactly what I wanted, the plural changes after certain numbers. The only caveat, is that adding new languages that have special plural changes would require modifications of the source code. – Jan S. Jul 01 '10 at 16:21
  • 1
    This is certainly not the way to go. Have your strings in resources files in /res/string. But it's good to use ChoiceFormat rather thna plurals because http://stackoverflow.com/questions/5651902/android-plurals-treatment-of-zero – rds Aug 15 '11 at 12:22
  • For the record, the code sample here is only intended to show how to use `ChoiceFormat`. I do not suggest that you should hard-code your language strings. Edited code to clarify. – gustafc Aug 15 '11 at 13:25
2

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.

Jan S.
  • 10,328
  • 3
  • 31
  • 36
  • I don't really understand why you bother mixing plurals and `MessageFormat`? If I were you I'd either try and figure out how to do this properly with only plurals, or I'd ditch plurals altogether and just go for `MessageFormat` for all languages - saying something like `MessageFormat.format(getString(R.string.year_format), n)` seems a lot easier than what you have now. – gustafc Jul 02 '10 at 06:25
  • Yeah, I think you are right, I will be going MessageFormat only. I just didn't want to throw away the plurals code. I can't use only plurals (which would be the best). THanks for the help :) – Jan S. Jul 03 '10 at 19:35
  • Don't use Plurals. They are buged. http://stackoverflow.com/questions/5651902/android-plurals-treatment-of-zero – rds Aug 15 '11 at 12:20
1

There's a built-in "plurals" support which is not well documented.
Mentioned here and you can see it in the Browser sources.

yanchenko
  • 56,576
  • 33
  • 147
  • 165
  • Thanks, I think this is what I need! Do you know the difference between using xliff and not? In one of the links they use this: %s thing In the other they use: %d matches – Jan S. Jun 29 '10 at 09:11
  • After trying this, it doesn't seem that it supports something like (I need this because in polish the plural for quantities from 2 to 4 is different than the rest). Is there a way I can achieve this? – Jan S. Jun 29 '10 at 09:21
  • I'm not using this myself, you'll need to search Android sources for examples. – yanchenko Jun 29 '10 at 10:55
  • As I already said in a previous comment, don't use Plurals, they don't support "zero". – rds Aug 17 '11 at 13:56
0

Note that I don't have any android specific experience... but I usually opt for having separate view files for each language i'm going to support, this allows for broader localisation than language alone, you can now also have slightly altered layouts if you know text in some other language can require for example wider buttons, or even different images.

Kris
  • 40,604
  • 9
  • 72
  • 101
  • With Android it is possible to do this. How do you do with strings that you need to set up at runtime? – Jan S. Jun 29 '10 at 12:01
  • The stuff i need to do at runtime is fairly limited but i use a "normal"i18n table through some of my own class methods like`String::stringWithLocalizedString('Good {timeofday} {username}')` and such that can be used with formatting functions. – Kris Jun 29 '10 at 12:39