smartness / translation-client
Laravel package to sync translations with SmartPMS Translation Manager
Package info
github.com/smartpricing/php-translation-client
pkg:composer/smartness/translation-client
Requires
- php: ^8.1
- guzzlehttp/guzzle: ^7.0
- illuminate/console: ^10.0|^11.0|^12.0|^13.0
- illuminate/http: ^10.0|^11.0|^12.0|^13.0
- illuminate/support: ^10.0|^11.0|^12.0|^13.0
Requires (Dev)
- orchestra/testbench: ^8.0|^9.0|^10.0|^11.0
- phpunit/phpunit: ^10.0|^11.0|^12.0|^13.0
README
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:missingandtranslations:cleanupreconcile the catalog with the actual$t()/trans()/__()/@langusage in your code - š Centralized Scan Config - Scan settings live on the project so every developer uses the same patterns (local
.envstill 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. Local config (config/translation-client.php or SMARTPMS_TRANSLATION_* env vars) always wins; the server values are a shared default; package defaults are used if neither is set. 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-runto 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:
- Email: dev@smartpricing.com
- Issues: GitHub Issues
License
The MIT License (MIT). Please see License File for more information.