Zend Locale & Zend_Currency: Region code - zend-framework

I am trying to create a connection between the Zend_Locale and the Zend_Currency using the browser language preferences.
BROWSER: en
$locale = new Zend_Locale(Zend_Locale::BROWSER);
Zend_Debug::Dump($locale->getLanguage());
Zend_Debug::Dump($locale->getRegion());
die;
Result:
string(2) "en"
bool(false)
BROWSER: en_US
$locale = new Zend_Locale(Zend_Locale::BROWSER);
Zend_Debug::Dump($locale->getLanguage());
Zend_Debug::Dump($locale->getRegion());
die;
Result:
string(2) "en"
string(2) "US"
Ho have I to solve this problem?
This is my plugin controller:
class MyProject_Controller_Plugin_Language extends Zend_Controller_Plugin_Abstract {
public function routeShutdown(Zend_Controller_Request_Abstract $request) {
$locale = new Zend_Locale(Zend_Locale::BROWSER);
$registry = Zend_Registry::getInstance();
// Check if the config file has been created
$isReady = MyProject_Main::isReady();
$module = $request->getModuleName ();
if($module == "default"){ // set the right session namespace per module
$ns = new Zend_Session_Namespace ( 'Default' );
}elseif($module == "admin"){
$ns = new Zend_Session_Namespace ( 'Admin' );
}else{
$ns = new Zend_Session_Namespace ( 'Default' );
}
// check the user request if it is not set, please get the old prefereces
$lang = $request->getParam ( 'lang', $ns->lang );
if(empty($lang)){ // Get the user preference
if(strlen($locale) == 2){ // Check if the Browser locale is formed with 2 chars
$lang = $locale;
}elseif (strlen($locale) > 4){ // Check if the Browser locale is formed with > 4 chars
$lang = substr($locale, 0, 2); // Get the language code from the browser preference
}
}
// Get the translate language or the default language: en
if(file_exists(PUBLIC_PATH . "/languages/$lang/$lang.mo")){
$translate = new Zend_Translate(array('adapter' => "MyProject_Translate_Adapter_Gettext", 'content' => PUBLIC_PATH . "/languages/$lang/$lang.mo", 'locale' => $lang, 'disableNotices' => true));
}else{
$translate = new Zend_Translate(array('adapter' => "MyProject_Translate_Adapter_Gettext", 'locale' => $lang, 'disableNotices' => true));
}
$registry->set('Zend_Translate', $translate);
$registry->set('Zend_Locale', $locale);
if($isReady){
$ns->langid = Languages::get_language_id_by_code($lang);
}else{
$ns->langid = 1;
}
$ns->lang = $lang;
}
}
Thanks

Obviously, the client's language preferences are a good first step, but won't help you in all cases. Even if they return a value, I would not use it to determine the appropriate currency as you described - users may set their preferred language/region to a foreign setting. Examples are expats and language learners. Though this may be an edge case, don't trust it to detect currency.
The most robust solution would be to use a geolocation service which also returns the currency for the location found, e.g. http://www.geoplugin.com/webservices/php.
The combination of both methods may also be a good solution. If they return conflicting values, offer the user a possibility to choose from the two found currencies (or from any other).

Related

ZF 2 Form Validation Translations

I use ZF 2.4 and I would like to change form validation messages to German, so I used code compatible with doc - https://framework.zend.com/manual/2.4/en/modules/zend.validator.messages.html
$translator = new \Zend\Mvc\I18n\Translator();
$translator->addTranslationFile(
'phpArray',
'./vendor/zendframework/zendframework/resources/languages/en/Zend_Validate.php', //or Zend_Captcha
'default',
'de_DE'
);
\Zend\Validator\AbstractValidator::setDefaultTranslator($translator);
Unfortunatelly EN is used still.. For example message "Value is required and can't be empty" is displayed from vendor/zendframework/zendframework/library/Zend/Validator/NotEmpty.php and isn't translated from vendor/zendframework/zendframework/resources/languages/de/Zend_Validate.php
No errors are there...
Could somebody help me, please? This is very important for me...
The translator is still pointing to the Locale of EN. What you want to do is set the translator Locale based on the user settings or maybe even a route parameter. To do that use the setLocale($locale) on your Translator.
So you could do this within your Application\Module.php:
$language = $event->getRouteMatch()->getParam('language', 'en_US');
$serviceManager = $event->getApplication()->getServiceManager();
$translator = $serviceManager->get('translator');
$translator
->setLocale($language)
->setFallbackLocale('en_US')
->addTranslationFilePattern(
'phpArray',
\Zend\I18n\Translator\Resources::getBasePath(),
\Zend\I18n\Translator\Resources::getPatternForValidator()
);
AbstractValidator::setDefaultTranslator($translator);
It is up to you to where you get the language locale from. If you don't have a route param defined for the language but want to use the user settings for example:
$language = 'en_US';
$authService = $auth = $serviceManager->get('zfcuser_auth_service');
if ($authService->hasIdentity()) {
$language = $authService->getIdentity()->getUserSettings()->getLanguage();
}
Haa, it works when I used below code:
public function onBootstrap(MvcEvent $e)
{
$eventManager = $e->getApplication()->getEventManager();
$moduleRouteListener = new ModuleRouteListener();
$moduleRouteListener->attach($eventManager);
$serviceManager = $e->getApplication()->getServiceManager();
$translator = $serviceManager->get('translator');
//$locale = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
$locale = 'de_DE';
//$locale = 'en_US';
$translator->setLocale(\Locale::acceptFromHttp($locale));
$translator->addTranslationFile(
'phpArray',
'./vendor/zendframework/zendframework/resources/languages/de/Zend_Validate.php',
'default',
'de_DE'
);
\Zend\Validator\AbstractValidator::setDefaultTranslator($translator);
}
I don't understand why it wasn't working before..

Uncaught exception 'Zend_Locale_Exception' with message 'The locale '' is no known locale'

i'm trying to make the translation of a website in zend framework, but, given that is the first time i use this framework, i'm having some problems. The last one is the one that gives me the error you're seeing as a title here.
I've set two important methods in the Bootstrap file:
protected function _initLocale()
{
$session = new Zend_Session_Namespace('ttb.l10n');
if ($session->locale)
{
$locale = new Zend_Locale($session->locale);
}
if ($locale === null)
{
try
{
$locale = new Zend_Locale('browser');
}
catch (Zend_Locale_Exception $e)
{
$locale = new Zend_Locale('en');
}
}
$registry = Zend_Registry::getInstance();
$registry->set('Zend_Locale', $locale);
}
protected function _initTranslate()
{
$translate = new Zend_Translate('array',
APPLICATION_PATH . '/../languages/',
'null',
array('scan' => Zend_Translate::LOCALE_FILENAME,
'disableNotices' => 0));
$registry = Zend_Registry::getInstance();
$registry->set('Zend_Translate', $translate);
return $translate;
}
The problem is that if I want the right translation in my pages i have to set the locale to the $translate variable and set it for every view I'll show to the user. The website has only one page, so i assumed the variable $translate would be avaliable in every action, and i changed the init method of the indexController.php:
public function init()
{
/* Initialize action controller here */
$registry = Zend_Registry::getInstance();
//The following is for the links that set the local language manually
$this->view->locale = $this->_getParam('locale');
if(!$this->view->locale){
$this->view->locale = $registry->get('Zend_Locale');
}
$translate->setLocale($locale);
}
The error i'm getting is the following (from zend server):
Function Name Zend_Application::bootstrap
Error Type E_ERROR
Source File /usr/local/zend/apache2/htdocs/project/public/index.php
Error String Uncaught exception 'Zend_Locale_Exception' with message 'The locale '' is no known locale'
If someone could help it would be much appreciated.
Thanks
SP

Zend Framework Router Hostname and Multi-Language support

Last two days I was fighting with Zend Framework Router and still didn't find solution.
Existing project has 2 different modules which work with the same domain e.g. www.domain1.com:
default - Contains public information, can be accessed via www.domain1.com
admin - Administrator interface, can be accessed via www.domain1.com/admin
Project is multi-langual and to keep language code it is transmitted as first parameter of every URL, e.g. www.domain1.com/en/ www.domain1.com/en/admin
Part of code which takes care is next plugin:
class Foo_Plugin_Language extends Zend_Controller_Plugin_Abstract
{
const LANGUAGE_KEY = 'lang';
public function routeStartup(Zend_Controller_Request_Abstract $request)
{
$languagesRegexp = implode('|', array_map('preg_quote', $this->_bootstrap->getLanguages()));
$routeLang = new Zend_Controller_Router_Route(
':' . self::LANGUAGE_KEY,
array(self::LANGUAGE_KEY => $this->_bootstrap->getLanguage()->toString()),
array(self::LANGUAGE_KEY => $languagesRegexp)
);
$router = $this->getFrontController()->getRouter();
$router->addDefaultRoutes();
$chainSeparator = '/';
foreach ($router->getRoutes() as $name => $route) {
$chain = new Zend_Controller_Router_Route_Chain();
$chain
->chain($routeLang, $chainSeparator)
->chain($route, $chainSeparator);
$new_name = $this->_formatLanguageRoute($name);
$router->addRoute($new_name, $chain);
}
protected function _formatLanguageRoute($name)
{
$suffix = '_' . self::LANGUAGE_KEY;
if (substr($name, -strlen($suffix)) == $suffix) return $name;
return $name . '_' . self::LANGUAGE_KEY;
}
public function routeShutdown(Zend_Controller_Request_Abstract $request)
{
$lang = $request->getParam(self::LANGUAGE_KEY, null);
$this->_bootstrap->setLanguage($lang);
$actual_lang = $this->_bootstrap->getLanguage()->toString();
$router = $this->getFrontController()->getRouter();
$router->setGlobalParam(self::LANGUAGE_KEY, $lang);
// Do not redirect image resize requests OR get js, css files
if (preg_match('/.*\.(jpg|jpeg|gif|png|bmp|js|css)$/i', $request->getPathInfo())) {
return true;
}
// redirect to appropriate language
if ($lang != $actual_lang) {
$redirector = Zend_Controller_Action_HelperBroker::getStaticHelper('redirector');
$params = array(self::LANGUAGE_KEY => $actual_lang);
$route = $this->_formatLanguageRoute($router->getCurrentRouteName());
return $redirector->gotoRouteAndExit($params, $route, false);
}
}
}
One of the first question is what do you think about such way to provide multi-lang support. What I've noticed is that all this chains dramatically decrease operation speed of the server, response time from the server is about 4 seconds...
Main question is: Currently I have to implement such feature: I have domain www.domain2.com that should work just with single module e.g. "foobar"... and it should be available via second url... or, of course, it should work like www.domain1.com/en/foobar by default...
To provide this functionality in Bootstrap class I'be implemented such part of code
// Instantiate default module route
$routeDefault = new Zend_Controller_Router_Route_Module(
array(),
$front->getDispatcher(),
$front->getRequest()
);
$foobarHostname = new Zend_Controller_Router_Route_Hostname(
'www.domain2.com',
array(
'module' => 'foobar'
)
);
$router->addRoute("foobarHostname", $foobarHostname->chain($routeDefault));
And that is not working and as I've found routeDefault always rewrite found correct model name "foobar" with value "default"
Then I've implemented default router like this:
new Zend_Controller_Router_Route(
':controller/:action/*',
array(
'controller' => 'index',
'action' => 'index'
);
);
But that still didn't work, and started work without language only when I comment "routeStartup" method in Foo_Plugin_Language BUT I need language support, I've played a lot with all possible combinations of code and in the end made this to provide language support by default:
class Project_Controller_Router_Route extends Zend_Controller_Router_Route_Module
{
/**
* #param string $path
* #param bool $partial
* #return array
*/
public function match($path, $partial = false)
{
$result = array();
$languageRegexp = '%^\/([a-z]{2})(/.*)%i';
if (preg_match($languageRegexp, $path, $matches)) {
$result['lang'] = $matches[1];
$path = $matches[2];
}
$parentMatch = parent::match($path);
if (is_array($parentMatch)) {
$result = array_merge($result, $parentMatch);
}
return $result;
}
}
So language parameter was carefully extracted from path and regular processed left part as usual...
But when I did next code I was not able to get access to the foobar module via www.domain2.com url, because of module name in request was always "default"
$front = Zend_Controller_Front::getInstance();
/** #var Zend_Controller_Router_Rewrite $router */
$router = $front->getRouter();
$dispatcher = $front->getDispatcher();
$request = $front->getRequest();
$routerDefault = new Project_Controller_Router_Route(array(), $dispatcher, $request);
$router->removeDefaultRoutes();
$router->addRoute('default', $routerDefault);
$foobarHostname = new Zend_Controller_Router_Route_Hostname(
'www.domain2.com',
array(
'module' => 'foobar'
)
);
$router->addRoute("foobar", $foobarHostname->chain($routerDefault));
Instead of summary:
Problem is that I should implement feature that will provide access for the secondary domain to the specific module of ZendFramework, and I should save multi-language support. And I cannot find a way, how to manage all of this...
Secondary question is about performance of chain router, it makes site work very-very slow...
The way I have solved problem with multilanguage page is in this thread:
Working with multilanguage routers in Zend (last post).
Ofcourse my sollution need some caching to do, but I think it will solve your problem.
Cheers.

Zend_Translate scan translation files

I've trying to use Zend_Translate from Zend Framework
I am using "POEdit" to generate "gettext" translation files.
My files are under /www/mysite.com/webapps/includes/locale (this path is in my include path).
I have:
pictures.en.mo
pictures.en.po
(I plan on having pictures.es.mo soon)
It all works fine if I manually do addTranslation() for each file. However I want to use the automatic file scanning method.
I tried both of those:
<?php
/*Localization*/
require_once 'Zend/Translate.php';
require_once 'Zend/Locale.php';
define('LOCALE','/www/mysite.com/webapps/includes/locale');
if(!empty($_GET['locale'])){
$locale = new Zend_Locale($_GET['locale']);
}
else{
$locale = new Zend_Locale();
}
$translate = new Zend_Translate('gettext', LOCALE, null, array('scan' => Zend_Translate::LOCALE_FILENAME));
if ( $translate->isAvailable( $locale->getLanguage() ) ){
$translate->setLocale($locale);
}
else{
$translate->setLocale('en');
}
And this:
<?php
/*Localization*/
require_once 'Zend/Translate.php';
require_once 'Zend/Locale.php';
define('LOCALE','/www/mysite.com/webapps/includes/locale');
if(!empty($_GET['locale'])){
$locale = new Zend_Locale($_GET['locale']);
}
else{
$locale = new Zend_Locale();
}
$translate = new Zend_Translate('gettext', LOCALE);
if ( $translate->isAvailable( $locale->getLanguage() ) ){
$translate->setLocale($locale);
}
else{
$translate->setLocale('en');
}
In both cases, I get a Notice: No translation for the language 'en' available. in /www/mysite.com/webapps/includes/Zend/Translate/Adapter.php on line 411
It also worked if I tried to do directory scanning.
i think there is just one little "bug".
$translate = new Zend_Translate(
'gettext',
LOCALE,
null,
array('scan' => Zend_Translate::LOCALE_DIRECTORY) // <--
);
If you use LOCALE_FILENAME, is ZF searching inside this FILE for your Translation.

Drupal - Include more than one user_profile_form on a page

Edit:
I think it is because the action is the same or something. I tried to modify the action using this:
function mytheme_user_profile_form($form) {
global $user;
$uid = $user->uid;
//print '<pre>'; print_r($form); print '</pre>';
$category = $form['_category']['#value'];
switch($category) {
case 'account':
$form['#action'] = '/user/'.$uid.'/edit?destination=user/'.$uid;
break;
case 'education':
$form['#action'] = '/user/'.$uid.'/edit/education?destination=user/'.$uid;
break;
case 'experience':
$form['#action'] = '/user/'.$uid.'/edit/experience?destination=user/'.$uid;
break;
case 'publications':
$form['#action'] = '/user/'.$uid.'/edit/publications?destination=user/'.$uid;
break;
case 'conflicts':
$form['#action'] = '/user/'.$uid.'/edit/conflicts?destination=user/'.$uid;
break;
}
//print '<pre>'; print_r($form); print '</pre>';
//print $form['#action'];
$output .= drupal_render($form);
return $output;
}
But, the form action, when the form is actually rendered is unchanged. They're all /user/%uid
Can I modify the form action?
I am including several different "categories" of the user profile form on one page, and the code will correctly output the forms I'm specifying. Each form is in a separate collapsible div.
My problem is twofold.
(1) The existing values for the fields aren't pre-populated and
(2) Clicking on "Save" for one section will result in a warning: Email field is required, regardless of which form you're actually saving
I am pretty sure that for problem #2, it is because the name of the button is the same in all cases, as is the form id.
print '<h3>– Account Settings</h3>';
print '<div class="expand">';
print(drupal_get_form('user_profile_form', $user, 'account'));
print '</div>';
print '<h3>– My Info</h3>';
print '<div class="expand">';
print(drupal_get_form('user_profile_form', $user, 'Personal'));
print '</div>';
print '<h3>– Experience</h3>';
print '<div class="expand">';
print(drupal_get_form('user_profile_form', $user, 'experience'));
print '</div>';
print '<h3>– Education</h3>';
print '<div class="expand">';
print(drupal_get_form('user_profile_form', $user, 'education'));
print '</div>';
Problem #1: ? Could you post the html source?
For problem #2:
OK, I'll step through the code here:
The validation handler for the user profile form (user_profile_form_validate()) calls
user_module_invoke('validate', $form_state['values'], $form_state['values']['_account'], $form_state['values']['_category']);
Which looks like
<?php
/**
* Invokes hook_user() in every module.
*
* We cannot use module_invoke() for this, because the arguments need to
* be passed by reference.
*/
function user_module_invoke($type, &$array, &$user, $category = NULL) {
foreach (module_list() as $module) {
$function = $module .'_user';
if (function_exists($function)) {
$function($type, $array, $user, $category);
}
}
}
?>
So, the validation handler for this form is going through every module looking for user hook functions and calling them with $type = 'validate'. (Note that 'category' param is optional here - contrib modules are not required to use it)
Let's look at user.module's user hook as an example to see what happens:
function user_user($type, &$edit, &$account, $category = NULL) {
if ($type == 'view') {
$account->content['user_picture'] = array(
'#value' => theme('user_picture', $account),
'#weight' => -10,
);
if (!isset($account->content['summary'])) {
$account->content['summary'] = array();
}
$account->content['summary'] += array(
'#type' => 'user_profile_category',
'#attributes' => array('class' => 'user-member'),
'#weight' => 5,
'#title' => t('History'),
);
$account->content['summary']['member_for'] = array(
'#type' => 'user_profile_item',
'#title' => t('Member for'),
'#value' => format_interval(time() - $account->created),
);
}
if ($type == 'form' && $category == 'account') {
$form_state = array();
return user_edit_form($form_state, (isset($account->uid) ? $account->uid : FALSE), $edit);
}
//<-- LOOK HERE -->
if ($type == 'validate' && $category == 'account') {
return _user_edit_validate((isset($account->uid) ? $account->uid : FALSE), $edit);
}
if ($type == 'submit' && $category == 'account') {
return _user_edit_submit((isset($account->uid) ? $account->uid : FALSE), $edit);
}
if ($type == 'categories') {
return array(array('name' => 'account', 'title' => t('Account settings'), 'weight' => 1));
}
}
So, it is only supposed to validate if the category == 'account'
In the function _use_edit_validate, we find:
// Validate the e-mail address:
if ($error = user_validate_mail($edit['mail'])) {
form_set_error('mail', $error);
}
There's your error message.
Since that form is only supposed to validate when the category == 'account', and your problem (#2) seems to be that it always validates regardless of the category, maybe your forms are not being rendered as unique form instances? Drupal might be rendering a complete form each time, and just setting a hidden form value to whatever the category is (like in this form's definition function in user_pages.inc $form['_category'] = array('#type' => 'value', '#value' => $category);)
It would be helpful to see the actual html source output.
==EDIT 10-15-09 in response to updated question===
OK, it looks like your method (editing $form['#action'] manually in the theme layer) may not be possible (see this post for reference). If you want to alter the form action you need to write a custom module that implements hook_form_alter() (it won't work in a theme template file). This function allows you to modify how a form is rendered, in your case the user modification form. There are more details on form modification here.
I am not 100% sure that's what you want to do though; (since it looks like you already must create a module) perhaps you want to hook into hook_user() instead; this function "... allows modules to react when operations are performed on user accounts.". You may be able to react to the category in this function and block/allow whichever user changes you like.
However, if it's just email address validation that is the problem, and if you are dealing with existing users, why don't you just make sure the email address is set before you save?