Merx

Localization

Merx ships English and German. Everything a customer gets to see can be translated, and prices are formatted by the language’s locale.

Translating a shop means replacing its words. Localizing it means everything else that changes with where a customer is: how a number is written, which currency a price is in, how much tax is on it. A German customer and an Austrian one read the same German sentences, and still need different prices and different tax.

Merx keeps those apart, and they’re set in three different places:

  • Messages — error messages and Panel labels, from Merx’ translation strings. This is the translation part.
  • Number formatting — how 1045.12 becomes € 1,045.12 or 1.045,12 €, from the locale of the language.
  • Currency and tax — which currency a price is in and which rate applies, from the pricing rules and tax rules. Both usually follow the country rather than the language, so they have guides of their own.

Messages

Merx ships en and de. Every string is listed in the translations reference, together with what it says in both languages.

The error.merx.* strings are the ones your customers see: they’re the messages behind the errors the API returns when an item can’t be added or the checkout form doesn’t validate. The field.*, page.*, section.* and tab.* strings only appear in the Panel.

Overriding a string

Register the strings under extensions in your config, keyed by language code. This works on any site, with or without languages, and needs no plugin of its own.

/site/config/config.php
<?php

return [
  'extensions' => [
    'translations' => [
      'en' => [
        'error.merx.emptycart' => 'Your cart is empty.',
      ],
      'de' => [
        'error.merx.emptycart' => 'Dein Warenkorb ist leer.',
      ],
    ],
  ],
];

On a multi-language site you can also keep the strings in the language file, next to the language definition itself.

/site/languages/de.php
<?php

return [
  'code' => 'de',
  'name' => 'Deutsch',
  'locale' => 'de_DE',
  'translations' => [
    'error.merx.emptycart' => 'Dein Warenkorb ist leer.',
  ],
];

Pick one of the two. A string set in both places is taken from the language file, and anything you leave alone still comes from Merx.

Adding a language

Merx falls back to English for any language it does not ship. To add one, register the full set of keys the same way — the translations reference lists them all.

Number formatting

Merx::formatCurrency() and Merx::formatPercent() format by locale, not by currency. A price in euros is written €1,045.12 for an English visitor and 1.045,12 € for a German one. Everything that prints a price goes through them, including $price->toString() and $field->toFormattedPrice().

The locale comes from LC_NUMERIC. On a multi-language site Kirby sets it per language:

/site/languages/de.php
<?php

return [
  'code' => 'de',
  'locale' => 'de_DE',
];

On a single-language site set Kirby’s locale option instead.

/site/config/config.php
<?php

return [
  'locale' => 'de_DE',
];

Both methods also take a locale as an argument, for the cases where you need a different one than the visitor’s.

/site/templates/product.php
<?= Merx::formatCurrency(1045.12, 'EUR', 'de_DE') ?>

Currency and tax

Neither is taken from the language. The currency comes from the pricing rule that applies to the request and the rate from the tax rule of the product — and those rules are where you decide what a language, a country or anything else should mean for your prices.

Language in API requests

The API is not a page request, so Merx cannot infer the language from the URL. Send it with the request, either as an X-Language header or as a language query parameter.

script.js
const response = await fetch('/api/shop/cart', {
  headers: {
    'X-Language': document.documentElement.lang,
  },
});

Without it, Merx answers in Kirby’s fallback language (English) — which means a customer can end up with an error message in a language they aren’t browsing in.