Building multi language website
Hi,
just out of curiosity. I would like to build a multi language site with FuelCMS soon and I am thinking what could be the best practise in building it and still being able to use CMS backend. It could be good to have possibility to edit pages in different languages. So far I have an idea to build a custom model together with view, which will load the appropriate page according some "language" key in the table.
Any better ideas?
Comments
The common theme seems to be to create pages in the base language, then add 'versions' of the page for other given languages. This allows you to add translations for pages you have, and not those you dont. My experience is that you dont get 100% of the site translated on day one. Since you already store versions and cache the live one, you just need to be able to manage multiple versions at once and publish them.
The good modules also allow you to change the Meta / Navigation / Breadcrumb / Slug for the page too. The one i've used I have the whole site in English, then added a French translation for certain pages under site.com/fr/path/to/page.
Version 1.1 could include auto-deciphering browser language and a drop-down on the page which sets the user's default language and stores in session / cookie.
Further, I did use the concept of "culture" in one project, because en_US might actually contain different content and approach to en_GB... Sites like microsoft and apple change their approach based on region.
The easiest part i guess, is to add to the fuel_page_variables table a "langID" row. after that i extend the pages controller and add a multi_lang controller. (pages contains a dropdown with all languages, onselect the page will refresh with the new languageID. This will guarantee me a global translation for all fields.
its a bit of work, but with your great logic not that hard.
// config
i've created a new config file with an array (array(0=>array('id'=>0, 'langCode=>'en' ...)))
// library
i've created a autoload library with some functions (get_current_lang, set_lang, create_lang_chooser ....) and a helper with some short-tag functions.
//pages
i extended the table "fuel_page_variables" with one more field, named "lang_id".
the page controller has a select-dropdown with all languages. on select, the page is reloading and the edit() function will load all entries from fuel_page_variables (where id AND lang_id). so for every entry i create a own lang entry. (thats perfekt, so i have a global multilang-modular system.
//tricky: the multilang navigation/breadcrumb etc.
i extended the table "navigation" with one more field named "opt_label".
its stores a serialized array with all other language-labels. (if present). therefor i extended the navigation modul (create, edit, _form function). now, if i edit a navigation item, there will be a input field for each language (btw, it's a pity, that we cant add a fieldset with you absolutely unbelievable great form_builder - ).
at least i extended the menu.php with some functions to get the right label (check which language is currently needed -> check if that id is in the serialized array from the opt_label field -> if yes -> replace it with the label info.
also i had to check the _create_link function to extend the links with (site/en/about/)
-> for that i needed by the way a new function, which i placed in your url_helper:
function is_admin_area() { $CI =& get_instance(); $fuel_path = $CI->config->item('fuel_path', 'fuel'); $fuel_path = substr($fuel_path , 0,-1); if($CI->uri->segment(1) == $fuel_path) { return TRUE; } else { return FALSE; } }
because the _create_link function is also used in the adminsection, and there we don't need those edits.
Thank you,
(sorry for my bad english | greetings from a small guy from austria)
(btw, fuel cms does already have a admincheck like my function above. (IN_FUEL_ADMIN)
...
please correct me:
in page_router.php after the check if cms or frontend i will load a languagefile
$this->lang->load('default', $this->multilang->get_current_langID);
$this->multilang->get_current_langID is the function from my new multilang class, which checks the url (/en/, /de/...) to return the right language file.
and now im able to load in every view my right language value $this->lang->line('hello world');
so i can extend fuel with all his modules with the current languagefile. (for example for the blog line('add_your_comment')
what do you think about that?
"the page controller has a select-dropdown with all languages. on select, the page is reloading and the edit() function will load all entries from fuel_page_variables (where id AND lang_id). so for every entry i create a own lang entry."
it would rock to have some hints on how to do all that... maybe Webcam feels like contributing some snippets ?
1. Create a config file or table to contain the language options (e.g. $config['languages'] = array('english' => 'English', 'spanish' => 'Spanish')... )
2. Modify the fuel/modules/fuel/models/pages_model.php file's form_fields method and add the following field. For the options value, substitute in your own language options. The value that will get saved in the fuel_page_variables table will be the key of that array.
$my_lang_options = $CI->config->item('languages'); $fields['lang_id'] = array('label' => 'Language', 'type' => 'select', 'options' => $my_lang_options);
3. Add a lang_id value to the fuel_page_variables table
4. Alter the fuel/modules/fuel/controllers/pages.php controller around line 357 so that the saving array includes the new lang_id value:
$save = array('page_id' => $id, 'name' => $key, 'value' => $value, 'type' => $val['type'], 'lang_id' => $CI->input->post('lang_id');
5. Alter the fuel/modules/fuel/assets/js/fuel/controller/PageController.js file so that you attach a change handler to the #lang_id field that will load in the proper fields/values when change the language in the select. This may require a change to the pages controller's layout_fields method so that it includes the lang_id value for pulling in the saved variable values.
6. Alter the pagevariables_model's _common_query() method to always select from a certain language:
$CI =& get_instance(); $language = $CI->config->item('language'); $this->db->where(array('lang_id' => $language));
That covers most of the backend part, but now there needs something on the front end to automatically set the language config value when someone is viewing the site. This part can be done a few different ways and people seem to have opinions as to which is the best way. The main ones being either reading it from a URL or from a cookie/session. If it were from a session, you could set a select dropdown on the front end for a viewer to set their language. Then a controller could set that session variable. You could then set a CI hook to set that language config value value that is used in step 6 above.
Hope this helps point you along the right path.
You need to consider a lot of things, before you start. such as the page_variables (there must be a lang_id)
Thats my 'Pages' Site: (in german)
in my db, i've extended the pages table like that: (also the navigation and blocks table)
The most important part was to add a new class to the Core (autoload)
class MY_Lang extends CI_Lang { var $languages = array( 'de' => array('name' => 'german', 'langCode' => 'de', 'id' => 0), 'en' => array('name' => 'english', 'langCode' => 'en', 'id' => 1) ); var $current_lang_id = 0; var $default_lang_id = 0; var $current_segment = '';
In this Class, there are a lot of other functions, as a foundation you can use the internationalization i18n Class. I extended it with some other functions like "get_admin_lang_chooser", which i'll call it in the module_create_edit_actions view file.
I've also extended the Base_module_model with two new functions and one new var.
var: $lang_id = 0; function 1: set_language(); function 2: get_language();
so we have the possibility, to add multilang features on modules too.
so... good luck!
I looked at your code..
First 2 typo corrections for anyone confused:
$my_lang_options = $CI->config->item('languages'); $save = array('page_id' => $id, 'name' => $key, 'value' => $value, 'type' => $val['type'], 'lang_id' => $this->input->post('lang_id'));
5. Alter the fuel/modules/fuel/assets/js/fuel/controller/PageController.js
I solved this by triggering change event
$('#layout').change(function(e){ var path = jqx.config.fuelPath + '/pages/layout_fields/' + $('#layout').val() + '/' + $('#id').val() +'/' + $('#lang_id').val(); $('#layout_vars').load(path, {}, function(){ _this.initSpecialFields(); }); }); $('#lang_id').change(function(e){ $('#layout').trigger('change'); });
I changed the controller
function layout_fields($layout, $id = NULL, $lang_id = NULL) { .... if (!empty($id)) { $page_vars = $this->pagevariables_model->find_all_by_page_id($id,$lang_id); $this->form_builder->set_field_values($page_vars); } .... }
I also changed pagevariables, i set the default lang_id in constructor.
function find_all_by_page_id($page_id,$lang_id=NULL) { .... if(!empty($lang_id)){ $this->lang_id = $lang_id; }; .... }
$this->db->where(array('lang_id' => $this->lang_id));
Maybe i should take a look at webcam's implementation
At the moment, changing the lang_id only changes the one page_Id.
I'm guessing I would need to iterate through the languages I have in my config file and duplicate the layout_fields found in the MY_fuel_layouts.php for every lang_id it finds. That's where I'm lost. Any help would be appreciated.
With regards to looping through the MY_fuel_layouts for each language, I think the idea mentioned above recommends using the lang_id dropdown and on change it ajax's in the new page_variable fields specific to the selected language.
@admin: location/slug? you mean at the frontend? such as www.page.com/en/page?
yes of course.
at the admin panel, it works mostly with ajax:
As a example, my navigation page: If i trigger the select, it will open a ajax request to
"fuel/navigation/lang_fields/2/0" uri_segment(3) => navID uri_segment(4) => langID
The same happens on my Block Page.
The first language, which will be loaded (on page load/reload), is always the default lang. So you can start with any language you want.
Pages Page ( sounds funny)
There i have to reload the whole page, because of some other features like my parent selector. its also necessary because of the linked navigation items on the right side => every language does have their own linked navigation location.
So, my pages url looks like this:
fuel/pages/edit/25/0 uri_segment(3) => pageID uri_segment(4) => langID
GENERALLY page_variables:
after the ajax request at the pages page, it will work that easy: get all where('page_id' => 22, 'lang_id' => '1')
IMPORTANT hope this helps, guys
I changed
UNIQUE KEY `page_id` (`page_id`,`name`)
to
UNIQUE KEY `page_id` (`page_id`,`name`,`lang_id`)
But the page_variables keep overwriting the old values I'm not pro enough to figure out why.
you save the page variables thru the page controller with the private _save_page_vars method, right?
$where = array('page_id' => $id, 'lang_id' => $langID) ;
damn, finally figured it out.. It deletes the old entries 6 lines above
// clear out all other variables $this->pagevariables_model->delete(array('page_id' => $id, 'lang_id' => $this->input->post('lang_id')));