How to create a multi-languages database. And change your web application language according to the URL. In other words, let’s localize our database and create a middleware to change the web app language.
Introduction: Understanding How Localization work
I have created two videos for this tutorial on YouTube:
Laravel localization with middleware
Laravel 9: Multi-Language CRUD With Eloquent API Resources – Database Localization
Laravel by default supports multi-language websites we call this feature Laravel’s Localization. It retrieves a string in various languages according to your application language. And we can configure our app language in config/app.php the
configuration file
'locale' => 'en',
'fallback_locale' => 'en',
These language strings can be stored in files within the lang
/ directory in a folder for each language we use.
/lang
/en
auth.php
pagination.php
validation.php
/ar
auth.php
pagination.php
validation.php
We can save a group of strings in a specific file. Laravel comes with English language files and if we want to add another language, We must create the language directory with the same files and array index. And make sure the language folder name is the same as the one you use in the configuration file.
If we open the en/auth.php
file
<?php
return [
'failed' => 'These credentials do not match our records.',
'password' => 'The provided password is incorrect.',
'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
'Unauthorised' =>'Unauthorised',
];
To add a translation so we create ar/auth.php
file as the following
<?php
return [
'failed' => 'بيانات الاعتماد هذه غير متطابقة مع البيانات المسجلة لدينا.',
'throttle' => 'لقد تعديت الحد المسموح لعمليات الدخول المتكررة. يرجى المحاولة مرة أخرى بعد :seconds ثانية.',
'password' => 'كلمة المرور غير صحيحة.',
'Unauthorised' =>'غير مسموح',
];
If you notice there is :seconds
that we don’t translate because this is a parameter in the translation string we can replace it with any value we want. this placeholder must be prefixed with :
Example: let’s say we have a translation string for welcoming the user in lang/en/message.php
.
'hello' => 'Hello!',
'welcome back' => 'Eww, Welcome back, :name',
To use it we use this statement __('translationFileName.Arrayindex')
and if we have a parameter we use __('translationFileName.Arrayindex', ['parameter'=>'value'])
echo __('messages.hello');
echo __('messages.welcome back', ['name' => 'Ahmed']);
Set the app language.
we will create a middleware to change the app language according to the local parameter in the URL. it will hold the language ID.
php artisan make:middleware SetAppLang
After creating the middleware we need to register it in the Http\Kernel.php
file.
protected $routeMiddleware = [
//
'setapplang' => \App\Http\Middleware\SetAppLang::class,
];
Let’s add the following code in Http\Middleware\SetAppLang.php
for changing the app language.
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\App;
class SetAppLang
{
public function handle(Request $request, Closure $next)
{
// url = domain/en/post
if (! in_array($request->segment(2), config('app.available_locales'))) {
abort(400);
}
App::setLocale($request->segment(2));
return $next($request);
}
}
- Our URL will be
example.com/{locale}/anything-else locale = lang_id
such asen, ar, es
- We get the
locale
via$request->segment(2)
then we check if it’s in ouravailable_locales
the array that will create later. App::setLocale($request->segment(2));
for setting the app language.
In config\app.php
let’s create available_locales
array
'locale' => 'en',
'available_locales' => [
'en',
'ar'
],
Finally, creating the routes, we will add the middleware to a group of routes with prefix locale
so we can create our URL format. example.com/{locale}/anything else
In routes\api.php
file
Route::middleware('setapplang')->prefix('{locale}')->group(function(){
Route::post('register',[RegisterController::class, 'register']);
//other routes here
});
Multi-Langauge Database structure
Our database structure will contain for each model such as category, two tables, the first one is a reference table for the ref_id and shared data between the different language versions of the post such as images and price. The second table will contain the post data for each language and it will have the language id column and the reference id as well.
We are going to create a categories table to store the category translations if Arabic or English and then we create category_refs for storing the shared data between the translations and reference id.
Here are the migrations for each table.
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('categories', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('lang_id')->nullable();
$table->string('name')->nullable();
$table->string('slug')->nullable();
$table->string('alt_image')->nullable();
$table->longText('description')->nullable();
$table->unsignedInteger('parent_id')->nullable() ;
$table->unsignedBigInteger('ref_id')->nullable() ;
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('categories');
}
};
lang_id
forlocale
of the URL and the language for the categoryen, ar, es
ref_id
for thecategory_refs
id
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('category_refs', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('image')->nullable();
$table->string('icon')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('category_refs');
}
};
The relationship between the two tables will be one to many
. And here are the model classes for getting the shared data we created ref()
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
protected $guarded = ['id'];
public function ref(){
return $this->belongsTo(CategoryRef::class,'ref_id');
}
}
And to get all the available translations for reference, we created children() function
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class CategoryRef extends Model
{
use HasFactory;
protected $guarded = ['id'];
public function children(){
return $this->hasMany(Category::class,'ref_id');
}
}
public function store(CategoryStoreRequest $request)
{
$data = $request->validated();
$dataRef = [];
if($request->hasFile('image')){
$dataX = $this->saveImageAndThumbnail($request->file('image'),true);
}
if(empty($request->ref_id)){
$categoryRef = CategoryRef::create($dataRef);
$data['ref_id']= $categoryRef->id;
}
$success['data'] = Category::create($data)
$success['success'] = true;
return response()->json( $success,200);
}
Here is the store function of the controller, if we create a category for the first time we create a category reference but if we create a translation of an existing category we send the ref_id
with the request.
2 thoughts on “Laravel 9 Localization With Multi-languages Database Tutorial”
Comments are closed.