summaryrefslogtreecommitdiff
path: root/www/wiki/extensions/Translate/ffs/JavaScriptFFS.php
diff options
context:
space:
mode:
Diffstat (limited to 'www/wiki/extensions/Translate/ffs/JavaScriptFFS.php')
-rw-r--r--www/wiki/extensions/Translate/ffs/JavaScriptFFS.php286
1 files changed, 286 insertions, 0 deletions
diff --git a/www/wiki/extensions/Translate/ffs/JavaScriptFFS.php b/www/wiki/extensions/Translate/ffs/JavaScriptFFS.php
new file mode 100644
index 00000000..e77724f2
--- /dev/null
+++ b/www/wiki/extensions/Translate/ffs/JavaScriptFFS.php
@@ -0,0 +1,286 @@
+<?php
+
+/**
+ * Generic file format support for JavaScript formatted files.
+ * @ingroup FFS
+ */
+abstract class JavaScriptFFS extends SimpleFFS {
+ public function getFileExtensions() {
+ return [ '.js' ];
+ }
+
+ /**
+ * Message keys format.
+ *
+ * @param string $key
+ *
+ * @return string
+ */
+ abstract protected function transformKey( $key );
+
+ /**
+ * Header of message file.
+ *
+ * @param string $code
+ * @param string[] $authors
+ */
+ abstract protected function header( $code, array $authors );
+
+ /**
+ * Footer of message file.
+ */
+ abstract protected function footer();
+
+ /**
+ * @param string $data
+ * @return array Parsed data.
+ */
+ public function readFromVariable( $data ) {
+ /* Parse authors list */
+ $authors = preg_replace( "#/\* Translators\:\n(.*?)\n \*/(.*)#s", '$1', $data );
+ if ( $authors === $data ) {
+ $authors = [];
+ } else {
+ $authors = explode( "\n", $authors );
+ $count = count( $authors );
+ for ( $i = 0; $i < $count; $i++ ) {
+ // Each line should look like " * - Translatorname"
+ $authors[$i] = substr( $authors[$i], 6 );
+ }
+ }
+
+ /* Pre-processing of messages */
+
+ /**
+ * Find the start and end of the data section (enclosed in curly braces).
+ */
+ $dataStart = strpos( $data, '{' );
+ $dataEnd = strrpos( $data, '}' );
+
+ /**
+ * Strip everything outside of the data section.
+ */
+ $data = substr( $data, $dataStart + 1, $dataEnd - $dataStart - 1 );
+
+ /**
+ * Strip comments.
+ */
+ $data = preg_replace( '#^(\s*?)//(.*?)$#m', '', $data );
+
+ /**
+ * Replace message endings with double quotes.
+ */
+ $data = preg_replace( "#\'\,\n#", "\",\n", $data );
+
+ /**
+ * Strip excess whitespace.
+ */
+ $data = trim( $data );
+
+ /**
+ * Per-key message processing.
+ */
+
+ /**
+ * Break in to segments.
+ */
+ $data = explode( "\",\n", $data );
+
+ $messages = [];
+ foreach ( $data as $segment ) {
+ /**
+ * Add back trailing quote, removed by explosion.
+ */
+ $segment .= '"';
+
+ /**
+ * Concatenate separated strings.
+ */
+ $segment = str_replace( '"+', '" +', $segment );
+ $segment = explode( '" +', $segment );
+ $count = count( $segment );
+ for ( $i = 0; $i < $count; $i++ ) {
+ $segment[$i] = ltrim( ltrim( $segment[$i] ), '"' );
+ }
+ $segment = implode( $segment );
+
+ /**
+ * Remove line breaks between message keys and messages.
+ */
+ $segment = preg_replace( "#\:(\s+)[\\\"\']#", ': "', $segment );
+
+ /**
+ * Break in to key and message.
+ */
+ $segments = explode( ': "', $segment );
+
+ /**
+ * Strip excess whitespace from key and value, then quotation marks.
+ */
+ $key = trim( trim( $segments[0] ), "'\"" );
+ $value = trim( trim( $segments[1] ), "'\"" );
+
+ /**
+ * Unescape any JavaScript string syntax and append to message array.
+ */
+ $messages[$key] = self::unescapeJsString( $value );
+ }
+
+ $messages = $this->group->getMangler()->mangle( $messages );
+
+ return [
+ 'AUTHORS' => $authors,
+ 'MESSAGES' => $messages
+ ];
+ }
+
+ /**
+ * @param MessageCollection $collection
+ * @return string
+ */
+ public function writeReal( MessageCollection $collection ) {
+ $header = $this->header( $collection->code, $collection->getAuthors() );
+
+ $mangler = $this->group->getMangler();
+
+ /**
+ * Get and write messages.
+ */
+ $body = '';
+ /**
+ * @var TMessage $message
+ */
+ foreach ( $collection as $message ) {
+ if ( strlen( $message->translation() ) === 0 ) {
+ continue;
+ }
+
+ $key = $mangler->unmangle( $message->key() );
+ $key = $this->transformKey( self::escapeJsString( $key ) );
+
+ $translation = self::escapeJsString( $message->translation() );
+
+ $body .= "\t{$key}: \"{$translation}\",\n";
+ }
+
+ if ( strlen( $body ) === 0 ) {
+ return false;
+ }
+
+ /**
+ * Strip last comma, re-add trailing newlines.
+ */
+ $body = substr( $body, 0, -2 );
+ $body .= "\n";
+
+ return $header . $body . $this->footer();
+ }
+
+ /**
+ * @param string[] $authors
+ * @return string
+ */
+ protected function authorsList( array $authors ) {
+ if ( $authors === [] ) {
+ return '';
+ }
+
+ $authorsList = '';
+ foreach ( $authors as $author ) {
+ $authorsList .= " * - $author\n";
+ }
+
+ // Remove trailing newline, and return.
+ return substr( " * Translators:\n$authorsList", 0, -1 );
+ }
+
+ // See ECMA 262 section 7.8.4 for string literal format
+ private static $pairs = [
+ "\\" => "\\\\",
+ "\"" => "\\\"",
+ "'" => "\\'",
+ "\n" => "\\n",
+ "\r" => "\\r",
+
+ // To avoid closing the element or CDATA section.
+ '<' => "\\x3c",
+ '>' => "\\x3e",
+
+ // To avoid any complaints about bad entity refs.
+ '&' => "\\x26",
+
+ /*
+ * Work around https://bugzilla.mozilla.org/show_bug.cgi?id=274152
+ * Encode certain Unicode formatting chars so affected
+ * versions of Gecko do not misinterpret our strings;
+ * this is a common problem with Farsi text.
+ */
+ "\xe2\x80\x8c" => "\\u200c", // ZERO WIDTH NON-JOINER
+ "\xe2\x80\x8d" => "\\u200d", // ZERO WIDTH JOINER
+ ];
+
+ /**
+ * @param string $string
+ * @return string
+ */
+ protected static function escapeJsString( $string ) {
+ return strtr( $string, self::$pairs );
+ }
+
+ /**
+ * @param string $string
+ * @return string
+ */
+ protected static function unescapeJsString( $string ) {
+ return strtr( $string, array_flip( self::$pairs ) );
+ }
+}
+
+/**
+ * File format support for Shapado, which uses JavaScript based format.
+ * @ingroup FFS
+ */
+class ShapadoJsFFS extends JavaScriptFFS {
+
+ /**
+ * @param string $key
+ *
+ * @return string
+ */
+ protected function transformKey( $key ) {
+ return $key;
+ }
+
+ /**
+ * @param string $code
+ * @param string[] $authors
+ * @return string
+ */
+ protected function header( $code, array $authors ) {
+ global $wgSitename;
+
+ $name = TranslateUtils::getLanguageName( $code );
+ $native = TranslateUtils::getLanguageName( $code, $code );
+ $authorsList = $this->authorsList( $authors );
+
+ /** @cond doxygen_bug */
+ return <<<EOT
+/** Messages for $name ($native)
+ * Exported from $wgSitename
+ *
+{$authorsList}
+ */
+
+var I18n = {
+
+EOT;
+ /** @endcond */
+ }
+
+ /**
+ * @return string
+ */
+ protected function footer() {
+ return "};\n\n";
+ }
+}