I am using Plural strings provided by android-sdk. I have used following code to create a plural string:
<plurals name="valuestr">
<item quantity="zero">Choose a value.</item>
<item quantity="one">%d unit.</item>
<item quantity="other">%d units.</item>
</plurals>
Java Code:
textView.setText(getResources().getQuantityString(R.plurals.valuestr,0,0));
When i am setting any value other than '0', this is working fine but when i am setting '0' it is showing '0 unit.'.
Please help!
Update
While searching more on the internet i came across a workaround which uses java.text.MessageFormat class:
<resources>
<string name="item_shop">{0,choice,0#No items|1#One item|1<{0} items}</string>
</resources>
Then, from the code all you have to do is the following:
String fmt = resources.getText(R.string.item_shop);
textView.setText(MessageFormat.format(fmt, amount));
You can read more about the format strings in the javadocs for MessageFormat
A post was recently made on G+ about this.
In short, it is because this will not pick the closest match by Integer ( 0 = zero), but because it will look for the best grammatical pick.
In your example, you use units.
The correct usage would be;
0 units
1 unit
2 units
Making, zero equal to pretty much any other quantity above 1
Read the full story here;
https://plus.google.com/116539451797396019960/posts/VYcxa1jUGNo
Plurals defined in <plurals> sections of resource files are only to be used for a grammatical distinction with respect to singular/plural strings. You should not use them for other display logic, as you did. You should add some checking logic in your code instead.
The Android developer's guide clearly states this:
Although historically called "quantity strings" (and still called that
in API), quantity strings should only be used for plurals. It would be
a mistake to use quantity strings to implement something like Gmail's
"Inbox" versus "Inbox (12)" when there are unread messages, for
example. It might seem convenient to use quantity strings instead of
an if statement, but it's important to note that some languages (such
as Chinese) don't make these grammatical distinctions at all, so
you'll always get the other string.
Your workaround - although working technically for your current implementation - does not appear like a clean solution either, in my opinion. Future business requirements may make it necessary to include more sophisticated logic than just displaying a different text. Or you may have a generic "no items selected" string in your resource file used at different locations, which could be reused only if you did not stick to your solution.
Generally, I would avoid using two different formatting techniques (String.format style formatter %d vs. MessageFormat style formatter {0} and pick one that you'd stick to in your whole application.
Related
I would like to use plurals for my Android project.
However, the values I provide can be float values.
So for instance, when setting 1.5 stars, I want this to understand, it's not 1 star but 1.5 stars.
<plurals name="stars">
<item quantity="one">%d star</item>
<item quantity="other">%d stars</item>
</plurals>
However, the Android system seems to use integer values (%d) only.
The method looks like this:
String getQuantityString(#PluralsRes int id, int quantity, Object... formatArgs)
where quantity is defined as Int.
Is there any solution for this?
After doing further research, it appears there is no good solution for this.
As also seen in the other answers, they always require a lot of "manual processing" to it requiring no different workflow than creating separate string resources.
The general suggestion seems to be rounding / processing the float values manually (e.g checking whether the float value matches 1.0) and then using apropriate Int values for the plurals call.
But aside from not really using plurals then this comes with the problem of other languages (e.g. I have no clue if 1.5 stars would also be plural in another language as it is in English) and thus these rounding options may not apply universally.
So the answer is: there seems to be no perfect solution (meaning solved "automatically" by the Android system).
What I actually do therefore is to simply pick exceptions and use different Strings there.
So the (pseudo code) way of doing currently looks like
// optionally wrap different languages around
// if language == English
when (amountStars) {
is 1.0 -> getString(R.string.stars_singular, 1)
... ->
else -> getString(R.string.stars_plural, amountStars)
}
// if language == Chinese ...
where additional cases have to be "hard coded". So for example you have to decide whether 0 means
"0 stars" (plural string) or
"no star" (singular string)
But there seems no real benefit of using plurals over separate string resources with common placeholders. On the other hand this (at last for me) gives more flexibility for formatting options. For example, one may create a text like "1 star and a half" where it becomes singular again (even though numerically we would write 1.5 stars).
Don't use plurals for fractional numbers. Just stick with basic string resources and use a placeholder:
<string name="fractional_stars">%1$s stars</string>
getString(R.string.fractional_stars, 0.5F.toString())
or
<string name="fractional_stars">% stars</string>
getString(R.string.half_a_star).replace("%", 0.5F.toString())
Simply do this:
getQuantityString(R.plurals.stars, quantity > 1f ? 2 : 1, quantity):
And replace the %d in your strings with %f.
getQuantityString takes a quantity of type Int and Object... formatArgs
If you round the quantity to Int you would make sure that any value in 1.0 -> 1.99 is a single item and other than that is a plural
resources.getQuantityString(
R.plurals.products_left_in_stock_message_plural,
leftInStock.toInt(), leftInStock.toString()
)
So you only round it the quantity but pass the actually value as an argument
<plurals name="products_left_in_stock_message_plural">
<item quantity="one">Only one item is available from this product</item>
<item quantity="other">There are only %s item is available from this product</item>
</plurals>
It is known that Androd string resources support xliff namespace to annotate non-translatable string formatting placeholders, like this
<string name="max_file_size_exceeded_template">File
<xliff:g example="some_image.jpg" id="file_name">%1$s</xliff:g>
is too big and could not be uploaded.</string>
It helps translators to undestand what parts of string should not be modified. But sometimes I need to annotate some strings max length it they are used in UI control with limited size. What I want is add text length limit to warn translators about this. Something like max-length="24" max-lines="2" length-unit="char" Maybe, xliff supports such thing or it can be achieved in other way.
I use Weblate for translations if it matters.
You can set this in Weblate, the Andoid format does not support this directly.
Click on edit (pencil) icon next to flags on the string.
Enter max-length:LENGTH as check flag.
See also https://docs.weblate.org/en/latest/admin/translating.html#additional-information-on-source-strings
Can someone please explain the xliff:g for strings/localization.
I understand that xliff:g is not supposed to translate anything inside the <> things, but I'm confused how exactly I'd use this in code.
An example I have in my case is the practice spanish translations that I have has:
<string name="order_quantity">Cantidad: <xliff:g id="quantity" example="2">%d/xliff:g</string>
I am now trying to get localized strings with xliff:g to work.
What is id here and what does it do? And what does it call?
Also what is the %d and what does it do? What is the point of example? Also, how would I call that into code, if at all?
Why can't someone just do the following code to insert the following xml:
<string name="quant">Quantity: </string>
into java like so:
getString(R.string.quant) + quantity
so that way it concactenates the quantity variable into the getString?
Minor typo in your example, there should be a closing tag:
<string name="order_quantity">Cantidad: <xliff:g id="quantity" example="2">%d</xliff:g></string>
The id attribute is just used to identify what the substitution parameter represents (in your case, it represents the quantity). It's as you said, a note, and not actually used programmatically.
That said, in Android Studio, if you have Code Folding enabled for strings, it will substitute in the ID when it shows the collapsed string. You'd see something like this:
// This...
mTextView.setText(getString(R.string.order_quantity, 2));
// Will show as this when folded:
mTextView.setText("Cantidad: {quantity}");
As for your second question, why not just use string concatenation? In other languages, the substitution may not go at the end of the string. You could have something like:
values/strings.xml
<string name="order_quantity">%d items</string>
values-es/strings.xml
<string name="order_quantity">Cantidad: %d</string>
So you can see that in this case, simply appending the strings together would not give you a valid result.
%d is used to represent a part of memory as an integer.
It's most commonly used to print some number to standard output, as follows:
#include <stdio.h>
int main()
{
int n = 42;
printf("The answer to life, universe and everything is %d", n);
return 0;
}
Unlike Java, where you simply concatenate numbers and strings etc., C uses this %something to indicate what is being written. %d indicates, that for example in the printf(), after the comma there will be an argument (in our case it's n), which should be represented as an int.
Refer to List of all format specifiers in C programming for a complete list of format specifiers
Also refer to Official Android Developers Documentation
I'm writing small app and I need to write duration of sport event in i18n. Im using PrettyTime library for date, but when I attempt to use DateUtils or PrettyTime, I have issues..
For example I want to say that duration is 2 minutes. I need some way to pass it to library which supports i18n and accept milliseconds and return Chars.
In android we have:
com.android.internal.R.plurals.duration_minutes
But I can't access to it from my App. Is there any way to make it using correct way and not writing own plurals for all languages?
Thank you
I am not sure which issues you are talking about in context of Android-DateUtils and PrettyTime-library. But I know for sure that Android-DateUtils does not perfectly manage the plural rules of various languages (especially not slavish languages or arabic because it only knows singular and one plural form which is too simple). See for example this Android-issue. About the PrettyTime-library, the same objection is valid if you consider Arabic - see the source.
My recommendation:
Try out my library Time4A (a new AAR-library). Then you can use this code to process a millisecond-input and to produce a localized minute-string:
// input
long millis = 1770123;
// create a duration
Duration<ClockUnit> duration = Duration.of(millis, ClockUnit.MILLIS);
// normalization to (rounded) minutes
duration = duration.with(ClockUnit.MINUTES.rounded());
String s = PrettyTime.of(Locale.ENGLISH).print(duration, TextWidth.WIDE);
System.out.println(s); // 30 minutes
Example for Korean (answer to comment of #Gabe Sechan):
String s = PrettyTime.of(new Locale("ko")).print(duration, TextWidth.WIDE);
System.out.println(s); // 30분 (korean translation of "30 minutes")
Example for Arabic (right to left):
String s = PrettyTime.of(new Locale("ar")).print(duration, TextWidth.WIDE);
System.out.println(s); // ٣٠ دقيقة
This solution currently supports ~90 languages (more than in PrettyTime-library) and three text widths (full, abbreviated or narrow). Accurate pluralization handling is automatically included. Time4A uses its own language resources based on CLDR-data (independent from Android). But you are free to override those resources by defining your own assets (in UTF-8).
About normalization: I just showed the variant which you have described in your question. However, there are many more ways how to normalize durations in Time4A(J). This page will give you more ideas how to use that feature.
If you still miss some languages then just tell me, and I will support it in the next versions of Time4A. Currently supported languages can be found in the tutorial.
Background
I work on an app that has many translations inside it.
I have the next English plural strings:
<plurals name="something">
<item quantity="one">added photo</item>
<item quantity="other">added %d photos</item>
</plurals>
and the French translation:
<plurals name="something">
<item quantity="one">a ajouté une photo</item>
<item quantity="other">a ajouté %d photos</item>
</plurals>
The problem
For both the French and Russian, I get the next warning:
The quantity 'one' matches more than one specific number in this
locale, but the message did not include a formatting argument (such as
%d). This is usually an internationalization error. See full issue
explanation for more.
when choosing to show details , it says:
Thins is, I don't get what should be done to fix it, and if there is even a problem...
The question
What exactly should I do with those strings? What should I tell the translators?
In French singular form is used when count is 0 or 1. In English singular form is used only when count is 1. 0 uses a plural form.
This is why you need to insert a placeholder (%d) in your French singular pattern.
I'm going to write an answer since this is quite an difficult explanation.
In various languages, nouns use the singular form if they end with 1. Refer to: http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html
Explaining in English there are languages where it's correct to say "added 1 photo" as well as "added 101 photo". Notice the "photo". So this means that you should always add "%d" on one strings as well. Android will choose the best scenario to use. Which means that in English it will choose "other" for numbers > 1 and on other languages it will choose "one" for numbers ended in one.
Resuming, add %d to your one string and should be fine. Also make sure your translators respect the plural rules for their language.