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

Filament Translations Google Laravel Package

tomatophp/filament-translations-google

A Filament plugin that extends Translation Manager with Google translation crawling to automatically translate your __() and trans() strings. Requires tomatophp/filament-translations; install via Composer and register the plugin in your panel provider.

View on GitHub
Deep Wiki
Context7

Getting Started

This package is a plugin for tomatophp/filament-translations, not a standalone translator. It contributes a Google-powered translation action to the Translations Manager UI. If the parent package isn't already working in your panel, nothing here will work.

Prerequisite checklist (do this first):

  1. tomatophp/filament-translations installed, migrations run, and the Translations Manager page reachable in your admin panel.
  2. Parent config has your source + target locales defined and the scan paths pointing at your lang/ directory (or wherever your __() / trans() keys live).
  3. A policy / authorization story for who may create translations — the Google action honours the parent package's create ability, so if your policy denies it, the button silently won't render for that user.

Then install this add-on:

composer require tomatophp/filament-translations-google
php artisan filament-translations-google:install

Register the plugin on the panel you actually want it on:

// app/Providers/Filament/AdminPanelProvider.php
->plugin(\TomatoPHP\FilamentTranslationsGoogle\FilamentTranslationsGooglePlugin::make())

Publish the stubs so you can read what knobs exist instead of guessing:

php artisan vendor:publish --tag="filament-translations-google-config"
php artisan vendor:publish --tag="filament-translations-google-lang"

Pin your version line explicitly. The v5 line requires Filament v5, Livewire 4, Laravel 12/13, PHP 8.2+, and tomatophp/filament-translations ^5.0. On Filament v4, pin the v4 branch line for both this package and the parent. A loose constraint will resolve a mismatched major and you'll get a runtime failure inside the action rather than a clear composer conflict:

"tomatophp/filament-translations": "^5.0",
"tomatophp/filament-translations-google": "^5.0"

Where to look first, in order of value:

  • config/filament-translations.php (parent) — locales, scan paths, storage, policy. This is the real configuration surface; the Google package has very little of its own.
  • The published config/filament-translations-google.php — confirm what locale mapping exists rather than assuming en_GB is accepted.
  • vendor/tomatophp/filament-translations-google/src — the action class is the contract. Documentation is thin, so read how it selects keys, whether it skips keys that already have a value, and how it converts locale codes before hitting the translator.

First use case — a deliberately tiny batch. Pick 5–10 keys that include at least one :placeholder and one pluralised string (apples|apple), scan, translate one target locale, then inspect the resulting language file by hand. This validates locale mapping, placeholder preservation, and whether existing values get clobbered — all before you point it at 2,000 keys.


Implementation Patterns

Keep the translator behind the container

The v5 release resolves the translator from the container. That is the single most useful architectural detail in the package — treat the Google binding as an implementation detail you own, not a given.

// app/Providers/AppServiceProvider.php
use Stichoza\GoogleTranslate\GoogleTranslate;

public function register(): void
{
    $this->app->bind(GoogleTranslate::class, function () {
        return (new GoogleTranslate())
            ->setClient(new \GuzzleHttp\Client([
                'proxy'  => config('services.translate.proxy'),
                'timeout' => 15,
            ]));
    });
}

Reasons to bind your own instance:

  • Corporate egress — the crawling endpoint is outbound HTTP; many networks only allow traffic through a proxy.
  • Timeouts — the default client timeout is not tuned for a Livewire request that must finish before PHP's max_execution_time.
  • Observability — wrap the client with a Guzzle middleware that logs every request/response; when a translation comes back wrong or the action hangs, this is the only debugging surface you get.
  • Replaceability — if you later move to Google Cloud Translation, Cloudflare, or DeepL, you change one binding.

Use a fake in tests and demos

Because the translator is container-resolved, CI never needs the network:

$this->app->bind(GoogleTranslate::class, fn () => new class extends GoogleTranslate {
    public function translate(string $text): string
    {
        return "[es] {$text}";
    }
});

This also makes seeded demo environments deterministic — you get obviously-fake translations instead of live machine output polluting screenshots or fixtures.

Wire it into your actual localization workflow

The realistic day-to-day loop for a Filament app:

  1. Developer adds __('invoices.mark_paid') in a resource.
  2. Run the parent package's scan (artisan command or UI) so the key appears in the Translations Manager.
  3. Translator/PM opens the panel, filters to the target locale, selects the untranslated keys, hits the Google action.
  4. Human review pass on the diff — this step is not optional and not built in.
  5. Commit the changed lang/ files in a PR titled something like i18n(es): machine-draft translations.

Because steps 1–3 mutate files in the repo, keep the translation output reviewable: one PR per locale, so a reviewer reads lang/es.json as a diff rather than fighting a 400-line noise commit.

Gate the action with a policy, not a role check

The action respects the create ability on the parent package's translation policy. Implement the gate there rather than hiding the page — it gives you a single, testable authorization point that both the UI and any future programmatic invocation share.

// app/Policies/TranslationPolicy.php
public function create(User $user): bool
{
    return $user->hasAnyRole(['super_admin', 'translator']);
}

Treat "who can trigger machine translation" as a distinct permission from "who can edit translations" if your team has non-technical ops staff — you want a QA gate between drafts and shipping.

Script the bulk case

For a large initial backfill, don't click through the UI in one request. The package ships no documented queue or retry workflow, so the safe pattern is to chunk: trigger the action on 50–100 keys at a time, let each request complete, and commit between batches. If you need true background processing, resolve the bound translator yourself and drive it from a queued job that writes to the same language files — you lose the UI but gain retry and rate limiting.

Keep the tooling in CI

composer test      # PEST
composer format    # php-cs-fixer
composer analyse   # PHPStan

Run these on your own app too, not just when contributing upstream. analyse in particular catches locale arrays that have drifted out of shape after a machine-translation pass.


Gotchas and Tips

This is scraping, not an API contract

Stichoza\GoogleTranslate\GoogleTranslate hits an unofficial Google Translate endpoint. There is no SLA, no quota console, no documented rate limit, and no guarantee of ToS compliance for your jurisdiction and volume. Consequences you must plan for:

  • 429s and silent failures. No documented retry logic ships with the package. Wrap the client with a retry middleware, or chunk batches small enough that a failure is cheap to redo.
  • Quality drift. The endpoint can change output over time with no versioning. Don't rely on re-running it and getting the same result.
  • Availability windows. A backfill that works at 10am may stall at 3pm. Never run a bulk pass as part of a deploy or a scheduled job without a fallback.

If any of these are unacceptable, bind a real Google Cloud Translation client instead — the container binding makes that a one-class change.

Locale codes are the most likely source of silent breakage

Laravel uses pt_BR and en_GB; Google wants pt and en. Check the published config for a mapping layer and confirm it covers every locale you configured in the parent. If a code isn't mapped, you'll either get an exception mid-batch or — worse — translations written into the wrong locale's file. Test with your weirdest locale first (the one with a region or script subtag), not with es.

"Lines without English text translate their key" is a footgun

Per the release notes, when a value has no English text the action translates the key itself. That's fine if your keys are English sentences ('Invoice marked as paid') and disastrous if they're dot-notation (invoices.status.mark_paid becomes "facturas.estado.marcar_pagado" in your Spanish file). Decide your key convention before adopting this, or filter those keys out of the batch.

Placeholders are preserved — but only :placeholders

The release explicitly guarantees :placeholders survive untranslated. It does not promise anything about:

  • Laravel pluralisation pipes: 'apple|apples'
  • ICU / trans_choice brace syntax: {0} No items|{1} One item
  • Blade interpolations inside lang strings: {{ $name }}
  • HTML tags and attributes in the string
  • PHP sprintf %s / %d tokens

Spot-check every one of these categories on a throwaway locale before trusting a bulk pass. Pluralised strings are the highest-risk class — a mangled | separator produces a runtime InvalidArgumentException from trans_choice, not a bad translation.

Do not assume it skips existing values

Read the action source before your first real run and confirm the behaviour when a key already has a translation in the target locale. If it overwrites, you will silently destroy hand-corrected copy on the next bulk pass. Mitigation either way: commit (or branch) before every run so git diff is your undo, and never run the action in production against a populated lang/ directory without a backup.

Large batches will time out inside a Livewire request

The action runs synchronously in the request that fires the Filament action. With a proxy, retries, and 300 keys, you can blow past max_execution_time and leave a half-written language file. Symptoms: the panel spinner never resolves, or the file is valid PHP but only partially translated. Keep batches in the 50–100 range, and if you raise max_execution_time for this, raise it in the FPM pool, not with ini_set.

Octane and service provider registration

The action is registered once under Laravel Octane — that's a fix, not a feature, but it means you should not add your own Filament::register… calls in a loop or a service provider that runs per-request (e.g. inside a middleware or a booted callback on a request-scoped singleton). If the action appears twice or not at all after octane:reload, check for a duplicate registration before blaming the package.

Clear caches after translation runs

If you use php artisan config:cache, your locale list is frozen at cache time — changing locales in the parent config won't be picked up by the action until you re-cache. Similarly, if your app caches compiled translations (lang:cache-style tooling, or a TranslationServiceProvider that preloads files), translated values won't appear in the UI until that cache is rebuilt. Standard symptom: the file on disk is correct, the site still shows English.

Debugging a bad translation

There is no built-in logging. When a string comes out wrong, your only signals are (a) the input string, (b) the target locale code the action passed, and (c) the raw response. Add a Guzzle middleware on your bound client that logs all three at debug level, gated behind an env flag. Without that, you're re-running the action and hoping to reproduce, which costs you another round-trip against a rate-limited endpoint.

Maturity reality check

Six stars, zero dependents, README-and-releases-only documentation. That is not a reason to avoid it, but it is a reason to: read the action class before trusting it, wrap the translator binding so you can swap it, keep the language files in git so you can revert, and be prepared to own the integration if upstream stalls. The MIT license and the small surface area (one plugin, one action, one binding) make that ownership cheap — as long as you don't spread its assumptions across the codebase.

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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin