Skip to content

Lang Extractor ​

Lang Extractor powers an in-app translation workflow. It scans your source for translatable strings, stores them so you can edit them in a language editor, and publishes your edits back to Laravel's language files. The cycle is: extract new keys, edit their translations, publish them to disk — with revert and sync available as recovery tools.

Quick start ​

Scan your app for new translation strings and add them as editable tokens:

bash
php artisan lang:extract

Then edit the translations in the language editor, and write your edits back to disk:

bash
php artisan lang:publish

Commands ​

Each command takes an optional language code; omit it to run for every language:

  • lang:extract — scan the source code for translatable strings and add any new ones as editable tokens. Existing tokens (and your edits) are never overwritten — only brand-new keys are added.
  • lang:publish — write the current translations back to Laravel's language files. This also triggers a rebuild of the compiled front-end translation bundles so JavaScript translations stay in sync.
  • lang:revert — discard edits, resetting changed translations back to the value they were extracted with.
  • lang:sync — rebuild the token list from the language files on disk. Use this when the files are the source of truth and you want the editor to match them.
bash
# Extract for Arabic only
php artisan lang:extract ar

# Publish every language
php artisan lang:publish

What gets extracted ​

Extraction finds free-form translation strings passed to __(), trans(), trans_choice(), Lang::get(), Lang::choice(), @lang and @choice. The argument may be single or double quoted, a heredoc or nowdoc, or several literals concatenated together; escape sequences are resolved exactly as PHP resolves them, so the extracted key matches the one your code looks up at runtime.

Arguments that are not fully literal — __($label) or __('Prefix ' . $suffix) — are skipped, because there is no static key to store.

Extraction deliberately skips file-based keys such as __('validation.required'), since those live in PHP language files rather than the editable JSON set. A string only counts as file-based when it has no whitespace and starts with the name of a PHP language file in the fallback locale, so a sentence like __('validation.rules are strict here') is still extracted. File-based keys enter the editor only through lang:sync.

By default the scan covers app, resources, routes, database/seeders, and public/assets, plus the views and scripts shipped by redot/core; directories that don't exist are ignored. Only .php and .js files are searched. Files ignored by Git are skipped in your application's directories, but not in redot/core's, which live under the ignored vendor directory.

Limitations ​

Extraction is static pattern matching, not a full PHP or JavaScript parser:

  • Dynamic keys are invisible. Variables, class constants, interpolated double-quoted strings or heredocs, and any mix of literals with variables — __($label), __('Prefix ' . $suffix), __("Hello $name") — are skipped, because there is no fixed key to store.
  • Only the listed helpers. Calls through a translator instance ($translator->get(...)) or other APIs are not scanned. Lang::get / Lang::choice must appear with that Lang:: prefix (an aliased use is fine; a renamed import is not).
  • File-based PHP language keys stay out of extract. Keys like __('validation.required') are filtered out; bring them into the editor with lang:sync instead.
  • Empty strings are dropped. __('') never becomes a token.
  • Scan scope is fixed for the Artisan command. lang:extract only walks the directories and extensions above. Other paths (other packages under vendor, custom folders, .vue files, and so on) are ignored unless you drive LangExtractor yourself. When you do, pass package sources to searchInPackages() instead of searchIn() so Git ignore rules don't filter them out.

Notes ​

  • Sync is destructive. lang:sync replaces all of a language's tokens with what's on disk, so any unpublished edits are lost.
  • Editing un-publishes. Changing a translation marks it as needing to be published again, so you always re-run lang:publish to apply it.

Proprietary — for use within the Redot Dashboard. Product site: redot.dev