liberusoftware/localization-mymemory
MyMemory translation provider for Liberusoftware Localization. Adds a configurable MyMemory-backed machine translation driver to Laravel apps, helping teams localize content and manage multilingual text through a simple API integration.
Install and confirm it wires itself up.
composer require liberusoftware/localization-mymemory
It is an optional adapter, so nothing happens until the host localization module is present and the driver is selected. Open vendor/liberusoftware/localization-mymemory/src/ first and read the service provider — that file tells you the exact contract it implements, the config key it registers under, and whether it binds a singleton or a factory. Because there are no dependents and no stars, treat the source as the documentation.
Publish and inspect the config.
php artisan vendor:publish --provider="Liberu\LocalizationMyMemory\MyMemoryServiceProvider"
You are looking for, at minimum: an enabled/toggle flag, an API endpoint, an optional contact email param (MyMemory raises the daily quota when you supply one), a cache store + TTL, and the driver/alias name. Wire those into .env rather than editing the published file.
Point the localization module at the driver. In the host module's config/localization.php (or wherever the provider list lives), add the adapter's driver key to the drivers/providers map. Some Liberu modules auto-discover via a LocalizationManager::extend() call; check whether this package self-registers or expects you to add it manually — getting this wrong is the usual "installed but silent" failure.
First use case: fill one missing key, not a whole file. Run a single string through the manager in php artisan tinker:
app(\Liberu\Localization\Contracts\Translator::class)
->translate('Welcome back', 'en', 'fr');
Confirm you get a string back, then hit it twice and confirm the second call is served from cache (watch your cache store or Cache::has()). That one loop validates credentials, driver registration, and caching at once — before you point it at 3,000 lang lines.
Keep it behind the host contract. Never type-hint the MyMemory class directly in your app code. Resolve Translator/TranslationProvider from the localization module so you can swap in DeepL, a human-review queue, or a stub for tests without touching call sites. This is the single most important pattern given the package's maturity level.
Translate on write, cache on read. MyMemory has no batch endpoint and a modest free quota, so the adapter's cache is the whole game. The practical workflow is: a job fires when a source string is created/changed → the job calls the provider → the result is persisted in your translations table and the package cache. Treat the package cache as a short-lived rate-limit shield and your DB as the source of truth. If you invert that, a cache flush becomes an outage and a quota bill.
Queue bulk operations with rate limiting. For backfilling a locale, dispatch one job per string (or small chunks) on a dedicated queue with Redis::throttle(...)->allow(5)->every(1)->then(...). Never loop over a lang file synchronously in a request or an Artisan command you'll want to interrupt — a 500-line fr.json backfill will take minutes and will trip 429s.
Wrap the driver in your own thin service. Something like App\Support\MachineTranslator with translate(), translateMany(), and isEnabled(). That gives you one place to add: skip-if-already-published, skip-if-source-is-a-placeholder, skip-if-locale-is-excluded, and structured logging of responseStatus. It also makes Http::fake() trivial in tests.
Test with Http::fake(), and disable in CI. The adapter almost certainly uses Laravel's HTTP client. Fake the endpoint in feature tests so CI never burns quota or depends on network egress; assert the cache is populated on the second call. In phpunit.xml set the enabled flag false by default and opt in per-test.
Staged rollout for a locale. Run the machine pass → mark those rows machine_translated = true → let translators filter on that flag in the admin UI and mark reviewed_at. This is how machine translation actually pays off; dumping raw output straight into the live site is how you get "Bitte warten Sie, bis der Administrator" shipped to customers.
Fallback chain. Prefer human translations → package cache → machine provider → source locale. Implement the chain in your service, not in the package; the adapter should be the last link, and it should be cheap to remove.
email parameter raises it. A single fr.json backfill can exhaust a day's allowance. Log the character count you send, not just the request count.responseStatus is not an HTTP status. MyMemory returns HTTP 200 with a JSON body containing responseStatus/responseDetails. A 403 or 429 inside the body will look like success to a naive ->json('responseData.translatedText') read and you will silently write nulls or the literal source string into your translations. Always assert responseStatus === 200 and throw otherwise.", ', &. Run html_entity_decode($text, ENT_QUOTES | ENT_HTML5, 'UTF-8') before persisting, or your translators will file bugs about apostrophes.:name, {{ $user }}, @lang, ICU plural braces, and HTML tags are frequently translated or reordered. Protect them with sentinel tokens (__PH0__) before sending and restore after, or better, skip strings with placeholders entirely and route them to a human queue.en|fr, en|pt-BR is inconsistent, and zh variants are painful. Add a mapping table from your app locales (de_DE, de-AT) to the provider's langpair values, and decide explicitly what de-AT falls back to..env and see no effect, run php artisan config:clear. Conversely, if you don't run config:cache in production, the adapter will read the raw environment on every request.composer.json and consider composer.lock review on update. If the repo goes quiet, your own MachineTranslator wrapper is the escape hatch — forking a 200-line adapter is a two-hour job.LocalizationManager::extend() hook (so you can register a second provider), a cache.store config (so translations don't pollute your default cache and evict sessions under Redis allkeys-lru), and an HTTP client binding (so you can set timeouts — the default client has none and a hung connection will block your queue worker).source === target and against chained translation (en → fr → de), which compounds error quickly and is hard to unwind later.How can I help you explore Laravel packages today?