【问题标题】:English dates to Russian with PHP使用 PHP 将英语日期转换为俄语
【发布时间】:2015-10-04 15:34:18
【问题描述】:

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

据我了解,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 日”之类的字词,我认为这是问题的一部分,因为“十月”的俄语翻译是“Октябрь”所以最终结果是我最终尝试解析的是“Октябрь 13, 2015”,它总是返回“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() 格式与最初显示日期的方式相匹配,但这给了我相同的最终结果。

附:我在我的本地主机 MAMP 服务器上运行这一切。生产/登台服务器在 nginx 上运行。

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

EDIT 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 日,星期二,这种方法不能提供可靠的方法。

【问题讨论】:

  • 谢谢@PaulCrovella,我会调查的。
  • 只是为了上下文,日期存储为格式化字符串而不是时间戳是否有原因?您能否提供一些$this->event_dates 内部的样本以便对其进行测试?
  • 我还要指出1970-01-01 00:00:00 UTC,其时间戳等于0 == null == false,是星期四。
  • @spenibus 日期以字符串形式出现,因为它是使用“高级自定义字段专业版”WordPress 插件创建的,该插件不允许您将日期保存为时间戳。我修改了我的问题,以便更好地了解环境。
  • 日期来自某种形式的存储?它们以任何一种语言存储?它们是从 ACF 日期选择器中保存的?

标签: php date datetime localization multilingual


【解决方案1】:

我在这里看到三个选项。


  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 有一个IntlDateFormatter

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

不过intl很挑剔;日期字符串必须与其 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 无法解析国际日期 - 就像 PHP。

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

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

【讨论】:

  • 感谢您的详细解答。我将努力编写自己的解析器,并希望它能成功。如果您有任何资源/提示/技巧,我很乐意听到/看到它们。
  • @TyBailey 我添加了将俄语/英语工作日添加到您的日期字符串之前的 php 代码 - 它会因您的平台和/或 php 版本而有所不同。如果它不起作用,请告诉我。
  • 这里有足够的信息让我组装我自己的解析器。感谢您花时间将这个详细的答案放在一起,因此您赢得了我的赏金。谢谢!
【解决方案2】:

使用DateTime::createFromFormatdate_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);
}

【讨论】:

  • 注意:输入格式好像是F j, Y。否则无可挑剔。该时间戳可以直接转到strftime
【解决方案3】:

您的理解是正确的:strtotime() 只会将 english 字符串解析为整数。您可能错过了,当函数无法进行翻译时,它会返回一个布尔值 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),因为它们与语言无关,不需要这个额外的处理步骤。

【讨论】:

  • 请查看我更新的问题。我无法将日期保存为时间戳,因为它是通过不允许这样做的 WP 插件生成的。
猜你喜欢
  • 2021-12-25
  • 2015-07-27
  • 1970-01-01
  • 2011-02-19
  • 2014-03-09
  • 2015-06-28
  • 1970-01-01
  • 1970-01-01
  • 2016-01-03
相关资源
最近更新 更多