Skip to main content

Translation System (Developer API)

The Flavor Starter theme ships its own translation registry — a single in-memory string table, backed by the theme's own database options and resolved at request time. It is independent of WordPress i18n: there are no .pot / .po / .mo files, no load_theme_textdomain(), and no gettext. Every translatable label in the theme, storefront, and modules is a dot-namespaced key that resolves to English by default and can be overridden per-string from the admin (Appearance → Flavor Options → Language) or supplied by a language pack.

This page is the developer contract for that system: how to read strings, how to register your own strings from a plugin, child theme, or module, and the naming conventions the registry expects.

Requires

Flavor Starter theme 7.4.0+. The registry lives in inc/translations.php; the bundled English defaults live in inc/translations/english-strings.php.

For the store-owner side (the admin UI that edits these strings), see the customer guide: Translation System.


Reading strings

Two global helpers read a resolved string. Use these instead of WordPress __() / _e() — the theme's strings are not registered with gettext and __() will not find them.

// Return the resolved string
$label = flavor_t('theme.header.cart'); // "Cart"

// Echo the resolved string, HTML-escaped
flavor_te('theme.header.cart'); // outputs: Cart

Signatures

flavor_t(string $key, ?string $default = null): string
flavor_te(string $key, ?string $default = null): void
HelperReturnsEscapingUse for
flavor_t($key, $default)The resolved stringNone — raw valueBuilding strings, attributes, values you escape yourself
flavor_te($key, $default)void (echoes)Wraps output in esc_html()Printing a label directly into HTML text

flavor_te() is exactly echo esc_html(flavor_t($key, $default)). If you need to output markup (e.g. a string that legitimately contains HTML), use echo flavor_t(...) and apply the escaping appropriate to your context.

Resolution order

flavor_t() resolves a key in this priority:

  1. User override — a genuine custom value saved from the Language admin screen (stored in the theme's own options table). Overrides that merely echo an inactive language pack are ignored when the frontend language is English.
  2. Active language pack — when the store's frontend_language option is not en (e.g. the bundled Greek pack), the pack's value for that key.
  3. Registered default — the English default supplied at registration time.
  4. The $default argument you passed to flavor_t().
  5. The key itself — if nothing above matched, the raw key string is returned (so a missing key is visible, never fatal).
Placeholders are not interpolated

The registry returns the stored string verbatim. Values may contain sprintf placeholders (%s, %1$s) or tokens like {year} — substituting them is the caller's responsibility:

$msg = sprintf(flavor_t('module.points_rewards.earn_per_review'), 10);

Registering your own strings

Register strings on the flavor_register_translations action. It fires once, on init (priority 5), immediately after the theme's own English defaults are loaded — so your keys join the same registry the theme and storefront use.

add_action('flavor_register_translations', function () {
// Batch register (recommended)
Flavor_Translation_Registry::registerMany([
'my_plugin.welcome' => 'Welcome to our store',
'my_plugin.cta' => 'Shop now',
]);

// …or a single string, with optional translator context
Flavor_Translation_Registry::register(
'my_plugin.badge', // key
'New', // English default
'Product badge label' // context shown to translators (optional)
);
});

Once registered, read them anywhere with flavor_t() / flavor_te():

echo flavor_t('my_plugin.welcome'); // returns the (possibly translated) string
flavor_te('my_plugin.cta'); // echoes it, HTML-escaped

registerMany() accepts two value shapes

Flavor_Translation_Registry::registerMany([
// 1. key => default
'my_plugin.welcome' => 'Welcome to our store',

// 2. key => ['default' => ..., 'context' => ...] (context optional)
'my_plugin.badge' => ['default' => 'New', 'context' => 'Product badge label'],
]);

Signatures

Flavor_Translation_Registry::register(string $key, string $default, string $context = ''): void
Flavor_Translation_Registry::registerMany(array $strings): void
caution
flavor_register_string() does not exist

There is no flavor_register_string() function anywhere in the theme. If you have seen it referenced, it is a myth — the calls will silently fatal. The only registration path is Flavor_Translation_Registry::register() / ::registerMany() inside a flavor_register_translations callback.


Key naming conventions

Keys are dot-namespaced, lowercase, structured as domain.area.item:

theme.header.cart
theme.footer.copyright
module.points_rewards.points_history
my_plugin.checkout.trust_badge
  • First segment = domain group. It buckets the string in the Language admin screen and in Flavor_Translation_Registry::getGrouped(). The theme uses theme.* and shop.*; bundled modules use module.<slug>.*. For an extension, use a stable vendor/plugin prefix (e.g. my_plugin.*) so your strings group together and never collide with core keys.
  • The theme's own English defaults are defined in inc/translations/english-strings.php (grouped by area) — a good reference for the naming style.
  • Keys should be stable identifiers, not English sentences. The English text is the value, not the key, so wording can change without breaking overrides.

Strings in JavaScript

On the frontend only, all resolved strings are localized to a global JS object named flavorTranslations (a flat { key: value } map), attached to the flavor-app script:

const strings = window.flavorTranslations || {};
const label = strings['theme.header.cart'] || 'Cart';

The map is already language-resolved (user override → language pack → default), so no client-side lookup logic is required. It is not available in wp-admin.


Language packs (advanced)

A language pack is a full key => translated-value map registered for a language code. The theme bundles a Greek pack, registered on init priority 6 (one step after string registration at priority 5) from inc/translations/greek-pack.php:

add_action('init', function () {
Flavor_Translation_Registry::registerLanguagePack('el', [
'theme.header.cart' => 'Καλάθι',
// …
]);
}, 6); // after string registration (priority 5)

Signature:

Flavor_Translation_Registry::registerLanguagePack(string $langCode, array $translations): void

Which pack is active is driven by the theme's frontend_language option (en | el, defaulting to en). When it is not en, matching pack values take priority over registered defaults but below genuine user overrides (see resolution order above). Register a custom pack on init at priority 6 or later so it lands after the core string registration.


Registry API reference

Flavor_Translation_Registry (in inc/translations.php) exposes these public static methods. Registration and reading (above) cover the common cases; the rest are used by the admin Language screen and the JS localizer.

MethodSignaturePurpose
registerregister(string $key, string $default, string $context = ''): voidRegister one string.
registerManyregisterMany(array $strings): voidRegister a batch (key => default or key => ['default'=>…,'context'=>…]).
getget(string $key, ?string $default = null): stringResolve a key (backs flavor_t()).
registerLanguagePackregisterLanguagePack(string $langCode, array $translations): voidRegister a full language pack.
getForJsgetForJs(?string $domain = null): arrayResolved key => value map for the frontend (backs flavorTranslations).
getAllgetAll(?string $domain = null): arrayAll registered strings, optionally filtered by domain prefix.
getGroupedgetGrouped(): arrayRegistered strings grouped by first key segment.
getAvailableLanguagesgetAvailableLanguages(): array['en', …registered pack codes].
getLanguagePackgetLanguagePack(string $langCode): arrayThe raw pack map for a language.
getUserTranslationsgetUserTranslations(): arrayThe saved user overrides.

Prefer the flavor_t() / flavor_te() helpers in templates and module code; reach for the class directly only when you need registration or bulk introspection.


Storage & data sovereignty

User overrides are persisted through the theme's own option helper (flavor_set_option()) into the theme's dedicated options table — never wp_options. Registered defaults and language packs are in-memory only (rebuilt each request from PHP). The registry filters out any "override" that merely equals a language-pack value or the English default, so only genuine custom strings are stored. See Data Sovereignty for the underlying rule.


See also