Changing the WooCommerce Currency Symbol
The filter changes a glyph and nothing else. The store still trades in the currency configured in settings, so a symbol swapped without a matching currency change makes the store lie about what it charges.
Check the second argument or you will change every currency at once. woocommerce_currency_symbol is called once for each currency whose symbol is needed, and a callback that returns a new glyph unconditionally rewrites the lot. On a single-currency store the bug is invisible. On a store that later adds a second currency, every price is suddenly wrong and the cause is a snippet added months earlier.
The correct shape
add_filter( 'woocommerce_currency_symbol', 'ti_currency_symbol', 10, 2 );
function ti_currency_symbol( $symbol, $currency ) {
if ( 'USD' === $currency ) {
return 'US$';
}
return $symbol;
}
Two arguments must be declared to receive $currency. Omitting the count leaves it undefined and the comparison silently fails, which sends the callback down whichever branch happens to be the fallback. Returning $symbol unchanged for everything else is what keeps the rest of the store correct.
The currency code arrives as a three-letter ISO string in upper case. Comparing against a lower-case literal is a quiet way to make the condition never match.
What the filter does not touch
This is a display concern, and a narrow one:
- The currency the store trades in comes from the WooCommerce settings and is what gateways, taxes, reports and exported orders use. The symbol is decoration on top of it.
- The currency code in the order is stored per order. Changing a symbol today does not rewrite history, and old orders keep the code they were placed with — which is correct, and also means reports will not match a store whose symbol implies something else.
- The position of the symbol relative to the number is a separate setting, together with thousand and decimal separators. Attaching a space to the returned symbol to nudge it is a workaround that breaks the moment the position setting changes.
- The price markup as a whole, including sale price structure, belongs to displayed prices.
The failure that costs money
The tempting shortcut is to leave the store currency as it was and change only the symbol, because the settings page warns that changing currency affects existing data. What that produces is a store displaying one currency and charging in another.
Nothing about this is subtle once it reaches a customer: the checkout shows a familiar glyph, the card statement shows a different currency, and the amounts differ by whatever the exchange rate happened to be. The gateway is not at fault, and neither is the filter — it did exactly what it was asked.
If the store genuinely needs to trade in a different currency, change the currency setting and accept the consequences for historical data. If it needs to serve several currencies, that is a multi-currency problem requiring conversion, per-currency pricing and gateway support, not a one-line filter.
Legitimate uses
There are real reasons to reach for it:
- Disambiguating a shared symbol. Several currencies use the dollar sign. Returning
US$,CA$orA$for the relevant codes removes a genuine ambiguity for international shoppers. - Matching a house style where the brand has always written the symbol a particular way.
- Replacing an entity with a character when a symbol renders as mojibake because of an encoding problem further up the stack, though fixing the encoding is the better repair.
In each case the store currency and the symbol still describe the same thing, which is the test the failure above does not pass.
Verify in the places prices appear
A symbol shows up in more templates than the product page, and a callback that misfires will not do so uniformly:
- A product tile in a grid and the single product page.
- The cart and the checkout totals, including tax lines.
- An order confirmation email, which renders outside the usual page context — worth checking on a store that has had trouble with emails not sending.
- The order screen in the admin, where amounts for historical orders should still read correctly.
If the symbol is right everywhere but the amount looks wrong, the issue is formatting or the currency setting rather than this filter, and the starting point is the store's currency options rather than a snippet. If prices are missing altogether, that is a different failure and it begins at products not showing.
Frequently asked
- Because the callback returned a new symbol without checking the second argument. The filter runs once per currency, so a callback that ignores which currency it was handed rewrites all of them.
- No. Gateways read the currency code from settings, not the displayed symbol. Changing the glyph alone produces a store that shows one currency and bills another.
- Position is a separate setting, not part of the symbol. It is controlled by the currency position option and the price format filter, so returning a symbol with a space attached is not the way to move it.