Giter Site home page Giter Site logo

tboronczyk / localization-middleware Goto Github PK

View Code? Open in Web Editor NEW
26.0 4.0 9.0 78 KB

PSR-15 middleware to assist primarily with language-based content negotiation and various other localization tasks

PHP 100.00%
middleware slim-framework psr-15 content-negotiation language

localization-middleware's Introduction

Localization Middleware

tboronczyk codecov FOSSA Status

Middleware to assist primarily with language-based content negotiation and various other localization tasks. It determines the appropriate locale based on the client’s request and sets an attribute on the request object making the value available to the rest of your application. Its callback hook offers a convenient way to initialize other libraries or execute code based on the locale value.

Version 2 conforms to PSR-15. Use version ^1.4 if you require the so-called “Double Pass” approach using __invoke().

Installation

Localization Middleware is installable via Composer.

composer require boronczyk/localization-middleware

Basic Example

Here is a basic usage example:

use Boronczyk\LocalizationMiddleware;

// register the middleware with your PSR-15 compliant framework
$availableLocales = ['en_US', 'fr_CA', 'es_MX', 'eo'];
$defaultLocale = 'en_US';
$app->add(new LocalizationMiddleware($availableLocales, $defaultLocale));

// reference the locale in your route callback
$app->get('/', function ($req, $resp, $args) {
    $attrs = $req->getAttributes();
    $locale = $attrs['locale'];
    return $resp->write("The locale is $locale.");
});

More Advanced Example

Here is a more advanced usage example:

use Boronczyk\LocalizationMiddleware;

// instanciate the middleware
$availableLocales = ['en_US', 'fr_CA', 'es_MX', 'eo'];
$defaultLocale = 'en_US';
$middleware = new LocalizationMiddleware($availableLocales, $defaultLocale);

// specify the order in which inputs are searched for the locale
$middleware->setSearchOrder([
    LocalizationMiddleware::FROM_CALLBACK,
    LocalizationMiddleware::FROM_URI_PATH,
    LocalizationMiddleware::FROM_URI_PARAM,
    LocalizationMiddleware::FROM_COOKIE,
    LocalizationMiddleware::FROM_HEADER
]);

// attempt to identify the locale using a callback
$middleware->setSearchCallback(
    function (Request $req) use (Container $c): string {
        $db = $c->get('GeoIp2Database');
        switch ($db->country($req->getAttribute('ip_address')) {
            case 'CA':
                return 'fr_CA';
            case 'US':
                return 'en_US';
            case 'MX':
                return 'es_MX';
            default:
                return '';
        }
    }
);

// execute logic once the locale has been identified
$middleware->setLocaleCallback(function (string $locale) {
    putenv("LANG=$locale");
    setlocale(LC_ALL, $locale);
    bindtextdomain('messages', 'Locale');
    bind_textdomain_codeset('messages', 'UTF-8');
    textdomain('messages');
});

// change the name of the uri parameter identifying the locale
$middleware->setUriParamName('hl');

// register the middleware with your PSR-15 compliant framework
$app->add($middleware);

// reference the locale in your route callback
 $app->get('/', function ($req, $resp, $args) {
    $attrs = $req->getAttributes();
    $locale = $attrs['locale'];
    $text = sprintf(_('The locale is %s.'), $locale);
    return $resp->write($text);
});

Configurable Behavior

The middleware component’s behavior is configurable though the following methods:

  • setAvailableLocales(array $locales)
    Sets the list of available locales after an instance has already been created.

    $middleware->setAvailableLocales(['en_US', 'fr_CA', 'pt_BR']);
    
  • setDefaultLocale(string $locale)
    Sets the default locale to return after an instance has already been created.

    $middleware->setDefaultLocale('fr_CA');
    
  • setSearchOrder(array $order)
    Sets the order in which inputs are searched for a suitable locale.

    $middleware->setSearchOrder([
        LocalizationMiddleware::FROM_URI_PATH,
        LocalizationMiddleware::FROM_URI_PARAM,
        LocalizationMiddleware::FROM_COOKIE,
        LocalizationMiddleware::FROM_HEADER
    ]);
    

    Adding or removing locale sources from the order modifies the search domain.

    // only search cookies and the Accept-Language header
    $middleware->setSearchOrder([
        LocalizationMiddleware::FROM_COOKIE,
        LocalizationMiddleware::FROM_HEADER
    ]);
    

    The available locale source constants are:

    • LocalizationMiddleware::FROM_URI_PATH
      Search for the locale in the URI path. The first directory value in the request path is considered the locale, for example https://example.com/en_US/foo.

    • LocalizationMiddleware::FROM_URI_PARAM
      Search for the locale in the URI parameter (the default parameter name is locale).

    • LocalizationMiddleware::FROM_COOKIE
      Search for the locale in cookies (the default cookie name is locale). Note: Using this will set a locale cookie for subsequent requests.

    • LocalizationMiddleware::FROM_HEADER
      Search for the locale in the HTTP Accept-Language header. Header searches make a best-effort search for locales, languages, and possible quality modifiers.

    • LocalizationMiddleware::FROM_CALLBACK
      Search for the locale using a custom callback function. The callback function is set with setSearchCallback().

    The default order is: FROM_URI_PATH, FROM_URI_PARAM, FROM_COOKIE, FROM_HEADER. Note that FROM_CALLBACK is not included by default.

  • setSearchCallback(callable $func)
    Sets a callback that is invoked when searching for the locale, offering the developer a chance to inject a locale of their choosing into the search. The callable’s signature is: function (Request $req): string.

    $middleware->setSearchCallback(
        function (Request $req) use (Container $c): string {
            $db = $c->get('GeoIp2Database');
            switch ($db->country($req->getAttribute('ip_address')) {
                case 'CA':
                    return 'fr_CA';
                case 'US':
                    return 'en_US';
                case 'MX':
                    return 'es_MX';
                default:
                    return '';
            }
        }
    );
    
  • setReqAttrName(string $name)
    Sets the name for the attribute attached to the request. The default name is locale.

    $middleware->setReqAttrName('lang');
    
    $app->get('/', function ($req, $resp, $args) {
        $attrs = $req->getAttributes();
        $lang = $attrs['lang'];
    });
    
  • setUriParamName(string $name)
    Sets the name for a URI parameter to specify the locale. The default name is locale.

    $middleware->setUriParamName('lang');
    
    https://example.com/mypage?lang=es_MX
    
  • setCookieName(string $name)
    Sets the name of the cookie to store the determined locale. The default name is locale.

    $middleware->setCookieName('lang');
    
  • setCookiePath(string $path)
    Sets the path of the cookie for which it will be returned by the client. The default path is /.

    $middleware->setCookiePath("/dir");
    
  • setCookieExpire(int $secs)
    Sets the duration of the locale cookie. The default value is 30 days.

    $middleware->setCookieExpire(3600); // 1 hour
    
  • setLocaleCallback(callable $func)
    Sets a callback that is invoked after the middleware identifies the locale, offering the developer a chance to conveniently initialize other libraries or execute other code with the value. The callable’s signature is: function (string $locale).

    $middleware->setLocaleCallback(function (string $locale) {
        putenv("LANG=$locale");
        setlocale(LC_ALL, $locale);
        bindtextdomain('messages', 'Locale');
        bind_textdomain_codeset('messages', 'UTF-8');
        textdomain('messages');
    });
    

License

FOSSA Status

localization-middleware's People

Contributors

bakurin avatar fgagne avatar fossabot avatar tboronczyk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

localization-middleware's Issues

error when no Accept-Language header present

An error occurs when there is locale set (via params, cookies, etc.) and no Accept-Language header present. The expected behavior in this case is the default locale is returned.

Only set the cookie based on specific sources

I really like the idea of this implementation, but I'm struggling a bit with incorporating it into my system. What I'd like to be able to do with it is:

  • Set the locale very early in the middleware queue based on cookie or header
  • Override that locale selection using a per-user setting, but this must be done later in the queue after authentication has been done
  • Set the cookie only in the case of a per-user setting, not when the locale is chosen based on the header
  • Unset the cookie as the queue unwinds, if the user logged out

As far as I can tell, this can only be accomplished with a combination of:

  • two separately-configured copies of the middleware inserted at different places in the queue
  • changing the logic to allow for a default not to be set (can be done now in the locale callback)
  • allowing for the locale callback to return an indication that the cookie should not be set (I guess I could do this by throwing an exception in my callback, and wrapping the call to the middleware in a function that catches and ignores this exception, but that just really feels kludgy)

So, my question is, is there a clean way to do what I'm thinking of already and I'm just missing it? If not, does some of this sound generally useful if I were to do a PR?

The middleware overrides 'Set-Cookie' header

The method LocalizationMiddleware::__invoke() uses MessageInterface::withHeader() method and overrides the http headers set before. Probably MessageInterface::withAddedHeader() should be considered to use instead?

Slim dependency

This middleware currently requires Slim 3 because concrete type are used. The Slim types should be replaced with their PSR-7 interfaces to allow the middleware to be used with other frameworks.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.