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.
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
| Helper | Returns | Escaping | Use for |
|---|---|---|---|
flavor_t($key, $default) | The resolved string | None — raw value | Building 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:
- 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.
- Active language pack — when the store's
frontend_languageoption is noten(e.g. the bundled Greek pack), the pack's value for that key. - Registered default — the English default supplied at registration time.
- The
$defaultargument you passed toflavor_t(). - The key itself — if nothing above matched, the raw key string is returned (so a missing key is visible, never fatal).
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
flavor_register_string() does not existThere 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 usestheme.*andshop.*; bundled modules usemodule.<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.
| Method | Signature | Purpose |
|---|---|---|
register | register(string $key, string $default, string $context = ''): void | Register one string. |
registerMany | registerMany(array $strings): void | Register a batch (key => default or key => ['default'=>…,'context'=>…]). |
get | get(string $key, ?string $default = null): string | Resolve a key (backs flavor_t()). |
registerLanguagePack | registerLanguagePack(string $langCode, array $translations): void | Register a full language pack. |
getForJs | getForJs(?string $domain = null): array | Resolved key => value map for the frontend (backs flavorTranslations). |
getAll | getAll(?string $domain = null): array | All registered strings, optionally filtered by domain prefix. |
getGrouped | getGrouped(): array | Registered strings grouped by first key segment. |
getAvailableLanguages | getAvailableLanguages(): array | ['en', …registered pack codes]. |
getLanguagePack | getLanguagePack(string $langCode): array | The raw pack map for a language. |
getUserTranslations | getUserTranslations(): array | The 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
- Translation System — the store-owner admin guide for editing these strings.
- Theme Hooks Reference → Translatable Strings — the
flavor_register_translationsaction in the hooks catalogue. - Hooks Overview — how theme actions/filters fit together.
- Creating a Module — registering strings from within a module.