NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages

The published config/translatable.php in full:

use RoundlyConsulting\Translatable\Enums\FallbackMode;

return [
    // Locale used when a requested locale has no value (FallbackMode::Fallback / Any, step 2).
    'fallback_locale' => env('TRANSLATABLE_FALLBACK_LOCALE', config('app.fallback_locale', 'en')),

    // How far the fallback chain reaches. none = exact only; fallback = exact -> fallback
    // locale; any = exact -> fallback locale -> first available (content never renders blank).
    // NOTE: `any` can surface a value from another locale when the requested + fallback
    // locales are empty — a cross-locale disclosure. See "Fallback modes".
    // Anything other than none/fallback/any throws rather than silently widening to `any`.
    'fallback' => env('TRANSLATABLE_FALLBACK', FallbackMode::Any),

    // Reject any locale key not in the supported list on writes (malformed keys are always
    // rejected regardless). Off by default, so any well-formed locale is accepted.
    // Env words are read as booleans (on/off, yes/no, true/false, 1/0); anything else throws.
    'strict_locales' => env('TRANSLATABLE_STRICT_LOCALES', false),

    // Default supported locales. Hosts SHOULD rebind SupportedLocales to their own source.
    'locales' => ['en', 'sk'],
];

Every key

KeyDefaultEnvPurpose
fallback_localeapp.fallback_locale, else enTRANSLATABLE_FALLBACK_LOCALELocale tried after the exact one. Not set — null or blank (TRANSLATABLE_FALLBACK_LOCALE=) — means its default: no fallback locale; anything else must be a well-formed locale key (en, pt-BR) or it throws the toolkit’s InvalidConfigurationException.
fallbackFallbackMode::AnyTRANSLATABLE_FALLBACKHow far the fallback chain reaches: none, fallback or any. A blank value is not set (any); any other value throws the toolkit’s InvalidConfigurationException on the first read.
strict_localesfalseTRANSLATABLE_STRICT_LOCALESReject writes for well-formed locales outside the supported list. The env value is read as a boolean: on/off, yes/no, true/false, 1/0. A blank value is not set (false); any other value throws the toolkit’s InvalidConfigurationException on the first write.
locales['en', 'sk']—Default supported locales, read by the default SupportedLocales binding. Must be a list of well-formed locale keys; a non-list or a bad entry throws the toolkit’s InvalidConfigurationException naming it.

Environment

Every key except locales is env-backed, so most apps never publish the config at all:

TRANSLATABLE_FALLBACK_LOCALE=en
TRANSLATABLE_FALLBACK=fallback      # none | fallback | any
TRANSLATABLE_STRICT_LOCALES=on      # on/off, yes/no, true/false, 1/0

The env values are handed over raw and read strictly: TRANSLATABLE_FALLBACK must be none, fallback or any, and TRANSLATABLE_STRICT_LOCALES one of on/off, yes/no, true/false, 1/0. A typo throws InvalidConfigurationException naming the key — it never silently widens to any or reads as off. A blank value is not set: TRANSLATABLE_FALLBACK= reads as any, TRANSLATABLE_STRICT_LOCALES= as off, and TRANSLATABLE_FALLBACK_LOCALE= means no fallback locale.

One source of truth for locales

The supported locales drive Translatable::supported(), isSupported(), fromInput(), rules() and search(), the model’s missingLocales(), strict mode and — through sluggable — per-locale slugs. The default binding (ConfigSupportedLocales) reads translatable.locales; to wrap a single source of truth instead, bind the SupportedLocales contract in your own service provider:

use RoundlyConsulting\Translatable\Contracts\SupportedLocales;

$this->app->bind(SupportedLocales::class, fn () => new class implements SupportedLocales {
    /** @return list<string> */
    public function supported(): array
    {
        return App\Localization\Locale::SUPPORTED;
    }
});

Translatable also binds sluggable’s SlugLocales contract to TranslatableSlugLocales — an adapter over the same SupportedLocales, translatable.fallback_locale and the app locale — so slugs are generated, indexed and bound for exactly the locales your translations use. Sluggable registers its own default with bindIf(), so provider order doesn’t matter; your own SlugLocales binding in a later provider wins over both. Slug options (separator, word cap, reserved words) live in config/sluggable.php.

Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

More ways to support, including crypto

By donating, you agree to our donation terms.

Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.