Search code examples
androidlocalearabic

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.


Solution

  • 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