I am setting up a multilingual site that deals with currencies. I want to be able to display correct currency formats based on the selected language. The server side PHP stuff is a doddle. Using a combination of PHPs NumberFormatter and strftime I have been able to format currencies and dates correctly.
There is however a requirement to have the same degree of formatting done client side with javascript.
I have come across Globalization (former jQuery plugin) and it looks quite promising.
If I want to display a dollar value in American English I can do something like this:
jQuery.preferCulture("en-US");
// Formatting price
var price = jQuery.format(3899.888, "c");
//Assigning stock price to the control
jQuery("#price").html(price);
and this will output:
$3,899.89
While doing:
jQuery.preferCulture("fr-FR");
// Formatting price
var price = jQuery.format(3899.888, "c");
//Assigning stock price to the control
jQuery("#price").html(price);
outputs:
3 899,89 €
which looks perfect. however, I have a need to output multiple currencies. So, if I have 'fr-FR' selected as my preferred culture, how can I output, say, a dollar value like so:
3 899,89 $
so that the format is French, but the value is American Dollar. I have looked but not found anyway to pass a currency symbol as an argument.
The only documented way to modify the currency symbol in Globalize is to change the numberFormat.currency.symbol property of a given culture—in this case, the fr-FR culture. This will kind of do what you want, but it’s not a very elegant solution, and you would need to manually build a table of correct symbols for each locale and write another method to swap them out. (n.b. It is possible to pass a third argument to Globalize.format with a different locale identifier, but this just formats the number using that locale’s cultural settings.) Looking at the culture definition syntax, there is simply no provision for displaying different currencies using a given locale.
If you were to look elsewhere, the dojo/currency module in the Dojo Toolkit does do exactly what you need, using data from the Unicode Common Locale Data Repository to determine how to represent various currencies in different locales. So you can set your locale to fr, write currency.format(3899.888, { currency: "USD" }), and it will output the currency in USD in the correct format for the French locale.
I had the same problem, in the end I just replaced the default currency symbol on the output with the symbol I wanted to display. It's a bit primitive but it keeps the formatting correct for the locale with the currency symbol you want.
function formatCurrency(value, format, symbol){
var formattedValue = Globalize.format(value, format);
if (typeof symbol === "string") {
formattedValue = formattedValue.replace(Globalize.culture().numberFormat.currency.symbol, symbol);
}
return formattedValue;
}
document.getElementById("price1").innerHTML = formatCurrency(123.34,"c"); //<-- $123.34
document.getElementById("price2").innerHTML = formatCurrency(123.34,"c","£"); //<-- £123.34
Here is the fiddle
Related
Well one can format a value like this:
const formatter new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' });
console.log(formatter.format(200));
This automatically selects the "correct" currency symbol. Now what I like is to actually get the symbol (given the locale). I could try formatToParts isn't really doing that since the "parts" are sometimes in a different order: in German the symbol is at the end while in English the symbol is before the amount.
I would like the symbol to be in front of other fields (ie input)
The main important thing to me is not even that the correct symbol is used (it should) - but most important it should always be the same as the one used for displaying.
I am creating new service, and need country ISO code based on mobile number. I am using google-libphonenumber package but in this package it giving information based on mobile and country ISO code.
I searched on google about this and offical npmjs site https://www.npmjs.com/package/google-libphonenumber but didn't get as per my requirement.
const phoneUtil = require('google-libphonenumber').PhoneNumberUtil.getInstance();
const number = phoneUtil.parseAndKeepRawInput('7358469469', 'IN');
const number = phoneUtil.parse('7358469469');
Expected result: I am expecting country ISO code based on mobile no.
Actual result: parseAndKeepRawInput and parse function expecting mobile and country ISO code to return detail information.
This is mission impossible, a phone number can be valid in various country. Phone number 7358469469 can be in India (+91-7358469469) or China (+86-7358469469) or any other countries.
However, if the phone number starts with +, you can parse the number and extract the country code, according to the country code specification. Please refer to https://countrycode.org/ for all countries' code list.
I was able to solve this problem by using the following code
const getRegionCodeForNumber = (number, defaultCountryCode = 'US') => {
const phoneUtil = i18n.phonenumbers.PhoneNumberUtil.getInstance()
const numberObj = phoneUtil.parseAndKeepRawInput(number, defaultCountryCode)
return phoneUtil.getRegionCodeForNumber(numberObj)
}
Calling it like this:
console.log(getRegionCodeForNumber('7358469469', 'IN'))
gives IN.
However #shaochuancs is right, to get something meaningful you'll need to pass the number in the international format starting with +, e.g.:
console.log(getRegionCodeForNumber('+86-7358469469', 'IN'))
gives CN.
The second parameter of getRegionCodeForNumber is default country code which the function will fall back to when the international country code with + is omitted.
I need to extract a valid date from a list of random strings. The date can be present in any date format("01/25/16", "25/01/2016", "20-01-2016", "3-Nov-2016" etc) with different kind of separators.
I tried the using Date.parse() and new Date() but these method also return a valid value for any number passed which ideally is not a date.
For Ex: Date.parse("1") = 978336000000
My current solution is to check each string with the following regex
if(!string.match(/^\d+$|[a-zA-Z]+\s*[a-zA-Z0-9]*/) && (string.length > 7)) {
const date = Date.parse(string)
return (!isNaN(date))
}
This regex works to identify date strings like "01/25/16", "25/01/2016", "20-01-2016"
This regex matches most of the regular text like "100", "hello", "123hello", "1h ello12" and lets in values like "123-123", "01/25/16" and Date.parse() identifies pretty good.
But this misses the date string like "23-Nov-2016" so I added one more regex along with previous one
if(((!string.match(/^\d+$|[a-zA-Z]+\s*[a-zA-Z0-9]*/) && (string.length > 7)) || ((string.match(/^\d+$|[a-zA-Z]+\s*[a-zA-Z0-9]*/) && string.toLowerCase.match(/jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec/))) {
const date = Date.parse(string)
return (!isNaN(date))
}
I definitely believe that there exists a much simpler solution than using this large sets of regex in javascript.
EDIT : I don't control the date input rules to specifically validate certain formats.
Unfortunately, I don't think there is a better solution than using a set of regular expressions.
The problem is that there are at least a million different ways to write the same date. It seems like no matter what date formats you have planned for, your users will always come up with something that doesn't fit. So I approached this in the following way for a project I'm working on:
Make a list of acceptable date formats.
Tell the users not to use different formats and enforce it via client-side validation.
In my case, I'm living in the US, and dates are usually written like 'M/D/YY'. To allow for a reasonable range of variation, I wrote my code to accept M/D/YY, M/D/YYYY, and M/D (where the current year is substituted if the year is omitted). These formats are recognized using regular expressions then parsed using the Moment.js library.
You may want to expand the list of permitted formats if your users habitually use them - that's fine. But the important thing is to realize that you can't plan for all possible formats - there are just too many variations.
If you can meet your users' expectations 90% of the time (with the most common formats) and train your users that these are the accepted formats, you'll have happy users and date parsing code that's not 10,000 lines long.
For some time, I've been using something like this to get my user's country (ISO-3166):
const region = navigator.language.split('-')[1]; // 'US'
I've always assumed the string would be similar to en-US -- where the country would hold the 2nd position of the array.
I am thinking this assumption is incorrect. According to MDN docs, navigator.language returns: "string representing the language version as defined in BCP 47." Reading BCP 47, the primary language subtag is guaranteed to be first (e.g., 'en') but the region code is not guaranteed to be the 2nd subtag. There can be subtags that preceed and follow the region subtag.
For example "sr-Latn-RS" is a valid BCP 47 language tag:
sr | Latn | RS
primary language | script subtag | region subtag
Is the value returned from navigator.language a subset of BCP 47 containing only language and region? Or is there a library or regex that is commonly used to extract the region subtag from a language tag?
Your solution is based on the false premise that the browser's language tag reliably matches the user's country. E.g., I have set my browser language to German, even though I am living nowhere near Germany at the moment, but rather in the United States.
Also, for example in Chrome, many language packs do not require you to specify the region modifier. Setting Chrome's display language to German
provides the following language tag:
> navigator.language
< "de"
No region tag at all, and a fairly common language.
Bottom line is, my browser setup results in language tag de, even though I live in the United States.
A more accurate and possibly reliable way to determine the user's location would be to derive it from the IP address associated with the request. There are numerous services that offer this service. ip-api.com is one of them:
$.get("http://ip-api.com/json", function(response) {
console.log(response.country); // "United States"
console.log(response.countryCode); // "US"
}, "jsonp");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
You can now extract the region from a locale identifier using the Locale object in the Internationalization API.
const { region } = new Intl.Locale('sr-Latn-RS') // region => 'RS'
Note that this is not currently compatible with Internet Explorer.
Regex found here: https://github.com/gagle/node-bcp47/blob/master/lib/index.js
var re = /^(?:(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)|(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang))$|^((?:[a-z]{2,3}(?:(?:-[a-z]{3}){1,3})?)|[a-z]{4}|[a-z]{5,8})(?:-([a-z]{4}))?(?:-([a-z]{2}|\d{3}))?((?:-(?:[\da-z]{5,8}|\d[\da-z]{3}))*)?((?:-[\da-wy-z](?:-[\da-z]{2,8})+)*)?(-x(?:-[\da-z]{1,8})+)?$|^(x(?:-[\da-z]{1,8})+)$/i;
let foo = re.exec('de-AT'); // German in Austria
let bar = re.exec('zh-Hans-CN'); // Simplified Chinese using Simplified script in mainland China
console.log(`region ${foo[5]}`); // 'region AT'
console.log(`region ${bar[5]}`); // 'region CN'
In Firefox, you can choose your language settings in preferences:
The list of languages has 269 items, 192 of which do not include any region code.
The region is only useful when a language has different variants depending on the location. This way users can tell the server in which language variant they prefer the response to be.
Do not use this approach to locate the user. It's too unreliable, because the user may not specify any region, or because the user could physically be in another place.
If you want to locate the user, you should use the Geolocation API.
Be careful you have navigator.language and navigator.languages.
langage :
console.log(navigator.language); // "fr"
langages :
console.log(navigator.languages); // ["fr", "fr-FR", "en-US", "en"]
To find countries see Wikipedia on ISO 3166-1 or use javascript lib :
i18n-iso-countries
country.js
ISO Country List - HTML select/dropdown snippet
Just as #TimoSta said,
Try this
$.getJSON('http://freegeoip.net/json/', function(result) {
alert(result.country_code);
});
from Get visitors language & country code with javascript (client-side). See answer of #noducks
The value you are receiving stems from the Accept-Language header of the HTTP request.
The values of the header can be quite complex like
Accept-Language: da, en-GB;q=0.8, en;q=0.7
As the name implies, the Accept-Language header basically defines acceptable languages, not countries.
A language tag may contain also additional location information, as in 'en-GB' but others like 'en' do not.
In case not, there is just no information about the country.
It is also not always possible to exactly map a language like 'en' to a country.
If the language is 'en', the country might be 'GB' but it may also be 'US'.
What you can do ;
Determine the country only, if the language contains one, as in 'en-GB'
If the language does not contain a country you have the following options :
A few languages are only used in one country, like 'da', danish which is spoken only in Denmark (I am guessing here), so you may map these cases.
You may use a default for other cases, depending on the language, e.g. map 'en' to 'GB'
You may use a general default like 'US' for all cases no country can be determined.
You can use additional information e.g. the clients IP address to determine the country
Finally you may ask the user to enter the country
I collected some additional information about the Accept-Language header here
So I know how to format a string or integer like 2000 to 2K, but how do I reverse it?
I want to do something like:
var string = "$2K".replace("/* K with 000 and remove $ symbol in front of 2 */");
How do I start? I am not very good regular expressions, but I have been taking some more time out to learn them. If you can help, I certainly appreciate it. Is it possible to do the same thing for M for millions (adding 000000 at the end) or B for billions (adding 000000000 at the end)?
var string = "$2K".replace(/\$(\d+)K/, "$1000");
will give output as
2000
I'm going to take a different approach to this, as the best way to do this is to change your app to not lose the original numeric information. I recognize that this isn't always possible (for example, if you're scraping formatted values...), but it could be useful way to think about it for other users with similar question.
Instead of just storing the numeric values or the display values (and then trying to convert back to the numeric values later on), try to update your app to store both in the same object:
var value = {numeric: 2000, display: '2K'}
console.log(value.numeric); // 2000
console.log(value.display); // 2K
The example here is a bit simplified, but if you pass around your values like this, you don't need to convert back in the first place. It also allows you to have your formatted values change based on locale, currency, or rounding, and you don't lose the precision of your original values.