Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Translations Sync Laravel Package

van-ons/laravel-translations-sync

Keep Laravel translation files in sync across locales. This package helps compare language files, spot missing or outdated keys, and manage translations so every locale stays consistent as your app grows.

View on GitHub
Deep Wiki
Context7

Getting Started

Check the platform requirements first (changed in v1.0.0)

v1.0.0 raised the floor to PHP 8.2 and Laravel 11.0. Composer will refuse to install on Laravel 10 / PHP 8.1, so if your app hasn't crossed that line yet, you stay on the 0.x line — pin it explicitly and don't expect fixes there.

composer require van-ons/laravel-translations-sync:^1.0
php artisan vendor:publish --tag="translations-sync-config"

The config file is the single most important thing to read first — it defines the provider driver, credentials, the local lang path, and the pull/push direction defaults. Read it top to bottom before running anything.

Discover the surface area

php artisan list translations
php artisan help translations:sync

You are looking for a small set of commands (typically a sync, and often separate pull/push verbs). If the command signature exposes --dry-run, use it. If it doesn't, back up lang/ (it is in git, so git status is your safety net) before the first run.

Set credentials via env, never the config file

TRANSLATIONS_SYNC_PROVIDER=api
TRANSLATIONS_SYNC_KEY=...
TRANSLATIONS_SYNC_PROJECT=...

If the package's config file doesn't read from env() for a given value, add the env() call yourself and keep the published config committed with safe defaults.

First use case: a pull before you start translating

The lowest-risk first run is a read-only pull against a throwaway branch:

git checkout -b translations/initial-pull
php artisan translations:sync --dry-run   # if supported
php artisan translations:sync --pull
git diff --stat lang/

You want to see new files appear under lang/{locale}/ and, ideally, an untouched working tree if the provider is already in sync. That diff is your proof the wiring works before you let it anywhere near a deploy.

Note on v1.0.0's changelog: the release is dominated by "Prepare for v1", CI workflow plumbing, and repo housekeeping (label enforcement, label sync). Those workflow entries are repository automation, not new artisan commands or config keys — don't go hunting for a translations:label verb. Expect the same command surface you had in 0.x.

Implementation Patterns

Pull-on-deploy, push-on-merge

The cleanest split is directional. Pulling on deploy means the running app always has the current copy; pushing on merge means translators get new keys as soon as developers add them.

# deploy script, after `composer install`, before `config:cache`
php artisan translations:sync --pull --force
# .github/workflows/translations.yml
on:
  push:
    branches: [main]
    paths: ['lang/**']
jobs:
  push:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: shivammathur/setup-php@v2
        with:
          php-version: '8.2'   # v1 requires >= 8.2
      - run: composer install --no-interaction --prefer-dist
      - run: php artisan translations:sync --push
        env:
          TRANSLATIONS_SYNC_KEY: ${{ secrets.TRANSLATIONS_SYNC_KEY }}

Pin the CI PHP version explicitly. A runner defaulting to 8.1 will fail to resolve the package now, and the error surfaces as a confusing "your requirements could not be resolved" rather than "you need PHP 8.2".

Scheduled pull for long-lived environments

On Laravel 11+ (now the only supported target) scheduling lives in routes/console.php — app/Console/Kernel.php no longer exists in a default app:

// routes/console.php
Schedule::command('translations:sync --pull')->hourly()->withoutOverlapping();

Remember to clear the translation cache afterwards if you cache compiled translations.

Treat the provider as a pluggable contract

Most packages of this shape ship a driver/contract abstraction. Bind your own implementation in a service provider when you need an internal API, a database table, or a fake:

// app/Providers/AppServiceProvider.php
public function register(): void
{
    $this->app->bind(
        \VanOns\TranslationsSync\Contracts\Provider::class,
        \App\Translations\InternalProvider::class,
    );
}

This is also the seam for tests: implement a FakeProvider returning a fixed array so your test suite never touches the network.

Scope the sync per locale or per group

If the commands expose --locale= and --group=, use them for incremental work rather than full syncs. A translator fixing de/auth.php shouldn't trigger a rewrite of every JSON file.

php artisan translations:sync --pull --locale=de --group=auth

Keep local edits upstream

The pattern that ages best: developers never hand-edit lang/{locale}/*.php for anything except the base locale (usually en). Everything else is owned by the provider. This avoids the merge conflicts that make translation sync painful, and gives you one canonical source per key.

Use the diff as a review artifact

In PRs that touch translations, run a dry-run sync and attach the diff. Reviewers can then see whether a key was renamed upstream or genuinely added, which is otherwise invisible in a 400-line PHP array diff.

Gotchas and Tips

The 0.x → 1.0 upgrade is a platform bump, not a config bump — but verify both. The declared breaking changes are PHP 8.2 / Laravel 11.0. The "Prepare for v1" commit, however, is exactly the kind of catch-all where a default flips or a config key is renamed. Before upgrading a working install, diff the published config against the old one (git diff config/translations-sync.php) and re-read the overwrite/merge options. Don't assume the changelog enumerated every behavioural change.

Overwrite semantics are the whole ballgame. The most common failure is a developer's local edit being silently clobbered by a pull, or a pull being skipped because the file exists. Find the config option controlling overwrite/merge and decide explicitly per environment: --force in CI, conservative locally. Never run a push and a pull with the same flags in the same command unless you understand which side wins.

Git churn from key ordering. Packages that rewrite whole arrays often re-sort or re-format keys, producing diffs that touch every line. If this happens, either configure a stable sort order or add lang/ to a .gitattributes linguist-generated entry — and consider whether the generated files should be committed at all in your deploy model.

JSON vs. PHP groups. Laravel has two translation storage styles: lang/{locale}.json for the __('Literal string') form, and lang/{locale}/{group}.php for __('group.key'). A sync tool may handle one, both, or mishandle the boundary. Verify a JSON-only key and a grouped key both round-trip before trusting it. Key collisions between the two styles are invisible until runtime.

Pluralization and placeholders survive or they don't. Check that :attribute, :count, {0} ... |[1,*] ... plural pipes, and @choice-style ranges come back byte-identical. Some providers normalize or strip pipes, and the corruption only shows up as a wrong plural form in production. Write one test that asserts on a pluralized string after a sync.

Empty strings vs. missing keys. A provider returning "" is not the same as omitting the key — Laravel's fallback locale behaves differently in each case. If your provider can return empty values for untranslated strings, decide whether to filter them out during the pull, otherwise you'll shadow your fallback_locale and show blanks to users.

Clear caches after syncing. php artisan config:cache bakes config in; if your sync reads credentials from config, caching before the sync will use stale values. In deploy pipelines the order is: pull translations → config:cache → route:cache → view:cache → optimize:clear-style event/translation cache flush if you cache strings. On Laravel 11 config:cache is still the step that will bite you.

Credential and rate-limit hygiene. Sync commands that hit a remote API need timeouts and retries. Wrap the scheduled job so a provider outage doesn't fail the deploy — a failed pull should be a warning, not a rollback, unless you explicitly require the latest strings. Keep the key out of config/translations-sync.php and out of anything that gets dumped by artisan about.

Version pinning: ^1.0, and mean it. The package is now past 1.0, so semver is a promise you can lean on for the first time — no more "a minor bump flipped the default". Pin ^1.0, prefer composer update --with-dependencies van-ons/laravel-translations-sync as a deliberate step, and read the changelog. If you vendor the provider contract (the only interface you really use), your exit route stays cheap.

Extension points worth knowing. Beyond the provider binding, look for events dispatched around sync (SyncStarted/SyncCompleted style) and any filter/hook config. Those are where you'd add audit logging (who synced what, when) or a Slack notification after a push, without forking. If v1.0.0 introduced none, keep the provider binding as your sole integration seam and wrap the command in your own artisan command if you need pre/post behaviour.

Debugging a sync that "did nothing". The usual suspects, in order: cached config, a lang path that resolves differently than you think (lang_path() vs. a hardcoded path — relevant if you've customized it), a locale that isn't in the configured list, or the command silently skipping existing files. Run with -v and check storage/logs/laravel.log for the package's own channel; many packages log at debug level only.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo