Arabic numbers in UI

Hello everyone,

We are facing an issue with Arabic numbers in the UI. Essentially Arabic numbers are being rendered when the user’s locale is set to Arabic and that’s creating an issue in some places when the text-line is expecting a number, please check attached screenshot below

This is preventing users from updating records until they manually type the number again in English (even if what they are updating in the record is something else not the number field). So is there a way to override this globally so that numbers still show in English everywhere even when the locale is set to Arabic? This is to standardize English numbers everywhere

I have investigated different macros and L10nFacadeImpl and came to realize that format() from l10n is being called at the root and there only the user’s locale is being passed (Same is happening with formatCurrency() where the user’s locale is being passed to it) code below

@Override
    public String format(Object value, String format) {
        return this.format(value, format, getLocale(), getTimeZone());
    }
    @Override
    public String format(Object value, String format, Locale locale, TimeZone tz) {
        if (value == null) return "";
        if (locale == null) locale = getLocale();
        if (tz == null) tz = getTimeZone();
        Class<?> valueClass = value.getClass();
        if (valueClass == String.class) return (String) value;
        if (valueClass == Timestamp.class) return formatTimestamp((Timestamp) value, format, locale, tz);
        if (valueClass == java.util.Date.class) return formatTimestamp((java.util.Date) value, format, locale, tz);
        if (valueClass == java.sql.Date.class) return formatDate((Date) value, format, locale, tz);
        if (valueClass == Time.class) return formatTime((Time) value, format, locale, tz);
        // this one needs to be instanceof to include the many sub-classes of Number
        if (value instanceof Number) return formatNumber((Number) value, format, locale);
        // Calendar is an abstract class, so must use instanceof here as well
        if (value instanceof Calendar) return formatDateTime((Calendar) value, format, locale, tz);
        // support formatting of Map and Collection using JSON
        if (value instanceof Map || value instanceof Collection) {
            String json = JsonOutput.toJson(value);
            return (json.length() > 128) ? JsonOutput.prettyPrint(json) : json;
        }
        return value.toString();
    }

I appreciate your help!

1 Like

If I understand you correctly, you have a user with an arabic locale and a <text-input> field that is submitting data to a transition with a service that is a <number-integer/> or <number-decimal/>.

The user expects english numbers 123 instead of arabic numbers: ١, ٢, ٣, and is rewriting the arabic numbers to the english numbers. I’m assuming that the user never wants to use or see arabic numbers (tell me if this is wrong).

If Moqui is automatically translating numbers from english to arabic I couldn’t find any code for that, so my assumption is that the text-line is just accepting any string instead of specifying the number-integer or number-decimal type to do actual number validation. If you look at the actual value in the database, is it an arabic number or a english number in the example you gave?

Let me know and we can dive into this further.

@michael Well I am populating a request item (the screenshot I attached initially) and giving the user the ability to update it (including its quantity), the quantity is populated through <text-line /> as follows;

<field name="quantity">
    <default-field title="Req Qty">
        <text-line size="5" />
    </default-field>
</field>

The transition I call to update the request item calls a service, and among the service in-parameters I have an auto-parameters to RequestItem entity, which results in the service expecting number-decimal for quantity. The value coming from the database is still in English not Arabic. After investigating the <text-line /> macro in DefaultScreenMacros.qvt.ftl, I noticed that <#assign fieldValue = sri.getFieldValueString(.node)> is called to get the actual value to show, if you check getFieldValueString’s implementation itself it calls String strValue = ec.l10nFacade.format(obj, format) and format function itself defaults to user’s locale through getLocale().

Also maybe it’s good to know that if I manually set the quantity’s parameter type to String;

<parameter name="quantity" type="String" />

Instead of relying on auto-parameters which sets it to number-decimal, then it accepts even Arabic numbers on submission. “Please enter a valid number” is showing currently because quantity’s type is number-decimal.

I want to change all numbers to English not to get around Arabic numbers issue only, but to standardize the whole system, I wouldn’t want any user to see or use Arabic numbers even if their locale is set to Arabic. I wouldn’t want any formatting method, including formatCurrency to rely on user’s locale but instead override that with English for example if possible.

Additional note: After further investigation, for numbers formatting, this is the method that is being called in L10nFacadeImpl.java

@Override public String formatNumber(Number input, String format, Locale locale) {
        if (locale == null) locale = getLocale();
        if (format == null || format.isEmpty()) {
            // BigDecimalValidator defaults to 3 decimal digits, if no format specified we don't want to truncate so small, use better defaults
            NumberFormat nf = locale != null ? NumberFormat.getNumberInstance(locale) : NumberFormat.getNumberInstance();
            nf.setMinimumFractionDigits(0);
            nf.setMaximumFractionDigits(12);
            nf.setMinimumIntegerDigits(1);
            nf.setGroupingUsed(true);
            return nf.format(input);
        } else {
            return bigDecimalValidator.format(input, format, locale);
        }
    }

Notice this line

NumberFormat nf = locale != null ? NumberFormat.getNumberInstance(locale) : NumberFormat.getNumberInstance();

which formats the number based on passed-in locale (even sets it to getLocale() if not passed) and Moqui is always passing the user’s locale (in my case Arabic) through higher level functions/implementations in this case and so that’s why numbers are showing in Arabic.
If I print input, I get 8
and then if I print nf.format(input), I get ٨
** NumberFormat.getNumberInstance(locale) is doing the actual conversion through java

Thanks for your time and attention.