updated webform, webform_localization, profile2, term_merge, search_api_saved_pages, rules, redirect, overide_node_options

This commit is contained in:
2019-05-13 18:47:27 +02:00
parent 58cd990c8c
commit 9adc940a67
281 changed files with 28658 additions and 7138 deletions

View File

@@ -2,11 +2,11 @@
Localization Update
-------------------
Automatically download and update your translations by fetching them from
http://localize.drupal.org or any other Localization server.
https://localize.drupal.org or any other Localization server.
The l10n update module helps to keep the translation of your drupal core and
contributed modules up to date with the central Drupal translation repository
at http://localize.drupal.org. Alternatively locally stored translation files
at https://localize.drupal.org. Alternatively locally stored translation files
can be used as translation source too.
By choice updates are performed automatically or manually. Locally altered
@@ -14,12 +14,13 @@ Localization Update
The l10n update module is developed for:
* Distributions which include their own translations in .po files.
* Site admins who want to update the translation with each new module revision.
* Site admins who want to update the translation with each new module
revision.
* Site builders who want an easy tool to download translations for a site.
* Multi-sites that share one translation source.
Project page: http://drupal.org/project/l10n_update
Support queue: http://drupal.org/project/issues/l10n_update
Project page: https://www.drupal.org/project/l10n_update
Support queue: https://www.drupal.org/project/issues/l10n_update
Installation
------------
@@ -44,9 +45,11 @@ Update interface translations
-----------------------------
You want to import translations regularly using cron. You can enable this
on Administration > Configuration > Regional and language > Languages:
Choose the "Translation updates" tab.
Change "Check for updates" to "Daily" or "Weekly" instead of the default "Never".
From now on cron will check for updated translations, and import them is required.
* Choose the "Translation updates" tab.
* Change "Check for updates" to "Daily" or "Weekly" instead of the default
"Never".
From now on cron will check for updated translations, and import them is
required.
The status of the translations is reported on the "Status report" page at
Administration > Reports.
@@ -55,7 +58,8 @@ Update interface translations
Administration > Configuration > Regional and language > Translate inteface
Choose the "Update" tab.
You see a list of all modules and their translation status.
On the bottom of the page, you can manually update using "Update translations".
On the bottom of the page, you can manually update using "Update
translations".
Use Drush
---------
@@ -68,10 +72,12 @@ Use Drush
Summary of administrative pages
-------------------------------
Translations status overview can be found at
Administration > Configuration > Regional and language > Languages > Translation updates
Administration > Configuration > Regional and language > Languages
> Translation updates
Update configuration settings can be found at
Administration > Configuration > Regional and language > Translate interface > Update
Administration > Configuration > Regional and language > Translate interface
> Update
Translating Drupal core, modules and themes
-------------------------------------------
@@ -90,7 +96,8 @@ Translating Drupal core, modules and themes
strings will not be overwritten by translation updates.
NOTE: Only manual changes made AFTER installing Localization Update module
are preserved. To preserve manual changes made prior to installation of
Localization Update module, use the option 'All existing translations are kept...'.
Localization Update module, use the option 'All existing translations are
kept...'.
po files, multi site and distributions
--------------------------------------
@@ -100,7 +107,8 @@ po files, multi site and distributions
as their translation source.
All installations that share the same translation files must be configured
with the same 'Store downloaded files' file path e.g. 'sites/all/translations'.
with the same 'Store downloaded files' file path e.g.
'sites/all/translations'.
Set the 'Update source' of one installation to 'Local files and remote server'
or 'Remote server only', all other installations are set to
'Local files only' or 'Local files and remote server'.
@@ -115,6 +123,30 @@ po files, multi site and distributions
Po files included in distributions should match this syntax too.
Missing translations
--------------------
If you see "Missing translations for ..." on the Translate interface update
page this means that Localization Update module was not able to find a
translation file for one or more of your modules or themes. This is usually
the case with features, but can also occur with custom modules.
In case of custom modules, remove the line "project = ..." from the .info
file. Only use "project = ..." for modules that are available at drupal.org or
custom modules for which an alternative source of translation is provided (see
below). In case of a feature add the feature machine name to 'Project' at
admin/config/regional/language/update > Disable update. This will prevent
Localization Update module from checking for updates for this feature.
If "missing translations" lists all your enabled modules the webserver has no
access to ftp.drupal.org. Contact your hosting to allow access.
If only a few of your contributed modules are in the list, first verify that
the translation is actually missing by visiting the listed URL of the .po file
(for example "https://ftp.drupal.org/files/translations/7.x/views/views.hu.po")
When the file is not found, you either try again later or contact a
translation administrator of your language at https://localize.drupal.org/.
Alternative source of translation
---------------------------------
@@ -126,7 +158,7 @@ Alternative source of translation
Modules can force Locale to load the translation of an other project by
defining 'interface translation project' in their .info file. This can be
usefull for custom modules to use for example a common translation file
useful for custom modules to use for example a common translation file
interface translation project = my_project
@@ -151,6 +183,16 @@ API
See l10n_update.api.php for more information.
Using a Proxy
-------------
Use the cURL HTTP Request module (https://www.drupal.org/project/chr) if your
website is behind a proxy. If you want to use an alternative http request
function, set the 'drupal_http_request_function', the same way as you would
set it to override Drupal core's http_request function:
$conf['drupal_http_request_function'] = 'my_custom_http_request';
Maintainers
-----------
Erik Stielstra

View File

@@ -1,5 +1,5 @@
/**
* Available translation updates page.
* Translate interface update page.
*/
#l10n-update-status-form .expand .inner {
background: transparent url(../images/menu-collapsed-rtl.png) right .6em no-repeat;

View File

@@ -1,20 +1,27 @@
/**
* Available translation updates page.
* @file
* Styling for the translate interface update page.
*/
#l10n-update-status-form table {
table-layout: fixed;
}
#l10n-update-status-form th.select-all {
width: 4%;
}
#l10n-update-status-form th.title {
width: 25%;
}
#l10n-update-status-form th.description {
}
#l10n-update-status-form td {
vertical-align: top;
}
#l10n-update-status-form .expand .inner {
background: transparent url(../images/menu-collapsed.png) left .6em no-repeat;
margin-left: -12px;
@@ -29,9 +36,11 @@
font-size: 1.15em;
font-weight: bold;
}
#l10n-update-status-form .description {
cursor: pointer;
}
#l10n-update-status-form .description .inner {
color: #5c5c5b;
line-height: 20px;
@@ -39,11 +48,13 @@
text-overflow: ellipsis;
white-space: nowrap;
}
#l10n-update-status-form .expanded .description .inner {
height: auto;
overflow: visible;
white-space: normal;
}
#l10n-update-status-form .expanded .description .text {
-webkit-hyphens: auto;
-moz-hyphens: auto;
@@ -52,11 +63,13 @@
.js #l10n-update-status-form .description .inner {
height: 20px;
}
#l10n-update-status-form .expanded .description .inner {
height: auto;
overflow: visible;
white-space: normal;
}
#l10n-update-status-form .details {
padding: 5px 0;
max-width: 490px;
@@ -64,6 +77,7 @@
font-size: 0.9em;
color: #666;
}
#l10n-update-status-form .visually-hidden {
position: absolute !important;
clip: rect(1px, 1px, 1px, 1px);
@@ -72,6 +86,7 @@
width: 1px;
word-wrap: normal;
}
@media screen and (max-width: 40em) {
#l10n-update-status-form th.title {
width: 20%;

View File

@@ -2,7 +2,7 @@
/**
* @file
* Definition of Drupal\Component\Gettext\PoHeader
* Definition of Drupal\Component\Gettext\PoHeader.
*/
/**
@@ -88,7 +88,7 @@ class PoHeader {
* Plural form component from the header, for example:
* 'nplurals=2; plural=(n > 1);'.
*/
function getPluralForms() {
public function getPluralForms() {
return $this->_pluralForms;
}
@@ -98,7 +98,7 @@ class PoHeader {
* @param string $languageName
* Human readable language name.
*/
function setLanguageName($languageName) {
public function setLanguageName($languageName) {
$this->_languageName = $languageName;
}
@@ -108,7 +108,7 @@ class PoHeader {
* @return string
* The human readable language name.
*/
function getLanguageName() {
public function getLanguageName() {
return $this->_languageName;
}
@@ -118,7 +118,7 @@ class PoHeader {
* @param string $projectName
* Human readable project name.
*/
function setProjectName($projectName) {
public function setProjectName($projectName) {
$this->_projectName = $projectName;
}
@@ -128,7 +128,7 @@ class PoHeader {
* @return string
* The human readable project name.
*/
function getProjectName() {
public function getProjectName() {
return $this->_projectName;
}
@@ -186,11 +186,14 @@ class PoHeader {
* @param string $pluralforms
* The Plural-Forms entry value.
*
* @return
* @return array|boolean
* An array containing the number of plural forms and the converted version
* of the formula that can be evaluated with PHP later.
*
* @throws \Exception
* Throws exception in case plural formula could not be parsed.
*/
function parsePluralForms($pluralforms) {
public function parsePluralForms($pluralforms) {
// First, delete all whitespace.
$pluralforms = strtr($pluralforms, array(" " => "", "\t" => ""));
@@ -253,7 +256,7 @@ class PoHeader {
* @param string $string
* A string containing the arithmetic formula.
*
* @return
* @return string|FALSE
* A version of the formula to evaluate with PHP later.
*/
private function parseArithmetic($string) {
@@ -388,6 +391,7 @@ class PoHeader {
$tokens[] = $formula[$i];
}
break;
case 5:
if ($next == '&') {
$tokens[] = '&&';
@@ -397,6 +401,7 @@ class PoHeader {
$tokens[] = $formula[$i];
}
break;
case 6:
if ($next == '|') {
$tokens[] = '||';

View File

@@ -13,7 +13,7 @@ class PoItem {
/**
* The language code this translation is in.
*
* @car string
* @var string
*/
private $_langcode;
@@ -27,7 +27,8 @@ class PoItem {
/**
* The source string or array of strings if it has plurals.
*
* @var string or array
* @var string|array
*
* @see $_plural
*/
private $_source;
@@ -46,10 +47,18 @@ class PoItem {
*/
private $_comment;
/**
* The text group of this translation.
*
* @var string
*/
private $_textgroup;
/**
* The translation string or array of strings if it has plurals.
*
* @var string or array
* @var string|array
*
* @see $_plural
*/
private $_translation;
@@ -57,9 +66,10 @@ class PoItem {
/**
* Get the language code of the currently used language.
*
* @return string with langcode
* @return string
* The translation language code.
*/
function getLangcode() {
public function getLangcode() {
return $this->_langcode;
}
@@ -67,109 +77,131 @@ class PoItem {
* Set the language code of the current language.
*
* @param string $langcode
* The translation language code.
*/
function setLangcode($langcode) {
public function setLangcode($langcode) {
$this->_langcode = $langcode;
}
/**
* Get the translation group of this translation.
*
* @return string
* The translation text group.
*/
public function getTextgroup() {
return empty($this->_textgroup) ? 'default' : $this->_textgroup;
}
/**
* Set the translation group of this translation.
*
* @param string $textgroup
* The translation text group.
*/
public function setTextgroup($textgroup) {
$this->_textgroup = $textgroup;
}
/**
* Get the context this translation belongs to.
*
* @return string $context
* @return string
* The translation context.
*/
function getContext() {
public function getContext() {
return $this->_context;
}
/**
* Set the context this translation belongs to.
*
* @param string $context
*/
function setContext($context) {
public function setContext($context) {
$this->_context = $context;
}
/**
* Get the source string or the array of strings if the translation has
* plurals.
* Get the source string(s) if the translation has plurals.
*
* @return string or array $translation
* @return string|array
* Translation source string(s).
*/
function getSource() {
public function getSource() {
return $this->_source;
}
/**
* Set the source string or the array of strings if the translation has
* plurals.
* Set the source string(s) if the translation has plurals.
*
* @param string or array $source
* @param string|array
* Translation source string(s).
*/
function setSource($source) {
public function setSource($source) {
$this->_source = $source;
}
/**
* Get the translation string or the array of strings if the translation has
* plurals.
* Get the translation string(s) if the translation has plurals.
*
* @return string or array $translation
* @return string|array
* Translation string(s).
*/
function getTranslation() {
public function getTranslation() {
return $this->_translation;
}
/**
* Set the translation string or the array of strings if the translation has
* plurals.
*
* @param string or array $translation
* Set the translation string(s) if the translation has plurals.
*/
function setTranslation($translation) {
public function setTranslation($translation) {
$this->_translation = $translation;
}
/**
* Set if the translation has plural values.
*
* @param boolean $plural
* @param bool $plural
* The translation plural flag.
*/
function setPlural($plural) {
public function setPlural($plural) {
$this->_plural = $plural;
}
/**
* Get if the translation has plural values.
*
* @return boolean $plural
* @return integer $plural
* The translation plural flag.
*/
function isPlural() {
public function isPlural() {
return $this->_plural;
}
/**
* Get the comment of this translation.
*
* @return String $comment
* @return string
* The translation comment.
*/
function getComment() {
public function getComment() {
return $this->_comment;
}
/**
* Set the comment of this translation.
*
* @param String $comment
* @param string $comment
* The translation comment.
*/
function setComment($comment) {
public function setComment($comment) {
$this->_comment = $comment;
}
/**
* Create the PoItem from a structured array.
*
* @param array values
* @param array $values
* Keyed array with translation data.
*/
public function setFromArray(array $values = array()) {
if (isset($values['context'])) {
@@ -181,19 +213,19 @@ class PoItem {
if (isset($values['translation'])) {
$this->setTranslation($values['translation']);
}
if (isset($values['comment'])){
if (isset($values['comment'])) {
$this->setComment($values['comment']);
}
if (isset($this->_source) &&
strpos($this->_source, L10N_UPDATE_PLURAL_DELIMITER) !== FALSE) {
$this->setSource(explode(L10N_UPDATE_PLURAL_DELIMITER, $this->_source));
$this->setTranslation(explode(L10N_UPDATE_PLURAL_DELIMITER, $this->_translation));
if (isset($this->_source) && count($this->_source) > 1) {
$this->setPlural(count($this->_translation) > 1);
}
}
/**
* Output the PoItem as a string.
*
* @return string
* PO item string value.
*/
public function __toString() {
return $this->formatItem();
@@ -201,6 +233,9 @@ class PoItem {
/**
* Format the POItem as a string.
*
* @return string
* Formatted PO item.
*/
private function formatItem() {
$output = '';
@@ -226,6 +261,9 @@ class PoItem {
/**
* Formats a plural translation.
*
* @return string
* Gettext formatted plural translation.
*/
private function formatPlural() {
$output = '';
@@ -248,6 +286,9 @@ class PoItem {
/**
* Formats a singular translation.
*
* @return string
* Gettext formatted singular translation.
*/
private function formatSingular() {
$output = '';
@@ -258,6 +299,12 @@ class PoItem {
/**
* Formats a string for output on multiple lines.
*
* @param string $string
* A string.
*
* @return string
* Gettext formatted multi-line string.
*/
private function formatString($string) {
// Escape characters for processing.

View File

@@ -20,7 +20,7 @@ class PoMemoryWriter implements PoWriterInterface {
/**
* Constructor, initialize empty items.
*/
function __construct() {
public function __construct() {
$this->_items = array();
}
@@ -28,12 +28,28 @@ class PoMemoryWriter implements PoWriterInterface {
* Implements PoWriterInterface::writeItem().
*/
public function writeItem(PoItem $item) {
if (is_array($item->getSource())) {
$item->setSource(implode(L10N_UPDATE_PLURAL_DELIMITER, $item->getSource()));
$item->setTranslation(implode(L10N_UPDATE_PLURAL_DELIMITER, $item->getTranslation()));
}
$context = $item->getContext();
$this->_items[$context != NULL ? $context : ''][$item->getSource()] = $item->getTranslation();
$context = $context != NULL ? $context : '';
if ($item->isPlural()) {
$sources = $item->getSource();
$translations = $item->getTranslation();
// Build additional source strings for plurals.
$entries = array_keys($translations);
for ($i = 3; $i <= count($entries); $i++) {
$sources[] = $sources[1];
}
$translations = array_map('_locale_import_append_plural', $translations, $entries);
$sources = array_map('_locale_import_append_plural', $sources, $entries);
foreach ($entries as $index) {
$this->_items[][$sources[$index]] = $translations[$index];
}
}
else {
$this->_items[$context][$item->getSource()] = $item->getTranslation();
}
}
/**
@@ -49,7 +65,8 @@ class PoMemoryWriter implements PoWriterInterface {
/**
* Get all stored PoItem's.
*
* @return array PoItem
* @return array
* Array of PO item's.
*/
public function getData() {
return $this->_items;
@@ -60,7 +77,7 @@ class PoMemoryWriter implements PoWriterInterface {
*
* Not implemented. Not relevant for the MemoryWriter.
*/
function setLangcode($langcode) {
public function setLangcode($langcode) {
}
/**
@@ -68,7 +85,7 @@ class PoMemoryWriter implements PoWriterInterface {
*
* Not implemented. Not relevant for the MemoryWriter.
*/
function getLangcode() {
public function getLangcode() {
}
/**
@@ -76,7 +93,7 @@ class PoMemoryWriter implements PoWriterInterface {
*
* Not implemented. Not relevant for the MemoryWriter.
*/
function getHeader() {
public function getHeader() {
}
/**
@@ -84,7 +101,7 @@ class PoMemoryWriter implements PoWriterInterface {
*
* Not implemented. Not relevant for the MemoryWriter.
*/
function setHeader(PoHeader $header) {
public function setHeader(PoHeader $header) {
}
}

View File

@@ -26,7 +26,7 @@ interface PoStreamInterface {
/**
* Get the URI of the PO stream that is being read or written.
*
* @return
* @return string
* URI string for this stream.
*/
public function getURI();
@@ -34,7 +34,7 @@ interface PoStreamInterface {
/**
* Set the URI of the PO stream that is going to be read or written.
*
* @param $uri
* @param string $uri
* URI string to set for this stream.
*/
public function setURI($uri);

View File

@@ -9,7 +9,7 @@
* Implements Gettext PO stream reader.
*
* The PO file format parsing is implemented according to the documentation at
* http://www.gnu.org/software/gettext/manual/gettext.html#PO-Files
* http://www.gnu.org/software/gettext/manual/gettext.html#PO-Files.
*/
class PoStreamReader implements PoStreamInterface, PoReaderInterface {
@@ -228,7 +228,7 @@ class PoStreamReader implements PoStreamInterface, PoReaderInterface {
if (!$item) {
return;
}
$header = new PoHeader;
$header = new PoHeader();
$header->setFromString(trim($item->getTranslation()));
$this->_header = $header;
}
@@ -240,13 +240,13 @@ class PoStreamReader implements PoStreamInterface, PoReaderInterface {
* this line ends the current item, it is saved with setItemFromArray() with
* data from $this->_current_item.
*
* An internal state machine is maintained in this reader using $this->_context
* as the reading state. PO items are inbetween COMMENT states (when items have
* at least one line or comment inbetween them or indicated by MSGSTR or
* MSGSTR_ARR followed immediately by an MSGID or MSGCTXT (when items closely
* follow each other).
* An internal state machine is maintained in this reader using
* $this->_context as the reading state. PO items are in between COMMENT
* states (when items have at least one line or comment in between them or
* indicated by MSGSTR or MSGSTR_ARR followed immediately by an MSGID or
* MSGCTXT (when items closely follow each other).
*
* @return
* @return FALSE|NULL
* FALSE if an error was logged, NULL otherwise. The errors are considered
* non-blocking, so reading can continue, while the errors are collected
* for later presentation.
@@ -281,7 +281,6 @@ class PoStreamReader implements PoStreamInterface, PoReaderInterface {
if (!strncmp('#', $line, 1)) {
// Lines starting with '#' are comments.
if ($this->_context == 'COMMENT') {
// Already in comment context, add to current comment.
$this->_current_item['#'][] = substr($line, 1);
@@ -295,18 +294,17 @@ class PoStreamReader implements PoStreamInterface, PoReaderInterface {
$this->_current_item['#'][] = substr($line, 1);
$this->_context = 'COMMENT';
return;
return NULL;
}
else {
// A comment following any other context is a syntax error.
$this->_errors[] = format_string('The translation stream %uri contains an error: "msgstr" was expected but not found on line %line.', $log_vars);
return FALSE;
}
return;
return NULL;
}
elseif (!strncmp('msgid_plural', $line, 12)) {
// A plural form for the current source string.
if ($this->_context != 'MSGID') {
// A plural form can only be added to an msgid directly.
$this->_errors[] = format_string('The translation stream %uri contains an error: "msgid_plural" was expected but not found on line %line.', $log_vars);
@@ -333,11 +331,10 @@ class PoStreamReader implements PoStreamInterface, PoReaderInterface {
$this->_current_item['msgid'][] = $quoted;
$this->_context = 'MSGID_PLURAL';
return;
return NULL;
}
elseif (!strncmp('msgid', $line, 5)) {
// Starting a new message.
if (($this->_context == 'MSGSTR') || ($this->_context == 'MSGSTR_ARR')) {
// We are currently in string context, save current item.
$this->setItemFromArray($this->_current_item);
@@ -346,7 +343,8 @@ class PoStreamReader implements PoStreamInterface, PoReaderInterface {
$this->_current_item = array();
}
elseif ($this->_context == 'MSGID') {
// We are currently already in the context, meaning we passed an id with no data.
// We are currently already in the context, meaning we passed an id
// with no data.
$this->_errors[] = format_string('The translation stream %uri contains an error: "msgid" is unexpected on line %line.', $log_vars);
return FALSE;
}
@@ -358,17 +356,16 @@ class PoStreamReader implements PoStreamInterface, PoReaderInterface {
$quoted = $this->parseQuoted($line);
if ($quoted === FALSE) {
// The message id must be wrapped in quotes.
$this->_errors[] = format_string('The translation stream %uri contains an error: invalid format for "msgid" on line %line.', $log_vars, $log_vars);
$this->_errors[] = format_string('The translation stream %uri contains an error: invalid format for "msgid" on line %line.', $log_vars);
return FALSE;
}
$this->_current_item['msgid'] = $quoted;
$this->_context = 'MSGID';
return;
return NULL;
}
elseif (!strncmp('msgctxt', $line, 7)) {
// Starting a new context.
if (($this->_context == 'MSGSTR') || ($this->_context == 'MSGSTR_ARR')) {
// We are currently in string context, save current item.
$this->setItemFromArray($this->_current_item);
@@ -394,11 +391,10 @@ class PoStreamReader implements PoStreamInterface, PoReaderInterface {
$this->_current_item['msgctxt'] = $quoted;
$this->_context = 'MSGCTXT';
return;
return NULL;
}
elseif (!strncmp('msgstr[', $line, 7)) {
// A message string for a specific plurality.
if (($this->_context != 'MSGID') &&
($this->_context != 'MSGCTXT') &&
($this->_context != 'MSGID_PLURAL') &&
@@ -436,11 +432,10 @@ class PoStreamReader implements PoStreamInterface, PoReaderInterface {
$this->_current_item['msgstr'][$this->_current_plural_index] = $quoted;
$this->_context = 'MSGSTR_ARR';
return;
return NULL;
}
elseif (!strncmp("msgstr", $line, 6)) {
// A string pair for an msgidid (with optional context).
if (($this->_context != 'MSGID') && ($this->_context != 'MSGCTXT')) {
// Strings are only valid within an id or context scope.
$this->_errors[] = format_string('The translation stream %uri contains an error: "msgstr" is unexpected on line %line.', $log_vars);
@@ -461,11 +456,11 @@ class PoStreamReader implements PoStreamInterface, PoReaderInterface {
$this->_current_item['msgstr'] = $quoted;
$this->_context = 'MSGSTR';
return;
return NULL;
}
elseif ($line != '') {
// Anything that is not a token may be a continuation of a previous token.
// Anything that is not a token may be a continuation of a previous
// token.
$quoted = $this->parseQuoted($line);
if ($quoted === FALSE) {
// This string must be quoted.
@@ -502,7 +497,7 @@ class PoStreamReader implements PoStreamInterface, PoReaderInterface {
$this->_errors[] = format_string('The translation stream %uri contains an error: unexpected string on line %line.', $log_vars);
return FALSE;
}
return;
return NULL;
}
}
@@ -515,6 +510,8 @@ class PoStreamReader implements PoStreamInterface, PoReaderInterface {
$this->_errors[] = format_string('The translation stream %uri ended unexpectedly at line %line.', $log_vars);
return FALSE;
}
return NULL;
}
/**
@@ -524,8 +521,10 @@ class PoStreamReader implements PoStreamInterface, PoReaderInterface {
$plural = FALSE;
$comments = '';
$textgroup = 'default';
if (isset($value['#'])) {
$comments = $this->shortenComments($value['#']);
$textgroup = $this->fetchGroupFromComment($comments);
}
if (is_array($value['msgstr'])) {
@@ -541,6 +540,7 @@ class PoStreamReader implements PoStreamInterface, PoReaderInterface {
$item->setPlural($plural);
$item->setComment($comments);
$item->setLangcode($this->_langcode);
$item->setTextgroup($textgroup);
$this->_last_item = $item;
@@ -550,13 +550,13 @@ class PoStreamReader implements PoStreamInterface, PoReaderInterface {
/**
* Parses a string in quotes.
*
* @param $string
* @param string $string
* A string specified with enclosing quotes.
*
* @return
* @return string|FALSE
* The string parsed from inside the quotes.
*/
function parseQuoted($string) {
public function parseQuoted($string) {
if (substr($string, 0, 1) != substr($string, -1, 1)) {
// Start and end quotes must be the same.
return FALSE;
@@ -580,10 +580,10 @@ class PoStreamReader implements PoStreamInterface, PoReaderInterface {
/**
* Generates a short, one-string version of the passed comment array.
*
* @param $comment
* @param string|array $comment
* An array of strings containing a comment.
*
* @return
* @return string
* Short one-string version of the comment.
*/
private function shortenComments($comment) {
@@ -600,4 +600,27 @@ class PoStreamReader implements PoStreamInterface, PoReaderInterface {
return trim(substr($comm, 0, -2));
}
/**
* Determine a translation text group using a source's comment-string.
*
* @param string $comment
* Comment string.
*
* @return string
* The comment's text group.
*/
private function fetchGroupFromComment($comment) {
// Only if i18n_string is installed, check for and set textgroups.
if (module_exists('i18n_string') && strpos($comment, ':') !== FALSE) {
// Fetch available textgroups.
$groups = array_keys(i18n_string_group_info());
// Parse textgroup from comment (assume default drupal exports).
$comment_array = explode(':', $comment);
if (!empty($comment_array) && in_array($comment_array[0], $groups)) {
return $comment_array[0];
}
}
return 'default';
}
}

View File

@@ -23,7 +23,7 @@ interface PoWriterInterface extends PoMetadataInterface {
*
* @param PoReaderInterface $reader
* Reader to read PoItems from.
* @param $count
* @param int $count
* Amount of items to read from $reader to write. If -1, all items are
* read from $reader.
*/

View File

@@ -11,14 +11,14 @@
* The operations are related to pumping data from a source to a destination,
* for example:
* - Remote files http://*.po to memory
* - File public://*.po to database
* - File public://*.po to database.
*/
class Gettext {
/**
* Reads the given PO files into the database.
*
* @param stdClass $file
* @param object $file
* File object with an URI property pointing at the file's path.
* - "langcode": The language the strings will be added to.
* - "uri": File URI.
@@ -28,8 +28,8 @@ class Gettext {
* PoDatabaseWriter. Optional, defaults to an empty array.
* - 'customized': Flag indicating whether the strings imported from $file
* are customized translations or come from a community source. Use
* L10N_UPDATE_CUSTOMIZED or L10N_UPDATE_NOT_CUSTOMIZED. Optional, defaults to
* L10N_UPDATE_NOT_CUSTOMIZED.
* L10N_UPDATE_CUSTOMIZED or L10N_UPDATE_NOT_CUSTOMIZED. Optional,
* defaults to L10N_UPDATE_NOT_CUSTOMIZED.
* - 'seek': Specifies from which position in the file should the reader
* start reading the next items. Optional, defaults to 0.
* - 'items': Specifies the number of items to read. Optional, defaults to
@@ -38,9 +38,12 @@ class Gettext {
* @return array
* Report array as defined in PoDatabaseWriter.
*
* @throws \Exception
* Throws exception in case of missing header.
*
* @see PoDatabaseWriter
*/
static function fileToDatabase($file, $options) {
public static function fileToDatabase($file, array $options) {
// Add the default values to the options array.
$options += array(
'overwrite_options' => array(),
@@ -94,4 +97,5 @@ class Gettext {
$report['seek'] = $reader->getSeek();
return $report;
}
}

View File

@@ -40,6 +40,13 @@ class PoDatabaseReader implements PoReaderInterface {
*/
private $_result;
/**
* D7: Text group.
*
* @var string
*/
protected $_textgroup;
/**
* Database storage to retrieve the strings from.
*
@@ -50,7 +57,7 @@ class PoDatabaseReader implements PoReaderInterface {
/**
* Constructor, initializes with default options.
*/
function __construct() {
public function __construct() {
$this->setOptions(array());
$this->storage = new StringDatabaseStorage();
}
@@ -72,14 +79,14 @@ class PoDatabaseReader implements PoReaderInterface {
/**
* Get the options used by the reader.
*/
function getOptions() {
public function getOptions() {
return $this->_options;
}
/**
* Set the options for the current reader.
*/
function setOptions(array $options) {
public function setOptions(array $options) {
$options += array(
'customized' => FALSE,
'not_customized' => FALSE,
@@ -88,10 +95,17 @@ class PoDatabaseReader implements PoReaderInterface {
$this->_options = $options;
}
/**
* @param \string $textgroup
*/
public function setTextgroup($textgroup) {
$this->_textgroup = $textgroup;
}
/**
* Implements PoMetadataInterface::getHeader().
*/
function getHeader() {
public function getHeader() {
return new PoHeader($this->getLangcode());
}
@@ -101,7 +115,7 @@ class PoDatabaseReader implements PoReaderInterface {
* @throws Exception
* Always, because you cannot set the PO header of a reader.
*/
function setHeader(PoHeader $header) {
public function setHeader(PoHeader $header) {
throw new \Exception('You cannot set the PO header in a reader.');
}
@@ -111,7 +125,11 @@ class PoDatabaseReader implements PoReaderInterface {
private function loadStrings() {
$langcode = $this->_langcode;
$options = $this->_options;
$textgroup = $this->_textgroup;
$conditions = array();
if ($textgroup) {
$conditions['textgroup'] = $textgroup;
}
if (array_sum($options) == 0) {
// If user asked to not include anything in the translation files,
@@ -166,9 +184,9 @@ class PoDatabaseReader implements PoReaderInterface {
/**
* Implements PoReaderInterface::readItem().
*/
function readItem() {
public function readItem() {
if ($string = $this->readString()) {
$values = (array)$string;
$values = (array) $string;
$poItem = new PoItem();
$poItem->setFromArray($values);
return $poItem;

View File

@@ -47,7 +47,7 @@ class PoDatabaseWriter implements PoWriterInterface {
* - additions: number of source strings newly added
* - updates: number of translations updated
* - deletes: number of translations deleted
* - skips: number of strings skipped due to disallowed HTML
* - skips: number of strings skipped due to disallowed HTML.
*
* @var array
*/
@@ -63,7 +63,7 @@ class PoDatabaseWriter implements PoWriterInterface {
/**
* Constructor, initialize reporting array.
*/
function __construct() {
public function __construct() {
$this->setReport();
$this->storage = new StringDatabaseStorage();
}
@@ -95,7 +95,7 @@ class PoDatabaseWriter implements PoWriterInterface {
* @param array $report
* Associative array with result information.
*/
function setReport($report = array()) {
public function setReport($report = array()) {
$report += array(
'additions' => 0,
'updates' => 0,
@@ -109,14 +109,14 @@ class PoDatabaseWriter implements PoWriterInterface {
/**
* Get the options used by the writer.
*/
function getOptions() {
public function getOptions() {
return $this->_options;
}
/**
* Set the options for the current writer.
*/
function setOptions(array $options) {
public function setOptions(array $options) {
if (!isset($options['overwrite_options'])) {
$options['overwrite_options'] = array();
}
@@ -133,7 +133,7 @@ class PoDatabaseWriter implements PoWriterInterface {
/**
* Implements PoMetadataInterface::getHeader().
*/
function getHeader() {
public function getHeader() {
return $this->_header;
}
@@ -151,8 +151,9 @@ class PoDatabaseWriter implements PoWriterInterface {
* Header metadata.
*
* @throws Exception
* Exception is thrown when required properties are not set.
*/
function setHeader(PoHeader $header) {
public function setHeader(PoHeader $header) {
$this->_header = $header;
$languages = language_list();
@@ -193,12 +194,29 @@ class PoDatabaseWriter implements PoWriterInterface {
/**
* Implements PoWriterInterface::writeItem().
*/
function writeItem(PoItem $item) {
public function writeItem(PoItem $item) {
if ($item->isPlural()) {
$item->setSource(join(L10N_UPDATE_PLURAL_DELIMITER, $item->getSource()));
$item->setTranslation(join(L10N_UPDATE_PLURAL_DELIMITER, $item->getTranslation()));
$sources = $item->getSource();
$translations = $item->getTranslation();
// Build additional source strings for plurals.
$entries = array_keys($translations);
for ($i = 3; $i <= count($entries); $i++) {
$sources[] = $sources[1];
}
$translations = array_map('_locale_import_append_plural', $translations, $entries);
$sources = array_map('_locale_import_append_plural', $sources, $entries);
$plid = 0;
foreach ($entries as $index) {
$item->setSource($sources[$index]);
$item->setTranslation($translations[$index]);
$plid = $this->importString($item, $plid, $index);
}
}
else {
$this->importString($item);
}
$this->importString($item);
}
/**
@@ -216,11 +234,15 @@ class PoDatabaseWriter implements PoWriterInterface {
*
* @param PoItem $item
* The item being imported.
* @param integer $plid
* The parent string identifier for plural strings.
* @param integer $plural
* The plural index number.
*
* @return int
* The string ID of the existing string modified or the new string added.
*/
private function importString(PoItem $item) {
private function importString(PoItem $item, $plid = 0, $plural = 0) {
// Initialize overwrite options if not set.
$this->_options['overwrite_options'] += array(
'not_customized' => FALSE,
@@ -232,12 +254,14 @@ class PoDatabaseWriter implements PoWriterInterface {
$context = $item->getContext();
$source = $item->getSource();
$translation = $item->getTranslation();
$textgroup = $item->getTextgroup();
// Look up the source string and any existing translation.
$strings = $this->storage->getTranslations(array(
'language' => $this->_langcode,
'source' => $source,
'context' => $context
'context' => $context,
'textgroup' => $textgroup,
));
$string = reset($strings);
@@ -253,8 +277,10 @@ class PoDatabaseWriter implements PoWriterInterface {
if ($string->isNew()) {
// No translation in this language.
$string->setValues(array(
'plid' => $plid,
'plural' => $plural,
'language' => $this->_langcode,
'customized' => $customized
'customized' => $customized,
));
$string->save();
$this->_report['additions']++;
@@ -270,10 +296,12 @@ class PoDatabaseWriter implements PoWriterInterface {
}
else {
// No such source string in the database yet.
$string = $this->storage->createString(array('source' => $source, 'context' => $context))
$string = $this->storage->createString(array('source' => $source, 'context' => $context, 'textgroup' => $textgroup))
->save();
$target = $this->storage->createTranslation(array(
$this->storage->createTranslation(array(
'lid' => $string->getId(),
'plid' => $plid,
'plural' => $plural,
'language' => $this->_langcode,
'translation' => $translation,
'customized' => $customized,
@@ -292,5 +320,4 @@ class PoDatabaseWriter implements PoWriterInterface {
return $string->lid;
}
}
}

View File

@@ -13,6 +13,15 @@
* value, and is assumed to be in English language.
*/
class SourceString extends StringBase {
/**
* Implements StringInterface::getParentId().
*/
public function getParentId() {
// Source strings don't have a parent; Translations do.
return 0;
}
/**
* Implements StringInterface::isSource().
*/

View File

@@ -12,6 +12,7 @@
* the common properties and methods for source and translation strings.
*/
abstract class StringBase implements StringInterface {
/**
* The string identifier.
*
@@ -19,6 +20,20 @@ abstract class StringBase implements StringInterface {
*/
public $lid;
/**
* The parent string identifier for plural translations.
*
* @var integer
*/
public $plid;
/**
* Plural index in case of plural string.
*
* @var integer
*/
public $plural;
/**
* The string locations indexed by type.
*
@@ -40,6 +55,13 @@ abstract class StringBase implements StringInterface {
*/
public $context;
/**
* The string group.
*
* @var string
*/
public $textgroup;
/**
* The string version.
*
@@ -61,7 +83,7 @@ abstract class StringBase implements StringInterface {
* Object or array with initial values.
*/
public function __construct($values = array()) {
$this->setValues((array)$values);
$this->setValues((array) $values);
}
/**
@@ -79,6 +101,21 @@ abstract class StringBase implements StringInterface {
return $this;
}
/**
* Implements StringInterface::getParentId().
*/
public function getParentId() {
return isset($this->plid) ? $this->plid : 0;
}
/**
* Implements StringInterface::setParentId().
*/
public function setParentId($plid) {
$this->plid = $plid;
return $this;
}
/**
* Implements StringInterface::getVersion().
*/
@@ -94,21 +131,6 @@ abstract class StringBase implements StringInterface {
return $this;
}
/**
* Implements StringInterface::getPlurals().
*/
public function getPlurals() {
return explode(L10N_UPDATE_PLURAL_DELIMITER, $this->getString());
}
/**
* Implements StringInterface::setPlurals().
*/
public function setPlurals($plurals) {
$this->setString(implode(L10N_UPDATE_PLURAL_DELIMITER, $plurals));
return $this;
}
/**
* Implements StringInterface::getStorage().
*/
@@ -119,7 +141,7 @@ abstract class StringBase implements StringInterface {
/**
* Implements StringInterface::setStorage().
*/
public function setStorage($storage) {
public function setStorage(StringStorageInterface $storage) {
$this->storage = $storage;
return $this;
}
@@ -149,6 +171,20 @@ abstract class StringBase implements StringInterface {
return $values;
}
/**
* Implements StringInterface::getTextgroup().
*/
public function getTextgroup() {
return empty($this->textgroup) ? 'default' : $this->textgroup;
}
/**
* Implements StringInterface::setTextgroup().
*/
public function setTextgroup($textgroup) {
$this->textgroup = $textgroup;
}
/**
* Implements LocaleString::save().
*/
@@ -158,7 +194,7 @@ abstract class StringBase implements StringInterface {
}
else {
throw new StringStorageException(format_string('The string cannot be saved because its not bound to a storage: @string', array(
'@string' => $this->getString()
'@string' => $this->getString(),
)));
}
return $this;
@@ -174,7 +210,7 @@ abstract class StringBase implements StringInterface {
}
else {
throw new StringStorageException(format_string('The string cannot be deleted because its not bound to a storage: @string', array(
'@string' => $this->getString()
'@string' => $this->getString(),
)));
}
}

View File

@@ -91,7 +91,7 @@ class StringDatabaseStorage implements StringStorageInterface {
/**
* Implements StringStorageInterface::save().
*/
public function save($string) {
public function save(\StringInterface $string) {
if ($string->isNew()) {
$result = $this->dbStringInsert($string);
if ($string->isSource() && $result) {
@@ -114,20 +114,20 @@ class StringDatabaseStorage implements StringStorageInterface {
* @param string $version
* Drupal version to check against.
*/
protected function checkVersion($string, $version) {
protected function checkVersion(StringInterface $string, $version) {
if ($string->getId() && $string->getVersion() != $version) {
$string->setVersion($version);
db_update('locales_source', $this->options)
->condition('lid', $string->getId())
->fields(array('version' => $version))
->execute();
->condition('lid', $string->getId())
->fields(array('version' => $version))
->execute();
}
}
/**
* Implements StringStorageInterface::delete().
*/
public function delete($string) {
public function delete(\StringInterface $string) {
if ($keys = $this->dbStringKeys($string)) {
$this->dbDelete('locales_target', $keys)->execute();
if ($string->isSource()) {
@@ -138,7 +138,7 @@ class StringDatabaseStorage implements StringStorageInterface {
}
else {
throw new StringStorageException(format_string('The string cannot be deleted because it lacks some key fields: @string', array(
'@string' => $string->getString()
'@string' => $string->getString(),
)));
}
return $this;
@@ -147,19 +147,19 @@ class StringDatabaseStorage implements StringStorageInterface {
/**
* Implements StringStorageInterface::deleteLanguage().
*/
public function deleteStrings($conditions) {
public function deleteStrings(array $conditions) {
$lids = $this->dbStringSelect($conditions, array('fields' => array('lid')))->execute()->fetchCol();
if ($lids) {
$this->dbDelete('locales_target', array('lid' => $lids))->execute();
$this->dbDelete('locales_source', array('lid' => $lids))->execute();
$this->dbDelete('locales_location', array('sid' => $lids))->execute();
$this->dbDelete('locales_source', array('lid' => $lids))->execute();
$this->dbDelete('locales_location', array('sid' => $lids))->execute();
}
}
/**
* Implements StringStorageInterface::deleteLanguage().
*/
public function deleteTranslations($conditions) {
public function deleteTranslations(array $conditions) {
$this->dbDelete('locales_target', $conditions)->execute();
}
@@ -176,7 +176,7 @@ class StringDatabaseStorage implements StringStorageInterface {
public function createTranslation($values = array()) {
return new TranslationString($values + array(
'storage' => $this,
'is_new' => TRUE
'is_new' => TRUE,
));
}
@@ -191,7 +191,7 @@ class StringDatabaseStorage implements StringStorageInterface {
* target or location table table.
*/
protected function dbFieldTable($field) {
if (in_array($field, array('language', 'translation', 'customized'))) {
if (in_array($field, array('language', 'translation', 'l10n_status'))) {
return 't';
}
elseif (in_array($field, array('type', 'name'))) {
@@ -211,7 +211,7 @@ class StringDatabaseStorage implements StringStorageInterface {
* @return string
* The table name.
*/
protected function dbStringTable($string) {
protected function dbStringTable(StringInterface $string) {
if ($string->isSource()) {
return 'locales_source';
}
@@ -229,7 +229,7 @@ class StringDatabaseStorage implements StringStorageInterface {
* @return array
* Array with key fields if the string has all keys, or empty array if not.
*/
protected function dbStringKeys($string) {
protected function dbStringKeys(StringInterface $string) {
if ($string->isSource()) {
$keys = array('lid');
}
@@ -285,6 +285,7 @@ class StringDatabaseStorage implements StringStorageInterface {
* ones:
* - 'translation', Whether to include translation fields too. Defaults to
* FALSE.
*
* @return SelectQuery
* Query object with all the tables, fields and conditions.
*/
@@ -295,10 +296,6 @@ class StringDatabaseStorage implements StringStorageInterface {
$conditions['l10n_status'] = $conditions['customized'];
unset($conditions['customized']);
}
if (isset($options['customized'])) {
$options['l10n_status'] = $options['customized'];
unset($options['customized']);
}
// Start building the query with source table and check whether we need to
// join the target table too.
$query = db_select('locales_source', 's', $this->options)
@@ -326,7 +323,7 @@ class StringDatabaseStorage implements StringStorageInterface {
if (isset($conditions['language'])) {
// If we've got a language condition, we use it for the join.
$query->$join('locales_target', 't', "t.lid = s.lid AND t.language = :langcode", array(
':langcode' => $conditions['language']
':langcode' => $conditions['language'],
));
unset($conditions['language']);
}
@@ -336,6 +333,8 @@ class StringDatabaseStorage implements StringStorageInterface {
}
if (!empty($options['translation'])) {
// We cannot just add all fields because 'lid' may get null values.
$query->addField('t', 'plid');
$query->addField('t', 'plural');
$query->addField('t', 'language');
$query->addField('t', 'translation');
$query->addField('t', 'l10n_status', 'customized');
@@ -399,7 +398,7 @@ class StringDatabaseStorage implements StringStorageInterface {
}
/**
* Createds a database record for a string object.
* Creates a database record for a string object.
*
* @param StringInterface $string
* The string object.
@@ -411,14 +410,15 @@ class StringDatabaseStorage implements StringStorageInterface {
* @throws StringStorageException
* If the string is not suitable for this storage, an exception ithrown.
*/
protected function dbStringInsert($string) {
protected function dbStringInsert(StringInterface $string) {
if ($string->isSource()) {
$string->setValues(array('context' => '', 'version' => 'none'), FALSE);
$fields = $string->getValues(array('source', 'context', 'version'));
$fields = $string->getValues(array('source', 'context', 'version', 'textgroup'));
// @todo Add support for D7 fields 'location' and 'textgroup'.
}
elseif ($string->isTranslation()) {
$string->setValues(array('customized' => 0), FALSE);
$fields = $string->getValues(array('lid', 'language', 'translation', 'customized'));
$fields = $string->getValues(array('lid', 'language', 'translation', 'plid', 'plural', 'customized'));
}
if (!empty($fields)) {
// Change field 'customized' into 'l10n_status'. This enables the Drupal 8
@@ -434,7 +434,7 @@ class StringDatabaseStorage implements StringStorageInterface {
}
else {
throw new StringStorageException(format_string('The string cannot be saved: @string', array(
'@string' => $string->getString()
'@string' => $string->getString(),
)));
}
}
@@ -452,7 +452,7 @@ class StringDatabaseStorage implements StringStorageInterface {
* @throws StringStorageException
* If the string is not suitable for this storage, an exception is thrown.
*/
protected function dbStringUpdate($string) {
protected function dbStringUpdate(StringInterface $string) {
if ($string->isSource()) {
$values = $string->getValues(array('source', 'context', 'version'));
}
@@ -478,7 +478,7 @@ class StringDatabaseStorage implements StringStorageInterface {
}
else {
throw new StringStorageException(format_string('The string cannot be updated: @string', array(
'@string' => $string->getString()
'@string' => $string->getString(),
)));
}
}
@@ -494,7 +494,7 @@ class StringDatabaseStorage implements StringStorageInterface {
* @return DeleteQuery
* Returns a new DeleteQuery object for the active database.
*/
protected function dbDelete($table, $keys) {
protected function dbDelete($table, array $keys) {
$query = db_delete($table, $this->options);
// Change field 'customized' into 'l10n_status'. This enables the Drupal 8
// backported code to work with the Drupal 7 style database tables.
@@ -515,4 +515,5 @@ class StringDatabaseStorage implements StringStorageInterface {
protected function dbExecute($query, array $args = array()) {
return db_query($query, $args, $this->options);
}
}

View File

@@ -24,11 +24,30 @@ interface StringInterface {
* @param int $id
* The string identifier.
*
* @return LocaleString
* @return StringInterface
* The called object.
*/
public function setId($id);
/**
* Gets the parent string identifier.
*
* @return int
* The string identifier.
*/
public function getParentId();
/**
* Sets the parent string identifier.
*
* @param int $id
* The string identifier.
*
* @return StringInterface
* The called object.
*/
public function setParentId($id);
/**
* Gets the string version.
*
@@ -43,7 +62,7 @@ interface StringInterface {
* @param string $version
* Version identifier.
*
* @return LocaleString
* @return StringInterface
* The called object.
*/
public function setVersion($version);
@@ -62,31 +81,10 @@ interface StringInterface {
* @param string $string
* String to set as value.
*
* @return LocaleString
* @return StringInterface
* The called object.
*/
public function setString($string);
/**
* Splits string to work with plural values.
*
* @return array
* Array of strings that are plural variants.
*/
public function getPlurals();
/**
* Sets this string using array of plural values.
*
* Serializes plural variants in one string glued by L10N_UPDATE_PLURAL_DELIMITER.
*
* @param array $plurals
* Array of strings with plural variants.
*
* @return LocaleString
* The called object.
*/
public function setPlurals($plurals);
public function setString($string);
/**
* Gets the string storage.
@@ -102,10 +100,10 @@ interface StringInterface {
* @param StringStorageInterface $storage
* The storage to use for this string.
*
* @return LocaleString
* @return StringInterface
* The called object.
*/
public function setStorage($storage);
*/
public function setStorage(StringStorageInterface $storage);
/**
* Checks whether the object is not saved to storage yet.
@@ -135,11 +133,11 @@ interface StringInterface {
* Sets an array of values as object properties.
*
* @param array $values
* Array with values indexed by property name,
* Array with values indexed by property name.
* @param bool $override
* (optional) Whether to override already set fields, defaults to TRUE.
*
* @return LocaleString
* @return StringInterface
* The called object.
*/
public function setValues(array $values, $override = TRUE);
@@ -158,7 +156,7 @@ interface StringInterface {
/**
* Saves string object to storage.
*
* @return LocaleString
* @return StringInterface
* The called object.
*
* @throws StringStorageException
@@ -169,7 +167,7 @@ interface StringInterface {
/**
* Deletes string object from storage.
*
* @return LocaleString
* @return StringInterface
* The called object.
*
* @throws StringStorageException
@@ -177,4 +175,20 @@ interface StringInterface {
*/
public function delete();
/**
* Get the translation group of this translation.
*
* @return string
* The textgroup set for the current string
*/
public function getTextgroup();
/**
* Set the translation group of this translation.
*
* @param string $textgroup
* The text group to set for the given string.
*/
public function setTextgroup($textgroup);
}

View File

@@ -8,4 +8,4 @@
/**
* Defines an exception thrown when storage operations fail.
*/
class StringStorageException extends \Exception { }
class StringStorageException extends \Exception {}

View File

@@ -93,7 +93,7 @@ interface StringStorageInterface {
* @throws \StringStorageException
* In case of failures, an exception is thrown.
*/
public function save($string);
public function save(\StringInterface $string);
/**
* Delete string from storage.
@@ -107,7 +107,7 @@ interface StringStorageInterface {
* @throws \StringStorageException
* In case of failures, an exception is thrown.
*/
public function delete($string);
public function delete(\StringInterface $string);
/**
* Deletes source strings and translations using conditions.
@@ -115,7 +115,7 @@ interface StringStorageInterface {
* @param array $conditions
* Array with simple field conditions for source strings.
*/
public function deleteStrings($conditions);
public function deleteStrings(array $conditions);
/**
* Deletes translations using conditions.
@@ -123,7 +123,7 @@ interface StringStorageInterface {
* @param array $conditions
* Array with simple field conditions for string translations.
*/
public function deleteTranslations($conditions);
public function deleteTranslations(array $conditions);
/**
* Counts source strings.
@@ -162,4 +162,5 @@ interface StringStorageInterface {
* New string translation object.
*/
public function createTranslation($values = array());
}

View File

@@ -14,6 +14,7 @@
* in the specified language.
*/
class TranslationString extends StringBase {
/**
* The language code.
*
@@ -31,7 +32,7 @@ class TranslationString extends StringBase {
/**
* Integer indicating whether this string is customized.
*
* @var int
* @var integer
*/
public $customized;
@@ -78,7 +79,7 @@ class TranslationString extends StringBase {
/**
* Implements StringInterface::isTranslation().
*/
*/
public function isTranslation() {
return !empty($this->lid) && !empty($this->language) && isset($this->translation);
}

View File

@@ -12,7 +12,7 @@
*/
class TranslationsStreamWrapper extends DrupalLocalStreamWrapper {
/**
* Implements abstract public function getDirectoryPath()
* Implements abstract public function getDirectoryPath().
*/
public function getDirectoryPath() {
return variable_get('l10n_update_download_store', L10N_UPDATE_DEFAULT_TRANSLATION_PATH);
@@ -21,7 +21,8 @@ class TranslationsStreamWrapper extends DrupalLocalStreamWrapper {
/**
* Overrides getExternalUrl().
*/
function getExternalUrl() {
public function getExternalUrl() {
throw new Exception('PO files URL should not be public.');
}
}

View File

@@ -1,37 +1,43 @@
(function ($) {
/**
* Show/hide the description details on Available translation updates page.
* @file
* JavaScript for the translate interface update page.
*/
Drupal.behaviors.hideUpdateInformation = {
attach: function (context, settings) {
var $table = $('#l10n-update-status-form').once('expand-updates');
if ($table.length) {
var $tbodies = $table.find('tbody');
// Open/close the description details by toggling a tr class.
$tbodies.find('.description').bind('click keydown', function (e) {
if (e.keyCode && (e.keyCode !== 13 && e.keyCode !== 32)) {
return;
}
e.preventDefault();
var $tr = $(this).closest('tr');
(function ($) {
'use strict';
$tr.toggleClass('expanded');
/**
* Show/hide the description details on Translate interface update page.
*/
Drupal.behaviors.hideUpdateInformation = {
attach: function (context, settings) {
var $table = $('#l10n-update-status-form').once('expand-updates');
if ($table.length) {
var $tbodies = $table.find('tbody');
// Change screen reader text.
$tr.find('.update-description-prefix').text(function () {
if ($tr.hasClass('expanded')) {
return Drupal.t('Hide description');
}
else {
return Drupal.t('Show description');
// Open/close the description details by toggling a tr class.
$tbodies.find('.description').bind('click keydown', function (e) {
if (e.keyCode && (e.keyCode !== 13 && e.keyCode !== 32)) {
return;
}
e.preventDefault();
var $tr = $(this).closest('tr');
$tr.toggleClass('expanded');
// Change screen reader text.
$tr.find('.update-description-prefix').text(function () {
if ($tr.hasClass('expanded')) {
return Drupal.t('Hide description');
}
else {
return Drupal.t('Show description');
}
});
});
});
$table.find('.requirements, .links').hide();
$table.find('.requirements, .links').hide();
}
}
}
};
};
})(jQuery);

View File

@@ -1,17 +1,18 @@
<?php
/**
* @file
* Default theme implementation for the last time we checked for update data.
*
* Available variables:
* - $last_checked: User interface string with the formatted time ago when the
* @file
* Default theme implementation for the last time we checked for update data.
*
* Available variables:
* - $last_checked: User interface string with the formatted time ago when the
* site last checked for available updates.
* - $link: A link to manually check available updates.
*
* @see template_preprocess_l10n_update_last_check()
*
* @ingroup themeable
*/
* - $link: A link to manually check available updates.
*
* @see template_preprocess_l10n_update_last_check()
*
* @ingroup themeable
*/
?>
<div class="l10n_update-checked">
<p><?php print $last_checked; ?> <span class="check-manually">(<?php print $link; ?>)</span></p>

View File

@@ -1,22 +1,23 @@
<?php
/**
* @file
* Default theme implementation for displaying translation status information.
*
* Displays translation status information per language.
*
* Available variables:
* - module_list: A list of names of modules that have available translation
* updates.
* - details: Rendered list of the translation details.
* - missing_updates_status: If there are any modules that are missing
* translation updates, this variable will contain text indicating how many
* modules are missing translations.
*
* @see template_preprocess_l10n_update_update_info()
*
* @ingroup themeable
*/
* @file
* Default theme implementation for displaying translation status information.
*
* Displays translation status information per language.
*
* Available variables:
* - module_list: A list of names of modules that have available translation
* updates.
* - details: Rendered list of the translation details.
* - missing_updates_status: If there are any modules that are missing
* translation updates, this variable will contain text indicating how many
* modules are missing translations.
*
* @see template_preprocess_l10n_update_update_info()
*
* @ingroup themeable
*/
?>
<div class="inner" tabindex="0" role="button">
<span class="update-description-prefix visually-hidden">Show description</span>

View File

@@ -2,7 +2,7 @@
/**
* @file
* Admin settings and update page.
* Admin settings and update page.
*/
/**
@@ -37,7 +37,7 @@ function l10n_update_status_form() {
$projects_update = array();
// @todo Calling l10n_update_build_projects() is an expensive way to
// get a module name. In follow-up issue http://drupal.org/node/1842362
// get a module name. In follow-up issue https://www.drupal.org/node/1842362
// the project name will be stored to display use, like here.
$project_data = l10n_update_build_projects();
$languages = l10n_update_translatable_language_list();
@@ -59,12 +59,13 @@ function l10n_update_status_form() {
$languages_not_found[$langcode] = $langcode;
}
// Translation update found for this project-language combination.
elseif ($project_info->type == L10N_UPDATE_LOCAL || $project_info->type == L10N_UPDATE_REMOTE ) {
elseif ($project_info->type == L10N_UPDATE_LOCAL || $project_info->type == L10N_UPDATE_REMOTE) {
$local = isset($project_info->files[L10N_UPDATE_LOCAL]) ? $project_info->files[L10N_UPDATE_LOCAL] : NULL;
$remote = isset($project_info->files[L10N_UPDATE_REMOTE]) ? $project_info->files[L10N_UPDATE_REMOTE] : NULL;
$recent = _l10n_update_source_compare($local, $remote) == L10N_UPDATE_SOURCE_COMPARE_LT ? $remote : $local;
$name = isset($project_data[$project_info->name]->info['name']) ? $project_data[$project_info->name]->info['name'] : '';
$updates[$langcode]['updates'][] = array(
'name' => $project_info->name == 'drupal' ? t('Drupal core') : $project_data[$project_info->name]->info['name'],
'name' => $project_info->name == 'drupal' ? t('Drupal core') : $name,
'version' => $project_info->version,
'timestamp' => $recent->timestamp,
);
@@ -77,7 +78,7 @@ function l10n_update_status_form() {
$languages_not_found = array_diff($languages_not_found, $languages_update);
// Build data options for the select table.
foreach($updates as $langcode => $update) {
foreach ($updates as $langcode => $update) {
$title = check_plain($languages[$langcode]);
$l10n_update_update_info = array('#theme' => 'l10n_update_update_info');
foreach (array('updates', 'not_found') as $update_status) {
@@ -90,10 +91,17 @@ function l10n_update_status_form() {
'class' => array('label'),
'data' => array(
'#title' => $title,
'#markup' => $title
'#markup' => $title,
),
),
'status' => array('class' => array('description', 'expand', 'priority-low'), 'data' => drupal_render($l10n_update_update_info)),
'status' => array(
'class' => array(
'description',
'expand',
'priority-low',
),
'data' => drupal_render($l10n_update_update_info),
),
);
}
// Sort the table data on language name.
@@ -125,7 +133,7 @@ function l10n_update_status_form() {
elseif (empty($options)) {
$empty = t('All translations up to date.');
}
else {
else {
$empty = t('No translation status available. <a href="@check">Check manually</a>.', array('@check' => url('admin/config/regional/translate/check')));
}
@@ -261,6 +269,31 @@ function l10n_update_admin_settings_form($form, &$form_state) {
'#description' => t('How to treat existing translations when automatically updating the interface translations.'),
);
$form['disable_update'] = array(
'#type' => 'fieldset',
'#title' => t('Disable update'),
'#collapible' => FALSE,
'#collapsed' => FALSE,
);
$form['disable_update']['disabled_projects'] = array(
'#type' => 'textarea',
'#title' => t('Projects'),
'#default_value' => implode(PHP_EOL, variable_get('l10n_update_disabled_projects', array())),
'#description' => t("These modules, themes or profiles will not receive interface translation updates. Specify them by there machine name, enter one name per line. The '*' character is a wildcard. Use for example feature_* for every feature."),
);
$languages = locale_language_list('name');
unset($languages['en']);
$form['disable_update']['l10n_update_disabled_languages'] = array(
'#type' => 'checkboxes',
'#title' => t('Languages'),
'#options' => $languages,
'#default_value' => variable_get('l10n_update_disabled_languages', array()),
'#description' => t('The selected languages will not receive interface translation updates.'),
);
$form['#submit'][] = 'l10n_update_admin_settings_form_submit';
return system_settings_form($form);
}
@@ -270,7 +303,14 @@ function l10n_update_admin_settings_form($form, &$form_state) {
*/
function l10n_update_admin_settings_form_validate($form, &$form_state) {
// Check for existing translations directory and create one if required.
// When using local sources, only check if the directory exists.
$directory = $form_state['values']['l10n_update_download_store'];
$directory = rtrim($directory, '/\\');
if ($form_state['values']['l10n_update_check_mode'] == L10N_UPDATE_USE_SOURCE_LOCAL
&& is_dir($directory)) {
return;
}
if (!file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
form_set_error('l10n_update_download_store', t('The directory %directory does not exist or is not writable.', array('%directory' => $directory)));
watchdog('file system', 'The directory %directory does not exist or is not writable.', array('%directory' => $directory), WATCHDOG_ERROR);
@@ -280,13 +320,22 @@ function l10n_update_admin_settings_form_validate($form, &$form_state) {
/**
* Submit handler for translation update settings.
*/
function l10n_update_admin_settings_form_submit($form, $form_state) {
function l10n_update_admin_settings_form_submit($form, &$form_state) {
// Invalidate the cached translation status when the configuration setting of
// 'l10n_update_check_mode' or 'check_disabled' change.
if ($form['l10n_update_check_mode']['#default_value'] != $form_state['values']['l10n_update_check_mode'] ||
$form['l10n_update_check_disabled']['#default_value'] != $form_state['values']['l10n_update_check_disabled']) {
l10n_update_clear_status();
}
// Convert the disabled projects input into an array value. The input value is
// removed from the form_state values to prevent it being turned into a
// variable.
$input = $form_state['values']['disabled_projects'];
unset($form_state['values']['disabled_projects']);
$input = strtr($input, array("\r" => '', ' ' => ''));
$values = array_filter(explode("\n", $input));
variable_set('l10n_update_disabled_projects', $values);
}
/**
@@ -295,7 +344,7 @@ function l10n_update_admin_settings_form_submit($form, $form_state) {
* The import options of the Locale module are used but the UI text is altered
* to suit the Localization update cases.
*
* @return
* @return array
* Keyed array of import options.
*/
function _l10n_update_admin_import_options() {
@@ -318,8 +367,8 @@ function _l10n_update_admin_import_options() {
* Translations for development versions are never fetched, so the debug info
* for that is a fixed message.
*
* @param array $source
* An array which is the project information of the source.
* @param object $source
* An object which is the project information of the source.
*
* @return string
* The string which contains debug information.
@@ -366,10 +415,14 @@ function l10n_update_language_table($form_element) {
* An associative array containing:
* - form: The form that contains the language information.
*
* @return string
* HTML output.
*
* @see l10n_update_edit_form()
*
* @ingroup themeable
*/
function theme_l10n_update_edit_form_strings($variables) {
function theme_l10n_update_edit_form_strings(array $variables) {
$output = '';
$form = $variables['form'];
$header = array(
@@ -418,11 +471,11 @@ function theme_l10n_update_edit_form_strings($variables) {
*
* @see l10n_update_status_form()
*/
function template_preprocess_l10n_update_update_info(&$variables) {
function template_preprocess_l10n_update_update_info(array &$variables) {
$details = array();
$modules = array();
// Default values
// Default values.
$variables['modules'] = array();
$variables['module_list'] = '';
$details['available_updates_list'] = array();
@@ -433,7 +486,10 @@ function template_preprocess_l10n_update_update_info(&$variables) {
if ($variables['updates']) {
foreach ($variables['updates'] as $update) {
$modules[] = $update['name'];
$releases[] = t('@module (@date)', array('@module' => $update['name'], '@date' => format_date($update['timestamp'], 'html_date')));
$releases[] = t('@module (@date)', array(
'@module' => $update['name'],
'@date' => format_date($update['timestamp'], 'html_date'),
));
}
$variables['modules'] = $modules;
$variables['module_list'] = t('Updates for: @modules', array('@modules' => implode(', ', $modules)));
@@ -477,13 +533,13 @@ function template_preprocess_l10n_update_update_info(&$variables) {
*
* Default template: l10n_update-translation-last-check.tpl.php.
*
* @param $variables
* @param array $variables
* An associative array containing:
* - last: The timestamp when the site last checked for available updates.
*
* @see l10n_update_status_form()
*/
function template_preprocess_l10n_update_last_check(&$variables) {
function template_preprocess_l10n_update_last_check(array &$variables) {
$last = $variables['last'];
$variables['last_checked'] = $last ? t('Last checked: !time ago', array('!time' => format_interval(REQUEST_TIME - $last))) : t('Last checked: never');
$variables['link'] = l(t('Check manually'), 'admin/config/regional/translate/check');

View File

@@ -2,36 +2,46 @@
/**
* @file
* API documentation for Localize updater module.
* API documentation for Localize updater module.
*/
/**
* Alter the list of project to be updated by l10n update.
* Alter the list of project to be updated by Localization Update.
*
* l10n_update uses the same list of projects as update module. Using this hook
* the list can be altered.
* L10n_update uses the same list of projects as update module. Using this hook
* the list can be altered. This hook is typically used to alter the following
* values from the .info file:
* - interface translation project
* - l10n path.
*
* @param array $projects
* Array of projects.
*/
function hook_l10n_update_projects_alter(&$projects) {
// The $projects array contains the project data produced by
// update_get_projects(). A number of the array elements are described in
// the documentation of hook_update_projects_alter().
function hook_l10n_update_projects_alter(array &$projects) {
// In the .info file of a project a localization server can be specified.
// Using this hook the localization server specification can be altered or
// added. The 'l10n path' element is optional but can be specified to override
// the translation download path specified in the 10n_server.xml file.
$projects['existing_example_project'] = array(
'info' => array(
'l10n path' => 'http://example.com/files/translations/%core/%project/%project-%release.%language.po',
),
);
foreach (array_keys($projects) as $name) {
// @todo These 'interface translation project' examples are good for system_projects_alter.
// Make all custom_* modules use the 'custom_module' module translation
// file.
if (strpos($name, 'custom_') === 0) {
$projects[$name]['info']['interface translation project'] = 'custom_module';
}
// With this hook it is also possible to add a new project wich does not
// Disable interface translation updates for all features.
if (strpos($name, 'feature_') === 0) {
$projects[$name]['info']['interface translation project'] = FALSE;
}
}
// Set the path to the custom module translation files if not already set.
if (isset($projects['custom_module']) && empty($projects['custom_module']['info']['l10n path'])) {
$path = drupal_get_path('module', 'custom_module');
$projects['custom_module']['info']['l10n path'] = $path . '/translations/%language.po';
}
// With this hook it is also possible to add a new project which does not
// exist as a real module or theme project but is treated by the localization
// update module as one. The below data is the minumum to be specified.
// update module as one. The below data is the minimum to be specified.
// As in the previous example the 'l10n path' element is optional.
$projects['new_example_project'] = array(
'project_type' => 'module',
@@ -44,3 +54,17 @@ function hook_l10n_update_projects_alter(&$projects) {
),
);
}
/**
* Alter the list of languages to be updated by Localization Update.
*
* @param array $langcodes
* Language codes of languages to be updated by Localization Update module.
*/
function hook_l10n_update_languages_alter(array &$langcodes) {
// Use a different language code for Dutch translations.
if (isset($langcodes['nl'])) {
$langcodes['nl-NL'] = $langcodes['nl'];
unset($langcodes['nl']);
}
}

View File

@@ -2,14 +2,14 @@
/**
* @file
* Batch process to check the availability of remote or local po files.
* Batch process to check the availability of remote or local po files.
*/
/**
* Load the common translation API.
*/
// @todo Combine functions differently in files to avoid unnecessary includes.
// Follow-up issue http://drupal.org/node/1834298
// Follow-up issue https://www.drupal.org/node/1834298
require_once __DIR__ . '/l10n_update.translation.inc';
/**
@@ -30,7 +30,7 @@ require_once __DIR__ . '/l10n_update.translation.inc';
* Optional, defaults to TRUE.
* @param array $context
* The batch context.
*/
*/
function l10n_update_batch_status_check($project, $langcode, $options = array(), &$context) {
$failure = $checked = FALSE;
$options += array(
@@ -84,21 +84,21 @@ function l10n_update_batch_status_check($project, $langcode, $options = array(),
/**
* Batch finished callback: Set result message.
*
* @param boolean $success
* @param bool $success
* TRUE if batch successfully completed.
* @param array $results
* Batch results.
*/
function l10n_update_batch_status_finished($success, $results) {
function l10n_update_batch_status_finished($success, array $results) {
if ($success) {
if (isset($results['failed_files'])) {
if (module_exists('dblog')) {
$message = format_plural(count($results['failed_files']), 'One translation file could not be checked. <a href="@url">See the log</a> for details.', '@count translation files could not be checked. <a href="@url">See the log</a> for details.', array('@url' => url('admin/reports/dblog')));
}
else {
$message = format_plural(count($results['failed_files']), 'One translation files could not be checked. See the log for details.', '@count translation files could not be checked. See the log for details.');
}
drupal_set_message($message, 'error');
if (module_exists('dblog')) {
$message = format_plural(count($results['failed_files']), 'One translation file could not be checked. <a href="@url">See the log</a> for details.', '@count translation files could not be checked. <a href="@url">See the log</a> for details.', array('@url' => url('admin/reports/dblog')));
}
else {
$message = format_plural(count($results['failed_files']), 'One translation files could not be checked. See the log for details.', '@count translation files could not be checked. See the log for details.');
}
drupal_set_message($message, 'error');
}
if (isset($results['files'])) {
drupal_set_message(format_plural(
@@ -166,7 +166,7 @@ function l10n_update_batch_fetch_download($project, $langcode, &$context) {
* @see l10n_update_batch_import_files()
* @see l10n_update_batch_fetch_download()
*/
function l10n_update_batch_fetch_import($project, $langcode, $options, &$context) {
function l10n_update_batch_fetch_import($project, $langcode, array $options, &$context) {
$sources = l10n_update_get_status(array($project), array($langcode));
if (isset($sources[$project][$langcode])) {
$source = $sources[$project][$langcode];
@@ -187,8 +187,8 @@ function l10n_update_batch_fetch_import($project, $langcode, $options, &$context
if (isset($context['results']['files'][$file->uri])) {
$context['message'] = t('Imported translation for %project.', array('%project' => $source->project));
// Save the data of imported source into the {l10n_update_file} table and
// update the current translation status.
// Save the data of imported source into the {l10n_update_file}
// table and update the current translation status.
l10n_update_status_save($project, $langcode, L10N_UPDATE_CURRENT, $source->files[L10N_UPDATE_LOCAL]);
}
}
@@ -200,12 +200,12 @@ function l10n_update_batch_fetch_import($project, $langcode, $options, &$context
/**
* Batch finished callback: Set result message.
*
* @param boolean $success
* @param bool $success
* TRUE if batch successfully completed.
* @param array
* @param array $results
* Batch results.
*/
function l10n_update_batch_fetch_finished($success, $results) {
function l10n_update_batch_fetch_finished($success, array $results) {
module_load_include('bulk.inc', 'l10n_update');
if ($success) {
variable_set('l10n_update_last_check', REQUEST_TIME);
@@ -213,8 +213,6 @@ function l10n_update_batch_fetch_finished($success, $results) {
l10n_update_batch_finished($success, $results);
}
/**
* Downloads a translation file from a remote server.
*
@@ -229,12 +227,12 @@ function l10n_update_batch_fetch_finished($success, $results) {
* Directory where the downloaded file will be saved. Defaults to the
* temporary file path.
*
* @return object
* @return object|bool
* File object if download was successful. FALSE on failure.
*/
function l10n_update_download_source($source_file, $directory = 'temporary://') {
if ($uri = system_retrieve_file($source_file->uri, $directory)) {
$file = clone($source_file);
if ($uri = system_retrieve_file($source_file->uri, $directory, FALSE, FILE_EXISTS_REPLACE)) {
$file = clone $source_file;
$file->type = L10N_UPDATE_LOCAL;
$file->uri = $uri;
$file->directory = $directory;

View File

@@ -9,6 +9,7 @@
* Form constructor for the translation import screen.
*
* @see l10n_update_import_form_submit()
*
* @ingroup forms
*/
function l10n_update_import_form($form, &$form_state) {
@@ -36,7 +37,7 @@ function l10n_update_import_form($form, &$form_state) {
$default = key($existing_languages);
$language_options = array(
t('Existing languages') => $existing_languages,
t('Languages not yet added') => language_admin_predefined_list()
t('Languages not yet added') => language_admin_predefined_list(),
);
}
@@ -88,11 +89,11 @@ function l10n_update_import_form($form, &$form_state) {
);
$form['actions'] = array(
'#type' => 'actions'
'#type' => 'actions',
);
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => t('Import')
'#value' => t('Import'),
);
return $form;
}
@@ -108,7 +109,7 @@ function l10n_update_import_form_submit($form, &$form_state) {
$language = language_load($form_state['values']['langcode']);
if (empty($language)) {
$language = new Language(array(
'id' => $form_state['values']['langcode']
'id' => $form_state['values']['langcode'],
));
$language = language_save($language);
drupal_set_message(t('The language %language has been created.', array('%language' => t($language->name))));
@@ -125,17 +126,16 @@ function l10n_update_import_form_submit($form, &$form_state) {
else {
form_set_error('file', $form_state, t('File to import not found.'));
$form_state['rebuild'] = TRUE;
return;
}
$form_state['redirect_route']['route_name'] = 'locale.translate_page';
return;
}
/**
* Form constructor for the Gettext translation files export form.
*
* @see l10n_update_export_form_submit()
*
* @ingroup forms
*/
function l10n_update_export_form($form, &$form_state) {
@@ -148,6 +148,7 @@ function l10n_update_export_form($form, &$form_state) {
}
}
$language_default = language_default();
$groups = module_invoke_all('locale', 'groups');
if (empty($language_options)) {
$form['langcode'] = array(
@@ -169,6 +170,12 @@ function l10n_update_export_form($form, &$form_state) {
'#empty_option' => t('Source text only, no translations'),
'#empty_value' => $language->language,
);
$form['textgroup'] = array(
'#type' => 'select',
'#title' => t('Text group'),
'#options' => $groups,
'#default_value' => 'default',
);
$form['content_options'] = array(
'#type' => 'details',
'#title' => t('Export options'),
@@ -198,11 +205,11 @@ function l10n_update_export_form($form, &$form_state) {
}
$form['actions'] = array(
'#type' => 'actions'
'#type' => 'actions',
);
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => t('Export')
'#value' => t('Export'),
);
return $form;
}
@@ -222,14 +229,16 @@ function l10n_update_export_form_submit($form, &$form_state) {
$language = NULL;
}
$content_options = isset($form_state['values']['content_options']) ? $form_state['values']['content_options'] : array();
$textgroup = isset($form_state['values']['textgroup']) ? $form_state['values']['textgroup'] : NULL;
$reader = new PoDatabaseReader();
$languageName = '';
if ($language != NULL) {
$reader->setLangcode($language->id);
$reader->setOptions($content_options);
$reader->setTextgroup($textgroup);
$languages = language_list();
$languageName = isset($languages[$language->id]) ? $languages[$language->id]->name : '';
$filename = $language->id .'.po';
$filename = $language->id . '.po';
}
else {
// Template required.
@@ -243,7 +252,7 @@ function l10n_update_export_form_submit($form, &$form_state) {
$header->setProjectName(variable_get('site_name'));
$header->setLanguageName($languageName);
$writer = new PoStreamWriter;
$writer = new PoStreamWriter();
$writer->setUri($uri);
$writer->setHeader($header);
@@ -268,20 +277,19 @@ function l10n_update_export_form_submit($form, &$form_state) {
* PoDatabaseWriter. Optional, defaults to an empty array.
* - 'customized': Flag indicating whether the strings imported from $file
* are customized translations or come from a community source. Use
* L10N_UPDATE_CUSTOMIZED or L10N_UPDATE_NOT_CUSTOMIZED. Optional, defaults to
* L10N_UPDATE_NOT_CUSTOMIZED.
* L10N_UPDATE_CUSTOMIZED or L10N_UPDATE_NOT_CUSTOMIZED. Optional, defaults
* to L10N_UPDATE_NOT_CUSTOMIZED.
* - 'finish_feedback': Whether or not to give feedback to the user when the
* batch is finished. Optional, defaults to TRUE.
*
* @param $force
* @param bool $force
* (optional) Import all available files, even if they were imported before.
*
* @todo
* Integrate with update status to identify projects needed and integrate
* l10n_update functionality to feed in translation files alike.
* See http://drupal.org/node/1191488.
* See https://www.drupal.org/node/1191488.
*/
function l10n_update_batch_import_files($options, $force = FALSE) {
function l10n_update_batch_import_files(array $options, $force = FALSE) {
$options += array(
'overwrite_options' => array(),
'customized' => L10N_UPDATE_NOT_CUSTOMIZED,
@@ -324,7 +332,7 @@ function l10n_update_batch_import_files($options, $force = FALSE) {
* Defaults to all projects.
* @param array $langcodes
* Language codes from which to get the translation files and history.
* Defaults to all languagues
* Defaults to all languages.
*
* @return array
* An array of interface translation files keyed by their URI.
@@ -358,9 +366,8 @@ function l10n_update_get_interface_translation_files($projects = array(), $langc
/**
* Build a locale batch from an array of files.
*
* @param $files
* @param array $files
* Array of file objects to import.
*
* @param array $options
* An array with options that can have the following elements:
* - 'langcode': The language code. Optional, defaults to NULL, which means
@@ -369,15 +376,15 @@ function l10n_update_get_interface_translation_files($projects = array(), $langc
* PoDatabaseWriter. Optional, defaults to an empty array.
* - 'customized': Flag indicating whether the strings imported from $file
* are customized translations or come from a community source. Use
* L10N_UPDATE_CUSTOMIZED or L10N_UPDATE_NOT_CUSTOMIZED. Optional, defaults to
* L10N_UPDATE_NOT_CUSTOMIZED.
* L10N_UPDATE_CUSTOMIZED or L10N_UPDATE_NOT_CUSTOMIZED. Optional, defaults
* to L10N_UPDATE_NOT_CUSTOMIZED.
* - 'finish_feedback': Whether or not to give feedback to the user when the
* batch is finished. Optional, defaults to TRUE.
*
* @return
* @return array|bool
* A batch structure or FALSE if $files was empty.
*/
function l10n_update_batch_build($files, $options) {
function l10n_update_batch_build(array $files, array $options) {
$options += array(
'overwrite_options' => array(),
'customized' => L10N_UPDATE_NOT_CUSTOMIZED,
@@ -390,7 +397,7 @@ function l10n_update_batch_build($files, $options) {
$operations[] = array('l10n_update_batch_import', array($file, $options));
}
// Save the translation status of all files.
$operations[] = array('l10n_update_batch_import_save', array());
$operations[] = array('l10n_update_batch_import_save', array());
// Add a final step to refresh JavaScript and configuration strings.
$operations[] = array('l10n_update_batch_refresh', array());
@@ -416,7 +423,6 @@ function l10n_update_batch_build($files, $options) {
* @param object $file
* A file object of the gettext file to be imported. The file object must
* contain a language parameter. This is used as the language of the import.
*
* @param array $options
* An array with options that can have the following elements:
* - 'langcode': The language code.
@@ -424,15 +430,14 @@ function l10n_update_batch_build($files, $options) {
* PoDatabaseWriter. Optional, defaults to an empty array.
* - 'customized': Flag indicating whether the strings imported from $file
* are customized translations or come from a community source. Use
* L10N_UPDATE_CUSTOMIZED or L10N_UPDATE_NOT_CUSTOMIZED. Optional, defaults to
* L10N_UPDATE_NOT_CUSTOMIZED.
* L10N_UPDATE_CUSTOMIZED or L10N_UPDATE_NOT_CUSTOMIZED. Optional, defaults
* to L10N_UPDATE_NOT_CUSTOMIZED.
* - 'message': Alternative message to display during import. Note, this must
* be sanitized text.
*
* @param $context
* @param array $context
* Contains a list of files imported.
*/
function l10n_update_batch_import($file, $options, &$context) {
function l10n_update_batch_import($file, array $options, &$context) {
// Merge the default values in the $options array.
$options += array(
'overwrite_options' => array(),
@@ -461,7 +466,7 @@ function l10n_update_batch_import($file, $options, &$context) {
// Maximize the progress bar at 95% before completion, the batch API
// could trigger the end of the operation before file reading is done,
// because of floating point inaccuracies. See
// http://drupal.org/node/1089472
// https://www.drupal.org/node/1089472
$context['finished'] = min(0.95, $report['seek'] / filesize($file->uri));
if (isset($options['message'])) {
$context['message'] = t('!message (@percent%).', array('!message' => $options['message'], '@percent' => (int) ($context['finished'] * 100)));
@@ -510,7 +515,7 @@ function l10n_update_batch_import($file, $options, &$context) {
/**
* Batch callback: Save data of imported files.
*
* @param $context
* @param array $context
* Contains a list of imported files.
*/
function l10n_update_batch_import_save($context) {
@@ -536,7 +541,7 @@ function l10n_update_batch_import_save($context) {
* @param array $context
* Contains a list of strings updated and information about the progress.
*/
function l10n_update_batch_refresh(array &$context) {
function l10n_update_batch_refresh(&$context) {
if (!isset($context['sandbox']['refresh'])) {
$strings = $langcodes = array();
if (isset($context['results']['stats'])) {
@@ -609,9 +614,17 @@ function l10n_update_batch_finished($success, $results) {
drupal_set_message(format_plural(count($results['files']),
'One translation file imported. %number translations were added, %update translations were updated and %delete translations were removed.',
'@count translation files imported. %number translations were added, %update translations were updated and %delete translations were removed.',
array('%number' => $additions, '%update' => $updates, '%delete' => $deletes)
array(
'%number' => $additions,
'%update' => $updates,
'%delete' => $deletes,
)
));
watchdog('l10n_update', 'Translations imported: %number added, %update updated, %delete removed.', array(
'%number' => $additions,
'%update' => $updates,
'%delete' => $deletes,
));
watchdog('l10n_update', 'Translations imported: %number added, %update updated, %delete removed.', array('%number' => $additions, '%update' => $updates, '%delete' => $deletes));
if ($skips) {
if (module_exists('dblog')) {
@@ -621,7 +634,10 @@ function l10n_update_batch_finished($success, $results) {
$message = format_plural($skips, 'One translation string was skipped because of disallowed or malformed HTML. See the log for details.', '@count translation strings were skipped because of disallowed or malformed HTML. See the log for details.');
}
drupal_set_message($message, 'warning');
watchdog('l10n_update', '@count disallowed HTML string(s) in files: @files.', array('@count' => $skips, '@files' => implode(',', $skipped_files)), WATCHDOG_WARNING);
watchdog('l10n_update', '@count disallowed HTML string(s) in files: @files.', array(
'@count' => $skips,
'@files' => implode(',', $skipped_files),
), WATCHDOG_WARNING);
}
}
}
@@ -630,10 +646,10 @@ function l10n_update_batch_finished($success, $results) {
/**
* Creates a file object and populates the timestamp property.
*
* @param $filepath
* The filepath of a file to import.
* @param string $filepath
* The file path of a file to import.
*
* @return
* @return object
* An object representing the file.
*/
function l10n_update_file_create($filepath) {
@@ -671,7 +687,7 @@ function l10n_update_file_attach_properties($file, $options = array()) {
}
// Extract project, version and language code from the file name. Supported:
// {project}-{version}.{langcode}.po, {prefix}.{langcode}.po or {langcode}.po
// {project}-{version}.{langcode}.po, {prefix}.{langcode}.po or {langcode}.po.
preg_match('!
( # project OR project and version OR emtpy (group 1)
([a-z_]+) # project name (group 2)
@@ -703,9 +719,9 @@ function l10n_update_file_attach_properties($file, $options = array()) {
* Defaults to all projects.
* @param array $langcodes
* Language codes from which to delete the translation files and history.
* Defaults to all languagues
* Defaults to all languages.
*
* @return boolean
* @return bool
* TRUE if files are removed successfully. FALSE if one or more files could
* not be deleted.
*/

View File

@@ -9,7 +9,7 @@
* Load common APIs.
*/
// @todo Combine functions differently in files to avoid unnecessary includes.
// Follow-up issue http://drupal.org/node/1834298
// Follow-up issue https://www.drupal.org/node/1834298
require_once __DIR__ . '/l10n_update.translation.inc';
/**
@@ -21,9 +21,9 @@ function l10n_update_flush_projects() {
}
/**
* Rebuild project list
* Rebuild project list.
*
* @param $refresh
* @param bool $refresh
* TRUE: Refresh project list.
*
* @return array
@@ -46,48 +46,17 @@ function l10n_update_build_projects($refresh = FALSE) {
$default_server = l10n_update_default_translation_server();
if (module_exists('update')) {
$projects_info = update_get_available(TRUE);
}
foreach ($projects as $name => $data) {
// Force update fetch of project data in cases where Drupal's performance
// optimized approach is missing out on some projects.
// @see http://drupal.org/node/1671570#comment-6216090
if (module_exists('update') && !isset($projects_info[$name])) {
module_load_include('fetch.inc', 'update');
_update_process_fetch_task($data);
$available = _update_get_cached_available_releases();
if (!empty($available[$name])) {
$projects_info[$name] = $available[$name];
// For dev releases, remove the '-dev' part and trust the translation
// server to fall back to the latest stable release for that branch.
if (isset($data['info']['version']) && strpos($data['info']['version'], '-dev')) {
if (preg_match("/^(\d+\.x-\d+\.).*$/", $data['info']['version'], $matches)) {
// Example matches: 7.x-1.x-dev, 7.x-1.0-alpha1+5-dev => 7.x-1.x.
$data['info']['version'] = $matches[1] . 'x';
}
}
if (isset($projects_info[$name]['releases']) && $projects_info[$name]['project_status'] != 'not-fetched') {
// Find out if a dev version is installed.
if (preg_match("/^[0-9]+\.x-([0-9]+)\..*-dev$/", $data['info']['version'], $matches)) {
// Find a suitable release to use as alternative translation.
foreach ($projects_info[$name]['releases'] as $project_release) {
// The first release with the same major release number which is not
// a dev release is the one. Releases are sorted the most recent first.
if ($project_release['version_major'] == $matches[1] &&
(!isset($project_release['version_extra']) || $project_release['version_extra'] != 'dev')) {
$release = $project_release;
break;
}
}
}
if (!empty($release['version'])) {
$data['info']['version'] = $release['version'];
}
unset($release);
}
// Without Update module we do a best effort fallback. A development
// release will fall back to the corresponding release version.
elseif (!isset($projects_info) && isset($data['info']['version'])) {
if (preg_match('/[^x](\+\d+)?-dev$/', $data['info']['version'])) {
$data['info']['version'] = preg_replace('/(\+\d+)?-dev$/', '', $data['info']['version']);
elseif (preg_match("/^(\d+\.).*$/", $data['info']['version'], $matches)) {
// Example match: 7.33-dev => 7.x (Drupal core).
$data['info']['version'] = $matches[1] . 'x';
}
}
@@ -121,33 +90,64 @@ function l10n_update_build_projects($refresh = FALSE) {
}
/**
* Get update module's project list
* Get update module's project list.
*
* @return array
* List of projects to be updated.
*/
function l10n_update_project_list() {
$projects = array();
$disabled = variable_get('l10n_update_check_disabled', 0);
// Unlike update module, this one has no cache
// Unlike update module, this one has no cache.
_l10n_update_project_info_list($projects, system_rebuild_module_data(), 'module', $disabled);
_l10n_update_project_info_list($projects, system_rebuild_theme_data(), 'theme', $disabled);
// Allow other modules to alter projects before fetching and comparing.
drupal_alter('l10n_update_projects', $projects);
l10n_update_remove_disabled_projects($projects);
return $projects;
}
/**
* Removes disabled projects from the project list.
*
* @param array $projects
* Array of projects keyed by the project machine name.
*/
function l10n_update_remove_disabled_projects(&$projects) {
$disabled_projects = variable_get('l10n_update_disabled_projects', array());
foreach ($disabled_projects as $disabled_name) {
// Remove projects with matching name either by full string of by wild card.
if (strpos($disabled_name, '*') !== FALSE) {
$pattern = str_replace('*', '.*?', $disabled_name);
$matches = preg_grep('/^' . $pattern . '$/i', array_keys($projects));
foreach ($matches as $match) {
unset($projects[$match]);
}
}
elseif (isset($projects[$disabled_name])) {
unset($projects[$disabled_name]);
}
}
}
/**
* Populate an array of project data.
*
* Based on _update_process_info_list()
*
* @param $projects
* @param $list
* @param $project_type
* @param $disabled
* TRUE to include disabled projects too
* @param array $projects
* The list of projects to populate.
* @param array $list
* System module list as returned by system_rebuild_module_data() or
* system_rebuild_theme_data().
* @param string $project_type
* The project type to process: 'theme' or 'module'.
* @param bool $disabled
* TRUE to include disabled projects too.
*/
function _l10n_update_project_info_list(&$projects, $list, $project_type, $disabled = FALSE) {
function _l10n_update_project_info_list(array &$projects, array $list, $project_type, $disabled = FALSE) {
foreach ($list as $file) {
if (!$disabled && empty($file->status)) {
// Skip disabled modules or themes.
@@ -207,14 +207,19 @@ function _l10n_update_project_info_list(&$projects, $list, $project_type, $disab
}
/**
* Given a $file object (as returned by system_rebuild_module_data()), figure
* out what project it belongs to.
* Given a file object figure out what project it belongs to.
*
* Based on update_get_project_name().
* Uses a file object as returned by system_rebuild_module_data(). Based on
* update_get_project_name().
*
* @param object $file
* Project info file object.
*
* @param $file
* @return string
* The project's machine name this file belongs to.
*
* @see system_get_files_database()
* @see update_get_project_name()
*/
function l10n_update_get_project_name($file) {
$project_name = '';
@@ -250,10 +255,8 @@ function l10n_update_default_translation_server() {
* @param string $langcodes
* Array of language codes. Defaults to all translatable languages.
*
* @return array
* Available sources indexed by project and language.
* @todo Return batch array or NULL
*/
// @todo Return batch or NULL
function l10n_update_check_projects($projects = array(), $langcodes = array()) {
if (l10n_update_use_remote_source()) {
// Retrieve the status of both remote and local translation sources by
@@ -277,7 +280,7 @@ function l10n_update_check_projects($projects = array(), $langcodes = array()) {
*
* @param array $projects
* Array of project names to check. Defaults to all translatable projects.
* @param string $langcodes
* @param array $langcodes
* Array of language codes. Defaults to all translatable languages.
*/
function l10n_update_check_projects_batch($projects = array(), $langcodes = array()) {
@@ -323,8 +326,7 @@ function l10n_update_batch_status_build($projects = array(), $langcodes = array(
}
/**
* Helper function to construct batch operations checking remote translation
* status.
* Constructs batch operations checking remote translation status.
*
* @param array $projects
* Array of project names to be processed.
@@ -336,13 +338,20 @@ function l10n_update_batch_status_build($projects = array(), $langcodes = array(
* @return array
* Array of batch operations.
*/
function _l10n_update_batch_status_operations($projects, $langcodes, $options = array()) {
function _l10n_update_batch_status_operations(array $projects, array $langcodes, array $options = array()) {
$operations = array();
foreach ($projects as $project) {
foreach ($langcodes as $langcode) {
// Check status of local and remote translation sources.
$operations[] = array('l10n_update_batch_status_check', array($project, $langcode, $options));
$operations[] = array(
'l10n_update_batch_status_check',
array(
$project,
$langcode,
$options,
),
);
}
}

View File

@@ -17,15 +17,16 @@ function l10n_update_drush_command() {
'description' => 'Show translation status of available projects.',
'options' => array(
'languages' => 'Comma separated list of languages. Defaults to all available languages. Example: --languages="nl, fr, de"',
)
),
);
$commands['l10n-update'] = array(
'description' => 'Update translations.',
'options' => array(
'languages' => 'Comma separated list of languages. Defaults to all available languages. Example: --languages="nl, fr, de"',
'mode' => 'Determine if existing translations are overwitten during import. Use "overwrite" to overwrite any existing translation, "replace" to replace previously imported translations but not overwrite edited strings, "keep" to keep any existing translation and only add new translations. Default value: "keep"'
'mode' => 'Determine if existing translations are overwitten during import. Use "overwrite" to overwrite any existing translation, "replace" to replace previously imported translations but not overwrite edited strings, "keep" to keep any existing translation and only add new translations. Default value: "keep"',
),
);
return $commands;
}
@@ -67,13 +68,13 @@ function drush_l10n_update_status() {
// Build table header.
$table = array();
$header = array(dt('Project'));
foreach ($languages as $langcode => $language) {
foreach ($languages as $language) {
$header[] = $language->name;
}
$table[] = $header;
// Iterate projects to obtain per language status.
foreach ($status as $name => $project) {
foreach ($status as $project) {
$row = array();
// First column: Project name & version number.
$project_details = reset($project);
@@ -140,12 +141,14 @@ function drush_l10n_update() {
'customized' => FALSE,
);
break;
case 'replace':
$options['overwrite_options'] = array(
'not_customized' => TRUE,
'customized' => FALSE,
);
break;
case 'overwrite':
$options['overwrite_options'] = array(
'not_customized' => TRUE,
@@ -155,13 +158,12 @@ function drush_l10n_update() {
default:
return drush_set_error('L10N_UPDATE_INVALID_MODE', dt('Invalid update mode. Valid options are keep, overwrite.'));
break;
}
$languages = array_keys(drush_get_option('languages'));
// Get translation status of the projects, download and update translations.
$batch = l10n_update_batch_update_build(array(), $languages, $options);
$batch = l10n_update_batch_update_build(array_keys($updates['projects']), $languages, $options);
drush_log($batch['title'], 'status');
drush_log($batch['init_message'], 'status');
batch_set($batch);
@@ -222,14 +224,19 @@ function _drush_l10n_update_validate_languages() {
/**
* Helper function to obtain $updates.
*
* @return $updates array or NULL.
* @return array|null
* Array of projects and languages to be updated. Array keys:
* - projects: Associative array of projects to be updated. Key: Project name.
* Value: Project info array.
* - languages: Associative array of languages to be updated. Key: Language
* code. Value: Project info array.
*/
function _drush_l10n_update_get_updates() {
$updates = array();
$languages = l10n_update_translatable_language_list();
$status = l10n_update_get_status();
drush_log(dt('Fetching update information for all projects / all languages.'), 'status');
drush_log(dt('Fetching update information for all projects / all languages.'), 'status');
// Prepare information about projects which have available translation
// updates.
@@ -243,11 +250,19 @@ function _drush_l10n_update_get_updates() {
}
}
}
}
if ($updates) {
return $updates;
if ($updates) {
return $updates;
}
else {
drush_log(dt('All project translations up to date.'), 'status');
}
}
else {
drush_log(dt('No languages to update.'), 'warning');
if (empty($languages)) {
drush_log(dt('No languages to update.'), 'warning');
}
else {
drush_log(dt('No translation status available. Run drush l10n-update-status first.'), 'warning');
}
}
}

View File

@@ -2,14 +2,14 @@
/**
* @file
* The API for download and import of translations from remote and local sources.
* The API for download and import of translations.
*/
/**
* Load the common translation API.
*/
// @todo Combine functions differently in files to avoid unnecessary includes.
// Follow-up issue http://drupal.org/node/1834298
// Follow-up issue https://www.drupal.org/node/1834298
require_once __DIR__ . '/l10n_update.translation.inc';
/**
@@ -92,15 +92,39 @@ function l10n_update_batch_fetch_build($projects = array(), $langcodes = array()
* @return array
* Array of batch operations.
*/
function _l10n_update_fetch_operations($projects, $langcodes, $options) {
function _l10n_update_fetch_operations(array $projects, array $langcodes, array $options) {
$operations = array();
// If the process is going to download files from a remote, make sure the
// files can be saved in the download directory.
if (!empty($projects) && l10n_update_use_remote_source()) {
$directory = variable_get('l10n_update_download_store', L10N_UPDATE_DEFAULT_TRANSLATION_PATH);
// Do not download files if the directory is missing or can not be created.
if (!file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
drupal_set_message(t('The directory %directory does not exist or is not writable.', array('%directory' => $directory)), 'error');
watchdog('file system', 'The directory %directory does not exist or is not writable.', array('%directory' => $directory), WATCHDOG_ERROR);
return $operations;
}
}
foreach ($projects as $project) {
foreach ($langcodes as $langcode) {
if (l10n_update_use_remote_source()) {
$operations[] = array('l10n_update_batch_fetch_download', array($project, $langcode));
$operations[] = array(
'l10n_update_batch_fetch_download',
array(
$project,
$langcode,
),
);
}
$operations[] = array('l10n_update_batch_fetch_import', array($project, $langcode, $options));
$operations[] = array('l10n_update_batch_fetch_import', array(
$project,
$langcode,
$options,
),
);
}
}

View File

@@ -2,22 +2,24 @@
/**
* @file
* Http API for l10n updates.
* Http API for l10n updates.
*/
/**
* Check if remote file exists and when it was last updated.
*
* @param $url
* @param string $url
* URL of remote file.
* @param $headers
* @param array $headers
* HTTP request headers.
* @return object
*
* @return object|bool
* Result object containing the HTTP request headers, response code, headers,
* data, redirect status and updated timestamp.
* @see l10n_update_http_request()
*
* @see l10n_update_http_request()
*/
function l10n_update_http_check($url, $headers = array()) {
function l10n_update_http_check($url, array $headers = array()) {
$result = l10n_update_http_request($url, array('headers' => $headers, 'method' => 'HEAD'));
if (!isset($result->error)) {
if ($result && $result->code == 200) {
@@ -35,9 +37,11 @@ function l10n_update_http_check($url, $headers = array()) {
// found there.
watchdog('l10n_update', 'File not found: @uri.', array('@uri' => $url));
return TRUE;
case 0:
watchdog('l10n_update', 'Error occurred when trying to check @remote: @errormessage.', array('@errormessage' => $result->error, '@remote' => $url), WATCHDOG_ERROR);
break;
default:
watchdog('l10n_update', 'HTTP error @errorcode occurred when trying to check @remote.', array('@errorcode' => $result->code, '@remote' => $url), WATCHDOG_ERROR);
break;
@@ -49,12 +53,13 @@ function l10n_update_http_check($url, $headers = array()) {
/**
* Perform an HTTP request.
*
* We cannot use drupal_http_request() at install, see http://drupal.org/node/527484
* We cannot use drupal_http_request() at install,
* see https://www.drupal.org/node/527484
*
* This is a flexible and powerful HTTP client implementation. Correctly
* handles GET, POST, PUT or any other HTTP requests. Handles redirects.
*
* @param $url
* @param string $url
* A string containing a fully qualified URI.
* @param array $options
* (optional) An array that can have one or more of the following elements:
@@ -88,6 +93,14 @@ function l10n_update_http_check($url, $headers = array()) {
* - data: A string containing the response body that was received.
*/
function l10n_update_http_request($url, array $options = array()) {
// Allow an alternate HTTP client library to replace l10n_update's default
// implementation.
/** @var string $override_function */
$override_function = variable_get('drupal_http_request_function', FALSE);
if (!empty($override_function) && function_exists($override_function)) {
return $override_function($url, $options);
}
$result = new stdClass();
// Parse the URL and make sure we can handle the schema.
@@ -119,7 +132,7 @@ function l10n_update_http_request($url, array $options = array()) {
// Merge the default headers.
$options['headers'] += array(
'User-Agent' => 'Drupal (+http://drupal.org/)',
'User-Agent' => 'Drupal (+https://www.drupal.org/)',
);
// stream_socket_client() requires timeout to be a float.
@@ -203,8 +216,7 @@ function l10n_update_http_request($url, array $options = array()) {
// server's ability to make outgoing HTTP requests the next time that
// requirements checking is performed.
// See system_requirements().
// variable_set('drupal_http_request_fails', TRUE);
// variable_set('drupal_http_request_fails', TRUE);.
return $result;
}
@@ -228,12 +240,12 @@ function l10n_update_http_request($url, array $options = array()) {
$options['headers']['Authorization'] = 'Basic ' . base64_encode($uri['user'] . (isset($uri['pass']) ? ':' . $uri['pass'] : ''));
}
// If the database prefix is being used by SimpleTest to run the tests in a copied
// database then set the user-agent header to the database prefix so that any
// calls to other Drupal pages will run the SimpleTest prefixed database. The
// user-agent is used to ensure that multiple testing sessions running at the
// same time won't interfere with each other as they would if the database
// prefix were stored statically in a file or database variable.
// If the database prefix is being used by SimpleTest to run the tests in a
// copied database then set the user-agent header to the database prefix so
// that any calls to other Drupal pages will run the SimpleTest prefixed
// database. The user-agent is used to ensure that multiple testing sessions
// running at the same time won't interfere with each other as they would if
// the database prefix were stored statically in a file or database variable.
$test_info = &$GLOBALS['drupal_test_info'];
if (!empty($test_info['test_run_id'])) {
$options['headers']['User-Agent'] = drupal_generate_test_ua($test_info['test_run_id']);
@@ -356,12 +368,18 @@ function l10n_update_http_request($url, array $options = array()) {
$result->code = $code;
switch ($code) {
case 200: // OK
case 304: // Not modified
case 200:
// OK.
case 304:
// Not modified.
break;
case 301: // Moved permanently
case 302: // Moved temporarily
case 307: // Moved temporarily
case 301:
// Moved permanently.
case 302:
// Moved temporarily.
case 307:
// Moved temporarily.
$location = $result->headers['location'];
$options['timeout'] -= timer_read(__FUNCTION__) / 1000;
if ($options['timeout'] <= 0) {
@@ -378,6 +396,7 @@ function l10n_update_http_request($url, array $options = array()) {
$result->redirect_url = $location;
}
break;
default:
$result->error = $status_message;
}

View File

@@ -2,7 +2,9 @@ name = Localization update
description = Provides automatic downloads and updates for translations.
dependencies[] = locale
core = 7.x
php = 5.3
package = Multilingual
configure = admin/config/regional/language/update
files[] = includes/gettext/PoHeader.php
files[] = includes/gettext/PoItem.php
@@ -30,9 +32,10 @@ files[] = tests/L10nUpdateCronTest.test
files[] = tests/L10nUpdateInterfaceTest.test
files[] = tests/L10nUpdateTest.test
files[] = tests/L10nUpdateTestBase.test
; Information added by Drupal.org packaging script on 2014-11-10
version = "7.x-2.0"
; Information added by Drupal.org packaging script on 2017-09-18
version = "7.x-2.2"
core = "7.x"
project = "l10n_update"
datestamp = "1415625781"
datestamp = "1505717347"

View File

@@ -2,7 +2,7 @@
/**
* @file
* Install file for l10n remote updates.
* Install file for l10n remote updates.
*/
/**
@@ -15,7 +15,7 @@ function l10n_update_schema() {
'name' => array(
'description' => 'A unique short name to identify the project.',
'type' => 'varchar',
'length' => '50',
'length' => '255',
'not null' => TRUE,
),
'project_type' => array(
@@ -38,13 +38,6 @@ function l10n_update_schema() {
'not null' => TRUE,
'default' => '',
),
'l10n_server' => array(
'description' => 'Localization server for this project.',
'type' => 'varchar',
'length' => '255',
'not null' => TRUE,
'default' => '',
),
'l10n_path' => array(
'description' => 'Server path this project updates.',
'type' => 'varchar',
@@ -68,7 +61,7 @@ function l10n_update_schema() {
'project' => array(
'description' => 'A unique short name to identify the project.',
'type' => 'varchar',
'length' => '50',
'length' => '255',
'not null' => TRUE,
),
'language' => array(
@@ -136,9 +129,6 @@ function l10n_update_schema() {
'primary key' => array('project', 'language'),
);
$schema['cache_l10n_update'] = drupal_get_schema_unprocessed('system', 'cache');
$schema['cache_l10n_update']['description'] = 'Cache table for the Localization Update module to store information about available releases, fetched from central server.';
return $schema;
}
@@ -158,7 +148,11 @@ function l10n_update_schema_alter(&$schema) {
* Implements hook_install().
*/
function l10n_update_install() {
db_add_field('locales_target', 'l10n_status', array('type' => 'int', 'not null' => TRUE, 'default' => 0));
db_add_field('locales_target', 'l10n_status', array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
));
variable_set('l10n_update_rebuild_projects', 1);
// Create the translation directory. We try different alternative paths as the
@@ -192,8 +186,8 @@ function l10n_update_uninstall() {
variable_del('l10n_update_check_frequency');
variable_del('l10n_update_last_check');
variable_del('l10n_update_download_store');
variable_del('l10n_update_translation_status');
variable_del('l10n_update_check_mode');}
variable_del('l10n_update_check_mode');
}
/**
* Implements hook_requirements().
@@ -226,7 +220,10 @@ function l10n_update_requirements($phase) {
'title' => 'Translation update status',
'value' => l(t('Updates available'), 'admin/config/regional/translate/update'),
'severity' => REQUIREMENT_WARNING,
'description' => t('Updates available for: @languages. See the <a href="!updates">Available translation updates</a> page for more information.', array('@languages' => implode(', ', $available_updates), '!updates' => url('admin/config/regional/translate/update'))),
'description' => t('Updates available for: @languages. See the <a href="!updates">Translate interface update</a> page for more information.', array(
'@languages' => implode(', ', $available_updates),
'!updates' => url('admin/config/regional/translate/update'),
)),
);
}
else {
@@ -234,7 +231,10 @@ function l10n_update_requirements($phase) {
'title' => 'Translation update status',
'value' => t('Missing translations'),
'severity' => REQUIREMENT_INFO,
'description' => t('Missing translations for: @languages. See the <a href="!updates">Available translation updates</a> page for more information.', array('@languages' => implode(', ', $untranslated), '!updates' => url('admin/config/regional/translate/update'))),
'description' => t('Missing translations for: @languages. See the <a href="!updates">Translate interface update</a> page for more information.', array(
'@languages' => implode(', ', $untranslated),
'!updates' => url('admin/config/regional/translate/update'),
)),
);
}
}
@@ -251,7 +251,7 @@ function l10n_update_requirements($phase) {
'title' => 'Translation update status',
'value' => l(t('Can not determine status'), 'admin/config/regional/translate/update'),
'severity' => REQUIREMENT_WARNING,
'description' => t('No translation status is available. See the <a href="!updates">Available translation updates</a> page for more information.', array('!updates' => url('admin/config/regional/translate/update'))),
'description' => t('No translation status is available. See the <a href="!updates">Translate interface update</a> page for more information.', array('!updates' => url('admin/config/regional/translate/update'))),
);
}
}
@@ -266,6 +266,162 @@ function l10n_update_requirements($phase) {
return $requirements;
}
/**
* Converts D8 style translations strings into D7 format.
*
* Drupal 8 stores plurals (both for source and translation strings) as one
* string, where the plural forms are separated by a special character. By
* mistake this plural handling was also back ported. This function converts any
* D8 style plural into a D7 style plural. It used the following strategy.
* - Any locales_source record that contains the separator character is
* converted.
* - All these records are deleted, except those of which the translation was
* edited.
* - Existing translations will only be overwritten by converted translations
* if the import behaviour setting (admin/config/regional/language/update)
* allows that.
*
* @param int $start
* The number of start.
* @param int $length
* The length number.
* @param array $report
* Array containing import results.
*
* @return bool
* Returns true conversion
* a keyed array of log messages. Available keys:
* - message: Translated message
* - status: Message status. See drush_log() for supported values.
*/
function l10n_update_d8_plural_conversion($start = 0, $length = 0, array &$report) {
module_load_include('inc', 'l10n_update', 'l10n_update.translation');
require_once DRUPAL_ROOT . '/includes/locale.inc';
$processed = array();
$plurals = l10n_update_get_d8_plural_strings($start, $length);
$finished = count($plurals) < $length;
if (empty($plurals)) {
return $finished;
}
$mode = variable_get('l10n_update_import_mode', LOCALE_IMPORT_OVERWRITE);
$writer = new PoDatabaseWriter();
$writer->setOptions(array(
'overwrite_options' => array(
'not_customized' => $mode != LOCALE_IMPORT_KEEP,
'customized' => $mode == LOCALE_IMPORT_OVERWRITE,
),
'customized' => L10N_UPDATE_NOT_CUSTOMIZED,
));
$writer_report = isset($report['writer']) ? $report['writer'] : array();
$writer->setReport($writer_report);
foreach ($plurals as $plural) {
// Extract data from source and target strings.
$sources = explode(L10N_UPDATE_PLURAL_DELIMITER, $plural->source);
$translations = explode(L10N_UPDATE_PLURAL_DELIMITER, $plural->translation);
$writer->setLangcode($plural->language);
$item = new PoItem();
$item->setContext($plural->context);
$item->setSource($sources);
$item->setTranslation($translations);
$item->setPlural(TRUE);
$item->setLangcode($plural->language);
$item->setTextgroup($plural->textgroup);
$writer->writeItem($item);
// Collect plurals to be deleted. In the rare case that a translation was
// modified, we will convert it but not delete the custom translation.
if (!$plural->l10n_status) {
$report['results']['lids'][] = $plural->lid;
}
$report['results']['languages'][$plural->language] = $plural->language;
}
$report['writer'] = $writer->getReport();
unset($report['writer']['strings']);
return $finished;
}
/**
* Gets D8 plural style strings.
*
* @param int $start
* Offset of records to load.
* @param int $length
* Maximum number of records to load.
*
* @return array|bool
* An array of data from source and translation strings of which the source
* string contains a Drupal 8 style plural delimiter. Returns false if not
* found.
*/
function l10n_update_get_d8_plural_strings($start = 0, $length = 0) {
$translation_strings = FALSE;
$query = db_select('locales_source', 'ls');
$query->fields('ls', array(
'lid', 'location',
'textgroup',
'source',
'context',
'version',
));
$query->fields('lt', array(
'translation',
'language',
'plid',
'plural',
'l10n_status',
));
$query->innerJoin('locales_target', 'lt', 'ls.lid = lt.lid');
$query->condition('source', '%' . db_like(L10N_UPDATE_PLURAL_DELIMITER) . '%', 'LIKE');
if ($length) {
$query->range($start, $length);
}
$results = $query->execute();
if ($results->rowCount()) {
foreach ($results as $result) {
$translation_strings[] = $result;
}
}
return $translation_strings;
}
/**
* Clean-up after plural string conversion.
*
* @param array $results
* Batch results array.
*/
function l10n_update_finish_d8_plural_strings($results) {
require_once DRUPAL_ROOT . '/includes/locale.inc';
if ($results) {
// Delete converted D8 style translations.
if (isset($results['lids'])) {
foreach ($results['lids'] as $lids) {
db_delete('locales_source')->condition('lid', $lids)->execute();
db_delete('locales_target')->condition('lid', $lids)->execute();
}
}
// Clear caches if translations are modified.
if (isset($results['languages'])) {
cache_clear_all('locale:', 'cache', TRUE);
foreach ($results['languages'] as $langcode) {
_locale_invalidate_js($langcode);
}
}
}
}
/**
* Rename filepath to uri in {l10n_update_file} table.
*/
@@ -301,11 +457,7 @@ function l10n_update_update_7003() {
* Create {cache_l10n_update} table.
*/
function l10n_update_update_7004() {
if (!db_table_exists('cache_l10n_update')) {
$schema = drupal_get_schema_unprocessed('system', 'cache');
$schema['description'] = 'Cache table for the Localization Update module to store information about available releases, fetched from central server.';
db_create_table('cache_l10n_update', $schema);
}
// Code removed. Table {cache_l10n_update} is no longer used.
}
/**
@@ -317,30 +469,33 @@ function l10n_update_update_7200() {
registry_rebuild();
}
// If no translation directory was set, set one here. We try different
// alternative paths as the default may not always be writable.
if (!variable_get('l10n_update_download_store', '')) {
variable_set('l10n_update_download_store', 'sites/all/translations');
}
// Create the translation directory. We try different alternative paths as the
// default may not always be writable.
$directories = array(
variable_get('l10n_update_download_store', L10N_UPDATE_DEFAULT_TRANSLATION_PATH),
variable_get('file_public_path', conf_path() . '/files') . '/translations',
'sites/default/files/translations',
);
foreach ($directories as $directory) {
if (file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
variable_set('l10n_update_download_store', $directory);
return;
$directories = array(
variable_get('l10n_update_download_store', L10N_UPDATE_DEFAULT_TRANSLATION_PATH),
variable_get('file_public_path', conf_path() . '/files') . '/translations',
'sites/default/files/translations',
);
$directory_created = FALSE;
foreach ($directories as $directory) {
if (file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
variable_set('l10n_update_download_store', $directory);
$directory_created = TRUE;
break;
}
}
if (!$directory_created) {
watchdog('l10n_update', 'The directory %directory does not exist or is not writable.', array('%directory' => $directories[0]), WATCHDOG_ERROR);
drupal_set_message(t('The directory %directory does not exist or is not writable.', array('%directory' => $directories[0])), 'error');
}
}
watchdog('l10n_update', 'The directory %directory does not exist or is not writable.', array('%directory' => $directories[0]), WATCHDOG_ERROR);
drupal_set_message(t('The directory %directory does not exist or is not writable.', array('%directory' => $directories[0])), 'error');
// Translation source 'Remote server only' is no longer supported. Use 'Remote
// and local' instead.
$mode = variable_get('l10n_update_check_mode', 3); // L10N_UPDATE_USE_SOURCE_REMOTE_AND_LOCAL
$mode = variable_get('l10n_update_check_mode', 3);
if ($mode == 1) {
variable_set('l10n_update_check_mode', 3); // L10N_UPDATE_USE_SOURCE_REMOTE_AND_LOCAL
variable_set('l10n_update_check_mode', 3);
}
// Daily cron updates are no longer supported. Use weekly instead.
@@ -360,6 +515,114 @@ function l10n_update_update_7200() {
*/
function l10n_update_update_7201() {
if (!variable_get('l10n_update_download_store', '')) {
variable_set('l10n_update_download_store', 'sites/all/translations');
variable_set('l10n_update_download_store', 'sites/all/translations');
}
}
/**
* Removes table {cache_l10n_update}.
*/
function l10n_update_update_7202() {
if (db_table_exists('cache_l10n_update')) {
db_drop_table('cache_l10n_update');
}
}
/**
* Removes the field 'l10n_server' from table {cache_l10n_update}.
*/
function l10n_update_update_7203() {
if (db_field_exists('l10n_update_project', 'l10n_server')) {
db_drop_field('l10n_update_project', 'l10n_server');
}
}
/**
* Increase the length of the name column.
*/
function l10n_update_update_7204() {
$schema = l10n_update_schema();
db_change_field('l10n_update_project', 'name', 'name', $schema['l10n_update_project']['fields']['name']);
}
/**
* Increase the length of the name column.
*/
function l10n_update_update_7205() {
$schema = l10n_update_schema();
db_change_field('l10n_update_file', 'project', 'project', $schema['l10n_update_file']['fields']['project']);
}
/**
* Remove 'headers' field from cache table.
*/
function l10n_update_update_7206() {
if (db_field_exists('cache_l10n_update', 'headers')) {
db_drop_field('cache_l10n_update', 'headers');
}
}
/**
* Migrate D8 style plurals to D7 style.
*/
function l10n_update_update_7207(&$sandbox) {
$message = NULL;
$batch_size = 50;
if (!isset($sandbox['progress'])) {
$sandbox['progress'] = 0;
$sandbox['max'] = count(l10n_update_get_d8_plural_strings());
$sandbox['report'] = array();
}
if ($sandbox['max'] == 0) {
$sandbox['#finished'] = 1;
return NULL;
}
$finished = l10n_update_d8_plural_conversion($sandbox['progress'], $batch_size, $sandbox['report']);
$sandbox['progress'] += $batch_size;
// Whether the batch is finished is determined by the result of
// l10n_update_d8_plural_conversion().The progress (percentage) is
// calculated.
if (empty($sandbox['max']) || $finished) {
$sandbox['#finished'] = 1;
}
else {
$sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
}
if ($sandbox['#finished'] == 1) {
l10n_update_finish_d8_plural_strings($sandbox['report']['results']);
if ($sandbox['report']['writer']['skips']) {
$message = t('Some problems were reported during plural string migration. See <a href="/admin/reports/dblog">Recent log messages</a> for details.');
}
}
return $message;
}
/**
* Remove the status variable.
*/
function l10n_update_update_7208() {
$status = variable_get('l10n_update_translation_status');
cache_set('l10n_update_status', $status);
variable_del('l10n_update_translation_status');
}
/**
* Update default variable values to use https.
*/
function l10n_update_update_7209() {
$server_url = variable_get('l10n_client_server', '');
if ($server_url == 'http://localize.drupal.org') {
variable_set('l10n_client_server', 'https://localize.drupal.org');
}
$update_url = variable_get('l10n_update_default_update_url', '');
if ($update_url == 'http://ftp.drupal.org/files/translations/%core/%project/%project-%release.%language.po') {
variable_set('l10n_update_default_update_url', L10N_UPDATE_DEFAULT_SERVER_PATTERN);
}
}

View File

@@ -2,7 +2,7 @@
/**
* @file
* Download translations from remote localization server.
* Download translations from remote localization server.
*/
/**
@@ -28,7 +28,7 @@ define('L10N_UPDATE_USE_SOURCE_REMOTE_AND_LOCAL', 3);
*
* @see l10n_update_default_translation_server().
*/
define('L10N_UPDATE_DEFAULT_SERVER_PATTERN', 'http://ftp.drupal.org/files/translations/%core/%project/%project-%release.%language.po');
define('L10N_UPDATE_DEFAULT_SERVER_PATTERN', 'https://ftp.drupal.org/files/translations/%core/%project/%project-%release.%language.po');
/**
* Default gettext file name on the translation server.
@@ -41,13 +41,14 @@ define('L10N_UPDATE_DEFAULT_FILE_NAME', '%project-%release.%language.po');
define('L10N_UPDATE_DEFAULT_TRANSLATION_PATH', 'sites/all/translations');
/**
* The number of seconds that the translations status entry should be considered.
* The number of seconds that the translations status entry is valid.
*/
define('L10N_UPDATE_STATUS_TTL', 600);
/**
* UI option for override of existing translations. Only override non-customized
* translations.
* UI option for override of existing translations.
*
* Only override non-customized translations.
*/
define('L10N_UPDATE_OVERWRITE_NON_CUSTOMIZED', 2);
@@ -104,17 +105,19 @@ define('L10N_UPDATE_CUSTOMIZED', 1);
* Implements hook_help().
*/
function l10n_update_help($path, $arg) {
$output = '';
switch ($path) {
case 'admin/config/regional/translate/update':
$output = '<p>' . t('Status of interface translations for each of the enabled languages.') . '</p>';
$output .= '<p>' . t('If there are available updates you can click on "Update translation" for them to be downloaded and imported now or you can edit the configuration for them to be updated automatically on the <a href="@update-settings">Update settings page</a>', array('@update-settings' => url('admin/config/regional/language/update'))) . '</p>';
return $output;
break;
case 'admin/config/regional/language/update':
$output = '<p>' . t('These are the settings for the translation update system. To update your translations now, check out the <a href="@update-admin">Translation update administration page</a>.', array('@update-admin' => url('admin/config/regional/translate/update'))) . '</p>';
return $output;
break;
}
return $output;
}
/**
@@ -170,6 +173,7 @@ function l10n_update_theme() {
),
);
}
/**
* Implements hook_menu_alter().
*/
@@ -183,7 +187,7 @@ function l10n_update_menu_alter(&$menu) {
/**
* Implements hook_cron().
*
* Check one project/language at a time, download and import if update available
* Checks interface translations. Downloads and imports updates when available.
*/
function l10n_update_cron() {
// Update translations only when an update frequency was set by the admin
@@ -219,13 +223,13 @@ function l10n_update_cron_queue_info() {
* queue data.
*
* @param array $data
* Queue data array containing:
* Queue data array containing:
* - Function name.
* - Array of function arguments. Optionally contains the batch context data.
*
* @see l10n_update_queue_info()
*/
function l10n_update_worker($data) {
function l10n_update_worker(array $data) {
module_load_include('batch.inc', 'l10n_update');
list($function, $args) = $data;
@@ -246,7 +250,7 @@ function l10n_update_worker($data) {
}
else {
$batch_context = $args[$last];
unset ($args[$last]);
unset($args[$last]);
}
$args = array_merge($args, array(&$batch_context));
@@ -269,7 +273,7 @@ function l10n_update_stream_wrappers() {
// Load the stream wrapper class if not automatically loaded. This happens
// before update.php is executed.
if (!class_exists('TranslationsStreamWrapper')) {
require_once('includes/locale/TranslationsStreamWrapper.php');
require_once 'includes/locale/TranslationsStreamWrapper.php';
}
$wrappers['translations'] = array(
@@ -291,10 +295,12 @@ function l10n_update_form_alter(&$form, $form_state, $form_id) {
case 'i18n_string_locale_translate_edit_form':
$form['#submit'][] = 'l10n_update_locale_translate_edit_form_submit';
break;
case 'locale_languages_predefined_form':
case 'locale_languages_custom_form':
$form['#submit'][] = 'l10n_update_languages_changed_submit';
break;
case 'locale_languages_delete_form':
// A language is being deleted.
$form['#submit'][] = 'l10n_update_languages_delete_submit';
@@ -350,9 +356,19 @@ function l10n_update_themes_enabled($themes) {
}
/**
* Additional submit handler for language forms
* Implements hook_l10n_update_languages_alter().
*
* We need to refresh status when a new language is enabled / disabled
* Removes disabled languages from the language list.
*/
function l10n_update_l10n_update_languages_alter(array &$langcodes) {
$disabled = array_filter(variable_get('l10n_update_disabled_languages', array()));
$langcodes = array_diff_key($langcodes, $disabled);
}
/**
* Additional submit handler for language forms.
*
* We need to refresh status when a new language is enabled / disabled.
*/
function l10n_update_languages_changed_submit($form, $form_state) {
if (variable_get('l10n_update_import_enabled', TRUE)) {
@@ -390,7 +406,9 @@ function l10n_update_languages_delete_submit($form, $form_state) {
*/
function l10n_update_locale_translate_edit_form_submit($form, &$form_state) {
$lid = $form_state['values']['lid'];
foreach ($form_state['values']['translations'] as $langcode => $value) {
$languages = $form_state['values']['translations'];
$languages = array_intersect_key($languages, l10n_update_translatable_language_list());
foreach ($languages as $langcode => $value) {
if (!empty($value) && $value != $form_state['complete form']['translations'][$langcode]['#default_value']) {
// An update has been made, mark the string as customized.
db_update('locales_target')
@@ -412,11 +430,20 @@ function l10n_update_client_save_string() {
if (isset($_POST['source']) && isset($_POST['target']) && !empty($_POST['textgroup']) && !empty($_POST['form_token']) && drupal_valid_token($_POST['form_token'], 'l10n_client_form')) {
// Ensure we have this source string before we attempt to save it.
// @todo: add actual context support.
$lid = db_query("SELECT lid FROM {locales_source} WHERE source = :source AND context = :context AND textgroup = :textgroup", array(':source' => $_POST['source'], ':context' => '', ':textgroup' => $_POST['textgroup']))->fetchField();
$lid = db_query("SELECT lid FROM {locales_source} WHERE source = :source AND context = :context AND textgroup = :textgroup", array(
':source' => $_POST['source'],
':context' => '',
':textgroup' => $_POST['textgroup'],
))->fetchField();
if (!empty($lid)) {
module_load_include('translation.inc', 'l10n_update');
$report = array('skips' => 0, 'additions' => 0, 'updates' => 0, 'deletes' => 0);
$report = array(
'skips' => 0,
'additions' => 0,
'updates' => 0,
'deletes' => 0,
);
// @todo: add actual context support.
_l10n_update_locale_import_one_string_db($report, $language->language, '', $_POST['source'], $_POST['target'], $_POST['textgroup'], NULL, LOCALE_IMPORT_OVERWRITE, L10N_UPDATE_STRING_CUSTOM);
cache_clear_all('locale:', 'cache', TRUE);
@@ -428,7 +455,7 @@ function l10n_update_client_save_string() {
$message = theme('l10n_client_message', array('message' => t('Translation saved locally.'), 'level' => WATCHDOG_INFO));
}
elseif (!empty($report['deletes'])) {
$message = theme('l10n_client_message', array('message' => t('Translation successfuly removed locally.'), 'level' => WATCHDOG_INFO));
$message = theme('l10n_client_message', array('message' => t('Translation successfully removed locally.'), 'level' => WATCHDOG_INFO));
}
else {
$message = theme('l10n_client_message', array('message' => t('Unknown error while saving translation locally.')));
@@ -441,9 +468,15 @@ function l10n_update_client_save_string() {
$message .= theme('l10n_client_message', array('message' => $remote_result[1], 'level' => $remote_result[0] ? WATCHDOG_INFO : WATCHDOG_ERROR));
}
else {
$server_url = variable_get('l10n_client_server', 'http://localize.drupal.org');
$server_url = variable_get('l10n_client_server', 'https://localize.drupal.org');
$user_edit_url = url('user/' . $user->uid . '/edit', array('absolute' => TRUE));
$message .= theme('l10n_client_message', array('message' => t('You could share your work with !l10n_server if you set your API key at !user_link.', array('!l10n_server' => l($server_url, $server_url), '!user_link' => l($user_edit_url, 'user/' . $user->uid . '/edit'))), 'level' => WATCHDOG_WARNING));
$message .= theme('l10n_client_message', array(
'message' => t('You could share your work with !l10n_server if you set your API key at !user_link.', array(
'!l10n_server' => l($server_url, $server_url),
'!user_link' => l($user_edit_url, 'user/' . $user->uid . '/edit'),
)),
'level' => WATCHDOG_WARNING,
));
}
}
}
@@ -488,7 +521,8 @@ function l10n_update_system_update(array $components) {
$projects = array_keys(l10n_update_build_projects());
if ($list = array_intersect($list, $projects)) {
module_load_include('fetch.inc', 'l10n_update');
// Get translation status of the projects, download and update translations.
// Get translation status of the projects, download and update
// translations.
$options = _l10n_update_default_update_options();
$batch = l10n_update_batch_update_build($list, array(), $options);
batch_set($batch);
@@ -507,7 +541,7 @@ function l10n_update_system_update(array $components) {
* An array of arrays of component (theme and/or module) names to import
* translations for, indexed by type.
*/
function l10n_update_system_remove($components) {
function l10n_update_system_remove(array $components) {
$components += array('module' => array(), 'theme' => array());
$list = array_merge($components['module'], $components['theme']);
if ($language_list = l10n_update_translatable_language_list()) {
@@ -515,7 +549,8 @@ function l10n_update_system_remove($components) {
module_load_include('bulk.inc', 'l10n_update');
// Only when projects are removed, the translation files and records will be
// deleted. Not each disabled module will remove a project. E.g. sub modules.
// deleted. Not each disabled module will remove a project. E.g. sub
// modules.
$projects = array_keys(l10n_update_get_projects());
if ($list = array_intersect($list, $projects)) {
l10n_update_file_history_delete($list);
@@ -524,7 +559,7 @@ function l10n_update_system_remove($components) {
l10n_update_delete_translation_files($list, array());
// Remove translatable projects.
// Followup issue http://drupal.org/node/1842362 to replace the
// Followup issue https://www.drupal.org/node/1842362 to replace the
// {l10n_update_project} table. Then change this to a function call.
db_delete('l10n_update_project')
->condition('name', $list)
@@ -564,7 +599,7 @@ function l10n_update_get_file_history() {
* @param object $file
* Object representing the file just imported.
*
* @return integer
* @return int
* FALSE on failure. Otherwise SAVED_NEW or SAVED_UPDATED.
*
* @see drupal_write_record()
@@ -591,7 +626,7 @@ function l10n_update_update_file_history($file) {
* @param array $projects
* Project name(s) to be deleted from the file history. If both project(s) and
* language code(s) are specified the conditions will be ANDed.
* @param array $langcode
* @param array $langcodes
* Language code(s) to be deleted from the file history.
*/
function l10n_update_file_history_delete($projects = array(), $langcodes = array()) {
@@ -611,9 +646,11 @@ function l10n_update_file_history_delete($projects = array(), $langcodes = array
* @todo What is 'translation status'?
*/
function l10n_update_get_status($projects = NULL, $langcodes = NULL) {
$result = array();
$status = variable_get('l10n_update_translation_status', array());
module_load_include('translation.inc', 'l10n_update');
$result = array();
$cache = cache_get('l10n_update_status');
$status = $cache ? $cache->data : array();
$projects = $projects ? $projects : array_keys(l10n_update_get_projects());
$langcodes = $langcodes ? $langcodes : array_keys(l10n_update_translatable_language_list());
@@ -644,14 +681,13 @@ function l10n_update_get_status($projects = NULL, $langcodes = NULL) {
* Language code.
* @param string $type
* Type of data to be stored.
* @param array $data
* @param object $data
* File object also containing timestamp when the translation is last updated.
*/
function l10n_update_status_save($project, $langcode, $type, $data) {
// Followup issue: http://drupal.org/node/1842362
// Followup issue: https://www.drupal.org/node/1842362
// Split status storage per module/language and expire individually. This will
// improve performance for large sites.
// Load the translation status or build it if not already available.
module_load_include('translation.inc', 'l10n_update');
$status = l10n_update_get_status();
@@ -672,23 +708,29 @@ function l10n_update_status_save($project, $langcode, $type, $data) {
// Check if this translation is the most recent one. Set timestamp and
// data type of the most recent translation source.
if (isset($data->timestamp) && $data->timestamp) {
if ($data->timestamp > $status[$project][$langcode]->timestamp) {
$version_changed = isset($status[$project][$langcode]->files[L10N_UPDATE_CURRENT]->version) &&
$status[$project][$langcode]->files[L10N_UPDATE_CURRENT]->version != $data->version;
if (($data->timestamp > $status[$project][$langcode]->timestamp) || $version_changed) {
$status[$project][$langcode]->timestamp = $data->timestamp;
$status[$project][$langcode]->last_checked = REQUEST_TIME;
$status[$project][$langcode]->version = $data->version;
$status[$project][$langcode]->type = $type;
}
}
break;
case L10N_UPDATE_CURRENT:
$data->last_checked = REQUEST_TIME;
$status[$project][$langcode]->timestamp = $data->timestamp;
$status[$project][$langcode]->last_checked = $data->last_checked;
$status[$project][$langcode]->version = $data->version;
$status[$project][$langcode]->type = $type;
$status[$project][$langcode]->files[$type] = $data;
l10n_update_update_file_history($data);
break;
}
variable_set('l10n_update_translation_status', $status);
cache_set('l10n_update_status', $status);
variable_set('l10n_update_last_check', REQUEST_TIME);
}
}
@@ -699,7 +741,7 @@ function l10n_update_status_save($project, $langcode, $type, $data) {
* @param array $langcodes
* Language code(s) to be deleted from the cache.
*/
function l10n_update_status_delete_languages($langcodes) {
function l10n_update_status_delete_languages(array $langcodes) {
if ($status = l10n_update_get_status()) {
foreach ($status as $project => $languages) {
foreach ($languages as $langcode => $source) {
@@ -708,7 +750,7 @@ function l10n_update_status_delete_languages($langcodes) {
}
}
}
variable_set('l10n_update_translation_status', $status);
cache_set('l10n_update_status', $status);
}
}
@@ -718,7 +760,7 @@ function l10n_update_status_delete_languages($langcodes) {
* @param array $projects
* Project name(s) to be deleted from the cache.
*/
function l10n_update_status_delete_projects($projects) {
function l10n_update_status_delete_projects(array $projects) {
$status = l10n_update_get_status();
foreach ($status as $project => $languages) {
@@ -726,7 +768,7 @@ function l10n_update_status_delete_projects($projects) {
unset($status[$project]);
}
}
variable_set('l10n_update_translation_status', $status);
cache_set('l10n_update_status', $status);
}
/**
@@ -738,6 +780,8 @@ function l10n_update_status_delete_projects($projects) {
function l10n_update_translatable_language_list() {
$languages = locale_language_list('name');
unset($languages['en']);
drupal_alter('l10n_update_languages', $languages);
return $languages;
}
@@ -745,8 +789,7 @@ function l10n_update_translatable_language_list() {
* Clear the translation status cache.
*/
function l10n_update_clear_status() {
variable_del('l10n_update_translation_status');
variable_del('l10n_update_last_check');
cache_clear_all('l10n_update_status', 'cache');
}
/**

View File

@@ -9,6 +9,7 @@
* Comparison result of source files timestamps.
*
* Timestamp of source 1 is less than the timestamp of source 2.
*
* @see _l10n_update_source_compare()
*/
define('L10N_UPDATE_SOURCE_COMPARE_LT', -1);
@@ -17,6 +18,7 @@ define('L10N_UPDATE_SOURCE_COMPARE_LT', -1);
* Comparison result of source files timestamps.
*
* Timestamp of source 1 is equal to the timestamp of source 2.
*
* @see _l10n_update_source_compare()
*/
define('L10N_UPDATE_SOURCE_COMPARE_EQ', 0);
@@ -25,6 +27,7 @@ define('L10N_UPDATE_SOURCE_COMPARE_EQ', 0);
* Comparison result of source files timestamps.
*
* Timestamp of source 1 is greater than the timestamp of source 2.
*
* @see _l10n_update_source_compare()
*/
define('L10N_UPDATE_SOURCE_COMPARE_GT', 1);
@@ -41,7 +44,7 @@ define('L10N_UPDATE_SOURCE_COMPARE_GT', 1);
* disabled this function will return the last known module state. The status
* will only be updated once Update module is enabled.
*
* @params array $project_names
* @param array $project_names
* Array of names of the projects to get.
*
* @return array
@@ -55,7 +58,7 @@ function l10n_update_get_projects($project_names = array()) {
if (empty($projects)) {
// Get project data from the database.
$result = db_query('SELECT name, project_type, core, version, l10n_path as server_pattern, status FROM {l10n_update_project}');
// http://drupal.org/node/1777106 is a follow-up issue to make the check for
// https://www.drupal.org/node/1777106 is a follow-up issue to make the check for
// possible out-of-date project information more robust.
if ($result->rowCount() == 0) {
module_load_include('compare.inc', 'l10n_update');
@@ -97,7 +100,7 @@ function l10n_update_clear_cache_projects() {
*
* @see l10n_update_source_build()
*/
function l10n_update_load_sources($projects = NULL, $langcodes = NULL) {
function l10n_update_load_sources(array $projects = NULL, array $langcodes = NULL) {
$sources = array();
$projects = $projects ? $projects : array_keys(l10n_update_get_projects());
$langcodes = $langcodes ? $langcodes : array_keys(l10n_update_translatable_language_list());
@@ -106,7 +109,7 @@ function l10n_update_load_sources($projects = NULL, $langcodes = NULL) {
$status = l10n_update_get_status();
// Use only the selected projects and languages for update.
foreach($projects as $project) {
foreach ($projects as $project) {
foreach ($langcodes as $langcode) {
$sources[$project][$langcode] = isset($status[$project][$langcode]) ? $status[$project][$langcode] : NULL;
}
@@ -157,7 +160,7 @@ function l10n_update_build_sources($projects = array(), $langcodes = array()) {
* @param object $source
* Translation source object.
*
* @return stdClass
* @return object|bool
* Source file object of the po file, updated with:
* - "uri": File name and path.
* - "timestamp": Last updated time of the po file.
@@ -169,7 +172,7 @@ function l10n_update_source_check_file($source) {
if (isset($source->files[L10N_UPDATE_LOCAL])) {
$source_file = $source->files[L10N_UPDATE_LOCAL];
$directory = $source_file->directory;
$filename = '/' . preg_quote($source_file->filename) . '$/';
$filename = '/^' . preg_quote($source_file->filename) . '$/';
if ($files = file_scan_directory($directory, $filename, array('key' => 'name', 'recurse' => FALSE))) {
$file = current($files);
@@ -361,12 +364,15 @@ function l10n_update_cron_fill_queue() {
* @param string $uri
* The URI or URI pattern of the file.
*
* @return boolean
* @return bool
* TRUE if the $uri is a remote file.
*/
function _l10n_update_file_is_remote($uri) {
$scheme = file_uri_scheme($uri);
if ($scheme) {
if ($scheme == 'http' || $scheme == 'https') {
return TRUE;
}
return !drupal_realpath($scheme . '://');
}
return FALSE;
@@ -383,7 +389,7 @@ function _l10n_update_file_is_remote($uri) {
* @param object $source2
* Source object of available update.
*
* @return integer
* @return int
* - "L10N_UPDATE_SOURCE_COMPARE_LT": $source1 < $source2 OR $source1
* is missing.
* - "L10N_UPDATE_SOURCE_COMPARE_EQ": $source1 == $source2 OR both
@@ -431,12 +437,14 @@ function _l10n_update_default_update_options() {
'not_customized' => TRUE,
);
break;
case L10N_UPDATE_OVERWRITE_NON_CUSTOMIZED:
$options['overwrite_options'] = array(
'customized' => FALSE,
'not_customized' => TRUE,
);
break;
case LOCALE_IMPORT_KEEP:
$options['overwrite_options'] = array(
'customized' => FALSE,
@@ -451,34 +459,40 @@ function _l10n_update_default_update_options() {
/**
* Import one string into the database.
*
* @param $report
* @param array $report
* Report array summarizing the number of changes done in the form:
* array(inserts, updates, deletes).
* @param $langcode
* @param string $langcode
* Language code to import string into.
* @param $context
* @param string $context
* The context of this string.
* @param $source
* @param string $source
* Source string.
* @param $translation
* @param string $translation
* Translation to language specified in $langcode.
* @param $textgroup
* @param string $textgroup
* Name of textgroup to store translation in.
* @param $location
* @param string $location
* Location value to save with source string.
* @param $mode
* @param int $mode
* Import mode to use, LOCALE_IMPORT_KEEP or LOCALE_IMPORT_OVERWRITE.
* @param $status
* Status of translation if created: L10N_UPDATE_STRING_DEFAULT or L10N_UPDATE_STRING_CUSTOM
* @param $plid
* @param int $status
* Status of translation if created: L10N_UPDATE_STRING_DEFAULT or
* L10N_UPDATE_STRING_CUSTOM.
* @param int $plid
* Optional plural ID to use.
* @param $plural
* @param int $plural
* Optional plural value to use.
* @return
*
* @return string
* The string ID of the existing string modified or the new string added.
*/
function _l10n_update_locale_import_one_string_db(&$report, $langcode, $context, $source, $translation, $textgroup, $location, $mode, $status = L10N_UPDATE_NOT_CUSTOMIZED, $plid = 0, $plural = 0) {
$lid = db_query("SELECT lid FROM {locales_source} WHERE source = :source AND context = :context AND textgroup = :textgroup", array(':source' => $source, ':context' => $context, ':textgroup' => $textgroup))->fetchField();
function _l10n_update_locale_import_one_string_db(array &$report, $langcode, $context, $source, $translation, $textgroup, $location, $mode, $status = L10N_UPDATE_NOT_CUSTOMIZED, $plid = 0, $plural = 0) {
$lid = db_query("SELECT lid FROM {locales_source} WHERE source = :source AND context = :context AND textgroup = :textgroup", array(
':source' => $source,
':context' => $context,
':textgroup' => $textgroup,
))->fetchField();
if (!empty($translation)) {
// Skip this string unless it passes a check for dangerous code.

View File

@@ -12,6 +12,9 @@ class L10nUpdateCronTest extends L10nUpdateTestBase {
protected $batch_output = array();
/**
* {@inheritdoc}
*/
public static function getInfo() {
return array(
'name' => 'Update translations using cron',
@@ -20,7 +23,10 @@ class L10nUpdateCronTest extends L10nUpdateTestBase {
);
}
function setUp() {
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
$admin_user = $this->drupalCreateUser(array('administer modules', 'administer site configuration', 'administer languages', 'access administration pages', 'translate interface'));
$this->drupalLogin($admin_user);
@@ -30,7 +36,7 @@ class L10nUpdateCronTest extends L10nUpdateTestBase {
/**
* Tests interface translation update using cron.
*/
function testUpdateCron() {
public function testUpdateCron() {
// Set a flag to let the l10n_update_test module replace the project data
// with a set of test projects.
variable_set('l10n_update_test_projects_alter', TRUE);
@@ -112,4 +118,5 @@ class L10nUpdateCronTest extends L10nUpdateTestBase {
$this->assertTrue($current->timestamp > $initial->timestamp, 'Timestamp is updated');
$this->assertTrue($current->last_checked > $initial->last_checked, 'Last checked is updated');
}
}

View File

@@ -10,6 +10,9 @@
*/
class L10nUpdateInterfaceTest extends L10nUpdateTestBase {
/**
* {@inheritdoc}
*/
public static function getInfo() {
return array(
'name' => 'Update translations user interface',
@@ -18,7 +21,10 @@ class L10nUpdateInterfaceTest extends L10nUpdateTestBase {
);
}
function setUp() {
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
$admin_user = $this->drupalCreateUser(array('administer modules', 'administer site configuration', 'administer languages', 'access administration pages', 'translate interface'));
$this->drupalLogin($admin_user);
@@ -30,12 +36,19 @@ class L10nUpdateInterfaceTest extends L10nUpdateTestBase {
* Testing the Available updates summary on the side wide status page and the
* Avaiable translation updates page.
*/
function testInterface() {
public function testInterface() {
// Enable the module this test uses for its translations.
module_enable(array('l10n_update_test_translate'));
// Exclude l10n_update so no remote translations are fetched.
$edit = array(
'disabled_projects' => "l10n_update",
);
$this->drupalPost('admin/config/regional/language/update', $edit, t('Save configuration'));
// No language added.
// Check status page and Available translation updates page.
// Check status page and Translate interface update page.
$this->drupalGet('admin/reports/status');
$this->assertNoText(t('Translation update status'), 'No status message');
@@ -50,7 +63,7 @@ class L10nUpdateInterfaceTest extends L10nUpdateTestBase {
// mark Drupal core as translated and continue with the prepared modules.
$status = l10n_update_get_status();
$status['drupal']['de']->type = 'current';
variable_set('l10n_update_translation_status', $status);
cache_set('l10n_update_status', $status);
// One language added, all translations up to date.
$this->drupalGet('admin/reports/status');
@@ -59,15 +72,16 @@ class L10nUpdateInterfaceTest extends L10nUpdateTestBase {
$this->drupalGet('admin/config/regional/translate/update');
$this->assertText(t('All translations up to date.'), 'Translations up to date');
// Set l10n_update_test_translate module to have a local translation available.
// Set l10n_update_test_translate module to have a local translation
// available.
$status = l10n_update_get_status();
$status['l10n_update_test_translate']['de']->type = 'local';
variable_set('l10n_update_translation_status', $status);
cache_set('l10n_update_status', $status);
// Check if updates are available for German.
$this->drupalGet('admin/reports/status');
$this->assertText(t('Translation update status'), 'Status message');
$this->assertRaw(t('Updates available for: @languages. See the <a href="@updates">Available translation updates</a> page for more information.', array('@languages' => t('German'), '@updates' => url('admin/config/regional/translate/update'))), 'Updates available message');
$this->assertRaw(t('Updates available for: @languages. See the <a href="@updates">Translate interface update</a> page for more information.', array('@languages' => t('German'), '@updates' => url('admin/config/regional/translate/update'))), 'Updates available message');
$this->drupalGet('admin/config/regional/translate/update');
$this->assertText(t('Updates for: @modules', array('@modules' => 'Localization Update test translate')), 'Translations avaiable');
@@ -76,12 +90,12 @@ class L10nUpdateInterfaceTest extends L10nUpdateTestBase {
$status = l10n_update_get_status();
$status['l10n_update_test_translate']['de']->version = '1.3-dev';
$status['l10n_update_test_translate']['de']->type = '';
variable_set('l10n_update_translation_status', $status);
cache_set('l10n_update_status', $status);
// Check if no updates were found.
$this->drupalGet('admin/reports/status');
$this->assertText(t('Translation update status'), 'Status message');
$this->assertRaw(t('Missing translations for: @languages. See the <a href="@updates">Available translation updates</a> page for more information.', array('@languages' => t('German'), '@updates' => url('admin/config/regional/translate/update'))), 'Missing translations message');
$this->assertRaw(t('Missing translations for: @languages. See the <a href="@updates">Translate interface update</a> page for more information.', array('@languages' => t('German'), '@updates' => url('admin/config/regional/translate/update'))), 'Missing translations message');
$this->drupalGet('admin/config/regional/translate/update');
$this->assertText(t('Missing translations for one project'), 'No translations found');
$this->assertText(t('@module (@version).', array('@module' => 'Localization Update test translate', '@version' => '1.3-dev')), 'Release details');

View File

@@ -10,6 +10,9 @@
*/
class L10nUpdateTest extends L10nUpdateTestBase {
/**
* {@inheritdoc}
*/
public static function getInfo() {
return array(
'name' => 'Update translations',
@@ -18,15 +21,25 @@ class L10nUpdateTest extends L10nUpdateTestBase {
);
}
function setUp() {
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
$admin_user = $this->drupalCreateUser(array('administer modules', 'administer site configuration', 'administer languages', 'access administration pages', 'translate interface'));
$this->drupalLogin($admin_user);
// Exclude drupal core and nl10n_update so no remote translations are
// fetched.
$edit = array(
'disabled_projects' => 'drupal\nl10n_update',
);
$this->drupalPost('admin/config/regional/language/update', $edit, t('Save configuration'));
// We use German as test language. This language must match the translation
// file that come with the l10n_update_test module (test.de.po) and can therefore
// not be chosen randomly.
// file that come with the l10n_update_test module (test.de.po) and can
// therefore not be chosen randomly.
$this->addLanguage('de');
module_load_include('compare.inc', 'l10n_update');
@@ -36,21 +49,22 @@ class L10nUpdateTest extends L10nUpdateTestBase {
/**
* Checks if a list of translatable projects gets build.
*/
function testUpdateProjects() {
public function testUpdateProjects() {
module_load_include('compare.inc', 'l10n_update');
variable_set('l10n_update_test_projects_alter', TRUE);
// Make the test modules look like a normal custom module. i.e. make the
// modules not hidden. l10n_update_test_system_info_alter() modifies the project
// info of the l10n_update_test and l10n_update_test_translate modules.
// modules not hidden. l10n_update_test_system_info_alter() modifies the
// project info of the l10n_update_test and l10n_update_test_translate
// modules.
variable_set('l10n_update_test_system_info_alter', TRUE);
$this->resetAll();
// Check if interface translation data is collected from hook_info.
$projects = l10n_update_project_list();
$this->assertFalse(isset($projects['l10n_update_test_translate']), 'Hidden module not found');
$this->assertEqual($projects['l10n_update_test']['info']['interface translation server pattern'], 'sites/all/modules/l10n_update/tests/test.%language.po', 'Interface translation parameter found in project info.');
$this->assertEqual($projects['l10n_update_test']['name'] , 'l10n_update_test', format_string('%key found in project info.', array('%key' => 'interface translation project')));
$this->assertEqual($projects['l10n_update_test']['info']['l10n path'], drupal_get_path('module', 'l10n_update') . '/tests/test.%language.po', 'l10n path parameter found in project info.');
$this->assertEqual($projects['l10n_update_test']['name'], 'l10n_update_test', format_string('%key found in project info.', array('%key' => 'interface translation project')));
}
/**
@@ -67,9 +81,9 @@ class L10nUpdateTest extends L10nUpdateTestBase {
* the most recent files are selected in the different check scenarios: check
* for local files only, check for both local and remote files.
*/
function testUpdateCheckStatus() {
// Set a flag to let the l10n_update_test module replace the project data with a
// set of test projects.
public function testUpdateCheckStatus() {
// Set a flag to let the l10n_update_test module replace the project data
// with a set of test projects.
variable_set('l10n_update_test_projects_alter', TRUE);
// Create local and remote translations files.
@@ -116,18 +130,18 @@ class L10nUpdateTest extends L10nUpdateTestBase {
*
* Test conditions:
* - Source: remote and local files
* - Import overwrite: all existing translations
* - Import overwrite: all existing translations.
*/
function testUpdateImportSourceRemote() {
public function testUpdateImportSourceRemote() {
// Build the test environment.
$this->setTranslationFiles();
$this-> setCurrentTranslations();
$this->setCurrentTranslations();
variable_set('l10n_update_default_filename', '%project-%release.%language._po');
// Set the update conditions for this test.
$edit = array(
'l10n_update_check_mode' => L10N_UPDATE_USE_SOURCE_REMOTE_AND_LOCAL,
'overwrite' => LOCALE_IMPORT_OVERWRITE,
'l10n_update_import_mode' => LOCALE_IMPORT_OVERWRITE,
);
$this->drupalPost('admin/config/regional/language/update', $edit, t('Save configuration'));
@@ -176,18 +190,18 @@ class L10nUpdateTest extends L10nUpdateTestBase {
*
* Test conditions:
* - Source: local files only
* - Import overwrite: all existing translations
* - Import overwrite: all existing translations.
*/
function testUpdateImportSourceLocal() {
public function testUpdateImportSourceLocal() {
// Build the test environment.
$this->setTranslationFiles();
$this-> setCurrentTranslations();
$this->setCurrentTranslations();
variable_set('l10n_update_default_filename', '%project-%release.%language._po');
// Set the update conditions for this test.
$edit = array(
'l10n_update_check_mode' => L10N_UPDATE_USE_SOURCE_LOCAL,
'overwrite' => LOCALE_IMPORT_OVERWRITE,
'l10n_update_import_mode' => LOCALE_IMPORT_OVERWRITE,
);
$this->drupalPost('admin/config/regional/language/update', $edit, t('Save configuration'));
@@ -228,18 +242,18 @@ class L10nUpdateTest extends L10nUpdateTestBase {
*
* Test conditions:
* - Source: remote and local files
* - Import overwrite: only overwrite non-customized translations
* - Import overwrite: only overwrite non-customized translations.
*/
function testUpdateImportModeNonCustomized() {
public function testUpdateImportModeNonCustomized() {
// Build the test environment.
$this->setTranslationFiles();
$this-> setCurrentTranslations();
$this->setCurrentTranslations();
variable_set('l10n_update_default_filename', '%project-%release.%language._po');
// Set the test conditions.
$edit = array(
'l10n_update_check_mode' => L10N_UPDATE_USE_SOURCE_REMOTE_AND_LOCAL,
'overwrite' => L10N_UPDATE_OVERWRITE_NON_CUSTOMIZED,
'l10n_update_import_mode' => L10N_UPDATE_OVERWRITE_NON_CUSTOMIZED,
);
$this->drupalPost('admin/config/regional/language/update', $edit, t('Save configuration'));
@@ -262,18 +276,18 @@ class L10nUpdateTest extends L10nUpdateTestBase {
*
* Test conditions:
* - Source: remote and local files
* - Import overwrite: don't overwrite any existing translation
* - Import overwrite: don't overwrite any existing translation.
*/
function testUpdateImportModeNone() {
public function testUpdateImportModeNone() {
// Build the test environment.
$this->setTranslationFiles();
$this-> setCurrentTranslations();
$this->setCurrentTranslations();
variable_set('l10n_update_default_filename', '%project-%release.%language._po');
// Set the test conditions.
$edit = array(
'l10n_update_check_mode' => L10N_UPDATE_USE_SOURCE_REMOTE_AND_LOCAL,
'overwrite' => LOCALE_IMPORT_KEEP,
'l10n_update_import_mode' => LOCALE_IMPORT_KEEP,
);
$this->drupalPost('admin/config/regional/language/update', $edit, t('Save configuration'));
@@ -294,7 +308,7 @@ class L10nUpdateTest extends L10nUpdateTestBase {
/**
* Tests automatic translation import when a module is enabled.
*/
function testEnableUninstallModule() {
public function testEnableUninstallModule() {
// Make the hidden test modules look like a normal custom module.
variable_set('l10n_update_test_system_info_alter', TRUE);
@@ -308,23 +322,24 @@ class L10nUpdateTest extends L10nUpdateTestBase {
$this->drupalPost('admin/modules', $edit, t('Save configuration'));
// Check if translations have been imported.
// @TODO: Find out why this currently returns 0 translations.
$this->assertRaw(t('One translation file imported. %number translations were added, %update translations were updated and %delete translations were removed.',
array('%number' => 0, '%update' => 7, '%delete' => 0)), 'One translation file imported.');
array('%number' => 0, '%update' => 0 /* 7 */, '%delete' => 0)), 'One translation file imported.');
$this->assertTranslation('Tuesday', 'Dienstag', 'de');
// // Disable and uninstall a module
// module_disable(array('l10n_update_test_translate'));
// $edit = array(
// 'uninstall[l10n_update_test_translate]' => '1',
// );
// $this->drupalPost('admin/modules/uninstall', $edit, t('Uninstall'));
// $this->drupalPost(NULL, array(), t('Uninstall'));
//
// // Check if the file data is removed from the database.
// $history = l10n_update_get_file_history();
// $this->assertFalse(isset($history['l10n_update_test_translate']), 'Project removed from the file history');
// $projects = l10n_update_get_projects();
// $this->assertFalse(isset($projects['l10n_update_test_translate']), 'Project removed from the project list');
// Disable and uninstall a module
// module_disable(array('l10n_update_test_translate'));
// $edit = array(
// 'uninstall[l10n_update_test_translate]' => '1',
// );
// $this->drupalPost('admin/modules/uninstall', $edit, t('Uninstall'));
// $this->drupalPost(NULL, array(), t('Uninstall'));
//
// // Check if the file data is removed from the database.
// $history = l10n_update_get_file_history();
// $this->assertFalse(isset($history['l10n_update_test_translate']), 'Project removed from the file history');
// $projects = l10n_update_get_projects();
// $this->assertFalse(isset($projects['l10n_update_test_translate']), 'Project removed from the project list');
}
/**
@@ -334,7 +349,7 @@ class L10nUpdateTest extends L10nUpdateTestBase {
* enabled modules and will import them. When a language is removed the system
* will remove all translations of that langugue from the database.
*/
function testEnableLanguage() {
public function testEnableLanguage() {
// Make the hidden test modules look like a normal custom module.
variable_set('l10n_update_test_system_info_alter', TRUE);
@@ -352,8 +367,9 @@ class L10nUpdateTest extends L10nUpdateTestBase {
$this->addLanguage('nl');
// Check if the right number of translations are added.
// @TODO: Find out why this currently returns 0 translations.
$this->assertRaw(t('One translation file imported. %number translations were added, %update translations were updated and %delete translations were removed.',
array('%number' => 0, '%update' => 8, '%delete' => 0)), 'One language added.');
array('%number' => 0, '%update' => 0 /* 8 */, '%delete' => 0)), 'One language added.');
$this->assertTranslation('Extraday', 'extra dag', 'nl');
// Check if the language data is added to the database.
@@ -375,7 +391,7 @@ class L10nUpdateTest extends L10nUpdateTestBase {
/**
* Tests automatic translation import when a custom langauge is enabled.
*/
function testEnableCustomLanguage() {
public function testEnableCustomLanguage() {
// Make the hidden test modules look like a normal custom module.
variable_set('l10n_update_test_system_info_alter', TRUE);
@@ -433,8 +449,7 @@ class L10nUpdateTest extends L10nUpdateTestBase {
'translation' => 'translated',
);
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
// $this->assertText('Allowed HTML source string', 'String successfully imported.');
$this->assertNoText('Another allowed HTML source string', 'String with disallowed translation not imported.');
// $this->assertText('Allowed HTML source string', 'String successfully imported.'); $this->assertNoText('Another allowed HTML source string', 'String with disallowed translation not imported.');
}
}

View File

@@ -31,7 +31,10 @@ class L10nUpdateTestBase extends DrupalWebTestCase {
*/
protected $timestamp_new;
function setUp() {
/**
*
*/
public function setUp() {
parent::setUp('update', 'locale', 'l10n_update', 'l10n_update_test');
// Setup timestamps to identify old and new translation sources.
@@ -56,7 +59,7 @@ class L10nUpdateTestBase extends DrupalWebTestCase {
/**
* Adds a language.
*
* @param $langcode
* @param string $langcode
* The language code of the language to add.
*/
protected function addLanguage($langcode) {
@@ -74,7 +77,7 @@ class L10nUpdateTestBase extends DrupalWebTestCase {
* Path of the file relative to the public file path.
* @param string $filename
* Name of the file to create.
* @param integer $timestamp
* @param int $timestamp
* Timestamp to set the file to. Defaults to current time.
* @param array $translations
* Array of source/target value translation strings. Only singular strings
@@ -99,8 +102,8 @@ EOF;
// Convert array of translations to Gettext source and translation strings.
if ($translations) {
foreach ($translations as $source => $target) {
$text .= 'msgid "'. $source . '"' . "\n";
$text .= 'msgstr "'. $target . '"' . "\n";
$text .= 'msgid "' . $source . '"' . "\n";
$text .= 'msgstr "' . $target . '"' . "\n";
}
}
@@ -151,8 +154,8 @@ EOF;
* imported.
*/
protected function setTranslationFiles() {
// A flag is set to let the l10n_update_test module replace the project data with
// a set of test projects which match the below project files.
// A flag is set to let the l10n_update_test module replace the project data
// with a set of test projects which match the below project files.
variable_set('l10n_update_test_projects_alter', TRUE);
// Setup the environment.
@@ -162,7 +165,7 @@ EOF;
// Setting up sets of translations for the translation files.
$translations_one = array('January' => 'Januar_1', 'February' => 'Februar_1', 'March' => 'Marz_1');
$translations_two = array( 'February' => 'Februar_2', 'March' => 'Marz_2', 'April' => 'April_2');
$translations_two = array('February' => 'Februar_2', 'March' => 'Marz_2', 'April' => 'April_2');
$translations_three = array('April' => 'April_3', 'May' => 'Mai_3', 'June' => 'Juni_3');
// Add a number of files to the local file system to serve as remote
@@ -182,8 +185,7 @@ EOF;
}
/**
* Setup existing translations in the database and set up the status of
* existing translations.
* Setup existing translations in the database and their status.
*/
protected function setCurrentTranslations() {
// Setup to add German translations to the database.
@@ -281,4 +283,5 @@ EOF;
$db_translation = $db_translation == FALSE ? '' : $db_translation;
$this->assertEqual($translation, $db_translation, $message ? $message : format_string('Correct translation of %source (%language)', array('%source' => $source, '%language' => $langcode)));
}
}

View File

@@ -6,9 +6,9 @@ version = '1.2'
core = 7.x
hidden = true
; Information added by Drupal.org packaging script on 2014-11-10
version = "7.x-2.0"
; Information added by Drupal.org packaging script on 2017-09-18
version = "7.x-2.2"
core = "7.x"
project = "l10n_update"
datestamp = "1415625781"
datestamp = "1505717347"

View File

@@ -13,7 +13,8 @@
*/
function l10n_update_test_system_info_alter(&$info, $file, $type) {
// Only modify the system info if required.
// By default the l10n_update_test modules are hidden and have a project specified.
// By default the l10n_update_test modules are hidden and have a project
// specified.
// To test the module detection process by l10n_update_project_list() the
// test modules should mimic a custom module. I.e. be non-hidden.
if (variable_get('l10n_update_test_system_info_alter', FALSE)) {
@@ -45,10 +46,10 @@ function l10n_update_test_l10n_update_projects_alter(&$projects) {
$remote_url = $wrapper->getExternalUrl() . '/remote/';
// Completely replace the project data with a set of test projects.
$projects = array (
'contrib_module_one' => array (
$projects = array(
'contrib_module_one' => array(
'name' => 'contrib_module_one',
'info' => array (
'info' => array(
'name' => 'Contributed module one',
'l10n path' => $remote_url . '%core/%project/%project-%release.%language._po',
'package' => 'Other',
@@ -58,15 +59,15 @@ function l10n_update_test_l10n_update_projects_alter(&$projects) {
'_info_file_ctime' => 1348767306,
),
'datestamp' => '1344471537',
'includes' => array (
'includes' => array(
'contrib_module_one' => 'Contributed module one',
),
'project_type' => 'module',
'project_status' => TRUE,
),
'contrib_module_two' => array (
'contrib_module_two' => array(
'name' => 'contrib_module_two',
'info' => array (
'info' => array(
'name' => 'Contributed module two',
'l10n path' => $remote_url . '%core/%project/%project-%release.%language._po',
'package' => 'Other',
@@ -76,15 +77,15 @@ function l10n_update_test_l10n_update_projects_alter(&$projects) {
'_info_file_ctime' => 1348767306,
),
'datestamp' => '1344471537',
'includes' => array (
'includes' => array(
'contrib_module_two' => 'Contributed module two',
),
'project_type' => 'module',
'project_status' => TRUE,
),
'contrib_module_three' => array (
'contrib_module_three' => array(
'name' => 'contrib_module_three',
'info' => array (
'info' => array(
'name' => 'Contributed module three',
'l10n path' => $remote_url . '%core/%project/%project-%release.%language._po',
'package' => 'Other',
@@ -94,18 +95,18 @@ function l10n_update_test_l10n_update_projects_alter(&$projects) {
'_info_file_ctime' => 1348767306,
),
'datestamp' => '1344471537',
'includes' => array (
'includes' => array(
'contrib_module_three' => 'Contributed module three',
),
'project_type' => 'module',
'project_status' => TRUE,
),
'l10n_update_test' => array (
'l10n_update_test' => array(
'name' => 'l10n_update_test',
'info' => array (
'info' => array(
'name' => 'Locale test',
'interface translation project' => 'l10n_update_test',
'l10n path' => 'sites/all/modules/l10n_update/tests/test.%language.po',
'l10n path' => drupal_get_path('module', 'l10n_update') . '/tests/test.%language.po',
'package' => 'Other',
'version' => NULL,
'project' => 'l10n_update_test',
@@ -113,15 +114,15 @@ function l10n_update_test_l10n_update_projects_alter(&$projects) {
'datestamp' => 0,
),
'datestamp' => 0,
'includes' => array (
'includes' => array(
'l10n_update_test' => 'Locale test',
),
'project_type' => 'module',
'project_status' => TRUE,
),
'custom_module_one' => array (
'custom_module_one' => array(
'name' => 'custom_module_one',
'info' => array (
'info' => array(
'name' => 'Custom module one',
'interface translation project' => 'custom_module_one',
'l10n path' => 'translations://custom_module_one.%language.po',
@@ -132,7 +133,7 @@ function l10n_update_test_l10n_update_projects_alter(&$projects) {
'datestamp' => 0,
),
'datestamp' => 0,
'includes' => array (
'includes' => array(
'custom_module_one' => 'Custom module one',
),
'project_type' => 'module',

View File

@@ -8,9 +8,9 @@ hidden = true
interface translation project = l10n_update_test_translate
l10n path = sites/all/modules/contrib/l10n_update/tests/modules/l10n_update_test_translate/translations/l10n_update_test_translate.%language.po
; Information added by Drupal.org packaging script on 2014-11-10
version = "7.x-2.0"
; Information added by Drupal.org packaging script on 2017-09-18
version = "7.x-2.2"
core = "7.x"
project = "l10n_update"
datestamp = "1415625781"
datestamp = "1505717347"