Showing Arabic digits everywhere

Hello,

In one of our projects, the client as per recent regulation needs to show all product prices in arabic digitss (٠١٢٣٤٥٦٧٨٩). So is there a way to do so globally such that if locale is ar then I can show these digits accordingly?

Dear Taher,

The answer to your question has several parts.

In short, Moqui delegates on Java NumberFormat for the actual formatting (detailed explanation below).
However, in order to format a number with digits such as ٠١٢٣٤٥٦٧٨٩ by using Java NumberFormat, you not only have to provide language and country as locale but also the extension.

See java - Setting Arabic numbering system locale doesn't show Arabic numbers - Stack Overflow

Therefore you will have to deal with how to provide the specific extended locale to Moqui.

1. How Moqui formats currency values

Moqui formats currency amounts by calling to ec.l10n.formatCurrency(amount, uomId).
Please see moqui/framework/src/main/groovy/org/moqui/impl/context/L10nFacadeImpl.java for its implementation.

The formatCurrency has several overloads with more parameters. All of then call in turn to the more generic one:
public String formatCurrency(Object amount, String uomId, Integer fractionDigits, Locale locale)

The function formatCurrency, in turn, uses Java NumberFormat for the actual formating:

// ...
if (locale == null) locale = getLocale();
// ...
NumberFormat nf = NumberFormat.getCurrencyInstance(locale);
// ...
return nf.format(amount);

However, screen renderer and widgets will call format currency just with amount and the uomId.
When the locale is not explicitly specified, then it is got with L10nFacadeImpl.getLocale() which in turn gets the locale from UserFacade.getLocale(). The locale returned will be the user locale when a user is logged in or defaults to the request locale for anonymous users.

See implementation in:
moqui/framework/src/main/groovy/org/moqui/impl/context/UserFacadeImpl.groovy#UserInfo.setInfo

Here in UserFacadeImpl.UserInfo.setInfo() is where the local is got form the current logged in user. If they are an anonymouse user, then the locale is got from the HttpServletRequest with ufi.request.getLocale().

2. How to make Java NumberFormat use proper arabic numbers

Note: Please bear in mind that some times the term “arabic numbers” may be confusing, because the “1234567890” are called “Arabic numerals” (in contrast to Roman numbers). However, here we are talking about numbers used in Arabic locales.

To test that you can output numbers with the desired digits, use the following:

import java.math.BigDecimal;
import java.text.NumberFormat;
import java.util.Locale;

    class ArabNumberDemo {
        public static void main(String args[]){

            // See https://stackoverflow.com/questions/29154887/setting-arabic-numbering-system-locale-doesnt-show-arabic-numbers
            
            // For supported "ar" locales, see:
            // https://www.oracle.com/java/technologies/javase/jdk8-jre8-suported-locales.html

            // Locale specifying just the language "ar".
            Locale languageOnly = new Locale("ar");
            // Locale specifying language and country (change the country to yours)    
            Locale languageCountry = new Locale("ar", "SA");
            // Locale with language, country and extensions (change the country and variant to yours)
            Locale languageCountryExtension = new Locale.Builder().setLanguageTag("ar-SA-u-nu-arab").build();
            
            // The value to format
            BigDecimal amount = new BigDecimal("1234567890.123");

            // The output
            System.out.print("Locale with language only: ");
            System.out.println(NumberFormat.getCurrencyInstance(languageOnly).format(amount));
            System.out.print( "Locale with language and country: ");
            System.out.println(NumberFormat.getCurrencyInstance(languageCountry).format(amount));
            System.out.print( "Locale with language, country and extension: ");
            System.out.println(NumberFormat.getCurrencyInstance(languageCountryExtension).format(amount));

        }
    }

Note: if this did not work, ensure that the font that you are using has support for all Unicode characters needed.

3. What to do now?

Now you are facing your real problem. If you just wanted to format a currency amount explicitly in your screen, then you could just call to ec.l10n.formatCurrency(amount, uomId, fractionDigits, locale), where locale would be the Java Locale built as it was explained above.

However, if you want that all OOTB Moqui screens and widgets output currency amounts in the format that you need, then you will have to manage how to provide the Java locale with extensions.

For anonymous users perhaps you could achieve that with a servlet filter so that when the request locale were just language and country you could append the extension to it.

For registered users, perhaps you could set the locale in their UserAccount.locale entity field.

Ideally, it would be nicer just to be able to override the formatCurrency function, but it does not seem that Moqui allows to inject a custom implementation of facades.

Best,

Francisco Faes
“www.blurbiness.com”

Fantastic feedback as usual @franciscofaes thank you so much.

I think I know where to go with this. But it would certainly be quite cool to be able to inject the interfaces of the execution context at runtime for specific situations. That’s worth thinking about.

Again excellent suggestions, and I think the locale solution fits nicely right now.