mathieutu/laravel-json-syncer
Keep JSON columns in sync with regular database columns in Laravel. Automatically mirror values stored in JSON attributes, such as translations, into dedicated columns so you can query, sort, and index them efficiently. Lightweight trait-based package with simple configuration.
mathieutu/laravel-json-syncer is a small artisan-command package aimed at one specific Laravel pain point: keeping the JSON translation files in resources/lang/*.json in sync with each other. If you use Laravel's JSON-based translations (__('Some sentence'), @lang('Some sentence')) rather than keyed PHP arrays, you quickly end up with en.json containing 300 keys and fr.json containing 240 — and no easy way to see or fill the gap.
Minimal steps:
composer require mathieutu/laravel-json-syncer — the service provider is auto-discovered on Laravel 5.5+, so no manual registration is needed. Release 1.3.0 adds support for Laravel 7, so the package constraint works on 5.5 → 7.x.php artisan vendor:publish and pick the json-syncer entry) if you want to change the reference locale, file paths, or output formatting. Defaults are usually fine.php artisan list | grep -i json to confirm the command signature registered by your installed version, then run the sync command once against your lang directory.git diff resources/lang and review what it did. Treat the first run as a discovery exercise — it will show you exactly how far out of sync your locales are.First real use case: you maintain en.json as the source of truth, a translator or a feature branch adds five new strings, and you want every other locale file to gain those five keys (with placeholder values) before the PR merges. That is the workflow this package exists for.
Reference-locale-first, generate the rest. Pick one locale as canonical (usually en, or whatever config/app.php's fallback_locale is) and treat every other JSON file as derived. Developers only ever add keys to the reference file; the sync command backfills everything else. This keeps merge conflicts confined to one file.
Pre-commit / CI parity gate. The highest-value pattern is enforcement rather than manual runs. Add the sync command to a composer script or a git pre-commit hook, then in CI run it and assert the working tree is clean:
php artisan <json-syncer-command>
git diff --exit-code resources/lang || (echo "Translation files out of sync" && exit 1)
This turns "someone forgot to update fr.json" from a review comment into a red build.
Post-merge cleanup. Run the sync immediately after merging a feature branch that touched the lang directory, before starting anything else. JSON files conflict badly; syncing early collapses most of the divergence while it's still small.
Pair it with key-extraction. The package reconciles files with each other — it does not know which keys your code actually uses. Run your extraction/lint tooling (or a grep for __(') first to get new keys into the reference file, then sync to fan them out. Order matters: extract → sync → commit.
Separate translation delivery from deploys. If translations are edited by non-developers (Crowdin, Lokalise, a CMS), decide which side owns the files. Either machines sync on every build and humans edit upstream, or humans edit the JSON directly and sync runs locally. Mixing both on the same files produces churn and lost strings.
Framework upgrade hygiene. 1.3.0 is a pure compatibility release (Laravel 7 + Travis config), so the integration story is unchanged. When bumping Laravel, check the package's constraint before upgrading — this package is maintained by a single author, and framework-support releases have historically lagged. If you're on Laravel 8+, verify support rather than assuming it, and keep a thin fallback plan (see below).
jq . resources/lang/xx.json before debugging anything else.fr string means users see a blank label rather than the English text.lang/ directory (some .php, some .json), the syncer only touches the JSON side — don't expect it to reconcile the two.resources/lang is often writable in local dev but read-only in containerised or CI environments. A sync that silently no-ops in CI defeats the parity gate.-v for the underlying exception, check that your lang path actually resolves (Laravel 7+ still uses resources/lang; older/newer versions have moved this), and confirm the command is discovering the same files you see in git ls-files resources/lang.Extension points. The command is the interface: wrap it in your own artisan command or composer script if you need custom pre/post-processing (extraction, sort normalisation, notifying a translation service). Because the package is a single-maintainer project and its scope is deliberately narrow, the safest posture for a production app is: pin the version, keep the integration behind one composer script or CI step, and if support ever stalls on a future Laravel release, replacing it with a ~30-line custom artisan command that merges JSON keysets is trivial. Design your workflow so that swap is cheap.
How can I help you explore Laravel packages today?