Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

English dates to Russian with PHP

I am building a multilingual site that will be displayed in both English and Russian. Everything in terms of translation is working very well, with the exception of dates.

As I understand it, PHP's strtotime() function is strictly for english dates as it says in the official docs "Parse about any English textual datetime description into a Unix timestamp."

I have already tried using setlocale( LC_TIME, 'ru_RU', 'russian' ); and then strftime() but it just keeps returning the unix January 1970 date.

What I am attempting to do is get the day name (i.e. Wednesday) from a date and display it. Normally, with an english date, I would just do it like $this->day = date('l', strtotime( $date )); but because I am trying to do it with both English and Russian I am doing it like so $this->day = strftime("%A", strtotime($date) );

My date is formatted in words like "October 13, 2015" which I'm assuming is part of the problem because the Russian translation for "October" is "Октябрь" So the end result that I end up trying to parse is "Октябрь 13, 2015" which always returns "Thursday" when in fact October 13 is Tuesday.

Here is everything I am doing to parse the date:

$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
}

This parses the english representation of the date perfectly, but everything else seems to get messed up. As a temporary fall back, I have removed the day of the week from the final display and am just stripping "October" or "Октябрь" from the initial date string and displaying that, but this is not what the client is expecting.

I have also tried this format: strftime('%B %e, %Y', $unix_date); in hopes that matching the strftime() format with the way the date is originally displayed would work, but that gave me the same end result.

P.S. I am running this all on my localhost MAMP server. The production/staging servers are running on nginx.

EDIT: This is a WordPress website and the date is being created via the 'Advanced Custom Fields Pro' plugin. The plugin doesn't allow users to save the date as a timestamp. I have already tried using WP's built in date_i18n($format, $unix_timestamp); function with no luck.

EDIT x2: I am currently doing something like this:

// 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;   

Which works for now, but I want to translate the day name as well. For example, Tuesday October 13, 2015 and this method doesn't provide a solid way of doing that.

like image 566
Ty Bailey Avatar asked Oct 04 '15 15:10

Ty Bailey


Video Answer


1 Answers

I see three options here.


  1. Write your own parser.

You can always write PHP code to convert the date yourself. It is dirty, it is brute force, but it is straight forward.

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. Use a library.

For example, 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'

However intl is very picky; the date string must match its exact format: No uppercase, no oктябрь, date must be before month, and it must ends with "r.".

Not flexible enough to worth the trouble, if you ask me.


  1. Ask an enterprise database.

I love SQL Server. Look:

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 ) );
-- 'вторник'

Or Oracle, also gets the job done:

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;
-- 'ВТОРНИК'

You don't need any i18n modules - they have them built-in.

MySQL cannot parse international date - just like PHP.

I know most common configs do not give you SQL Server or Oracle, but they are free, and you don't need to use them as your main database. Once you got them setup and connected, many datetime i18n problems can be solved with a single query.

But be careful. This is exactly how I fell in love with SQL Server.

like image 192
Sheepy Avatar answered Nov 11 '22 17:11

Sheepy