aboutsummaryrefslogtreecommitdiff
path: root/cli/help-translation-tool.php
diff options
context:
space:
mode:
Diffstat (limited to 'cli/help-translation-tool.php')
-rwxr-xr-xcli/help-translation-tool.php543
1 files changed, 0 insertions, 543 deletions
diff --git a/cli/help-translation-tool.php b/cli/help-translation-tool.php
deleted file mode 100755
index 6323eae..0000000
--- a/cli/help-translation-tool.php
+++ /dev/null
@@ -1,543 +0,0 @@
-#!/usr/bin/env php
-<?php
-/**
- * help-translation-tool.php
- *
- * Exports db data for the help content, tooltips and tours into a .po file or
- * reimports the translated strings into the db.
- *
- * Since we need to obtain the row to inssert/update the translated content,
- * this information is coded into the corresponding filename and line number.
- *
- * By using a specific range for line number, we can determine what type the
- * translated string is:
- *
- * range | context | location | file | line number
- * --------------+------------+------------------------+-------+-------------
- * 10000 - 19999 | - | help_content.label | route | position
- * 20000 - 29999 | content_id | help_content.content | route | position
- * 30000 - 39999 | tour_id | help_tours.name | - | version
- * 40000 - 49999 | tour_id | help_tours.description | - | version
- * 50000 - 59999 | tour_id | help_tour_steps.title | route | step
- * 60000 - 69999 | tour_id | help_tour_steps.tip | route | step
- * 70000 - 79999 | tooltip_id | help_tooltips.content | route | version
- *
- * @author Jan-Hendrik Willms <tleilax+studip@gmail.com>
- * @license GPL2 or any later version
- * @copyright Stud.IP Core Group
- * @since 3.1
- */
-
-require_once 'studip_cli_env.inc.php';
-
-define('MAX_LINE_LENGTH', 60);
-
-/**
- * Escapes a string for use in .po file.
- *
- * @param String $string String to escape
- * @return String Escaped string
- */
-function po_escape($string) {
- return str_replace('"', '\\"', $string);
-}
-
-/**
- * Unescapes a string for use in .po file.
- *
- * @param String $string String to unescape
- * @return String Unescaped string
- */
-function po_unescape($string) {
- $replaces = [
- '\\"' => '"',
- '\\n' => "\n",
- ];
- $string = str_replace(array_keys($replaces), array_values($replaces), $string);
- return $string;
-}
-
-/**
- * Prepares a string for use in .po file.
- *
- * @param String $string String to use in .po file
- * @return String Processed string
- */
-function po_stringify($string) {
- $string = str_replace("\r", '', $string);
- $chunks = explode("\n", $string);
-
- if (count($chunks) === 1 && mb_strlen($chunks[0]) < MAX_LINE_LENGTH) {
- return '"' . po_escape($chunks[0]) . '"';
- }
-
- $result = '""' . "\n";
- foreach ($chunks as $index => $chunk) {
- $chunk = wordwrap($chunk, MAX_LINE_LENGTH);
- $parts = explode("\n", $chunk);
- foreach ($parts as $idx => $line) {
- $current_last = $idx === count($parts) - 1;
- $last = ($current_last && $index === count($chunks) - 1);
-
- $result .= '"' . po_escape($line) . ($last ? '' : ($current_last ? '\\n' : ' ')) . '"'. "\n";
- }
- }
- return rtrim($result, "\n");
-}
-
-/**
- * Returns the id for a help entitiy based on the given index and other
- * credentials. This function also copies existing data and settings if
- * the entity in the given language is newly created.
- *
- * @param String $version Stud.IP version to use for the new entry
- * @param String $language Language to use for the new entry
- * @param Array $message Complete message item from parsed .po file
- * @param String $route Associated route (if any)
- * @param int $index Type index for the entity
- * @param int $position Position/version of the entity
- * @return String Id of the entity
- */
-function get_id($version, $language, $message, $route, $index, $position) {
- static $ids = [];
-
- if ($index < 3) {
- // Entity is help content
- $hash = md5('content#' . join('#', compact(words('temp version language route position'))));
- } elseif ($index < 7) {
- // Entity is help tour content
- $hash = md5('tour#' . $message['context'] . '#' . join('#' , compact(words('version language'))));
- } elseif ($index == 7) {
- // Entity is help tooltip
- $hash = md5('tooltip#' . $message['context'] . '#' . join(words('position language')));
- } else {
- throw new RuntimeException('Unknown index "' . $index . '"');
- }
-
- // If id has not yet been generated
- if (!isset($ids[$hash])) {
- if ($index < 3) {
- // Help content
-
- // Try to get content id by primary key
- $query = "SELECT content_id
- FROM help_content
- WHERE route = :route AND studip_version = :version
- AND language = :language AND position = :position
- AND custom = 0";
- $statement = DBManager::get()->prepare($query);
- $statement->bindValue(':route', $route);
- $statement->bindValue(':version', $version);
- $statement->bindValue(':language', $language);
- $statement->bindValue(':position', $position);
- $statement->execute();
-
- // Use found id or generate new one
- $id = $statement->fetchColumn() ?: md5(uniqid('help_content', true));
- $ids[$hash] = $id;
- } elseif ($index < 7) {
- // Help tour
-
- // Is there any previous generated content?
- // We have to use the hash generated above as the new id since
- // there is no other way to exactly identify an already created
- // entity for the given language and version
- $query = "SELECT tour_id
- FROM help_tours
- WHERE tour_id = :tour_id";
- $statement = DBManager::get()->prepare($query);
- $statement->bindValue(':tour_id', $hash);
- $statement->execute();
-
- $id = $statement->fetchColumn();
- if (!$id) {
- // If no previous generated content is available, prepare
- // database for new content
- $id = $hash;
-
- // Copy settings from tour
- $query = "INSERT INTO help_tours
- SELECT :id AS tour_id, '' AS name, '' AS description,
- type, roles, version, :language AS language,
- :version AS studip_version, installation_id,
- UNIX_TIMESTAMP() AS mkdate
- FROM help_tours
- WHERE tour_id = :tour_id";
- $statement = DBManager::get()->prepare($query);
- $statement->bindValue(':id', $id);
- $statement->bindValue(':language', $language);
- $statement->bindValue(':version', $version);
- $statement->bindValue(':tour_id', $message['context']);
- $statement->execute();
-
- // Copy individual steps
- $query = "INSERT INTO help_tour_steps
- SELECT :id AS tour_id, step, '' AS title, '' AS tip,
- orientation, interactive, css_selector, route,
- author_id, UNIX_TIMESTAMP() AS mkdate
- FROM help_tour_steps
- WHERE tour_id = :tour_id";
- $statement = DBManager::get()->prepare($query);
- $statement->bindValue(':id', $id);
- $statement->bindValue(':tour_id', $message['context']);
- $statement->execute();
-
- // Copy tour audiences
- $query = "INSERT INTO help_tour_audiences
- SELECT :id AS tour_id, range_id, type
- FROM help_tour_audiences
- WHERE tour_id = :tour_id";
- $statement = DBManager::get()->prepare($query);
- $statement->bindValue(':id', $id);
- $statement->bindValue(':tour_id', $message['context']);
- $statement->execute();
-
- // Copy tour settings
- $query = "INSERT INTO help_tour_settings
- SELECT :id AS tour_id, active, access
- FROM help_tour_settings
- WHERE tour_id = :tour_id";
- $statement = DBManager::get()->prepare($query);
- $statement->bindValue(':id', $id);
- $statement->bindValue(':tour_id', $message['context']);
- $statement->execute();
- }
- $ids[$hash] = $id;
- } elseif ($index == 7) {
- // Help tooltip
-
- // Nothing needs to be done, just copy the tooltip id
- // (This is the only table that has the id and version/language
- // info as primary key)
- $ids[$hash] = $message['context'];
- }
- }
-
- // Return id from cache
- return $ids[$hash];
-}
-
-// Error message: Not via cli or invalid parameters
-if (!isset($_SERVER['argv'], $_SERVER['argc']) || $_SERVER['argc'] < 2) {
- print 'Usage: ' . (@$_SERVER['argv'][0] ?: basename(__FILE__)) . ' [--version] [--language] [--force] <import|export> [file]' . "\n";
- die(1);
-}
-
-// Parse command line options
-$opts = [
- 'short' => 'v:l:f',
- 'long' => [
- 'force',
- 'version:',
- 'language:'
- ]
-];
-$options = getopt($opts['short'], $opts['long']);
-$force = isset($options['f']) || isset($options['force']);
-$version = @$options['version'] ?: @$options['v']
- ?: DBManager::get()->query("SELECT MAX(studip_version) FROM help_content LIMIT 1")->fetchColumn()
- ?: $GLOBALS['SOFTWARE_VERSION'];
-$language = @$options['language'] ?: @$options['l'] ?: mb_substr(Config::get()->DEFAULT_LANGUAGE, 0, 2);
-
-// Remove option from arguments
-$remove = [];
-foreach (str_split($opts['short']) as $opt) {
- if ($opt !== ':') {
- $remove[] = '-' . $opt;
- }
-}
-foreach ($opts['long'] as $opt) {
- $remove[] = '--' . rtrim($opt, ':');
-}
-$_SERVER['argv'] = array_values(array_diff($_SERVER['argv'], $remove));
-
-if ($_SERVER['argv'][1] === 'export') {
- // Export
-
- // Get output file name
- // Either from second parameter or use default at temp path
- $output = $_SERVER['argv'][2] ?: ($GLOBALS['TMP_PATH'] . '/studip-help-content-' . $version . '-' . $language . '.po');
-
- // Error message: Script will not overwrite existing file unless forced
- if (file_exists($output) && !$force) {
- printf('Error: Output file "%s" exists. Use --force to overwrite.' . "\n", $output);
- die(2);
- }
-
- // Error message: Output directory does not exist
- $output_dir = dirname($output);
- if (!file_exists($output_dir)) {
- printf('Error: Directory for output "%s" does not exist.' . "\n", $output_dir);
- die(3);
- }
- // Error message: Output directory is not writable
- if (!is_writable($output_dir)) {
- printf('Error: Directory for output "%s" is not writable.' . "\n", $output_dir);
- die(4);
- }
-
- // Open output file for writing
- $fp = fopen($output, 'w+');
- // Error message: Output file could not be openend for writing
- if (!is_resource($fp)) {
- printf('Error: Could not open output file "%s" for writing.' . "\n", $output);
- die(5);
- }
-
- // Write .po header
- fputs($fp, '# Jan-Hendrik Willms <tleilax+studip@gmail.com>, 2014.' . "\n");
- fputs($fp, '# Generated content' . "\n");
- fputs($fp, 'msgid ""' . "\n");
- fputs($fp, 'msgstr ""' . "\n");
- fputs($fp, '"Project-Id-Version: STUDIP-' . $GLOBALS['SOFTWARE_VERSION'] . '\\n"' . "\n");
- fputs($fp, '"Language: STUDIP-' . $language . '\\n"' . "\n");
- fputs($fp, '"Report-Msgid-Bugs-To: tleilax+studip@gmail.com' . '\\n"' . "\n");
- fputs($fp, '"POT-Creation-Date: ' . date('r') . '\\n"' . "\n");
- fputs($fp, '"PO-Revision-Date: ' . date('r') . '\\n"' . "\n");
- fputs($fp, '"Last-Translator: Stud.IP Core Group <info@studip.de>\\n"' . "\n");
- fputs($fp, '"Language-Team: Stud.IP Core Group <info@studip.de>\\n"' . "\n");
- fputs($fp, '"MIME-Version: 1.0\\n"' . "\n");
- fputs($fp, '"Content-Type: text/plain; charset=UTF-8\\n"' . "\n");
- fputs($fp, '"Content-Transfer-Encoding: 8bit\\n"' . "\n");
- fputs($fp, "\n");
-
- // Load all data from db in one big query
- $query = "SELECT label AS content, CONCAT(route, ':', 10000 + position) AS occurence
- FROM help_content
- WHERE studip_version = :version
- AND language = :language
- AND custom = 0
- -- Help content label
-
- UNION
-
- SELECT CONCAT(content, '{#$#}', content_id) AS content, CONCAT(route, ':', 20000 + position) AS occurence
- FROM help_content
- WHERE studip_version = :version
- AND language = :language
- AND custom = 0
- -- Actual help content
-
- UNION
-
- SELECT CONCAT(name, '{#$#}', tour_id) AS content, CONCAT('tours.php:', 30000 + version) AS occurence
- FROM help_tours
- WHERE studip_version = :version
- AND language = :language
- -- Help tour name
-
- UNION
-
- SELECT CONCAT(description, '{#$#}', tour_id) AS content, CONCAT('tours.php:', 40000 + version) AS occurence
- FROM help_tours
- WHERE studip_version = :version
- AND language = :language
- -- Help tour description
-
- UNION
-
- SELECT CONCAT(title, '{#$#}', tour_id) AS content, CONCAT(route, ':', 50000 + step) AS occurence
- FROM help_tour_steps
- JOIN help_tours USING (tour_id)
- WHERE studip_version = :version
- AND language = :language
- -- Individual help tour step title
-
- UNION
-
- SELECT CONCAT(tip, '{#$#}', tour_id) AS content, CONCAT(route, ':', 60000 + step) AS occurence
- FROM help_tour_steps
- JOIN help_tours USING (tour_id)
- WHERE studip_version = :version
- AND language = :language
- -- Individual help tour step content
-
- UNION
-
- SELECT CONCAT(t0.content, '{#$#}', t0.tooltip_id) AS content, CONCAT(t0.route, ':', 70000 + t0.version) AS occurence
- FROM help_tooltips AS t0
- LEFT JOIN help_tooltips AS t1
- ON t0.language = t1.language
- AND t0.tooltip_id = t1.tooltip_id
- AND t0.version < t1.version
- WHERE t0.language = :language AND t1.tooltip_id IS NULL
- -- Help tooltip
- ";
- $statement = DBManager::get()->prepare($query);
- $statement->bindValue(':version', $version);
- $statement->bindValue(':language', $language);
- $statement->execute();
- $statement->setFetchMode(PDO::FETCH_GROUP | PDO::FETCH_COLUMN);
-
- // Loop through each row and write .po entry
- foreach ($statement as $content => $occurences) {
- list($content, $context) = explode('{#$#}', $content);
-
- fputs($fp, '#: ' . implode(' ', $occurences) . "\n");
- if ($context) {
- fputs($fp, 'msgctxt "' . $context . '"' . "\n");
- }
- fputs($fp, 'msgid ' . po_stringify($content) . "\n");
- fputs($fp, 'msgstr ""' . "\n");
- fputs($fp, "\n");
- }
-
- // Close output file
- fclose($fp);
-} elseif ($_SERVER['argv'][1] === 'import') {
- // Import
-
- // Error message: Invalid parameters
- if ($_SERVER['argc'] < 4) {
- print 'Usage: ' . $_SERVER['argv'][0] . ' import [--language] <file> <version>';
- die(6);
- }
-
- // Set input file and version from parameters
- $input = $_SERVER['argv'][2];
- $version = $_SERVER['argv'][3];
-
- // Error message: Input file does not exists or is not readable
- if (!file_exists($input) || !is_readable($input)) {
- printf('Error: Input file "%s" does not exist or is not readable.' . "\n", $input);
- die(7);
- }
-
- // Open input file for reading
- $fp = fopen($input, 'r');
- // Error message: Input file could not be opened for reading
- if (!is_resource($fp)) {
- printf('Error: Could not open input file "%s" for reading.' . "\n", $input);
- die(5);
- }
-
- // Parse input .po file
- // This is pretty straight forward, yet hacky.
- // The script tries to detect comments (only #:, # by itself is ignored),
- // message context, message id and message content in this order.
- // Any empty line will write to messages array.
- // This routine will probably break for any .po file that differs from the
- // ones created in transifex.
- // This is just supposed to work, not to be beautiful. ;)
- $messages = [];
- $context = '';
- $id = '';
- $content = '';
- $occurences = [];
- $last = false;
- $count = 0;
- while (!feof($fp) && $row = fgets($fp)) {
- $count += 1;
-
- $row = trim($row);
- if ($row[0] === '#' && $row[1] !== ':') {
- continue;
- }
- if ($row[0] === '#') {
- $occurences = array_merge($occurences, explode(' ', mb_substr($row, 2)));
- $occurences = array_filter($occurences);
- $last = 'occurence';
- } elseif (preg_match('/^\msgctxt\\s+"(.*?)"$/', $row, $match)) {
- $context = $match[1];
- $last = 'context';
- } elseif (preg_match('/^msgid\\s+"(.*?)"$/', $row, $match)) {
- $id = po_unescape($match[1]);
- $last = 'id';
- } elseif (preg_match('/^msgstr\\s+"(.*?)"$/', $row, $match)) {
- $content = po_unescape($match[1]);
- $last = 'content';
- } elseif (preg_match('/^"(.*?)"$/', $row, $match) && in_array($last, words('id content'))) {
- if ($last === 'id') {
- $id .= po_unescape($match[1]);
- } else {
- $content .= po_unescape($match[1]);
- }
- } elseif (!$row && $last === 'content') {
- $messages[$context . '#' . $id] = compact(words('context id content occurences'));
-
- $context = '';
- $id = '';
- $content = '';
- $occurences = [];
- $last = false;
- } else {
- printf('Parse error at line %u.' . "\n", $count);
- printf('Last item was "%s".' . "\n", $last);
- printf('Current row: %s' . "\n", $row);
- die(6);
- }
- }
- fclose($fp);
-
- // Parse meta information (no context & no id = item at '#')
- $meta = [];
- foreach (explode("\n", $messages['#']['content']) as $row) {
- $row = trim($row);
- if (!$row) {
- continue;
- }
-
- list($index, $content) = array_map('trim', explode(':', $row, 2));
- $meta[$index] = $content;
- }
- unset($messages['#']);
-
- // Get language
- $language = mb_strtolower($meta['Language']);
-
- // Define db queries for each type (see comment block at the top of
- // this file, type is distinguished by the line number / 10000)
- $queries = [];
- $queries[1] = "INSERT INTO help_content (content_id, language, label, icon, content, route, studip_version, position, custom, installation_id, mkdate)
- VALUES (:id, :language, :content, 'info', '', :route, :version, :position, 0, '', UNIX_TIMESTAMP())
- ON DUPLICATE KEY UPDATE label = VALUES(label)";
- $queries[2] = "INSERT INTO help_content (content_id, language, label, icon, content, route, studip_version, position, custom, installation_id, mkdate)
- VALUES (:id, :language, '', 'info', :content, :route, :version, :position, 0, '', UNIX_TIMESTAMP())
- ON DUPLICATE KEY UPDATE content = VALUES(content)";
- $queries[3] = "INSERT INTO help_tours (tour_id, name, description, type, roles, version, language, studip_version, installation_id, mkdate)
- VALUES (:id, :content, '', 'tour', '', :position, :language, :version, '', UNIX_TIMESTAMP())
- ON DUPLICATE KEY UPDATE name = VALUES(name)";
- $queries[4] = "INSERT INTO help_tours (tour_id, name, description, type, roles, version, language, studip_version, installation_id, mkdate)
- VALUES (:id, '', :content, 'tour', '', :position, :language, :version, '', UNIX_TIMESTAMP())
- ON DUPLICATE KEY UPDATE description = VALUES(description)";
- $queries[5] = "INSERT INTO help_tour_steps (tour_id, step, title, tip, interactive, css_selector, route, author_id, mkdate)
- VALUES (:id, :position, :content, '', 0, '', :route, '', UNIX_TIMESTAMP())
- ON DUPLICATE KEY UPDATE title = VALUES(title)";
- $queries[6] = "INSERT INTO help_tour_steps (tour_id, step, title, tip, interactive, css_selector, route, author_id, mkdate)
- VALUES (:id, :position, '', :content, 0, '', :route, '', UNIX_TIMESTAMP())
- ON DUPLICATE KEY UPDATE tip = VALUES(tip)";
- $queries[7] = "INSERT INTO help_tooltips (tooltip_id, language, version, content, author_id, mkdate, route)
- VALUES (:id, :language, :position, :content, '', UNIX_TIMESTAMP(), :route)
- ON DUPLICATE KEY UPDATE content = VALUES(content)";
-
- // Prepare statements and prebind version and language
- $statements = array_map([DBManager::get(), 'prepare'], $queries);
- foreach ($statements as $index => $statement) {
- $statement->bindValue(':version', $version);
- $statement->bindValue(':language', $language);
-
- $statements[$index] = $statement;
- }
-
- // Process each message, skip the ones with empty content
- foreach ($messages as $message) {
- if (empty($message['content'])) {
- continue;
- }
-
- foreach ($message['occurences'] as $occurence) {
- list($route, $lineno) = explode(':', $occurence);
- $index = floor($lineno / 10000);
- $position = $lineno % 10000;
-
- $id = get_id($version, $language, $message, $route, $index, $position);
-
- $statement = $statements[$index];
- $statement->bindValue(':id', $id);
- $statement->bindValue(':content', $message['content']);
- $statement->bindValue(':route', $route);
- $statement->bindValue(':position', $position);
- $statement->execute();
- }
- }
-}