Zend framework 2 - How to make a language switcher - php

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
);

Related

SilverStripe overwriting URLSegmentFilter static

URLSegmentFilter has a static array $default_replacements which holds, among others, the string to convert ampersands to (from & to -and-) for URL's.
I'm trying to extend the class and overwrite this static to translate the ampersand convert (only value is english and).
How can I overwrite the owner static for this goal?
class URLSegmentFilterExtension extends Extension {
private static $default_replacements = array(
'/&/u' => '-and-', // I need to translate this using _t()
'/&/u' => '-and-', // And this one
'/\s|\+/u' => '-',
'/[_.]+/u' => '-',
'/[^A-Za-z0-9\-]+/u' => '',
'/[\/\?=#]+/u' => '-',
'/[\-]{2,}/u' => '-',
'/^[\-]+/u' => '',
'/[\-]+$/u' => ''
);
}
First of all: The URLSegmentFilter mainly operates in the CMS context, where you usually just have a single locale (depending on the settings of the editing Member). So using _t() alone might not be very helpful? So you'd probably have to get the current editing locale (assuming you're using Fluent or Translatable) and set the locale for translations temporarily.
I don't see a way to hook in translations via an Extension there. I think you'd be better off creating a custom subclass and use it via Injector.
Something like this should work:
<?php
class TranslatedURLSegmentFilter extends URLSegmentFilter
{
public function getReplacements()
{
$currentLocale = i18n::get_locale();
$contentLocale = Translatable::get_current_locale();
// temporarily set the locale to the content locale
i18n::set_locale($contentLocale);
$replacements = parent::getReplacements();
// merge in our custom replacements
$replacements = array_merge($replacements, array(
'/&/u' => _t('TranslatedURLSegmentFilter.UrlAnd', '-and-'),
'/&/u' => _t('TranslatedURLSegmentFilter.UrlAnd', '-and-')
));
// reset to CMS locale
i18n::set_locale($currentLocale);
return $replacements;
}
}
Then you have to enable the custom URLSegmentFilter via config by putting something like this in your mysite/_config/config.yml file:
Injector:
URLSegmentFilter:
class: TranslatedURLSegmentFilter
Update: The above example assumes you're using the Translatable module. If you're using Fluent, replace the following line:
$contentLocale = Translatable::get_current_locale();
with:
$contentLocale = Fluent::current_locale();
You can update configuration dynamically in mysite/_config.php
$defaultReplacements = Config::inst()->get('URLSegmentFilter', 'default_replacements');
$translatedAnd = _t('URLSegmentFilter.And','-and-');
$defaultReplacements['/&/u'] = $translatedAnd;
$defaultReplacements['/&/u'] = $translatedAnd;
Config::inst()->Update('URLSegmentFilter', 'default_replacements', $defaultReplacements);

Should I use an array or object to reference system settings?

I am building a site that has 30-40 system settings. These are editable in a text file. I want to make sure that I set the site up with the most logical and less resource-intensive method of pulling these settings.
I have been thinking about using an array:
$system['language'] = 'en';
$system['version'] = '0.1';
And referencing them throughout the site like: echo $system['version'];
Or, I can set them up as an object:
class SiteConfig {
public $language = 'en';
public $version = '0.1';
}
$system = new SiteConfig;
And referencing them throughout the site like: echo $system->version;
Which is best to use, or does anyone have a better suggestion?
The approach taken by some frameworks (Laravel for example) is to create a settings file, that you pull in with require.
Your various settings are all defined in that file in a single array, that is returned:
settings.php
<?php
return array(
'language' => 'en',
'version' => '0.1',
'timezone' => 'America/Sao_Paulo',
);
Then you can use it like:
$system = require 'settings.php';
echo $system['version'];
See example #5 on the php.net docs for include for more about this return.

how to exclude some routes when user is not logged in zfcuser?

So, i am new in Zend Framework, i installed ZfcUser and i want when the user is not logged to didn't access to some routes for example : /blog/article/add,
actually i use <?php if($this->zfcUserIdentity()) :?> to check if the user is logged but how can i redirect the user to my login route/user if is try to access to /blog/article/add,
so plz if someone has any idea i will be very appreciative :)
I disagree with the selected answer because it's not really practical to do these kind of things inside every action of every controller that you want to deny access to.
There are two big modules out there that are used for this task. The first one being BjyAuthorize and the other big one being ZfcRbac. Please check them out. ZfcRbac is my favorite (because i wrote documentation for it) but it requires PHP 5.4+
Simple Way:
in controller:
if (!$this->zfcUserAuthentication()->hasIdentity()) {
return $this->redirect()->toRoute('route_to_login',array('param' => $param));
}
Some more:
(after auth zfcuset will redirect you to previous controller)
in module.config.php
'controllers' => array(
'invokables' => array(
'zfcuser' => 'Application\Controller\UserController',
),
...
),
next copy file vendor\zf-commons\zfc-user\src\ZfcUser\Controller\UserController.php
to module Application (same folder as in module.config.php)
delete all function except authenticateAction
add:
use Zend\Session\Container;
and change
return $this->redirect()->toRoute($this->getOptions()->getLoginRedirectRoute());
to
$redirect_session = new Container('redirect');
$redirect = $redirect_session->redirect;
if($redirect!='')
{
unset($_SESSION['redirect']);
return $this->redirect()->toRoute($redirect, array('lang' => $lang));
}
return $this->redirect()->toRoute($this->getOptions()->getLoginRedirectRoute(),array('param' => $param));
and at last add in controller
use Zend\Session\Container;
...
if (!$this->zfcUserAuthentication()->hasIdentity()) {
$redirect_session = new Container('redirect');
$redirect_session->redirect = 'route_to_this_controller_action';
return $this->redirect()->toRoute('route_to_login',array('param' => $param));
}

Zend Framework 2 set global module variable

I want to make it so the variable "user" is global to all modules, so I've added this code
public function onBootstrap(MvcEvent $e)
{
$eventManager = $e->getApplication()->getEventManager();
$e->getViewModel()->setVariable('user',$e->getApplication()->getServiceManager()->get('auth_service')->getIdentity());
}
It works fine for the layout file, meaning that if I do var_dump($user) in the layout.phtml it will output the expected results, although in a view doing the same results in
Notice: Undefined variable: user in C:\webserver\apache\htdocs...
Any help on why this is happening? Am I doing something wrong?
Use Config-Variables to make stuff application-wide available.
// module.config.php
return array(
'someVariable' => 'someValue',
// all the other stuff
);
And all you have to to is access the config. Of course the access varies depending on where you're trying to access it, but ultimately it's done like this:
// Example for being inside any of your Controllers
$servoceLocator = $this->getServiceLocator();
$config = $serviceLocator->get('config');
$myValue = $config['someVariable'];
Hope it's clear enough.
Have you tried $this->user in view i think this should work.
In Zend Framework 2 there is a build in view helper called "Identity" that is available in all views. It requires that you register the service manager in a config.module.php like that:
'service_manager' => array(
...
'invokables' => array(
...
'Zend\Authentication\AuthenticationService' => 'Zend\Authentication\AuthenticationService',
),
)
and then in a view, you can access your registered "user" like this:
<?php echo $this->identity()?>
which will return your user. Here is a link to the zend documentation

What are the step you have to go through when updating an application to be multilingual?

I need to update an application which is built on Zend Framework.
Most of the text is hard-coded in views scripts, forms, etc.
The application will be available in say, 3 languages AND is language specific (the content is not the same for all) and will have one domain per language (ie: mygreatsite.com, monsupersite.com, ilmiosupersite.com, etc.)
First question:
What is the best way to "handle" this kind of application?
I can imagine several solution like:
One copy per language, using different db, etc... (probably not the best way for maintenance)
Only one application, handling different content, db, etc, depending on the locale (based on the route)
Second question:
What should I need to know about the existing code to start the "migration"?
What about any best practice when building a i18n website?
What are the best adapter? (I already used gettext() and I think it's the best)
I am by no means an expert but this is what I do.
I use array as my translation adapter because it’s easier for my clients to update as they are just regular Joes. And I use translation keys instead of sentences. For example
Some people would use
$this->translate(‘Some sentence to translate’);
I use
$this->translate(‘default-index-dashboard-title’);
This makes it far easier for me to know where the text I’m looking for is to change. I don’t know if there are any advantages other than that though.
You will need to setup your translation adapter and translation cache (if you want) in your bootstrap. I do mine like this:
protected function _initLocale()
{
$locale = new Zend_Locale(Zend_Locale::BROWSER);
$config = Zend_Registry::get('config');
Zend_Registry::set('Zend_Locale', $locale->toString());
return $locale;
}
protected function _initTranslation()
{
try{
$translate = new Zend_Translate(array('adapter' => 'array', 'content' => ROOT . '/callmanagement/languages/' . strtolower(Zend_Registry::get('Zend_Locale')) . '.php'));
}catch(Exception $e){
$translate = new Zend_Translate(array('adapter' => 'array', 'content' => ROOT . '/callmanagement/languages/en_gb.php'));
}
Zend_Registry::set('Zend_Translate', $translate);
return $translate;
}
I would use a single code base unless the sites are completely different and store the shared data in one database and have other databases for the site specific stuff.
You can setup multiple db adapters either in the bootstrap or in the congfig.
$dbLocal = new Zend_Db_Adapter_Pdo_Mysql(array(
'host' => 'localhost',
'username' => $result['user'],
'password' => $result['password'],
'dbname' => $result['database']
));
Zend_Db_Table_Abstract::setDefaultAdapter($dbLocal);
$dbShared = new Zend_Db_Adapter_Pdo_Mysql(array(
'host' => 'localhost',
'username' => ‘root’,
'password' => 'pass',
'dbname' => 'dbname'
));
Zend_Registry::set('db_local', $dbLocal);
Zend_Registry::set('db_shared', $dbShared);
return $dbLocal;
You can get Zend Form to translate for you just put your translation key into the label field.
$this->addElement(‘text’, ‘test’, array(‘label’ => ‘translation-key’, ‘required’ => true)); etc.
Then if you are using Zend_Db_Table_Abstract classes you can change the default schema and database connection like this:
class Default_Model_Table_Topics extends Zend_Db_Table_Abstract
{
protected $_name = 'topics';
protected $_id = 'topic_id';
protected $_rowClass = 'Default_Model_Topic';
protected $_schema = 'dbname';
protected $_adapter = 'db_shared';
}
If you need any more examples I’ll try and help.

Categories