Вывод даты в виде "27 февраль 2010" это уродливо. Ни в русском, ни в украинском, ни в польском - и, я думаю, что это касается большинства других славянских языков - так не говорят и не пишут.
Понятно, что через какое-то время привыкаешь и просто не замечаешь, но есть ведь настойчивые заказчики, которые тыкают носом разработчика в эти "ляпы" и требующие исправления ошибок (по их мнению). Спасибо им за это. Это нужно, хотя и раздражает.
Вывод блока с кастомным обработчиком дат.
Итак, по требованию заказчика вывести в блоке нормальные даты я написал модуль. Модуль выводит блок, где определённым образом форматируется вывод анонсов и собственно исправляются даты. Точно такой же блок можно сделать во Views, если бы не кривые даты...
Короче говоря код модуля я приводить не буду, а только функцию, которая исправляет даты. Этот код может очень пригодится тем, кто не сможет дочитать пост до конца.
Ок. Код всего модуля приложен к статье, чтобы уменьшить соотношение код/текст, чтобы не объяснять как делается вызов фунции и в каком формате должна быть дата - изучайте сами, если это вам нужно. Даты начала события и конца хранятся в одном ССК-поле, которое называется field_event_date и имеет тип Date.
Но этот модуль можно не использовать, а применить патч для модуля Date, но об этом ниже.
function _calendar_list_conv_date($a, $format='front') {
global $language;
$date = strtotime($a);
switch($format) {
case 'front':
$monthes = array (
'en' => array('January','February','March','April','May','June','July','August','September','October','November','December'),
'ru' => array('января','февраля','марта','апреля','мая','июня','июля','августа','сентября','октября','ноября','декабря'),
'uk' => array('січня','лютого','березня','квітня','травня','червня','липня','серпня','вересня','жовтня','листопада','грудня'),
);
$weekday = array (
'en' => array('Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday'),
'ru' => array('Понедельник','Вторник','Среда','Четверг','Пятница','Суббота','Воскресенье'),
'uk' => array('Понеділок','Вівторок','Середа','Четвер','П’ятниця','Субота','Неділя'),
);
switch ($language->language) {
case 'ru':
case 'uk':
$output = date("j",$date) .' '. $monthes[$language->language][date("n",$date)-1] .', '. $weekday[$language->language][date("N",$date)-1];
break;
case 'en':
default:
$output = $monthes[$language->language][date("n",$date)-1] .' '. date("j",$date) .', '. $weekday[$language->language][date("N",$date)-1];
break;
}
break;
case 'short':
$output = date('d.m.Y', $date); //20.02.2010
break;
default:
$output = date("j",$date).' '.$monthes[date("n",$date)-1].' '.date("Y",$date).' '.date("H",$date).':'.date("i",$date);
break;
}
return $output;
}
После созерцания блока с "хорошими" датами заказчик успокоился и потребовал сделать "правильно" в остальных местах сайта...
Я понял, что нужно кардинально другое решение - универсальное и системное. Очень много времени ушло на то, чтобы понять какой именно модуль и как меняет даты - возможно сказалось то, что температура была большая.
Сам код решения был написан и оттестирован где-то за полдня.
Патч модуля Date, чтобы получить корректную обработку дат для всего сайта
Изменяются только 3 функции в файле date_api.module модуля Date:
- date_t
- date_t_strings
- date_format_date
Ниже код уже изменённых функций - его можно использовать, если вы не можете применить патч, который прилагается к статье.
Этот патч добавляет названия месяцев в родительном падеже.
* A function to translate ambiguous short date strings.
*
* Example: Pass in 'Monday', 'day_abbr' and get the translated
* abbreviation for Monday.
*
* param string $string
* param string $context
* param int $langcode
* return translated value of the string
*/
function date_t($string, $context, $langcode = NULL) {
//static $replace = array();
if (empty($replace[$langcode])) {
// The function to create the date string arrays is kept separate
// so those arrays can be directly accessed by other functions.
date_t_strings($replace, $langcode);
}
switch ($context) {
case 'day_name':
case 'day_abbr':
case 'day_abbr1':
case 'day_abbr2':
$untranslated = array_flip(date_week_days_untranslated());
break;
case 'month_name':
case 'month_genitive': //Добавляем в обработку новый контекст
case 'month_abbr':
$untranslated = array_flip(date_month_names_untranslated());
break;
case 'ampm':
$untranslated = array_flip(array('am', 'pm', 'AM', 'PM'));
break;
case 'datetime':
$untranslated = array_flip(array('Year', 'Month', 'Day', 'Week', 'Hour', 'Minute', 'Second', 'All Day', 'All day'));
break;
case 'datetime_plural':
$untranslated = array_flip(array('Years', 'Months', 'Days', 'Weeks', 'Hours', 'Minutes', 'Seconds'));
break;
case 'date_order':
$untranslated = array_flip(array('Every', 'First', 'Second', 'Third', 'Fourth', 'Fifth'));
break;
case 'date_order_reverse':
$untranslated = array_flip(array('', 'Last', 'Next to last', 'Third from last', 'Fourth from last', 'Fifth from last'));
break;
case 'date_nav':
$untranslated = array_flip(array('Prev', 'Next', 'Today'));
break;
}
$pos = $untranslated[$string];
return $replace[$langcode][$context][$pos];
}
/**
* Construct translation arrays from pipe-delimited strings.
*
* Combining these strings into a single t() gives them the context needed
* for better translation.
*/
function date_t_strings(&$replace, $langcode) {
$replace[$langcode]['day_name'] = explode('|', trim(t('!day-name Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday', array('!day-name' => ''), $langcode)));
$replace[$langcode]['day_abbr'] = explode('|', trim(t('!day-abbreviation Sun|Mon|Tue|Wed|Thu|Fri|Sat', array('!day-abbreviation' => ''), $langcode)));
$replace[$langcode]['day_abbr1'] = explode('|', trim(t('!day-abbreviation S|M|T|W|T|F|S', array('!day-abbreviation' => ''), $langcode)));
$replace[$langcode]['day_abbr2'] = explode('|', trim(t('!day-abbreviation SU|MO|TU|WE|TH|FR|SA', array('!day-abbreviation' => ''), $langcode)));
$replace[$langcode]['ampm'] = explode('|', trim(t('!ampm-abbreviation am|pm|AM|PM', array('!ampm-abbreviation' => ''), $langcode)));
$replace[$langcode]['datetime'] = explode('|', trim(t('!datetime Year|Month|Day|Week|Hour|Minute|Second|All Day|All day', array('!datetime' => ''), $langcode)));
$replace[$langcode]['datetime_plural'] = explode('|', trim(t('!datetime_plural Years|Months|Days|Weeks|Hours|Minutes|Seconds', array('!datetime_plural' => ''), $langcode)));
$replace[$langcode]['date_order'] = explode('|', trim(t('!date_order Every|First|Second|Third|Fourth|Fifth', array('!date_order' => ''), $langcode)));
$replace[$langcode]['date_order_reverse'] = explode('|', trim(t('!date_order |Last|Next to last|Third from last|Fourth from last|Fifth from last', array('!date_order' => ''), $langcode)));
$replace[$langcode]['date_nav'] = explode('|', trim(t('!date_nav Prev|Next|Today', array('!date_nav' => ''), $langcode)));
// These start with a pipe so the January value will be in position 1 instead of position 0.
$replace[$langcode]['month_name'] = explode('|', trim(t('!month-name |January|February|March|April|May|June|July|August|September|October|November|December', array('!month-name' => ''), $langcode)));
//Строка с названиями месяцев, которую ещё нужно будет перевести на нужные языки:
$replace[$langcode]['month_genitive'] = explode('|', trim(t('!month-genitive |January|February|March|April|May|June|July|August|September|October|November|December', array('!month-name' => ''), $langcode)));
$replace[$langcode]['month_abbr'] = explode('|', trim(t('!month-abbreviation |Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec', array('!month-abbreviation' => ''), $langcode)));
}
/**
* Reworked from Drupal's format_date function to handle pre-1970 and
* post-2038 dates and accept a date object instead of a timestamp as input.
*
* Translates formatted date results, unlike PHP function date_format().
*
* param $oject
* A date object, could be created by date_make_date().
* param $type
* The format to use. Can be "small", "medium" or "large" for the preconfigured
* date formats. If "custom" is specified, then $format is required as well.
* param $format
* A PHP date format string as required by date(). A backslash should be used
* before a character to avoid interpreting the character as part of a date
* format.
* return
* A translated date string in the requested format.
*/
function date_format_date($date, $type = 'medium', $format = '', $langcode = NULL) {
if (empty($date)) {
return '';
}
switch ($type) {
case 'small':
case 'short':
$format = variable_get('date_format_short', 'm/d/Y - H:i');
break;
case 'large':
case 'long':
$format = variable_get('date_format_long', 'l, F j, Y - H:i');
break;
case 'custom':
$format = $format;
break;
case 'medium':
default:
$format = variable_get('date_format_medium', 'D, m/d/Y - H:i');
}
$max = strlen($format);
$datestring = '';
for ($i = 0; $i < $max; $i++) {
$c = $format[$i];
switch ($c) {
// Use date_t() for ambiguous short strings that need translation.
// We send long day and month names to date_t(), along with context.
case 'l':
$datestring .= date_t(date_format($date, 'l'), 'day_name', $langcode);
break;
case 'D':
$datestring .= date_t(date_format($date, 'l'), 'day_abbr', $langcode);
break;
case 'F':
//Собственно логика вывода названия месяца в зависимости от ситуации:
if ((strpos($format, 'd') === FALSE) && (strpos($format, 'j') === FALSE)) {
$datestring .= date_t(date_format($date, 'F'), 'month_name', $langcode);
}
else {
$datestring .= date_t(date_format($date, 'F'), 'month_genitive', $langcode);
}
break;
case 'M':
$datestring .= date_t(date_format($date, 'F'), 'month_abbr', $langcode);
break;
case 'A':
case 'a':
$datestring .= date_t(date_format($date, $c), 'ampm', $langcode);
break;
// The timezone name translations can use t().
case 'e':
case 'T':
$datestring .= t(date_format($date, $c));
break;
// Remaining date parts need no translation.
case 'O':
$datestring .= sprintf('%s%02d%02d', (date_offset_get($date) < 0 ? '-' : '+'), abs(date_offset_get($date) / 3600), abs(date_offset_get($date) % 3600) / 60);
break;
case 'P':
$datestring .= sprintf('%s%02d:%02d', (date_offset_get($date) < 0 ? '-' : '+'), abs(date_offset_get($date) / 3600), abs(date_offset_get($date) % 3600) / 60);
break;
case 'Z':
$datestring .= date_offset_get($date);
break;
case '\\':
$datestring .= $format[++$i];
break;
default:
if (strpos('BdcgGhHiIjLmnsStTUwWYyz', $c) !== FALSE) {
$datestring .= date_format($date, $c);
}
else if ($c == 'r') {
$datestring .= format_date($date, 'custom', 'D, d M Y H:i:s O', $langcode);
}
else {
$datestring .= $c;
}
}
}
return $datestring;
}
Я добавил комменты в код. Идея в том, что название месяца в родительном падеже должно выводиться только, если выводится рядом с числом месяца. Во всех остальных случаях - именительный падеж.
Пример:
23 февраля 2010, а не 23 февраль 2010.
Но март 2010, а не марта 2010
Как заставить все это работать?
- Применить патч, который приложен к статье или скопировать уже изменённые функции вместо старых. Рецепт о том, как применить патч (есть варианты для Линукс, и для Винды): Применение заплат (patch)
- Открыть страницу "Перевод интерфейса" (admin/build/translate/search) и найти строку:
!month-genitive |January|February|March|April|May|June|July|August|September|October|November|December
- Сделать её перевод на нужный язык. Учтите, что для английского названия месяцев обычно пишутся с заглавной, а у нас - нет. Названия
месяцев должны быть в родильном падеже. Для русского и украинского языков это выглядит так:- Русский:
!month-genitive |января|февраля|марта|апреля|мая|июня|июля|августа|сентября|октября|ноября|декабря
- Украинский:
!month-genitive |січня|лютого|березня|квітня|травня|червня|липня|серпня|вересня|жовтня|листопада|грудня
- Русский:
- Сохранить переводы и проверить отображение дат.
Будущее патча
Все эти манипуляции (применение патча и перевод вручную строк) нужно делать только пока патч не будет принят разработчиками модуля Date и не выпущен релиз с этими изменениями.
Пока я не вижу причин, которые могут припятствовать, но всякое бывает и, даже, если патч будет принят, то дата выхода релиза может отодвигаться по другим причинам.
Англоязычным товарищам этот патч не нужен, потому что в английском языке этой проблемы с датами нет и они могут счесть его не важным, поэтому я попробую опубликовать эту же статью на хабре и прошу поддержать также issue с патчем на drupal.org:
- http://drupal.org/node/728350 - Multilanguage month support
- http://vladsavitsky.habrahabr.ru/blog/85861/ - статья на хабре.
Вложение | Размер |
---|---|
calendar_list.zip | 2.25 КБ |
date_api.module.patch | 1.62 КБ |
Комментарии
огромное спасибо!
Но я подожду, пока включат в сам модуль
Мне интересно - один я считаю, что модуль date с самого начала должен быть сделан по-иному - чтобы функцию вывода времени на каком-то языке писал разработчик, разговаривающий на этом языке.
А не извращаться с переводами - временные конструкции на разных языках совсем разные.
//вот майкрософты в своем .net так и сделали - не только выводит правильно, но даже спокойно парсит строку с датой на любом языке. И с форматами - что первым в строке - дата или месяц - проблем нет.
Спасибо за патч, это великолепная новость!
На хабре удалось таки выложить статью, но только в персональный блог - кармы оказалось маловато...
Влад, оставь ссылку, плюсанём, сможешь в друпал поместить
Спасибо, в закладки и ждем включения патча в релиз.
На хабре топик удалось переправить в блог "Друпал" - спасибо всем, кто добавил карму.
Пришлось там урезать код и тот, что остался - привести в порядок (добавить специально подсветку синтаксиса).
Всё как то руки не доходили, а тут готовое решение, спасибо!
Хорошая новость, отдал плюс. На хабре разметка смотрится хуже
лучше бы вы не на хабре плюсовали, а подписались бы на issue на drupal.org, так это будет заметнее для создателя модуля
Поддерживаю. Отписался там. Есть практика - комментарии для модулей/патчей имеют большое значение для жизни кода.
У меня была мысль использовать month-abbreviation |Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec
для месяцев в родительном падеже. Все равно аббревиатуры пропадают, у меня, по крайней мере. Но там все дело портил "May" (одинаково для аббревиатуры и полного названия месяца). Да и решение, конечно, не универсальное.
"Идея в том, что название месяца в родительном падеже должно выводиться только, если выводится рядом с числом месяца. Во всех остальных случаях - именительный падеж." Идея замечательная, конечно, и значительно облегчает жизнь. Но, вот например, нельзя в этом случае вывести заголовок "События сентября".
Влад, небольшая опечатка: !month-genitive |січеня ( січня, как указано в _calendar_list_conv_date )
Спасибо за поддержку. Надеюсь, что патч войдёт в релиз и всем будет счастье.
Да уж. Правильно "січень" - сейчас исправлю везде. Спасибо.
Извините а можно "левый" вопрос?
Что за тема используется в качестве примера - очень понравилась:-)
Заранее спасибо!
ЗЫ На хабре нету эккаунта так хоть тут нашел:-)
Влад, у вас хоть на одном сайте есть места, где использовать названия месяцев нужно в именительном падеже? Я в свое время, когда решал эту проблему, просто заменил переводы с "март" на "марта" и т.д. и с тех пор ни разу не сталкивался с ситуацией, где этого бы не хватало.
Я же писал - это сайт заказчика. Я понимаю, что когда делаешь для себя, то и париться с этим не будешь - ставишь вывод цифрами и проблемы нет.
Но на их сайте выводятся названия месяцев как в именительном, так и в родительном падеже.
спасибо, хороший патч!!
так и сделал, спасибо.
Может не в тему
Влад, а к $submitted этот патч отношение имеет или только к сск полям?
Хочу добится того чтобы время в submitted отображалось как на http://drupaldance.com только без дня
Как добиться такого?
к вопросу выше: нужно в шаблоне материалы испольовать date_format_date() я правильно понимаю? справшиваю потому что при использовании
ничего не происходит, а при
говорит: string(2) " "
пробовал без $language, результат тот же
помогите плиз
Я в шаблоне вывожу:
Месяцы переводил через стандартный перевод строк.
Возможно торможу, но после патчинга в переводе не появилось строки !month-genitive*
Подскажите плиз что сделал не так.
таже самая проблема...
Я столкнулся с той же проблемой. Решилось всё выполнением команды "переустановить модули" из devel для всех модулей date
Получить корректную обработку дат для всего сайта только этим патчем не удастся. Есть еще функция format_date в common.inc, которую использует множество модулей. В том числе Views.
Мало того, исправлением format_date тоже не ограничиться, так как тот же Views обрабатывает тайтлы для каждого аргумента отдельно (пример - View для новостей с аргументами <год>/<месяц>/<день>). Печально.
спасибо
Обновил функции в модуле, но в поиске для перевода не находит данную строку! В чем может быть проблема? Кэш чистил.
нужно ещё calendar_list.zip установить в модули - тогда в поиске перевода появится !month-genitive
после проделанных процедур формат даты изменился, а вот вывод статей через views по-прежнему "18 Октябрь"
угу аналогично, добавил вручную эту строку через po файл но ничего не работает
даже указал строку 689 где это встречается в модуле date_api.module:689
месяца берутся из !long-month-name
спасибо, по описанию легко воспроизвел на Drupal 7 и одном из последних Date
Для модуля : date
Version : 6.x-2.9
Есть патч?
Вручную изменил функции.
Спасибо, то что нужно было.
Друпал 7. Теперь все названия месяцев только в родительном падеже. А на вкладке "Год" календаря, там где выводится все месяцы года, нужно именно в именительном падеже.