nikic / FastRoute

Fast request router for PHP
Other
5.13k stars 444 forks source link

remove defualt language from url #169

Open perspolise opened 6 years ago

perspolise commented 6 years ago

I have Multi language CMS For Front and Admin Page.

For English language(defualt without en):

example.com/articles/

For France Language(with fr):

example.com/fr/articles/

Now, for my url Like This code:

$dispatcher = FastRoute\simpleDispatcher(function(FastRoute\RouteCollector $r) {
    $r->addRoute('GET', '/{lang:(?:en|fr)}/articles', 'get_all_users_handler');
});

$httpMethod = $_SERVER['REQUEST_METHOD'];
$uri = $_SERVER['REQUEST_URI'];

// Strip query string (?foo=bar) and decode URI
if (false !== $pos = strpos($uri, '?')) {
    $uri = substr($uri, 0, $pos);
}
$uri = rawurldecode($uri);

$routeInfo = $dispatcher->dispatch($httpMethod, $uri);
switch ($routeInfo[0]) {
    case FastRoute\Dispatcher::NOT_FOUND:
       echo 'BAd';
        break;
    case FastRoute\Dispatcher::METHOD_NOT_ALLOWED:
        $allowedMethods = $routeInfo[1];
        // ... 405 Method Not Allowed
        break;
    case FastRoute\Dispatcher::FOUND:
        $handler = $routeInfo[1];
        $vars = $routeInfo[2];
        echo 'Found Found';
        break;
}

My Url Routed And Work With (fr and en) path method:

example.com/en/articles and example.com/fr/articles.

But I need to remove default language from url. How do fix this problem ?!

aromot commented 6 years ago

From my experience, it is better to remove the language from the URI before using it with FastRoute.

// ...
$uri = rawurldecode($uri);
$parts = explode('/', ltrim($uri, '/'));
$lang = array_shift($parts);
$uriWithoutLang = implode('/', $parts);

$routeInfo = $dispatcher->dispatch($httpMethod, $uriWithoutLang );
// ...

:warning: Note: the piece of code above doesn't work properly if a URL is called without any language (example.com/articles), you have to set the default language and process the URI correctly.

perspolise commented 6 years ago

My Problem Exactly this: I need to Call Url for default language(en) Without language example.com/articles/ And Call For Another Language With fr example.com/fr/articles/

Example for Symphony url router

aromot commented 6 years ago

there are various ways to manage a default language. This could be the topic of en whole article... so basically our starting point is this: your website is multilingual and accepts a limited set of languages (for example : "en", "fr", "es", "de") and you have a default language (for example "en"). Here's the code:

// ...

// this is list of languages my site accepts
$languages = ['en', 'fr', 'es', 'de'];

// this is my default language
$defaultLang = 'en';

// $currentLang is the language I'll use to show the contents
$currentLang = $defaultLang;

$uri = ltrim(rawurldecode($uri), '/');
$parts = explode('/', $uri);

if( ! empty($parts) && in_array(strtolower($parts[0]), $languages)) {
    $currentLang = array_shift($parts);
}

// routableUri is the URI you'll provide to FastRoute
$routableUri = implode('/', $parts);

// ...

You can go further... for example, if no language is found in the URI, you can look into the request headers (the "accept" header).

perspolise commented 6 years ago

You test This Code?! I change your code for test to the function like this:

function urlss(){

$uri = $_SERVER['REQUEST_URI'];

$languages = ['en', 'fr', 'es', 'de'];

// this is my default language
$defaultLang = 'en';

// $currentLang is the language I'll use to show the contents
$currentLang = $defaultLang;

$uri = ltrim(rawurldecode($uri), '/');
$parts = explode('/', $uri);

if( ! empty($parts) && in_array(strtolower($parts[0]), $languages)) {
    $currentLang = array_shift($parts);
}

// routableUri is the URI you'll provide to FastRoute
$routableUri = implode('/', $parts);

return $routableUri;

}]

Now, test in my localhost:

localhost/cms/users

echo $routableUri;

And output is:

cms/users

this output not true for your idea and not work true. can u check your idea in full code and then share it. Thanks

Zvax commented 6 years ago

The easiest way would be to move the language after the "articles" bit, so that you could have an /{section}[/{lang}] route definition. You could also write a second route with the same handler but without the lang part, and you'd be settled.

aromot commented 6 years ago

@perspolise : no I didn't test the code I sent. It wasn't meant to be taken "as-is". The idea was more to give a track/hint. I think it's difficult to provide a code you can simply copy/paste and push the button to get it run (a dream we all share to save time and go running in fields of flowers). My main point is to invite you thinking of a strategy to manage the languages for your application. My basic suggestion is:

  1. define a list of available languages for your application,
  2. define a default language you'll use when, a. there is no language provided (localhost/cms/users), b. the provided language is unknown (localhost/ru/cms/users).
  3. in your code, get the currentLanguage (ex: en) and the routableURI, a URI without any language in it (ex: cms/users): this is exactly what the purpose of the code I provided in my previous message.
  4. call FastRoute (indeed we're in a thread related to that repository) using the routableURI and store the currentLanguage somewhere so that you'll be able to use it in your route handler.

I hope I was clear enough and good luck to you!

ldonis commented 6 years ago

@perspolise I had the same issue a couple of weeks a go, I was trying to implement exactly what you need for a website built with Slim framework

domain.com
domain.com/es
domain.com/es/about
domain.com/about

but, this is not possible :(
$r->get('/{lang}/{page}')

I found a (possible) solution building a dispatcher using FastRoute and parsing my url manually.

Url parsing (pseudo code):
$langs = ['en', 'es'];
$url = '/es/about';
if ($url string has any of $langs) then
remove url id from url
define('lang', 'es') so, now I can use the selected language from anywhere in my page code

My new url after removing the language:
$url = '/about';

You can see the working code here

Now I only had to build a dispatcher for my well formatted url (without language id on it)

// New RouteCollector 
$router = new \FastRoute\RouteCollector(
    new \FastRoute\RouteParser\Std,
    new \FastRoute\DataGenerator\GroupCountBased
);

// Building routes for website
$router->get('/', 'landing');
$router->get('/about/', 'about');

// Create new dispatcher object
$dispatcher = new \FastRoute\Dispatcher\GroupCountBased($router->getData());

// Dispatch for the current route
// remember that at this point my $url is "/about" and my language is in a defined constant
$dispatcher = $dispatcher->dispatch('GET', $url);

// Website found HTTP_OK
if ($dispatcher[0] === \FastRoute\Dispatcher::FOUND) {
        // code to show my website
}

You can see the working code here

This is just an experiment just to solve the problem with the optional language parameter in url, I'm working so hard testing this "framework" to migrate my websites from Slim framework some point in the future.

Any comments are always welcomed :)

perspolise commented 5 years ago

@ldonis : Working code link is not found. please update

SignpostMarv commented 5 years ago

Personal preference:

$dispatcher = FastRoute\simpleDispatcher(function(FastRoute\RouteCollector $r) {
    $r->addRoute('GET', '/{lang:(?:en|fr)}/articles', 'get_all_users_handler');
    $r->addRoute('GET', '/articles', 'get_all_users_handler');
});

Where lang is defined as: $lang = $args['lang'] ?? $default_lang;