FOX - WooCommerce Currency Switcher Professional

Change Currency by Language in WooCommerce: Polylang, TranslatePress

To change currency by language in WooCommerce, FOX needs a few lines of code: there is no setting that ties a currency to a language, because the language belongs to Polylang, TranslatePress, Weglot or GTranslate, not to the currency switcher. The code is short, and the snippets that circulate work, but they come with three surprises: visitors can no longer pick another currency, cached pages flip back to the old currency, and in-browser translators never reach the code at all. Below are two patterns, one that ties the currency to the language for good and one that only switches it when the language changes, and what to do with each translation plugin.

Read the current language in PHP

Every pattern starts with the language of the current page. Each translation plugin exposes it differently:

PluginCurrent language in PHPExample value
Polylangpll_current_language()de
TranslatePressglobal $TRP_LANGUAGE;de_DE
Weglotweglot_get_current_language()de
WPMLapply_filters( 'wpml_current_language', null )de
Polylang, TranslatePress (alternative)get_locale()de_DE

The value must match your code exactly, including case and the separator: en_US, not en-US or en-us. When a language "does nothing", print the value once (for example into an HTML comment in the footer) and copy it from there. For any other plugin, look in its documentation for the function that returns the current language; if it has none, use the JavaScript pattern below, which needs only the page's lang attribute. WPML users can also follow switch currency with language in WPML. WooCommerce Multilingual has its own multi-currency mode: leave it off when FOX handles the currencies.

Pattern 1: the currency always follows the language

Use this when each language stands for one market: German pages always in euros, the English US pages always in dollars. The code sets the currency on every page view, so it overrides the switcher, the welcome currency and the GeoIP rules.

/*
 * FOX: the currency always follows the language.
 * Child theme functions.php or a code snippets plugin.
 */
add_action( 'template_redirect', function () {
    if ( ! class_exists( 'WOOCS' ) ) {
        return;
    }
    global $WOOCS;

    // Language => currency. Use the values your plugin returns (see the table).
    $map = array(
        'en' => 'USD',
        'de' => 'EUR',
        'fr' => 'EUR',
        'bg' => 'BGN',
    );

    // Polylang. For another plugin, replace this line with its function.
    $lang = function_exists( 'pll_current_language' ) ? pll_current_language() : '';

    if ( isset( $map[ $lang ] ) ) {
        $WOOCS->set_currency( $map[ $lang ] );
        // Tells FOX's script that this page was built in this currency (needed with page cache).
        $_GET['currency'] = $map[ $lang ];
    }
} );

Why it is written this way:

  • template_redirect, not wp_head. It runs before the theme prints anything, so headers, menus and mini carts already get the language's currency, and it does not run for AJAX requests, where the language is often unknown. Older snippets on wp_head work in most themes, but anything that reads the currency earlier, such as a header cart built before wp_head, still gets the old one.
  • The $_GET['currency'] line. On a cached shop, FOX redraws prices after loading in the currency stored for the visitor; that redraw is what makes a page show the right currency "for a second" and then flip back. A page built with a currency in the request skips the redraw. It also stops a GeoIP rule from replacing the language currency on a visitor's first page. The cost: on every page FOX refreshes the mini cart once, as it does after a currency switch.
  • Page cache and the cart. A cached page does not run PHP, so the currency stored for the visitor is not updated by this code. Set Currency storage to Transient and turn on I am using cache plugin on my site: in that combination FOX's script copies the currency of the page into the visitor's storage, so the cart and checkout follow the language too. Background in currency switcher resets to default currency.

Since the switcher can no longer change anything, hide it, or show only the currency of the current language. Visitors who want another currency change the language.

Pattern 2: switch with the language, keep the switcher

Most shops want something softer: a visitor who opens the German pages gets euros, can still switch to dollars, and keeps dollars while browsing in German. That needs to remember which language the currency was last set for, and to act only when the language changes. Doing it in the browser has two advantages: it works on cached pages, and it works with any translation plugin that gives each language its own pages, because it reads only the lang attribute of the page.

/*
 * FOX: switch the currency once when the page language changes;
 * the visitor can pick another currency afterwards.
 * Front end, in the footer: a code snippets plugin or the theme's
 * custom JavaScript field.
 */
window.addEventListener('load', function () {
    var map = { en: 'USD', de: 'EUR', fr: 'EUR', bg: 'BGN' }; // language: currency
    var lang = (document.documentElement.lang || '').slice(0, 2).toLowerCase();
    var params = new URLSearchParams(window.location.search);
    var last = null;

    if (!map[lang]) {
        return;
    }
    try {
        last = localStorage.getItem('my_currency_lang');
        localStorage.setItem('my_currency_lang', lang);
    } catch (e) {
        return; // storage blocked: leave the currency alone
    }
    // The visitor just picked a currency, or the language has not changed.
    if (params.has('currency') || last === lang) {
        return;
    }
    params.set('currency', map[lang]);
    window.location.replace(window.location.pathname + '?' + params.toString() + window.location.hash);
});

When the language changes, the script reloads the page once with ?currency=EUR, the same parameter FOX's own switcher uses, so FOX stores the currency for the visitor. Afterwards the visitor's own choice stands until they change the language again. Notes:

  • The first two letters of lang are used, so de-DE and de-AT both count as de. If two variants of one language need different currencies, use the full value in the map.
  • On the very first visit nothing is remembered yet, so the script switches to the language's currency, after the welcome currency and GeoIP rules have chosen one. To let GeoIP decide the first currency instead, add if (last === null) { return; } right after the try block.
  • Do not combine it with Pattern 1. The PHP code would override the visitor's choice on the next page.

GeoIP, the welcome currency and per-language currency lists

  • Which rule wins. With Pattern 1, the language wins on every page, and the GeoIP rules and the Welcome currency no longer matter. With Pattern 2, GeoIP decides the first currency only if you add the first-visit line above; after that, the language decides whenever it changes. In both cases Checkout by GeoIP rules, if enabled, still makes customers pay in their country's currency at checkout; see checkout currency based on customer country.
  • Hide a currency for one language. The woocs_currency_manipulation_before_show filter changes the list every FOX switcher shows. It hides the currency from the switcher only; it does not change the currency of a visitor who already has it, which Pattern 1 or 2 takes care of.
/*
 * FOX: do not offer PLN on the English pages.
 * Child theme functions.php or a code snippets plugin.
 */
add_filter( 'woocs_currency_manipulation_before_show', function ( $currencies ) {
    if ( function_exists( 'pll_current_language' ) && pll_current_language() === 'en' ) {
        unset( $currencies['PLN'] );
    }
    return $currencies;
} );

If the reason for one currency per language is the price itself, round amounts per market rather than converted ones, combine the language rule with fixed prices per currency.

GTranslate and other in-browser translators

The free GTranslate widget, and other services that translate the page in the browser, switch the language without loading a new page. No request reaches WordPress, so no PHP code can react, and the page's lang attribute may not change either. With such a translator, neither pattern above can see the language change. Options:

  • If your plan gives every language its own address (a subdirectory or subdomain), each language is a separate page load. Check the page source of a translated page: if <html lang="…"> shows the language, Pattern 2 works as it is.
  • Translation proxies may tell WordPress the language in a request header; GTranslate's paid plans use X-GT-LANG, read in PHP as $_SERVER['HTTP_X_GT_LANG']. Print it once before relying on it: if it is empty, your setup does not send it, and only the provider's support can say why.
  • Otherwise, keep the currency independent of the language: let GeoIP rules choose the first currency and the switcher do the rest. See automatically change currency by country.

If the currency does not follow the language

  • Print the language value and compare it with the keys of your map, character for character.
  • The switcher "does nothing": Pattern 1 is active. That is how it works; use Pattern 2 if visitors must choose.
  • The currency is right for a moment and then changes back: the page came from a cache and the $_GET['currency'] line is missing, or another snippet or plugin also sets the currency. Search the theme and snippet plugins for set_currency.
  • The page is right but the cart shows the old currency: with a page cache, use Transient storage with the cache option, as described in Pattern 1.
  • A custom mini cart shows the new symbol with old amounts: it keeps its own cached cart data. Test with the standard WooCommerce mini cart to confirm.
  • Purge every cache (plugin, server, CDN) after adding or changing the code, and test in a private window.

If the currency still does not follow the language, open a topic on the FOX support forum with the translation plugin you use, your snippet and a link to two language versions of one page.