Skip to content

Commit

Permalink
Merge pull request #221 from resurtm/get-text-i18n
Browse files Browse the repository at this point in the history
[WIP] Gettext support
  • Loading branch information
qiangxue committed May 11, 2013
2 parents 4feec1d + 124b153 commit a062edd
Show file tree
Hide file tree
Showing 9 changed files with 728 additions and 0 deletions.
Binary file added tests/unit/data/i18n/test.mo
Binary file not shown.
64 changes: 64 additions & 0 deletions tests/unit/data/i18n/test.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: \n"
"PO-Revision-Date: \n"
"Last-Translator: resurtm <[email protected]>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.5.5\n"

msgctxt "context1"
msgid ""
"Aliquam tempus elit vel purus molestie placerat. In sollicitudin tincidunt\n"
"aliquet. Integer tincidunt gravida tempor. In convallis blandit dui vel "
"malesuada.\n"
"Nunc vel sapien nunc, a pretium nulla."
msgstr ""
"Олицетворение однократно. Представленный лексико-семантический анализ "
"является\n"
"психолингвистическим в своей основе, но механизм сочленений полидисперсен. "
"Впечатление\n"
"однократно. Различное расположение выбирает сюжетный механизм сочленений."

msgctxt "context1"
msgid "String number two."
msgstr "Строка номер два."

msgctxt "context2"
msgid ""
"The other\n"
"\n"
"context.\n"
msgstr ""
"Другой\n"
"\n"
"контекст.\n"

msgctxt "context1"
msgid ""
"Missing\n"
"\r\t\"translation."
msgstr ""

msgctxt "context1"
msgid ""
"Nunc vel sapien nunc, a pretium nulla.\n"
"Pellentesque habitant morbi tristique senectus et netus et malesuada fames "
"ac turpis egestas."
msgstr "Короткий перевод."

msgid "contextless"
msgstr ""

msgctxt "context2"
msgid ""
"test1\\ntest2\n"
"\\\n"
"test3"
msgstr ""
"тест1\\nтест2\n"
"\\\n"
"тест3"
14 changes: 14 additions & 0 deletions tests/unit/framework/i18n/GettextMessageSourceTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace yiiunit\framework\i18n;

use yii\i18n\GettextMessageSource;
use yiiunit\TestCase;

class GettextMessageSourceTest extends TestCase
{
public function testLoadMessages()
{
$this->markTestSkipped();
}
}
95 changes: 95 additions & 0 deletions tests/unit/framework/i18n/GettextMoFileTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php

namespace yiiunit\framework\i18n;

use yii\i18n\GettextMoFile;
use yiiunit\TestCase;

class GettextMoFileTest extends TestCase
{
public function testLoad()
{
$moFile = new GettextMoFile();
$moFilePath = __DIR__ . '/../../data/i18n/test.mo';
$context1 = $moFile->load($moFilePath, 'context1');
$context2 = $moFile->load($moFilePath, 'context2');

// item count
$this->assertCount(3, $context1);
$this->assertCount(2, $context2);

// original messages
$this->assertArrayNotHasKey("Missing\n\r\t\"translation.", $context1);
$this->assertArrayHasKey("Aliquam tempus elit vel purus molestie placerat. In sollicitudin tincidunt\naliquet. Integer tincidunt gravida tempor. In convallis blandit dui vel malesuada.\nNunc vel sapien nunc, a pretium nulla.", $context1);
$this->assertArrayHasKey("String number two.", $context1);
$this->assertArrayHasKey("Nunc vel sapien nunc, a pretium nulla.\nPellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.", $context1);

$this->assertArrayHasKey("The other\n\ncontext.\n", $context2);
$this->assertArrayHasKey("test1\\ntest2\n\\\ntest3", $context2);

// translated messages
$this->assertFalse(in_array("", $context1));
$this->assertTrue(in_array("Олицетворение однократно. Представленный лексико-семантический анализ является\nпсихолингвистическим в своей основе, но механизм сочленений полидисперсен. Впечатление\nоднократно. Различное расположение выбирает сюжетный механизм сочленений.", $context1));
$this->assertTrue(in_array('Строка номер два.', $context1));
$this->assertTrue(in_array('Короткий перевод.', $context1));

$this->assertTrue(in_array("Другой\n\nконтекст.\n", $context2));
$this->assertTrue(in_array("тест1\\nтест2\n\\\nтест3", $context2));
}

public function testSave()
{
// initial data
$s = chr(4);
$messages = array(
'Hello!' => 'Привет!',
"context1{$s}Hello?" => 'Привет?',
'Hello!?' => '',
"context1{$s}Hello!?!" => '',
"context2{$s}\"Quotes\"" => '"Кавычки"',
"context2{$s}\nNew lines\n" => "\nПереносы строк\n",
"context2{$s}\tTabs\t" => "\tТабы\t",
"context2{$s}\rCarriage returns\r" => "\rВозвраты кареток\r",
);

// create temporary directory and dump messages
$poFileDirectory = __DIR__ . '/../../runtime/i18n';
if (!is_dir($poFileDirectory)) {
mkdir($poFileDirectory);
}
if (is_file($poFileDirectory . '/test.mo')) {
unlink($poFileDirectory . '/test.mo');
}

$moFile = new GettextMoFile();
$moFile->save($poFileDirectory . '/test.mo', $messages);

// load messages
$context1 = $moFile->load($poFileDirectory . '/test.mo', 'context1');
$context2 = $moFile->load($poFileDirectory . '/test.mo', 'context2');

// context1
$this->assertCount(2, $context1);

$this->assertArrayHasKey('Hello?', $context1);
$this->assertTrue(in_array('Привет?', $context1));

$this->assertArrayHasKey('Hello!?!', $context1);
$this->assertTrue(in_array('', $context1));

// context2
$this->assertCount(4, $context2);

$this->assertArrayHasKey("\"Quotes\"", $context2);
$this->assertTrue(in_array('"Кавычки"', $context2));

$this->assertArrayHasKey("\nNew lines\n", $context2);
$this->assertTrue(in_array("\nПереносы строк\n", $context2));

$this->assertArrayHasKey("\tTabs\t", $context2);
$this->assertTrue(in_array("\tТабы\t", $context2));

$this->assertArrayHasKey("\rCarriage returns\r", $context2);
$this->assertTrue(in_array("\rВозвраты кареток\r", $context2));
}
}
95 changes: 95 additions & 0 deletions tests/unit/framework/i18n/GettextPoFileTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php

namespace yiiunit\framework\i18n;

use yii\i18n\GettextPoFile;
use yiiunit\TestCase;

class GettextPoFileTest extends TestCase
{
public function testLoad()
{
$poFile = new GettextPoFile();
$poFilePath = __DIR__ . '/../../data/i18n/test.po';
$context1 = $poFile->load($poFilePath, 'context1');
$context2 = $poFile->load($poFilePath, 'context2');

// item count
$this->assertCount(4, $context1);
$this->assertCount(2, $context2);

// original messages
$this->assertArrayHasKey("Missing\n\r\t\"translation.", $context1);
$this->assertArrayHasKey("Aliquam tempus elit vel purus molestie placerat. In sollicitudin tincidunt\naliquet. Integer tincidunt gravida tempor. In convallis blandit dui vel malesuada.\nNunc vel sapien nunc, a pretium nulla.", $context1);
$this->assertArrayHasKey("String number two.", $context1);
$this->assertArrayHasKey("Nunc vel sapien nunc, a pretium nulla.\nPellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.", $context1);

$this->assertArrayHasKey("The other\n\ncontext.\n", $context2);
$this->assertArrayHasKey("test1\\\ntest2\n\\\\\ntest3", $context2);

// translated messages
$this->assertTrue(in_array("", $context1));
$this->assertTrue(in_array("Олицетворение однократно. Представленный лексико-семантический анализ является\nпсихолингвистическим в своей основе, но механизм сочленений полидисперсен. Впечатление\nоднократно. Различное расположение выбирает сюжетный механизм сочленений.", $context1));
$this->assertTrue(in_array('Строка номер два.', $context1));
$this->assertTrue(in_array('Короткий перевод.', $context1));

$this->assertTrue(in_array("Другой\n\nконтекст.\n", $context2));
$this->assertTrue(in_array("тест1\\\nтест2\n\\\\\nтест3", $context2));
}

public function testSave()
{
// initial data
$s = chr(4);
$messages = array(
'Hello!' => 'Привет!',
"context1{$s}Hello?" => 'Привет?',
'Hello!?' => '',
"context1{$s}Hello!?!" => '',
"context2{$s}\"Quotes\"" => '"Кавычки"',
"context2{$s}\nNew lines\n" => "\nПереносы строк\n",
"context2{$s}\tTabs\t" => "\tТабы\t",
"context2{$s}\rCarriage returns\r" => "\rВозвраты кареток\r",
);

// create temporary directory and dump messages
$poFileDirectory = __DIR__ . '/../../runtime/i18n';
if (!is_dir($poFileDirectory)) {
mkdir($poFileDirectory);
}
if (is_file($poFileDirectory . '/test.po')) {
unlink($poFileDirectory . '/test.po');
}

$poFile = new GettextPoFile();
$poFile->save($poFileDirectory . '/test.po', $messages);

// load messages
$context1 = $poFile->load($poFileDirectory . '/test.po', 'context1');
$context2 = $poFile->load($poFileDirectory . '/test.po', 'context2');

// context1
$this->assertCount(2, $context1);

$this->assertArrayHasKey('Hello?', $context1);
$this->assertTrue(in_array('Привет?', $context1));

$this->assertArrayHasKey('Hello!?!', $context1);
$this->assertTrue(in_array('', $context1));

// context2
$this->assertCount(4, $context2);

$this->assertArrayHasKey("\"Quotes\"", $context2);
$this->assertTrue(in_array('"Кавычки"', $context2));

$this->assertArrayHasKey("\nNew lines\n", $context2);
$this->assertTrue(in_array("\nПереносы строк\n", $context2));

$this->assertArrayHasKey("\tTabs\t", $context2);
$this->assertTrue(in_array("\tТабы\t", $context2));

$this->assertArrayHasKey("\rCarriage returns\r", $context2);
$this->assertTrue(in_array("\rВозвраты кареток\r", $context2));
}
}
37 changes: 37 additions & 0 deletions yii/i18n/GettextFile.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/

namespace yii\i18n;

use yii\base\Component;

/**
* GettextFile is the base class for representing a Gettext message file.
*
* @author Qiang Xue <[email protected]>
* @since 2.0
*/
abstract class GettextFile extends Component
{
/**
* Loads messages from a file.
* @param string $filePath file path
* @param string $context message context
* @return array message translations. Array keys are source messages and array values are translated messages:
* source message => translated message.
*/
abstract public function load($filePath, $context);

/**
* Saves messages to a file.
* @param string $filePath file path
* @param array $messages message translations. Array keys are source messages and array values are
* translated messages: source message => translated message. Note if the message has a context,
* the message ID must be prefixed with the context with chr(4) as the separator.
*/
abstract public function save($filePath, $messages);
}
59 changes: 59 additions & 0 deletions yii/i18n/GettextMessageSource.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/

namespace yii\i18n;

use Yii;

class GettextMessageSource extends MessageSource
{
const MO_FILE_EXT = '.mo';
const PO_FILE_EXT = '.po';

/**
* @var string
*/
public $basePath = '@app/messages';
/**
* @var string
*/
public $catalog = 'messages';
/**
* @var boolean
*/
public $useMoFile = true;
/**
* @var boolean
*/
public $useBigEndian = false;

protected function loadMessages($category, $language)
{
$messageFile = Yii::getAlias($this->basePath) . '/' . $language . '/' . $this->catalog;
if ($this->useMoFile) {
$messageFile .= static::MO_FILE_EXT;
} else {
$messageFile .= static::PO_FILE_EXT;
}

if (is_file($messageFile)) {
if ($this->useMoFile) {
$gettextFile = new GettextMoFile(array('useBigEndian' => $this->useBigEndian));
} else {
$gettextFile = new GettextPoFile();
}
$messages = $gettextFile->load($messageFile, $category);
if (!is_array($messages)) {
$messages = array();
}
return $messages;
} else {
Yii::error("The message file for category '$category' does not exist: $messageFile", __METHOD__);
return array();
}
}
}
Loading

0 comments on commit a062edd

Please sign in to comment.