Depending on the user role, I need to show different texts in my Zend project.
For normal users I'm using the "en" language.
For new users I want something like "en_new".
However, the language "en_new" always reverts to just "en".
I'm using the locale_directory scan system to automatically detect languages.
The translate adapter calls Zend_Locale::findLocale() internally in addTranslation() (at least in ZF 1.1x). This in turn checks whether the locale is on a whitelist. Yours is not, obviously. I didn't dig too deep into the code, but it's quite probable that the next step is to revert from en_xxx to just en which is what happens in your case.
See the sources:
library/Zend/Translate/Adapter.php - addTranslation method
library/Zend/Locale.php - findLocale method
I am currently evaluating something similar, for some users I want some texts to be differently translated. And I also ran into the problem to not be able to create a custom locale value.
Tough what I found out in my tests seems to solve/work around the problem.
See also here: Combining multiple translation sources
What I am doing is to just add a custom translation to my default ones.
$translateDef = new Zend_Translate(
array(
'adapter' => 'gettext',
'content' => 'locale/default/',
'locale' => 'auto',
'scan' => Zend_Translate::LOCALE_DIRECTORY
)
);
$translateCust = new Zend_Translate(
array(
'adapter' => 'gettext',
'content' => 'locale/custom/',
'locale' => 'auto',
'scan' => Zend_Translate::LOCALE_DIRECTORY
)
);
$translateDef->addTranslation(array(
'content' => $translateCust
)
);
And the folder structure looks like this:
locale/
default/
de
en
custom/
de
en
So when doing the addTranslation it seems to overvwrites the existing ones, so for your new users, you could add custom folder with the proper translations.
For my tests this worked so far, but haven't evaluated it in depth yet.
Related
I built an application that uses ZF2 for authentification, routing, error pages etc., but the core functionalities on each view are implemented in AngularJS. The whole thing is localized, but in 2 seperate instances:
We have the ZF2 Translator, configured in module.config.php
'translator' => array(
'locale' => 'de_DE',
'translation_file_patterns' => array(
array(
'type' => 'phparray',
'base_dir' => __DIR__ . '/../language',
'pattern' => '%s.php',
),
),
),
containing key=>value pairs like 'app.frontend.title' => 'Title'.
And the Angular-Translate module, configured by
$translateProvider.useStaticFilesLoader({
prefix: '/lang/',
suffix: '.json'
});
containing a nested JSON object like {'app': {'buttons': {'send': 'Send now'}}}'
The PHP part contains some headlines, content for <title>, navigation,... pretty much everything that is displayed outside of my AngularJS apps. The Angular-JSON contains l10n for a lot of buttons, dialogs etc.
Is there a possibility to unify these two? Doesn't matter if I access the AngularJS json file from the php script or the other way round (get the .json dynamically served by PHP for Angular). But I can't figure out how to read JSON for the ZF2 Translator.
Why would have you have some translations handled by PHP and some others by Angular JS? Couldn't you get rid of the ZF2 Translator?
How to enable multitranslation in yii 2 basic framework? I have tried, but it is not working. I get no error, but translation is not showing. Code:
public function actionLang(){
$lang = \Yii::$app->request->get('lang');
if($lang && in_array($lang,['en-US','ar-SA'])){
$cookie = new Cookie();
$cookie->name = '_lang';
$cookie->value = $lang;
$cookie->expire = time() + 60 * 60 * 24 * 180;
\Yii::$app->response->cookies->add($cookie);
}
$this->redirect(['index']);
}
I'm using this function in SiteController.
Internationalization in Yii is not a one-action job. Here's the documentation on how to make your website multilingual:
https://github.com/yiisoft/yii2/blob/master/docs/guide/tutorial-i18n.md
If docs are unclear, here is a tutorial:
http://code.tutsplus.com/tutorials/programming-with-yii2-localization-with-i18n--cms-23140
If you have gone through all the steps and merely wish to set the current language, you can use:
\Yii::$app->language = 'xxx';
where xxx is a language code in accordance with ISO 639-2.
Here are the mentioned standard's entries:
http://www.loc.gov/standards/iso639-2/php/code_list.php
First of all, from what I have gathered, you are trying to identify a language requested from the current request by doing $lang = \Yii::$app->request->get('lang'); and then set it in the cookie. In my opinion, this should be used as a "helper", meaning, it is useful to know the language preference of the returning client, but you still have to manage languages via URL, i.e. http://yoursite.com/en or http://yoursite.com/de should serve different languages.
Now, there are actually very good plugins out there for multilingual URL management, such as yii2-localeurls , which you can find here. I use it in production in multiple projects and highly recommend it.
To actually manage translations, you have to have a folder in the root of your project (if you are using advance template, you should have it inside frontend/backend/console ) to store the actual block translations, call it messages .
Now, under #app/messages create folders for each non-default language, for example #app/messages/de for German translations.
If you go your config in #app/config/main.php, look for i18n key inside the components array. If you can't find such a key, simply put the following into the components array:
'i18n' => [
'translations' => [
'app*' => [
'class' => 'yii\i18n\PhpMessageSource',
'basePath' => '#app/messages',
'sourceLanguage' => 'en',
'fileMap' => [
'app' => 'app.php',
'app/error' => 'error.php',
],
],
'*' => [
'class' => 'yii\i18n\PhpMessageSource',
]
],
],
Now, you will need to create a translation file inside the relevant directory. In the above configuration, we declared that the default language used is en, that means that all original messages would be in English. Go to #app/messages/de and create a translations file inside that directory. You can call it whatever you like, but for this example, call it site.php.
In this site.php put the following:
return [
'Translate this!' => 'Your relevant translation here in whichever language',
'Translate this also!!!' => 'Stuff...'
];
If all done correctly, when you access your page via http://yousite.com/de, when using Yii::t('site', 'Translate this!') you should be getting 'Your relevant translation here in whichever language' instead.
The beauty of Yii 2 is that it is extremely well documented. Visit the official documentation if you are stuck, it really explains everything quite well.
I'm currently working on translating an existing Zend Framework 2 project that is spread around multiple modules.
My understanding of the translate functionality of ZF2, is that you can have as many translation files, providing each are 'namespaced' to a different text_domain. This works fine in practice, with each module having the following in their module.config.php file:
...
'translator' => array (
'locale' => 'en_US',
'translation_file_patterns' => array (
array (
'type' => 'phparray',
'base_dir' => __DIR__ . '/../language',
'pattern' => '%s_default.php',
'text_domain' => 'ExampleModule'
),
),
),
...
Which adds a ../language/*_default.php file to the translation list with a text_domain of ExampleModule. All good so far.
Now, the translator itself needs to know which text_domain to pick a translation from and will use default if one isn't provided.
So, inside and at the top of all of my view *.phtml files, I have:
$this->plugin('translate')->setTranslatorTextDomain('ExampleModule');
$this->formLabel()->setTranslatorTextDomain('ExampleModule');
$this->formText()->setTranslatorTextDomain('ExampleModule');
Which tells all proceeding $this->translate() blocks and form elements which text_domain to use.
This is great, and works fine, but it doesn't sit well with the DRY principle in that I have similar code at the top of every view. I attempted to extend the ViewModel class so I can pick a different ViewModel class in the controller and have the above code already baked in, but the plugins aren't available at that stage.
How would I include the above code on every/most views without having to type it each time?
After searching endlessly, I found that the default renderer - PhpRenderer - can be accessed via the onBootstrap method of Module.php (reference).
As the view scripts are rendered by PhpRenderer the $this variable points to PhpRenderer (reference). This means that you can attach the code I needed to Module.php as below:
// Get the default ViewRenderer (PhpRenderer) and setup the correct text domain for derivative plugins
$viewRenderer = $e->getApplication()->getServiceManager()->get('ViewRenderer');
$viewRenderer->plugin('translate')->setTranslatorTextDomain('ExampleModule');
$viewRenderer->formLabel()->setTranslatorTextDomain('ExampleModule');
$viewRenderer->formText()->setTranslatorTextDomain('ExampleModule');
As the current namespace matches the text_domain I need, the above can be simplified by swapping 'ExampleModule' with __NAMESPACE__.
EDIT: If you're looking for a different text_domain per module; you'll need in just one Module.php:
$viewRenderer = $e->getApplication()->getServiceManager()->get('ViewRenderer');
$eventManager->getSharedManager()->attach('Zend\Mvc\Controller\AbstractActionController', 'dispatch', function($e) use ($viewRenderer) {
$controller = $e->getTarget();
$controllerClass = get_class($controller);
$moduleNamespace = substr($controllerClass, 0, strpos($controllerClass, '\\'));
$viewRenderer->plugin('translate')->setTranslatorTextDomain($moduleNamespace);
$viewRenderer->formLabel()->setTranslatorTextDomain($moduleNamespace);
$viewRenderer->formText()->setTranslatorTextDomain($moduleNamespace);
}, 100);
Novice Zend Framework developer here trying to figure out a simple solution to a Zend Routing problem. I'm sure one of you pros can lend a hand.
I have a website (built in Zend Framework using Zend_Navigation) that contains 75% static HTML page content and a few controllers. Top level navigation is built in Zend_Navigation, looping through partials.
Because of my work I build a lot of sites along these lines (containing lots of static pages) so I want to get this right. I don't want to set up controllers and actions for each and every one of these static pages (there are many) and I wanted to create a solution where I used Zend_Controller_Router_Route to route all static content automatically through to a StaticController whose job it would be to include or render .phtml pages based on a controller/action pairing in the URL from some sort of directory like /layouts/staticpages
Because of SEO and various reasons I don't want to have the controller pairing in the URL for these static pages be visible as /static/page/page1... It has to be "real world descriptions" of the /section/page (eg. advantages/someadvantage )
Here is the problem: Using Zend_Controller_Router_Route can do the job when I set up the correct routes BUT it messes something awful with Zend Navigation... I assume because Zend_Navigaion doesn't play nice with on-the-fly controller/action switching.
Code example:
$router = Zend_Controller_Front::getInstance()->getRouter();
$route = new Zend_Controller_Router_Route('advantages/:page/*',
array('controller' => 'static', 'action' => 'display', 'mode' => 'advantages',
'page' => 'index'));
$router->addRoute('advantages', $route);
This handles the job of switching pages in the "advantages" section well enough, but Zend_Navigation's automatic controller/action writing AND the highlighting of "active" nodes ends up being all screwed up because it now thinks that its controller is "static" and action is "display".
Is Zend_Navigation fundamentally incompatible with Zend_Controller_Router_Route? Is there a better way of doing this single static page controller or handling static content across the board?
Since you are using one controller/action for all static pages, you must customize your Zend Navigation before displaying it.
Check Example 4 in the Zend Documentation.
// the following route is added to the ZF router
Zend_Controller_Front::getInstance()->getRouter()->addRoute(
'article_view', // route name
new Zend_Controller_Router_Route(
'a/:id',
array(
'module' => 'news',
'controller' => 'article',
'action' => 'view',
'id' => null
)
)
);
// a page is created with a 'route' option
$page = new Zend_Navigation_Page_Mvc(array(
'label' => 'A news article',
'route' => 'article_view',
'module' => 'news', // required for isActive(), see note above
'controller' => 'article', // required for isActive(), see note above
'action' => 'view', // required for isActive(), see note above
'params' => array('id' => 42)
));
// returns: /a/42
$page->getHref();
So I'm writing a framework on which I want to base a few apps that I'm working on (the framework is there so I have an environment to work with, and a system that will let me, for example, use a single sign-on)
I want to make this framework, and the apps it has use a Resource Oriented Architecture.
Now, I want to create a URL routing class that is expandable by APP writers (and possibly also by CMS App users, but that's WAYYYY ahead in the future) and I'm trying to figure out the best way to do it by looking at how other apps do it.
I prefer to use reg ex over making my own format since it is common knowledge. I wrote a small class that I use which allows me to nest these reg ex routing tables. I use to use something similar that was implemented by inheritance but it didn't need inheritance so I rewrote it.
I do a reg ex on a key and map to my own control string. Take the below example. I visit /api/related/joe and my router class creates a new object ApiController and calls it's method relatedDocuments(array('tags' => 'joe'));
// the 12 strips the subdirectory my app is running in
$index = urldecode(substr($_SERVER["REQUEST_URI"], 12));
Route::process($index, array(
"#^api/related/(.*)$#Di" => "ApiController/relatedDocuments/tags",
"#^thread/(.*)/post$#Di" => "ThreadController/post/title",
"#^thread/(.*)/reply$#Di" => "ThreadController/reply/title",
"#^thread/(.*)$#Di" => "ThreadController/thread/title",
"#^ajax/tag/(.*)/(.*)$#Di" => "TagController/add/id/tags",
"#^ajax/reply/(.*)/post$#Di"=> "ThreadController/ajaxPost/id",
"#^ajax/reply/(.*)$#Di" => "ArticleController/newReply/id",
"#^ajax/toggle/(.*)$#Di" => "ApiController/toggle/toggle",
"#^$#Di" => "HomeController",
));
In order to keep errors down and simplicity up you can subdivide your table. This way you can put the routing table into the class that it controls. Taking the above example you can combine the three thread calls into a single one.
Route::process($index, array(
"#^api/related/(.*)$#Di" => "ApiController/relatedDocuments/tags",
"#^thread/(.*)$#Di" => "ThreadController/route/uri",
"#^ajax/tag/(.*)/(.*)$#Di" => "TagController/add/id/tags",
"#^ajax/reply/(.*)/post$#Di"=> "ThreadController/ajaxPost/id",
"#^ajax/reply/(.*)$#Di" => "ArticleController/newReply/id",
"#^ajax/toggle/(.*)$#Di" => "ApiController/toggle/toggle",
"#^$#Di" => "HomeController",
));
Then you define ThreadController::route to be like this.
function route($args) {
Route::process($args['uri'], array(
"#^(.*)/post$#Di" => "ThreadController/post/title",
"#^(.*)/reply$#Di" => "ThreadController/reply/title",
"#^(.*)$#Di" => "ThreadController/thread/title",
));
}
Also you can define whatever defaults you want for your routing string on the right. Just don't forget to document them or you will confuse people. I'm currently calling index if you don't include a function name on the right. Here is my current code. You may want to change it to handle errors how you like and or default actions.
Yet another framework? -- anyway...
The trick is with routing is to pass it all over to your routing controller.
You'd probably want to use something similar to what I've documented here:
http://www.hm2k.com/posts/friendly-urls
The second solution allows you to use URLs similar to Zend Framework.
Use a list of Regexs to match which object I should be using
For example
^/users/[\w-]+/bookmarks/(.+)/$
^/users/[\w-]+/bookmarks/$
^/users/[\w-]+/$
Pros: Nice and simple, lets me define routes directly
Cons: Would have to be ordered, not making it easy to add new things in (very error prone)
This is, afaik, how Django does it
I think a lot of frameworks use a combination of Apache's mod_rewrite and a front controller. With mod_rewrite, you can turn a URL like this: /people/get/3 into this:
index.php?controller=people&method=get&id=3. Index.php would implement your front controller which routes the page request based on the parameters given.
As you might expect, there are a lot of ways to do it.
For example, in Slim Framework , an example of the routing engine may be the folllowing (based on the pattern ${OBJECT}->${REQUEST METHOD}(${PATTERM}, ${CALLBACK}) ):
$app->get("/Home", function() {
print('Welcome to the home page');
}
$app->get('/Profile/:memberName', function($memberName) {
print( 'I\'m viewing ' . $memberName . '\'s profile.' );
}
$app->post('/ContactUs', function() {
print( 'This action will be fired only if a POST request will occure');
}
So, the initialized instance ($app) gets a method per request method (e.g. get, post, put, delete etc.) and gets a route as the first parameter and callback as the second.
The route can get tokens - which is "variable" that will change at runtime based on some data (such as member name, article id, organization location name or whatever - you know, just like in every routing controller).
Personally, I do like this way but I don't think it will be flexible enough for an advanced framework.
Since I'm working currently with ZF and Yii, I do have an example of a router I've created as part of a framework to a company I'm working for:
The route engine is based on regex (similar to #gradbot's one) but got a two-way conversation, so if a client of yours can't run mod_rewrite (in Apache) or add rewrite rules on his or her server, he or she can still use the traditional URLs with query string.
The file contains an array, each of it, each item is similar to this example:
$_FURLTEMPLATES['login'] = array(
'i' => array( // Input - how the router parse an incomming path into query string params
'pattern' => '#Members/Login/?#i',
'matches' => array( 'Application' => 'Members', 'Module' => 'Login' ),
),
'o' => array( // Output - how the router parse a query string into a route
'#Application=Members(&|&)Module=Login/?#' => 'Members/Login/'
)
);
You can also use more complex combinations, such as:
$_FURLTEMPLATES['article'] = array(
'i' => array(
'pattern' => '#CMS/Articles/([\d]+)/?#i',
'matches' => array( 'Application' => "CMS",
'Module' => 'Articles',
'Sector' => 'showArticle',
'ArticleID' => '$1' ),
),
'o' => array(
'#Application=CMS(&|&)Module=Articles(&|&)Sector=showArticle(&|&)ArticleID=([\d]+)#' => 'CMS/Articles/$4'
)
);
The bottom line, as I think, is that the possibilities are endless, it just depend on how complex you wish your framework to be and what you wish to do with it.
If it is, for example, just intended to be a web service or simple website wrapper - just go with Slim framework's style of writing - very easy and good-looking code.
However, if you wish to develop complex sites using it, I think regex is the solution.
Good luck! :)
You should check out Pux https://github.com/c9s/Pux
Here is the synopsis
<?php
require 'vendor/autoload.php'; // use PCRE patterns you need Pux\PatternCompiler class.
use Pux\Executor;
class ProductController {
public function listAction() {
return 'product list';
}
public function itemAction($id) {
return "product $id";
}
}
$mux = new Pux\Mux;
$mux->any('/product', ['ProductController','listAction']);
$mux->get('/product/:id', ['ProductController','itemAction'] , [
'require' => [ 'id' => '\d+', ],
'default' => [ 'id' => '1', ]
]);
$mux->post('/product/:id', ['ProductController','updateAction'] , [
'require' => [ 'id' => '\d+', ],
'default' => [ 'id' => '1', ]
]);
$mux->delete('/product/:id', ['ProductController','deleteAction'] , [
'require' => [ 'id' => '\d+', ],
'default' => [ 'id' => '1', ]
]);
$route = $mux->dispatch('/product/1');
Executor::execute($route);
Zend's MVC framework by default uses a structure like
/router/controller/action/key1/value1/key2/value2
where router is the router file (mapped via mod_rewrite, controller is from a controller action handler which is defined by a class that derives from Zend_Controller_Action and action references a method in the controller, named actionAction. The key/value pairs can go in any order and are available to the action method as an associative array.
I've used something similar in the past in my own code, and so far it's worked fairly well.
Try taking look at MVC pattern.
Zend Framework uses it for example, but also CakePHP, CodeIgniter, ...
Me personally don't like the MVC model, but it's most of the time implemented as "View for web" component.
The decision pretty much depends on preference...