Cached translations in CakePHP 3.7.4 - cakephp

I have taken over a project on cakephp.
The problem is that I can not change translation texts.
Under src/Locale I have:
en_EN
default.mo
default.po
no_NO
default.mo
default.po
cake.pot
default.pot
In controller
public function view($id)
{
$order = $this->Order->get($id);
$this->set(compact('order'))
}
In view I have a form where is translated text by default
<?= $this->Form->control('email_message', [
'type' => 'textarea',
'rows' => 15,
'help' => sprintf('Email will be sent to %s', h($order->contact_email)),
'default' => __('pickup_mailtext')
]) ?>
No I have in
en_EN
default.po
msgid "pickup_mailtext"
msgstr "This is the old pickup mailtext"
if I change it to
msgid "pickup_mailtext"
msgstr "This is the NEW pickup mailtext"
Nothing changes. I have deleted everything in persistent directory.
Also in Config/app.php default language is set to no_NO, but as I mentioned before this string is under en_EN
under
no_NO
default.po
There is:
msgid "pickup_mailtext"
msgstr ""
I have also noticed, tad this string is in
en_EN
default.mo
but if I try to modify it I get Internal server error.
So my question is:
1. How to get this translation working? Why this string is not changing?
2. If the default language is set to no_NO, then why the translations is in en_EN and why it is getting translated instead of being empty?
3. How to clear those .mo files?
Thanks

.mo files are the compiled binary versions of the respective .po files, you can't just modify them with a text editor, you need to recompile the .po files instead, using a program like msgfmt, or one with a GUI like Poedit.
CakePHP will by default prefer .mo files over .po files (the former are usually faster to parse), ie if a .mo file exists it will be used instead of a possible .po file with the same name, so if you change only the .po file, nothing will happen as that file is not being used.
If changing the default locale in config/app.php has no effect, then the locale might get changed somewhere else in your app. Check \Cake\I18n\I18n::getLocale() in your view template to figure what locale is actually being used at that point, and set a breakpoint in \Cake\I18n\I18n::setLocale() (vendor/cakephp/cakephp/src/I18n/I18n.php) or log a stacktrace to figure from where in the code the locale is being set.

Related

CakePHP plugin localization in different files

I've made ContentManager plugin for my cakephp (3.2) apps. It has base translation file in /plugins/ContentManager/src/Locale/ru/content_manager.po.
Now I want to add some additions translations on application level. So I need some additional po file for my plugin. If I put one more content_manager.po file in app\src\Locale\ru folder, it overrides base file.
Is it possible to add file like content_manager.0.po, content_manager.base.po or something, and make whem work together? Anyway I need to extend plugin transalations somehow.
base file contains strings like these
msgid "tab_MenuItems"
msgid "content.type.News"
msgid "users.role.admin"
and so on.
Additional file contains application-specific strings like:
msgid "content.type.Products"
msgid "content.type.Orders"
msgid "users.role.manager"
plugin itself uses translations like (example, smarty) this:
{foreach $registered_types as $ct}
<li>{__d("content_manager", "content.type.$ct")}</li>
{/foreach}
You can add a default.po file in your apps ru locale folder, this file will by default be used by the so called "fallback loader", which is going to be used in case the requested message ID cannot be found in the primary domain, ie in content_manager.po.
In case you need more control, you can always build custom loaders/packages, like for example:
use Cake\I18n\I18n;
use Cake\I18n\MessagesFileLoader;
I18n::config('content_manager', function ($name, $locale) {
$fileLoader = new MessagesFileLoader('content_manager', $locale, 'po');
/* #var $package \Aura\Intl\Package */
$package = $fileLoader();
$package->setFallback('content_manager.base');
return $package;
});
This would create a loader that internally pre-loads the default content_manager domain messages, and set the fallback loader domain to content_manager.base, so that it would try to load messages from your content_manager.base.po file in case there's no translation present in the default domain.
And don't forget to clear the cache (tmp/cache/persistent) after adding/modifying translations/loaders.
See also
Cookbook > Internationalization & Localization > Creating Generic Translators
API > \Cake\I18n\I18n::config()
API > \Cake\I18n\MessagesFileLoader

CakePHP i18n __ function returns array

I'm running into a problem with the __ function, and I'm not sure whether it's a bug in Cake (3.2.8), Aura\Intl, or my code. I've tried the same thing in Cake 1.3, and it works as I expect it to, but it's possible that my expectations are simply that way because that's how it worked in 1.3. :-)
When I am building my menus, I use things like __('Teams'), but I also have pages that use things like __n('Team', 'Teams', count($player->teams)). The i18n shell extracts these into the default.pot separately, so when I translate it to French, it's like this:
msgid "Teams"
msgstr "Équipe"
msgid "Team"
msgid_plural "Teams"
msgstr[0] "Équipe"
msgstr[1] "Équipes"
If I call __('Team'), I correctly get 'Équipe' returned, and if I call __n('Team', 'Teams', $x), I correctly get 'Équipe' or 'Équipes' returned, depending on the value of $x. But if I call __('Teams') I get back
Array
(
[0] => Équipe
[1] => Équipes
)
This is the case even if I eliminate the msgid "Teams" section, leaving only the plural definition.
In Cake 1.3, __('Teams') would simply return 'Équipes'. (Don't know what it might do in 2.x, as I skipped over that entirely.) So, whose bug is this?
You have two Teams message IDs. The problem is that the CakePHP message file parser stores the messages in a key => value fashion, where the message ID is used as the key, resulting in the Teams messages for msgid_plural to override the Teams message from the preceding msgid.
https://github.com/cakephp/cakephp/blob/3.2.8/src/I18n/Parser/PoFileParser.php#L149
https://github.com/cakephp/cakephp/blob/3.2.8/src/I18n/Parser/PoFileParser.php#L172
https://github.com/cakephp/cakephp/blob/3.2.8/src/I18n/Parser/MoFileParser.php#L137-L140
Since gettext seems to be able to handle this, I'd say that it's at least a missing feature (which might be intentional), however it might even be a bug, I can't tell for sure (but I'd tend towards bug). For clarification, open an issue over at GitHub.
To (temporarily) workaround this issue you could use contexts.
Duplicate message definition
This is problematic:
msgid "Teams" <- same string
msgstr "Équipe"
msgid "Team"
msgid_plural "Teams" <- same string
The first means you have or are expecting to have __('Teams') in the application code, which expects to return a string.
The second scenario is going to create ambiguous data when the po file is parsed. The class responsible for converting po files to the array format is the PoFileParser, which contains these lines:
$messages[$singular] = $translation; // <- a string
...
$messages[$key] = $plurals; // <- an array
Where $messages is the array used to lookup translations, indexed by the translation key.
So, the reason for the observed behavior is because this code:
__('Teams');
is going to look for $messages['Teams']
This code:
__n('Team', 'Teams', 2);
is going to look for $messages['Teams'][<index>], and the $messages array is going to contain the parsed data from the plural translation only, which overwrote the "singular" version of the string as it was earlier in the file.
The code-level solution is to ensure that all msgid and msgid_plural keys are unique (since they are essentially, the same thing).
Bad translation definition
You may well find at some point in the future that translations like __('Teams') are very problematic, depending on how that loose word is being used, they are an indicator of bad translation definitions.
To give an example, CakePHP used to have translations of this form in baked output:
...
sprintf(__('Invalid %s', true), 'Team');
...
Which was later changed to:
__('Invalid Team', true)
Because the translation of Invalid %s can change depending on what %s is - so can the translation of %s.

Different coding sets in database and website

I have a website with very simple news system (posting, editting, deleting etc). All my html pages are saved in UTF-8 formatting, everything displayes correctly.
I specify using UTF in every header:
For saving news to database, I use simple scripts like (all values come from a html form):
$newsTitel = isset($_POST['title']) ? $_POST['title'] : 'Untitled';
$submitDate = $date = date('Y/m/d');
$content = isset($_POST['newstext']) ? $_POST['newstext'] : 'No content';
include 'includes/dbconnect.php';
mysql_query("SET CHARACTER SET utf8");
mysql_query("SET NAMES 'utf8'");
$query = mysql_query("INSERT INTO news SET date='$submitDate',subject='$newsTitel',news='$content'");
The data get saved to database but in a weird format (coding). There are characters like à ¡ Ä etc which makes the content almost unreadable. Other problem is that when loading this content back to html forms (for editting news) it displays in this weird coding. When I looked into the specification of the database I use, it says that it saves data in UTF-8.
I use phpMyAdmin to access the MYSQL database.
So to sum it up:
Pages: saved in UTF8, all have correct header
Database: interaction with the server: utf8_czech_ci, tables in the same format
What I do not understand at all is this strange bevaior:
1) I save the data into the database using the script above
2) I take a look into phpMyAdmin and see broken encoding
3) I load the data back into my website and display them using this:
<?php
include 'includes/dbconnect.php';
$data = mysql_query("SELECT * FROM news ORDER BY id DESC limit 20") or die(mysql_error());
while($info = mysql_fetch_array( $data ))
{
echo '<article><h3> '.$info['subject'].'</h3><div id="date">'.$info['date'].'</div>';
echo '<p>'.$info['news']. '</p></article>';
}
?>
The encoding is correct and no weird characters are displayed.
4) I load the exact same data into a html form (for edition purposes) and see the same broken encoding as in the database.
What happened? I really dont get it. I tried fixing this by re-saving everything in utf8, alterign tables and changing their encodings into different utf8 versions etc...
This is example of a data I pass to the database (it is in czech with html tags):
<p>Vařila myšička kašičku</p>
<img src="someImage.jpg">
<p>Další text</p>
Thanks for any help...
The commands for specifying the character set should be:
set names 'utf8';
If you check the result returned from your queries at the moment, what does it say? If I try it in the monitor I get the following:
mysql> set names 'UTF-8';
ERROR 1115 (42000): Unknown character set: 'UTF-8'
Have you tried using set names 'utf8' before connecting for the SELECT as well? The characters you're saying are output make me think you're getting back the correct bytes for UTF-8, but they're being interpreted as ISO-8859-1.
You are not escaping single quotes or some other html chars.
Use mysql_real_escape_string.
$newsTitel = isset($_POST['title']) ? mysql_real_escape_string($_POST['title']) : 'Untitled';

i18n in cake - how to use language shortcut like en instead of eng

I have my app ready for translation but unfortunately I developed it using 'en', 'de' and 'pl' language shortcuts instead of 'eng', 'ger' and 'pol'. Now I see that when I do Configure::write('Config.language','pl') cake doesn't want to read translations from App/Locale/pl/LC_MESSAGES/default.po - but it works with pol for example. The problem is that all my links, interniationalized files and so on are using 'en','de','pl' - I can't just change it to 'eng', 'pol' and so on... How I can force cake to use my configuration?
You can use 2 letter language code for Config.language configuration value and your urls. But name your locale folders using the corresponding 3 letter code. So for your language codes 'en', 'de', 'pl' the folders would be Locale/eng, Locale/deu, Locale/pol respectively. Cake will properly look in the corresponding folder for translation files.

the best way to make codeigniter website multi-language. calling from lang arrays depends on lang session?

I'm researching hours and hours, but I could not find any clear, efficient way to make it :/
I have a codeigniter base website in English and I have to add a Polish language now. What is the best way to make my site in 2 language depending visitor selection?
is there any way to create array files for each language and call them in view files depends on Session from lang selection? I don't wanna use database.
Appreciate helps! I'm running out of deadline :/ thanks!!
Have you seen CodeIgniter's Language library?
The Language Class provides functions
to retrieve language files and lines
of text for purposes of internationalization.
In your CodeIgniter system folder you'll
find one called language containing sets
of language files. You can create your
own language files as needed in order
to display error and other messages in
other languages.
Language files are typically stored in
your system/language directory. Alternately
you can create a folder called language
inside your application folder and store
them there. CodeIgniter will look first
in your application/language directory.
If the directory does not exist or the
specified language is not located there
CI will instead look in your global
system/language folder.
In your case...
you need to create a polish_lang.php and english_lang.php inside application/language/polish
then create your keys inside that file (e.g. $lang['hello'] = "Witaj";
then load it in your controller like $this->lang->load('polish_lang', 'polish');
then fetch the line like $this->lang->line('hello'); Just store the return value of this function in a variable so you can use it in your view.
Repeat the steps for the english language and all other languages you need.
Also to add the language to the session, I would define some constants for each language, then make sure you have the session library autoloaded in config/autoload.php, or you load it whenever you need it. Add the users desired language to the session:
$this->session->set_userdata('language', ENGLISH);
Then you can grab it anytime like this:
$language = $this->session->userdata('language');
In the controller add following lines when you make the cunstructor
i.e, after
parent::Controller();
add below lines
$this->load->helper('lang_translate');
$this->lang->load('nl_site', 'nl'); // ('filename', 'directory')
create helper file lang_translate_helper.php with following function and put it in directory system\application\helpers
function label($label, $obj)
{
$return = $obj->lang->line($label);
if($return)
echo $return;
else
echo $label;
}
for each of the language, create a directory with language abbrevation like en, nl, fr, etc., under
system\application\languages
create language file in above (respective) directory which will contain $lang array holding pairs label=>language_value as given below
nl_site_lang.php
$lang['welcome'] = 'Welkom';
$lang['hello word'] = 'worde Witaj';
en_site_lang.php
$lang['welcome'] = 'Welcome';
$lang['hello word'] = 'Hello Word';
you can store multiple files for same language with differently as per the requirement
e.g, if you want separate language file for managing backend (administrator section) you can use it in controller as $this->lang->load('nl_admin', 'nl');
nl_admin_lang.php
$lang['welcome'] = 'Welkom';
$lang['hello word'] = 'worde Witaj';
and finally
to print the label in desired language, access labels as below in view
label('welcome', $this);
OR
label('hello word', $this);
note the space in hello & word you can use it like this way as well :)
whene there is no lable defined in the language file, it will simply print it what you passed to the function label.
I second Randell's answer.
However, one could always integrate a GeoIP such as http://www.maxmind.com/app/php
or http://www.ipinfodb.com/. Then you can save the results with the codeigniter session class.
If you want to use the ipinfodb.com api You can add the ip2locationlite.class.php file to your codeigniter application library folder and then create a model function to do whatever geoip logic you need for your application, such as:
function geolocate()
{
$ipinfodb = new ipinfodb;
$ipinfodb->setKey('API KEY');
//Get errors and locations
$locations = $ipinfodb->getGeoLocation($this->input->ip_address());
$errors = $ipinfodb->getError();
//Set geolocation cookie
if(empty($errors))
{
foreach ($locations as $field => $val):
if($field === 'CountryCode')
{
$place = $val;
}
endforeach;
}
return $place;
}
For easier use CI have updated this so you can just use
$this->load->helper('language');
and to translate text
lang('language line');
and if you want to warp it inside label then use optional parameter
lang('language line', 'element id');
This will output
// becomes <label for="form_item_id">language_key</label>
For good reading
http://ellislab.com/codeigniter/user-guide/helpers/language_helper.html
I've used Wiredesignz's MY_Language class with great success.
I've just published it on github, as I can't seem to find a trace of it anywhere.
https://github.com/meigwilym/CI_Language
My only changes are to rename the class to CI_Lang, in accordance with the new v2 changes.
When managing the actual files, things can get out of sync pretty easily unless you're really vigilant. So we've launched a (beta) free service called String which allows you to keep track of your language files easily, and collaborate with translators.
You can either import existing language files (in PHP array, PHP Define, ini, po or .strings formats) or create your own sections from scratch and add content directly through the system.
String is totally free so please check it out and tell us what you think.
It's actually built on Codeigniter too! Check out the beta at http://mygengo.com/string
Follow this https://github.com/EllisLab/CodeIgniter/wiki/CodeIgniter-2.1-internationalization-i18n
its simple and clear, also check out the document # http://ellislab.com/codeigniter/user-guide/libraries/language.html
its way simpler than
I am using such code in config.php:
$lang = 'ru'; // this language will be used if there is no any lang information from useragent (for example, from command line, wget, etc...
if (!empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) $lang = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'],0,2);
$tmp_value = $_COOKIE['language'];
if (!empty($tmp_value)) $lang = $tmp_value;
switch ($lang)
{
case 'ru':
$config['language'] = 'russian';
setlocale(LC_ALL,'ru_RU.UTF-8');
break;
case 'uk':
$config['language'] = 'ukrainian';
setlocale(LC_ALL,'uk_UA.UTF-8');
break;
case 'foo':
$config['language'] = 'foo';
setlocale(LC_ALL,'foo_FOO.UTF-8');
break;
default:
$config['language'] = 'english';
setlocale(LC_ALL,'en_US.UTF-8');
break;
}
.... and then i'm using usualy internal mechanizm of CI
o, almost forget! in views i using buttons, which seting cookie 'language' with language, prefered by user.
So, first this code try to detect "preffered language" setted in user`s useragent (browser). Then code try to read cookie 'language'. And finaly - switch sets language for CI-application
you can make a function like this
function translateTo($language, $word) {
define('defaultLang','english');
if (isset($lang[$language][$word]) == FALSE)
return $lang[$language][$word];
else
return $lang[defaultLang][$word];
}
Friend, don't worry, if you have any application installed built in codeigniter and you wanna add some language pack just follow these steps:
1. Add language files in folder application/language/arabic (i add arabic lang in sma2 built in ci)
2. Go to the file named setting.php in application/modules/settings/views/setting.php. Here you find the array
<?php /*
$lang = array (
'english' => 'English',
'arabic' => 'Arabic', // i add this here
'spanish' => 'Español'
Now save and run the application. It's worked fine.

Resources