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? ;-)
Related
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);
Basically what I'm trying to achieve is to access from code two related resources.
Consider this example, the best solution I can think of to my problem:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="black">⬛</string><color name="black_c">#000000</color>
<string name="white">⬜</string><color name="white_c">#ffffff</color>
</resources>
Given a string N in my code I can access both the second string associated to it (⬛ or ⬜) or the color by adding "_c" to the end of the N string.
So, if N="black" I can use N to retrieve both ⬛ and #000000 (with N + "_c")
Is there a better way to do this? My solution feels a bit hacky. Hope I managed to explain what I'm trying to achieve, thanks!
I have another proposal. I hope it will help you.
If you have a colors.xml and a strings.xml (within the values directory)
<!-- colors.xml -->
<resources>
<color name="black">#000000</color >
</resources>
<!-- strings.xml -->
<resources>
<string name="black">Some black string</string>
</resources>
Using the same name you can access both of them if you are able to get the different id (ie R.string.black or R.color.black). The getIdentifier()` method can do it. So you can try (not tested)
String name = "black;
String choice = "color"; //or "string" dependending on if you want the color or the string
int resID = getResources().getIdentifier(name, choice, getPackageName());
Resources resources = getResources();
//Then access using
//If choice=="color"
int color = resources.getColor(resId);
//If choice=="string"
String text = resources.getString(resId);
Ya it sounds a bit hacky. You could use styles to group all your resources (as attributes to that style) and then read those styles. More info here: How to retrieve style attributes programmatically from styles.xml
But this again sounds hacky. What's your actual requirement? The way you suggested sounds OK. You can go ahead with that implementation since from a performance standpoint, it doesn't take any extra over head. Just make sure you write a neat API to fetch data from your xml.
In res/values/strings.xml, I have
<?xml version="1.0" encoding="utf-8"?>
<resources>
<plurals name="days_amount">
<item quantity="one">%d day EN</item>
<item quantity="other">%d days EN</item>
</plurals>
</resources>
And in res/values-fr/strings.xml, i have
<?xml version="1.0" encoding="utf-8"?>
<resources>
<plurals name="days_amount">
<item quantity="one">%d jour FR</item>
<item quantity="other">%d jours FR</item>
</plurals>
</resources>
With English locale res.getQuantityString(R.plurals.days_amount, 0, 0) returns:
"0 days"
which is okay per English rules (aka zero quantity <=> several quantities).
But with a French locale, it returns:
"0 jours"
which is NOT okay per French rules (aka zero quantity <=> singular quantity).
It should have returned "O jour"
French rules: http://www.unicode.org/cldr/charts/25/supplemental/language_plural_rules.html#fr
So, is this a bug, or am I doing something wrong ?
Your code looks correct to me. Here's why:
The Android documentation contains the text zero, one, two, few, many, and other, which exactly matches the wording of the Unicode language plural rules. This suggests that Android uses them.
These plural rules state that the French case of 0 books is handled by the one case.
Since the Android documentation doesn't explicitly mention the Unicode language plural rules, you should request clarification from the Android support.
Before you do that, you should consult this other question, where the French rules obviously worked.
Indeed your concern is right, in French amount 0 should be treated as 'one' instead of 'other'. The issue is in some Android APIs: API <= 16
So in case you want still to support API 16 just add an extra string key for the zero amount. Let's assume you have:
<plurals name="label_message_reminder_text">
<item quantity="other">Vous avez %1$s nouveaux messages</item>
<item quantity="one">Vous avez %1$s nouveau message</item>
</plurals>
Just add an extra string:
<string name="label_message_reminder_text_zero">Vous avez %1$s nouveau message</string>
And then when retrieving plurals can do something like:
String newMessagesText = getMyString(context, resourceId, amount);
messageTextView.setText(String.format(newMessagesText, amount));
And you can have a method to do the getMyString():
public String getMyString(Context context, int resourceId, int amount){
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN) {
if(amount == 0) {
switch (resourceId) {
case R.plurals.label_message_reminder_text:
return context.getResources().getString(R.string.label_message_reminder_text_zero);
}
}
}
return context.getResources().getQuantityString(resourceId, amount);
}
You're doing something wrong. It's doing exactly what you're telling it to.
Your rules say that when the value is other, use "days" or "jours". You then pass in the value 0, which is other, so it uses "days" and "jours", as requested.
You could try setting a third value for quantity "zero" which has the language-specific rules.
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);
Hi I build a quiz application.
I have the following (values/)question.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="question">
<item name="correct">A</item>
<item name="wrong">B</item>
<item name="wrong">C</item>
<item name="wrong">D</item>
</string-array>
</resources>
I would like to have a question with four possible answers but when i retrieve my answers in Java.. I don't know which answer is correct. So I decided to use name attribute in the item tags to pass a value of 'correct' or 'wrong' answer.
Is there anyway to get the name along with the tag value?
because when i use String[] test = res.getStringArray(R.array.question); I can only get the value of each item in my array.
or because this is my 1st time in Android. is there other suitable approach to do this?
thanks
You need to use Handler to parse the xml.
to get attribute value, code is :
attributes.getValue("name")
Try these links for reference:
first and
second
I have made many quizzes, and one nice way is to put correct option always on top, right after question. You can use random function to shuffle options while displaying in an activity.
in Activity:
String[] questionArray = getResources().getStringArray(R.id.question);
then you have
questionArray[0] -> A
questionArray[1] -> B
questionArray[2] -> C
questionArray[3] -> D