dejwcake/craftable-translatable
Adds multilingual, translatable field support to the Craftable Laravel admin panel, letting you manage content in multiple languages with locale tabs and translation-aware form fields. Simple to install and configure for building multilingual admin interfaces.
Architecture fit. The package follows idiomatic Laravel extension patterns: a HasTranslations trait on Eloquent models, a Translatable service for locale resolution, and a TranslatableFormRequest that bridges translatable attributes into Laravel's validator. This is a low-coupling design — it composes with the framework rather than replacing it, so it slots into an existing Laravel codebase without imposing a new persistence or validation paradigm. For teams already storing per-locale content in JSON or side tables, the fit is high; for teams using a dedicated translation package or a CMS-managed i18n layer, this is duplicative.
Integration feasibility. High. 2.0.1 is a maintenance release with an explicit "nothing to do when upgrading" statement, no schema changes, no config changes, and no deprecations. Adoption effort is a composer update plus a regression run. The changes are internal hardening: closure parameter types, a defensive is_array() guard before array_search() in rules(), and container-based app resolution in getLocale().
Technical risk. Low, with two narrow caveats worth verifying rather than assuming:
HasTranslations::toArray(), Translatable::getLocales(), and TranslatableFormRequest is invisible to normal consumers, but any code that overrides these methods or subclasses the form request and re-declares the closures could hit a signature mismatch or a stricter type error under strict_types.is_array($rule) guard fixes a real defect: string rules (e.g. 'required|string|max:255') were previously passed into array_search() and would emit warnings or behave unpredictably. The fix changes runtime behavior for affected call sites — a correctness improvement, but validation output or error message shape may shift for teams that unknowingly depended on the buggy path.getLocale() resolving via the container instead of the app() helper is a testability and Octane-safety improvement; confirm it behaves identically in console commands and queued jobs where the container is present but the request lifecycle differs.Key questions.
TranslatableFormRequest or override toArray() / getLocales() / getLocale()? (Determines whether the typing changes are truly a no-op for us.)^2.0.rules() fix, and do we have tests asserting on those messages?Stack fit. Assumes a Laravel application on PHP 8.x with Eloquent models that hold translatable attributes. The package is additive: trait on the model, optional form request base class for validation, no migrations or service providers to wire beyond standard package discovery. Teams not using Form Requests get value from the model trait alone, so partial adoption is viable.
Migration path. Straightforward, staged:
^2.0 in composer.json and upgrade in a branch; run the full test suite plus any locale-specific feature tests.toArray(), getLocale(), getLocales(), and subclasses of TranslatableFormRequest; inspect each for closure-signature compatibility.rules() for string-form rules on translatable fields — this is the exact surface the guard touches — and confirm validation still passes and messages are unchanged.Compatibility. No breaking changes declared in 2.0.1. The upgrade is patch-level in semver terms, so a lockfile bump is sufficient once step 2 and 3 above are cleared. The one non-obvious incompatibility class is subclass overrides, which semver does not protect.
Sequencing. Do the 2.0.0 breaking-change review first (it gates the whole ^2.0 line), then this patch, then the validation-path audit, then queue/console smoke tests. If we are already on 2.0.0, steps 1–3 collapse into a single afternoon of work.
Maintenance. Minimal. The package is small, has no runtime infrastructure footprint, and this release adds no configuration surface. Ongoing cost is essentially dependency-bump hygiene plus periodic review of upstream releases. The visible CI investment — matrix across PostgreSQL and MariaDB, healthchecked docker-compose — is a positive signal that the maintainer is testing against realistic database backends rather than SQLite only, which matters if we rely on JSON column querying or collation-sensitive translation lookups.
Support. Single-maintainer risk is the primary concern for any package of this shape; the assessment should record maintainer responsiveness and release cadence as tracked metrics rather than assume them. Patch releases with clear "nothing to do when upgrading" notes are a good sign of disciplined release management. Our mitigation is a thin wrapper layer around the trait's public methods so that a fork or replacement is a contained change.
Scaling. No runtime scaling concerns — the trait resolves locales in-process and does not add queries beyond what the model already issues, provided we eager-load or constrain translation relations ourselves. Watch for N+1 patterns when rendering many translated models in a list view, and for JSON-column translation storage on very large tables where locale-filtered queries may not use indexes effectively on either PostgreSQL or MariaDB.
Failure modes.
getLocale() returning an unexpected locale in a job or console context could produce content in the wrong language without erroring. Detect via assertions in queue/console tests.rules() guard changes the code path for string rules; a mis-handled array/string boundary could make a translatable field fail validation in one locale only. Detect via per-locale form request tests.Ramp-up. Low. A Laravel-proficient engineer can be productive with the trait in under an hour and with the form request integration in half a day. The main onboarding item is documenting our internal convention for where translations live, how fallback is configured, and which locales the app must support — the package's README covers the API but not our operational rules, so that belongs in our own docs.
Recommendation. Adopt/upgrade to 2.0.1. Effort is small and the release is a net correctness improvement, particularly the rules() guard. Gate the upgrade on the two verification tasks above (subclass/override audit, string-rule validation audit) and on reviewing the 2.0.0 changelog if we have not already moved to the 2.x line.
How can I help you explore Laravel packages today?