Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fallback locale handler #731

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ The configuration options are:

- **supportedLocales** Languages of your app (Default: English & Spanish).
- **useAcceptLanguageHeader** If true, then automatically detect language from browser.
- **handleFallbackLocale** Manage fallback language.
- **hideDefaultLocaleInURL** If true, then do not show default locale in url.
- **localesOrder** Sort languages in custom order.
- **localesMapping** Rename url locales.
Expand Down
14 changes: 11 additions & 3 deletions src/Mcamara/LaravelLocalization/LanguageNegotiator.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,15 @@ public function __construct($defaultLocale, $supportedLanguages, Request $reques
* Quality factors in the Accept-Language: header are supported, e.g.:
* Accept-Language: en-UK;q=0.7, en-US;q=0.6, no, dk;q=0.8
*
* @param array $ignoreLanguages
*
* @return string The negotiated language result or app.locale.
*/
public function negotiateLanguage()
public function negotiateLanguage( $ignoreLanguages = [] )
{
$matches = $this->getMatchesFromAcceptedLanguages();
foreach ($matches as $key => $q) {
if( in_array($key, $ignoreLanguages) ) continue;

$key = ($this->configRepository->get('laravellocalization.localesMapping')[$key]) ?? $key;

Expand All @@ -113,21 +116,26 @@ public function negotiateLanguage()
// Search for acceptable locale by 'regional' => 'af_ZA' or 'lang' => 'af-ZA' match.
foreach ( $this->supportedLanguages as $key_supported => $locale ) {
if ( (isset($locale['regional']) && $locale['regional'] == $key) || (isset($locale['lang']) && $locale['lang'] == $key) ) {
if( in_array($key_supported, $ignoreLanguages) ) continue 2;
return $key_supported;
}
}
}
// If any (i.e. "*") is acceptable, return the first supported format
if (isset($matches['*'])) {
reset($this->supportedLanguages);

while ( in_array( key($this->supportedLanguages), $ignoreLanguages) ) {
next($this->supportedLanguages);
}

return key($this->supportedLanguages);
}

if ($this->use_intl && !empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
$http_accept_language = Locale::acceptFromHttp($_SERVER['HTTP_ACCEPT_LANGUAGE']);

if (!empty($this->supportedLanguages[$http_accept_language])) {
if (!empty($this->supportedLanguages[$http_accept_language]) && !in_array( $http_accept_language, $ignoreLanguages)) {
return $http_accept_language;
}
}
Expand All @@ -136,7 +144,7 @@ public function negotiateLanguage()
$remote_host = explode('.', $this->request->server('REMOTE_HOST'));
$lang = strtolower(end($remote_host));

if (!empty($this->supportedLanguages[$lang])) {
if (!empty($this->supportedLanguages[$lang]) && !in_array( $lang, $ignoreLanguages)) {
return $lang;
}
}
Expand Down
57 changes: 55 additions & 2 deletions src/Mcamara/LaravelLocalization/LaravelLocalization.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ class LaravelLocalization
*/
protected $defaultLocale;

/**
* Default fallback locale.
*
* @var string
*/
protected $defaultFallbackLocale;

/**
* Supported Locales.
*
Expand Down Expand Up @@ -136,13 +143,19 @@ public function __construct()
$this->request = $this->app['request'];
$this->url = $this->app['url'];

// set default locale
$this->defaultLocale = $this->configRepository->get('app.locale');
$supportedLocales = $this->getSupportedLocales();

// set default locale
$this->defaultLocale = $this->configRepository->get('app.locale');
if (empty($supportedLocales[$this->defaultLocale])) {
throw new UnsupportedLocaleException('Laravel default locale is not in the supportedLocales array.');
}

// set default fallback locale
$this->defaultFallbackLocale = $this->configRepository->get('app.fallback_locale');
if (empty($supportedLocales[$this->defaultFallbackLocale])) {
throw new UnsupportedLocaleException('Laravel default fallback locale is not in the supportedLocales array.');
}
}

/**
Expand Down Expand Up @@ -190,6 +203,14 @@ public function setLocale($locale = null)
}
}

// Method available from laravel 7.21.0
if (method_exists($this->app, 'setFallbackLocale')) {
$this->app->setFallbackLocale($this->getCurrentFallbackLocale());
} else {
$this->configRepository->set('app.fallback_locale', $this->getCurrentFallbackLocale());
$this->app['translator']->setFallback($this->getCurrentFallbackLocale());
}

$this->app->setLocale($this->currentLocale);

// Regional locale such as de_DE, so formatLocalized works in Carbon
Expand Down Expand Up @@ -413,6 +434,16 @@ public function getDefaultLocale()
return $this->defaultLocale;
}

/**
* Returns default fallback locale.
*
* @return string
*/
public function getDefaultFallbackLocale()
{
return $this->defaultFallbackLocale;
}

/**
* Return locales mapping.
*
Expand Down Expand Up @@ -581,6 +612,28 @@ public function getCurrentLocale()
return $this->configRepository->get('app.locale');
}

/**
* Returns current fallback language.
*
* @return string current fallback language
*/
public function getCurrentFallbackLocale()
{
if ($this->configRepository->get('laravellocalization.handleFallbackLocale') == 'related'){
if($this->checkLocaleInSupportedLocales(explode('_', $this->getCurrentLocaleRegional())[0]))
return explode('_', $this->getCurrentLocaleRegional())[0];
}

if (($this->configRepository->get('laravellocalization.handleFallbackLocale') == 'header') && !$this->app->runningInConsole()) {
$negotiator = new LanguageNegotiator($this->getDefaultFallbackLocale(), $this->getSupportedLocales(), $this->request);

return $negotiator->negotiateLanguage([$this->getCurrentLocale()]);
}

// or get application default fallback language
return $this->configRepository->get('app.fallback_locale');
}

/**
* Returns current regional.
*
Expand Down
8 changes: 8 additions & 0 deletions src/config/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,14 @@
// If false, system will take app.php locale attribute
'useAcceptLanguageHeader' => true,

// Handle the locale used when translation key don't match result at current locale
//
// Valid options are
// - false (Not handle fallback locale and use app.fallback_locale)
// - 'related' (Use current locale without country code as fallback locale if it is in supported locales. e.g "en-US" will use "en", "pt-BR" will use "pt")
// - 'header' (Use the first language from browser as fallback locale if it is not equals the app locale, else use the second one)
'handleFallbackLocale' => false,

// If `hideDefaultLocaleInURL` is true, then a url without locale
// is identical with the same url with default locale.
// For example, if `en` is default locale, then `/en/about` and `/about`
Expand Down