Search by

smartness / translation-client

smartness

Laravel package to sync translations with SmartPMS Translation Manager

Package info

github.com/smartpricing/php-translation-client

pkg:composer/smartness/translation-client

Statistics

Installs: 3 566

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

1.5.0 2026-09-23 16:36 UTC

README

Latest Version on Packagist Total Downloads

A Laravel package to synchronize translations between your Laravel application and a centralized translation management system. Pull translations from the server or push local translations back to keep everything in sync.

Features

  • šŸš€ One Command Install - Get started in seconds
  • šŸ”„ Bi-directional Sync - Pull and push translations with simple commands
  • šŸŒ Multi-language - Support for all languages
  • šŸ“¦ Laravel Compliant - Generates proper Laravel translation files with nested arrays
  • ⚔ CI/CD Ready - Perfect for automated deployments
  • šŸ”’ Secure - API token authentication
  • šŸŽÆ Smart Filtering - Filter by language, status, or specific files
  • ā¬†ļø Push Support - Send local translations back to the server
  • šŸ”Ž Source-Code Sync - translations:missing and translations:cleanup reconcile the catalog with the actual $t() / trans() / __() / @lang usage in your code
  • šŸ  Centralized Scan Config - Scan settings live on the project so every developer uses the same patterns (local .env still wins)

Requirements

  • PHP 8.1 or higher
  • Laravel 10, 11, 12, or 13

Installation

Install via Composer:

composer require smartness/translation-client

The package will automatically register itself via Laravel's package auto-discovery.

Configuration

Step 1: Publish Configuration (Optional)

php artisan vendor:publish --tag=translation-client-config

This creates a config/translation-client.php file where you can customize settings.

Step 2: Configure Environment Variables

Add these variables to your .env file:

# Required: Your API token
TRANSLATION_API_TOKEN=your_api_token_here

# Required: API endpoint URL
TRANSLATION_API_URL=https://your-translation-service.com/api

# Optional: Override default settings
TRANSLATION_OUTPUT_DIR=  # Default: lang_path()
TRANSLATION_FORMAT=php   # Options: php, json, raw
TRANSLATION_STATUS=approved  # Filter: approved, pending, rejected
TRANSLATION_TIMEOUT=30   # HTTP timeout in seconds

# Optional: Source-scanning overrides (used by translations:cleanup and translations:missing).
# These are fetched from the server's project config by default, so most teams
# don't need to set them. Local values always override the server config.
SMARTPMS_TRANSLATION_SCAN_DIRS="resources,app"
SMARTPMS_TRANSLATION_SCAN_EXTENSIONS="php,blade.php,ts,tsx,js,jsx,vue"
SMARTPMS_TRANSLATION_KEY_PATTERN=
SMARTPMS_TRANSLATION_PREFIX_PATTERN=

Note: You'll receive your API token and endpoint URL from your translation service administrator.

Usage

Pulling Translations (Download)

Pull all translations from the server:

php artisan translations:pull

Pull translations for a specific language:

php artisan translations:pull --language=en

Preview changes without saving (dry-run):

php artisan translations:pull --dry-run

Test API connection:

php artisan translations:pull --test

Advanced Pull Options

# Override format for this pull
php artisan translations:pull --format=json

# Override status filter
php artisan translations:pull --status=approved

# Combine multiple options
php artisan translations:pull --language=de --status=approved --dry-run

Pushing Translations (Upload)

Push all local translations to the server:

php artisan translations:push

Push translations for a specific language:

php artisan translations:push --language=en

Push a specific translation file:

php artisan translations:push --language=en --file=messages

Preview without actually pushing:

php artisan translations:push --dry-run

Overwrite existing translations on the server:

php artisan translations:push --overwrite

Use a custom translation directory:

php artisan translations:push --dir=/path/to/translations

Advanced Push Options

# Combine multiple options
php artisan translations:push --language=en --file=auth --overwrite

# Preview what will be pushed
php artisan translations:push --dry-run --language=de

Reconciling the Catalog with Source Code

These two commands scan your local source for $t('…'), useTranslate('…'), i18n.t('…'), trans('…'), __('…') and @lang('…') calls and reconcile what they find with what the server stores.

Both commands first fetch the project's central scan configuration from GET /translation-projects/config. Only the regex patterns (scan_key_pattern, scan_prefix_pattern) can be shared from the server; scan_dirs and scan_extensions are taken from local config or the package defaults only (never the server), since they control which local files are read. Local config always wins. Scan directories are also confined to the application base path. The resolved config is printed at the start of each run so you can verify which source provided each value.

translations:missing — Find Keys Used in Code but Absent Remotely

# Dry-run: report keys referenced in source that don't exist on the server.
php artisan translations:missing

# Create those keys as placeholder rows on the project's primary language
# (value=null, missing=true, is_new=true), so they show up in the New Keys UI.
php artisan translations:missing --insert

Sample output:

Scan configuration:
  scan_dirs        (server ) resources, app
  scan_extensions  (server ) php, blade.php, ts, tsx, vue
  key_pattern      (default) (?:^|[^\w$])(?:\$?t|useTranslat(?:e|ion)|i18n…
  prefix_pattern   (default) (?:^|[^\w$])(?:\$?t|useTranslat(?:e|ion)|i18n…

Scanned 412 files.
Found 837 unique used key(s).
Remote keys: 540
Used keys sent: 837
Missing keys: 12
  - dashboard.banner.welcome
  - dashboard.banner.dismiss
  …

translations:cleanup — Find Keys Stored Remotely but Unused in Code

# Dry-run: report stale keys.
php artisan translations:cleanup

# Actually delete them from the remote project.
php artisan translations:cleanup --delete

Dynamic keys built with template literals (e.g. $t(`amenities.${name}`)) contribute a static prefix that protects every matching remote key from deletion — no false positives when the key set is computed at runtime.

Output Structure

The package creates translation files following Laravel's standard structure:

lang/
ā”œā”€ā”€ en/
│   ā”œā”€ā”€ messages.php
│   ā”œā”€ā”€ validation.php
│   └── auth.php
ā”œā”€ā”€ de/
│   ā”œā”€ā”€ messages.php
│   ā”œā”€ā”€ validation.php
│   └── auth.php
└── es/
    ā”œā”€ā”€ messages.php
    ā”œā”€ā”€ validation.php
    └── auth.php

Example Generated File

<?php

// lang/en/auth.php

return [
    'failed' => 'These credentials do not match our records.',
    'password' => 'The provided password is incorrect.',
    'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',

    // Nested arrays for dot-notation keys
    'verification' => [
        'sent' => 'A fresh verification link has been sent to your email address.',
        'verified' => 'Your email address has been verified.',
    ],
];

Programmatic Usage

You can use the Translation Client directly in your code:

<?php

namespace App\Services;

use Smartness\TranslationClient\TranslationClient;

class TranslationSync
{
    public function __construct(
        protected TranslationClient $client
    ) {}

    // Pull translations
    public function syncTranslations(string $language): array
    {
        // Fetch translations as Laravel PHP arrays
        $response = $this->client->fetchAsPhp($language);

        return $response['data'];
    }

    public function syncAsJson(string $language): array
    {
        // Fetch translations as JSON format
        $response = $this->client->fetchAsJson($language);

        return $response['data'];
    }

    public function getRawTranslations(string $language): array
    {
        // Fetch raw format with metadata
        $response = $this->client->fetchRaw($language);

        return $response['data'];
    }

    // Push translations
    public function pushTranslations(array $translations, bool $overwrite = false): array
    {
        // Push all translations
        return $this->client->push($translations, [
            'overwrite' => $overwrite,
        ]);
    }

    public function pushLanguageTranslations(string $language, array $translations, bool $overwrite = false): array
    {
        // Push translations for a specific language
        return $this->client->pushLanguage($language, $translations, $overwrite);
    }

    public function pushFileTranslations(string $language, string $filename, array $translations): array
    {
        // Push a specific translation file
        return $this->client->pushFile($language, $filename, $translations);
    }

    public function verifyConnection(): bool
    {
        return $this->client->testConnection();
    }
}

Available Methods

Method Description Returns
fetchAsPhp(?string $language) Fetch translations as nested PHP arrays (Laravel format) array
fetchAsJson(?string $language) Fetch translations as flat JSON structure array
fetchRaw(?string $language) Fetch raw format with full metadata array
fetch(array $options) Fetch with custom options array
push(array $translations, array $options) Push translations to the server array
pushLanguage(string $language, array $translations, bool $overwrite) Push translations for a specific language array
pushFile(string $language, string $filename, array $translations, bool $overwrite) Push a specific translation file array
fetchProjectConfig() Fetch the centralized scan config from the server. Returns null on failure so callers can fall back to local config. ?array
cleanup(array $usedKeys, array $usedPrefixes, bool $delete) Report (and optionally delete) remote keys not referenced in source. array
discoverMissing(array $usedKeys, array $usedPrefixes, bool $insert) Report (and optionally insert) keys referenced in source but missing remotely. array
testConnection() Verify API connection and token bool

CI/CD Integration

GitHub Actions

Add this workflow to automatically sync translations:

# .github/workflows/sync-translations.yml
name: Sync Translations

on:
  schedule:
    - cron: '0 */6 * * *'  # Every 6 hours
  workflow_dispatch:  # Allow manual trigger

jobs:
  sync-translations:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.3'

      - name: Install dependencies
        run: composer install --no-dev --optimize-autoloader

      - name: Pull translations
        env:
          TRANSLATION_API_TOKEN: ${{ secrets.TRANSLATION_API_TOKEN }}
          TRANSLATION_API_URL: ${{ secrets.TRANSLATION_API_URL }}
        run: php artisan translations:pull

      - name: Commit and push changes
        run: |
          git config user.name "GitHub Actions"
          git config user.email "actions@github.com"
          git add lang/
          if git diff --staged --quiet; then
            echo "No translation changes"
          else
            git commit -m "chore: update translations [skip ci]"
            git push
          fi

GitLab CI

# .gitlab-ci.yml
sync-translations:
  stage: deploy
  script:
    - composer install --no-dev --optimize-autoloader
    - php artisan translations:pull
    - git config user.name "GitLab CI"
    - git config user.email "ci@gitlab.com"
    - git add lang/
    - git diff --staged --quiet || git commit -m "chore: update translations [skip ci]"
    - git push https://${GITLAB_USER}:${GITLAB_TOKEN}@${CI_REPOSITORY_URL#*@}
  only:
    - schedules
  variables:
    TRANSLATION_API_TOKEN: $TRANSLATION_API_TOKEN
    TRANSLATION_API_URL: $TRANSLATION_API_URL

Configuration Reference

All configuration options available in config/translation-client.php:

return [
    // API endpoint (required)
    'api_url' => env('TRANSLATION_API_URL'),

    // API authentication token (required)
    'api_token' => env('TRANSLATION_API_TOKEN'),

    // Output directory for translation files
    // Default: lang_path() resolves to 'lang/' directory
    'output_dir' => env('TRANSLATION_OUTPUT_DIR', null),

    // Format: json, php, or raw
    'format' => env('TRANSLATION_FORMAT', 'php'),

    // Status filter: approved, pending, rejected, or null (all)
    'status_filter' => env('TRANSLATION_STATUS', 'approved'),

    // HTTP request timeout in seconds
    'timeout' => env('TRANSLATION_TIMEOUT', 30),
];

Error Handling

The package provides clear, actionable error messages:

# Missing configuration
āŒ API token not configured. Please set TRANSLATION_API_TOKEN in your .env file.

# Invalid credentials
āŒ Authentication failed: Invalid API token. Please check your TRANSLATION_API_TOKEN configuration.

# Connection issues
āŒ API error: Failed to connect to translation service: Connection timeout

# No translations found
⚠ No translations found matching the criteria.

Troubleshooting

"API token not configured"

Solution: Add your API token to .env:

TRANSLATION_API_TOKEN=your_token_here
TRANSLATION_API_URL=https://your-service.com/api

"Authentication failed"

Solution: Verify your API token is correct. Contact your translation service administrator if needed.

"Connection timeout"

Solutions:

  • Check your network connection
  • Verify the API URL is correct
  • Increase timeout: TRANSLATION_TIMEOUT=60

Translations not updating

Solutions:

  • Run with --dry-run to preview changes
  • Check status filter: --status=approved
  • Verify you have translations in the system

Advanced Usage

Custom Fetch Options

use Smartness\TranslationClient\TranslationClient;

$client = app(TranslationClient::class);

$response = $client->fetch([
    'format' => 'php',
    'language' => 'en',
    'status' => 'approved',
    'missing' => false,
    'filename' => 'messages',
]);

Custom Output Directory

# Save to different directory
TRANSLATION_OUTPUT_DIR=/path/to/custom/lang

Multiple Environments

# Development
TRANSLATION_API_URL=https://dev-translation-service.com/api

# Production
TRANSLATION_API_URL=https://translation-service.com/api

Security

  • āœ… API token authentication
  • āœ… HTTPS required for API communication
  • āœ… Token validation before requests
  • āœ… No sensitive data in logs

Important: Never commit your API token to version control. Always use environment variables.

Support

For issues, questions, or feature requests:

License

The MIT License (MIT). Please see License File for more information.

Credits