Download the PHP package vortech/laravel-localize without Composer

On this page you can find all versions of the php package vortech/laravel-localize. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package laravel-localize

Tests Latest version Downloads PHP version Laravel 13+

Typed translation tables for Eloquent models: one table per model with real columns.
An explicit locale view with exact fallback rules, partial transactional saves and a per-locale unique rule.


Why

Translations in a JSON column are hard to index, hard to constrain and hard to query. Localize gives every model its own translation table with ordinary columns, so a slug gets a unique index per locale and a description can be text.

JSON column Localize
Unique slug per locale no index to lean on a normal unique index, plus the UniqueTranslation rule
Column types everything is JSON string, text, whatever your migration says
Reading $model->name and a guess about the locale $model->locale('en')->name, always explicit
Fallback whatever the package decided requested locale, then the app locale, then null, per column
Saving rewrite the whole blob only the submitted columns, in one transaction
Translation classes one per model none

Requirements

Installation

localize:install publishes config/localize.php. The service provider is discovered automatically.

Usage

1. Declare the model

The listed columns are not part of the model's fillable columns: they live in the translation table and are checked against the attribute's whitelist.

2. Create the tables

generates the migration below from the model: the table name, the key type (foreignId, foreignUlid or foreignUuid), a nullable string per declared column (text for body, content, description, excerpt, notes, summary and text), the (product_id, locale) unique index and, with #[LocalizedSlug], the (locale, slug) one. Check the column types before you migrate. It accepts a class name or a short name from App\Models, and --force replaces a migration generated before. Written by hand it looks like this:

The schema is yours, in a migration you version. The translation table is the singular of the model's table plus _translations, with the model's foreign key (product_id).

3. Read

Fallback works per column. With hu as the app locale:

Stored value Result
row missing, or the column is null fallback to the app locale
'' ''
'0' '0'
missing in the app locale too null

app.fallback_locale is not a step, and there is no en-GB → en or "first existing language" fallback. Reading an undeclared column throws UnknownTranslationAttribute.

Reading as a property. $product->name is not translated unless the model opts in:

Reading $product->name goes through the locales relation like any view, so eager load it in lists (lazy loading rules apply). Assigning is refused because the column does not exist on the model's own table. A real attribute of the same name with a value wins, so do not keep a translated column on the main table. The array form, $fillable and the dirty attributes never see the column.

A view fixes the requested locale and the app locale when it is created. A later app()->setLocale() does not change it.

4. Write

Input Effect
omitted locale or column unchanged
null clears that column
'' stores an empty string
new locale creates the row
[] no-op
unknown column, wrong shape or value type exception, nothing is written

Both calls use the same writer. The payload is validated before the first query, everything runs in one transaction on the model's connection, and only the submitted columns are written. Locale keys are used exactly as given (en-GB and pt_BR both work). A fallback value is never copied into a locale.

The model must be saved first (UnsavedTranslationOwner otherwise), and the package never saves the model's own pending changes. To create a model and its translations together, wrap both in your own transaction:

Rows are inserted or updated, never upserted. On MySQL an upsert reacts to any unique index, so a slug clash could overwrite another product's row; here a clash raises a QueryException and the whole call rolls back. Two requests creating the same locale row for the first time at the same moment can also end in that exception: the unique index decides, and the loser should retry. There are no Eloquent events on the translation rows; see Events for the one this package dispatches.

5. Validate

Which languages and columns are required is your Form Request's decision. The package validates only the technical shape. For uniqueness per locale:

6. Eager loading

locales() is a normal HasMany of the stored rows. The view reads the loaded relation, so after eager loading there are no queries per model:

To load fewer rows, name the locales you read. The app locale you fall back to is always included:

A callback as the second argument narrows the loaded rows further. It receives the translation table query, and a row it leaves out is also missing from the fallback:

If the relation is not loaded, the normal Laravel lazy loading rules apply, including Model::preventLazyLoading(). After a save, a loaded relation is reloaded; after refresh() the next read is current. A change made by another process or rolled back by an outer transaction is not seen until you reload.

7. Serialize and edit

The view is Arrayable, Jsonable (toJson()) and JsonSerializable, with the declared columns in declaration order. The model's own toArray() is untouched; in an API resource add 'translation' => $this->resource->locale()->toArray().

Events

After a write is committed, one Vortech\Localize\Events\TranslationChanged event is dispatched for every column that changed value. A column saved with the value it already has dispatches nothing, and neither does a write that fails.

A generated slug is a change like any other, so it dispatches too.

8. Query

Filtering by a translated column is explicit, in the locale you name:

whereLocale() and whereLocales() take a translatable column (anything else throws UnknownTranslationAttribute) and match rows where it is not null. whereHasLocale() takes a callback that receives the translation table query. None of them use the display fallback: it never changes what a query finds. withLocales() eager loads the stored rows; withLocales(['en']) loads only those locales plus the current app locale, which the fallback reads. All of them are Eloquent scopes, so they chain with anything and work on relations.

Route model binding by a translated slug is described under Slugs.

9. Deleting

Deleting a model removes its translations through the foreign key's cascadeOnDelete(). With soft deletes the translations stay and are used again after restore(). Setting a column to null does not delete the row.

10. Factories

withLocales() is added to every Eloquent factory. It saves the locales right after the model is created, for each model of a batch:

With make() nothing is saved, because the model does not exist yet.

11. Locale routes

Route::localized() wraps routes in a {locale} URL prefix and the SetLocale middleware:

Redirecting / to a locale and detecting it from Accept-Language are yours to add.

12. Slugs

#[LocalizedSlug] generates the slug of each locale from another column, unique per locale:

13. Routing by slug

On a model with #[LocalizedSlug] the slug is the route key, so {article} binds by the slug in the URL's locale (no fallback) and the model comes back with its locales loaded:

/en/articles/fekete-polo is a 404, as the slug belongs to Hungarian. Use {article:id} where you want the id (admin). Use {article}, not {article:slug}, on slug routes: only the plain form generates the URL from the model.

14. Language switcher, canonical and hreflang

LocaleLinks builds the current page's URL in every locale. It needs a named route inside Route::localized(); each bound slug model contributes its slug of the locale, and other parameters are reused.

Two Blade components cover the common case:

head renders <link rel="canonical"> and one <link rel="alternate" hreflang="..."> per available locale (pt_BR becomes pt-BR). switcher renders a list of links with aria-current on the current locale and a disabled entry where the page has no translation; the switcher keeps the query string. Publish the markup with php artisan vendor:publish --tag=localize-views.

There is no x-default alternate.

JSON columns

A column can hold structured data. Cast it with array and write and read it as an array:

Declaration rules

Configuration

config/localize.php:

Key Default
locale_length 35 the longest locale key a write accepts; match your locale column
table_suffix '_translations' appended to the singular of the model's table: product_translations
locales ['en'] the locales Route::localized() accepts in the URL; writes are not restricted by it

Good to know

Testing

The suite uses Pest, runs on SQLite through Orchestra Testbench and requires PHP 8.4+. Static analysis runs at PHPStan level max, and Rector (composer test:refactor) and Pint keep the style. To run against another database: DB_CONNECTION=mysql DB_DATABASE=localize DB_USERNAME=root composer test.

Changelog

See CHANGELOG for what has changed recently.

Contributing

See CONTRIBUTING for details.

Security

If you discover a security issue, please email [email protected] instead of using the issue tracker.

Credits

License

The MIT License (MIT). See the License File for more information.



All versions of laravel-localize with dependencies

PHP Build Version
Package Version
Requires php Version ^8.4
illuminate/console Version ^13.0
illuminate/contracts Version ^13.0
illuminate/database Version ^13.0
illuminate/filesystem Version ^13.0
illuminate/http Version ^13.0
illuminate/routing Version ^13.0
illuminate/support Version ^13.0
illuminate/validation Version ^13.0
illuminate/view Version ^13.0
laravel/prompts Version ^0.3
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package vortech/laravel-localize contains the following files

Loading the files please wait ...