What is the best way to display the currency symbol locale-dependent? - java

I would like to display the currency symbol ($, €) dependent on the current browser locale.
What is the best approach to do so?
I tried:
locale = FacesContext.getCurrentInstance().getViewRoot().getLocale();
System.out.println(locale); //gives: "en"
Currency.getInstance(locale).getSymbol(); //java.lang.IllegalArgumentException
Currency.getInstance(locale.GERMANY).getSymbol(); //gives €
How can I get the symbol based on locale dependent browser setting (which is "en" here)?
Update
locale.getLanguage() > "de"
locale.getDefault() > "de_DE"
Nevertheless, Currency.getInstance(locale).getSymbol(); fails.

Currency depends on the Country-Part of Locale. Since en does not contain a country part it is an illegal argument for creating a Currency instance.
In other words: Would you expect $, US$, AU$ or £ for Locale "en"? Or something else? There is no currency for "English". There are currencies for the US, GB, Australia and so on but not for English.
Edit
If the user configured his browser properly then you'll get indeed a Locale with both: Country and Language Part (e.g. en-US). These locales you can use the way you've done it in your question.
BUT you should consider using Geotargeting based on IP-Address. There exist databases like GEO-IP and MaxMind. Be aware that there are differences - an US student on semester abroad in Germany surfing with his laptop. His browser may return en-US but a GEO-IP database will target most probably to Germany. But maybe this is exactly what you want?!
Finally you can use one of these approaches as primary targeting factor and the second as backup. When both fail then switch to a default (e.g. US$)

Related

Which "default Locale" is which?

With UNIX locales, the breakdown of which means what is relatively well documented.
LC_COLLATE (string collation)
LC_CTYPE (character conversion)
LC_MESSAGES (messages shown in UI)
LC_MONETARY (formatting of monetary values)
LC_NUMERIC (formatting of non-monetary numeric values)
LC_TIME (formatting of date and time values)
LANG (fallback if any of the above are not set)
Java has a different categorisation which doesn't quite match the real world (as usual):
Locale.getDefault()
Locale.getDefault(Locale.Category.DISPLAY)
Locale.getDefault(Locale.Category.FORMAT)
If you read the documentation on these, Locale.getDefault(Locale.Category.DISPLAY) appears to correspond to LC_MESSAGES while Locale.getDefault(Locale.Category.FORMAT) appears to correspond to some combination of LC_MONETARY+LC_NUMERIC+LC_TIME.
There are problems, though.
If you read the JDK source, you start to find many worrying things. For instance, ResourceBundle.getBundle(String) - which is entirely about string messages - uses Locale.getDefault(), not Locale.getDefault(Locale.Category.DISPLAY).
So I guess what I want to know is:
Which of these methods is supposed to be used for which purpose?
Related, but I made a little test program to see which Java locales corresponded to which UNIX locales and got even more surprising results.
import java.util.Locale;
public class Test {
public static void main(String[] args) {
System.out.println(" Unqualified: " + Locale.getDefault());
System.out.println(" Display: " + Locale.getDefault(Locale.Category.DISPLAY));
System.out.println(" Format: " + Locale.getDefault(Locale.Category.FORMAT));
}
}
Locales according to my shell:
$ locale
LANG="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_CTYPE="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_ALL="en_US.UTF-8"
Output of the program:
$ java Test
Unqualified: en_AU
Display: en_AU
Format: en_AU
So it turns out Java doesn't even get it from the UNIX locale. It must be using some other back door to get the settings without using those.
It's hard to understand what you are asking here. Instead, you make a statement that reveals that you're not necessary a Java programmer. It's OK, it does not matter really.
Few things to clarify:
The Locale class is in JDK since Java 1.1
Things like Locale.Builder, Locale.Category and many others are here from Java 7 (JDK 1.7)
Locale-aware classes and methods like DateFormat, NumberFormat, Collator, ResourceBundle, String.toLowerCase(Locale), String.toUpperCase(Locale) and many, many more are here for quite a long time each (long before JDK 1.7)
Prior to Java 7/JDK 1.7 there was only one method of acquiring current OS Locale - call Locale.getDefault() (that is without parameters)
In other words, prior to Java 7, Java's Locale Model was as simple as one system property composed of a language, a country and an optional locale variant. That has changed with Java 7 (end was further extended with Java 8...) and now you have two system properties, one for formatting and one for displaying user interface messages.
The problem is, there is substantial amount of legacy code written in Java and this could shouldn't break when you upgrade the platform. And that is exactly why you still have parameterless Locale.getDefault() around. Moreover (you may test it yourself), Locale.getDefault() is basically interchangeable with Locale.getDefault(Locale.Category.DISPLAY).
Now, I said formatting and user interface messages. Basically, formatting is not only formatting, but things like character case conversion (LC_CTYPE), collation (LC_COLLATE) as well. Sort of anything but user interface messages. Sort of, because default character encoding (which depends on an OS, BTW) is not part of Locale. Instead you need to call Charset.defaultCharset().
And the fallback rules (built in Java, not read from OS) could be worked out with ResourceBundle.Control class. And as we know, it is rather related to UI category...
The reason why Java Locale Model is different from POSIX (not UNIX, it's more universal), is the simple fact that there are quite a few platforms out there. And these platforms doesn't necessary use POSIX... I mean not only Operating Systems, but things like web... Java is striving to be universal and versatile. As the result Java's Locale Model is convoluted, tough luck.
I have to add that nowadays, it's not only the language and the country, but there are also things like preferred script, calendar system, numbering system, specific collation settings and possibly more. It even works sometimes.

Currency Symbol Internationalization Java

I'm trying to get the number format according to current locale but I have a problem with the currency symbol.
This is my method:
import java.util.Locale;
import java.text.NumberFormat;
public void i18nCurrency(Locale currentLocale) {
Double price = 9876543.21;
NumberFormat currencyFormatter =
NumberFormat.getCurrencyInstance(currentLocale);
System.out.println(currencyFormatter.format(price));
}
It prints: ¤ 9 876 543,21 for uk and ¤9.876.543,21 for german. The number format is correct, but I need to get the currency symbol as well. Why I can't get the symbol?
The symbol you're getting is a universal currency placeholder. It is displayed when currency is unknown.
You probably wonder why it is unknown. Well, from your description you simply called the method passing something like Locale.GERMAN. If you did, there is no way of knowing what currency to use, because:
Euro is a currency of Germany and Austria
Swiss Frank (SFr.) is a currency of Switzerland
Each of these countries has German as at least one of their official languages. In order to resolve the problem, you always need to pass a country, i.e. call the method with Locale.GERMANY as a parameter.
Now, the harder part. It is all fairly easy when you are working with desktop application. All you have to do is to detect current OS locale like this:
Locale currentLocale = Locale.getDefault(LocaleCategory.FORMAT);
However, this method won't work with web applications. I suspect this is the case. Well, the Locale that web browsers give you might be not suitable for formatting currencies, as they may lack information about the country.
The recommended way to solve this issue is to create user profile and ask users to select the Locale (separately for UI translations and formatting purposes).
I still have to point out one important thing, because I don't want you to run into problems. When you have some monetary value in your application (usually it should be an instance of BigDecimal class, as double is not suitable for this purpose), it represents some value in a specific currency. Be it Euro, British Pound, or a Dollar, but the value is specific. It doesn't really make sense to format this value for specific country currency, as you should first change the amount (I believe you understand why).
What you probably need instead, is overriding the currency symbol or currency code to match your currency. The format and the symbol placement should obviously stay intact.
Please consider this example:
Currency dollar = Currency.getInstance("USD");
NumberFormat fmt = NumberFormat.getCurrencyInstance(Locale.GERMANY); //this gets € as currency symbol
BigDecimal monetaryAmount = BigDecimal.valueOf(12.34d);
String originalEuros = fmt.format(monetaryAmount);
System.out.println(originalEuros);
fmt.setCurrency(dollar); // change the currency symbol to $
String modifiedDollars = fmt.format(monetaryAmount);
System.out.println(modifiedDollars);
This prints:
12,34 €
12,34 USD
Wait, why? The answer to your question lies in this subtle code snippet:
System.out.println(currency.getSymbol(Locale.GERMANY));
System.out.println(currency.getSymbol(Locale.US));
The result:
USD
$
What gets printed depends on a Locale. It is probably better this way, I cannot tell...
I believe, unless you are creating Internet currency exchange application, you should stick to my example.

Implementing Bitcoin and java.util.Currency

I'm trying to add bitcoin as a currency to display on my site. I've got exchange rates and everything, but I keep getting an IllegalArgumentException whenever I use java.util.Currency.getInstance("BTC"). This makes sense since it's not included in the list of ISO 4217 currency codes, and also not in Java 7. I've seen a couple of options, but nothing that really solves my issue.
According to the Java platform docs, you can override a specific locale's currency by creating a file $JAVA_HOME/lib/currency.properties. This is a problem since bitcoin is not tied to a specific locale, nor should it be used in place of any country's currency.
Another similar situation was presented in this StackOverflow post, where China had a second currency code to be used, so the solution was to build your own currency.data file that added a second currency for the China locale. This is better, but there is still the issue of tying a currency to a locale.
Has anyone run into this problem or found a workaround? I know bitcoin is relatively new, but it'd be cool to be able to display prices in bitcoin format.
You cannot use BTC as the currency code for bitcoins under ISO 4217. BT is reserved for Bhutan. However, ISO 3166-1 reserves several country codes for user definition. Additionally, the wiki for ISO 4217 lists XBT as a currency code for bitcoins (unofficially, of course).
Locale.Builder b = new Locale.Builder();
b.setRegion("XB");
Locale xb = b.build();
Currency bitcoin = Currency.getInstance(xb);
Your currency.properties file will look like:
XB=XBT,000,3
Unfortunately, you cannot have 8 for the minor unit because the parsing for java.util.Currency only handles a minor unit of 0-3:
Pattern propertiesPattern = Pattern.compile("([A-Z]{3})\\s*,\\s*(\\d{3})\\s*,\\s*([0-3])");
How to handle bitcoin money is explained well on the documentation. Take a look at this https://en.bitcoin.it/wiki/Proper_Money_Handling_(JSON-RPC)
Hope it helps

Locale Currency Symbol

I having some problems getting the default currency symbol of the system.
I am getting the currency symbol this way:
Currency currency = Currency.getInstance(Locale.getDefault());
Log.v("TAG",currency.getSymbol());
When the system language is in English (United States) the right symbol shows up ($).
But when i choose the language Portuguese (Portugal) it returns this symbol ¤.
What can be causing this?
This seems to be a known issue (http://code.google.com/p/android/issues/detail?id=38622.
I came to a possible solution this way:
Since the problem is in the Symbol and not the Currency code, i solved this problem creating a staticMap where the key is the CurrencyCode and the value is the Symbol.
public static final Map<String, String> MYCURRENCIES = new HashMap<String, String>(){
{
put("EUR","€");
put("USD","$");
(..)
}
};
In order to get all (or almost) the currencies codes available in the locales information you can do something like this:
for (Locale ll: Locale.getAvailableLocales()){
try {
Currency a = Currency.getInstance(ll);
Log.v("MyCurrency",a.getCurrencyCode()+"#"+a.getSymbol());
}catch (Exception e){
// when the locale is not supported
}
}
After you created you Map with the CurrencyCode and Symbol you just have to something like this:
Currency currency = Currency.getInstance(Locale.getDefault());
String curSymbol = MYCURRENCIES.get(currency.getCurrencyCode());
Some thoughts;
Could it be that you're getting the right symbol for (Euro) but your font/log doesn't have it and it only looks like that symbol?
Locale is a pretty frisky class, it's constructors have no error checking. You can easily see locales that aren't supported or don't really exist in the standards (such as "de_US" for "German as spoken in the US").
Note that locale data is not necessarily available for any of the locales pre-defined as constants in this class except for en_US, which is the only locale Java guarantees is always available, and it differs in different Android releases, and also can be limited by the operator or custom builds. pt_PT was added in 2.3.
Regarding the option you presented, if you check out Unicode's standards as they have been implemented in API8 and up, Portugal exists only as a territory (and not the combination).
It may be better if you therefore create a partial locale instance from the country code alone, and then get the currency symbol for Portugal for instance. You will not be able to comply with the predefined statics of Locale's class as it pretty narrowly supports different locales (It's a short list), but it should work as this locale has the data you are looking for in the system.
I would also try and see if currency.toString() returns the EUR (the ISO 4217 code of the currency, usually a three letter acronym).
NumberFormat currencyFormatter = NumberFormat.getCurrencyInstance();
String cur = currencyFormatter.format(yourValue);
This Formats your int/double to a currency based on the Device Language
String currency = cur.replaceAll("[0-9.,]","");
And this is how to get just the currency Symbol, by replacing all numbers, dots and commas

Get currency symbol aka localeconv() in ColdFusion?

I'm doing some javascript work inside a ColdFusion shopping cart, and I need to be able to format some numbers in js which will mimic LScurrencyFormat() in CF.
Currently we are taking the first (left,1) character of a formatted string but that doesn't work for currencies like Yen or Euro which come after the number, not to mention any multiple character currency symbols.
What I need to find, based on the current CF locale, is
currency symbol
decimal delimiter (, or .)
leading or trailing (before or after the number)
From there i can run my own js formatting to make the formatted numbers come out as expected on the page.In php we can use localeconv() to get these values... how can I find them in CF?
I am not aware of any built in functions. However, you can obtain the first two items from java. As far as the third, the closest suggestion I have seen is to parse the localized number pattern and detect the position of the currency sign ie \u00A4. Note: It is just a mask placeholder. It is not the same as the actual currency symbols like "$" or "£".
Edit:
As discussed in the comments, getLocale() returns some user friendly name which unfortunately does not quite line up with java's. The easiest way to get the java locale object for the current request is using getPageContext().getResponse().getLocale().
<cfscript>
// Get the current locale as a java object
javaLocale = getPageContext().getResponse().getLocale();
// get numeric settings for that locale
currency = createObject("java", "java.text.DecimalFormat").getCurrencyInstance(javaLocale);
symbols = currency.getDecimalFormatSymbols();
// 164 => decimal code point for currency sign
currencyPattern = currency.toLocalizedPattern();
result.hasTrailingCurrencySymbol = currencyPattern.indexOf(javacast("int", 164)) > 0;
result.currencySymbol = symbols.getCurrencySymbol();
result.decimalSeparator= symbols.getDecimalSeparator();
WriteDump(result);
</cfscript>
getLocale() returns the old cf5 style locale "names" but only for those locales supported by cf5. if you dump out the supported locales (Server.Coldfusion.SupportedLocales) you'll see the goofy old cf5 style locale names as well as the core java locale IDs (ie both "Chinese(China)" and "zh_CN"). if your locale wasn't one of the cf5 supported locales you should see the core java locale ID (ie th_TH for thai, thailand). see
http://cfbugs.adobe.com/cfbugreport/flexbugui/cfbugtracker/main.html#bugId=82474
as a small tweak to leigh's answer, you should also be concerned with the currency/locale's fraction digits. for instance in normal practice, you can't have part of a yen (ie 1.1 isn't quite kosher). you can get that info from the Currency class's getDefaultFractionDigits() method:
result.fractionDigits=currency.getDefaultFractionDigits();

Categories

Resources