Skip to content

Commit

Permalink
Create view to display translation inconsistencies
Browse files Browse the repository at this point in the history
  • Loading branch information
flodolo committed Mar 3, 2016
1 parent 1ea9391 commit 9e16a09
Show file tree
Hide file tree
Showing 14 changed files with 486 additions and 115 deletions.
117 changes: 117 additions & 0 deletions app/classes/Transvision/Consistency.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<?php
namespace Transvision;

/**
* Consistency class
*
* Functions used to analyze translation consistency
*
* @package Transvision
*/
class Consistency
{
/**
* Find duplicated strings (strings with the same text, ignoring case).
*
* @param array $strings_array Array of strings in the form
* string_id => string_value
*
* @return array Array of duplicated strings, same format as the
* input array
*/
public static function findDuplicates($strings_array)
{
$duplicates = [];
// Use array_filter to exclude empty strings
$strings_array = array_filter($strings_array);
asort($strings_array);

$previous_key = '';
$previous_value = '';
foreach ($strings_array as $key => $value) {
if (strcasecmp($previous_value, $value) === 0) {
$duplicates[$previous_key] = $previous_value;
$duplicates[$key] = $value;
}
$previous_value = $value;
$previous_key = $key;
}

return $duplicates;
}

/**
* Filter out strings that should not be evaluted for consistency.
*
* @param array $strings_array Array of strings in the form
* string_id => string_value
* @param string Repository identifier
*
* @return array Array of filtered strings, with known false positives
* removed
*/
public static function filterStrings($strings_array, $repo)
{
if (in_array($repo, Project::getDesktopRepositories())) {
$repository_type = 'desktop';
} elseif (in_array($repo, Project::getGaiaRepositories())) {
$repository_type = 'gaia';
} else {
$repository_type = $repo;
}

// Determine if a string should be excluded
$ignore_string = function ($key, $value) use ($repository_type) {
// Ignore strings of length 1 (e.g. accesskeys) and empty strings.
if (strlen($value) <= 1) {
return true;
}

// Exclude CSS rules, "width:" or "height:"
if (Strings::inString($value, ['width:', 'height:'])) {
return true;
}

// Exclude CSS width values like '38em'
if (preg_match('/[\d|\.]+em/', $value)) {
return true;
}

if ($repository_type == 'gaia') {
// Ignore plural forms containing [] in the key
if (Strings::inString($key, ['[', ']'])) {
return true;
}

// Ignore accessibility strings
if (strpos($key, 'accessibility.properties') !== false) {
return true;
}
}

if ($repository_type == 'desktop') {
/*
Ignore some specific files:
- AccessFu.properties: there are accessibility
strings that remain identical for English in full and
abbreviated form.
- defines.inc (language pack attribution)
- region.properties
*/
if (Strings::inString($key, ['AccessFu.properties', 'defines.inc', 'region.properties'])) {
return true;
}
}

return false;
};

foreach ($strings_array as $key => $value) {
if ($ignore_string($key, $value)) {
unset($strings_array[$key]);
}
}

return $strings_array;
}
}
26 changes: 20 additions & 6 deletions app/classes/Transvision/Strings.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,29 @@ public static function endsWith($haystack, $needles)
}

/**
* Check if $needle is in $haystack
* Check if $needles are in $haystack
*
* @param string $haystack String to analyse
* @param string $needle The string to look for
* @return boolean True if the $haystack string contains $needle
* @param string $haystack String to analyze
* @param mixed $needles The string (or array of strings) to look for
* @param boolean $match_all True if we need to match all $needles, false
* if it's enough to match one. Default: false
*
* @return boolean True if the $haystack string contains any/all $needles
*/
public static function inString($haystack, $needle)
public static function inString($haystack, $needles, $match_all = false)
{
return mb_strpos($haystack, $needle, $offset = 0, 'UTF-8') !== false ? true : false;
$matches = 0;
foreach ((array) $needles as $needle) {
if (mb_strpos($haystack, $needle, $offset = 0, 'UTF-8') !== false) {
// If I need to match any needle, I can stop at the first match
if (! $match_all) {
return true;
}
$matches++;
}
}

return $matches == count($needles);
}

/**
Expand Down
8 changes: 8 additions & 0 deletions app/controllers/consistency.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php
namespace Transvision;

// Get requested repo and locale
require_once INC . 'l10n-init.php';

include MODELS . 'consistency.php';
include VIEWS . 'consistency.php';
78 changes: 42 additions & 36 deletions app/inc/dispatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,38 +20,27 @@
$page_title = '3 locales search';
$page_descr = 'One source locale, get search results for two target locales';
break;
case 'news':
$controller = 'changelog';
$page_title = 'Transvision News and Release Notes';
$page_descr = '';
$css_include = ['changelog.css'];
case 'accesskeys':
$view = 'accesskeys';
$page_title = 'Access Keys';
$page_descr = 'Check your access keys.';
break;
case 'rss':
$controller = 'changelog';
case Strings::StartsWith($url['path'], 'api'):
$controller = 'api';
$page_title = 'API response';
$page_descr = '';
$template = false;
break;
case 'stats':
$view = 'showrepos';
$page_title = 'Status Overview';
$page_descr = 'Repository status overview.';
break;
case 'repocomparison':
$view = 'repocomparison';
break;
case 'gaia':
$view = 'gaia';
$page_title = 'Gaia Comparison';
$page_descr = 'Check the Status of your GAIA strings across repositories.';
break;
case 'channelcomparison':
$controller = 'channelcomparison';
$page_title = 'Channel Comparison';
$page_descr = 'Compare strings from channel to channel.';
break;
case 'accesskeys':
$view = 'accesskeys';
$page_title = 'Access Keys';
$page_descr = 'Check your access keys.';
case 'consistency':
$experimental = true;
$controller = 'consistency';
$page_title = 'Translation Consistency';
$page_descr = 'Analyze translation consistency across repositories.';
break;
case 'credits':
$view = 'credits';
Expand All @@ -64,13 +53,42 @@
$page_descr = 'Create and download your own <abbr title="Translation Memory eXchange">TMX</abbr> file containing the strings you need.';
$css_include = ['tmx.css'];
break;
case 'gaia':
$view = 'gaia';
$page_title = 'Gaia Comparison';
$page_descr = 'Check the Status of your GAIA strings across repositories.';
break;
case 'news':
$controller = 'changelog';
$page_title = 'Transvision News and Release Notes';
$page_descr = '';
$css_include = ['changelog.css'];
break;
case 'productization':
$view = 'productization';
$page_title = 'Productization Overview';
$page_descr = 'Show productization aspects for this locale.';
$css_include = ['productization.css'];
break;
case 'repocomparison':
$view = 'repocomparison';
break;
case 'rss':
$controller = 'changelog';
$template = false;
break;
case 'showrepos':
$experimental = true;
$controller = 'health_status';
$page_title = 'Health status';
$page_descr = 'Check the health status of locales.';
$css_include = ['health.css'];
break;
case 'stats':
$view = 'showrepos';
$page_title = 'Status Overview';
$page_descr = 'Repository status overview.';
break;
case 'string':
$controller = 'onestring';
$page_title = 'All translations for this string:';
Expand All @@ -86,18 +104,6 @@
$page_title = 'Variables Overview';
$page_descr = 'Show potential errors related to missing or mispelled variables in your strings.';
break;
case 'productization':
$view = 'productization';
$page_title = 'Productization Overview';
$page_descr = 'Show productization aspects for this locale.';
$css_include = ['productization.css'];
break;
case Strings::StartsWith($url['path'], 'api'):
$controller = 'api';
$page_title = 'API response';
$page_descr = '';
$template = false;
break;
default:
$controller = 'mainsearch';
break;
Expand Down
1 change: 1 addition & 0 deletions app/inc/urls.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
'rss' => 'rss',
'stats' => 'stats',
'channelcomparison' => 'channelcomp',
'consistency' => 'consistency',
'accesskeys' => 'keys',
'credits' => 'credits',
'downloads' => 'downloads',
Expand Down
75 changes: 75 additions & 0 deletions app/models/consistency.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php
namespace Transvision;

// Build arrays for the search form, ignore mozilla.org
$channel_selector = Utils::getHtmlSelectOptions(
array_diff($repos_nice_names, ['mozilla.org']),
$repo,
true
);

$target_locales_list = Utils::getHtmlSelectOptions(
Project::getRepositoryLocales($repo),
$locale
);

$reference_locale = Project::getReferenceLocale($repo);

// Set a default for the number of strings to display
$strings_number = 0;

$source_strings = Utils::getRepoStrings($reference_locale, $repo);
// Ignore empty strings in translations
$target_strings = array_filter(Utils::getRepoStrings($locale, $repo));

if (! empty($source_strings)) {
// Remove known problematic strings
$duplicated_strings_english = Consistency::filterStrings($source_strings, $repo);
// Find strings that are identical in English
$duplicated_strings_english = Consistency::findDuplicates($duplicated_strings_english);
}

if (! empty($duplicated_strings_english)) {
$inconsistent_translations = [];
$available_translations = [];

/*
Using CachingIterator to be able to look ahead during the foreach
cycle, since it's an associative array.
http://php.net/manual/en/class.cachingiterator.php
*/
$collection = new \CachingIterator(new \ArrayIterator($duplicated_strings_english));
foreach ($collection as $key => $value) {
// Ignore this string if is not available in the localization
if (! isset($target_strings[$key])) {
$available_translations = [];
continue;
}

$available_translations[] = $target_strings[$key];
/*
If the current English string is different from the previous one,
or I am at the last element, I need to store it with all available
translations collected so far.
$collection->getInnerIterator()->current() stores the value of
the next element.
*/
if (! $collection->hasNext() || $collection->getInnerIterator()->current() != $value) {
$available_translations = array_unique($available_translations);
/*
Store this string only if there's more than one translation.
If there's only one, translations are consistent.
*/
if (count($available_translations) > 1) {
$inconsistent_translations[] = [
'source' => $value,
'target' => $available_translations,
];
}
$available_translations = [];
}
}

$strings_number = count($inconsistent_translations);
}
10 changes: 4 additions & 6 deletions app/scripts/generate_sources
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,10 @@ if ($gaia_versions) {
// Create a JSON file with the list of all supported repositories in the correct order
$json_repositories = [];
foreach ($repositories as $repository) {
if ($repository['type'] != 'test') {
$json_repositories[intval($repository['display_order'])] = [
'id' => $repository['id'],
'name' => $repository['name']
];
}
$json_repositories[intval($repository['display_order'])] = [
'id' => $repository['id'],
'name' => $repository['name']
];
}
ksort($json_repositories);
echo "* Saving JSON record of all supported repositories\n";
Expand Down
Loading

0 comments on commit 9e16a09

Please sign in to comment.