I am trying to load plurals in the error text in my TextInputEditText:
<android.support.design.widget.TextInputEditText
android:id="#+id/et_subject_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textCapWords"
android:lines="1"
android:maxLines="1"
android:singleLine="true"
android:text="#{ model.name }"
android:hint="#string/hint_name"
app:validateRegex='#{"^*\\S{2}.*$"}'
app:validateRegexMessage="#{#plurals/error_too_short(model.minNameLength)}"/>
But in my app it shows my string but with %d instead of model.minNameLength value. The app:validateRegex is an attribute is defined by Data Binding Validator library.
My plurals:
<plurals name="error_too_short">
<item quantity="one" >Too short, enter at least %d visible character.</item>
<item quantity="other" >Too short, enter at least %d visible characters.</item>
</plurals>
What's wrong? Or has it a some another way to show plurals in xml?
P.S. I use a data binding engine V2 if it's important.
In the official Android docs:
When using the getQuantityString() method, you need to pass the count twice if your string includes
string formatting with a number. For example, for the string
%d songs found, the first count parameter selects the appropriate plural string and
the second count parameter is inserted into the %d placeholder. If your plural
strings do not include string formatting, you don't need to pass the third parameter to getQuantityString.
So, as you want model.minNameLength to define which plural version to select as well as to insert its value in the string, you should provide it twice. So, the databinding expression should instead be like:
app:validateRegexMessage="#{#plurals/error_too_short(model.minNameLength, model.minNameLength)}"
It should look like this:
<plurals name="recent_messages">
<item quantity="zero">No Recent Messages</item>
<item quantity="one">1 Recent Message</item>
<item quantity="other">%1$d Recent Messages</item>
</plurals>
mContext.getResources().getQuantityString(R.plurals.recent_messages, count, count);
Related
I want to make the app show me the string like "24 girls";
but when I use dataBinding , the space in string can't be shown,the string change like "24girls".
This is my code:
I would suggest you to use plurals for this.
In your strings.xml add this:
<plurals name="scores">
<item quantity="one">%d Girl</item>
<item quantity="other">%d Girls</item>
</plurals>
and in your layout file
android:text="#{#plurals/scores(setScore.score, setScore.score)}"
The first setScore.score is used to decide which string should be used from the plurals.
And the second setScore.score is for argument %d we mentioned in plurals.
You may make this in layout:
android:text="#{setScore.score + ' ' + #string/score_string}"
But as for me better variant is create a special getter in viewmodel which will return string.
For example:
String getScoreFormatted() {
return String.format(getString(R.string.score_string), score);
}
And use it in your layout:
android:text="#{setScore.scoreFormatted}"
Why is better? Because you may code any logic by java or kotlin in you method and your layout will be cleaner.
Create a string resource:
<string name="score_string">%1d girls</string>
and then use it like this:
android:text="#{String.format(#string/score_string, setScore.score)}"
just give whitespace +' '+ in between score and score string
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.
In an android app, I have the following string resources:
<plurals name="test">
<item quantity="zero">"I have 0 item"</item>
<item quantity="one">"I have 1 item"</item>
<item quantity="other">"I have several items"</item>
</plurals>
And the following line of code:
String text = getResources().getQuantityString(R.plurals.test, 0)
which I would expect to return
I have 0 item
But it actually returns
I have 1 item
Why ?
Quantity Strings are broken on some Plattforms and phones as the issue Tracker and this discussion "Should Plurals and Quantity Strings be used" points out. It depends on many factors which you cannot control (i.e. localization on the phone).
One solution can be to take an external library like this one, which mimes the same functionallity.
Another solution is stated in the documentation of plurals in android. Avoid using it and use "quantity-neutral" formulations like "Books: 1"
Change the code like this
String text = getResources().getQuantityString(R.plurals.test, 0,0);
If have the following plural ressource in my strings.xml:
<plurals name="item_shop">
<item quantity="zero">No item</item>
<item quantity="one">One item</item>
<item quantity="other">%d items</item>
</plurals>
I'm showing the result to the user using:
textView.setText(getQuantityString(R.plurals.item_shop, quantity, quantity));
It's working well with 1 and above, but if quantity is 0 then I see "0 items".
Is "zero" value supported only in Arabic language as the documentation seems to indicate?
Or am I missing something?
The Android resource method of internationalisation is quite limited. I have had much better success using the standard java.text.MessageFormat.
Basically, all you have to do is use the standard string resource like this:
<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 = getResources().getText(R.string.item_shop).toString();
textView.setText(MessageFormat.format(fmt, amount));
You can read more about the format strings in the javadocs for MessageFormat
From http://developer.android.com/guide/topics/resources/string-resource.html#Plurals:
Note that the selection is made based on grammatical necessity. A string for zero in English will be ignored even if the quantity is 0, because 0 isn't grammatically different from 2, or any other number except 1 ("zero books", "one book", "two books", and so on). Don't be misled either by the fact that, say, two sounds like it could only apply to the quantity 2: a language may require that 2, 12, 102 (and so on) are all treated like one another but differently to other quantities. Rely on your translator to know what distinctions their language actually insists upon.
In conclusion, 'zero' is only used for certain languages (same goes for 'two' 'few' etc.) because the other languages do not have a special conjugation and therefore the 'zero' field is considered unnecessary
Here is a workaround I am using to handle this issue without switching to MessageFormat.
First I extract the "zero" string into its own string resource.
<string name="x_items_zero">No items.</string>
<plurals name="x_items">
<!-- NOTE: This "zero" value is never accessed but is kept here to show the intended usage of the "zero" string -->
<item quantity="zero">#string/x_items_zero</item>
<item quantity="one">One item.</item>
<item quantity="other">%d items.</item>
</plurals>
Then I have some convenience methods in my own ResourcesUtil
public static String getQuantityStringZero(Resources resources, int resId, int zeroResId, int quantity) {
if (quantity == 0) {
return resources.getString(zeroResId);
} else {
return resources.getQuantityString(resId, quantity, quantity);
}
}
public static String getQuantityStringZero(Resources resources, int resId, int zeroResId, int quantity, Object... formatArgs) {
if (quantity == 0) {
return resources.getString(zeroResId);
} else {
return resources.getQuantityString(resId, quantity, formatArgs);
}
}
Now anytime I want to use a specific string for quantity zero I call:
String pluralString = ResourcesUtil.getQuantityStringZero(
getContext().getResources(),
R.plural.x_items,
R.string.x_items_zero,
quantity
);
I wish there was something better but this at least gets the job done while keeping the string resource XML legible.
Android is using the CLDR plurals system, and this is just not how it works (so don't expect this to change).
The system is described here:
http://cldr.unicode.org/index/cldr-spec/plural-rules
In short, it's important to understand that "one" does not mean the number 1. Instead these keywords are categories, and the specific numbers n that belong to each category are defined by rules in the CLDR database:
http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html
While there appears to be no language which uses "zero" for anything other than 0, there are languages which assign 0 to "one". There are certainly plenty of cases where "two" contains other numbers than just 2.
If Android where to allow you to do what you intended, your applications could not be properly translated into any number of languages with more complex plural rules.
If you are using data binding you can work around this with something like:
<TextView ...
android:text="#{collection.size() > 0 ? #plurals/plural_str(collection.size(), collection.size()) : #string/zero_str}"/>
I've wrote a Kotlin extension to handle all the scenarios that I can think about.
I have zeroResId as optional, so that sometimes we want to handle the zero by displaying "No Items", rather than "0 Items".
English treats zero grammatically as plural.
The selection of which string to use is made solely based on
grammatical necessity.
In English, a string for zero is ignored even
if the quantity is 0, because 0 isn't grammatically different from 2,
or any other number except 1 ("zero books", "one book", "two books",
and so on).
https://developer.android.com/guide/topics/resources/string-resource.html#Plurals
fun Context.getQuantityStringZero(
quantity: Int,
pluralResId: Int,
zeroResId: Int? = null
): String {
return if (zeroResId != null && quantity == 0) {
resources.getString(zeroResId)
} else {
resources.getQuantityString(pluralResId, quantity, quantity)
}
}
Android's implementation seems correct unlike iOS (see details here).
The correct way to implement this should be the same are in MessageFormat, which means that for non-grammatical categories, you should add explicit rules (using numbers instead of categories). A correct implementation could look like this:
<plurals name="item_shop">
<item quantity="0">No item</item>
<item quantity="1">One item</item>
<item quantity="one">%d item</item>
<item quantity="other">%d items</item>
</plurals>
See here you are using the number 0 instead of the plural category zero which does not apply to English.
Using MessageFormat, this would translate to this (you can test here):
{messageNumber, plural, =0 {No item.} =1 {One item.} one {# item.} other {# items.}}
In English the category one is equal to 1 so in the example one should never be used but this is not true for all languages, and when spelling out a number, you are better make sure that you know which plural rule applies for that language.
I've been attempting to utilize the plurals resource with Android but have not had any luck.
Here is my resource file for my plurals:
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<plurals name="meters">
<item quantity="one">1 meter</item>
<item quantity="other">
<xliff:g id="count">%d</xliff:g>
meters
</item>
</plurals>
<plurals name="degrees">
<item quantity="one">1 degree</item>
<item quantity="other">
<xliff:g id="count">%d</xliff:g>
degrees
</item>
</plurals>
</resources>
...and then here is the code I am using when I attempt to extract the quantity string from my resources:
Resources res = this.getResources();
tTemp.setText(res.getQuantityString(R.plurals.degrees, this.mObject.temp_c.intValue()));
...but the text in the TextView remains to be %d degrees and %d meters.
Does anyone know what is happening? I've debugged the code and the res.getQuantityString(...) call is returning a String whose value is %d degrees or %d meters. Though when the quantity happens to be 1 it does correctly evalute to 1 degree or 1 meter.
Thanks in advance for any help!
Regards, celestialorb.
It appears that you need to specify the count twice, the first is used to determine the string to use, and the second is the one that is replaced into the string. e.g.
Resources res = this.getResources();
int tv = this.mObject.temp_c.intValue();
tTemp.setText(res.getQuantityString(R.plurals.degrees, tv, tv));
And at least in my testing so far, the xliff:g elements in the resource aren't needed.
Android "supports" the use of plurals by use of R.plurals which is practically undocumented. Diving into the source code reveals that you should be able to have the following possible versions of a string:
"zero"
"one"
"few" (for exactly 2)
"other" (for 3 and above)
However, I've found that only "one" and "other" actually work (despite the others being used in the android source!).
To use plurals you want to declare you pluralizable strings in a similar way to normal string resources:
<resources>
<plurals name="match">
<!-- Case of one match -->
<item quantity="one">1 match</item>
<!-- Case of several matches -->
<item quantity="other">%d matches</item>
</plurals>
</resources>
Then to actually use them in code, use code similar to what superfell suggested above:
String text = getResources().getQuantityString(R.plurals.match, myIntValue, myIntValue);
myTextView.setText(text);
Same problem here! I guess it is just a flaw in the documentation. The "pure" getQuantitiyString(int, int) method does just get a text resource, without any formatting. As superfell stated: just use the getQuantityString(int, int, Object...) method and hand over your integer value twice.
I'd hoped this worked the same way you did, but it simply doesn't!!
PS: maybe check an answer as the correct one? ;-)