blackstone/laravel-model-translations-sync

Synchronize Laravel model translations between models, language_lines, files and external translation flows.

Maintainers

Package info

github.com/blackstone/laravel-model-translations-sync

pkg:composer/blackstone/laravel-model-translations-sync

Statistics

Installs: 4

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

0.0.4 2026-03-12 09:29 UTC

This package is auto-updated.

Last update: 2026-03-12 09:30:17 UTC


README

Laravel package for synchronizing translations between:

  • translatable Eloquent model JSON attributes via spatie/laravel-translatable
  • language_lines via spatie/laravel-translation-loader
  • local Laravel lang files
  • external translation services such as Crowdin, Lokalise or custom project commands

Pipeline target:

Models -> DB (language_lines) -> external service -> DB -> Models -> Files

Requirements

  • PHP 8.3, 8.4+
  • Laravel 11 or 12
  • spatie/laravel-translatable
  • spatie/laravel-translation-loader

Installation

composer require blackstone/laravel-model-translations-sync

Publish config:

php artisan vendor:publish --tag=model-translations-config

Model setup

Add HasTranslations from Spatie and the package trait:

use Illuminate\Database\Eloquent\Model;
use Spatie\Translatable\HasTranslations;
use BlackstonePro\ModelTranslationsSync\Traits\ModelTranslatable;

class Product extends Model
{
    use HasTranslations;
    use ModelTranslatable;

    protected $fillable = ['title', 'description'];

    public array $translatable = ['title', 'description'];
}

Default behavior:

  • getModelTranslationNamespace() returns snake_case model basename
  • getTranslatableAttributesForSync() returns $this->translatable

You can override both methods in the model if needed.

Storage format in language_lines

Model translations are stored as:

  • group = models.{namespace}
  • key = {id}.{attribute}
  • text = {"en":"...", "fr":"..."}

Example:

group = models.product
key = 12.title
text = {"en":"iPhone","fr":"iPhone"}

Configuration

Published config file: config/model-translations.php

Main options:

  • models.auto_discover: discover models from configured paths
  • models.paths: directories for model discovery
  • models.list: explicit model class list
  • models.namespace_map: manual namespace to model mapping
  • locales: supported locales for sync
  • default_locale: fallback locale
  • ignore_groups: groups ignored during file export
  • export_path: destination for generated lang files
  • export.overwrite, export.pretty_print, export.sort_keys
  • sync.stop_on_error: stop or continue when a pipeline step fails
  • sync.pipeline: ordered list of artisan commands for the sync pipeline

Commands

Export models to language_lines:

php artisan translations:export-models {model?} {--fresh} {--chunk=500} {--dry-run}

Import model translations from language_lines:

php artisan translations:import-models {--locale=} {--group=} {--chunk=500} {--dry-run}

Export DB translations to local files:

php artisan translations:export-files {--dry-run}

Import local files back into DB:

php artisan translations:import-files {--dry-run}

Run the full pipeline:

php artisan translations:sync {--dry-run}

Pipeline is config-driven. Example:

'sync' => [
    'stop_on_error' => true,
    'pipeline' => [
        ['command' => 'translations:export-models', 'enabled' => true],
        ['command' => 'crowdin:upload', 'enabled' => true],
        ['command' => 'crowdin:download', 'enabled' => true],
        ['command' => 'translations:import-models', 'enabled' => true],
        ['command' => 'translations:export-files', 'enabled' => true],
    ],
],

translations:sync executes enabled commands in order, warns about missing or failing steps, and stops only when sync.stop_on_error is true.

Generating translatable JSON migrations

The package can generate a snapshot-based Laravel migration for safely converting existing scalar/text translatable columns into JSON columns without runtime model scanning inside the migration.

php artisan translations:make-translatable-migration convert_translatable_fields_to_json --locale=en

Available options:

php artisan translations:make-translatable-migration
    {name?}
    {--paths=*}
    {--locale=en}
    {--chunk=500}
    {--force}

The command:

  • discovers models using Spatie\Translatable\HasTranslations
  • filters only real DB columns
  • writes a fixed snapshot of models, tables, primary keys and translatable attributes
  • generates a self-contained migration in database/migrations

The generated migration:

  • creates __json_tmp columns in up()
  • wraps old scalar values as {"<locale>":"value"}
  • renames temp JSON columns back to the original names
  • restores values back to text columns in down() using the chosen locale

Source of truth

  • translations:export-models: models are source of truth
  • translations:import-models: language_lines is source of truth
  • translations:export-files: language_lines is source of truth
  • translations:import-files: lang files are source of truth

File formats

Supported export/import targets:

  • resources/lang/{locale}/*.php
  • resources/lang/{locale}.json
  • resources/lang/{locale}/models.php

Model translations in models.php are structured as:

return [
    'product' => [
        12 => [
            'title' => 'iPhone',
            'description' => 'Smartphone',
        ],
    ],
];

Testing

composer test