The Internet is global and buyers are located all around the world. As such, selling products online comes with challenges. According to Statista, the online shopping cart abandonment rate worldwide for 2019 is around 70%.
To reduce this rate, a checkout experience optimized to fit customer expectations and preferences in terms of pricing, language and currency requires a company to adapt their systems for a global economy. Although this could be an intimidating task, it is relatively simple with services like Ipregistry and Open Exchange Rates.
In this article, you will learn how to convert product prices to your visitor's local currency, in real-time, using the Ipregistry and Open Exchange Rates APIs. Here is a demo of what you will get:
https://ipregistry.co/tutorials/currency-localization/
Getting Started
The first step is to get the API keys that are required to invoke the APIs from Ipregistry and Open Exchange Rates. If you don't already have an Ipregistry account, sign up for a free account. Then, create an account at Open Exchange Rates. Although their pricing page does not prominently display their free plan, don't fear, there is one and it's free forever!
Now that you are ready, we will create a simple Web page that uses vanilla JavaScript to display a list of products and their respective prices in the visitor's currency. The only library you'll use in this project is the money.js library for converting product prices to the visitor's local currency.
The overall process is quite simple and involves only 4 steps:
- Fetching product data (for the sake of the tutorial we will use a static JSON file).
- Retrieving your visitor's location data using the Ipregistry API.
- Getting the currency exchange information from Open Exchange Rates.
- Displaying products with the converted currency values.
Building the Product Page
Let's say you have the following items for sale in your e-commerce pages:
[
{ "sku": "TRE-9231", "name" : "On Cloud Nine Pillow", "price" : 24.99 },
{ "sku": "BEH-5591", "name" : "Simply Sweet Blouse", "price" : 59.95 },
{ "sku": "WKS-7731", "name" : "Uptown Girl Blouse", "price" : 89.95 }
]
First, create a new file called items.json
, and copy the JSON above into the file. Then, create an HTML file named index.html
with the following content:
<html lang="en">
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Online Store</title>
</head>
<body>
<table id="products">
<thead>
<tr>
<th>SKU</th>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody></tbody>
</table>
<script src="https://openexchangerates.github.io/money.js/money.min.js"></script>
<script>
// Here is where to put the JavaScript code we will write in the next sections
</script>
</body>
</html>
This file acts as the entry point. As you can see, the only dependency on this page is the Open Exchange Rates' money.js library.
Fetching Data
Now it's time to fetch all data we need. Although, we could use the official JavaScript client libraries for Ipregistry and Open Exchange Rates, here we prefer to minimize the number of dependencies and perform direct HTTP requests using the standard fetch
interface.
Also, for the sake of this tutorial, we are using async/await instead of promises when invoking fetch
. This should not be an issue since all modern browsers support async/await. However, if your aim is to target old browsers, we suggest adding a build step and/or to include polyfills. The advantage of using async/await is that it saves you from the Promise callback hell that is associated with multiple asynchronous operations.
Let's first define a utility function to retrieve the raw JSON returned by an HTTP GET request using fetch
:
async function fetchJSON(url) {
const response = await fetch(url);
return await response.json();
}
As you can see, the Fetch API is a huge improvement over XMLHttpRequest. In two lines of code, the fetchJSON
performs an HTTP GET request to the provided URL and uses the response object's json
method to transform the JSON structure into a usable JavaScript object.
Next, get all required data with the following three lines:
const exchange = await fetchJSON('https://openexchangerates.org/api/latest.json?app_id=<YOUR_API_KEY>');
const ipinfo = await fetchJSON('https://api.ipregistry.co?key=<YOUR_API_KEY>&fields=currency');
const products = await fetchJSON('items.json');
These three lines fetch respectively fresh currency exchange information, the visitor's currency, and product data. This code is straight-forward, but let's focus on the Ipregistry request.
Firstly, Ipregistry’s API returns location-based information about the device that issued the request. In this case, that is the user's browser, and that is exactly what we want. Remember, you are converting product prices to the user's currency based upon their current location, and you automatically get that by issuing a request with no user-specific fields.
Secondly, you don't need to know everything about the user's location. The only thing you really care about is the currency information. Therefore, you can use the fields
query parameter to tell the Ipregistry API that you only want the currency object in the returned JSON structure. This saves bandwidth and resources.
Optimizing retrieval
If you have some experience with await
you should have noticed that our 3 calls to retrieve required data are sequential and each call is waiting for its result before performing the next one. What if we were performing requests in parallel? This way we could save time and enhance user experience.
Using async/await
that's really simple. You dispatch requests with the fetchJSON
utility function. Each call returns a promise that you save in a variable. Once done, you only have to await for all promises:
const exchangePromise = fetchJSON('https://openexchangerates.org/api/latest.json?app_id=<YOUR_API_KEY>');
const ipinfoPromise = fetchJSON('https://api.ipregistry.co?key=<YOUR_API_KEY>&fields=currency');
const productsPromise = fetchJSON('items.json');
const exchange = await exchangePromise;
const ipinfo = await ipinfoPromise;
const products = await productsPromise;
Displaying the Products
Now that we have the data, all we need is to use them appropriately to produce the expected result.
Let's get a reference to the <tbody>
element in the table and start iterating over the products. The goal here is to use the tbody object to append a new row for each iteration through the products array. While that sounds like a lot of tedious DOM code, it's actually quite simple. The API for working with tables and their related objects are very intuitive:
const tbody = document.getElementById('products').tBodies[0];
products.forEach(function(product) {
const row = tbody.insertRow();
const skuCell = row.insertCell();
const nameCell = row.insertCell();
const priceCell = row.insertCell();
skuCell.innerHTML = product.sku;
nameCell.innerHTML = product.name;
// price cell here
});
In this code, a new row is created using insertRow
. This automatically creates the resulting <tr>
element and appends it to the <tbody>
element. Using the newly created row object, you can then create the cells for that row using the insertCell
method. This method automatically creates the necessary <td>
element and appends it to the row.
The final piece of this puzzle is about localizing the price. The money.js library provides multiple manners to convert values from one currency to another. First, you need to initialize its base and rates information. It just takes two lines of code using data from the Open Exchange Rates API:
fx.base = exchange.base;
fx.rates = exchange.rates;
Then, we can use the fluent approach as shown below:
fx(price).from('FROM_CURRENCY_CODE').to('TO_CURRENCY_CODE');
For this tutorial, we will say that FROM_CURRENCY_CODE
is USD
(for American Dollars), but feel free to use whatever currency you want. The TO_CURRENCY_CODE
is the one detected (via Ipregistry) for the user who views the page. To plug in your values, the code will look like the following:
fx(product.price).from('USD').to(location.currency.code).toFixed(2);
Conversions rarely result in clean numbers. There is a good chance the resulting price will have more than two decimal places. Therefore, we use toFixed(...)
to constrain the result to two decimal places.
Converting the price is only part of the solution. You also need to include the correct currency symbol. Thankfully, you already have that information from the Ipregistry response. Just use the ipinfo.currency.symbol
property and concatenate it with the conversion result, like this:
priceCell.innerHTML = location.currency.symbol + '' +
fx(product.price).from('USD').to(ipinfo.currency.code).toFixed(2);
Formatting the price
The result is not yet perfect. When we display the price, we always append the currency symbol before the product price but this is not correct. Based on the user locale and the amount (positive or negative), the symbol must be placed differently. For instance, if a US user is browsing the page, the price must be in USD with the $ symbol put right before the price. However, a French user should see the price in Euros with the € placed after the price and separated with a space.
You wonder how to manage this? No need for yet another library, the Ipregistry response already includes all the information you need to format prices appropriately. Here is the function we will use:
function localizeAndFormatPrice(product, ipinfo) {
const localizedPrice = fx(product.price).from('USD').to(ipinfo.currency.code);
const currentFormat = localizedPrice > 0 ? ipinfo.currency.format.positive : ipinfo.currency.format.negative;
return currentFormat.prefix + localizedPrice.toFixed(2) + currentFormat.suffix;
}
At the end you should get a code that looks like the following:
(async function() {
async function fetchJSON(url) {
const response = await fetch(url);
return await response.json();
}
function localizeAndFormatPrice(product, ipinfo) {
const localizedPrice = fx(product.price).from('USD').to(ipinfo.currency.code);
const currentFormat = localizedPrice > 0 ? ipinfo.currency.format.positive : ipinfo.currency.format.negative;
return currentFormat.prefix + localizedPrice.toFixed(2) + currentFormat.suffix;
}
const exchangePromise = fetchJSON('https://openexchangerates.org/api/latest.json?app_id=<YOUR_API_KEY>');
const ipinfoPromise = fetchJSON('https://api.ipregistry.co?key=<YOUR_API_KEY>&fields=currency');
const productsPromise = fetchJSON('items.json');
const exchange = await exchangePromise;
const ipinfo = await ipinfoPromise;
const products = await productsPromise;
fx.base = exchange.base;
fx.rates = exchange.rates;
const tbody = document.getElementById('products').tBodies[0];
products.forEach(function(product) {
const row = tbody.insertRow();
const skuCell = row.insertCell();
const nameCell = row.insertCell();
const priceCell = row.insertCell();
skuCell.innerHTML = product.sku;
nameCell.innerHTML = product.name;
priceCell.innerHTML = localizeAndFormatPrice(product, ipinfo);
});
})();
Add the script in the HTML file created at the beginning of the tutorial and save the file. Then, deploy index.html
and items.json
to your server. Once done, open the HTML page. You should see the products displayed with the prices converted to your currency.
If you want to test the sample locally, you need an HTTP server since fetch
can only resolve files using HTTP. Any HTTP server will do. For instance, lite-server can get you up and running with no configuration. Another quick solution that works on most Unix based machine is to use Python and its embedded server. Open a shell in the folder that contains your files and run the next command:
python -m http.server
Personalizing Experiences
Converting your price in different global markets is a critical component for avoiding cart abandonment and ensuring your ecommerce company's international success. Location data help to solve this use case but also to enrich and personalize your user's experiences in different manners. Converting product and service prices in real-time is just one way location data can help you build meaningful experiences.
No matter what kind of site you are building, use location data to help your users feel at home. As a reward, they'll more than likely come back.