WooCommerce Guide

Deep, performant integration with WooCommerce - translates products, categories, attributes, and emails. Syncs inventory across language variants. Supports per-language currencies.

How It Works

The WooCommerce addon activates automatically when WooCommerce is installed and active. No manual registration is needed.

What Gets Translated

ContentTranslated
Product title + contentYes
Product short description (excerpt)Yes
Product purchase note (_purchase_note)Yes
External product button text (_button_text)Yes
Variation description (_variation_description)Yes
Product categories (product_cat)Yes
Product tags (product_tag)Yes
Product attributes (pa_*)Yes (Dynamic)
Product attribute valuesYes
Product image alt textYes (Via Media Translation)
Product gallery alt textYes (Via Media Translation)

What Stays Shared (Not Translated)

FieldWhy Shared
Stock / Stock StatusSame physical product
SKUUnique identifier across all languages
Price / Sale PriceKept in sync; use currency table for conversion
Weight / DimensionsPhysical product properties
Virtual / DownloadableProduct nature, not language-dependent
Total Sales counterAggregated across all languages

Orders

shop_order is not a translatable post type. Orders are language-tagged using the _perflocale_language meta key (set when the order is placed) but are never duplicated per language. This ensures inventory and reporting remain accurate.

Settings

Located at PerfLocale → Settings → WooCommerce (tab only visible when WooCommerce is active).

Inventory Sync

Keep stock, SKU, and pricing identical across all language variants of a product. Fires on woocommerce_process_product_meta after each product save.

Order Email Language

Send order confirmation, processing, completed, and other status emails in the language the customer used when placing the order. The language is stored on the order as _perflocale_language meta.

Email Subject & Heading Translation

WooCommerce email subjects, headings, and additional content are registered as translatable strings when you click Scan for Strings or save WC email settings. They appear on the PerfLocale Strings page under domain woocommerce with contexts like email_subject_customer_processing_order.

When the source text changes (e.g., you edit the subject in WooCommerce → Settings → Emails), PerfLocale migrates the old translation to the new string with a Needs Update status so translators know to review it. The old translation text is preserved as a starting point.

Custom email types can be added via the perflocale/woocommerce/translatable_email_ids filter (see Hooks documentation).

Translating Email Body Templates

Required: install the WordPress and WooCommerce language packs for every target language, otherwise email body templates silently fall back to English.

PerfLocale translates email subject and heading through the PerfLocale Strings system (your own translations, entered on the Strings page). The email body template is rendered by WooCommerce itself using standard __() / _e() calls. PerfLocale switches the WordPress locale before the body renders (via switch_to_locale() on woocommerce_email_header), so WooCommerce’s own translation files take over for the body text.

However, switch_to_locale() only has an effect if the corresponding locale’s .mo files are actually installed on the server. If they’re missing, WooCommerce falls back to English - the body will appear untranslated even though the subject and heading are correctly in the target language.

Install language packs from the WordPress admin via Settings → General → Site Language (WordPress core) and WooCommerce → Status → Tools, or via WP-CLI:

# Install WordPress core + all active plugins' language packs for German and French.
wp language core install de_DE fr_FR
wp language plugin install --all de_DE fr_FR

# For a specific plugin only:
wp language plugin install woocommerce de_DE fr_FR

How to verify on your site: wp language core list --status=installed should show every locale you target with PerfLocale. If a language is listed in PerfLocale but missing from that output, order emails in that language will render subjects correctly but bodies will appear in English.

This is also true for the Workflow Notifier emails sent to translators - the subject uses your template verbatim, but the rendered status labels (Assigned, In progress, Review, Approved) come from PerfLocale’s own text domain and will only appear translated when the PerfLocale .mo files for that locale are present.

Per-Language Currency

Display product prices converted to a language-specific currency. Configure the currency code and exchange rate per language. Prices are converted at display time - all transactions use WooCommerce's default base currency.

Automatic Exchange Rate Sync

Fetch live exchange rates from a configurable API provider on a scheduled interval. Supports Frankfurter (ECB), ExchangeRate-API, Open Exchange Rates, CurrencyFreaks, and Fixer.io. Custom providers can be added via the perflocale/woocommerce/exchange_rate_providers filter. See Exchange Rates documentation for full details.

Product Attributes

All registered WooCommerce attributes (pa_* taxonomies) are automatically discovered and registered as translatable. New attributes created in WooCommerce → Attributes are included without any configuration.

URLs

All WooCommerce pages include the language prefix automatically:

  • /de/shop/ - product archive
  • /de/cart/ - cart
  • /de/checkout/ - checkout
  • /de/my-account/ - account
  • /de/my-account/orders/ - order history
  • /de/my-account/downloads/ - downloads

Developer Hooks

Filters

perflocale/woocommerce/synced_product_fields

Control which meta keys are synced across language variants.

add_filter( 'perflocale/woocommerce/synced_product_fields', function ( array $fields, int $product_id ): array {
	// Add a custom field to the sync list.
	$fields[] = '_my_custom_field';

	// Or remove a field if you want per-language pricing.
	$fields = array_diff( $fields, [ '_price', '_regular_price', '_sale_price' ] );

	return $fields;
}, 10, 2 );

Parameters:

  • array $fields - Default list of meta keys.
  • int $product_id - ID of the product being saved.

perflocale/woocommerce/skip_inventory_sync

Prevent inventory sync for a specific product.

add_filter( 'perflocale/woocommerce/skip_inventory_sync', function ( bool $skip, int $product_id ): bool {
	// Skip sync for products in a specific category.
	if ( has_term( 'digital', 'product_cat', $product_id ) ) {
		return true;
	}
	return $skip;
}, 10, 2 );

perflocale/woocommerce/translate_string

Override translation for a WooCommerce gateway title, description, or shipping label.

add_filter( 'perflocale/woocommerce/translate_string', function ( ?string $translated, string $text, string $slug, string $context ): ?string {
	if ( $slug === 'de' && $text === 'Direct bank transfer' ) {
		return 'Direktüberweisung';
	}
	return $translated; // Return null to fall through to default lookup.
}, 10, 4 );

Parameters:

  • string|null $translated - Null (no override yet) or a translated string.
  • string $text - Original text.
  • string $slug - Current language slug.
  • string $context - Gateway ID or empty string.

perflocale/woocommerce/email_translation_enabled

Disable email language switching for a specific order.

add_filter( 'perflocale/woocommerce/email_translation_enabled', function ( bool $enabled, int $order_id ): bool {
	// Always send admin notifications in site default language.
	return $enabled;
}, 10, 2 );

Actions

perflocale/woocommerce/inventory_synced

Fires after inventory sync completes successfully.

add_action( 'perflocale/woocommerce/inventory_synced', function ( int $product_id, array $synced_ids, array $fields ): void {
	error_log( "Synced product #{$product_id} to: " . implode( ', ', $synced_ids ) );
}, 10, 3 );

Parameters:

  • int $product_id - Source product that was saved.
  • int[] $synced_ids - Language variant IDs that received the update.
  • string[] $fields - Meta keys that were synced.

Behavior Notes

  • No double-selling: Inventory sync runs at priority 100 on woocommerce_process_product_meta, after WooCommerce has saved all product data.
  • Recursion guard: InventorySync tracks in-progress syncs in a static array to prevent circular updates between language variants.
  • Locale switching: LanguageRouter::filter_locale() overrides the WordPress locale to match the detected language. All WooCommerce UI strings using __() are automatically served in the correct language.
  • Cache: WooCommerce product transients are cleared for each sibling after sync (wc_delete_product_transients).
  • REST API: Inventory sync also fires on woocommerce_rest_insert_product_object so bulk edits via the REST API are covered.