Android Conversion to Ordinal Format - android

Before I go reinventing the wheel, does Android have any facility for converting an integer to an ordinal string with multi-language support? That is, it would convert the integer 3 to "3rd" in English and "3eme" in French.
I can see how to do this myself using a bit of logic along with Android's automatic string substitution, but thought that this surely must have been encountered by others, and not just for use with dates.

Java nor Android have support for creating ordinal strings. Android does have support for creating plural string resources, but not ordinals.

If the range for which you need ordinals is limited, then you are probably best to use a <string-array> to define them:
<string-array name="ordinals">
<item>zeroth</item>
<item>1st</item>
<item>2nd</item>
<item>3rd</item>
<item>4th</item>
<item>5th</item>
<item>6th</item>
</string-array>
and then access it via:
String ordinal = getResources().getStringArray(R.array.ordinals)[count];
Of course this doesn't get you automatic translation into other languages - you have to do that yourself (and if your count goes outside the range in this simplistic code you will get an exception).

Just for future reference, I came across this issue and discovered that now you can use the ICU MessageFormat to do something like this:
import android.icu.text.MessageFormat
fun toOrdinal(day: String): String {
val formatter = MessageFormat("{0,ordinal}", Locale.getDefault())
return formatter.format(arrayOf(day.toInt()))
}
You will get these results:
1 -> 1st
2 -> 2nd
3 -> 3rd
4 -> 4th
5 -> (...)

Related

Converting Numbers to Local (UTF8) Bengali numbers

I am trying to convert the English numbers (1, 2, 3) to Bengali numbers (১, ২, ৩).
For example, if I get 10000, then I want to show like ১০,০০০.
I can replace the number one by one with the Bengali counterpart using replaceAll method
But I want to know if there is an alternative solution to do that instead of the above.
Use this:
val convertedString = String.format(Locale.forLanguageTag("bn"), "%d", 1234567890)
I have used NumberFormat from popular library Intl and converted it easily like below
NumberFormat("##,##,##,###", "bn").format(10000)
And the output is:
১০,০০০

Android Plurals for float values

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>

How to use Android xliff:g

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

How to describe duration in Android?

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.

Plurals not working as intended in android

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.

Categories

Resources