Inspected version: 0.1.0 (private) · Part of the veap-repo monorepo (packages/veap-translatable).
@veap/translatable adds multilingual field storage to any @veap/database model. Instead of a translation table per model, it stores all translations for all models in one polymorphic translations table reached through a morphMany relation.
Entry points#
The manifest exports ., ./migrations, ./ui and ./package.json.
Locale management#
A process-global locale plus an AsyncLocalStorage scope:
| Export | Behavior |
|---|---|
getLocale() | Current locale; the AsyncLocalStorage store wins, otherwise the global current locale. |
setLocale(locale) | Sets the global current locale. |
getDefaultLocale() / setDefaultLocale(locale) | The default locale (initial value "en"). |
getFallbackLocale() / setFallbackLocale(locale) | The fallback locale used when a requested translation is missing (initial value "en"). |
withLocale(locale, callback) | Runs callback with locale scoped through AsyncLocalStorage; returns its result. |
The locale state is global, not request-scoped, unless you wrap work in withLocale().
Models and mixins#
Translation— thetranslationsmodel (static table = "translations"), withtranslatable()(morphTo) and date casts. Registered inMorphMapastranslation/translations.TranslatableModel— a base class extendingModelthat overridesquery()to returnTranslatableQueryBuilderandgetAttribute/setAttribute/toJSONto route translatable fields through the translation store.Translatable(Base)— a mixin adding the same behavior to a class that already extends another base.ITranslatableModel/TranslatableClass— the typing helpers for both forms.
Declare translatable fields on the class with static translatable = ["title", "slug", "content"].
Instance API#
| Method | Behavior |
|---|---|
translations() | The morphMany(Translation, "translatable") relation. |
getTranslation(field, locale?, fallback = true) | Value for a locale, falling back to the fallback locale, default locale, then the physical column. |
setTranslation(field, locale, value) | Stages a translation (a null/undefined value deletes it on save). |
getTranslations(field?) | One field as { locale: value }, or all fields as { field: { locale: value } }. |
setTranslations(field, { locale: value }) | Stages several locales for a field. |
fillTranslations(map) | Accepts either { field: { locale: value } } or { locale: { field: value } }. |
loadTranslations(locales?) | Loads translations from the database into the in-memory cache and relation. |
saveTranslations() / deleteTranslations() | Persist or remove staged/owned translations. |
getAttribute / setAttribute / toJSON | Overridden so translatable fields behave like normal properties. |
setAttribute also accepts an object to set several locales at once (post.title = { pl, en }). For non-object values it stages the value for the active locale.
Lifecycle: saved() calls saveTranslations() and deleted() calls deleteTranslations().
Query builder#
TranslatableQueryBuilder extends ModelQueryBuilder and eager-loads translations by default. Methods:
whereTranslation(key, value, locale?),orWhereTranslation(...)whereTranslationLike(key, pattern, locale?),orWhereTranslationLike(...)whereTranslationIn(key, values, locale?)withTranslations()/withoutTranslations()orderByTranslation(key, direction?, locale?)— orders by a correlated subquery against thetranslationstable.
Migrations#
The /migrations entry point exports a migration that creates the translations table (translatable_type, translatable_id, locale, key, value, timestamps), a unique constraint on (translatable_type, translatable_id, locale, key) and two lookup indexes. The down() intentionally does not drop the shared table because several packages share it.
UI (./ui)#
TranslatableField— a single field with a locale switcher (value/onChangeoperate onRecord<locale, string>).TranslatableFormCard— a card of translatable fields keyed by locale ({ locale: { field: value } }), with per-locale completion tracking.TranslatableTabs— the locale tab strip (locales,activeLocale,onChange,completedMap).
TranslatableField and TranslatableTabs default to ["pl", "en"], while the locale manager defaults to "en"; set the locale explicitly when the two must agree.