Fixing problems with month/daynames in internationalized dates

Permalink 1 user found helpful
Printing out a localized date with month/week-names is quite a challenge at the moment in C5. If you call the builtin methods that use the date() function you can't get month or week names for languages other than english. strftime() will give you these, but only if the server has the proper locales installed.
So my suggestion would be to add a function DateHelper::strftime() that behaves just like the native version but reads it's strings from the global message.mo files.
The implementation would be like this:
public function strftime($format,$timestamp=false) {
      /* tokens that get a localized name */
      $format = str_replace('%a', $this->intToDayname(date('w', $timestamp)),$format);
      $format = str_replace('%A', $this->intToDayname(date('w', $timestamp), true),$format);
      $format = str_replace('%b', $this->intToMonthname(date('n', $timestamp)),$format); 
      $format = str_replace('%B', $this->intToMonthname(date('n', $timestamp), true),$format);
      $format = str_replace('%P', $this->amPmToName(date('a', $timestamp)),$format);
      $format = str_replace('%p', strtoupper($this->amPmToName(date('a', $timestamp))),$format);
      /* tokens that resolve to a pattern */
      if (strpos($format,"%r") !== false) {
         $format = str_replace('%r', $this->strftime(t('%I:%M:%S %p'),$timestamp),$format);
      }
      if (strpos($format,"%x") !== false) {
         $format = str_replace('%x', $this->strftime(t('%m/%d/%Y'),$timestamp),$format);
      }

In addition to that you also need the functions intToDayname(), intToMonthname() and amPmToName(). See attached date.php for these.

What do you think about this solution?

1 Attachment

PatrickHeck
 
PatrickHeck replied on at Permalink Reply 1 Attachment
PatrickHeck
Updated customized date.php to a new version. (see attachment)
notzen replied on at Permalink Reply
notzen
It will be fantastic if was possible to have a little are in the dashboard just to select the country and the relative date / time format.

you made an excellent work and i see it is well commented (comments are important I think...)
PatrickHeck replied on at Permalink Reply
PatrickHeck
Thanks for your feedback, nozen.

The idea is to add the preferred date format in the messages.po file. e.g. for German it would be:
msgid "%m/%d/%Y"
msgstr "%d. %B %Y"

That means you don't have to select the date format in additon to your locale. Whenever you want to output a date you just write
$dh = Loader::helper('date');
echo $dh->strftime("%x");

( The constants in localization.php work the same way. With the limitation that they can't contain translated month/day names. )

Personally I'm not a big fan of cluttering the dashboard with too much stuff. It quickly get's too confusing.
PatrickHeck replied on at Permalink Reply
PatrickHeck
Please note that there is also another, much more elegant solution: This is to use the Date functions that come with Zend. These already have month- and daynames for all kinds of languages included and also support date() formatted masks. This makes it a lot easier to implement with C5. See this pull reuquest for the implementation:
https://github.com/concrete5/concrete5/pull/308...
Mainio replied on at Permalink Reply
Mainio
Hi Patrick!

This is just what I was getting to next. I think also that your solution including the date/time format to messages.mo file would be really cool! Usually people speaking the same language want date displayed the same way. Of course there are few exceptions (different countries speaking the same language). However, it would solve most of these issues.

One suggestion to your pull request. Regarding to the other pull request related to the t() function issue, there is now a function available in the Localization class that would give the active locale. The pull request got authorized yesterday, so the code is now there available in github.

So, I'd suggest changing these:
defined('ACTIVE_LOCALE') && ACTIVE_LOCALE != 'en_US'

to this:
Localization::activeLocale()



This would also give the active locale if it's changed during the request (to something different than ACTIVE_LOCALE) which would also make the date/time formatting issue more flexible.

Actually I also think that setting the DATE_APP_xx constants in
/concrete/config/localization.php

file is still not an optimal solution. This is because of the issue we discussed also earlier that the config/localization.php gets loaded BEFORE add-on on_start() method calls because of which those definitions would be wrong now if some of the add-ons change the locale.


What do you think?


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

I'm glad that your changes are already merged into the core. Made the changes according to your suggestions:
https://github.com/patrickheck/concrete5/commit/52171f4dbaf3efbb694d...

Maybe the locale dependent date formats (DATE_APP_...) should also become a static property of the Localization class?
Mainio replied on at Permalink Reply
Mainio
Hi,

Great.

However, I think these should not come from the Localization class, I also came out with the same idea. I really think the Localization class should only be kept for the translation things. Otherwise it would become quite more complicated for that issue.

Also, if you think about these concepts together, I really think that localizing strings is something different from localizing values (e.g. dates).

I really feel that what you have discussed earlier about this topic (putting the localized strings into t(), checking out Zend's Date functions, etc.) is really leading towards where this discussion should lead to.

Currently there is the problem I'm pointing at that when /concrete/config/localization.php is loaded, the locale might differ from the one that is really used later during the loading process. Because of this, the DATE_APP.. constants might not be correctly defined.


Antti
PatrickHeck replied on at Permalink Reply
PatrickHeck
Hey Antti,

from what I understand, that means, that the last line of the dateTimeFormatLocal() function (https://github.com/patrickheck/concrete5/blob/52171f4dbaf3efbb694d23... ) should be changed from:
return $date->toString($mask);

to
return $date->toString(t($mask));


Then the rest can be dealt with like described above. Is that what you had in mind as well?

Patrick
PatrickHeck replied on at Permalink Reply
PatrickHeck
The other option would be to add the t() function in the template when the helper is called.
instead of
$dh = Loader:helper("date");
echo $dh->date('%m/%d/%Y');

it would become
$dh = Loader:helper("date");
echo $dh->date(t('%m/%d/%Y'));


I think this is more consistent with the way translation works generally - but it requires more changes throughout the code.
Mainio replied on at Permalink Reply
Mainio
I also feel that this should be something that is left for the developer who is outputting the content. The main problem here is that throughout concrete5, different blocks/pages/dashboard items/etc. might be using the defined constants like DATE_APP.. that might be set up incorrectly (as discussed before).

I haven't checked out how they are generally used in the core and how wide they are spread out but I would think that changing every piece of code that uses those constants might be a bit tough job to do. However, I feel it would be the correct solution.

The correct way in my opinion would be in the /config/localization.php NOT TO include the strings inside t() function. Then it would be up to the code to tell e.g.
echo date(time(), t(DATE_APP_GENERIC_MDY_FULL));


This way it would not be tied up to those pre-defined constants what gets printed out each time a date/time is printed out.

Currently the core expects people to add their strings inside the t() function so why would the date case be any different?


The only problem here is that those strings would not be included in the translation file this way if you read the strings e.g. with Poedit. However, there could be a custom plain PHP file that would include those strings so that Poedit could also catch them up.
PatrickHeck replied on at Permalink Reply
PatrickHeck
Great - I think your suggestion would be the best implementation.
I also wouldn't worry about Poedit's lack to auto-detect too much. Concrete5.5 will have many dynamic strings inside t() functions - so a pot file has to be shipped with it anyway. ( see https://github.com/concrete5/concrete5/issues/227... )

Would you like to suggest this change to the core team?
Mainio replied on at Permalink Reply
Mainio
Maybe some centralized file where those dynamic strings would also be included in the generated translation files?
JohntheFish replied on at Permalink Reply
JohntheFish
The issue I have with Date/time is US vs UK date formats (UK is the same as most European). Because the site is still English, there is no internationalisation. I can set a bunch of date definitions in config/site.php, but I continue to run across occasional surprises where those definitions are not applied.
Mainio replied on at Permalink Reply
Mainio
Yeah, that's actually what I was pointing at when I said "with few exceptions".

But, I think that could also be solved with the messages.mo solution, just set up a language for en_GB and in that file, the only translations to add would be the date/time formats.

Sounds like the best solution so far to me... Actually this solution should already work in most of the cases with the current version of c5. However, as you said, there might still be occasional problems when the proper date/time formats are not used e.g. in some add-ons.

Antti
pepefernandez replied on at Permalink Reply
pepefernandez
Hi everybody!

Looking for a solution to show not only the spanish date format, but also tranbslations,
and after reading all of your replies, I've finally discovered that PHP is not for me!. (You, guys, speak chinese to me!! :-).

Any way, wht do you think about my solution to show spanish dates format and also days and months names. I know it's not an elegant solution and it does not affect globally to any site, as youmust hard code each template on wich you'd like to show any date.

This is my solution: (Just used it in the blog posts template);

<?php 
$meses = array('','Enero','Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre');
$dias = array('', 'Lunes','Martes','Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo');
$mes=$meses[date($c->getCollectionDatePublic('n'))];
$dia=$dias[date($c->getCollectionDatePublic('N'))];
$dianumero=$c->getCollectionDatePublic('j');
?>
<p><?php  echo t('Publicado por:');?>
<span class="post-author">
<?php  echo $c->getVersionObject()->getVersionAuthorUserName(); ?> a las <a href="<?php  $c->getLinkToCollection;?>"><?php  echo $c->getCollectionDatePublic('g:i a')?> del <?php  echo $dia?> <?php  echo $dianumero?> de <?php  echo $mes?> de <?php  echo $c->getCollectionDatePublic('Y')?></a></span></p>


Notice that I'm not a programmer and know nothing about PHP!!!. Just let me know if this is the correct aproach to the problem.

Thanks and cheers!
PatrickHeck replied on at Permalink Reply
PatrickHeck
Like mentioned above - I would just implement the solution from the pull request:
https://github.com/concrete5/concrete5/pull/308...
This should automatically support most languages.

However, your code looks like it should work as well. Are you experiencing problems?
pepefernandez replied on at Permalink Reply
pepefernandez
Hi there, Patrick;

Thanks for your answer. Any way, as I commented, I'm not an PHP "coinossceur", so, I've tried to test the code from your GitHub link, and it did not work It crashed my system, so I had to restore the original file.

By the moment my solution works without problems,

It's uncomfortable, as I have to hard code each of the templates I want the teh dates to be shown; meanwhile I'll study -read. some PHP books to understand better the core code of C5.

Thanks again!!
PatrickHeck replied on at Permalink Best Answer Reply 1 Attachment
PatrickHeck
Since the pull request has been stuck for a long time in github I am posting the code here so people can use it in the meantime. Just copy the contents to your top-level directory.
Afterwards you can do the following in your template code:
$dh = Loader::helper('date');
echo $dh->date("j F Y");

All translatable names will automatically be printed in the language currently defined through ACTIVE_LOCALE.
$dh->date() uses the exact same syntax as php's native date()
http://php.net/manual/de/function.date.php...
pepefernandez replied on at Permalink Reply
pepefernandez
Thanks Patrick!!!. I'll try it right now!!!.
Studio8 replied on at Permalink Reply
Hi Patrick,

can you tell me where exactly too put these two lines of code?


$dh = Loader::helper('date');
echo $dh->date("j F Y");

Thanks!
mhzpower replied on at Permalink Reply
Hi, i'm running concrete5 5.6.1.2 with ProEvents and i was wondering why this solution doesn't work for me. Is there any updates for this version?

Thanks