Getting Section home, language and locale

Permalink Browser Info Environment
How to get the language for the current section has been discussed in depth already, but mostly you also need to know the home page for the current section and sometimes also the locale (e.g. for strftime). I started adding this code to all of my themes:
$lang = LANGUAGE;
$locale = ACTIVE_LOCALE;
$home = DIR_REL;
if( Package::getByHandle('multilingual') ) {
   $ms = MultilingualSection::getCurrentSection();
   if (is_object($ms)) {
      $home = Loader::helper('navigation')->getLinkToCollection($ms, true);
      $lang = $ms->getLanguage();
      $locale = $lang . "_" . $ms->getIcon();
   }
}

Then somewhere in the template you have code like this
<html lang="<?=$lang?>">

and
<a href="<?=$home?>/">HOME</a>

What do you think about this? Did I overlook any problems?

Type: Discussion
Status: New
PatrickHeck
View Replies: View Best Answer
Mainio replied on at Permalink Reply
Mainio
Hi,

Kinda the same what I do with my themes. Would be great if there would be some helper method to automate it, though...

Also, as you already mentioned in the other discussion, with language manager I usually do this with the $GLOBALS['LOCALE'] definition because it already points to the "current" locale that is selected.

I think this is actually a minor issue in the multilingual package/c5 core because I feel that there should be a constant that would define under what language (or locale) the user currently is. I feel that ACTIVE_LOCALE should point to the "selected" locale instead of where it now points.

Other thing is also that there is no way for developers to override the locale settings which also made the t() function issue hard in Language Manager. We needed to write few lines over from the localization class to get the desired functionality.

And the reason why developers cannot override the locale constants is that they are defined before any event is launched that I could grab on in the package.

These are few things I feel could be fixed in the c5 core regarding the multilingual functionality and developers.

Br,
Antti
PatrickHeck replied on at Permalink Reply
PatrickHeck
Hi Antti,

I totally agree! This should all be included in the helper class.

Do you have any ideas how to approach the ACTIVE_LOCALE / t() problem? Maybe you can open a pull request at the repository.

Patrick
Mainio replied on at Permalink Reply
Mainio
Hi,

This is quite a tricky question for me because I'm not that familiar with the c5 core and also I cannot always know what the core team is thinking without meeting them in person.

However, I can suggest something here but I really have no idea whether this would affect to some other things in the core, so please be sure to check through whether this is a good solution.

Here's what I would do:
1. In dispatcher.php: Move the following
require(dirname(__FILE__) . '/config/localization.php');

AFTER the package startup events are launched (I edited this, see below). This would allow developers to override the localization definitions, which I think would be a good thing for developers. This would also allow us to set the ACTIVE_LOCALE from the Multilingual package.


EDIT: In fact, it would be enough to move the localization.php require line AFTER these lines:
## Package events
require(dirname(__FILE__) . '/startup/packages.php');

This would allow us to set those variables from the package before the localization.php is loaded.


2. In the Multilingual package, just grab on to that event, check for the current Multilingual section and set the ACTIVE_LOCALE, etc. accordingly.

I think that there is some reason why the localization configuration is run that early in the dispatcher, and I would think the reason for that is that some of the other configurations / classes require these settings to be already set. So it might be best to load the events class earlier in the dispatcher.php and create a new event that would be run before the localization.

Please consult the core team about this issue, I think they have much better view of what happens in dispatcher and in loading those configurations / classes.

Br,
Antti

EDIT2: Spent some time for checking through the files that are loaded between those require lines and I don't see any reason why the localization.php require could not be moved after the package startup events.

EDIT3: This is still not the most optimal solution because developers cannot change the order of how the package startup events are launched. So, if Multilingual package startup events sets the locale variables, they cannot be set after that from any other package which I still think is not good for developers. In my opinion the optimal solution would be to store the locale in a global variable that could easily be overridden by developers. Once the "define" function is used, that constant is really defined for that request, there's no way to override that in PHP (without PHP extensions).

So, this would require the following changes to
/concrete/config/localization.php
global $ACTIVE_LOCALE;
if (defined($ACTIVE_LOCALE) && $ACTIVE_LOCALE != '') {
  define("ACTIVE_LOCALE", $ACTIVE_LOCALE);
} else if ($u->getUserDefaultLanguage() != '') {
...
PatrickHeck replied on at Permalink Reply
PatrickHeck
I'd like to post a workaround for the t() problem. It works if you use the two letter language code in your URL. (likehttp://blabla.com/c5site/index.php/en/about)... If such a code is detected it will automatically set the locale according to that. That way all other functions that rely on LOCALE (including t()) will work properly.

Just put this at the end of your config/site.php:
$locales = array(
         "en" => "en_US",
         "es" => "es_ES",
         "fr" => "fr_FR",
         "it" => "it_IT",
         "de" => "de_DE",
         "zh" => "zh_CN",
         "ar" => "ar_AW");
$regexp = '/^'.preg_quote(DIR_REL,'/').'\/(index.php\/)?([^\/$]+)/';
preg_match($regexp,$_SERVER['REQUEST_URI'],$match);
if (isset($locales[$match[2]])) {
   define('LOCALE', $locales[$match[2]]);
} else {
   define('LOCALE', 'de_DE');
}

The list of locales is just an example. You have to add the ones you need to make it work.

Please note that this code works with arbitrary sub directories and with or without pretty urls.

EDIT: Changed the regular expression to always return the first segment of the url no matter how long it is. Also it works now with or without a trailing "/"
Mainio replied on at Permalink Reply
Mainio
This is actually quite good workaround because this cannot be done in packages themselves. However, this does not completely solve the t() function issue e.g. when browsing dashboard pages.

One thing that this does not solve is the tool-urls. For example when browsing the dashboard, there are parts quite often loaded from tool urls. In these urls you don't have the /en/ language defined. This might also happen in some rare cases on pages that have blocks using the tool urls (e.g. for AJAX purposes).

Now coming back to this issue when you posted a message and I got a reminder, I think I now know why the localization.php is loaded BEFORE the package on_start events. It's clearly because there might be packages that use the ACTIVE_LOCALE or LOCALE definitions.

However, it has not been documented anywhere that these definitions should be defined already when the on_start event is launched. There's a lot of other variables also that are not defined at the point when on_start is fired for packages. Therefore, I'd see no reason why it couldn't be done what I suggested in my previous message.

Also, I strongly feel that there should be a way for developers to set the variable. I think a proper way to do this would be to take the language definition completely out of the defined variables. For example this could be done:
Localization::setLocale('en_US');
// At some other place:
$locale = Localization::getLocale();


This way the Localization class would store the locale in a static variable and it would be overridable by developers in a quite easy way.

Concrete5 uses the Zend_Translate object for doing the actual translations and in that class for example this is possible:
// For more information:http://framework.zend.com/manual/en/zend.translate.using.html...
$translate = new Zend_Translate(array(
   'adapter' => 'gettext',
   'content' => '...',
   'locale'  => 'de'
));
$translate->addTranslation(array(
   'content' => '...',
   'locale'  => 'fr'
));
// This would translate to German
echo $translate->_("My German Text");
// This would translate to French
$translate->setLocale('fr');
echo $translate->_("My French Text");


Actually it is also possible to call the addTranslation() function from C5 like this:
Localization::addSiteInterfaceLanguage('de_DE');


This requires you to have de_DE.mo file in /languages/site/ directory. I think this might also be another workaround, just store your translations in the /site/ dir and call that function with the correct locale. I'm not sure if this works (have not tested it) but at least I've used it for custom site-specific translations.

However, it's still a workaround and the core team should really check into this issue...

I also checked the 5.5 core and this issue has still not been resolved there.

Antti
Mainio replied on at Permalink Reply
Mainio
Another thing what I'd like to point out here is that this could also be done by manually calling the Zend_Translate object's function what we first thought out when making the Language Manager. However, if concrete5 has already set the ACTIVE_LOCALE to en_US, it does not even create that object (check out Localization::__construct() method).

Antti
Mainio replied on at Permalink Reply
Mainio
And one more thing, checking the /en/ from the address bar does not also work with some add-ons like eCommerce that has e.g. URLs with /checkout/ start.

Also, for example for the /profile/ pages it does not work.

Antti
PatrickHeck replied on at Permalink Reply
PatrickHeck
Hi Antti,

I think you're right - It would be the best to store the locale as static variable instead of a constant. That way it would be a lot more flexible. Also for example the edit bar and the site content could have different languages that way.

Does anyone from the core team have an opinion about this?

However, aren't the problems you mention about dashboard,checkout and profile pages something that has to be adressed in a complete different way? Even if the internationalization addon could set the LOCALE early itself - it would still just trace back to the topmost parent that is defined as a language tree, right? So there is no buildin way yet to deal with one first-level page that contains mixed language children.

Patrick
Mainio replied on at Permalink Reply
Mainio
>> aren't the problems you mention about dashboard,checkout and profile pages something that has to be adressed in a complete different way

A simple cookie and/or session variable might solve the problem for the system to remember user's preferred language. If that does not find the correct language, then it would fall back to the default one or expect some custom functionality (e.g. browser detection from Language Manager).

In the free internationalization add-on there is already a cookie variable that can be set in user-specific way from:
/packages/multilingual/blocks/switch_language/tools/switch.php

That code sets the DEFAULT_LANGUAGE cookie for the user depending what the user chose from the switch language block.


Antti
Mainio replied on at Permalink Best Answer Reply
Mainio
And by the way, if you're in contact with the core team about this issue, I've already told them that I'd be more than happy to help out in the internationalization related problems.

One thing I talked about with them is about the language storage (currently the MyGengo is a temporary solution what I got from the conversation) and said to them that I'd be willing to help them out if they are developing some new kind of language storing solution because this is closely related to our Language Manager add-on. Currently we haven't integrated to MyGengo because 1) they don't offer a public API and 2) core team said that it's only a temporary solution.

So, just want to point out that I'm usable when it comes to the internationalization part. I'd really like to get things working properly also there.

Antti
PatrickHeck replied on at Permalink Reply
PatrickHeck
Hmmm... I didn't know that there already is a session variable in the addon.
Since you have a brilliant insight into the subject, maybe you should just open a pull request with your proposed changes to the core.

Patrick
Mainio replied on at Permalink Reply
Mainio
Maybe I'll change few words with Andrew before doing that to see his thoughts. However, I really think few lines should be re-written in the Localization class, it would solve like 95% of these problems.

Antti
Mainio replied on at Permalink Reply
Mainio
Just to let you know, I sent email to Andrew who passed it to Ryan and he suggested me to make the change. It's now in there as a pull request:
https://github.com/concrete5/concrete5/pull/354...

Ryan promised to check it out in the next few days.

So, after this change it's up to the Internationalization add-on to handle the case correctly. Or any other add-on developers. At least there's now possibility to also handle the t() problem correctly.

I'll see also if I'll make a pull request to the Internationalization add-on as well if they approve my core pull request first (it would require that).

Antti
MattWaters replied on at Permalink Reply
MattWaters
Thanks Mainio!

Andrew's pretty busy working on our 5.5 release at the moment but I would imagine he'll be able to take a look at your submission sometime in the next couple of weeks.
wasabili replied on at Permalink Reply
wasabili
Hi Patrick

Sounds like a great idea. But it doesn't work for me. Don't know why. Could it be when you send the form, that the URL is changing into a string without any language marks in it?

Thanks
PatrickHeck replied on at Permalink Reply
PatrickHeck
Hi Wasabili,

I don't quite understand what the problem is. Can you explain a little further what exactly happens?

Best
Patrick
wasabili replied on at Permalink Reply
wasabili
Hi Patrick

Well when I use your script in the site.php it seams that the page only taks the "de_DE" no mater what language the page is on. But I saw that when I send the form, that the URL is changing to index.php?somestrings … and the /en/ language is gone.
You know what I mean?

cya
PatrickHeck replied on at Permalink Reply
PatrickHeck
Wasabili,
yes - you're right. It doesn't work if the code is not part of the url. So my suggestion is not really the final solution... merely something you might get along with until a better solution is available.
Best
Patrick

concrete5 Environment Information

Browser User-Agent String

Hide Post Content

This will replace the post content with the message: "Content has been removed by an Administrator"

Hide Content

Request Refund

You have not specified a license for this support ticket. You must have a valid license assigned to a support ticket to request a refund.