Ok, here we go: Maybe I should explain the project a little: It is a system for managing online E-Learning courses. It has several customers that can be assigned to courses. The customers can manage their courses in coursecategories. Courses and coursecategories have titles and descriptions which are translated. Originally, we had German and English, so I did it the "Jobeet way". Then a customer said, they wanted to also store the data also in Simplified Chinese. Thats when I started to rebuild the translation system. Now, I have a system, where i can simply add a language in my configuration file and be done with it. Here is how it works:
1. in the apps settings.yml, I add the available cultures, like ... application_cultures: code: [en, de, zh] name: [English, Deutsch, Simp.Chinese] 2. In the schema.yml I have to set the "required" option for the translated values to false, so that I won't get an error on saving the form. 3. Then, I had to write an custom validator, that validates that a title (or any other mandatory field) is set in at least one language. In configure() of the main Form (not the I18n): ... $cultures=sfConfig::get('sf_application_cultures'); $this->embedI18n($cultures['code']); for($i=0;$i<count($cultures['code']);$i++) { $this->widgetSchema->setLabel($cultures['code'][$i], $cultures['name'][$i]); } $this->validatorSchema->setPostValidator( new sfValidatorCallback(array('callback' => array($this, 'validateOneTitle'))) ); ... And then write a method validateOneTitle() public function validateOneTitle($validator, $values) { $cultures=sfConfig::get('sf_application_cultures'); for($i=0;$i<count($cultures['code']);$i++) { if(!empty($values[$cultures['code'][$i]]['title'])) { return $values; } } throw new sfValidatorError($validator, 'You need to enter the title in the language, you are editing.'); } That is all in the form class. Now we have to add a small filter form that can be used to trigger the ajax call. It looks like this: class LanguageFilterForm extends BaseForm { public function configure() { $cultures=sfConfig::get('sf_application_cultures'); for($i=0;$i<count($cultures['code']);$i++){ $mycultures[$cultures['code'][$i]]=$cultures['name'][$i]; } $this->widgetSchema['languages']=new sfWidgetFormSelect(array('choices'=>$mycultures)); } } The more tricky parts are the template and the action class. Lets start with the template: In the editSuccess.php the div for the form that is reloaded by the ajax-call is located. I do all my ajax-stuff with jquery. In my example, I use the form for a above explained "Coursecategory". <div id="canvas"> <img id="loader" src="/images/loader.gif" style="vertical-align: middle; display: none" /> <?php include_partial('form', array('form'=>$form,'editingLanguageCode'=>$editingLanguageCode,'editingLanguageName'=>$editingLanguageName,'coursecategory'=>$coursecategory))?> </div> <?php slot('col2_content') ?> <?php include_partial('global/ajaxLanguageForm', array('module'=>$sf_context->getModuleName(),'action'=>'edit','object'=>$coursecategory,'languageForm'=>$languageForm))?> <?php end_slot() ?> We pass all the required data to the form partial (the form object, the coursecategory object (which of course isn't nessessary, but "grown" code), and the languagecode and name). The partial in the slot is a dropdownbox, that I include in all forms for translated Objects, therefor it resides in the layout folder and gets the module and action passed, as well as the LanguageFilterForm. The code of the partial is like: <?php echo javascript_tag() ?> $(document).ready(function() { $("#languages").change(function() { $('#loader').show(); var myLang=$(this).children('option:selected').val(); $("#canvas").load( $(this).parents('form').attr('action'), { lang: myLang }, function() { $('#loader').hide(); } ); }); }); <?php end_javascript_tag() ?> <form action="<?php echo url_for($module.'/'.$action.(!$object->isNew() ? '?id='.$object->getId() : '')) ?>" method="post"> <?php echo $languageForm['languages']->renderError() ?> <?php echo $languageForm['languages'] ?> <?php echo $languageForm['_csrf_token']?> </form> Now the form partial is still missing. I leave this code out, since it is a little long. The important thing is that the form is not rendered by <?php echo $form?> but as single form fields. I split the form into two partials, one is the form with non translated values and the other is the form with translations. So in the main form, I include: <?php include_partial('i18nForm', array('form'=>$form,'editingLanguageCode'=>$editingLanguageCode));?> The I18nForm partial now needs to change the language code in the form fields to store the translated values like: <tr> <th> <label for="coursecategory_<?php echo $editingLanguageCode?>_title"><?php echo __('Title') ?> *</label> </th> <td> <div class="error_message"><?php echo $form[$editingLanguageCode]['title']->renderError() ?></div> <?php echo $form[$editingLanguageCode]['title']->render(array('class' => 'input')) ?> </td> </tr> Thats all in the templates, only the action is missing. Since i have to process two forms (languageform and Objectform), I placed the form creation code in a separate method. Most of the code is pretty standard. public function executeNew(sfWebRequest $request) { $this->getForms($request->getParameter('lang')); } public function executeCreate(sfWebRequest $request) { $this->forward404Unless($request->isMethod(sfRequest::POST)); $this->getForms($request->getParameter('lang')); $this->processForm($request, $this->form); $this->setTemplate('new'); } /** * Edit a Coursecategory * * @param sfWebRequest $request A request object */ public function executeEdit(sfWebRequest $request) { $this->coursecategory=CoursecategoryPeer::retrieveByPk($request->getParameter('id')); $this->getForms($request->getParameter('lang'),$this->coursecategory); if($request->isXmlHttpRequest()) { return $this->renderPartial('coursecategory/form', array('form' => $this->form,'coursecategory'=>$this->coursecategory,'editingLanguageCode'=>$this->editingLanguageCode,'editingLanguageName'=>$this->editingLanguageName)); } } public function executeUpdate(sfWebRequest $request) { $this->forward404Unless($request->isMethod(sfRequest::POST) || $request->isMethod(sfRequest::PUT)); $this->forward404Unless($Coursecategory = CoursecategoryPeer::retrieveByPk($request->getParameter('id')), sprintf('Object Coursecategory does not exist (%s).', $request->getParameter('id'))); $this->getForms($request->getParameter('lang'),$Coursecategory); $this->processForm($request, $this->form); $this->setTemplate('edit'); } protected function processForm(sfWebRequest $request, sfForm $form) { $form->bind($request->getParameter($form->getName()), $request->getFiles($form->getName())); if ($form->isValid()) { $this->getUser()->setFlash('notice', 'Coursecategory saved successfully.'); $Coursecategory = $form->save(); $this->redirect('@coursecategory'); } } protected function getForms($lang=null,$coursecategory=null) { $editingLanguage=$this->getUser()->getEditingLanguage($lang); $this->editingLanguageCode=$editingLanguage['code']; $this->editingLanguageName=$editingLanguage['name']; $this->form = new CoursecategoryForm($coursecategory); $this->languageForm=new LanguageFilterForm(); $this->languageForm->setDefault('languages',$this->editingLanguageCode); } Last thing, I forgot was that the default language should be the one, the user selected for himself, so that is the call to this->getUser()->getEditingLanguage($lang); In myUser.class.php public function getEditingLanguage($lang=null) { $cultures=sfConfig::get('sf_application_cultures'); $editingLanguage['code']=$this->getCulture(); if(!is_null($lang)) { $editingLanguage['code']=$lang; } for($i=0;$i<count($cultures['code']);$i++){ if($cultures['code'][$i]==$editingLanguage['code']) { $editingLanguage['name']=$cultures['name'][$i]; } } return $editingLanguage; } That's all, I hope I didn't forget anything. If anything is unclear, feel free to ask. Here is also some screenshot of the form: http://www.mda.ch/lms/Bildschirmfoto.png http://www.mda.ch/lms/Bildschirmfoto-1.png Regards, Christopher. -----Ursprüngliche Nachricht----- Von: symfony-users@googlegroups.com [mailto:symfony-us...@googlegroups.com] Im Auftrag von Tom Ptacnik Gesendet: Donnerstag, 10. Juni 2010 10:10 An: symfony users Betreff: [symfony-users] Re: Managing i18n content for frontend in the backend I prefer codewise, but if you can show me also how it look in action, I don't mind :) On 9 čvn, 08:08, "Christopher Schnell" <ty...@mda.ch> wrote: > Hi, > > you like to see it codewise or in action? > > Regards, > Christopher. > > -----Ursprüngliche Nachricht----- > Von: symfony-users@googlegroups.com [mailto:symfony-us...@googlegroups.com] > Im Auftrag von Tom Ptacnik > Gesendet: Dienstag, 8. Juni 2010 16:03 > An: symfony users > Betreff: [symfony-users] Re: Managing i18n content for frontend in the backend > > Hi, > > thank you for your response. > > I've managed this today as I sad in my first report (one translation > on the form via mergeForm, selected language is set in the session), > but I would realy like to see your solution. > > Regards, Tom > > On 8 čvn, 09:17, "Christopher Schnell" <ty...@mda.ch> wrote: > > > > > > > Hi, > > > I've done something like this, but with an AJAX approach. The user can > > select the language he wants to edit and the form adjusts to the selected > > language. Then I only have to validate that at least one language does exist > > and adjust the __toString() methods of translated objects to provide > > language fallback. > > > It is a little tricky, but it works. If you need more information, I will > > try to provide some detailed examples. The advantage is, that you don't have > > to get the language in the form, but rather render only the form, required. > > > Hope, this helps a little. > > > Regards, > > Christopher. > > > -----Ursprüngliche Nachricht----- > > Von: symfony-users@googlegroups.com [mailto:symfony-us...@googlegroups.com] > > Im Auftrag von Tom Ptacnik > > Gesendet: Dienstag, 8. Juni 2010 08:54 > > An: symfony users > > Betreff: [symfony-users] Managing i18n content for frontend in the backend > > > Hi, > > > I want to tell you my thoughts about managing I18N content in the > > backend and want to know your opinions. > > > I want to internationalize my frontend app - classic (i18n/ > > messages.xx.xml + object with I18n behaviour) .. no problem > > > Then I need to manage internationalized objects (News) in the backend. > > I have attributes: title and content (doctrine - i18n behaviour). > > I want English, Deutch and Czech language. > > > I need to somehow to display this languages on the form. > > > possibility 1) > > In the master form use $this->embedI18n(array('en', 'de', 'cs')); .. > > like in Jobeet example. > > I don't like this solution because the form is too long. 2 fields for > > every language. (imagine that I want to add some mor languages, or > > more internationalized fields ... I thing this is not suitable for me) > > > possibility 2) > > Show only fields for selected translation. There is some solution here > > [url1]http://forum.symfony-project.org/index.php/t/16823/... I > > almost like it. > > > I want to do something like this [url1]. For this I can use > > embedI18n(array('onlyOneSelectedLang')) or > > mergeForm(xxFormTranslation()) > > I think I'll go with mergeForm() if I don't hit a snag with something. > > > One thing I dont like on the solution in [url1] is that he use use > > sf_culture in the sfUser object for selecting languege and > > sf_default_culture setting. I don't think that is correct, because > > this stuff is for internationalizing the backend, not for the managing > > the internationalized content. Am I right? > > > So if I don't want to use sf_culture - I have to figer out how to > > KNOW which language is selected .. which translation show on the form. > > > I've thought about two solutions > > 1) store it into the session -> something like sf_culture in sf_user > > object > > 2) store in only in the URL ( backend_dev.php/news/1/edit/ > > edited_lang ) > > > If you have read this till this end and you've some opinion or you > > have deal with something similar .. tell me how you did it. > > > I want to do this: > > - Show some tabs-like links for changing the language of the form > > content (EN, DE, CZ) > > - Store this selected lang into the session (contentCulture) > > - On the form show only the fields for the selected lang by > > mergeForm() function > > > What do you thing abou it? > > > -- > > If you want to report a vulnerability issue on symfony, please send it to > > security at symfony-project.com > > > You received this message because you are subscribed to the Google > > Groups "symfony users" group. > > To post to this group, send email to symfony-users@googlegroups.com > > To unsubscribe from this group, send email to > > symfony-users+unsubscr...@googlegroups.com > > For more options, visit this group > > athttp://groups.google.com/group/symfony-users?hl=en > > -- > If you want to report a vulnerability issue on symfony, please send it to > security at symfony-project.com > > You received this message because you are subscribed to the Google > Groups "symfony users" group. > To post to this group, send email to symfony-users@googlegroups.com > To unsubscribe from this group, send email to > symfony-users+unsubscr...@googlegroups.com > For more options, visit this group > athttp://groups.google.com/group/symfony-users?hl=en -- If you want to report a vulnerability issue on symfony, please send it to security at symfony-project.com You received this message because you are subscribed to the Google Groups "symfony users" group. To post to this group, send email to symfony-users@googlegroups.com To unsubscribe from this group, send email to symfony-users+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/symfony-users?hl=en -- If you want to report a vulnerability issue on symfony, please send it to security at symfony-project.com You received this message because you are subscribed to the Google Groups "symfony users" group. To post to this group, send email to symfony-users@googlegroups.com To unsubscribe from this group, send email to symfony-users+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/symfony-users?hl=en