Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Arabic number in Arabic text in Android

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.

like image 537
kingston Avatar asked Dec 14 '15 12:12

kingston


People also ask

How can I type Arabic numbers?

Go to Tools > Options > click on the "Complex scripts" tab, then under General: Numeral select "Context". That way, numbers will appear Hindi (i.e. Arabic) when you're writing Arabic and Arabic (i.e. English) when you're writing English (as you probably know these numbers "1,2,3" are called Arabic numerals).


5 Answers

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

like image 178
Konstantin Loginov Avatar answered Oct 17 '22 12:10

Konstantin Loginov


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

like image 21
MohammadL Avatar answered Oct 17 '22 11:10

MohammadL


Set your TypeFace as per below for Arabic

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

abcd is Arabic font.

textview.setTypeface(font);
like image 26
Devendra Kulkarni Avatar answered Oct 17 '22 13:10

Devendra Kulkarni


The default Locale is constructed statically at runtime for your application process from the system property settings, so it will represent the Locale selected on that device when the application was launched. Typically, this is fine, but it does mean that if the user changes their Locale in settings after your application process is running, the value of getDefaultLocale() probably will not be immediately updated.

If you need to trap events like this for some reason in your application, you might instead try obtaining the Locale available from the resource Configuration object, i.e.

Locale current = getResources().getConfiguration().locale;

You may find that this value is updated more quickly after a settings change if that is necessary for your application.

        // please try below code
 double distance = 2.3; // ex. distance is 2.3
        Locale current = getResources().getConfiguration().locale; //get current locale
        Log.d("Locale", current + " ");


        if(current.toString().equals("ar_EG")){ //for arabic
            char[] arabicChars = {'٠','١','٢','٣','٤','٥','٦','٧','٨','٩'};
            StringBuilder builder = new StringBuilder();
            String str="2.3";
            for(int i =0;i<str.length();i++)
            {
                if(Character.isDigit(str.charAt(i)))
                {
                    builder.append(arabicChars[(int)(str.charAt(i))-48]);
                }
                else
                {
                    builder.append(str.charAt(i));
                }
            }
            Log.d("Locale"," " +builder.toString()+" كم"); // get distance in arabic كم ٢.٣
        }else if (current.toString().equals("en_US")){
            Log.d("Locale"," " +distance+" KM"); // get distance in us english 2.3 KM
        }
like image 39
Krunal Patel Avatar answered Oct 17 '22 13:10

Krunal Patel


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.

like image 44
Mahozad Avatar answered Oct 17 '22 12:10

Mahozad