JAVASCRIPTshopifyadvanced
Shopify Multi-Currency Converter
Add multi-currency support with automatic conversion and detection
Faisal Yaqoob
November 19, 2025
#shopify#currency#multi-currency#conversion#internationalization
Code
javascript
1 // Currency Converter Class 2 class CurrencyConverter { 3 constructor(options = {}) { 4 this.shopCurrency = options.shopCurrency || 'USD'; 5 this.currentCurrency = this.getCurrentCurrency(); 6 this.rates = {}; 7 this.selectors = { 8 money: '[data-currency-price]', 9 switcher: '[data-currency-switcher]', 10 }; 11
12 this.init(); 13 } 14
15 async init() { 16 // Load exchange rates 17 await this.loadExchangeRates(); 18
19 // Create currency switcher 20 this.createCurrencySwitcher(); 21
22 // Convert all prices on page 23 this.convertAllPrices(); 24
25 // Watch for dynamically added prices 26 this.observePrices(); 27 } 28
29 getCurrentCurrency() { 30 // Check localStorage 31 const saved = localStorage.getItem('selectedCurrency'); 32 if (saved) return saved; 33
34 // Auto-detect from user location (requires API) 35 // For now, return shop currency 36 return this.shopCurrency; 37 } 38
39 async loadExchangeRates() { 40 try { 41 // Option 1: Use a free exchange rate API 42 const response = await fetch('https://api.exchangerate-api.com/v4/latest/' + this.shopCurrency); 43 const data = await response.json(); 44 this.rates = data.rates; 45
46 // Option 2: Use Shopify's built-in currency files (if available) 47 // const response = await fetch('/services/javascripts/currencies'); 48 // Parse and use Shopify's rates 49
50 } catch (error) { 51 console.error('Failed to load exchange rates:', error); 52 // Fallback rates 53 this.rates = { 54 'USD': 1.00, 55 'EUR': 0.85, 56 'GBP': 0.73, 57 'CAD': 1.25, 58 'AUD': 1.35, 59 }; 60 } 61 } 62
63 createCurrencySwitcher() { 64 const switchers = document.querySelectorAll(this.selectors.switcher); 65
66 switchers.forEach(switcher => { 67 const currencies = Object.keys(this.rates); 68
69 const select = document.createElement('select'); 70 select.className = 'currency-select'; 71
72 currencies.forEach(code => { 73 const option = document.createElement('option'); 74 option.value = code; 75 option.textContent = this.getCurrencySymbol(code) + ' ' + code; 76
77 if (code === this.currentCurrency) { 78 option.selected = true; 79 } 80
81 select.appendChild(option); 82 }); 83
84 select.addEventListener('change', (e) => { 85 this.changeCurrency(e.target.value); 86 }); 87
88 switcher.appendChild(select); 89 }); 90 } 91
92 changeCurrency(newCurrency) { 93 this.currentCurrency = newCurrency; 94 localStorage.setItem('selectedCurrency', newCurrency); 95
96 this.convertAllPrices(); 97
98 // Trigger custom event 99 document.dispatchEvent(new CustomEvent('currency:changed', { 100 detail: { currency: newCurrency } 101 })); 102 } 103
104 convertAllPrices() { 105 const priceElements = document.querySelectorAll(this.selectors.money); 106
107 priceElements.forEach(el => { 108 this.convertPrice(el); 109 }); 110 } 111
112 convertPrice(element) { 113 const basePrice = parseFloat(element.dataset.currencyPrice); 114
115 if (isNaN(basePrice)) return; 116
117 const rate = this.rates[this.currentCurrency] || 1; 118 const convertedPrice = basePrice * rate; 119
120 element.textContent = this.formatMoney(convertedPrice, this.currentCurrency); 121 } 122
123 formatMoney(amount, currency) { 124 const symbol = this.getCurrencySymbol(currency); 125
126 return symbol + amount.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ','); 127 } 128
129 getCurrencySymbol(currency) { 130 const symbols = { 131 'USD': '$', 132 'EUR': '€', 133 'GBP': '£', 134 'JPY': '¥', 135 'CAD': 'C$', 136 'AUD': 'A$', 137 'CHF': 'Fr', 138 'CNY': '¥', 139 'SEK': 'kr', 140 'NZD': 'NZ$', 141 }; 142
143 return symbols[currency] || currency + ' '; 144 } 145
146 observePrices() { 147 // Watch for new price elements added to DOM 148 const observer = new MutationObserver((mutations) => { 149 mutations.forEach((mutation) => { 150 mutation.addedNodes.forEach((node) => { 151 if (node.nodeType === 1) { 152 const prices = node.querySelectorAll?.(this.selectors.money); 153 prices?.forEach(el => this.convertPrice(el)); 154 } 155 }); 156 }); 157 }); 158
159 observer.observe(document.body, { 160 childList: true, 161 subtree: true 162 }); 163 } 164
165 // Get user's country for auto-detection 166 async detectUserCountry() { 167 try { 168 const response = await fetch('https://ipapi.co/json/'); 169 const data = await response.json(); 170 return data.country_code; 171 } catch (error) { 172 console.error('Failed to detect country:', error); 173 return null; 174 } 175 } 176
177 // Map country to currency 178 getCurrencyByCountry(countryCode) { 179 const mapping = { 180 'US': 'USD', 181 'GB': 'GBP', 182 'CA': 'CAD', 183 'AU': 'AUD', 184 'DE': 'EUR', 185 'FR': 'EUR', 186 'IT': 'EUR', 187 'ES': 'EUR', 188 'JP': 'JPY', 189 // Add more mappings 190 }; 191
192 return mapping[countryCode] || this.shopCurrency; 193 } 194 } 195
196 // Initialize 197 document.addEventListener('DOMContentLoaded', () => { 198 window.currencyConverter = new CurrencyConverter({ 199 shopCurrency: 'USD' 200 }); 201 });
Shopify Multi-Currency Converter
Implement multi-currency support with automatic currency detection, conversion, and a currency switcher dropdown.
// Currency Converter Class
class CurrencyConverter {
constructor(options = {}) {
this.shopCurrency = options.shopCurrency || 'USD';
this.currentCurrency = this.getCurrentCurrency();
this.rates = {};
this.selectors = {
money: '[data-currency-price]',
switcher: '[data-currency-switcher]',
};
this.init();
}
async init() {
// Load exchange rates
await this.loadExchangeRates();
// Create currency switcher
this.createCurrencySwitcher();
// Convert all prices on page
this.convertAllPrices();
// Watch for dynamically added prices
this.observePrices();
}
getCurrentCurrency() {
// Check localStorage
const saved = localStorage.getItem('selectedCurrency');
if (saved) return saved;
// Auto-detect from user location (requires API)
// For now, return shop currency
return this.shopCurrency;
}
async loadExchangeRates() {
try {
// Option 1: Use a free exchange rate API
const response = await fetch('https://api.exchangerate-api.com/v4/latest/' + this.shopCurrency);
const data = await response.json();
this.rates = data.rates;
// Option 2: Use Shopify's built-in currency files (if available)
// const response = await fetch('/services/javascripts/currencies');
// Parse and use Shopify's rates
} catch (error) {
console.error('Failed to load exchange rates:', error);
// Fallback rates
this.rates = {
'USD': 1.00,
'EUR': 0.85,
'GBP': 0.73,
'CAD': 1.25,
'AUD': 1.35,
};
}
}
createCurrencySwitcher() {
const switchers = document.querySelectorAll(this.selectors.switcher);
switchers.forEach(switcher => {
const currencies = Object.keys(this.rates);
const select = document.createElement('select');
select.className = 'currency-select';
currencies.forEach(code => {
const option = document.createElement('option');
option.value = code;
option.textContent = this.getCurrencySymbol(code) + ' ' + code;
if (code === this.currentCurrency) {
option.selected = true;
}
select.appendChild(option);
});
select.addEventListener('change', (e) => {
this.changeCurrency(e.target.value);
});
switcher.appendChild(select);
});
}
changeCurrency(newCurrency) {
this.currentCurrency = newCurrency;
localStorage.setItem('selectedCurrency', newCurrency);
this.convertAllPrices();
// Trigger custom event
document.dispatchEvent(new CustomEvent('currency:changed', {
detail: { currency: newCurrency }
}));
}
convertAllPrices() {
const priceElements = document.querySelectorAll(this.selectors.money);
priceElements.forEach(el => {
this.convertPrice(el);
});
}
convertPrice(element) {
const basePrice = parseFloat(element.dataset.currencyPrice);
if (isNaN(basePrice)) return;
const rate = this.rates[this.currentCurrency] || 1;
const convertedPrice = basePrice * rate;
element.textContent = this.formatMoney(convertedPrice, this.currentCurrency);
}
formatMoney(amount, currency) {
const symbol = this.getCurrencySymbol(currency);
return symbol + amount.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}
getCurrencySymbol(currency) {
const symbols = {
'USD': '$',
'EUR': '€',
'GBP': '£',
'JPY': '¥',
'CAD': 'C$',
'AUD': 'A$',
'CHF': 'Fr',
'CNY': '¥',
'SEK': 'kr',
'NZD': 'NZ$',
};
return symbols[currency] || currency + ' ';
}
observePrices() {
// Watch for new price elements added to DOM
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === 1) {
const prices = node.querySelectorAll?.(this.selectors.money);
prices?.forEach(el => this.convertPrice(el));
}
});
});
});
observer.observe(document.body, {
childList: true,
subtree: true
});
}
// Get user's country for auto-detection
async detectUserCountry() {
try {
const response = await fetch('https://ipapi.co/json/');
const data = await response.json();
return data.country_code;
} catch (error) {
console.error('Failed to detect country:', error);
return null;
}
}
// Map country to currency
getCurrencyByCountry(countryCode) {
const mapping = {
'US': 'USD',
'GB': 'GBP',
'CA': 'CAD',
'AU': 'AUD',
'DE': 'EUR',
'FR': 'EUR',
'IT': 'EUR',
'ES': 'EUR',
'JP': 'JPY',
// Add more mappings
};
return mapping[countryCode] || this.shopCurrency;
}
}
// Initialize
document.addEventListener('DOMContentLoaded', () => {
window.currencyConverter = new CurrencyConverter({
shopCurrency: 'USD'
});
});
Liquid Template Integration
<!-- Product Price -->
<div class="product-price">
<span data-currency-price="{{ product.price | divided_by: 100.0 }}">
{{ product.price | money }}
</span>
</div>
<!-- Compare at Price -->
{% if product.compare_at_price > product.price %}
<span class="compare-price" data-currency-price="{{ product.compare_at_price | divided_by: 100.0 }}">
{{ product.compare_at_price | money }}
</span>
{% endif %}
<!-- Currency Switcher -->
<div class="currency-switcher">
<label>Currency:</label>
<div data-currency-switcher></div>
</div>
<!-- Cart Total -->
<div class="cart-total">
<span>Total:</span>
<span data-currency-price="{{ cart.total_price | divided_by: 100.0 }}">
{{ cart.total_price | money }}
</span>
</div>
CSS Styling
.currency-switcher {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 0;
}
.currency-select {
padding: 8px 30px 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
background: #fff;
cursor: pointer;
font-size: 14px;
appearance: none;
background-image: url('data:image/svg+xml;utf8,<svg fill="%23333" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M7 10l5 5 5-5z"/></svg>');
background-repeat: no-repeat;
background-position: right 8px center;
background-size: 16px;
}
.currency-select:hover {
border-color: #999;
}
.currency-select:focus {
outline: none;
border-color: #0066cc;
}
[data-currency-price] {
font-weight: bold;
}
Shopify Markets Integration
// For stores using Shopify Markets
class ShopifyMarketsConverter extends CurrencyConverter {
async loadExchangeRates() {
// Use Shopify's native multi-currency
if (window.Shopify && window.Shopify.currency) {
this.rates = window.Shopify.currency.rates || {};
} else {
await super.loadExchangeRates();
}
}
changeCurrency(newCurrency) {
// Update Shopify's currency
if (window.Shopify && window.Shopify.currency) {
window.Shopify.currency.active = newCurrency;
}
super.changeCurrency(newCurrency);
// Reload cart with new currency
this.updateCartCurrency(newCurrency);
}
async updateCartCurrency(currency) {
try {
await fetch('/cart/update.js', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
attributes: {
currency: currency
}
})
});
} catch (error) {
console.error('Failed to update cart currency:', error);
}
}
}
Auto-Detection on First Visit
// Auto-detect currency on first visit
document.addEventListener('DOMContentLoaded', async () => {
const converter = new CurrencyConverter({
shopCurrency: 'USD'
});
// If no saved currency, auto-detect
if (!localStorage.getItem('selectedCurrency')) {
const country = await converter.detectUserCountry();
if (country) {
const currency = converter.getCurrencyByCountry(country);
converter.changeCurrency(currency);
}
}
});
Features
- Real-Time Conversion: Live exchange rates from API
- Auto-Detection: Detect user's location and currency
- Persistent Selection: Saves currency choice in localStorage
- Dynamic Updates: Converts newly loaded prices automatically
- Shopify Markets: Compatible with Shopify's native solution
- Custom Formatting: Proper currency symbols and decimals
- Event System: Triggers events for integration
- Fallback Rates: Works even if API fails
Related Snippets
Shopify Free Shipping Progress Bar
Show customers how much more they need to spend for free shipping
JAVASCRIPTshopifybeginner
javascriptPreview
// Free Shipping Bar Class
class FreeShippingBar {
constructor(options = {}) {
this.threshold = options.threshold || 5000; // Amount in cents
...#shopify#shipping#cart+2
11/25/2025
View
Shopify Sticky Add to Cart Bar
Keep the add to cart button visible as users scroll down product pages
JAVASCRIPTshopifybeginner
javascriptPreview
// Sticky Add to Cart Class
class StickyAddToCart {
constructor(options = {}) {
this.mainButton = document.querySelector(options.mainButton || '[data-main-add-to-cart]');
...#shopify#sticky#add-to-cart+2
11/22/2025
View
Shopify Wishlist with LocalStorage
Add wishlist/favorites functionality without requiring a Shopify app
JAVASCRIPTshopifyintermediate
javascriptPreview
// Wishlist Class
class Wishlist {
constructor(options = {}) {
this.storageKey = options.storageKey || 'shopify_wishlist';
...#shopify#wishlist#favorites+1
11/28/2025
View