0

From the MDN Documentation, I can format a currency value with

var locale = 'fr-CA';
var number = 123456.789;
var currency = 'EUR';

console.log(new Intl.NumberFormat(locale, { style: 'currency', currency: currency }).format(number));
// expected output: "123.456,79 €"

But is it possible to list all available currencies, localized? The point of this question is to build a localized currency selector, without requiring to fetch data from a third party URL or module.

For example :

const localeEl = document.getElementById('locales');
const selectEl = document.getElementById('currencies');
const inputEl = document.getElementById('number');
const outputEl = document.getElementById('output');

// locales already provided from app
const locales = ['fr-CA', 'en-US', 'de-DE'];

// REQUIREMENT: retrieve all known currencies here
const currencies = ['CAD', 'USD', 'EUR'];

locales.forEach(locale => {
   const optionEl = document.createElement('option');
   optionEl.value = locale;
   
   // OPTIONAL retrieve locale name (localized)
   optionEl.text = locale;
   
   localeEl.appendChild(optionEl);
});

currencies.forEach(currency => {
   const optionEl = document.createElement('option');
   optionEl.value = currency;
   
   // OPTIONAL retrieve localized currency name here
   optionEl.text = currency;
   
   selectEl.appendChild(optionEl);
});

const updateOutput = () => {
  const locale = localeEl.children[localeEl.selectedIndex].value;
  const currency = selectEl.children[selectEl.selectedIndex].value;
  const number = inputEl.value;

  outputEl.innerHTML = new Intl.NumberFormat(locale, { style: 'currency', currency: currency }).format(number);
}

localeEl.addEventListener('change', updateOutput);
selectEl.addEventListener('change', updateOutput);
inputEl.addEventListener('change', updateOutput);
<select id="locales"></select>
<select id="currencies"></select>
<input type="number" id="number" />
<div id="output"></div>
Yanick Rochon
  • 51,409
  • 25
  • 133
  • 214
  • https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat Not sure why you couldn't just include them all and reference based off of how you're doing above. I don't think I understand what you mean when you say localized. Are you referring to local to the current geo-location, or local like embedding them all in the javascript code? – Jason Owens Dec 04 '19 at 20:42
  • Localized as in French, the USD currency is `$US` while in English it is simply `$`. Similarly, `CAD` in French is `$CA`, and `CA$` in English. – Yanick Rochon Dec 04 '19 at 20:58

1 Answers1

0

It is indeed possible to list all currencies supported by a user's browser without having to fetch data from a 3rd party resource.

However, currently, the solution is not too elegant or efficient:

function getSupportedCurrencies() {
  function $(amount, currency) {
    let locale = 'en-US';
    let options = {
      style: 'currency',
      currency: currency,
      currencyDisplay: "name"
    };
    return Intl.NumberFormat(locale, options).format(amount);
  }
  const getAllPossibleThreeLetterWords = () => {
    const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    const arr = [];
    let text = '';
    for (let i = 0; i < chars.length; i++) {
      for (let x = 0; x < chars.length; x++) {
        for (let j = 0; j < chars.length; j++) {
          text += chars[i];
          text += chars[x];
          text += chars[j];
          arr.push(text);
          text = '';
        }
      }
    }
    return arr;
  };
  let ary = getAllPossibleThreeLetterWords();
  let currencies = [];
  const rx = /(?<= ).+/; // This line doesn't work in Firefox versions older than version 78 due to bug 1225665: https://bugzilla.mozilla.org/show_bug.cgi?id=1225665
  ary.forEach((cur) => {
    let output = $(0, cur).trim();
    if (output.replace(/^[^ ]+ /, '') !== cur) {
      let obj = {};
      obj.code = cur;
      obj.name = output.match(rx)[0];
      currencies.push(obj);
    }
  });
  return currencies;
}
console.log(getSupportedCurrencies());

Based on my proof of concept above, Mr. Polywhirl created a memory optimized version that uses a product iterable instead of loading all those 3 letter guesses into an array upfront.

It is very disappointing that the spec doesn't provide a method to accomplish this in a more direct (non brute force guessing) way.

Ideally, the spec should evolve to standardize this type of environmental reflection. The specification should provide a getSupportedCurrencies function as part of the standard API, so that programmers don't have to resort to inefficient Guess Based Solutions.

Lonnie Best
  • 9,936
  • 10
  • 57
  • 97
  • The issue with this is that the user **must** install the locales in order to view the currencies, so a user using the app would see the Yen currency (because it has the locale), but another user would not (because they do not have the locale). That's not a preferable solution. :/ – Yanick Rochon Jul 06 '20 at 17:47