I have a i18n route in Li3 that looks like:
Router::connect('/{:locale:[a-z]{2}/{:args}', [], [
'continue' => true,
'persist' => ['locale'],
]);
This way, when a user (or a crawler) enters my site with a language prefix, the locale is used to generate every link on the site.
For SEO purposes, I need to generate URLs in other locales such as:
GET /en/profile/john-doe
Canonical URL: https://www.example.com/en/profile/john-doe
Link hreflang for es: https://www.example.com/es/profile/john-doe
Link hreflang for pt: https://www.example.com/pt/profile/john-doe
My currency approach is cloning the current request, changing the locale, removing locale from the persist array, and using $request->to('url', ['absolute' => true]);.
But I can't get rid of the locale.
Any suggestions on how to address this?
I finally solved it extending HTML helper class:
use lithium\template\helper\Html as BaseHtml;
class Html extends BaseHtml
{
/**
* Returns href lang link for a locale for the current request.
*
* #param string $locale
* #return string <link />
*/
public function getHrefLinkForLocale($locale)
{
return $this->link(
'Canonical URL for ' . $locale,
compact('locale') + $this->getRequest()->params,
[
'absolute' => true,
'rel' => 'alternate',
'type' => 'alternate',
'hreflang' => $locale,
]
);
}
}
Related
I am trying to use the google indexer api with symfony and therefore i need to generate the same URLs dynamically from my job-entity (in the database) like i am already using in the frontend.
My controller function looks (reduced) like this
/**
*
* #Route({
* "de": "/profile/{name}-{id}/career/{jobname}-{jobid}",
* }, name="somename")
*/
public function detailfunction($name, $id, $jobname, $jobid)
{ // some code
}
In my frontend i get the following url rendered by twig (path function):
https://www.mydomain.xy/profile/This+is+a+company+name-23/career/Worker+Montage+%2528mwd%2529-135
So now i need to send the exact same url to google so it updates the index whenever this page is modified.
I try to generate this url in the controller of my "google indexer" function like this:
$job = $this->getDoctrine()->getRepository(Jobs::class)->findBy(....);
$url = $this->generateUrl('somename', array('name' => $job->getCompany()->getName(),
'id' => $job->getCompany()->getid(),
'jobname" => $job->getTitle(),
'jobid' => $job->getId()));
// Debug
echo $url;
Unfortunately it outputs "ERROR : Parameter "jobname" for route "somename" must match "[^/]++" ("Worker Montage (m/w/d)" given) to generate a corresponding URL"
So it doesn´t encode the data that comes from the database for the url generator. I have been wondering how symfony (or twig as well) actually encodes internally but i am completely lost and very thankful for a hint.
Although i am still not sure of how exactly symfony / twig encodes the url, i wrote a small workaround function and call it before passing it to the generateURL function.
array('name' => $this->urlcleaner($job->getCompany()->getName()),
'id' => $job->getCompany()->getId(),
private function urlcleaner($string){
$string = preg_replace('/[^A-Za-z0-9\-ığşçöüÖÇŞİıĞ()\/]/', ' ', $string);
$string = str_replace("/","",$string);
$string = str_replace("-","",$string);
$string = urlencode($string);
return $string;
}
I am developing a Zend Framework 2 Application and now I want to implement a language switcher from where guest/registered user can choose the language they want, the thing I can't understand is how is it made in Zend Framework 2 using the storage ( not from urls ), I want to keep the preffered language of guest in the storage once he selects one, and for the registered users I can retrieve the preffered one from cookie/database and reuse it with storage. But where and how should I start/implement this?
Thank you in advance.
Setup your Locales in your global.config.php:
'locale' => array(
'default' => 'en_US',
'available' => array(
'de_DE' => 'Deutsch',
'nl_NL' => 'Dutch',
'en_US' => 'English',
'fr_FR' => 'French',
),
),
So in your Application\Module.php you can add a method which sets the default Zend\Translator\Translator:
class Module {
public function onBootstrap(MvcEvent $e)
{
$applicaton = $e->getApplication();
$serviceManager = $application->getServiceManager();
// Just a call to the translator, nothing special!
$serviceManager->get('translator');
$this->initTranslator($e);
// Etc, more of your bootstrap function.
}
protected function initTranslator(MvcEvent $event)
{
$serviceManager = $event->getApplication()->getServiceManager();
// Zend\Session\Container
$session = New Container('language');
$translator = $serviceManager->get('translator');
$translator
->setLocale($session->language)
->setFallbackLocale('en_US');
}
}
So now the default Locale is en_US as the session has no Locale available. For changing the locale you need to catch the users input and validate the available locales you support, provided in your global.config.php. So in order to change it you might need to add a controller action which catches the input of the user and sets the new locale. Example of the controller action without any form usage!
public function changeLocaleAction()
{
// New Container will get he Language Session if the SessionManager already knows the language session.
$session = new Container('language');
$language = $this->getRequest()->getPost()->language;
$config = $this->serviceLocator->get('config');
if (isset($config['locale']['available'][$language]) {
$session->language = $language;
$this->serviceLocator->get('translator')->setLocale($session->language);
}
}
The session allows the users to change their locale and remember it until the session ends, so they won't need to change it when they get back after a while. Hope this will help you and can help you to write some code to save it for your registered users on your application.
I am not sure, my approach will work or not. Please try:
We can have 3 params for translate method.
$translator->translate($message, $textDomain, $locale);
The $locale parameter is taken from the locale, set in the translator and that's why we usually not set manually in the code. So, you can use like below :
$localeVar = 'de_DE'; OR $localeVar = 'en_US'; // according to user's selection
echo $this->translate("Translate me", $textDomain, $localeVar);
You can have a key value pair - key can be user selected language and value can be any one of the language.
array(
'english' => 'en_US',
'deutch' => 'de_DE',
'frecnh' => 'fr_FR',
// other language
);
I was wondering if there was an easy and best practices way to make routes in CakePHP (routes.php file) to map userIDs to a vanity url?
I have (terrible way to do this) the following test code in my routes page:
$users = array
(
1 => 'firstname-lastname',
2 => 'firstname2-lastname2'
);
//profiles
foreach($users as $k => $v)
{
// LESSONS (Profiles)
Router::connect('/:user', array('controller' => 'teachers', 'action' => 'contentProfile', $k),
array('user' => '(?i:'.$v.')'));
}
The above code routes my teachers controller with conProfile as the action from:
mydomain.com/teachers/contentProfile/1
to
mydomain.com/firstname-lastname
Can I connect to the db from the routing page? Is that not a good idea in terms of performance? Let me know what's the best way to do this.
You can create a custom route class that will look up passed urls in the database and translate them to the correct user id. Setting a long cache time should mitigate any performance impact of hitting the DB.
The book documentation is a little thin, however, but the basic structure is this:
class TeachersRoute extends CakeRoute {
/**
* Modify incoming parameters so that controller receives the correct data
*/
function parse($url) {
$params = parent::parse($url);
// Add / modify parameter information
// The teacher id should be sent as the first value in the $params['pass'] array
return $params;
// Or return false if lookup failed
}
/**
* Modify parameters so calls like HtmlHelper::url() output the correct value
*/
function match($url) {
// modify parameters
// add $url['slug'] if only id provided
return parent::match($url);
}
And then in your routes:
Router::connect(
'/:slug',
array(
'controller' => 'teachers',
'action' => 'contentProfile'
),
array(
'slug' => '[a-zA-Z0-9_-]+'
'routeClass' => 'TeachersRoute',
)
);
In my layout-script I wish to create a link, appending [?|&]lang=en to the current url, using the url view helper. So, I want this to happen:
http://localhost/user/edit/1 => http://localhost/user/edit/1?lang=en
http://localhost/index?page=2 => http://localhost/index?page=2&lang=en
I have tried the solution suggested Zend Framework: Append query strings to current page url, accessing the router directly, but that does work.
My layout-script contains:
English
If the default route is used, it will append /lang/en, but when another route is used, nothing is appended at all.
So, is there any way to do this with in Zend without me having to parse the url?
Edit
Sorry for my faulty explanation. No, I haven't made a custom router. I have just added other routes. My bad. One route that doesn't work is:
$Routes = array(
'user' => array(
'route' => 'admin/user/:mode/:id',
'defaults' => array('controller' => 'admin', 'action' => 'user', 'mode'=>'', 'id' => 0)
)
);
foreach( $Routes as $k=>$v ) {
$route = new Zend_Controller_Router_Route($v['route'], $v['defaults']);
$router->addRoute($k, $route);
}
Upd:
You must add wildcard to your route or define 'lang' parameter explicitly.
'admin/user/:mode/:id/*'
Additionally, according to your comment, you can do something like this:
class controllerplugin extends Zend_Controller_Plugin_Abstract
{
function routeShutdown($request)
{
if($request->getParam('lang', false) {
//store lang
Zend_Controller_Front::getInstance()->getRouter()->setGlobalParam('lang', NULL); //check if this will remove lang from wildcard parameters, have no working zf installation here to check.
}
}
}
I bumped into a problem and I can't seem to find a good solution to make it work. I have to make some dynamic routes into a Zend Framework project. I'll explain shortly what my problem is:
I need to have dynamic custom routes that "extend" the default route (module/controller/action/params). The project I'm working for has several partners and the routes have to work with those.
To store the partners I've made a static class and it looks like this.
<?php
class App_Partner
{
static public $partners = array(
array(
'name' => 'partner1',
'picture' => 'partner1.jpg'
),
array(
'name' => 'partner2',
'picture' => 'partner2.jpg'
),
array(
'name' => 'partner3',
'picture' => 'partner3.jpg'
)
);
static public function routePartners() {
$partners = array();
foreach(self::$partners as $partner) {
array_push($partners, strtolower($partner['name']));
}
$regex = '(' . implode('|', $partners) . ')';
return $regex;
}
}
So App_Partner::routePartners() return me a string like (partner1|partner2|partner3) which I use to create the right routes. My goal is to have the custom routes for each partner for every route I have set in the Bootstrap. So if I have a route add-product.html set I want it to work for each partner as partner1/add-product.html, partner2/add-product.html and partner3/add-product.html.
Also, partner1/, partner2/, partner3 should route to default/index/index.
In fact, I made this thing to work using routes like the one below.
<?php
$routeProposal = new Zend_Controller_Router_Route_Regex(
App_Partner::routePartners() . '?/?proposals.html',
array(
'module' => 'default',
'controller' => 'proposal',
'action' => 'index',
'page' => 1
),
array( 1 => 'partner'),
"%s/proposals.html"
);
$router->addRoute('proposal', $routeProposal);
The problem
The above route works fine if I use a partner in the request URI, but if I don't, I get double slashes like public//proposals.html because of the reverse route set in the route above to be "%s/proposals.html". I can't seem to find a way to avoid this reverse route because I build my urls using the url view helper and if the reverse route isn't set I get an exception stating this.
I also need the routes to work without a partner set, which will be the default way (add-product.html, proposals.html etc).
From your description, it seems like you're looking for a zend router chain, where your partner is an optional chain.
Here's a similar question, but using a hostname route : Zend Framework: get subdomain parameter from route. I adapted it to solve your problem, just put the following in your Bootstrap.php to initialize the routing :
protected function _initRoute()
{
$this->bootstrap('FrontController');
$router = $this->getResource('FrontController')->getRouter();
// Default route
$router->removeDefaultRoutes();
$defaultRoute = new Zend_Controller_Router_Route(
':controller/:action/*',
array(
'module' => 'default',
'controller' => 'index',
'action' => 'index',
)
);
$router->addRoute('default', $defaultRoute);
$partnerRoute = new Zend_Controller_Router_Route(
':partner',
array('partner' => 'none'),
array('partner' => '^(partner1|partner2|partner3)$')
);
$router->addRoute('partner', $partnerRoute->chain($defaultRoute));
}
Change as you see fit. In your controllers you will only get a value for the partner parameter if it was actually specified AND valid (you will get a routing error if the partner doesn't exist)...
I use a similar process to detech lang, in my route (but with a ini file).
You can use a default value for you partners parameter to make the route working without partner, and add a ? to your regex.
But actually, I don't know how to avoid the double //...
Hope that helps.
EDIT: For your information, here is a simplified version of my route with language:
routes.lang.type = "Zend_Controller_Router_Route"
routes.lang.route = "lang/:language/*"
routes.lang.reqs.language = "^(en|fr|nl|de)?$"
routes.lang.defaults.language = none
routes.lang.defaults.module = default
routes.lang.defaults.controller = index
routes.lang.defaults.action = language