shas/translations-editor
A web-based editor for Laravel language files. Browse translation keys across locales, edit or add strings in a clean interface, and save changes directly to your project's translation files without touching PHP arrays by hand.
Upgrade/install the package
composer require vendor/package:^2.0
php artisan vendor:publish --tag="package-config"
v2.0.0 is a major bump, so even though the headline change is "added online translation functionality," diff the published config against your existing config/package.php before assuming a drop-in upgrade — major versions routinely rename keys, namespaces, and driver strings. Run php artisan config:clear and commit the republished config.
Configure the online translation driver The new feature is almost certainly opt-in. Expect something like:
// config/package.php
'driver' => env('TRANSLATION_DRIVER', 'file'), // or 'online'
'online' => [
'provider' => env('TRANSLATION_PROVIDER', 'deepl'),
'api_key' => env('TRANSLATION_API_KEY'),
'endpoint' => env('TRANSLATION_ENDPOINT'),
],
Add the credentials to .env and to .env.example, phpunit.xml (as null/fakes), and your deployment secrets store. Never commit a real key.
Look here first
CHANGELOG.md / UPGRADE.md for the v1 → v2 migration notes.Translator, TranslationDriver) to type-hint or fake in tests.First use case: translate a missing key on demand
// app/Http/Controllers/DashboardController.php
public function index(Translator $translator)
{
$greeting = $translator->online()->translate('Welcome back, :name', 'fr', ['name' => $user->name]);
return view('dashboard', compact('greeting'));
}
Or, more likely for the file-based workflow, a console command that scans lang/en/*.php, detects keys absent from other locales, and fills them via the online provider:
php artisan translations:sync --locale=fr --driver=online --dry-run
Always run with --dry-run (or your package's equivalent) first, then review the git diff before committing machine output.
Keep the online driver behind an abstraction. Wrap it in a small app-level service so you can swap providers, add caching, and fake it in tests:
namespace App\Services;
use Vendor\Package\Contracts\Translator;
class TranslationService
{
public function __construct(private Translator $translator) {}
public function translate(string $text, string $locale, array $replace = []): string
{
return Cache::remember(
"tr:{$locale}:" . md5($text),
now()->addDay(),
fn () => $this->translator->driver('online')->translate($text, $locale, $replace)
);
}
}
Batch, don't loop. Online translation is an HTTP round-trip per call (or per batch). Never call it inside a Blade loop or a foreach over a collection of user content — collect the strings, dedupe them, and issue one batch request. Most providers accept arrays of up to 50–128 segments.
Queue the writes, not the reads. Syncing translations to disk/DB is slow and failure-prone; push it to a job:
class SyncLocaleTranslations implements ShouldQueue
{
public $tries = 3;
public $backoff = [10, 60, 300];
public function __construct(public string $locale) {}
public function handle(TranslationService $service): void
{
foreach (Lang::getMissingKeys($this->locale) as $key => $source) {
$service->store($this->locale, $key, $service->translate($source, $this->locale));
}
}
}
Use it for user-generated content, not UI strings. The strongest day-to-day use case is translating dynamic content — product descriptions, support tickets, comments — where you can't hand-write translations. Keep static UI strings in lang/ files under version control and human review; machine-translated UI copy tends to be subtly wrong and hard to audit.
Locale negotiation. Online providers expect specific codes (en-US, pt-BR, zh-Hans) that often differ from Laravel's en_US / pt_BR. Add an explicit mapping table in config and normalise at the boundary, not scattered through call sites.
Fallback chain. Wire the online driver as a fallback, not the primary, so a provider outage degrades gracefully:
'fallback_chain' => ['file', 'online', 'source'],
Cache aggressively, invalidate by version. Cache keyed on locale + hash(source) + provider so a provider switch doesn't serve stale translations.
The major version bump is the real risk. "Added a feature" in a v2 usually ships alongside renamed config keys, changed method signatures, or a new required dependency. Run the full test suite and grep your codebase for the package namespace before deploying. If there's no UPGRADE.md, compare the v1 and v2 tags on the config file and service provider directly.
Cost and rate limits are silent until they aren't. Add a hard cap ('online.max_characters_per_day' => 100_000) and log every request. A stray translate() inside a loop over 10k rows will burn through a paid quota in one request. Handle 429 with exponential backoff — the package's built-in retry may be off by default.
Placeholders and HTML get mangled. :name, {count}, <strong>, and %s are frequently corrupted by machine translation. Verify the driver preserves them; if not, tokenise placeholders (__PH1__), translate, then substitute back. Test every locale with a string containing all placeholder syntaxes you use.
Pluralisation is not a translation problem you can outsource. Providers rarely handle Laravel's trans_choice pipe syntax ({0} none|{1} one|[2,*] many). Translate the variants individually and assemble the plural string yourself.
Never machine-translate into a locale you ship without review. Especially legal text, error messages, and anything with a brand name. Add a do_not_translate config list (['Acme', 'Pro Plan', 'OAuth']) and verify the provider honours it — many providers let you pass a glossary or "ignore tags".
Testing: fake the HTTP layer, not the package. Http::fake() on the provider endpoint keeps tests fast and offline-friendly. Also add a test asserting the container resolves the file driver when TRANSLATION_API_KEY is null, so a missing env var in CI doesn't silently turn into live API calls.
Config caching quirks. Because the driver is typically env-driven, a stale bootstrap/cache/config.php after a deploy will keep the old credentials or the old driver. Put php artisan config:cache in your deploy script after secrets are injected, and clear it in local dev when switching drivers.
Escape output, always. Machine-translated text coming back from a third party is untrusted input. Render with {{ }} / e(), never {!! !!}, unless you've explicitly sanitised the provider response.
Extension point to watch: if the package exposes a driver contract, implement a CachedTranslator or FallbackTranslator decorator and bind it in a service provider rather than editing vendor config. That survives the next major bump.
Debugging checklist when a translation comes back in the source language: (1) is the driver actually online in the cached config? (2) is the source locale correctly detected — providers will happily "translate" en → en? (3) is the cached value from an earlier failed run? (4) did the batch silently drop segments over the provider's limit? Log the raw request/response payloads at debug level for the first week after rollout.
How can I help you explore Laravel packages today?