定制 tonydev/lara-glot 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

tonydev/lara-glot

Composer 安装命令:

composer require tonydev/lara-glot

包简介

Auto-translation for Laravel models, language files, and Filament. Drivers for Anthropic Claude, OpenAI, DeepL, Google, and Ollama, with glossary locking and aggressive caching.

README 文档

README

Automatic translation for Laravel. LaraGlot translates Eloquent models with JSON translatable attributes, nested page-builder content, and static PHP language files — and it goes to great lengths to never pay for the same translation twice.

Five drivers ship out of the box: Anthropic Claude, OpenAI, DeepL, Google Translate, and Ollama for local models. Laravel placeholders, URLs, HTML tags, and your brand terms survive translation intact.

Table of Contents

Requirements

Requirement Version
PHP ^8.3
Laravel ^11.0 | ^12.0 | ^13.0
spatie/laravel-translatable ^6.0 (for model translation)
filament/filament ^4.0 | ^5.0 (for the admin UI)

Installation

composer require tonydev/lara-glot

The service provider is auto-discovered. Then publish the config:

php artisan vendor:publish --tag=lara-glot-config

Two drivers need an extra package; the rest use Laravel's HTTP client directly:

# Google (unofficial endpoint, no API key required)
composer require stichoza/google-translate-php:^5.3

# Ollama (local models)
composer require cloudstudio/ollama-laravel:^2.0

If you use queued model translation, make sure job batching is migrated:

php artisan queue:batches-table
php artisan migrate

Running php artisan migrate also creates the lara_glot_translation_retries table, which tracks values that fell back to source text so laraglot:retry can re-attempt them. It's optional — without it, translation still works and LaraGlot logs a single notice instead of tracking fallbacks.

Configuration

Pick a driver and set its key in .env:

LARAGLOT_DRIVER=anthropic
ANTHROPIC_API_KEY=sk-ant-...

# or
LARAGLOT_DRIVER=openai
OPENAI_API_KEY=sk-...

# or
LARAGLOT_DRIVER=deepl
DEEPL_API_KEY=...

Every driver accepts the same tuning knobs in config/lara-glot.php: chunk size, retries, retry delay, concurrency, HTTP timeout, and cache TTL. The defaults are sensible; you rarely need to touch them.

For slow reasoning models (e.g. DeepSeek-class models behind an OpenAI-compatible endpoint), raise the timeout and/or lower the chunk size:

LARAGLOT_OPENAI_TIMEOUT=180
LARAGLOT_OPENAI_CHUNK_SIZE=10

When a chunk still times out (or gets rate-limited), the LLM drivers automatically retry it in smaller sub-chunks before falling back, so one slow request no longer drops a whole chunk of strings to source text.

Fallback detection

When a driver fails on a string, that string falls back to the source text. LaraGlot refuses to let that go unnoticed:

  • laraglot:files --sync prints a per-file translated / identical-to-source count, warns when the fallback share exceeds lara-glot.fallback.warn_ratio (default 0.5), and exits non-zero on failures.
  • When every translatable string in a file comes back identical to the source — the signature of a failed run — the file is not written and the job fails so it can retry, instead of an untranslated English file masquerading as done. Disable via LARAGLOT_FAIL_ON_FULL_FALLBACK=false if a locale is legitimately identical to the source.

Protected values

Values that are references rather than prose — 'link' => 'route:contact', bare URLs, mailto:/tel: links — are passed through untranslated. Patterns are configurable in lara-glot.protected_value_patterns.

Drivers

Driver Needs a key Best for
anthropic yes Best translation quality. Defaults to claude-haiku-4-5 (fast, cheap); set claude-sonnet-5 when nuance matters most.
openai yes Works with any OpenAI-compatible endpoint — OpenAI, Azure, Groq, Together AI. Defaults to gpt-5-mini.
deepl yes Strong European-language quality at a flat per-character price. Free-tier keys (ending :fx) are detected automatically.
google no Prototyping and low-volume work. Uses the unofficial web endpoint — no SLA, so don't build production on it. Long strings are split on sentence boundaries automatically (LARAGLOT_GOOGLE_MAX_LENGTH, default 1500 chars) since the free endpoint 500s on long payloads, and every response is verified to have preserved placeholder tokens before being accepted.
ollama no Fully local and free. Point it at any model your hardware can run. Defaults to llama3.2.

To use another OpenAI-compatible provider, change the base URL — no code changes:

# Groq
LARAGLOT_OPENAI_BASE_URL=https://api.groq.com/openai/v1
LARAGLOT_OPENAI_MODEL=llama-3.3-70b-versatile

# Azure OpenAI
LARAGLOT_OPENAI_BASE_URL=https://your-deployment.openai.azure.com/v1

Keeping Costs Down

This is where LaraGlot earns its keep. A translation API call is made only when a string has genuinely never been translated before:

  1. In-process cache. Within one request or job, a repeated string costs a RAM lookup, nothing more.
  2. Persistent cache. Every translation is stored in your Laravel cache (30 days by default), keyed per string, per language pair, per driver. Re-running a command, re-saving a model, or rebuilding language files re-uses everything already translated.
  3. In-batch deduplication. If "Save" appears forty times across your language files, it is sent to the API once and fanned back out to all forty keys.
  4. Change detection. Model translation tracks a hash of the source content and skips fields that haven't changed since the last run.
  5. Per-string caching, not per-chunk. Editing one string in a file invalidates that string only — the other forty-nine stay cached.

In practice a second full run over an unchanged project costs zero API calls. You can verify this yourself: every driver exposes getStats() with cache hits, misses, and the actual API call count.

Glossary and Brand Terms

Two tools to keep terminology under control:

'glossary' => [
    // Never translated — byte-identical in every language.
    // Works with all five drivers.
    'protected_terms' => ['LaraGlot', 'Acme Cloud'],

    // Forced translations per target locale.
    // Applied by the LLM drivers (anthropic, openai, ollama).
    'terms' => [
        'checkout' => ['de' => 'Kasse', 'fr' => 'paiement'],
    ],
],

protected_terms are shielded with the same token mechanism used for URLs, so even Google and DeepL can't mangle them. Matching is case-sensitive and whole-word: LaraGlot is protected, laraglots is not.

Changing the glossary automatically invalidates the affected cached translations — you never serve stale wording after a terminology decision.

Quality Review Pass

Machine translation is accurate but often stiff. With the review pass enabled, each freshly translated chunk gets a second call where the model acts as a native-speaker reviewer: it fixes overly literal phrasing, replaces vocabulary no real product would use, and returns already-good translations unchanged.

LARAGLOT_REVIEW=true

Cost notes, because they matter:

  • The review only runs on strings that weren't cached — and the reviewed result is what gets cached. You pay the extra call once per string, ever.
  • If the review call fails for any reason, the first draft is kept. A translation is never lost to the reviewer.
  • LLM drivers only; Google and DeepL ignore the flag.

Placeholder Protection

Translation engines will happily turn :count into :nombre and break your app. Before any text leaves your server, LaraGlot replaces sensitive tokens with opaque markers, and swaps them back afterwards:

:name                   → __BRACE_0__
https://example.com     → __URL_1__
<span translate="no">…  → __HTML_2__
LaraGlot (glossary)     → __TERM_3__

Input:

"Welcome back, :name! Visit https://example.com for details."

Sent to the API:

"Welcome back, __BRACE_0__! Visit __URL_1__ for details."

After restore:

"Bienvenue, :name ! Visitez https://example.com pour plus de détails."

This is a hard guarantee enforced in code, not a prompt instruction, and it applies to every driver and every translation path.

Model Setup

Add the trait to any model using spatie/laravel-translatable:

use Spatie\Translatable\HasTranslations;
use Tonydev\LaraGlot\Traits\HasSmartTranslations;

class Page extends Model
{
    use HasTranslations;
    use HasSmartTranslations;

    public $translatable = ['title', 'body', 'seo_title', 'seo_description'];
}

Register it in config/lara-glot.php:

'models' => [
    \App\Models\Page::class,
],

Nested JSON content (page-builder sections, repeaters) is translated recursively. Keys listed in ignored_keys — identifiers, security values, media paths, locale/format config, geo and ordering fields, timestamps — are passed through untouched. The published config ships with a comprehensive list of keys that are never human-readable labels (id, uuid, slug, sku, token, avatar, latitude, created_at, …); add your own app-specific keys there. Deliberately absent are words like email or price that are often translatable labels in language files.

Language File Translation

Translates your lang/en/*.php files into every configured locale, preserving nested key structure:

# Everything, queued
php artisan laraglot:files

# One file, one locale, synchronously
php artisan laraglot:files auth --locale=fr --sync

# Overwrite existing translation files
php artisan laraglot:files --force

Files listed in exclude_files (framework files like validation) are skipped.

Artisan Commands

laraglot:sync

Scans registered models and dispatches translation jobs for anything new or changed.

php artisan laraglot:sync                          # all registered models
php artisan laraglot:sync "App\Models\Page"        # one model
php artisan laraglot:sync --locale=fr --locale=de  # limit target locales
php artisan laraglot:sync --force                  # ignore change detection

laraglot:files

Translates PHP language files. See the section above for options (file?, --locale=, --force, --sync).

laraglot:audit

Reports every language-file key and model attribute whose value is byte-identical to the source locale — the signature of a silent translation fallback — and can repair them in place:

php artisan laraglot:audit                        # audit all files + registered models
php artisan laraglot:audit --locale=ar --locale=de
php artisan laraglot:audit --files-only --file=footer
php artisan laraglot:audit --models-only "--model=App\Models\Faq"
php artisan laraglot:audit --repair               # re-translate ONLY the identical values
php artisan laraglot:audit --threshold=0.5        # flag at ≥50% identical (default 0.85)
  • Exits non-zero when any file/model locale is flagged at or above the threshold, so it works as a CI gate or scheduled canary (Schedule::command('laraglot:audit')).
  • --repair re-translates only the values still equal to the source (missing keys included) with a forced cache refresh, so fixing fallbacks never re-bills the whole corpus and evicts poisoned cache entries.
  • Values that are supposed to be identical across locales belong in lara-glot.audit.allowlist — they're never flagged or re-billed. The default list ships with common acronyms, units, and platform names ("OK", "SMS", "PDF", "iOS", "PayPal", …); extend it in the published config. ignored_keys and protected_value_patterns are excluded automatically.

laraglot:retry

Whenever a value falls back to source text during a translation run, LaraGlot records it in the lara_glot_translation_retries table — a fallback is never silent. This command re-attempts those units:

php artisan laraglot:retry               # process everything currently due
php artisan laraglot:retry --limit=100
  • Bounded, with back-off: each failed attempt reschedules the unit at base_delay_minutes × 2^attempts (default 30 min base); after max_attempts (default 5) the unit is parked as exhausted and the command exits non-zero so it surfaces for human review instead of re-billing forever. Tune via lara-glot.retry.
  • Never wastes an API call: units whose source text changed, whose key was removed, or that were already healed elsewhere (an audit --repair, a --force run, a manual edit) are resolved without touching the driver.
  • Idempotent + resumable: run it on a schedule (Schedule::command('laraglot:retry')->hourly()) and coverage converges; exhausted strings that are legitimately identical belong in lara-glot.audit.allowlist.

Queue Setup

Heavy work runs on Laravel queues (translations by default):

php artisan queue:work --queue=translations,default --timeout=620 --tries=4

For production, run the worker under Supervisor with --timeout above the model job timeout (600s).

Filament Integration

Register the plugin in your panel provider:

use Tonydev\LaraGlot\LaraGlotPlugin;

public function panel(Panel $panel): Panel
{
    return $panel->plugin(LaraGlotPlugin::make());
}

You get a management page showing translation coverage per model and language, with actions to queue missing or forced re-translations.

Custom Drivers

Register your own engine without forking the package. In a service provider's boot():

use Tonydev\LaraGlot\Services\TranslationService;

TranslationService::extend('my-engine', fn () => new MyEngineDriver());

Then set LARAGLOT_DRIVER=my-engine. The driver needs to implement Tonydev\LaraGlot\Contracts\TranslationDriverInterface — two methods, translate() and translateBatch(). Extend AbstractLlmDriver instead if your engine is prompt-based and you want chunking, caching, deduplication, glossary support, and the review pass for free; then the only method to write is sendChunk().

Custom Prompts

The built-in LLM prompt asks for translations that read like a native speaker wrote them, matching the register of the source text. If you want full control:

'prompt' => 'Translate the JSON array from {source} to {target}. ... your rules ...',

The string replaces the built-in prompt entirely; {source} and {target} are substituted. Changing it invalidates the affected cache entries, same as glossary changes.

Failure Behaviour

Two rules, depending on where you are:

At render time (a page reading a translation), failure is graceful: a failed translation returns the original string, never an exception and never half-translated token soup. Placeholders are restored even on the fallback path — users never see __VAR_0__.

In jobs and commands, failure is loud — a batch that writes English into lang/ar/ while reporting success is corruption, not resilience:

  • Chunks retry with exponential back-off; transient failures (timeouts, 429s, 5xx) additionally retry in halved sub-chunks before any string falls back.
  • A batch driver error throws — the job fails visibly and retries, and nothing gets cached or written.
  • A file whose output is 100% identical to the source is treated as a failed run: not written, job failed.
  • Any individual string that does fall back is recorded in the retry table and re-attempted by laraglot:retry — a fallback is acceptable, a silent fallback is not.
  • Corrupt cache entries read as misses and are evicted automatically; forced refreshes (--force, --repair, retries) bypass every cache layer, including the driver's own.
  • Everything is logged under the [LaraGlot:Driver] tag.

Upgrading to 2.1

2.1 fixes a data-corrupting bug and tightens failure behaviour. Read this if you ran 2.0 in production:

  • Critical fix — multibyte corruption: under Laravel's default process concurrency driver, translations to Arabic, Chinese, Hindi, and other multibyte scripts were silently replaced with the English source (unserialize(): Error at offset … in the log was the only trace). Fixed for both file and model translation. Audit your existing localesphp artisan laraglot:audit reports the damage and --repair heals only the affected values.
  • Filament v4/v5 only: the admin page uses the Schemas API and a non-static $view; Filament 3 panels are no longer supported.
  • Run php artisan migrate: creates the optional lara_glot_translation_retries table used by laraglot:retry.
  • Behaviour change: batch translation failures in jobs/commands now throw instead of silently writing source text; a 100% source-identical file fails the job instead of being written. Escape hatch: LARAGLOT_FAIL_ON_FULL_FALLBACK=false.
  • ignored_keys defaults grew substantially (identifiers, media paths, timestamps, …). If any of the new defaults collide with translatable label keys in your app, remove them in your published config.
  • New config blocksretry, audit (threshold + allowlist), fallback, protected_value_patterns, per-driver timeout, drivers.google.max_length — all optional with sensible defaults; re-publish the config to pick them up.

Upgrading from 1.x

  • ignored_keys defaults shrank to a generic list. If you relied on removed defaults (cta_url, is_active, and similar app-specific keys), add them to your published config.
  • Ollama caching changed from chunk-level to string-level. Old chunk cache entries are simply ignored; strings re-cache individually on the next run.
  • New config blocks (glossary, review, prompt, drivers.anthropic) are optional — re-publish the config or copy them in if you want the new features.
  • Model defaults moved to current generations (gpt-5-mini, llama3.2). Pin your previous model via env if you need to stay put.

License

MIT. Created by TONY THE DEVELOPER

tonydev/lara-glot 适用场景与选型建议

tonydev/lara-glot 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 11 次下载、GitHub Stars 达 0, 最近一次更新时间为 2026 年 04 月 05 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「i18n」 「translation」 「laravel」 「localization」 「google-translate」 「deepl」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 tonydev/lara-glot 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 tonydev/lara-glot 我们能提供哪些服务?
定制开发 / 二次开发

基于 tonydev/lara-glot 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 11
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 32
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-04-05