(bug 33658) support for {{GRAMMAR:}} in jqueryMsg
authorSanthosh Thottingal <santhosh.thottingal@gmail.com>
Mon, 2 Apr 2012 13:01:10 +0000 (18:31 +0530)
committerAntoine Musso <hashar@free.fr>
Tue, 10 Apr 2012 09:47:39 +0000 (11:47 +0200)
This add GRAMMAR support to the mediawiki.jqueryMsg module:

1. make jqueryMsg understand GRAMMAR(case insensitive)
2. mw.language get convertGrammar, can be overridden per language as in
   php
3. Introduce resourceloader module ResourceLoaderLanguageDataModule
4. Language.php get a method to filter wgGrammerForms for the current
   contentLanguage.
5. Qunit tests
6. This code was originally written in jsgrammar branch of svn and had
   reviewed during the last slush time.

Change-Id: I90dd0b2f0cb30fd30539896c292829adc4fc7364

includes/AutoLoader.php
includes/resourceloader/ResourceLoaderLanguageDataModule.php [new file with mode: 0644]
languages/Language.php
resources/Resources.php
resources/mediawiki.language/mediawiki.language.js
resources/mediawiki/mediawiki.jqueryMsg.js
tests/qunit/suites/resources/mediawiki/mediawiki.jqueryMsg.test.js

index eaeda49..a1bbc3c 100644 (file)
@@ -739,6 +739,7 @@ $wgAutoloadLocalClasses = array(
        'ResourceLoaderUserModule' => 'includes/resourceloader/ResourceLoaderUserModule.php',
        'ResourceLoaderUserOptionsModule' => 'includes/resourceloader/ResourceLoaderUserOptionsModule.php',
        'ResourceLoaderUserTokensModule' => 'includes/resourceloader/ResourceLoaderUserTokensModule.php',
+       'ResourceLoaderLanguageDataModule' => 'includes/resourceloader/ResourceLoaderLanguageDataModule.php',
        'ResourceLoaderWikiModule' => 'includes/resourceloader/ResourceLoaderWikiModule.php',
 
        # includes/revisiondelete
diff --git a/includes/resourceloader/ResourceLoaderLanguageDataModule.php b/includes/resourceloader/ResourceLoaderLanguageDataModule.php
new file mode 100644 (file)
index 0000000..e1d6fb4
--- /dev/null
@@ -0,0 +1,79 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @author Santhosh Thottingal
+ * @author Timo Tijhof
+ */
+
+/**
+ * ResourceLoader module for populating language specific data.
+ */
+class ResourceLoaderLanguageDataModule extends ResourceLoaderModule {
+
+       /**
+        * Get the grammer forms for the site content language.
+        *
+        * @return array
+        */
+       protected function getSiteLangGrammarForms() {
+               global $wgContLang;
+               return $wgContLang->getGrammarForms();
+       }
+
+       /**
+        * @param $context ResourceLoaderContext
+        * @return string Javascript code
+        */
+       public function getScript( ResourceLoaderContext $context ) {
+               global $wgContLang;
+
+               return Xml::encodeJsCall( 'mw.language.setData', array(
+                       $wgContLang->getCode(),
+                       array( 'grammarForms' => $this->getSiteLangGrammarForms() )
+               ) );
+       }
+
+       /**
+        * @param $context ResourceLoaderContext
+        * @return array|int|Mixed
+        */
+       public function getModifiedTime( ResourceLoaderContext $context ) {
+               $cache = wfGetCache( CACHE_ANYTHING );
+               $key = wfMemcKey( 'resourceloader', 'langdatamodule', 'changeinfo' );
+
+               $forms = $this->getSiteLangGrammarForms();
+               $hash = md5( serialize( $forms ) );
+
+               $result = $cache->get( $key );
+               if ( is_array( $result ) ) {
+                       if ( $result['hash'] === $hash ) {
+                               return $result['timestamp'];
+                       }
+               }
+               $timestamp = wfTimestamp();
+               $cache->set( $key, array( 'hash' => $hash, 'timestamp' => $timestamp ) );
+               return $timestamp;
+       }
+
+       /**
+        * @return array
+        */
+       public function getDependencies() {
+               return array( 'mediawiki.language' );
+       }
+}
index 8db7e84..463ea19 100644 (file)
@@ -3233,7 +3233,18 @@ class Language {
                }
                return $word;
        }
-
+       /**
+        * Get the grammar forms for the content language
+        * @return array of grammar forms
+        * @since 1.20
+        */
+       function getGrammarForms() {
+               global $wgGrammarForms;
+               if ( isset( $wgGrammarForms[$this->getCode()] ) && is_array( $wgGrammarForms[$this->getCode()] ) ) {
+                        return $wgGrammarForms[$this->getCode()];
+               }
+               return array();
+       }
        /**
         * Provides an alternative text depending on specified gender.
         * Usage {{gender:username|masculine|feminine|neutral}}.
index b891a1f..2855f9a 100644 (file)
@@ -21,6 +21,9 @@ return array(
        'user.options' => array( 'class' => 'ResourceLoaderUserOptionsModule' ),
        'user.tokens' => array( 'class' => 'ResourceLoaderUserTokensModule' ),
 
+       // Scripts for the dynamic language specific data, like grammar forms.
+       'mediawiki.language.data' => array( 'class' => 'ResourceLoaderLanguageDataModule' ),
+
        /* Skins */
 
        'skins.chick' => array(
index 4abfa4b..67b605c 100644 (file)
@@ -7,7 +7,52 @@
  */
 ( function( $, mw ) {
 
-mw.language = {
+var language = {
+       /**
+        * @var data {Object} Language related data (keyed by language,
+        * contains instances of mw.Map).
+        * @example Set data
+        * <code>
+        *     // Override, extend or create the language data object of 'nl'
+        *     mw.language.setData( 'nl', 'myKey', 'My value' );
+        * </code>
+        * @example Get GrammarForms data for language 'nl':
+        * <code>
+        *     var grammarForms = mw.language.getData( 'nl', 'grammarForms' );
+        * </code>
+        */
+       data: {},
+
+       /**
+        * Convenience method for retreiving language data by language code and data key,
+        * covering for the potential inexistance of a data object for this langiage.
+        * @param langCode {String}
+        * @param dataKey {String}
+        * @return {mixed} Value stored in the mw.Map (or undefined if there is no map for
+          the specified langCode).
+        */
+       getData: function ( langCode, dataKey ) {
+               var langData = language.data;
+               if ( langData[langCode] instanceof mw.Map ) {
+                       return langData[langCode].get( dataKey );
+               }
+               return undefined;
+       },
+
+       /**
+        * Convenience method for setting language data by language code and data key.
+        * Creates a data object if there isn't one for the specified language already.
+        * @param langCode {String}
+        * @param dataKey {String}
+        * @param value {mixed}
+        */
+       setData: function ( langCode, dataKey, value ) {
+               var langData = language.data;
+               if ( !( langData[langCode] instanceof mw.Map ) ) {
+                       langData[langCode] = new mw.Map();
+               }
+               langData[langCode].set( dataKey, value );
+       },
        /**
         * Process the PLURAL template substitution
         *
@@ -122,7 +167,28 @@ mw.language = {
                return ( forms.length === 3 ) ? forms[2] : forms[0];
        },
 
+       /**
+        * Grammatical transformations, needed for inflected languages.
+        * Invoked by putting {{grammar:form|word}} in a message.
+        * The rules can be defined in $wgGrammarForms global or grammar
+        * forms can be computed dynamically by overriding this method per language
+        *
+        * @param word {String}
+        * @param form {String}
+        * @return {String}
+        */
+       convertGrammar: function ( word, form ) {
+               var grammarForms = language.getData( mw.config.get( 'wgContentLanguage' ), 'grammarForms' );
+               if ( grammarForms && grammarForms[form] ) {
+                       return grammarForms[form][word] || word;
+               }
+               return word;
+       },
+
        // Digit Transform Table, populated by language classes where applicable
        'digitTransformTable': null
 };
+
+mw.language = language;
+
 } )( jQuery, mediaWiki );
index 3e33bde..043ebce 100644 (file)
                        }
                        var forms = nodes.slice(1);
                        return this.language.gender( gender, forms );
-               }
+               },
 
+               /**
+                * Transform parsed structure into grammar conversion.
+                * Invoked by putting {{grammar:form|word}} in a message
+                * @param {Array} of nodes [{Grammar case eg: genitive}, {String word}]
+                * @return {String} selected grammatical form according to current language
+                */
+               grammar: function( nodes ) {
+                       var form = nodes[0];
+                       var word = nodes[1];
+                       return word && form && this.language.convertGrammar( word, form );
+               }
        };
 
        // deprecated! don't rely on gM existing.
index 265ec2a..2abe016 100644 (file)
@@ -41,3 +41,15 @@ test( 'mw.jqueryMsg Gender', function() {
        ok( mw.messages.set( 'gender-msg-wrong', '{{gender}} is awesome' ), 'mw.messages.set: Register' );
        equal( parser( 'gender-msg-wrong', 'female' ) , ' is awesome', 'Wrong syntax used, but ignore the {{gender}}' );
 } );
+
+
+test( 'mw.jqueryMsg Grammar', function() {
+       expect( 5 );
+       var parser = mw.jqueryMsg.getMessageFunction();
+       ok( parser, 'Parser Function initialized' );
+       // Hope the grammar form grammar_case_foo is not valid in any language
+       ok( mw.messages.set( 'grammar-msg', 'Przeszukaj {{GRAMMAR:grammar_case_foo|{{SITENAME}}}}' ), 'mw.messages.set: Register' );
+       equal( parser( 'grammar-msg' ) , 'Przeszukaj ' + mw.config.get( 'wgSiteName' ) , 'Grammar Test with sitename' );
+       ok( mw.messages.set( 'grammar-msg-wrong-syntax', 'Przeszukaj {{GRAMMAR:grammar_case_xyz}}' ), 'mw.messages.set: Register' );
+       equal( parser( 'grammar-msg-wrong-syntax' ) , 'Przeszukaj ' , 'Grammar Test with wrong grammar template syntax' );
+} );