Comments (9)
How about change private $baseroute = '';
to public $baseroute = '';
So,
$router = new \Bramus\Router\Router();
// before route middleware
$router->before('GET', '/(.*)', function($slug) {
if (in_array(segment(1), ['en', 'id'])) {
Session::set('language', segment(1));
} else {
redirect(DOMAIN . $slug);
}
});
$router->baseroute = '/' . segment(1);
$router->all('/(.*)', function($slug) {
echo $slug; // domain.com/en/title -> title
});
I just thinking about on/off multilingual, like
$ismultilingual = true;
$router = new \Bramus\Router\Router();
if ($ismultilingual) {
// before route middleware
$router->before('GET', '/(.*)', function($slug) {
if (in_array(segment(1), ['en', 'id'])) {
Session::set('language', segment(1));
} else {
redirect(DOMAIN . $slug);
}
});
$router->baseroute = '/' . segment(1);
}
$router->all('/(.*)', function($slug) {
echo $slug; // domain.com/en/title -> title
});
from router.
If I had to manage a multilingual site, I would do something like this (of course, you should have your class in a different file):
class Router extends \Bramus\Router\Router
{
/**
* List of accepted languages
* @var array
*/
public $langs = [
'en',
'id',
];
/**
* Default language
* @var string
*/
public $default_lang = 'en';
protected function getCurrentUri()
{
$uri = parent::getCurrentUri();
$uri = $this->parseLang($uri);
return $uri;
}
/**
* Parse the uri string
*
* @param string $uri
* @return string
* @throws \Exception
*/
protected function parseLang($uri)
{
$arr_uri = explode('/', $uri);
if (in_array($arr_uri[1], $this->langs)) {
// if a lang was given and it is in the list
$lang = $arr_uri[1];
$uri = $arr_uri[2];
} else {
// if no lang was given or the one given is not in the list
if (!in_array($this->default_lang, $this->langs)) {
// exception if the default language is not in the list
throw new \Exception('The default language is not valid.');
}
$lang = $this->default_lang;
}
$this->useLang($lang);
return $uri;
}
/**
* Use the language string
* @param string $lang Language string
*/
protected function useLang($lang)
{
// do whatever you want with lang
var_dump($lang);
}
}
$router = new Router;
$router->get('/hello', function () {
echo 'Hello World!';
});
$router->run();
What this does is quite simple:
- if a language is given, it removes it from the uri;
- if no language is given or the language is invalid, it uses the default language;
- if the default language is not in the
lang
array, it throws an exception; - it calls the
Router::useLang($lang)
method and passes it the language (even if the default was used).
I will do a PR to change the visibility of Router::handle()
from private
to protected
, so that a the signature of Router::run()
can be modified. If this is accepted, you would be able to do this:
$router->run(null, function ($lang) { /* use $lang */ });
This would lead to a cleaner code than having a Router::useLang()
method.
Another option would be to have a property containing a callable, which would be called (if it is callable) by Router::parseLang()
.
from router.
Hi all,
Checking the replies further I can see benefits into making $baseroute
accessible from outside the class itself, as suggested by @ncaneldiee. It does indeed allow one to get the wanted result rather quickly. It has some downsides however:
- it necessitates the availability of the posted
segment
function - the codefragment which alters
$baseroute
is quite mindboggling - the handling function does not know which language it is serving (using the session to persist it is a hack on a hack ;))
The suggested quick fix to "just use mount()
" also has the first downside mentioned above: it necessitates the availability of the segment
function. On the upside though said function could be altered in such a way so that it doesn't need to persist the $language
in the session.
Changing the visibility of Router::handle()
as suggested by @tleb is not totally clear to me right now. I think I'm missing the actual change to Router::run()
in order to see how it would work. I'm also a bit sceptical about it right now, as it would require two adjustments: one to Router::handle()
and one to Router::run()
.
Based on the idea of the custom Router
class as suggested by @tleb I had my own take at this problem. The result is a MultilangRouter
class which:
- Is straightforward (it reads rather easy if I say so myself)
- Is self-organising: if the requested language is not one of the specified ones it redirects to the default language
- Requires no change in the base
Bramus\Router\Router
- Passes the language to the callback function so that it knows which language it is serving
<?php
// Require the base Router classes
require_once __DIR__ . '/../src/Bramus/Router/Router.php';
/**
* A Multilingual Router
*/
class MultilangRouter extends \Bramus\Router\Router {
/**
* The Default language
* @var string
*/
private $defaultLanguage;
/**
* List of allowed languages
* @var array
*/
private $allowedLanguages= [];
/**
* A Multilingual Router
* @param array $allowedLanguages
* @param string $defaultLanguage
*/
public function __construct(array $allowedLanguages, $defaultLanguage) {
// Store passed in data
$this->allowedLanguages = $allowedLanguages;
$this->defaultLanguage = (in_array($defaultLanguage, $allowedLanguages) ? $defaultLanguage : $allowedLanguages[0]);
// Visiting the root? Redirect to the default language index
$this->match('GET|POST|PUT|DELETE|HEAD', '/', function() {
header('location: /' . $this->defaultLanguage);
exit();
});
// Create a before handler to make sure the language checks out when visiting anything but the root.
// If the language doesn't check out, redirect to the default language index
$this->before('GET|POST|PUT|DELETE|HEAD', '/([a-z0-9_-]+)(/.*)?', function($language, $slug = null) {
// The given language does not appear in the array of allowed languages
if (!in_array($language, $this->allowedLanguages)) {
header('location: /' . $this->defaultLanguage);
exit();
}
});
}
}
// Create a Multilingual Router. It will automatically validate the language parameter in the URL. If that fails it will redirect to the default language.
$router = new MultilangRouter(
['en','nl','fr'], //= allowed languages
'nl' // = default language
);
$router->get('/([a-z0-9_-]+)', function($language) {
exit('This is the ' . $language . ' index');
});
$router->get('/([a-z0-9_-]+)/([a-z0-9_-]+)', function($language, $slug) {
exit('This is the ' . $language . ' version of ' . $slug);
});
$router->get('/([a-z0-9_-]+)/(.*)', function($language, $slug) {
exit('This is the ' . $language . ' version of ' . $slug . ' (multiple segments allowed)');
});
// Thunderbirds are go!
$router->run();
(Note that I have defined the pattern for the language as being [a-z0-9_-]+
instead of\w{2}
as this pattern will also catch requested to/whatever
for example, enforcing a redirect to the default language.)
(Also note that one can still use the mount()
function if needed, in case that first [a-z0-9_-]+
in each pattern might be too overwhelming for one. But that brings us back to the very first suggested fix: just usemount()
)
The MultilangRouter
works fine with these URLs:
/nl
→"This is the nl index"
/en
→"This is the en index"
/fr
→"This is the fr index"
/nl/foo
→"This is the nl version of foo"
/en/foo
→"This is the en version of foo"
/fr/foo
→"This is the fr version of foo"
/nl/foo/bar
→"This is the nl version of foo/bar (multiple segments allowed)"
/en/foo/bar
→"This is the en version of foo/bar (multiple segments allowed)"
/fr/foo/bar
→"This is the fr version of foo/bar (multiple segments allowed)"
It also redirects nicely in case a faulty URL (viz. one with an invalid $language
is entered):
/
→ redirect to/nl
/de
→ redirect to/nl
/de/foo
→ redirect to/nl
/de/foo/bar
→ redirect to/nl
Regards,
B!
from router.
I adapted my class to what @ncaneldiee wanted to do: execute a function which has as a parameter the language, then create routes as normal.
As you showed in your example, their are different ways of implementing a multilingual router. One could be what @ncaneldiee is looking for, another one could be the one you showed and I am sure many other ways could be found.
Of course, no solution is perfect, they all have their advantages and drawbacks.
The reason I wanted to change the visibility of Router::handle()
to protected
is because it is required to pass a callable from Router::run()
to Router::parseLang()
(in my example). Here is the path it would take:
Router::run()
Router::handle()
Router::getCurrentUri()
Router::parseLang()
I know this seems weird, but, in a multilingual router, passing a callable to the run
method which would get the language seems pretty logic (to me).
// could be used to set some value in the configuration, for example
$router->run(null, 'set_lang_config');
This is just an example which (to me) shows that most/every methods in a non-final class should be set to protected
, so that the class behaviour can be adjusted to the needs and requirements of each user of the package.
Of course, this is just my opinion, and I'll just remove the concerned commit from #19 if you disagree.
from router.
@tleb idea about the custom router class is very nice (i will use this way), but i not too sure about changing the visibility of handle()
method
Maybe seems to be good if add @bramus example in readme.md
as an example of implementing a multilingual router, very easy to understand
Many thanks all
from router.
To be honest, I don't see the problem of setting handle()
to protected
. I now (since I had problems with named constructors and inheritance) put the methods I was putting in private
in protected
. protected
simply allows the addition of optional parameters, which is something that doesn't go against the L of SOLID.
from router.
Hi @tleb. What I'm still missing to understand your example completely is the altered Router::run()
method (viz. the second argument). Could you post the complete code of the altered versions (some branch, or a gist)?
from router.
Ok, I got lost in my own explanation, and ended up saying something completely wrong. Sorry I was blocked in my idea.
What I was thinking about would need to a complete rewriting of the class.
I'll remove the concerned commit.
from router.
Heh, have that too sometimes :-)
from router.
Related Issues (20)
- Multilingual default route without lang
- Generating URLs HOT 1
- Form submits a GET request without a submit-button HOT 1
- um... controller blank page HOT 1
- how to run php file with command .for example: php index.php Home index HOT 1
- How can i serve the static folder using Router? HOT 1
- $match arrgument not being used anywhere
- after router middleware not keeping states?
- How can I setup this middleware? HOT 1
- router in subfolder HOT 2
- There is no Cache funciton! so Please Add Caching Feature! HOT 1
- 404 error executing more than twice
- No executing middleware HOT 2
- I have uploaded my system that works with this router to the hosting, and it's not functioning. HOT 1
- Post values not coming
- Accessors and named routes
- How to do the following HOT 4
- Lazy loading class
- Undefined array key warning HOT 1
- Tag 1.6.x patch release?
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from router.