Localization uses the URL given for the request. In order to achieve this purpose, a route group should be added into the routes.php
file. It will filter all pages that must be localized.
// app/Http/routes.php
<?php
/* ---------------------------------------------------------
| Application Routes
| ---------------------------------------------------------
*/
Route::localizedGroup(function () {
// ADD ALL LOCALIZED ROUTES INSIDE THIS GROUP
Route::get('/', function() {
return view('home');
});
Route::get('test', function() {
return view('test');
});
});
// OTHER PAGES THAT SHOULD NOT BE LOCALIZED
Once this localized route group is added to the routes file, a user can access all locales added into supported-locales
(en
, es
and fr
by default, look at the config section to change that option).
For example, a user can now access three different locales, using the following addresses:
http://your-project-url
http://your-project-url/en
http://your-project-url/es
http://your-project-url/fr
If the locale is not present in the url or it is not defined in supported-locales
, the system will use the application default locale or the user's browser default locale (if defined in config file).
Once the locale is defined, the locale variable will be stored in a session (if the middleware is enabled), so it is not necessary to write the lang uri section in the url after defining it once, using the last known locale for the user.
If the user accesses to a different locale this session value would be changed, translating any other page he visits with the last chosen locale.
Moreover, this package includes a middleware object to redirect all "non-localized" routes to the corresponding "localized".
So, if a user navigates to http://your-project-url/test
and the system has this middleware active and en
as the current locale for this user, it would redirect (301) him automatically to http://your-project-url/en/test. This is mainly used to avoid duplicate content and improve SEO (Search Engine Optimization) performance.
Note: All Localization middleware(s) are registered in Localization RoutingServiceProvider, so you don't have to register the middleware(s) in the your
app/Http/Kernel.php
.
By using the Route::localizedGroup()
, the middleware(s) are automatically inserted in Route attributes based on config/localization.php
file.
And if you want to do it manually:
// app/Http/routes.php
<?php
Route::group([
'prefix' => Localization::setLocale(),
'middleware' => [
'localization-session-redirect',
'localization-redirect',
],
], function() {
/** ADD ALL LOCALIZED ROUTES INSIDE THIS GROUP **/
Route::get('/', function() {
return view('hello');
});
Route::get('test',function(){
return view('test');
});
});
/** OTHER PAGES THAT SHOULD NOT BE LOCALIZED **/
In order to activate it, you just have to attach middleware(s) to the routes you want to be accessible localized.
If you want to hide the default locale but always show other locales in the url, switch the hide-default-in-url
config value to true. Once it's true, if the default locale is en (english) all URLs containing /en/ would be redirected to the same url without this fragment '/' but maintaining the locale as en (English).
IMPORTANT - When hide-default-in-url
is set to true, the unlocalized root is treated as the applications default locale app.locale
. Because of this language negotiation using the Accept-Language header will NEVER occur when hide-default-in-url
is true.
/**
* Returns an URL adapted to $locale or current locale.
*
* @param string|null $locale
* @param string|null $url
* @param array $attributes
* @param bool|false $showHiddenLocale
*
* @return string|false
*/
public function getLocalizedURL($locale = null, $url = null, array $attributes = [], $showHiddenLocale = false)
// OR
/**
* Returns an URL adapted to $locale or current locale.
*
* @param string $url
* @param string|null $locale
*
* @return string
*/
public function localizeURL($url = null, $locale = null);
It returns a localized URL to the desired locale.
/**
* It returns an URL without locale (if it has it).
*
* @param string|false $url
*
* @return string
*/
public function getNonLocalizedURL($url = null)
It returns a clean URL of any localization.
/**
* Returns an URL adapted to the route name and the locale given.
*
* @param string|bool $locale
* @param string $transKey
* @param array $attributes
* @param bool|false $showHiddenLocale
*
* @return string|false
*/
public function getUrlFromRouteName($locale, $transKey, array $attributes = [], $showHiddenLocale = false)
It returns a route, localized to the desired locale using the locale passed.
If the translation key does not exist in the locale given, this function will return false
.
For a quick use, you can use the localized_route()
helper:
/**
* Get a localized URL with a given trans route name.
*
* @param string $transRoute
* @param array $attributes
* @param string|null $locale
*
* @return string
*/
function localized_route($transRoute, array $attributes = [], $locale = null)
/**
* Return an array of all supported Locales.
*
* @return \Arcanedev\Localization\Entities\LocaleCollection
*/
public function getSupportedLocales()
It returns all locales as a Arcanedev\Localization\Entities\LocaleCollection
Collection. For more details, check the LocaleCollection Entity.
/**
* Get supported locales keys.
*
* @return array
*/
public function getSupportedLocalesKeys()
It returns an array with all the supported locales keys.
/**
* Set the supported locales.
*
* @param array $supportedLocales
*
* @return self
*/
public function setSupportedLocales(array $supportedLocales)
Set the localization's supported locales.
/**
* Returns current language.
*
* @return string
*/
public function getCurrentLocale()
It returns the key of the current locale.
/**
* Returns current language.
*
* @return \Arcanedev\Localization\Entities\Locale
*/
public function getCurrentLocaleEntity()
It returns the Entity
of the current locale. Check the Arcanedev\Localization\Entities\Locale
class for more details.
/**
* Returns current locale name.
*
* @return string
*/
public function getCurrentLocaleName()
It returns the name of the current locale (For example English
, Spanish
or Arabic
...).
/**
* Returns current locale script.
*
* @return string
*/
public function getCurrentLocaleScript()
It returns the ISO 15924 code of the current locale (For example Latn
, Cyrl
or Arab
...).
/**
* Returns current locale direction.
*
* @return string
*/
public function getCurrentLocaleDirection()
It returns the direction of the current locale: ltr
(Left to Right) or rtl
(Right to Left).
/**
* Returns current locale native name.
*
* @return string
*/
public function getCurrentLocaleNative()
It returns the native name of the current locale.
/**
* Returns current locale regional.
*
* @return string
*/
public function getCurrentLocaleRegional()
It returns the regional of the current locale. The regional locale could be used with the setlocale() method.
/**
* Get all locales.
*
* @return \Arcanedev\Localization\Entities\LocaleCollection
*/
public function getAllLocales()
It returns all locales as a Arcanedev\Localization\Entities\LocaleCollection
Collection. For more details, check the LocaleCollection Entity.
/**
* Set and return current locale.
*
* @param string $locale
*
* @return string
*/
public function setLocale($locale = null)
This function will change the application's current locale.
If the locale is not passed or null
, the locale will be determined via a cookie or the session (if stored previously), browser Accept-Language header or the default application locale (depending on your config file).
/**
* Sets the base url for the site.
*
* @param string $url
*/
public function setBaseUrl($url)
/**
* Check if Locale exists on the supported locales collection.
*
* @param string|bool $locale
*
* @return bool
*/
public function isLocaleSupported($locale)
/**
* Get locales navigation bar.
*
* @return string
*/
public function localesNavbar()
If you're supporting multiple locales in your project you will probably want to provide the users with a way to change language.
Note : You can publish and modify the blade template markups.
The localesNavbar
function would work as desired and it will translate the routes to all translated languages.
Note: Don't forget to add any new route to the translation file.
IMPORTANT: You may have an issue with localesNavbar
method if you're using the Route bindings, See Issue #19 for more details.
If you're using some route bindings by using $router->bind()
or $router->model()
.
You need to implement the Arcanedev\Localization\Contracts\RouteBindable
interface to your binded class to render the correct wildcard values.
For example:
<?php namespace App;
// Other use statements...
use Arcanedev\Localization\Contracts\RouteBindable;
class User
extends Model
implements AuthenticatableContract,
AuthorizableContract,
CanResetPasswordContract,
RouteBindable
{
//...
/**
* Get the wildcard value from the class.
*
* @return int|string
*/
public function getWildcardValue()
{
return $this->id; // You can return whatever you want (username, hashed id ...)
}
//...
}
The Arcanedev\Localization\Entities\LocaleCollection
class extends from Illuminate\Support\Collection
, so it provides a fluent, convenient wrapper for working with locales data (Locale
entities).
For more details, check the Illuminate\Support\Collection documentation.
The Locale
Entity implements the Illuminate\Contracts\Support\Arrayable
, Illuminate\Contracts\Support\Jsonable
and JsonSerializable
to simplify the conversion.
The available methods:
/**
* Get local key.
*
* @return string
*/
public function key()
/**
* Get locale name.
*
* @return string
*/
public function name()
/**
* Get locale Script.
*
* @return string
*/
public function script()
/**
* Get locale direction.
*
* @return string
*/
public function direction()
/**
* Get locale native.
*
* @return string
*/
public function native()
/**
* Get locale regional.
*
* @return string
*/
public function regional()
/**
* Check if it is a default locale.
*
* @return bool
*/
public function isDefault()
/**
* Create Locale instance.
*
* @param string $key
* @param array $data
*
* @return self
*/
public static function make($key, array $data)
You can adapt your URLs depending on the language you want to show them. For example:
http://your-project-url/en/about
http://your-project-url/es/acerca (acerca is about in Spanish)
http://your-project-url/fr/a-propos (a-propos is about in French)
Or with wildcards :
http://your-project-url/en/view/5
http://your-project-url/es/ver/5 (view == ver in Spanish)
http://your-project-url/fr/afficher/5 (view == afficher in French)
This would be redirected to the same controller using the proper middleware and setting up the translation files as follows :
// config/localization.php
<?php
return [
//...
/* ------------------------------------------------------------------------------------------------
| Route
| ------------------------------------------------------------------------------------------------
*/
'route' => [
'middleware' => [
'localization-session-redirect' => true, // Optional
'localization-cookie-redirect' => false, // Optional
'localization-redirect' => true, // Optional
'localized-routes' => true, // Required to be true
'translation-redirect' => false, // Optional
],
],
//...
];
Routes :
// app/Http/routes.php
<?php
/* ------------------------------------------------------------------------------------------------
| Application Routes
| ------------------------------------------------------------------------------------------------
*/
Route::localizedGroup(function () {
Route::get('/', function () {
return view('home');
});
Route::transGet('routes.about', function () {
return view('about');
});
Route::transGet('routes.view', function ($id) {
return view('page', compact('id'));
});
});
In the routes file you just have to enable the localized-routes
middleware and the Route::transGet
function to every route you want to translate using the translation key.
Note: Route::transGet is a translated get method, Route::transPost for the post method and so on.
Then you have to create the translation files and add there every key you want to translate. I suggest to create a routes.php
file inside your resources/lang/{locale}
folder.
For the previous example, I have created three translations files, these three files would look like:
// resources/lang/en/routes.php
<?php
return [
'about' => 'about',
'view' => 'view/{id}', // we add a route parameter
// other translated routes
];
// resources/lang/es/routes.php
<?php
return [
'about' => 'acerca',
'view' => 'ver/{id}', // we add a route parameter
// other translated routes
];
// resources/lang/fr/routes.php
<?php
return [
'about' => 'a-propos',
'view' => 'afficher/{id}', // we add a route parameter
// other translated routes
];
Once files are saved, you can access to these urls without any problem :
http://your-project-url/en/about
http://your-project-url/es/acerca
http://your-project-url/fr/a-propos
Or:
http://your-project-url/en/view/5
http://your-project-url/es/ver/5
http://your-project-url/fr/afficher/5
The required steps to make a model translatable are:
- First you need to add the
Arcanedev\Localization\Traits\HasTranslations
. - Next you should add a public methods
getTranslatableAttributes()
which returns an array with all the names of attributes you wish to make translatable. - Finally you should make sure that all translatable attributes are set to the
text
type in your database. If your database supportsjson
columns, use that.
Here's an example of a prepared model:
use Arcanedev\Localization\Traits\HasTranslations;
use Illuminate\Database\Eloquent\Model;
class Tag extends Model
{
use HasTranslations;
/**
* Get the translatable attributes.
*
* @return array
*/
public function getTranslatableAttributes()
{
return ['name'];
}
}
The easiest way to get a translation for the current locale is to just get the property for the translated attribute:
$tag->name; // Gets the translated name
You can also use the getTranslation
method to get the attribute translation:
/***
* Get the translated attribute.
*
* @param string $key
* @param string $locale
* @param bool $useFallback
*
* @return mixed
*/
public function getTranslation($key, $locale, $useFallback = true)
You can also an alias the alias:
/**
* Get the translated attribute (alias).
*
* @param string $key
* @param string $locale
*
* @return mixed
*/
public function trans($key, $locale = '')
To set the translation for an attribute, you need to call the setTranslation
method.
/**
* Set a translation.
*
* @param string $key
* @param string $locale
* @param string $value
*
* @return self
*/
public function setTranslation($key, $locale, $value)
Don't forget to save your model after you've done the translation.
$tag->setTranslation('name', 'en', 'Tutorials');
$tag->save();
You can chain the calls like this:
$tag->setTranslation('name', 'en', 'Tutorials')->save();
You can forget a translation for a specific field:
/**
* Forget a translation.
*
* @param string $key
* @param string $locale
*
* @return self
*/
public function forgetTranslation($key, $locale)
You can forget all translations for a specific locale:
/**
* Forget all the translations by the given locale.
*
* @param string $locale
*
* @return self
*/
public function flushTranslations($locale)
/**
* Get the translations for the given key.
*
* @param string $key
*
* @return array
*/
public function getTranslations($key)
/**
* Set the translations.
*
* @param string $key
* @param array $translations
*
* @return self
*/
public function setTranslations($key, array $translations)
Example:
$tag->setTranslations('name', [
'en' => 'Tutorials',
'nl' => 'Tutoriaux',
]);
You can immediately set translations when creating a model. Here's an example:
$tag = Tag::create([
'name' => [
'en' => 'Tutorials',
'fr' => 'Tutoriaux',
],
]);
If you're using MySQL 5.7
or above, it's recommended that you use the json data type for housing translations in the db. This will allow you to query these columns like this:
Tag::whereRaw('name->"$.en" = \'Tutorials\'')->get();
In laravel >=5.2.23
, you can use the fluent syntax:
Tag::where('name->en', 'Tutorials')->get();
Make sure to return the value of the translated attribute in the accessor & mutator:
This is an example with multiple translated attributes name
& slug
:
use Arcanedev\Localization\Traits\HasTranslations;
use Illuminate\Database\Eloquent\Model;
class Tag extends Model
{
use HasTranslations;
protected $fillable = ['name', 'slug'];
/**
* Get the translatable attributes.
*
* @return array
*/
public function getTranslatableAttributes()
{
return ['name', 'slug'];
}
/**
* Get the name attribute (accessor).
*
* @param string $name
*
* @return string
*/
public function getNameAttribute($name)
{
return Str::ucfirst($name);
}
/**
* Set the slug attribute (mutator).
*
* @param string $slug
*
* @return string
*/
public function setSlugAttribute($slug)
{
return Str::slug($slug);
}
}
You can capture the URL parameters during translation if you wish to translate them too.
To do so, just create an event listener for the routes.translation
event like so:
Event::listen('routes.translation', function ($locale, $route, $attributes) {
// You can store these translation in you database
// or using the laravel's localization folders
$translations = [
'view' => [
'en' => [
'slug' => 'hello',
],
'es' => [
'slug' => 'hola',
],
'fr' => [
'slug' => 'salut',
],
],
];
// This is just a dummy thing to fetch and merge the translation.
if (
isset($translations[$route]) && isset($translations[$route][$locale])
) {
$attributes = array_merge($attributes, $translations[$route][$locale]);
}
return $attributes;
});
Be sure to pass $locale
and $route
and $attributes
as parameters to the closure and you should return a translated $attributes
.
And also make sure that localized-routes
and translation-redirect
are enabled.
// config/localization.php
<?php
return [
//...
/* ------------------------------------------------------------------------------------------------
| Route
| ------------------------------------------------------------------------------------------------
*/
'route' => [
'middleware' => [
'localization-session-redirect' => true, // Optional
'localization-cookie-redirect' => false, // Optional
'localization-redirect' => true, // Optional
'localized-routes' => true, // Required to be true
'translation-redirect' => true, // Required to be true
],
],
//...
];
You may also use Event Subscribers.
Right after calling setTranslation()
method the Arcanedev\Localization\Events\TranslationHasBeenSet
event will be fired.
An you can access these properties:
/** @var \Illuminate\Database\Eloquent\Model */
public $model;
/** @var string */
public $attribute;
/** @var string */
public $locale;
/** @var mixed */
public $oldValue;
/** @var mixed */
public $newValue;