Version: 0.1.0 Author: Jubayer Hossain — Broodweb License: GPL-2.0-or-later Requires: WordPress 6.0+ · PHP 8.1+ · WooCommerce
BW Multi-Currency lets a WooCommerce store display and charge in more than one currency without duplicating a single product. You enter prices once in your base currency; the plugin converts everything WooCommerce renders — catalogue, variations, cart, shipping, coupons, checkout and the resulting order — into whichever currency the visitor is viewing.
Rates can be entered by hand or pulled automatically from a free exchange-rate provider on a schedule. Visitors can pick a currency themselves, or — when BW Multilingual is installed — the currency can follow the site language, so a Dutch page shows € and a Bangla page shows ৳ with no extra click.
The plugin is a companion to BW Multilingual and shares its conventions, but it runs perfectly well on its own.
1. How it works
One set of prices, many currencies
Your products keep exactly one price, stored in the base currency — the currency you author in, which should match your WooCommerce currency setting. Nothing in the database is duplicated or rewritten.
On each front-end request the plugin decides which currency the visitor should see, then hooks into WooCommerce’s price and formatting filters. Every amount WooCommerce is about to read is multiplied by the base → target rate on its way out, and the currency code, symbol, decimal count and separators are swapped to match. The storefront ends up fully consistent — a product page, the mini-cart and the checkout total all agree — while the stored data never changes.
When the active currency is the base currency, every callback returns its input untouched. There is no conversion overhead for visitors on the default currency.
Resolving the active currency
The active currency is decided once per request, on plugins_loaded at priority 9 — early enough that WooCommerce (priority 10) sees the filters before it renders anything.
The order of precedence depends on the sync mode:
| Step | Manual mode | Hybrid mode | Automatic mode |
|---|---|---|---|
| 1 | ?bwmc_currency= in the URL |
?bwmc_currency= in the URL |
— |
| 2 | bwmc_currency cookie |
bwmc_currency cookie |
— |
| 3 | Geolocation (if enabled) | Geolocation (if enabled) | — |
| 4 | Base currency | Currency mapped to the site language | Currency mapped to the site language |
Once a visitor clicks the switcher, the choice is written to a cookie that lasts one month and the ?bwmc_currency= parameter is stripped from the URL with a redirect, so links stay clean and cacheable.
Contexts that never convert
Three contexts are forced to the base currency so stored data is never silently rewritten:
- WP-Admin screens — order editing, reports and product pricing always show base-currency figures.
- WP-Cron — scheduled jobs run in base currency.
- Any request where the plugin is not on the storefront.
Front-end AJAX and REST (Store API) requests do resolve normally, because the block cart and checkout must show the same currency as the page the visitor is on.
Standalone or paired
Everything related to language sync degrades gracefully. If BW Multilingual is not active, the bridge reports as inactive, the Sync tab explains why it’s unavailable, auto and hybrid modes silently behave as manual, and the combined switcher falls back to the currency-only switcher. Nothing errors, nothing is half-configured.
2. Installation
Requirements
| Requirement | Minimum |
|---|---|
| WordPress | 6.0 |
| PHP | 8.1 |
| WooCommerce | Required for price conversion |
| BW Multilingual | Optional — needed only for language sync |
WooCommerce is what the price engine hooks into. Without it, the plugin activates and the settings screen works, but nothing is converted.
Steps
- Upload the
bw-multicurrencyfolder to/wp-content/plugins/, or install the ZIP from Plugins → Add New → Upload Plugin. - Activate it from the Vstavljalniki screen.
- Open Multi-Currency in the admin menu.
On first activation the plugin reads your WooCommerce currency setting and adopts it as the base currency, seeding the enabled list and a 1.0 rate for it. If you install before WooCommerce, the base defaults to USD and you’ll want to correct it on the Currencies tab.
Deactivating clears the rate-refresh cron event. Your settings are kept, so reactivating picks up exactly where you left off.
3. Quick start
Five minutes, in order:
- Multi-Currency → Currencies. Confirm the base currency matches your WooCommerce currency — a mismatch shows a warning notice, because prices would otherwise convert from the wrong starting point. Then tick the currencies you want to offer. The base is always enabled and can’t be unticked.
- Rates. Choose Automatic to fetch live rates on a schedule, or Manual to type them in. Even in automatic mode it’s worth filling in manual rates for anything your provider might not cover — they act as the fallback.
- Sync (only meaningful with BW Multilingual active). Pick how currency relates to language:
- Manual — the two are independent.
- Automatic — currency follows the language and the currency switcher disappears.
- Hybrid — language sets the default, visitors can still override it.
- Switcher. Turn on the floating pill, or drop
[bwmc_switcher]into a menu, widget or template. - Test. Load the shop as a logged-out visitor, switch currency, add something to the cart and walk through checkout. Confirm the mini-cart, shipping and total all follow.
4. Currencies
The registry
The plugin ships with a catalogue of 43 currencies, keyed by ISO 4217 code so the keys line up with WooCommerce’s own currency codes. Each entry carries everything needed to render an amount correctly:
| Field | Purpose |
|---|---|
name |
English display name |
symbol |
Currency symbol (€, ৳, RM, …) |
flag |
Emoji flag, rendered as an SVG image |
decimals |
Decimal places (0 for JPY, KRW, VND, IDR, HUF) |
position |
left, right, left_space, right_space |
thousand |
Thousands separator for that locale |
decimal |
Decimal separator for that locale |
Coverage spans the Americas, Europe, the Middle East, Africa, South Asia and East Asia — including BDT, INR, PKR, LKR, NPR, NGN, KES and EGP, which many multi-currency plugins omit.
The catalogue is filterable, so you can add a currency or override a symbol without touching plugin files. See Developer reference.
Base currency
The base currency is the one your prices are entered in. It should always match WooCommerce → Settings → General → Currency. If the two drift apart, the admin screen shows a warning, because every conversion starts from the base and a mismatch means every displayed price is wrong by the size of one rate.
Changing the base after go-live invalidates any fetched live rates — they’re stored with the base they were fetched against and are ignored when that base changes, so the next refresh re-fetches cleanly.
Enabled currencies
Enabled currencies are what visitors can switch to. The base is always included, and any currency you disable is immediately dropped from the switcher, the language map and gateway restrictions on the next render — a stale mapping can never surface a currency you no longer offer.
Flag rendering
Flag emoji are output as SVG images from WordPress.org’s emoji CDN rather than as raw characters. Windows has no flag-emoji font and would otherwise show bare letter pairs (“NL” instead of 🇳🇱). The emoji itself stays as the alt text, so a blocked or failed image degrades to the character.
5. Exchange rates
A rate is always expressed as how many units of the target currency equal one unit of the base. If your base is USD and EUR sits at 0.92, a $100 product shows as €92.
Manual rates
Type the rates in on the Rates tab. They’re stored with the rest of your settings, never change on their own, and are exactly what a store wants when it prices deliberately rather than tracking the market — a round €89 rather than €87.43.
Manual rates also serve as the safety net in automatic mode: if the provider doesn’t return a rate for a currency, the manual one is used instead.
Automatic rates
In automatic mode a provider is called on a schedule and the results are stored separately from your settings, along with a timestamp, the provider used, and the last error.
Two providers ship with the plugin, both free and neither requiring an API key:
| Provider | Source | Coverage | Best for |
|---|---|---|---|
| ER-API (default) | open.er-api.com | ~160 currencies | Most stores — includes BDT, NGN, LKR and other currencies ECB doesn’t publish |
| Frankfurter | api.frankfurter.dev | ~30 major currencies | Stores that want central-bank sourced numbers (official ECB reference rates) |
Choose the refresh interval — hourly, twice daily or daily. The cron event is kept in step with your settings automatically: switching to automatic schedules it, switching back to manual clears it, and changing the interval reschedules it.
Save & refresh now on the Rates tab saves your changes and immediately fetches, so you can verify a provider works without waiting for cron.
Markup
The markup percentage (from -50% to +100%) is added to fetched rates only — never to manual ones. It exists to absorb payment-processor FX spread and rate drift between refreshes: a 2% markup means a rate that moves 1.5% against you between daily fetches still doesn’t cost you margin.
Failure behaviour
A failing provider never wipes what you have. If a fetch errors, the stored rates are left alone and only the error message and timestamp are recorded — the storefront keeps converting with the last good numbers, and the error appears in the status line under the Rates tab.
Similarly, results are merged rather than replaced, so a provider that omits one currency (Frankfurter and BDT, for example) doesn’t drop a rate a previous fetch obtained. Any currency the provider didn’t cover is listed in the status line so you know to supply a manual rate for it.
The rates table
The Rates tab shows three columns per currency so you can always see what’s actually in effect:
| Column | Meaning |
|---|---|
| Manual rate | What you typed in |
| Live rate | What the provider last returned (before markup) |
| Effective | The rate actually used for conversion right now |
Rounding
Three rounding rules, set on the Advanced tab, applied after conversion:
| Mode | Behaviour | Example |
|---|---|---|
| Exact | Round to the currency’s own decimal count | 55.204 → 55.20 |
| Nearest whole | Round to the nearest integer | 55.20 → 55 |
| Round up | Always round up to the next integer | 55.20 → 56 |
Whole-number rounding suits currencies where decimal amounts look strange — a price of ৳6,047.32 reads better as ৳6,048 — and produces cleaner numbers than a raw conversion ever will.
6. Language sync
This section applies when BW Multilingual is active. Without it, the Sync tab explains that the plugin is running standalone and the mode stays effectively manual.
The three modes
Manual — currency and language are unrelated. A visitor reading in Dutch can browse in dollars if they want. The currency switcher is visible.
Automatic — currency is a pure function of the language. Switching to Dutch switches to euros; there is nothing for the visitor to choose, so the currency switcher renders nothing at all. This is the cache-friendliest mode, because every visitor on a given URL sees the same currency.
Hybrid — the language decides the default, but a visitor who explicitly picks a currency keeps that choice. The best of both for stores whose language regions don’t map perfectly onto currency preferences.
The mapping table
The Sync tab lists every active BW Multilingual language with a currency dropdown and the rate currently in effect. Each language can be set to:
- Auto — follow the locale-based suggestion (shown in the dropdown label so you can see what it resolves to).
- Base — pin this language to the base currency explicitly.
- A specific currency — an explicit override.
How Auto suggests
Auto works from the language’s locale, not guesswork:
- Take the country part of the locale —
nl_NL→NL→ EUR. - If the locale has no country part, fall back to a language-code table —
bn→ BDT,ja→ JPY,tr→ TRY, and so on. - If neither yields a result, or the suggested currency isn’t enabled, use the base currency.
The euro is mapped across all twenty eurozone countries, so nl_NL, de_DE, fr_FR, es_ES, pt_PT and the rest all resolve to EUR without any configuration.
Because Auto is evaluated on each request rather than baked in, enabling a new currency later activates it for its languages immediately — you don’t have to revisit the mapping table. If a language’s suggestion points at a currency you haven’t enabled, the tab tells you so directly under the dropdown.
Language sync and the Store API
Block cart and checkout call the Store API over REST, and REST URLs carry no language prefix — so BW Multilingual would resolve them to the default language. The bridge handles this by reading the request’s Referer header, which points at the localized page that made the call, exactly as BW Multilingual does for its own AJAX. The block checkout therefore stays on the visitor’s currency instead of snapping back to the default.
7. The switcher
Shortcodes
[bwmc_switcher]
[bwmc_switcher style="inline" show="symbol_code"]
| Attribute | Values | Default |
|---|---|---|
style |
dropdown, inline |
From settings |
show |
symbol, code, flag, flag_code, symbol_code, name |
From settings |
The switcher renders nothing at all in two cases: when the sync mode is Automatic (there’s nothing to choose), and when fewer than two currencies are enabled.
The combined switcher
[bwmc_combined_switcher]
[bwmc_combined_switcher style="inline"]
With BW Multilingual active, this renders a single control showing both language and currency — 🇳🇱 NL · € — where each item is a language whose link carries both the localized URL and that language’s mapped currency. One click switches both.
In automatic sync mode the link omits the currency parameter, because the currency follows the language on its own. Without BW Multilingual, the combined switcher falls back to the ordinary currency switcher.
If you use the combined switcher, turn off BW Multilingual’s own floating switcher — otherwise you’ll have two pills stacked in the same corner.
The floating pill
Set on the Switcher tab, output automatically in the footer:
| Option | Result |
|---|---|
| Off | No floating switcher |
| Currency — bottom right / left | Currency-only pill |
| Language + currency — bottom right / left | Combined pill (requires BW Multilingual) |
Appearance
The appearance controls mirror BW Multilingual’s exactly, so the two switchers can sit side by side and look like one control family:
| Setting | Range | Default |
|---|---|---|
| Font size | 8–40 px | 14 |
| Flag size | 8–48 px | 18 |
| Pill background | Hex colour | #7c4dff |
| Pill text | Hex colour | #ffffff |
| Menu background | Hex colour | #ffffff |
| Menu text | Hex colour | #182b2f |
CSS and JavaScript are both inlined — no extra HTTP requests. The dropdown closes on outside click and on Escape, and the toggle carries aria-haspopup and aria-expanded for screen readers.
Styling hooks
| Class | Element |
|---|---|
.bwmc-switcher |
Wrapper |
.bwmc-switcher--dropdown / --inline |
Layout variant |
.bwmc-switcher--floating / --left |
Floating pill and its side |
.bwmc-current |
The dropdown toggle |
.bwmc-flag, .bwmc-symbol, .bwmc-code, .bwmc-name |
Label parts |
.is-current |
The active currency’s link |
.is-open |
Open dropdown state |
8. What gets converted
Prices
Every product and variation price filter WooCommerce exposes is hooked — price, regular price and sale price, at both product and variation level. Variation price ranges are cached by WooCommerce, so the plugin adds the active currency to the cache key; without this, a EUR visitor could be served a USD range from cache.
Empty values are preserved rather than converted. WooCommerce uses an empty string to mean “no price” or “no sale price”, and casting that to zero would turn an unpriced product into a free one.
Shipping
Shipping rates are converted after the shipping methods have calculated them, including their tax components. The shipping package is tagged with the active currency first, so WooCommerce’s own rate cache is not shared between currencies.
Cart fragments
The mini-cart fragment is cached in the browser’s sessionStorage under a hash key. The plugin salts that key with the active currency, so switching currency never leaves a stale mini-cart total sitting in the header.
Coupons
| Coupon type | Behaviour |
|---|---|
| Fixed cart / fixed product discount | Converted from base to active currency |
| Percentage discount | Untouched — a percentage is currency-agnostic |
| Minimum spend | Converted |
| Maximum spend | Converted |
This matters more than it first appears: a “$50 minimum spend” coupon that wasn’t converted would be trivially easy to qualify for in one currency and nearly impossible in another.
Number formatting
Alongside the amount, the plugin swaps the currency code, symbol, decimal count, decimal separator, thousands separator and symbol position — all read from the currency registry. A euro price renders as € 1.234,56 and a Swedish one as 1 234,56 kr, each following its own local convention rather than inheriting the store’s.
9. Payments and orders
Orders are charged in the displayed currency
When a visitor checks out in euros, the order is created in euros. This is the honest behaviour — the customer pays what the page said — but it has a direct consequence: your payment gateways must support every currency you enable.
Restricting gateways by currency
The Advanced tab lists every enabled WooCommerce gateway with a checkbox per currency:
- Nothing checked — the gateway is available for all currencies (the default).
- Specific currencies checked — the gateway is hidden at checkout for any other currency.
This is how you handle a local processor that only settles in one currency: check BDT for it, and visitors browsing in euros simply won’t see it as an option.
The order audit trail
Every converted order is stamped with three pieces of meta, for both classic and block checkout:
| Meta key | Contents |
|---|---|
_bwmc_base_currency |
The base currency at the time of the order |
_bwmc_rate |
The exact rate the order was priced at |
_bwmc_base_total |
The order total converted back to base currency |
The order edit screen renders this as a readable line beneath the order details — “charged in EUR at rate 1 USD = 0.92. Base value ≈ 108.70 USD” — so reconciling a month of mixed-currency orders doesn’t require guessing which rate was in effect on which day.
Orders in the base currency are not stamped, since there’s nothing to record.
10. Caching
Currency selection and full-page caching are in natural tension: a cache stores one copy of a URL, but two visitors on that URL may need different prices.
The plugin resolves this precisely rather than bluntly. On each request it works out what an uncookied visitor would see on that URL. If the current visitor’s currency differs — because of a cookie or a geolocation result — the response defines DONOTCACHEPAGE, which BW Multilingual’s page cache and essentially every WordPress caching plugin honour. That single visitor’s response is excluded; everyone else still gets a cached page.
In Automatic sync mode this never triggers at all. Currency is a function of the language, the language is a function of the URL, and every visitor on a URL sees the same thing — so caching stays fully enabled. If page-cache hit rate matters to you and your languages map cleanly onto currencies, automatic mode is the mode to choose.
Two events flush BW Multilingual’s per-language page cache automatically, since both change every rendered price: saving currency settings, and a successful live-rate refresh.
11. Settings reference
All settings live in a single option, bwmc_settings. Fetched live rates are stored separately in bwmc_auto_rates.
Currencies tab
| Setting | Key | Default |
|---|---|---|
| Base currency | base_currency |
WooCommerce currency, or USD |
| Enabled currencies | enabled_currencies |
[base] |
Rates tab
| Setting | Key | Values | Default |
|---|---|---|---|
| Rate source | rate_mode |
manual, auto |
manual |
| Provider | rate_provider |
erapi, frankfurter |
erapi |
| Refresh interval | rate_refresh |
hourly, twicedaily, daily |
daily |
| Markup | rate_markup |
-50 to 100 (%) | 0 |
| Manual rates | rates |
ISO code → float | [] |
Sync tab
| Setting | Key | Values | Default |
|---|---|---|---|
| Resolution mode | sync_mode |
manual, auto, hybrid |
manual |
| Language map | lang_currency_map |
lang → '' (auto) / BASE / ISO code |
[] |
Switcher tab
| Setting | Key | Values | Default |
|---|---|---|---|
| Shortcode style | switcher_style |
dropdown, inline |
dropdown |
| Shortcode label | switcher_show |
symbol, code, flag, flag_code, symbol_code, name |
flag_code |
| Floating switcher | switcher_floating |
'', floating, floating-left, combined, combined-left |
'' |
| Floating label | switcher_float_show |
as above | symbol_code |
| Font size | sw_font_size |
8–40 | 14 |
| Flag size | sw_flag_size |
8–48 | 18 |
| Pill background / text | sw_pill_bg / sw_pill_text |
hex | #7c4dff / #ffffff |
| Pill radius | sw_pill_radius |
0–100 | 100 |
| Menu background / text | sw_menu_bg / sw_menu_text |
hex | #ffffff / #182b2f |
| Menu radius / width | sw_menu_radius / sw_menu_width |
0–40 / 0–600 | 8 / 0 (auto) |
Advanced tab
| Setting | Key | Values | Default |
|---|---|---|---|
| Price rounding | rounding |
none, nearest, up |
none |
| Geolocation default | geo_default |
boolean | false |
| Gateway currencies | gateway_currencies |
gateway id → ISO codes ([] = all) |
[] |
Geolocation
When enabled, a first-time visitor with no cookie gets a currency derived from their country, using WooCommerce’s own geolocation — server geo headers or the local MaxMind database. No external API is called, so it adds no latency to the request.
It applies only when the language hasn’t already decided a currency, only for enabled currencies, and only once — the result is written to the cookie, so subsequent requests skip the lookup. An explicit visitor choice always wins.
12. Developer reference
Filters
| Filter | Arguments | Purpose |
|---|---|---|
bwmc_currencies |
$list |
Add, remove or modify currency definitions |
bwmc_active_currency |
$code |
Override the resolved currency for a request |
bwmc_rate |
$rate, $to, $base |
Override a single conversion rate |
bwmc_rate_providers |
$providers |
Register additional exchange-rate providers |
Actions
| Action | Arguments | Fires |
|---|---|---|
bwmc_loaded |
$plugin |
After the plugin boots |
bwmc_rates_refreshed |
$rates, $provider_id |
After a successful live fetch |
Adding a currency
add_filter( 'bwmc_currencies', function ( $list ) {
$list['ISK'] = [
'name' => 'Icelandic Króna',
'symbol' => 'kr',
'flag' => '🇮🇸',
'decimals' => 0,
'position' => 'right_space',
'thousand' => '.',
'decimal' => ',',
];
return $list;
} );
Adding a rate provider
Extend BWMC_Provider and register the instance. The base class gives you a get_json() helper with error handling already built in.
class My_Rate_Provider extends BWMC_Provider {
public function id() {
return 'my-provider';
}
public function label() {
return 'My Provider';
}
public function fetch( $base, array $targets ) {
$data = $this->get_json( 'https://example.com/rates?base=' . rawurlencode( $base ) );
if ( is_wp_error( $data ) ) {
return $data;
}
$out = [];
foreach ( $targets as $code ) {
if ( isset( $data['rates'][ $code ] ) && (float) $data['rates'][ $code ] > 0 ) {
$out[ $code ] = (float) $data['rates'][ $code ];
}
}
return $out;
}
}
add_filter( 'bwmc_rate_providers', function ( $providers ) {
$providers[] = new My_Rate_Provider();
return $providers;
} );
fetch() must return an array of code => rate (the value of one base unit) or a WP_Error. Returning a WP_Error preserves the last good rates and surfaces your message in the admin status line.
Reading state in your own code
BWMC_Resolver::current(); // Active currency code
BWMC_Resolver::is_base(); // Is the active currency the base?
BWMC_Resolver::mode(); // manual | auto | hybrid (effective)
BWMC_Settings::base_currency(); // Base currency code
BWMC_Settings::enabled_currencies(); // All enabled codes, base first
BWMC_Rates::rate( 'EUR' ); // Effective base → EUR rate
BWMC_Rates::convert( 49.00, 'EUR' ); // Convert a base amount
BWMC_Currencies::get( 'EUR' ); // Full currency definition
BWMC_Bridge::active(); // Is BW Multilingual available?
Classes
| Class | Responsibility |
|---|---|
BWMC_Plugin |
Singleton orchestrator; wires everything on boot |
BWMC_Settings |
The single settings option, with sanitization |
BWMC_Currencies |
The currency registry and label helpers |
BWMC_Resolver |
Decides the active currency per request |
BWMC_Rates |
Rate lookup, conversion and rounding |
BWMC_Rate_Service |
Scheduled live-rate fetching and storage |
BWMC_Provider |
Provider base class and registry |
BWMC_Bridge |
Adapter to BW Multilingual and geolocation |
BWMC_Price_Engine |
All WooCommerce filters |
BWMC_Switcher |
Shortcodes, styles and the floating pill |
BWMC_Admin |
The settings screen and order note |
Storage
| Type | Name | Contents |
|---|---|---|
| Option | bwmc_settings |
All configuration |
| Option | bwmc_auto_rates |
Fetched rates, base, timestamp, provider, last error |
| Cookie | bwmc_currency |
Visitor’s chosen currency (1 month, HttpOnly) |
| Query arg | bwmc_currency |
Switch trigger, stripped by redirect |
| Cron event | bwmc_refresh_rates |
Live-rate refresh |
| Order meta | _bwmc_base_currency, _bwmc_rate, _bwmc_base_total |
Conversion audit trail |
The plugin creates no database tables.
13. Troubleshooting
Prices aren’t converting at all. Check that WooCommerce is active — the price engine only registers its filters when it is. Then confirm you have at least one non-base currency enabled and a rate set for it.
A currency shows the same numbers as the base. That currency has no rate. When no manual rate is set and the provider hasn’t returned one, conversion falls back to 1:1 rather than pricing at zero. Set a manual rate on the Rates tab, or check the status line for a provider error.
The switcher doesn’t appear. Three possible reasons: the sync mode is Automatic (the switcher is hidden by design — the currency follows the language), fewer than two currencies are enabled, or the shortcode is in a context that doesn’t run shortcodes.
A warning says my base currency doesn’t match WooCommerce. Set them the same. Whichever one is wrong, correct it — the base currency is the starting point for every conversion, so a mismatch makes every displayed price wrong by one rate.
Live rates aren’t updating. Check the status line under the Rates tab, which shows the provider, interval, when rates were last fetched and any error. Press Refresh now to fetch immediately and see the result. If cron never fires, your host may have DISABLE_WP_CRON set — wire up a real system cron hitting wp-cron.php.
Frankfurter returns an error for one of my currencies. Frankfurter serves ECB reference rates, which cover roughly thirty major currencies. BDT, NGN, LKR and similar aren’t among them. Switch to ER-API, or keep Frankfurter and add manual rates for the currencies it doesn’t cover.
Cached pages show the wrong currency. Confirm your caching layer honours DONOTCACHEPAGE. If it doesn’t, use Automatic sync mode, which is fully cache-safe because currency depends only on the URL.
Checkout offers a gateway that can’t take the currency. Restrict it on the Advanced tab — check only the currencies that gateway supports.
Old prices persist after a rate change. Saving settings and a successful rate refresh both flush BW Multilingual’s page cache. Third-party caching plugins and CDNs need their own flush.
14. Uninstalling
Deactivating clears the rate-refresh cron event and leaves everything else intact — settings, fetched rates and order meta all survive, so reactivation restores your configuration exactly.
Deleting the plugin through WordPress removes the bwmc_settings option. Order meta is deliberately preserved, since it’s part of your financial record and deleting it would break historical reconciliation.
Prices return to your WooCommerce currency immediately. Nothing in your product data was ever modified, so there is no migration to undo.
BW Multi-Currency is developed by Broodweb.