21

EDIT

I'm porting my app to Arabic locale. I have some getString() with parameters like:

getString(R.string.distance, distance)

where <string name="distance">%1d km</string>

The requirement is that in Arabic I should show it like this: "2.3 كم".

If I set as the locale for Saudi Arabia (country = "sa") or UAE (country = "ae") the number are shown in Eastern-Arabic but my client wants them in Western-Arabic.

The solution here is to use Egypt as a country in the locale but this is not possible for me.

I tried:

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void setAppContextLocale(Locale savedLocale) {
    Locale.Builder builder = new Locale.Builder();
    builder.setLocale(savedLocale).setExtension(Locale.UNICODE_LOCALE_EXTENSION, "nu-latn");
    Locale locale = builder.build();
    Configuration config = new Configuration();
    config.locale = locale;
    config.setLayoutDirection(new Locale(savedLocale.getLanguage()));
    mAppContext.getResources().updateConfiguration(config, mContext.getResources().getDisplayMetrics());
}

as suggested in this question but after that the country is ignored so both SA and AE locales use the strings in the default file.

kingston
  • 11,053
  • 14
  • 62
  • 116
  • 9
    just a clarification, the numbers **0, 1, 2, 3, 4, 5, 6, 7, 8, 9** are Arabic, the other shape of the numbers are Indian – sameh.q Jan 05 '16 at 10:58
  • For more information about different numerals refer to [this Wikipeida article](https://en.wikipedia.org/wiki/Eastern_Arabic_numerals#Numerals). – Mahozad Nov 04 '21 at 14:33

6 Answers6

36

There's such issue in Google's bugtracker: Arabic numerals in arabic language intead of Hindu-Arabic numeral system

If particularly Egypt locale doesn't work due to some customer's issue(I can understand it), then you can format your string to any other western locales. For example:

 NumberFormat nf = NumberFormat.getInstance(new Locale("en","US")); //or "nb","No" - for Norway
 String sDistance = nf.format(distance);
 distanceTextView.setText(String.format(getString(R.string.distance), sDistance));

If solution with new Locale doesn't work at all, there's an ugly workaround:

public String replaceArabicNumbers(String original) {
    return original.replaceAll("١","1")
                    .replaceAll("٢","2")
                    .replaceAll("٣","3")
                    .....;
}

(and variations around it with Unicodes matching (U+0661,U+0662,...). See more similar ideas here)

Upd1: To avoid calling formatting strings one by one everywhere, I'd suggest to create a tiny Tool method:

public final class Tools {

    static NumberFormat numberFormat = NumberFormat.getInstance(new Locale("en","US"));

    public static String getString(Resources resources, int stringId, Object... formatArgs) {
        if (formatArgs == null || formatArgs.length == 0) {
            return resources.getString(stringId, formatArgs);
        }

        Object[] formattedArgs = new Object[formatArgs.length];
        for (int i = 0; i < formatArgs.length; i++) {
            formattedArgs[i] = (formatArgs[i] instanceof Number) ?
                                  numberFormat.format(formatArgs[i]) :
                                  formatArgs[i];
        }
        return resources.getString(stringId, formattedArgs);
    }
}

....

distanceText.setText(Tools.getString(getResources(), R.string.distance, 24));

Or to override the default TextView and handle it in setText(CharSequence text, BufferType type)

public class TextViewWithArabicDigits extends TextView {
    public TextViewWithArabicDigits(Context context) {
        super(context);
    }

    public TextViewWithArabicDigits(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public void setText(CharSequence text, BufferType type) {
        super.setText(replaceArabicNumbers(text), type);
    }

    private String replaceArabicNumbers(CharSequence original) {
        if (original != null) {
            return original.toString().replaceAll("١","1")
                    .replaceAll("٢","2")
                    .replaceAll("٣","3")
                    ....;
        }

        return null;
    }
}

I hope, it helps

Community
  • 1
  • 1
Konstantin Loginov
  • 15,802
  • 5
  • 58
  • 95
  • OK this solution would require to change all the places where I'm using a number in a string that's why I'm trying to avoid it. But thank you for the information about the old Android issue. – kingston Jan 05 '16 at 16:20
  • @kingston you can simplify it by introducing new class `TextViewWithArabicDigits extends TextView`, where `override` `setText(CharSequence text, BufferType type)` and add check if there's any wrong digits - replace them there. Then you just need to use this `TextViewWithArabicDigits` instead of `TextView`. Not much extra code. – Konstantin Loginov Jan 05 '16 at 16:27
  • @kingston added the proposal into my answer. – Konstantin Loginov Jan 05 '16 at 16:39
  • thanks. I like your second solution (with the helper method). Let's see whether there is something simpler... – kingston Jan 05 '16 at 17:48
  • @kingston yep, actually it's really interesting question, good job! So let's see where it will go. – Konstantin Loginov Jan 05 '16 at 17:50
13

There is an easy way. Taking your integer number and format it to string, and it will apply localization implicitly to numbers by this method:

String YourNumberString = String.format("%d", YourNumberInteger);

So 123 will become ١٢٣ and so on.

For more information see section "Format numbers" at: https://developer.android.com/training/basics/supporting-devices/languages

MohammadL
  • 2,398
  • 1
  • 19
  • 36
3

Set your TypeFace as per below for Arabic

Typeface font = Typeface.createFromAsset(getAssets(), "fonts/abcd.TTF");

abcd is Arabic font.

textview.setTypeface(font);
Robin Kanters
  • 5,018
  • 2
  • 20
  • 36
  • Do you mean not to use the Arabic locale and use only a different font? But that would break the RTL alignment of the text – kingston Dec 14 '15 at 13:19
1

As the question title has the generic word "Android", I propose another Kotlin solution for Kotlin developers ending up here. This might not be the best solution regarding performance.

fun String.convertDigitsToWesternArabic() = this
    // Replace Eastern Arabic numerals
    .replace(Regex("[٠-٩]")) { match -> (match.value.single() - '٠').toString() }
    // Replace Persian/Urdu numerals
    .replace(Regex("[۰-۹]")) { match -> (match.value.single() - '۰').toString() }
val result = "۱۲۳ کم".convertDigitsToWesternArabic() // result: "123 کم"

See this Wikipedia article for difference between these numerals.

Mahozad
  • 18,032
  • 13
  • 118
  • 133
0

I resolved this problem by using format method of String class, it works very well:

String correctLocaleTime = String.format(new Locale("en","US"),
            "%02d:%02d", 
            minutes, 
            seconds)

Just pass a local instance variable into the first parameter of format method.


This code is just for people who want to understand the context of my solution:

 @Override
    public void onBindViewHolder(final RecordingsViewHolder holder, int position) {

        RecordingItem item = getItem(position);
        long itemDuration = item.getLength();

        long minutes = TimeUnit.MILLISECONDS.toMinutes(itemDuration);
        long seconds = TimeUnit.MILLISECONDS.toSeconds(itemDuration)
                - TimeUnit.MINUTES.toSeconds(minutes);

        holder.vLength.setText(String.format(new Locale("en","US"),
                "%02d:%02d",
                minutes,
                seconds));
}
iLyas
  • 1,047
  • 2
  • 13
  • 30
-1

In JAVA

String.format(Locale.US, getString(R.string.distance), distance)

In Kotlin we can add extension function:

fun Resources.formatStringWithArabicNumbers(resId: Int, vararg args: Any?)
    = String.format(Locale.US, this.getString(resId), *args)

And we can call it like:

resource.formatStringWithArabicNumbers(R.string.distance, distance)
Baku
  • 1
  • 1
  • 1
    Welcome to SO! While the question does not post the language, it is clearly not Kotlin. Please only post answers in the same language as the question has been asked in. – Ryan C Dec 16 '19 at 22:14