WordPress plugin intelligence profile

MHM Currency Switcher

Multi-currency support for WooCommerce. Let your customers browse, shop, and checkout in their preferred currency with real-time exchange rates.

WP PlugIndex snapshot

Current directory snapshot

Tested up to WordPress 7.1
Current version2.2.1
WordPress coverageTested up to WordPress 7.1
Minimum stackWP 6.6 · PHP 7.4
Directory statusactive
Last directory update19 Sept 20261mo ago
Active installations10Adoption metric — not a quality score
WordPress.org rating5.0 / 5 from 2 ratings
Official packageDirect from WordPress.org

Repository metrics are context, not quality scores. Active installations measure adoption and can strongly reflect a plugin’s age and historical popularity. Ratings, recent updates and “Tested up to” metadata also do not guarantee modern code, error-free operation, security, feature completeness or suitability for a particular site. Why active installs don’t tell the whole story →

WP PlugIndex currently records MHM Currency Switcher version 2.2.1 as active, updated 1mo ago and tested up to WordPress 7.1.

Overview

About this plugin

MHM Currency Switcher adds multi-currency support to your WooCommerce store. Customers can browse products, add items to their cart, and complete checkout in their preferred currency with real-time exchange rates.

Key Features

  • Add multiple currencies with real-time exchange rates
  • Cache compatibility mode — works behind a page cache without serving one visitor’s currency to everybody
  • Currency switcher via shortcode — usable in a text widget, nav menu, or Elementor
  • Product page price display widget with country flags
  • Navigation menu integration — add switcher to any WordPress menu
  • Automatic exchange rate fetching (ExchangeRate-API)
  • Fee & rounding configuration per currency
  • Cookie-based currency persistence
  • WooCommerce HPOS compatible
  • Elementor widgets included
  • Every currency WooCommerce offers
  • Scheduled automatic exchange rate updates
  • Geolocation-based currency detection
  • Fixed prices per product
  • “How to use” tab in the settings screen, naming every way the switcher can be placed

Documentation and support

  • Documentation — setup, shortcodes, cache compatibility and FAQ, in English and Turkish
  • Support forum
  • MaxHandMade on YouTube — WordPress and WooCommerce videos from the author

Cache compatibility mode

A page cache stores the HTML your server produced for whoever asked first. When

prices are converted on the server, that means the first visitor’s currency is

what every later visitor is served. Cache compatibility mode, which is on by

default, avoids this: anonymous shop, archive and product pages are rendered in

your base currency, so the same cached page is correct for everyone, and the

browser converts the prices it can see afterwards through a REST request to this

plugin.

The split is deliberate. Only displayed prices are converted in the browser.

Cart, checkout, order totals, order emails and the WooCommerce REST API are

always calculated on the server in the currency the customer actually chose, so

the amount charged cannot be altered from the browser.

You can switch the mode off under WooCommerce > MHM Currency >

Advanced, in which case prices are converted on the server as they were before

this feature existed. Read “Known limits” below before deciding either way —

both settings have consequences, and they are different ones.

Known limits

These are consequences of how cache compatibility mode works, not defects. They

are listed so you can decide with your eyes open.

Turning the mode off does not restore the exact 1.0.0 behaviour

Three fixes sit above the setting and stay in place whether it is on or off:

prices are no longer converted on admin screens or in admin AJAX (which used to

write a converted price into an order line item), wc/v3 REST reads are pinned

to the base currency, and scheduled tasks and WP-CLI no longer convert. Switching

0 display behaviour — prices converted on the

server — and nothing else.

Search engines and crawlers see your base prices

With the mode on, the page a crawler fetches has not been through the browser,

so it carries base-currency prices, and so does the machine-readable product data

in it. See the structured data question above; the mismatch is deliberate.

Converted prices replace the base ones a moment after the page appears

The page arrives with your base-currency prices already on screen — nothing is

hidden waiting for JavaScript — and the browser swaps in the converted ones as

soon as its request comes back, each price fading over 200ms as it changes. On a

slow connection the base price is readable for longer before the swap. Visitors

who have asked their system for reduced motion get the swap without the fade.

With JavaScript disabled, or the endpoint unreachable, base prices stay

Nothing breaks and no error is shown to the visitor — the page simply keeps the

base-currency prices it was rendered with, and the reason is written to the

browser console. Cart and checkout are unaffected, because they never depended

on the browser in the first place.

Variable products make one extra request, and some swatch plugins lose the price

On a cacheable page the plugin forces WooCommerce to fetch variation prices over

AJAX, because the variations JSON WooCommerce would otherwise embed in the page

carries base-currency prices that its own scripts write straight into the page.

The cost is one request when a visitor picks a variation, and that

data-product_variations is false: third-party colour or size swatch plugins

that read prices out of that JSON instead of asking WooCommerce may stop showing

a price. If you use such a plugin, check a variable product before going live.

The mini-cart relies on WooCommerce cart fragments

A mini-cart is rendered on every page, so it cannot be classified per request.

It is rendered in the base currency and then corrected by WooCommerce’s own cart

fragment refresh, which is a server-side conversion. If cart fragments are

disabled on your site — some themes and optimisation plugins dequeue them — the

cached mini-cart total stays in the base currency while the rest of the page

converts. The plugin watches for this and says so in the admin when it happens;

a site with no mini-cart at all is never warned about it.

A cart or checkout on a page WooCommerce does not know about must be excluded from your cache

Page caches exclude cart and checkout automatically because they recognise the

pages WooCommerce assigned. If you have put a cart or checkout shortcode or block

on some other page, exclude that page yourself. Two things go wrong otherwise:

the conversion decision flips to “convert” partway through the render, so the

rest of the page is printed already converted and without the markers the browser

looks for, and blocks-based cart and checkout embed their amounts in the page as

JSON while rendering. Either way the first visitor’s currency is what the cache

then hands to everyone. Cart contents are personal anyway; such a page should not

be cached.

`?currency=` multiplies your cache entries

A currency can be requested in the URL, and a cache treats every distinct URL as

currency=GBP stores the

same page more than once. The switcher itself does not produce these URLs — it

writes a cookie and leaves the address alone. On a cached page it converts the

prices where they stand; on the cart page, for a logged-in visitor, or with

cache compatibility switched off, it reloads instead. Either way the URL is the

one the visitor was already on, so no extra cache entry is created.

currency= link also applies to that page view only: it deliberately sets no

cookie, so the next page the visitor opens is back in your base currency unless

they use the switcher. That is not an oversight — a link that silently pinned a

currency could show one currency in the catalogue while the cart, which reads

the cookie, charged another. If you want a campaign link that sticks, send

visitors to a page carrying the switcher rather than relying on the parameter.

WooCommerce Analytics adds different currencies together

An order is stored in the currency the customer paid in, and WooCommerce

Analytics reports every order’s figures in your store currency without

converting them back. 38 in your base

currency, so once you take orders in more than one currency the revenue

figures in Analytics, and the totals in the customer panel on the order

screen, are sums of unlike amounts. The orders themselves are correct — each

one keeps its own currency, total and the exchange rate it was placed at, and

this plugin stores that rate on the order. It is the aggregate reports that

cannot be read as money. Nothing in this plugin can fix that from the outside;

if you need accurate multi-currency reporting, export the orders and convert

them using the rate recorded on each one.

Logged-in visitors are converted on the server

Logged-in visitors take the server-side path, which is correct as long as your

cache does what nearly all of them do and never serves cached pages to logged-in

users. An edge cache or CDN configured to cache without looking at cookies is the

exception, and there a logged-in visitor’s converted page can be stored and

served on. If you cache at the edge, confirm it varies on the login cookie.

External services

This plugin connects to two third-party services to keep currency conversion

rates up to date. What each one is sent, and when, is described separately

below because the two are not the same. Both requests are made with PHP’s

WP_Http transport, which in WordPress’s default configuration sends a

User-Agent header of the form WordPress/{version}; {your site's URL} —

so the site’s own address leaves with every request to either service, not

just the data described below. That header is filterable

( http_headers_useragent, http_request_args ), so a site that has changed

it will send something different.

ExchangeRate-API

What it is: a commercial exchange-rate API, used as the primary source of

exchange rates.

What is sent, and when: the three-letter base currency code you have

configured (for example USD ), sent as part of the request URL —

com/v4/latest/{BASE_CURRENCY} — when you

press “Sync rates” in the admin panel, when you run wp mhmcs rates-sync

from the command line, and on the schedule you configure under automatic

rate updates (hourly, twice daily, or daily). No other data from your site

is included.

com/terms

(ExchangeRate-API publishes its privacy policy inside that same page rather

)

European Central Bank (ECB) daily reference rates

What it is: the ECB’s public daily reference-rate feed, used as the fallback

when ExchangeRate-API cannot be reached.

What is sent, and when: nothing beyond the User-Agent described above. The

feed is a fixed, parameter-free address —

xml — so no

currency code or other value is sent to the ECB; the same document is

returned to every requester. It is only requested when ExchangeRate-API’s

request has failed. The feed is EUR-based and covers roughly thirty

currencies rather than the hundreds ExchangeRate-API carries. If your base

currency is outside that set, this source returns nothing at all and your

existing rates are left unchanged until the next attempt; if only one of

your configured target currencies is outside that set, the other target

currencies still update and the unsupported one is simply left without a

rate from this source.

” Its terms of

use are stated on its Disclaimer & Copyright page, which is the closest

equivalent and is linked below.

html

html

The ECB’s reference-rates page separately states that using these rates for

transaction purposes is strongly discouraged:

html

That page, not the two links above, is the source of that caution. This

plugin uses the feed to convert prices for display and checkout, which is

the kind of transactional use that notice is about; if that matters for your

shop, review that page before relying on this fallback.

Redirecting a source

If your network blocks the ECB feed, the mhmcs_fallback_rates_url filter

receives the URL, the base currency code and which source is being filtered

— always 'ecb', since ECB is now the only fallback — so the request can

be pointed elsewhere.

Visitor geolocation (through WooCommerce)

When “Enable geolocation-based currency detection” is on, the plugin asks

WooCommerce which country a visitor is in, through WooCommerce’s own

WC_Geolocation API. 0 that call explicitly switches WooCommerce’s

remote-API fallback OFF, so this plugin makes no third-party request for it:

  • Behind CloudFlare — the country is read from the CF-IPCountry request

header. Nothing leaves your server.

  • Otherwise — WooCommerce’s LOCAL MaxMind database file, if one is installed.

Again nothing leaves your server.

  • With neither — detection simply finds nothing, and the visitor keeps the

store’s base currency until they choose one. No third-party lookup is made.

Earlier versions called WC_Geolocation::geolocate_ip() with its bare defaults,

which let WooCommerce fall back to a remote geolocation service and send the

visitor’s IP address there. That fallback is now disabled, matching what

WooCommerce core itself does on the storefront.

The setting is off by default. Resolving a country from an IP address is

data processing a shop owner should switch on deliberately, so a fresh install

leaves it off and shops that already configured it keep their own choice.

To make detection work without CloudFlare, add a free MaxMind GeoLite2 license

key under WooCommerce > Settings > Integration > MaxMind Geolocation;

WooCommerce then downloads a local database file and answers from it.

WooCommerce geolocation documentation:

com/document/maxmind-geolocation-integration/

Source code

The settings screen is a React application, and what ships inside the plugin is

js. The readable source it is

built from is not in the package, so here is where to find it and how to

reproduce the build.

Full source, including the unminified JavaScript:

com/MaxHandMade/mhm-currency-switcher

The source of the bundle is admin-app/src/. It is compiled with WordPress’s

own build tooling, @wordpress/scripts, and nothing else:

npm install npm run build

php

dependency map the plugin reads when enqueuing the script. No other build step,

minifier or bundler is involved, and no code is generated at install time or at

runtime.

WordPress.org metadata

Plugin tags

Topics listed for this plugin in the WordPress.org directory.

  • currency switcher
  • exchange rate
  • multi currency
  • woocommerce
Compatibility & maintenance

Compatibility & maintenance

Tested with WP 7.1

Marked as tested with the current WordPress 7.1 release.

Current WordPress releaseWordPress 7.1
WordPress.org Tested up to7.1
Minimum requirementsWordPress 6.6 · PHP 7.4
Update activity19 Sept 20261mo ago

“Tested up to” is directory metadata supplied by the plugin author. WP PlugIndex does not turn an older declaration into a claim that the plugin is incompatible.

Changes recorded by WP PlugIndex

Recent tracked changes

Version2.2.02.2.1
WordPress compatibility7.17.1.1
Version2.1.02.2.0
Tracking & source

WP PlugIndex tracks public WordPress.org directory metadata and records selected version, compatibility and availability changes as they are detected.

Plugin watchlist

Watch this plugin

Sign in to your WP PlugIndex account or create a free account. Free accounts can watch up to 5 plugins and see what changed.

Watch limit

Watchlist limit reached

You have reached your current active watch limit.