Skip to content

Commit

Permalink
New feature #14782: Possibility to override single translations by da…
Browse files Browse the repository at this point in the history
…tabase (#2213)
  • Loading branch information
Shnoulle committed Feb 8, 2022
1 parent b87691e commit 1ed3005
Show file tree
Hide file tree
Showing 10 changed files with 329 additions and 61 deletions.
3 changes: 2 additions & 1 deletion application/config/internal.php
Expand Up @@ -202,8 +202,9 @@
'secure' => ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || (isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443))
),
),
'sourceLanguage' => 'en',
'messages' => array(
'class' => 'CGettextMessageSource',
'class' => 'application.core.LSMessageSource',
'cachingDuration'=>3600,
'forceTranslation' => true,
'useMoFile' => true,
Expand Down
2 changes: 1 addition & 1 deletion application/config/version.php
Expand Up @@ -12,7 +12,7 @@
*/

$config['versionnumber'] = '5.3.0-dev';
$config['dbversionnumber'] = 479;
$config['dbversionnumber'] = 480;
$config['buildnumber'] = '';
$config['updatable'] = true;
$config['templateapiversion'] = 3;
Expand Down
56 changes: 0 additions & 56 deletions application/core/LSCGettextMessageSource.php

This file was deleted.

143 changes: 143 additions & 0 deletions application/core/LSMessageSource.php
@@ -0,0 +1,143 @@
<?php

/**
* LSMessageSource class file.
*
* @author Denis Chenu
* @link http://www.yiiframework.com/
* @copyright 2021 LimeSurvey
* License: GNU/GPL License v3 or later, see LICENSE.php
*/

class LSMessageSource extends CMessageSource
{
public const CACHE_KEY_PREFIX = 'Yii.LSMessageSource.';
public const MO_FILE_EXT = '.mo';
public const PO_FILE_EXT = '.po';

/**
* @var integer the time in seconds that the messages can remain valid in cache.
* @see CDbMessageSource::cachingDuration
* @see CGettextMessageSource::cachingDuration
*/
public $cachingDuration = 0;
/**
* @var string the ID of the cache application component that is used to cache the messages.
* Defaults to 'cache' which refers to the primary cache application component.
* @see CDbMessageSource::cacheID
* @see CGettextMessageSource::cacheID
*/
public $cacheID = 'cache';
/**
* @var string the base path for all translated messages. Defaults to null, meaning
* @see CGettextMessageSource::basePath
*/
public $basePath;
/**
* @var boolean whether to load messages from MO files. Defaults to true.
* @see CGettextMessageSource::useMoFile
*/
public $useMoFile = true;
/**
* @var boolean whether to use Big Endian to read MO files.
* Defaults to false. This property is only used when {@link useMoFile} is true.
* see CGettextMessageSource::useBigEndian
*/
public $useBigEndian = false;

/**
* Loads the message translation for the specified language and category.
* @see CGettextMessageSource::loadMessages
* @see CDbMessageSource::loadMessages
* @param string $category the message category, unused in LimeSurvey core (always '')
* @param string $language the target language
* @return array the loaded messages
*/
protected function loadMessages($category, $language)
{
$messageFile = $this->basePath . DIRECTORY_SEPARATOR . $language . DIRECTORY_SEPARATOR . $language;
if ($this->useMoFile) {
$messageFile .= self::MO_FILE_EXT;
} else {
$messageFile .= self::PO_FILE_EXT;
}

if (
$this->cachingDuration > 0
&& $this->cacheID !== false
&& ($cache = Yii::app()->getComponent($this->cacheID)) !== null
) {
$key = self::CACHE_KEY_PREFIX . $messageFile . $category . '.' . $language;
if (($data = $cache->get($key)) !== false) {
return unserialize($data);
}
}

/* Messages by getext */
if (is_file($messageFile)) {
if ($this->useMoFile) {
$file = new CGettextMoFile($this->useBigEndian);
} else {
$file = new CGettextPoFile();
}
$messagesGettext = $file->load($messageFile, $category);
} else {
$messagesGettext = array();
}

/* Messages by DB */
if (App()->getConfig('DBVersion') >= 480) {
$messagesDb = $this->loadMessagesFromDb($category, $language);
} else {
$messagesDb = array();
}

$messages = array_merge(
$messagesGettext,
$messagesDb
);

if (isset($cache)) {
/* $key is set if $cache is set */
$cache->set($key, serialize($messages), $this->cachingDuration);
}

return $messages;
}

/**
* @see CDbMessageSource::getDbConnection
* @throws CException if {@link connectionID} application component is invalid
* @return CDbConnection the DB connection used for the message source.
*/
public function getDbConnection()
{
return App()->getDb();
}

/**
* @see CDbMessageSource::loadMessagesFromDb
* @param string $category the message category, unused in LimeSurvey core (always '')
* @param string $language the target language
* @return array the messages loaded from database
*/
protected function loadMessagesFromDb($category, $language)
{
$command = $this->getDbConnection()->createCommand()
->select("t1.message AS message, t2.translation AS translation")
->from(array(
"{{source_message}} t1",
"{{message}} t2"
))
->where(
't1.id=t2.id AND t1.category=:category AND t2.language=:language',
array(':category' => $category, ':language' => $language)
);
$messages = array();
foreach ($command->queryAll() as $row) {
$messages[$row['message']] = $row['translation'];
}

return $messages;
}
}
1 change: 0 additions & 1 deletion application/core/LSYii_Application.php
Expand Up @@ -303,7 +303,6 @@ public function setLanguage($sLanguage)
}

$sLanguage = preg_replace('/[^a-z0-9-]/i', '', $sLanguage);
$this->messages->catalog = $sLanguage;
App()->session['_lang'] = $sLanguage; // See: http://www.yiiframework.com/wiki/26/setting-and-maintaining-the-language-in-application-i18n/
parent::setLanguage($sLanguage);
}
Expand Down
33 changes: 33 additions & 0 deletions application/helpers/update/updates/Update_480.php
@@ -0,0 +1,33 @@
<?php

namespace LimeSurvey\Helpers\Update;

class Update_480 extends DatabaseUpdateBase
{
public function up()
{
$this->db->createCommand()->createTable(
'{{source_message}}',
[
'id' => "pk",
'category' => "string(35)",
'message' => "text",
],
$this->options
);
$this->db->createCommand()->createTable(
'{{message}}',
[
'id' => "integer NOT NULL",
'language' => "string(16)",
'translation' => "text",
],
$this->options
);
$this->db->createCommand()->addPrimaryKey(
'{{message_pk}}',
'{{message}}',
['id', 'language']
);
}
}
4 changes: 2 additions & 2 deletions application/libraries/PluginManager/PluginBase.php
Expand Up @@ -107,11 +107,11 @@ protected function setLocaleComponent()
return;
}

// Set plugin specific locale file to locale/<lang>/<lang>.mo
// Set plugin specific locale file to locale/<lang>/<lang>.mo, and DB replacement
\Yii::app()->setComponent(
get_class($this) . 'Messages',
[
'class' => 'LSCGettextMessageSource',
'class' => 'LSMessageSource',
'cachingDuration' => 3600,
'forceTranslation' => true,
'useMoFile' => true,
Expand Down
25 changes: 25 additions & 0 deletions installer/create-database.php
Expand Up @@ -1121,6 +1121,31 @@ function populateDatabase($oDB)
'attributes' => "text NULL",
], $options);

// language tables: sourcemessage + message and constraint
$oDB->createCommand()->createTable(
'{{source_message}}',
[
'id' => "pk",
'category' => "string(35)",
'message' => "text",
],
$options
);
$oDB->createCommand()->createTable(
'{{message}}',
[
'id' => "integer NOT NULL",
'language' => "string(16)",
'translation' => "text",
],
$options
);
$oDB->createCommand()->addPrimaryKey(
'{{message_pk}}',
'{{message}}',
['id', 'language']
);

// Install default plugins.
foreach (LsDefaultDataSets::getDefaultPluginsData() as $plugin) {
unset($plugin['id']);
Expand Down
13 changes: 13 additions & 0 deletions tests/TestHelper.php
Expand Up @@ -38,6 +38,19 @@ public function importAll()
Yii::app()->loadHelper('admin/activate');
}

/**
* Reset existing cache (file by default)
*/
public function resetCache()
{
if (method_exists(Yii::app()->cache, 'flush')) {
Yii::app()->cache->flush();
}
if (method_exists(Yii::app()->cache, 'gc')) {
Yii::app()->cache->gc();
}
}

/**
* @param string $title
* @param int $surveyId
Expand Down

0 comments on commit 1ed3005

Please sign in to comment.