summaryrefslogtreecommitdiff
path: root/www/wiki/extensions/Translate/ffs/JavaFFS.php
diff options
context:
space:
mode:
Diffstat (limited to 'www/wiki/extensions/Translate/ffs/JavaFFS.php')
-rw-r--r--www/wiki/extensions/Translate/ffs/JavaFFS.php272
1 files changed, 272 insertions, 0 deletions
diff --git a/www/wiki/extensions/Translate/ffs/JavaFFS.php b/www/wiki/extensions/Translate/ffs/JavaFFS.php
new file mode 100644
index 00000000..4485b8bf
--- /dev/null
+++ b/www/wiki/extensions/Translate/ffs/JavaFFS.php
@@ -0,0 +1,272 @@
+<?php
+
+/**
+ * JavaFFS class implements support for Java properties files.
+ * This class reads and writes only utf-8 files. Java projects
+ * need to run native2ascii on them before using them.
+ *
+ * This class adds a new item into FILES section of group configuration:
+ * \c keySeparator which defaults to '='.
+ * @ingroup FFS
+ */
+class JavaFFS extends SimpleFFS implements MetaYamlSchemaExtender {
+ public function supportsFuzzy() {
+ return 'write';
+ }
+
+ public function getFileExtensions() {
+ return [ '.properties' ];
+ }
+
+ protected $keySeparator = '=';
+
+ /**
+ * @param FileBasedMessageGroup $group
+ */
+ public function __construct( FileBasedMessageGroup $group ) {
+ parent::__construct( $group );
+
+ if ( isset( $this->extra['keySeparator'] ) ) {
+ $this->keySeparator = $this->extra['keySeparator'];
+ }
+ }
+
+ /**
+ * @param string $data
+ * @return array Parsed data.
+ * @throws MWException
+ */
+ public function readFromVariable( $data ) {
+ $data = self::fixNewLines( $data );
+ $lines = array_map( 'ltrim', explode( "\n", $data ) );
+ $authors = $messages = [];
+ $linecontinuation = false;
+
+ $key = '';
+ $value = '';
+ foreach ( $lines as $line ) {
+ if ( $linecontinuation ) {
+ $linecontinuation = false;
+ $valuecont = $line;
+ $valuecont = str_replace( '\n', "\n", $valuecont );
+ $value .= $valuecont;
+ } else {
+ if ( $line === '' ) {
+ continue;
+ }
+
+ if ( $line[0] === '#' || $line[0] === '!' ) {
+ $match = [];
+ $ok = preg_match( '/#\s*Author:\s*(.*)/', $line, $match );
+
+ if ( $ok ) {
+ $authors[] = $match[1];
+ }
+
+ continue;
+ }
+
+ if ( strpos( $line, $this->keySeparator ) === false ) {
+ throw new MWException( "Line without separator '{$this->keySeparator}': $line." );
+ }
+
+ list( $key, $value ) = self::readRow( $line, $this->keySeparator );
+ if ( $key === '' ) {
+ throw new MWException( "Empty key in line $line." );
+ }
+ }
+
+ // @todo This doesn't handle the pathological case of even number of trailing \
+ if ( strlen( $value ) && $value[strlen( $value ) - 1] === "\\" ) {
+ $value = substr( $value, 0, strlen( $value ) - 1 );
+ $linecontinuation = true;
+ } else {
+ $messages[$key] = ltrim( $value );
+ }
+ }
+
+ $messages = $this->group->getMangler()->mangle( $messages );
+
+ return [
+ 'AUTHORS' => $authors,
+ 'MESSAGES' => $messages,
+ ];
+ }
+
+ /**
+ * @param MessageCollection $collection
+ * @return string
+ */
+ protected function writeReal( MessageCollection $collection ) {
+ $header = $this->doHeader( $collection );
+ $header .= $this->doAuthors( $collection );
+ $header .= "\n";
+
+ $output = '';
+ $mangler = $this->group->getMangler();
+
+ /**
+ * @var TMessage $m
+ */
+ foreach ( $collection as $key => $m ) {
+ $value = $m->translation();
+ $value = str_replace( TRANSLATE_FUZZY, '', $value );
+
+ if ( $value === '' ) {
+ continue;
+ }
+
+ // Just to give an overview of translation quality.
+ if ( $m->hasTag( 'fuzzy' ) ) {
+ $output .= "# Fuzzy\n";
+ }
+
+ $key = $mangler->unmangle( $key );
+ $output .= self::writeRow( $key, $this->keySeparator, $value );
+ }
+
+ if ( $output ) {
+ return $header . $output;
+ }
+
+ return '';
+ }
+
+ /**
+ * Writes well-formed properties file row with key and value.
+ * @param string $key
+ * @param string $sep
+ * @param string $value
+ * @return string
+ * @since 2012-03-28
+ */
+ public static function writeRow( $key, $sep, $value ) {
+ /* Keys containing the separator need escaping. Also escape comment
+ * characters, though strictly they would only need escaping when
+ * they are the first character. Plus the escape character itself. */
+ $key = addcslashes( $key, "#!$sep\\" );
+ // Make sure we do not slip newlines trough... it would be fatal.
+ $value = str_replace( "\n", '\\n', $value );
+
+ return "$key$sep$value\n";
+ }
+
+ /**
+ * Parses non-empty properties file row to key and value.
+ * @param string $line
+ * @param string $sep
+ * @return string[]
+ * @since 2012-03-28
+ */
+ public static function readRow( $line, $sep ) {
+ if ( strpos( $line, '\\' ) === false ) {
+ /* Nothing appears to be escaped in this line.
+ * Just read the key and the value. */
+ list( $key, $value ) = explode( $sep, $line, 2 );
+ } else {
+ /* There might be escaped separators in the key.
+ * Using slower method to find the separator. */
+
+ /* Make the key default to empty instead of value, because
+ * empty key causes error on callers, while empty value
+ * wouldn't. */
+ $key = '';
+ $value = $line;
+
+ /* Find the first unescaped separator. Example:
+ * First line is the string being read, second line is the
+ * value of $escaped after having read the above character.
+ *
+ * ki\ts\\s\=a = koira
+ * 0010010010000
+ * ^ Not separator because $escaped was true
+ * ^ Split the string into key and value here
+ */
+
+ $len = strlen( $line );
+ $escaped = false;
+ for ( $i = 0; $i < $len; $i++ ) {
+ $char = $line[$i];
+ if ( $char === '\\' ) {
+ $escaped = !$escaped;
+ } elseif ( $escaped ) {
+ $escaped = false;
+ } elseif ( $char === $sep ) {
+ $key = substr( $line, 0, $i );
+ // Excluding the separator character from the value
+ $value = substr( $line, $i + 1 );
+ break;
+ }
+ }
+ }
+
+ /* We usually don't want to expand things like \t in values since
+ * translators cannot easily input those. But in keys we do.
+ * \n is exception we do handle in values. */
+ $key = trim( $key );
+ $key = stripcslashes( $key );
+ $value = ltrim( $value );
+ $value = str_replace( '\n', "\n", $value );
+
+ return [ $key, $value ];
+ }
+
+ /**
+ * @param MessageCollection $collection
+ * @return string
+ */
+ protected function doHeader( MessageCollection $collection ) {
+ if ( isset( $this->extra['header'] ) ) {
+ $output = $this->extra['header'];
+ } else {
+ global $wgSitename;
+
+ $code = $collection->code;
+ $name = TranslateUtils::getLanguageName( $code );
+ $native = TranslateUtils::getLanguageName( $code, $code );
+ $output = "# Messages for $name ($native)\n";
+ $output .= "# Exported from $wgSitename\n";
+ }
+
+ return $output;
+ }
+
+ /**
+ * @param MessageCollection $collection
+ * @return string
+ */
+ protected function doAuthors( MessageCollection $collection ) {
+ $output = '';
+ $authors = $collection->getAuthors();
+ $authors = $this->filterAuthors( $authors, $collection->code );
+
+ foreach ( $authors as $author ) {
+ $output .= "# Author: $author\n";
+ }
+
+ return $output;
+ }
+
+ public static function getExtraSchema() {
+ $schema = [
+ 'root' => [
+ '_type' => 'array',
+ '_children' => [
+ 'FILES' => [
+ '_type' => 'array',
+ '_children' => [
+ 'header' => [
+ '_type' => 'text',
+ ],
+ 'keySeparator' => [
+ '_type' => 'text',
+ ],
+ ]
+ ]
+ ]
+ ]
+ ];
+
+ return $schema;
+ }
+}