I'm going ahead to develop the localization files for my project and i am not sure if i am doing the good choice.
<?php
$translation= array(
"sentence" => array ("fr" => "phrase", "it" => "frase")
);
function _($toTranslate = '', $lang = 'en'){
if($toTranslate != ''){
if(!array_key_exists($toTranslate[$lang], $translation))
return $toTranslate;
else
return ${$lang}[$toTranslate];
}
}
?>
I clearly no idea to know if i am doing it well.
Not good enough.
Improvement to your implementation.
Place each language in an single file would be better for performance and maintenance. For example, make a folder called "language", it contains "fr.inc.php", "de.inc.php" and so on. In the config file, you have a line like
$config['language'] = 'de';
You can also load the language code from GET parameter:
$config['language'] = htmlspecialchars($_GET['lang']);
Remember to have check if the language file exists or not (file_exists())
In your bootstrap code, you load the language file (check it before)
require_once LANGUANGE_DIR . '/' . $config['language'] . '.inc.php';
and when you need the language resource string, you call your "_" function, now it should look like
function _($LANG_RES_ID) {
global $lang;
return isset($lang[$LANG_RES_ID] ? $lang[$LANG_RES_ID] : $LANG_RES_ID;
}
2 Use gettext
see php.net: http://www.php.net/gettext
Also, many open source apps are good cases for study, phpMyAdmin, for example.
Related
I'm wrapping up a project using Cakes internationalization features to allow our application to be translated into different languages. That's worked great.
A problem I've noticed though is there are a few places where text is added via JavaScript and this text does not currently come from the server at all. It's for things like dialogue boxes and a few pieces of text that change based on a users selection.
How have you handled this in your own applications? How would you handle this? Is there a library or component that handles this. What about any jQuery libraries?
You can also do it using JavaScript translation files with this format:
lang = {
no: "No",
yes: "Ja",
agreed: "Akkoord"
}
One file per language, for example: lang.nl.js, lang.es.js, lang.en.js...
Then, you can check the current language and, depending on it, load one or another file:
if($this->Session->read('Config.language') == 'es'){
$this->Html->script('lang.es', array('inline' => false));
}else{
$this->Html->script('lang.en', array('inline' => false));
}
And inside your javascripts, instead of using something like this:
alert("Yes");
You should use this:
alert(lang.yes);
And that's it :)
CakePHP does not have a built-in / standard way of localizing JavaScript. It does offer various ways to localize strings 'in general'. See Internationalization & Localization
To localize strings that are output by JavaScript, consider;
For 'static' strings (i.e. strings that are not depending on the content of your website), create localization files for your scripts.
Many plugins use this approach
For example, see this page on localizing the JQuery-UI date picker UI/Datepicker/Localization
If you're already localizing strings in your website via .po files, and want to use the same translations in your JavaScript, you may consider to dynamically create the translation-files as mentioned in 1.), for example;
In your app/Config/routes.php, enable parsextensions, see File Extensions
Router::parseExtensions('json');
Create a controller that will output strings localized as JavaScript/JSON
http://example.com/localized/strings/eng.json
class LocalizedController extends AppController {
public function strings($lang)
{
if('json' !== $this->request->ext) {
throw new NotFoundException();
}
// Switch to the requested language
Configure::write('Config.language', $lang);
$strings = array(
'hello',
'world',
);
//translated the strings
$translations = array();
foreach ($strings as $string) {
$translations[$string] = __($string);
}
// build and send a JSON response
$this->autoRender = false;
$this->response->type('json');
$this->response->body(json_encode($translations));
return $this->response;
}
}
This json file should now be accessible via http://example.com/localized/strings/eng.json and can be loaded from within your javascripts at runtime
note
Just to clarify; the example is untested and just to illustrate the idea of dynamically creating JSON (or JavaScript) files containing localized strings. The code is far from efficient and (at least part of) the code should not be inside the controller, but (for example) inside a model.
For translating JavaScript inside my CakePHP applications, I use this library : https://github.com/wikimedia/jquery.i18n , it's the one used in Wikipedia.
You have all the necessary files inside the src folder. It's quite easy to set up and use. Of course it works with any kind of application, not only CakePHP !
Here's a solution i'm using for cakePHP 3 :
in your layout file ( mine is default.ctp ) :
if( isset( $translated_js ) && !empty( $translated_js ) ){
$this->Html->scriptStart($block_render);
echo "var translated_js = " . json_encode( $translated_js ) . ";";
$this->Html->scriptEnd();
}
Now in any controller add a beforeRender method :
public function beforeRender(Event $event){
parent::beforeRender( $event );
$translated_js = [
'reinit_map' => __('Reinit map to default'),
];
$this->set( 'translated_js' , $translated_js );
}
This way you can use the gettext instructions.
In your JS files you can now use the translated eelements this way :
translated_js.reinit_map
Hope it helps someone searching a way to translate texts and pass to JS
I had the same problem like you and i found this link very helpful: http://jamnite.blogspot.de/2009/05/cakephp-form-validation-with-ajax-using.html
It's not up to date, but the main principle should be clear.
Checkout this CakePHP plugin: https://github.com/wvdongen/CakePHP-I18nJs
It uses the functionality of Drupal 8 JavaScript translations. It has CakePHP console functions to generate .po file(s) (exactly as you're used with Cake), and to generate your translated .po files to JavaScript.
I use a more straightforward method. ( I do not know if it is the best, but it works ).
Inside the template file I define a series of hidden fields with the messages that js might need.
echo( $this->Form->hidden( 'msg-select-promotion-items', [ 'value' => __( 'Select promotion items' ) ] ) );
Here we take advantage of cake's own localization system.
And then in the js file :
alert( $('input[name=msg-select-promotion-items]').val() );
Hope this helps.
Regards.
Facundo.
I took the easier path:
alert( "<?php echo __('This is my translated string'); ?>" )
This way you can keep all translations in a single place: the .po file
I have managed to make my URL i18n compliant using Zend_Controller_Router.
Ie:
en/user/login becomes fr/utilisateur/connexion and both URLs go to the same controller/action.
The problem I am facing is the following
I have a language switcher that is displayed as follow :
Français
English
Italiano
etc.
The currently active language doesn't have an anchor tag, but all others do.
For the languages that have an anchor on them I am building the URL and I want them to be translated in their specific languages.
Currently if I am in French all the urls get builded in french, even if I set the #local key as a param in the URL view helper (tried "#locale" => 'en', "#locale" => new Zend_Locale('en'))
en/utilisateur/connexion
it/utilisateur/connexion
instead of
en/user/login
it/utente/collegamento
because the locale used while building the URL is the one that is defined application wide.
EDIT
I digged a bit deeper in my issue an I found that only the current locale add its resources loaded, which mean I can't get route in the proper language for the route to be built in the right language
My new issue is : how do I load multiple language translation resources?
(I am planning to implement cache in the far future (near release), so I might get better performances)
Hope this helps re the new issue.
"My new issue is : how do I load multiple language translation resources?"
Just a quick caveat to say I didn't write the code below, it was taken from a example app put together by the guys at Zend however it might help to have it broken down like this.
Their approach for the example app was using a csv file containing translations and using your config file (the default one in a new project being application.ini) to specify the path to all your language translation resources.
like this:
;; Languages
language.file.en = APPLICATION_PATH "/../language/en/translate.csv"
language.file.fr = APPLICATION_PATH "/../language/fr/translate.csv"
language.file.de = APPLICATION_PATH "/../language/de/translate.csv"
language.file.es = APPLICATION_PATH "/../language/es/translate.csv"
language.name.zz = Language
language.name.en = English
language.name.fr = Français
language.name.de = Deutsche
language.name.es = Español
And in each csv file, if you are using something like Excel or Open Office to create it, column A would be the original language and column B the translation.
As an example where English is the original language and a Spanish translation is required:
A B
login entrar
logout salir
You could do that for all the words/phrases you want to translate.
If a translation isn't found then the default original word is used.
Your main application bootstrap could have something like this:
protected function _initLanguage()
{
$options = $this->getOptions();
Zend_Registry::set('language',$options['language']);
$langSess = new Zend_Session_Namespace('language');
if (!isset($langSess->translate)) {
$translate = new Zend_Translate('csv', $options['language']['file']['en']);
$langSess->translate = $translate;
}
if (!isset($langSess->locale)) {
$langSess->locale = 'en';
}
Zend_Locale::setDefault($langSess->locale);
}
As the translate object is held in the session you can use it in any of your views etc using something like:
$langSess = new Zend_Session_Namespace('language');
$translate = $langSess->translate;
and:
<?php echo $translate->_('login') ?>
where you want something to be translated on selecting an alternative language. In the example above the word login would appear when English is selected and entrar when Spanish is selected.
There's loads more to Zend_Translate than this and several ways to implement it so I'm not saying this is the best way by any means.
If it helps or if I can give you more info let me know.
Cheers,
Dave
Ok, here's the deal: I am constructing a Drupal website that has several different sections. Each section is a view that displays a content type. (Each section has it's own content type) For example, I have a view that points to ?q=blog which displays content type blog.
All the sections look a little different than each other. Not like 'website-within-a-website' different but different enough that they can't all use the same template file and each be modified with CSS. Each section needs it's own page.tpl.php.
Unfortunately, AFAIK Drupal theme's .info files can only either assign one page.tpl.php for the entire theme or assign a page-node-####.tpl.php for each node. There is going to be lots of content on this website so setting Drupal to make a new identical page-node-####.tpl.php for every created node would get unmanagable very fast.
To solve this problem, I am going to use pathauto to create an alias for each content type. For example, all nodes of content type blog are given an alias ?q=blog/[post title]. Modify template.php to use page-blog.tpl.php for any page who's alias starts with the word 'blog'.
Other people have tried doing this sort of thing and have created functions such as the one described. Unfortunately, all the ones I have seen are for Drupal 6 or below. I have tried modifying existing ones with no success. So far, though, I think this is on the right track:
function basic_preprocess_page(&$vars, $hook) {
...
if( module_exists('path') ) {
$alias = drupal_get_path_alias( $_GET['q'] );
$site_section = "blog";
if( strpos( $alias, $site_section ) === 0 ) {
$VARIABLE_THAT_TELLS_THE_PAGE_WHAT_TEMPLATE_TO_USE = "/path/to/page-blog.php";
}
}
}
I cannot find $VARIABLE_THAT_TELLS_THE_PAGE_WHAT_TEMPLATE_TO_USE does anyone know what it is?
Maybe my site is structured badly. If anyone knows how to restructure my site so I can more easily make a theme with seperate sections please share how!
Thanks a million! (c:
EDIT: Perhaps I need to use template suggestions instead. Does anyone know the function or variable to use to set this?
They changed the name of this array key in D7 and I haven't seen it documented anywhere. I finally figured this out after a good bit of debugging. You can override the theme template in template.php with a hook_preprocess_page() like so:
function myTheme_preprocess_page(&$vars) {
global $node;
if ($node->type == 'blog') {
$vars['theme_hook_suggestions'] = array('my__blog_template'); // use my--blog-template.tpl.php, note '-' = '_'
}
elseif ($node->type == 'articles') {
$vars['theme_hook_suggestions'] = array('article__node_template'); // use article--node-template.tpl.php
}
}
Oh and don't forget to flush the Drupal caches after making changes to your template.php.
Ok, I found it:
http://drupal.org/node/223440#comment-991840
$alias = drupal_get_path_alias($_GET['q']);
if ($alias != $_GET['q']) {
$template_filename = 'page';
foreach (explode('/', $alias) as $path_part) {
$template_filename = $template_filename . '-' . $path_part;
$variables['template_files'][] = $template_filename;
}
}
Credit to this function goes to user mfb.
I had a lot of trouble with this so I will explain it here in case anyone finds it useful.
This function goes in your template.php. It needs to be part of the <theme name>_preprocess_page function. What it does is it takes the alias and then explodes it into a bunch of different components. For example if you are on a page with the alias ?q=blog/blog-post-title it would be exploded into blog and blog-post-title. It then turns each component into a name for a template suggestion. It puts each template suggestion into the template_files[] array (inside the $variables[] array) so that the page now has two new template suggestions:
page-blog, and page-blog-blog-post-title
Template suggestions are alternate template files. In this case they are for pages, but they don't necessarily have to be. You can have template suggestions for anything you can think of including blocks, nodes and the like. Don't let the name 'template suggestion' fool you. Template suggestions will be used over default templates as long as they exist. I don't know why it was named like that. I think it should be renamed.
What you do, then, now that you've set up Drupal to look for a template suggestion that points to your alias, is create a new template file where all the rest are in your theme. In this case, let's say I want to theme my entire blog section. In the templates folder I should create a file named page--blog.tpl.php (note the --double hyphens--) with the layout I want.
Drupal will use the most specific template suggestion it can find so if you wanted you could make one blog post to look completely different than the rest of the site long as you make a template for it named page--blog--blog-post-title and put it in your theme's templates directory. (again, note the double hyphens.)
Ok for sure this has been asked and answered already but i somehow can't find a proper tutorial.
I want to keep the text displayed to users somewhere else and to prevent my code from becoming too large and unreadable.
My site won't be internationalized. I just want to have some kind of file with key-value structure and get the text from there. I want to keep the text in files, not in the database as some tutorials suggest.
I found a solution which will work but i am not sure whether this is a good approach.
I am thinking of using parse_ini_file and to keep my texts in .ini file. Is there something wrong with this approach? Could you suggest something better?
I put all language data in arrays. Its easy and also we can add multi-language support
lang/en.php
<?php
return array(
'index' => 'Homepage',
'feedback' => 'Feedback'
'logout' => 'Logout from profile',
)
?>
lang/ru.php
<?php
return array(
'logout' => 'Выйти из профиля',
)
?>
Then we can load languages:
$lang = include('lang/en.php');
if(isset($_GET['lang']))
{
$lang = array_merge($lang, include('lang/ru.php'));
}
After all it $lang will look like:
Array
(
[index] => Homepage
[feedback] => Feedback
[logout] => Выйти из профиля
)
And we can very simple use it:
function __($name) {
global $lang;
return $lang[$name];
}
Somewhere in the site template:
...
<title><?=__('index')?></title>
</head>
<body>
<?=__('feedback')?>
why not use a plain text file with commas or some uncommon character to hold this data? you can read it and parse it into an array with
$file = file_get_contents("/path/to/file");
$lines = explode('\r', $file);
foreach($lines as $line) $message[substr($line, 0, strpos($line, ','))] = substr($line, strpos($line, ','));
then you should have an array like $messages[3] = "No soup for you!";
the file might look like:
1,The site is down.
2,Try again.
3,No soup for you!
4,Signs point to yes.
(I probably have some of the arguments misplaced in those functions - i always forget which is the needle and which the haystack.)
You can process your data in a script. In this script, you call a certain source (e.g. the ini file you suggest). Then you use a template engine. For this engine, you point towards a template file and give the template all the variables.
The template generates the html and inserts the variables at the right place. This way, you keep you php (business logic) code clean, away from the presentation (the template). Also you can manage the variables in one file (ini/xml but this can be something completely different).
For template engines, Smarty is the most known of all. There are also pure php-based template systems, just Google for them to find one that suits your needs.
I do like this:
$defaultLang = array('Home','Logout',etc)
$otherLang=array( 'ru' => array('Home_in_ru','logout_in_ru',etc);
you translate like this:
echo translate('Home');
function is:
function translate($msg) {
if ($_GET['lang']=='en')
return $msg;
return $otherLang[$_GET['lang']][array_search($msg,$defaultLang)];
}
// Note the function is simplified up there
As you can see the default case deosnt' need to load anything or do any operation, the function just returns back the argument passed
i like the answer with the lang/en.php file. but instead of a file for each language, i use a file for each web page (or class, etc). this keeps file sizes lower and i create a 3D array:
`return array( "EN" => array( "title" => "Welcome - Good Morning", ...),
"TG" => array( "title" => "Mabuhay - Magandang Umaga Po", ...)
);'
Real easy to add new language strings too...
This makes it real easy for language translation contractors since they can see the native language in close proximity to the foreign in 1 editor,,,
I'm posting this question on it's own as I found post one which lacks full explanation or the best way to do it.
Should I use a language file to name items like:
$first_name= 'name';
$last_name = 'last_name';
and then include this file depending on selection?
I have an app which I need to have in two languages.
Will like something in this lines:
http://www.myapp.com/?lang=en
http://www.myapp.com/?lang=es
Should I use a constants file for this purpose?
constants.php
define('OPERATION_NOT_ALLOWED_EN', 'This operation is not allowed!');
define('OPERATION_NOT_ALLOWED_ES', 'This operation is not allowed!');
What is the recommended way of accomplishing this?
You can follow in the footsteps of phpBB - they use a $lang[] array with code that looks like:
$lang['Next'] = 'Next';
$lang['Previous'] = 'Previous';
$lang['Goto_page'] = 'Goto page';
$lang['Joined'] = 'Joined';
$lang['IP_Address'] = 'IP Address';
$lang['Select_forum'] = 'Select a forum';
$lang['View_latest_post'] = 'View latest post';
$lang['View_newest_post'] = 'View newest post';
$lang['Page_of'] = 'Page <b>%d</b> of <b>%d</b>'; // Replaces with: Page 1 of 2 for example
$lang['ICQ'] = 'ICQ Number';
$lang['AIM'] = 'AIM Address';
$lang['MSNM'] = 'MSN Messenger';
$lang['YIM'] = 'Yahoo Messenger';
It is of course included from a file specified in the user's settings, but you'd be fine with your suggestion of ?lang=en in the query string accessing something similar to a constants.php file.
You might want to name the file lang_en.php or something similar for clarity.
That will work for strings like you gave in your example. But there are other complications. For example, do you want to format dates in the appropriate format for the locale (es vs en?). How about formatting of numbers and currencies? Also, you might have "dynamic" messages: "You have $x messages in your inbox". Where you want to pass variables. Check out some of the php localization libraries. Here is an online tutorial: http://www.devx.com/webdev/Article/38732.
Also look at the framework you are using. Many of them have localization support.
How should you implement it? Like Zend_Translate.
I would use locale-specific files with constants, eg:
locale_en.php:
define('GREETING', "Welcome to Widgets Inc');
and
locale_de.php:
define('GREETING', 'Wir begruessen Sie zu Widgets Inc');
in your setup you detect the locale and do:
if ($locale == 'en') {
require 'locale_en.php';
} else if ($locale == 'de') {
require 'locale_de.php';
}
This gives you the benefit of constants and makes localization reasonably transparent to your application in that you don't have to do if ($locale ... all over the place. If anything is locale-specific, just put it in the locale files.
If you want to add a locale file just copy an existing one and change all the references rather than trawling your codebase for them.
You can also put code in these files like correctly formatting numbers and dates for these locales (including the right names in each language but also the use of commas, quotes, periods and so on), currency conversions and displays, etc.
There are free 3rd party controls to do this using an ISO standard XML file (I wrote a database utility to create, edit & export into this format).
The other answers are very manual and involve more work than using this control does.
The control you need is found at:
http://ezcomponents.org/docs/api/trunk/introduction_Translation.html
After the eZ Components are installed on the server, you need to retrieve the base control required for all eZ Components
require_once "ezc/Base/base.php";
/**
* __autoload()
*
* #param mixed $className
* #return
*/
function __autoload( $className )
{
ezcBase::autoload( $className );
}
Then you must define where the XML language file is located (see: ISO639-2, ISO3166, and Qt Linguist)
$config["language_code"] = "en_us"; // as defined by ISO639-2 and ISO3166
// grab our translation XML file
$backend = new ezcTranslationTsBackend( dirname( __FILE__ ). '/translations' );
$backend -> setOptions( array( 'format' => $config["language_code"].'.xml' ) );
// create a manager object
$manager = new ezcTranslationManager( $backend );
$language = $manager->getContext( $config["language_code"], 'strings' );
now you can grab strings by simply calling the following function
getTranslation( "SOME_KEY" );
and to retrieve phrases that have parameters use the following syntax, please note the relation between [KEYWORD] and "keyword" is intentional and recommended
getTranslation( "FIND_[KEYWORD]_BY_[TYPE]", array("keyword" => $keyword, "type" => $type ) );
an example of a TS XML file is (should be called en_US.xml)
<!DOCTYPE TS>
<TS>
<context>
<name>strings</name>
<message>
<source>ZONE_TYPE</source>
<translation>Zone Type</translation>
</message>
<message>
<source>ZONE_TOOL</source>
<translation>Zone Tool</translation>
</message>
<message>
<source>HELLO_[NAME]_WELCOME_TO</source>
<translation>Hello, %name, welcome to Webfood Admin</translation>
</message>
<message>
<source>YOUR_ADMINISTRATIVE_SESSION_HAS</source>
<translation>Your administrative session has timed out. Please login again.</translation>
</message>
</context>
</TS>
I would simply have a setting, in your PHP sessions, that stores the language used, perhaps ask the user before or after they log in which language they want, and store it to their user table if you have accounts. There's no reason to keep sending a URL value over and over, that's a bad idea.