Skip to content

Commit

Permalink
Merge pull request #3 from omaralalwi/feat/supportlocal-switcher-midd…
Browse files Browse the repository at this point in the history
…lewares

feat/ support language switcher middlewares
  • Loading branch information
omaralalwi authored Nov 20, 2024
2 parents c54f692 + 6a3bc76 commit 6a79e1e
Show file tree
Hide file tree
Showing 7 changed files with 231 additions and 23 deletions.
70 changes: 69 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ Its lightweight design and flexibility make it an excellent choice for applicati
- [Defining LexiTranslatable Models](#defining-lexitranslatable-models)
- [update or Create Translations](#update-or-Create-translations)
- [Retrieving Translations](#retrieving-translations)
- [Cache Handling](#cache-handling)
- [More Examples](#more-examples)
- [Helper Functions](#helper-functions)
- [Usage in Queries](#Usage-in-Queries)
- [Cache Handling](#cache-handling)
- [Using middlewares](#using-middlewares-for-locale-management)
- [Testing](#testing)
- [Alternative Solutions](#alternative-solutions)
- [Changelog](#changelog)
Expand Down Expand Up @@ -190,6 +191,72 @@ If you add additional locales for translations, make sure to include them in the

---

### Using Middlewares for Locale Management

**(this is Optional)**

**This section is optional , it is additional features to handle language switching for API Or Web , without need to install another package .**

LexiTranslate provides built-in middlewares to handle locale switching seamlessly for both web and API requests.
These middlewares simplify the process of dynamically setting the application's locale based on user input or request headers.


#### **1 . WebLocalized Middleware**

The `WebLocalized` middleware is designed to handle locale switching for web requests. It determines the locale based on the following order of priority:
- The `locale` route parameter.
- The `locale` query string parameter.
- The current session's locale.
- The `locale` stored in cookies.
- The application's default locale.

#### Registering the Middleware

```php
// Other middlewares...
'localized.web' => \Omaralalwi\LexiTranslate\Middleware\WebLocalized::class,
```
[Register Middleware in Laravel](https://laravel.com/docs/11.x/middleware#registering-middleware)

#### Applying the Middleware to Routes

just add `locale` prefix for all routes that want to apply multilingual for them .

```php
Route::prefix('{locale}')->middleware('localized.web')->group(function () {
// your routes
});
```
OR
```php
Route::middleware(['localized.web'])->group(function () {
Route::get('/{locale}/dashboard', function () {
return view('dashboard');
});
});
```

#### **2. ApiLocalized Middleware**

The `ApiLocalized` middleware is designed for API requests. It sets the application's locale based on the value of a custom header defined in your configuration file (`api_locale_header_key`). If the header is not provided, it defaults to the application's default locale.

#### Registering the Middleware

```php
// Other middlewares...
'localized.api' => \Omaralalwi\LexiTranslate\Middleware\WebLocalized::class,
```

#### Applying the Middleware to API Routes

```php
Route::middleware(['localized.api'])->group(function () {
// your routes
});
```

---

## Features

- **Dynamic Morph Relationships:** Manage translations across different models with ease, thanks to its dynamic morph able relationships.
Expand All @@ -198,6 +265,7 @@ If you add additional locales for translations, make sure to include them in the
- **Simple, Intuitive API:** A clean and consistent API for adding, retrieving, and managing translations.
- **Eloquent-Friendly:** Seamlessly integrates with Laravel's Eloquent ORM, making it easy to work with translated data while maintaining the power of Laravel’s query builder.
- **Search and Filter:** Scopes for search and filters by translations .
- **Built-in middlewares** to handle locale switching seamlessly for both web and API requests.
- **Feature Tests:** supported with Feature Tests .
- **Customize table name:** in config file you can change `table_name` to any name as you want.

Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"homepage": "https://github.com/omaralalwi/lexi-translate",
"license": "MIT",
"type": "library",
"version": "1.0.5",
"version": "1.0.6",
"authors": [
{
"name": "Omar alalwi",
Expand Down
36 changes: 30 additions & 6 deletions config/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,20 @@
| Set this to true to enable caching of translations. Caching helps improve
| performance, especially for large-scale applications by reducing database
| queries. Disable caching during development or if your data is highly
| dynamic to avoid outdated translations.
|
*/

'use_cache' => true,


/*
|--------------------------------------------------------------------------
| Cache Key Prefix
|--------------------------------------------------------------------------
|
| Define a prefix for translation cache keys. This helps to avoid key
| collisions when using a shared cache storage across multiple systems.
|
*/
'cache_prefix' => 'lexi_trans',

/*
Expand All @@ -39,21 +47,37 @@
|
| If caching is enabled, you can define the cache duration (TTL) in hours.
| The translations will remain cached for this period, after which the cache
| use the default cache TTL from the application.
| will be refreshed. If this value is not set, it will use the default cache
| TTL from the application's caching configuration.
|
| Example: 6 hours is default cache settings
| Example: 6 hours is the default cache duration.
|
*/
'cache_ttl' => 6, // Cache translations for 6 hours

/*
|--------------------------------------------------------------------------
| API Locale Header Key
|--------------------------------------------------------------------------
|
| Define the header key used to specify the desired locale in API requests.
| This value is checked in the middleware to set the application's locale
| dynamically based on the incoming request.
|
| Example: 'Accept-Language'
|
*/
'api_locale_header_key' => 'Accept-Language',

/*
|--------------------------------------------------------------------------
| Supported Locales
|--------------------------------------------------------------------------
|
| Specify the list of supported locales in your application. These locales
| are used when creating translations from requests. You can add as many
| performance, avoid adding unused locales.
| are used when creating translations from requests. Add as many locales as
| your application needs, but avoid including unused locales to improve
| performance and manageability.
|
| Example: ['en', 'ar', 'fr', 'es']
|
Expand Down
39 changes: 39 additions & 0 deletions src/Middleware/ApiLocalized.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

namespace Omaralalwi\LexiTranslate\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Config;
use Symfony\Component\HttpFoundation\Response;
use Omaralalwi\LexiTranslate\Traits\HasLocale;

class ApiLocalized
{
use HasLocale;

/**
* Handle an incoming API request and set the application locale.
*
* @param \Illuminate\Http\Request $request The incoming HTTP request.
* @param \Closure $next The next middleware or handler in the pipeline.
* @return \Symfony\Component\HttpFoundation\Response The processed HTTP response.
*/
public function handle(Request $request, Closure $next): Response
{
$defaultLocal = app()->getLocale();
$headerFlag = Config::get('lexi-translate.api_locale_header_key');
$requestLocale = $request->header($headerFlag, $defaultLocal);
$locale = $this->getValidatedLocale($requestLocale);

if(!$locale) {
$locale = app()->getLocale();
}

App::setLocale($locale);
$request->merge(['locale' => $locale]);

return $next($request);
}
}
41 changes: 41 additions & 0 deletions src/Middleware/WebLocalized.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

namespace Omaralalwi\LexiTranslate\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\Config;
use Symfony\Component\HttpFoundation\Response;
use Omaralalwi\LexiTranslate\Traits\HasLocale;

class WebLocalized
{
use HasLocale;

/**
* Handle an incoming request to switch the app locale.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return \Symfony\Component\HttpFoundation\Response
*/
public function handle(Request $request, Closure $next): Response
{
$requestLocale = $request->route('locale') ?: $request->get('locale') ?: session('locale') ?: cookie('locale');
$locale = $this->getValidatedLocale($requestLocale);

if(!$locale) {
$locale = app()->getLocale();
}

if ($locale && in_array($locale, Config::get('lexi-translate.supported_locales'))) {
App::setLocale($locale);
session(['locale' => $locale]);
cookie()->queue(cookie('locale', $locale, 60 * 24 * 365));
}

return $next($request);
}
}
48 changes: 48 additions & 0 deletions src/Traits/HasLocale.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

namespace Omaralalwi\LexiTranslate\Traits;

use Illuminate\Support\Facades\Config;
use Omaralalwi\LexiTranslate\Enums\Language;

trait HasLocale
{
/**
* Get the supported locales for translations.
*
* @return array
*/
public function getSupportedLocales(): array
{
return Config::get('lexi-translate.supported_locales', []);
}

/**
* Get the validated locale.
*
* This function checks if the provided locale is in the list of supported locales.
* If the locale is valid, it returns it; otherwise, it returns the application's default locale.
*
* @param string|null $locale The locale to check.
* @return string The validated locale.
*/
public function getValidatedLocale(?string $locale): string
{
return ($locale && $this->isValidLocale($locale)) ? $locale : app()->getLocale();
}

/**
* Check if the given locale is valid (exists in the supported locales).
*
* @param string|null $locale The locale to check.
* @return bool True if the locale is valid, false otherwise.
*/
public function isValidLocale(?string $locale): bool
{
if (!$locale) {
return false;
}

return in_array($locale, $this->getSupportedLocales());
}
}
18 changes: 3 additions & 15 deletions src/Traits/LexiTranslatable.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,15 @@

namespace Omaralalwi\LexiTranslate\Traits;

use Illuminate\Support\Facades\Config;
use Omaralalwi\LexiTranslate\Enums\Language;
use Omaralalwi\LexiTranslate\Models\Translation;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Support\Facades\Cache;
use Illuminate\Database\Eloquent\Collection;
use Omaralalwi\LexiTranslate\Models\Scones\TranslationsScopes;
use Omaralalwi\LexiTranslate\Traits\HasLocale;

trait LexiTranslatable
{
use HasCache, TranslationsScopes;
use HasCache, TranslationsScopes, HasLocale;

/**
* Get the list of translatable fields for the model.
Expand All @@ -24,16 +22,6 @@ public function getTranslatableFields(): array
return $this->translatableFields ?? [];
}

/**
* Get the supported locales for translations.
*
* @return array
*/
public function getSupportedLocales(): array
{
return Config::get('lexi-translate.supported_locales',);
}

/**
* Retrieve fresh translations without caching.
*
Expand All @@ -60,7 +48,7 @@ public function translations(): MorphMany
*/
public function translate(string $column, ?string $locale = null): string
{
$locale = $locale && in_array($locale, $this->getSupportedLocales()) ? $locale : app()->getLocale();
$locale = $this->getValidatedLocale($locale);
$originalText = '';

$modelType = class_basename($this);
Expand Down

0 comments on commit 6a79e1e

Please sign in to comment.