Fields & Floors Collectors
Search every numbered sports card on eBay by print run, autograph, rookie status and price, then save searches and get an email when a new card lists.
翻譯準備中
Multi-currency support for WooCommerce with real exchange rates — and it keeps working behind a page cache, where most currency switchers break. Per-currency fees and rounding, fixed prices per product, geolocation, five ways to place the switcher, translation-ready.
翻譯準備中
取自儲存庫 README
正在載入完整 README。 以下為已儲存的摘錄。
Multi-currency support for WooCommerce. Visitors browse, add to cart and check out
in the currency they choose, with exchange rates fetched automatically — and it
keeps working behind a page cache, which is where most currency switchers break.檢視完整 README ↗Multi-currency support for WooCommerce. Visitors browse, add to cart and check out in the currency they choose, with exchange rates fetched automatically — and it keeps working behind a page cache, which is where most currency switchers break.
It survives a page cache. A page cache stores the HTML produced for whoever asked first, so a plugin that converts prices on the server serves the first visitor's currency to everyone after them. This plugin renders anonymous catalogue pages in the shop's base currency — one cached copy, correct for everybody — and the browser corrects the displayed prices afterwards. Cart, checkout, order totals and order emails are always calculated on the server, so the amount charged can never be changed from the browser.
Everything else follows from that decision:
| Real exchange rates | ExchangeRate-API, with an ECB fallback if it is unreachable |
| Per-currency control | manual or automatic rate, a fee (fixed or percentage), and rounding rules, each currency on its own |
| Fixed prices per product | override the converted price for a specific product and currency |
| Five ways to place the switcher | two shortcodes, two Elementor widgets, or a navigation menu item |
| Geolocation | detect the visitor's country and preselect a matching currency (off by default, opt-in) |
| 283 flag icons | SVG, bundled, no external requests |
| Translation-ready | full .pot template ships with the plugin; language packs (Turkish included) are delivered through WordPress.org once translations are published there |
| WP-CLI | sync rates, inspect a currency, flush the cache, list what is configured |
A note on geolocation. Country detection goes through WooCommerce's own
WC_GeolocationAPI, and since 2.2.0 the plugin calls it with WooCommerce's remote-API fallback switched off -- so nothing about the visitor ever leaves your server. The country comes from theCF-IPCountryheader behind CloudFlare, or from a local MaxMind database file; with neither, detection finds nothing and the visitor keeps the base currency. The setting is off by default. Setup steps are in the user guide.
The switcher in a live shop's navigation menu, and a shop priced in US dollars as a visitor sees it after choosing Turkish lira:
The settings screen — currencies, then display, then the rest:
WordPress 6.6 is a hard floor, not a guess: the settings screen is a React app
that depends on the react-jsx-runtime script handle, which WordPress core only
registers from 6.6. On 6.5 and below WordPress refuses the install.
mhm-currency-switcher folder into wp-content/plugins/, or upload
the release ZIP under Plugins → Add New → Upload Plugin.Adding a currency does not put anything on your storefront — the switcher has to be placed somewhere a visitor can reach it. The plugin's own How to use tab lists every option with copyable code; in short:
| Where | How |
|---|---|
| Any post, page or text widget | the [mhmcs_currency_switcher] shortcode |
| Elementor | drag the Currency Switcher widget onto the layout |
| Navigation menu (themes that support menus or widgets) | Appearance → Menus, add the Currency Switcher item |
| Block themes | the core Shortcode block — this plugin does not provide its own block yet |
| A product page, in several currencies at once | the [mhmcs_currency_prices] shortcode, or the Currency Prices Elementor widget |
[mhmcs_currency_switcher]The dropdown a visitor picks a currency from.
| Attribute | Values | Default |
|---|---|---|
size | small, medium, large | the size saved under Display Options |
[mhmcs_currency_prices]One product's price in several currencies at once.
| Attribute | What it does |
|---|---|
currencies | comma-separated codes, e.g. USD,EUR. Without it, the currencies chosen under Display Options are used. Codes you have not configured are ignored. |
product_id | price a specific product instead of the one being viewed |
show_flags | true or false, overriding the saved Display Options setting |
price | price a fixed amount instead of a product — mainly for testing a layout |
wp mhmcs rates-sync # fetch fresh exchange rates
wp mhmcs rates-get EUR # show the raw and effective rate for one currency
wp mhmcs cache-flush # drop the rate cache
wp mhmcs currencies-list # list configured currencies
wp mhmcs status # overview: base currency, rates, schedule
On by default, under WooCommerce → MHM Currency → Advanced.
Anonymous shop, archive and product pages render in the base currency and the
browser converts the displayed prices through POST /wp-json/mhmcs/v1/convert.
The endpoint prices products the caller names, reading each amount on the server
— a browser cannot dictate a price. Cart, checkout, order totals, order emails
and the WooCommerce REST API are always converted server-side in the currency
the customer actually chose.
Turning the mode off makes the storefront convert on the server again, as it did before the feature existed. Read the limits below before deciding either way: both settings have consequences, and they are different ones.
Full explanations live in readme.txt under "Known limits". In short:
wc/v3 reads pinned to the base currency, no conversion under cron or WP-CLI.prefers-reduced-motion gets the swap
without the fade.data-product_variations is false and third-party swatch plugins that read
prices out of that JSON may stop showing one.?currency= multiplies cache entries. The switcher does not generate such
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 off, it reloads the same URL instead.Composer, Node.js 18+, and Docker for the integration tests.
composer install
npm install && npm run build
composer test # PHPUnit, unit — no external dependencies
composer lint # PHPCS
composer analyze # PHPStan
npm run test:js # Jest + jsdom, covers assets/js/
npm run lint:js # ESLint
npm run test:js exists because assets/js/ is the storefront's load-bearing
surface and no PHP gate can see it.
They need a real WordPress + WooCommerce install. The one-command Docker runner needs nothing but Docker and matches what CI does:
bin/test-integration-docker.sh # WP latest
PHP_VERSION=7.4 WC_VERSION=9.1.4 bin/test-integration-docker.sh 6.6
It tests one WordPress/WooCommerce pair per run; CI runs four pairs — PHP 7.4/WP 6.6/WC 9.1.4, PHP 8.1/WP 6.8/WC 9.8.5, PHP 8.2 and 8.4 on WP latest/WC latest — plus the latest pair with HPOS. The lowest pair is pinned to the declared floor, and every leg fails unless the WordPress and WooCommerce it installed are the versions it asked for. A weekly canary runs the suite on WordPress trunk.
To verify the declared floor itself — which the test suite cannot, because it never loads an admin page — build a ZIP and open it on that version:
python bin/build-release.py
bin/verify-wp-floor.sh up wordpress:6.6-php8.1-apache 8150 floor-ok build/mhm-currency-switcher.2.3.0.zip
bin/verify-wp-floor.sh down floor-ok
bin/make-i18n.sh regenerates the whole chain — .pot, .po, .mo,
.l10n.php and the md5-named JSON WordPress looks up for the React app. Run it
inside Docker; the script re-executes itself there, because WP-CLI's plugin
detection fails silently on Windows and produces a catalogue missing strings.
GPLv2 or later. See LICENSE.