英语日期到俄语 PHP

English dates to Russian with PHP

我正在构建一个将以英语和俄语显示的多语言网站。翻译方面的一切都很好,除了日期。

据我了解,PHP 的 strtotime() 函数严格用于英文日期,正如官方文档“Parse about any English textual datetime description into a Unix timestamp”中所述。

我已经尝试使用 setlocale( LC_TIME, 'ru_RU', 'russian' ); 然后 strftime() 但它只是不断返回 unix January 1970 日期。

我想做的是从日期中获取日期名称(即星期三)并显示它。通常,如果是英语约会,我会像 $this->day = date('l', strtotime( $date )); 那样做,但因为我想同时用英语和俄语约会,所以我会这样做 $this->day = strftime("%A", strtotime($date) );

我的日期格式为“2015 年 10 月 13 日”,我认为这是问题的一部分,因为 "October" 的俄语翻译是“ Октябрь”所以我最终尝试解析的最终结果是“Октябрь 13, 2015”,它总是 returns "Thursday" 而实际上 10 月 13 日是星期二。

这是我为解析日期所做的一切:

$this->month_arr = array();

// Loop through $event_dates array created above
if(isset($_GET['lang']) && $_GET['lang'] == 'ru') {
    setlocale(LC_ALL, 'ru_RU.utf8', 'rus_RUS.1251', 'rus', 'russian');
}

foreach($this->event_dates as $date => $time) {

    // Create the unix timestamp to parse with PHP
    $unix_date = strtotime($date);
    $this->month = strftime("%B", $unix_date);
    $this->day = strftime("%A", $unix_date);
    $this->year = strftime("%G", $unix_date);

    // Create new array with date info
    $this->month_arr[$this->month][] = $this->day; // Day of week
    $this->month_arr[$this->month][] = $date; // Full textual date
    $this->month_arr[$this->month][] = $this->year; // 4 Digit Year
    $this->month_arr[$this->month][] = $time; // Start/end time array
}

这完美地解析了日期的英文表示,但其他一切似乎都搞砸了。作为临时回退,我从最终显示中删除了星期几,只是从初始日期字符串中删除 "October" 或 "Октябрь" 并显示它,但这不是客户所期望的。

我也尝试过这种格式:strftime('%B %e, %Y', $unix_date); 希望将 strftime() 格式与最初显示日期的方式相匹配会起作用,但最终结果相同。

P.S。我 运行 这一切都在我的本地主机 MAMP 服务器上。 production/staging 服务器在 nginx 上 运行。

编辑: 这是一个 WordPress 网站,日期是通过“Advanced Custom Fields Pro”插件创建的。该插件不允许用户将日期保存为时间戳。我已经尝试使用 WP 的内置 date_i18n($format, $unix_timestamp); 函数,但没有成功。

编辑 x2: 我目前正在做这样的事情:

// Russian Months
$ru_months = array( 'Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь' );

// English Months
$en_months = array( 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' );

// Get date string from ACF
$date_arr = explode(" ", get_sub_field('date'));

// Get the month name
$month = $date_arr[0];

// Get the array index of the month name
$month_index = array_search($month, $en_months);    

// Piece together new date string in Russian translation
$date = $ru_months[$month_index] . " " . $day . ", " . $year;   

目前可行,但我也想翻译日期名称。例如,2015 年 10 月 13 日,星期二,此方法并未提供可靠的方法。

你的理解是正确的:strtotime()只会将english字符串解析成整数。您可能错过了当函数无法进行翻译时,它 returns 一个布尔值 false:

$date = "October 13, 2015";
$rudate = "октября 13, 2015";
var_dump(strtotime( $date )); //outputs int(1444687200)
var_dump(strtotime( $rudate )); //ouputs bool(false)

当您尝试使用布尔值调用 date()strftime() 时,PHP 出于某种原因决定默认为某个日期,该日期恰好是星期四。

var_dump(date('l', false)); //outputs string(8) "Thursday"
var_dump(strftime("%A", false)); //outputs string(8) "Thursday"

由于您的数据已经存储,您必须先对其进行变异,然后才能使用现有的 PHP 函数对其进行处理。由于您只需将俄语翻译成英语,因此更改相当轻松:

//You should, of course, add the other months in.
function dateToEnglish($str)
{
    $str = str_replace('января', 'January', $str);
    //...
    $str = str_replace('октября', 'October', $str);
    //...
    $str = str_replace('Декабрь', 'December', $str);

    return $str;
}

var_dump(strtotime(dateToEnglish($rudate))); //outputs int(1444687200)

您可以使用它在进一步处理之前将日期解析为英文:

foreach($this->event_dates as $date => $time) {

    // Create the unix timestamp to parse with PHP
    $unix_date = strtotime(dateToEnglish($date));
    // ...
}

您要支持的每种语言都需要手动添加,这是此策略的主要缺点。但是,由于您使用字符串而不是时间戳,因此您解决此问题的方法有些严重 code smell

将来,您应该将日期存储为时间戳(请参阅 DateTime),因为它们与语言无关,不需要这一额外的处理步骤。

我在这里看到三个选项。


  1. 编写自己的解析器。

您可以随时编写 PHP 代码来自己转换日期。 很脏,很暴力,但是很直接

function add_weekday( $date ) {
   $ru_weekdays = array( 'Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота', 'Воскресенье' );
   $en_weekdays = array( 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday' );
   $ru_months = array( 'Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь' );
   $en_months = array( 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' );
   $en_date = str_replace( $ru_months, $en_months, $date );
   $weekdays = $en_date === $date ? $en_weekdays : $ru_weekdays;
   // $wday = strptime( $en_date, '%B %e, %Y' );       // Linux/Mac
   // return $weekdays[ $wday['tm_wday']-1 ]." $date"; // Linux/Mac
   return $weekdays[ DateTime::createFromFormat( 'M j, Y', $en_date )->format( 'N' )-1 ]." $date"; // PHP 5.3+
}
echo add_weekday( 'Октябрь 13, 2015' ); // Вторник Октябрь 13, 2015
echo add_weekday( 'October 13, 2015' ); // Tuesday October 13, 2015

  1. 使用库。

例如,PECL intl module has an IntlDateFormatter:

$formatter = new IntlDateFormatter( 'ru', IntlDateFormatter::LONG, IntlDateFormatter::NONE );
$datetime = $formatter->parse( "13 октября 2015 r." );
$weekday = $datetime->format( 'l' ); // 'Tuesday'

不过国际很挑剔;日期字符串必须匹配其 exact 格式: 没有大写,没有 oктябрь,日期必须在月份之前,并且必须以 "r.".

结尾

如果你问我,不够灵活值得麻烦。


  1. Ask 一个 企业 数据库。

我爱SQL Server。看:

SELECT datename( dw, Try_Parse( N'Октябрь 13, 2015' AS date USING 'Ru-RU' ) );
-- 'Tuesday', if default language is English

SET LANGUAGE 'Russian';
SELECT datename( dw, Try_Parse( N'Октябрь 13, 2015' AS date ) );
-- 'вторник'

Oracle,也搞定了:

SELECT TO_CHAR( TO_DATE( 'Октябрь 13, 2015', 'MON DD, YYYY', 'nls_date_language = Russian' )
   , 'DAY', 'nls_date_language = Russian' ) FROM DUAL;
-- 'ВТОРНИК'

ALTER SESSION SET NLS_LANGUAGE = 'RUSSIAN';
SELECT TO_CHAR( TO_DATE( 'Октябрь 13, 2015', 'MON DD, YYYY' ), 'DAY' ) FROM DUAL;
-- 'ВТОРНИК'

您不需要任何 i18n 模块 - 它们已内置。

MySQL cannot parse international date - just like PHP.

我知道最常见的配置不会为您提供 SQL 服务器或 Oracle,但它们是免费的,您不需要将它们用作主数据库。 设置并连接它们后,许多日期时间 i18n 问题都可以通过单个查询解决。

但是要小心。这正是我 fell in love 使用 SQL 服务器的方式。

使用DateTime::createFromFormat and date_default_timezone_set,即:

date_default_timezone_set('Europe/Moscow');
$ruDate = 'Октябрь 13, 2015';
$enDate = DateTime::createFromFormat('F j, Y', ruMonthsToEn($ruDate));
echo  $enDate->format('l, F, j, Y');
//Tuesday, October, 13, 2015
echo  $enDate->getTimestamp();
//1444764954

function ruMonthsToEn($date){
    $ruMonths = array( 'Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь' );
    $enMonths = array( 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' );
    return str_replace($ruMonths, $enMonths, $date);
}