Appearance
Change any text of a Laravel site from the admin, in any language
The shop's button says Add to cart, and the owner wants Add to basket. On a Laravel site that is a ticket: find which package says it, add a line to lang/, commit, deploy. Next month the owner wants a German site, and a developer is copying English sentences from a dozen packages into a file for a translator, then again after every update, when new sentences have appeared and nobody can say which.
WordPress sites answer this with Loco Translate. A Laravel application has no such screen of its own, since Laravel keeps its sentences in code and in files that belong to whoever wrote them. kitloom/ is that screen: every text your site and its admin say, listed by the package that says it, with your own line laid over the package's without touching its file.
Ask the agent
With kitloom/ai connected, an agent whose user may manage phrases does it from your chat:
- "Which modules have no German yet?" (
phrases_, the texts translated of the total per language)packages - "Show me the shop's texts about delivery." (
phrases_)list - "Say Add to basket instead of Add to cart, everywhere." (
phrases_, as a line on the site or as a draft for you to read)set
An agent's line goes through the same checks as yours: one that loses what its text fills in is refused, with a message it reads.
What a phrase is
A phrase is a text a developer wrote in the code: Read more, Add to cart, Your order is on its way. What you typed yourself is something else: the site's title, a page, a menu item, a product's name. Those are content, and kitloom/languages translates them with the rest of the site. Phrases never touches them.
One word, on every page
Phrases → Phrases lists where the texts come from: each module, each theme, the core packages and This site, your application's own code. Each row counts its texts and, for every language, how many have a line. Open a package in a language and each text has three columns:
- Source, the English as the code says it. Under it, Also in 2 other packages when others say the very same words: Laravel keeps one dictionary, so your line is read wherever they appear.
- Shipped, what the package itself translates it as. Read-only, since the file belongs to the package.
- Yours, your line. Type it, leave the field, and it is on the site: the page cache is cleared with it. Empty it and the shipped words stand again.
A line must keep what its text fills in: :, :, %s, and the | between plural forms. Add : without : would draw a sentence with no figure in it, so such a line is refused, with a note saying what is missing.
A language kitloom does not ship
Add language takes a locale: de, uk, pt_. It gets a column, and each package starts at zero. Type the lines you care about, or, with AI Writer set up, press Translate empty with AI: the texts with no line go to your own model in the background and come back as drafts. A draft is kept and marked, and it is shown nowhere until a person has read it and pressed Publish drafts. A line the model returned without its placeholders is dropped and counted, so a broken sentence never reaches a page.
To make the site speak it, set Site Language in Settings → General to that locale; what has no line yet stays in English. With kitloom/languages, a page speaks that module's languages instead: add German there, and its lines come from here. A line written for fr is read on a Canadian French page too, unless fr_ has one of its own.
Your words in git
A line in the database is right for an owner; a developer may want it in a file. Three ways out:
- Import JSON brings in a dictionary,
"English text":: a translator's work, or the"line" lang/of another site.de. json - Export JSON gives a package's lines as its own
lang/, merged with what it ships if you ask: the file a developer commits, so the next site has your translation out of the box.de. json - Move to code writes every line of a language into the application's
lang/, which Laravel reads over the packages'. Tick remove the rows and the file alone speaks from then on: for a site that has settled and keeps its words in git.de. json
The same from the console, for a deploy or a check in CI:
bash
php artisan phrases:scan --check # the texts without a line; fails when any
php artisan phrases:export de --package=kitloom/shop --merge # the shop's lang/de.json
php artisan phrases:import translations.json --locale=de # a dictionary into GermanFor a Laravel developer
The lines live in a phrases table of the module's own: group, key, locale, text, status, source and who wrote it. They reach Laravel's translator through spatie/laravel-translation-loader, whose manager reads a group from the language files as Laravel does and lays the loaders' lines over them. The module names itself as the one loader rather than using spatie's Language, whose one JSON column of locales has no room for a status or an author per language, and whose fallback would show an English override on a French page.
Which texts a package says is read from its code by the same scanner the packages' translation tests use: __, trans_, @lang(), a post type's labels, t() in a theme's scripts. The lines of a group are read once into the cache and forgotten when one is written, and a long-lived Octane or queue worker compares a version at the start of each request, so one worker's edit reaches the others. Vue and React themes get the same lines. It comes with both admins, Filament and Nova, behind its own permission, manage_.
Watch it
Two short lessons from the admin, narrated:
Try it
The module comes with kitloom, for either admin, and is off until you ask for it. On a Filament site:
bash
composer require kitloom/phrases kitloom/phrases-filament
php artisan migrateOn a Nova site, the second package is kitloom/. Open a sandbox in one click, with no sign-up: Filament or Nova. Every detail, from the filters to the console commands and the agent's tools, is in the Phrases guide.