summaryrefslogtreecommitdiff
path: root/resources/src/mediawiki.language
diff options
context:
space:
mode:
Diffstat (limited to 'resources/src/mediawiki.language')
-rw-r--r--resources/src/mediawiki.language/languages/bs.js19
-rw-r--r--resources/src/mediawiki.language/languages/dsb.js19
-rw-r--r--resources/src/mediawiki.language/languages/fi.js47
-rw-r--r--resources/src/mediawiki.language/languages/ga.js38
-rw-r--r--resources/src/mediawiki.language/languages/he.js29
-rw-r--r--resources/src/mediawiki.language/languages/hsb.js19
-rw-r--r--resources/src/mediawiki.language/languages/hu.js23
-rw-r--r--resources/src/mediawiki.language/languages/hy.js29
-rw-r--r--resources/src/mediawiki.language/languages/la.js50
-rw-r--r--resources/src/mediawiki.language/languages/os.js69
-rw-r--r--resources/src/mediawiki.language/languages/ru.js57
-rw-r--r--resources/src/mediawiki.language/languages/sl.js19
-rw-r--r--resources/src/mediawiki.language/languages/uk.js37
-rw-r--r--resources/src/mediawiki.language/mediawiki.cldr.js32
-rw-r--r--resources/src/mediawiki.language/mediawiki.language.fallback.js35
-rw-r--r--resources/src/mediawiki.language/mediawiki.language.init.js81
-rw-r--r--resources/src/mediawiki.language/mediawiki.language.js171
-rw-r--r--resources/src/mediawiki.language/mediawiki.language.months.js56
-rw-r--r--resources/src/mediawiki.language/mediawiki.language.numbers.js258
19 files changed, 1088 insertions, 0 deletions
diff --git a/resources/src/mediawiki.language/languages/bs.js b/resources/src/mediawiki.language/languages/bs.js
new file mode 100644
index 00000000..b56e4b29
--- /dev/null
+++ b/resources/src/mediawiki.language/languages/bs.js
@@ -0,0 +1,19 @@
+/*!
+ * Bosnian (bosanski) language functions
+ */
+
+mediaWiki.language.convertGrammar = function ( word, form ) {
+ var grammarForms = mediaWiki.language.getData( 'bs', 'grammarForms' );
+ if ( grammarForms && grammarForms[form] ) {
+ return grammarForms[form][word];
+ }
+ switch ( form ) {
+ case 'instrumental': // instrumental
+ word = 's ' + word;
+ break;
+ case 'lokativ': // locative
+ word = 'o ' + word;
+ break;
+ }
+ return word;
+};
diff --git a/resources/src/mediawiki.language/languages/dsb.js b/resources/src/mediawiki.language/languages/dsb.js
new file mode 100644
index 00000000..69c36cc0
--- /dev/null
+++ b/resources/src/mediawiki.language/languages/dsb.js
@@ -0,0 +1,19 @@
+/*!
+ * Lower Sorbian (Dolnoserbski) language functions
+ */
+
+mediaWiki.language.convertGrammar = function ( word, form ) {
+ var grammarForms = mediaWiki.language.getData( 'dsb', 'grammarForms' );
+ if ( grammarForms && grammarForms[form] ) {
+ return grammarForms[form][word];
+ }
+ switch ( form ) {
+ case 'instrumental': // instrumental
+ word = 'z ' + word;
+ break;
+ case 'lokatiw': // lokatiw
+ word = 'wo ' + word;
+ break;
+ }
+ return word;
+};
diff --git a/resources/src/mediawiki.language/languages/fi.js b/resources/src/mediawiki.language/languages/fi.js
new file mode 100644
index 00000000..453a675d
--- /dev/null
+++ b/resources/src/mediawiki.language/languages/fi.js
@@ -0,0 +1,47 @@
+/*!
+ * Finnish (Suomi) language functions
+ * @author Santhosh Thottingal
+ */
+
+mediaWiki.language.convertGrammar = function ( word, form ) {
+ var grammarForms, aou, origWord;
+
+ grammarForms = mediaWiki.language.getData( 'fi', 'grammarForms' );
+ if ( grammarForms && grammarForms[form] ) {
+ return grammarForms[form][word];
+ }
+
+ // vowel harmony flag
+ aou = word.match( /[aou][^äöy]*$/i );
+ origWord = word;
+ if ( word.match( /wiki$/i ) ) {
+ aou = false;
+ }
+ //append i after final consonant
+ if ( word.match( /[bcdfghjklmnpqrstvwxz]$/i ) ) {
+ word += 'i';
+ }
+
+ switch ( form ) {
+ case 'genitive':
+ word += 'n';
+ break;
+ case 'elative':
+ word += ( aou ? 'sta' : 'stä' );
+ break;
+ case 'partitive':
+ word += ( aou ? 'a' : 'ä' );
+ break;
+ case 'illative':
+ // Double the last letter and add 'n'
+ word += word.slice( -1 ) + 'n';
+ break;
+ case 'inessive':
+ word += ( aou ? 'ssa' : 'ssä' );
+ break;
+ default:
+ word = origWord;
+ break;
+ }
+ return word;
+};
diff --git a/resources/src/mediawiki.language/languages/ga.js b/resources/src/mediawiki.language/languages/ga.js
new file mode 100644
index 00000000..fb4e9396
--- /dev/null
+++ b/resources/src/mediawiki.language/languages/ga.js
@@ -0,0 +1,38 @@
+/*!
+ * Irish (Gaeilge) language functions
+ */
+
+mediaWiki.language.convertGrammar = function ( word, form ) {
+ /*jshint onecase:true */
+ var grammarForms = mediaWiki.language.getData( 'ga', 'grammarForms' );
+ if ( grammarForms && grammarForms[form] ) {
+ return grammarForms[form][word];
+ }
+ switch ( form ) {
+ case 'ainmlae':
+ switch ( word ) {
+ case 'an Domhnach':
+ word = 'Dé Domhnaigh';
+ break;
+ case 'an Luan':
+ word = 'Dé Luain';
+ break;
+ case 'an Mháirt':
+ word = 'Dé Mháirt';
+ break;
+ case 'an Chéadaoin':
+ word = 'Dé Chéadaoin';
+ break;
+ case 'an Déardaoin':
+ word = 'Déardaoin';
+ break;
+ case 'an Aoine':
+ word = 'Dé hAoine';
+ break;
+ case 'an Satharn':
+ word = 'Dé Sathairn';
+ break;
+ }
+ }
+ return word;
+};
diff --git a/resources/src/mediawiki.language/languages/he.js b/resources/src/mediawiki.language/languages/he.js
new file mode 100644
index 00000000..d1eba43b
--- /dev/null
+++ b/resources/src/mediawiki.language/languages/he.js
@@ -0,0 +1,29 @@
+/*!
+ * Hebrew (עברית) language functions
+ */
+
+mediaWiki.language.convertGrammar = function ( word, form ) {
+ var grammarForms = mediaWiki.language.getData( 'he', 'grammarForms' );
+ if ( grammarForms && grammarForms[form] ) {
+ return grammarForms[form][word];
+ }
+ switch ( form ) {
+ case 'prefixed':
+ case 'תחילית': // the same word in Hebrew
+ // Duplicate prefixed "Waw", but only if it's not already double
+ if ( word.slice( 0, 1 ) === 'ו' && word.slice( 0, 2 ) !== 'וו' ) {
+ word = 'ו' + word;
+ }
+
+ // Remove the "He" if prefixed
+ if ( word.slice( 0, 1 ) === 'ה' ) {
+ word = word.slice( 1 );
+ }
+
+ // Add a hyphen (maqaf) before numbers and non-Hebrew letters
+ if ( word.slice( 0, 1 ) < 'א' || word.slice( 0, 1 ) > 'ת' ) {
+ word = '־' + word;
+ }
+ }
+ return word;
+};
diff --git a/resources/src/mediawiki.language/languages/hsb.js b/resources/src/mediawiki.language/languages/hsb.js
new file mode 100644
index 00000000..2d6b733e
--- /dev/null
+++ b/resources/src/mediawiki.language/languages/hsb.js
@@ -0,0 +1,19 @@
+/*!
+ * Upper Sorbian (Hornjoserbsce) language functions
+ */
+
+mediaWiki.language.convertGrammar = function ( word, form ) {
+ var grammarForms = mediaWiki.language.getData( 'hsb', 'grammarForms' );
+ if ( grammarForms && grammarForms[form] ) {
+ return grammarForms[form][word];
+ }
+ switch ( form ) {
+ case 'instrumental': // instrumental
+ word = 'z ' + word;
+ break;
+ case 'lokatiw': // lokatiw
+ word = 'wo ' + word;
+ break;
+ }
+ return word;
+};
diff --git a/resources/src/mediawiki.language/languages/hu.js b/resources/src/mediawiki.language/languages/hu.js
new file mode 100644
index 00000000..d72a1c05
--- /dev/null
+++ b/resources/src/mediawiki.language/languages/hu.js
@@ -0,0 +1,23 @@
+/*!
+ * Hungarian language functions
+ * @author Santhosh Thottingal
+ */
+
+mediaWiki.language.convertGrammar = function ( word, form ) {
+ var grammarForms = mediaWiki.language.getData( 'hu', 'grammarForms' );
+ if ( grammarForms && grammarForms[form] ) {
+ return grammarForms[form][word];
+ }
+ switch ( form ) {
+ case 'rol':
+ word += 'ról';
+ break;
+ case 'ba':
+ word += 'ba';
+ break;
+ case 'k':
+ word += 'k';
+ break;
+ }
+ return word;
+};
diff --git a/resources/src/mediawiki.language/languages/hy.js b/resources/src/mediawiki.language/languages/hy.js
new file mode 100644
index 00000000..9cae360b
--- /dev/null
+++ b/resources/src/mediawiki.language/languages/hy.js
@@ -0,0 +1,29 @@
+/*!
+ * Armenian (Հայերեն) language functions
+ */
+
+mediaWiki.language.convertGrammar = function ( word, form ) {
+ /*jshint onecase:true */
+ var grammarForms = mediaWiki.language.getData( 'hy', 'grammarForms' );
+ if ( grammarForms && grammarForms[form] ) {
+ return grammarForms[form][word];
+ }
+
+ // These rules are not perfect, but they are currently only used for site names so it doesn't
+ // matter if they are wrong sometimes. Just add a special case for your site name if necessary.
+
+ switch ( form ) {
+ case 'genitive': // սեռական հոլով
+ if ( word.slice( -1 ) === 'ա' ) {
+ word = word.slice( 0, -1 ) + 'այի';
+ } else if ( word.slice( -1 ) === 'ո' ) {
+ word = word.slice( 0, -1 ) + 'ոյի';
+ } else if ( word.slice( -4 ) === 'գիրք' ) {
+ word = word.slice( 0, -4 ) + 'գրքի';
+ } else {
+ word = word + 'ի';
+ }
+ break;
+ }
+ return word;
+};
diff --git a/resources/src/mediawiki.language/languages/la.js b/resources/src/mediawiki.language/languages/la.js
new file mode 100644
index 00000000..52e8dd44
--- /dev/null
+++ b/resources/src/mediawiki.language/languages/la.js
@@ -0,0 +1,50 @@
+/*!
+ * Latin (lingua Latina) language functions
+ * @author Santhosh Thottingal
+ */
+
+mediaWiki.language.convertGrammar = function ( word, form ) {
+ var grammarForms = mediaWiki.language.getData( 'la', 'grammarForms' );
+ if ( grammarForms && grammarForms[form] ) {
+ return grammarForms[form][word];
+ }
+ switch ( form ) {
+ case 'genitive':
+ // only a few declensions, and even for those mostly the singular only
+ word = word.replace( /u[ms]$/i, 'i' ); // 2nd declension singular
+ word = word.replace( /ommunia$/i, 'ommunium' ); // 3rd declension neuter plural (partly)
+ word = word.replace( /a$/i, 'ae' ); // 1st declension singular
+ word = word.replace( /libri$/i, 'librorum' ); // 2nd declension plural (partly)
+ word = word.replace( /nuntii$/i, 'nuntiorum' ); // 2nd declension plural (partly)
+ word = word.replace( /tio$/i, 'tionis' ); // 3rd declension singular (partly)
+ word = word.replace( /ns$/i, 'ntis' );
+ word = word.replace( /as$/i, 'atis' );
+ word = word.replace( /es$/i, 'ei' ); // 5th declension singular
+ break;
+ case 'accusative':
+ // only a few declensions, and even for those mostly the singular only
+ word = word.replace( /u[ms]$/i, 'um' ); // 2nd declension singular
+ word = word.replace( /ommunia$/i, 'am' ); // 3rd declension neuter plural (partly)
+ word = word.replace( /a$/i, 'ommunia' ); // 1st declension singular
+ word = word.replace( /libri$/i, 'libros' ); // 2nd declension plural (partly)
+ word = word.replace( /nuntii$/i, 'nuntios' );// 2nd declension plural (partly)
+ word = word.replace( /tio$/i, 'tionem' ); // 3rd declension singular (partly)
+ word = word.replace( /ns$/i, 'ntem' );
+ word = word.replace( /as$/i, 'atem');
+ word = word.replace( /es$/i, 'em' ); // 5th declension singular
+ break;
+ case 'ablative':
+ // only a few declensions, and even for those mostly the singular only
+ word = word.replace( /u[ms]$/i, 'o' ); // 2nd declension singular
+ word = word.replace( /ommunia$/i, 'ommunibus' ); // 3rd declension neuter plural (partly)
+ word = word.replace( /a$/i, 'a' ); // 1st declension singular
+ word = word.replace( /libri$/i, 'libris' ); // 2nd declension plural (partly)
+ word = word.replace( /nuntii$/i, 'nuntiis' ); // 2nd declension plural (partly)
+ word = word.replace( /tio$/i, 'tione' ); // 3rd declension singular (partly)
+ word = word.replace( /ns$/i, 'nte' );
+ word = word.replace( /as$/i, 'ate');
+ word = word.replace( /es$/i, 'e' ); // 5th declension singular
+ break;
+ }
+ return word;
+};
diff --git a/resources/src/mediawiki.language/languages/os.js b/resources/src/mediawiki.language/languages/os.js
new file mode 100644
index 00000000..787be36d
--- /dev/null
+++ b/resources/src/mediawiki.language/languages/os.js
@@ -0,0 +1,69 @@
+/*!
+ * Ossetian (Ирон) language functions
+ * @author Santhosh Thottingal
+ */
+
+mediaWiki.language.convertGrammar = function ( word, form ) {
+ var grammarForms = mediaWiki.language.getData( 'os', 'grammarForms' ),
+ // Ending for allative case
+ endAllative = 'мæ',
+ // Variable for 'j' beetwen vowels
+ jot = '',
+ // Variable for "-" for not Ossetic words
+ hyphen = '',
+ // Variable for ending
+ ending = '';
+
+ if ( grammarForms && grammarForms[form] ) {
+ return grammarForms[form][word];
+ }
+ // Checking if the $word is in plural form
+ if ( word.match( /тæ$/i ) ) {
+ word = word.slice( 0, -1 );
+ endAllative = 'æм';
+ }
+ // Works if word is in singular form.
+ // Checking if word ends on one of the vowels: е, ё, и, о, ы, э, ю, я.
+ else if ( word.match( /[аæеёиоыэюя]$/i ) ) {
+ jot = 'й';
+ }
+ // Checking if word ends on 'у'. 'У' can be either consonant 'W' or vowel 'U' in cyrillic Ossetic.
+ // Examples: {{grammar:genitive|аунеу}} = аунеуы, {{grammar:genitive|лæппу}} = лæппуйы.
+ else if ( word.match( /у$/i ) ) {
+ if ( !word.slice( -2, -1 ).match( /[аæеёиоыэюя]$/i ) ) {
+ jot = 'й';
+ }
+ } else if ( !word.match( /[бвгджзйклмнопрстфхцчшщьъ]$/i ) ) {
+ hyphen = '-';
+ }
+
+ switch ( form ) {
+ case 'genitive':
+ ending = hyphen + jot + 'ы';
+ break;
+ case 'dative':
+ ending = hyphen + jot + 'æн';
+ break;
+ case 'allative':
+ ending = hyphen + endAllative;
+ break;
+ case 'ablative':
+ if ( jot === 'й' ) {
+ ending = hyphen + jot + 'æ';
+ }
+ else {
+ ending = hyphen + jot + 'æй';
+ }
+ break;
+ case 'superessive':
+ ending = hyphen + jot + 'ыл';
+ break;
+ case 'equative':
+ ending = hyphen + jot + 'ау';
+ break;
+ case 'comitative':
+ ending = hyphen + 'имæ';
+ break;
+ }
+ return word + ending;
+};
diff --git a/resources/src/mediawiki.language/languages/ru.js b/resources/src/mediawiki.language/languages/ru.js
new file mode 100644
index 00000000..2077b6be
--- /dev/null
+++ b/resources/src/mediawiki.language/languages/ru.js
@@ -0,0 +1,57 @@
+/*!
+ * Russian (Русский) language functions
+ */
+
+// These tests were originally made for names of Wikimedia
+// websites, so they don't currently cover all the possible
+// cases.
+
+mediaWiki.language.convertGrammar = function ( word, form ) {
+ 'use strict';
+
+ var grammarForms = mediaWiki.language.getData( 'ru', 'grammarForms' );
+ if ( grammarForms && grammarForms[form] ) {
+ return grammarForms[form][word];
+ }
+ switch ( form ) {
+ case 'genitive': // родительный падеж
+ if ( word.slice( -1 ) === 'ь' ) {
+ word = word.slice( 0, -1 ) + 'я';
+ } else if ( word.slice( -2 ) === 'ия' ) {
+ word = word.slice( 0, -2 ) + 'ии';
+ } else if ( word.slice( -2 ) === 'ка' ) {
+ word = word.slice( 0, -2 ) + 'ки';
+ } else if ( word.slice( -2 ) === 'ти' ) {
+ word = word.slice( 0, -2 ) + 'тей';
+ } else if ( word.slice( -2 ) === 'ды' ) {
+ word = word.slice( 0, -2 ) + 'дов';
+ } else if ( word.slice( -1 ) === 'д' ) {
+ word = word.slice( 0, -1 ) + 'да';
+ } else if ( word.slice( -3 ) === 'ные' ) {
+ word = word.slice( 0, -3 ) + 'ных';
+ } else if ( word.slice( -3 ) === 'ник' ) {
+ word = word.slice( 0, -3 ) + 'ника';
+ }
+ break;
+ case 'prepositional': // предложный падеж
+ if ( word.slice( -1 ) === 'ь' ) {
+ word = word.slice( 0, -1 ) + 'е';
+ } else if ( word.slice( -2 ) === 'ия' ) {
+ word = word.slice( 0, -2 ) + 'ии';
+ } else if ( word.slice( -2 ) === 'ка' ) {
+ word = word.slice( 0, -2 ) + 'ке';
+ } else if ( word.slice( -2 ) === 'ти' ) {
+ word = word.slice( 0, -2 ) + 'тях';
+ } else if ( word.slice( -2 ) === 'ды' ) {
+ word = word.slice( 0, -2 ) + 'дах';
+ } else if ( word.slice( -1 ) === 'д' ) {
+ word = word.slice( 0, -1 ) + 'де';
+ } else if ( word.slice( -3 ) === 'ные' ) {
+ word = word.slice( 0, -3 ) + 'ных';
+ } else if ( word.slice( -3 ) === 'ник' ) {
+ word = word.slice( 0, -3 ) + 'нике';
+ }
+ break;
+ }
+ return word;
+};
diff --git a/resources/src/mediawiki.language/languages/sl.js b/resources/src/mediawiki.language/languages/sl.js
new file mode 100644
index 00000000..d20d0b34
--- /dev/null
+++ b/resources/src/mediawiki.language/languages/sl.js
@@ -0,0 +1,19 @@
+/*!
+ * Slovenian (Slovenščina) language functions
+ */
+
+mediaWiki.language.convertGrammar = function ( word, form ) {
+ var grammarForms = mediaWiki.language.getData( 'sl', 'grammarForms' );
+ if ( grammarForms && grammarForms[form] ) {
+ return grammarForms[form][word];
+ }
+ switch ( form ) {
+ case 'mestnik': // locative
+ word = 'o ' + word;
+ break;
+ case 'orodnik': // instrumental
+ word = 'z ' + word;
+ break;
+ }
+ return word;
+};
diff --git a/resources/src/mediawiki.language/languages/uk.js b/resources/src/mediawiki.language/languages/uk.js
new file mode 100644
index 00000000..550a388c
--- /dev/null
+++ b/resources/src/mediawiki.language/languages/uk.js
@@ -0,0 +1,37 @@
+/*!
+ * Ukrainian (Українська) language functions
+ */
+
+mediaWiki.language.convertGrammar = function ( word, form ) {
+ var grammarForms = mediaWiki.language.getData( 'uk', 'grammarForms' );
+ if ( grammarForms && grammarForms[form] ) {
+ return grammarForms[form][word];
+ }
+ switch ( form ) {
+ case 'genitive': // родовий відмінок
+ if ( word.slice( -4 ) !== 'вікі' && word.slice( -4 ) !== 'Вікі' ) {
+ if ( word.slice( -1 ) === 'ь' ) {
+ word = word.slice(0, -1 ) + 'я';
+ } else if ( word.slice( -2 ) === 'ія' ) {
+ word = word.slice(0, -2 ) + 'ії';
+ } else if ( word.slice( -2 ) === 'ка' ) {
+ word = word.slice(0, -2 ) + 'ки';
+ } else if ( word.slice( -2 ) === 'ти' ) {
+ word = word.slice(0, -2 ) + 'тей';
+ } else if ( word.slice( -2 ) === 'ды' ) {
+ word = word.slice(0, -2 ) + 'дов';
+ } else if ( word.slice( -3 ) === 'ник' ) {
+ word = word.slice(0, -3 ) + 'ника';
+ }
+ }
+ break;
+ case 'accusative': // знахідний відмінок
+ if ( word.slice( -4 ) !== 'вікі' && word.slice( -4 ) !== 'Вікі' ) {
+ if ( word.slice( -2 ) === 'ія' ) {
+ word = word.slice(0, -2 ) + 'ію';
+ }
+ }
+ break;
+ }
+ return word;
+};
diff --git a/resources/src/mediawiki.language/mediawiki.cldr.js b/resources/src/mediawiki.language/mediawiki.cldr.js
new file mode 100644
index 00000000..f6fb8f10
--- /dev/null
+++ b/resources/src/mediawiki.language/mediawiki.cldr.js
@@ -0,0 +1,32 @@
+( function ( mw ) {
+ 'use strict';
+
+ /**
+ * Namespace for CLDR-related utility methods.
+ *
+ * @class
+ * @singleton
+ */
+ mw.cldr = {
+ /**
+ * Get the plural form index for the number.
+ *
+ * In case none of the rules passed, we return `pluralRules.length` -
+ * that means it is the "other" form.
+ *
+ * @param {number} number
+ * @param {Array} pluralRules
+ * @return {number} plural form index
+ */
+ getPluralForm: function ( number, pluralRules ) {
+ var i;
+ for ( i = 0; i < pluralRules.length; i++ ) {
+ if ( mw.libs.pluralRuleParser( pluralRules[i], number ) ) {
+ break;
+ }
+ }
+ return i;
+ }
+ };
+
+}( mediaWiki ) );
diff --git a/resources/src/mediawiki.language/mediawiki.language.fallback.js b/resources/src/mediawiki.language/mediawiki.language.fallback.js
new file mode 100644
index 00000000..b1bab02a
--- /dev/null
+++ b/resources/src/mediawiki.language/mediawiki.language.fallback.js
@@ -0,0 +1,35 @@
+/*
+ * Language-fallback-chain-related utilities for mediawiki.language.
+ */
+( function ( mw, $ ) {
+ /**
+ * @class mw.language
+ */
+
+ $.extend( mw.language, {
+
+ /**
+ * Get the language fallback chain for current UI language (not including the language itself).
+ *
+ * @return {string[]} List of language keys, e.g. `['de', 'en']`
+ */
+ getFallbackLanguages: function () {
+ return mw.language.getData(
+ mw.config.get( 'wgUserLanguage' ),
+ 'fallbackLanguages'
+ ) || [];
+ },
+
+ /**
+ * Get the language fallback chain for current UI language, including the language itself.
+ *
+ * @return {string[]} List of language keys, e.g. `['pfl', de', 'en']`
+ */
+ getFallbackLanguageChain: function () {
+ return [ mw.config.get( 'wgUserLanguage' ) ]
+ .concat( mw.language.getFallbackLanguages() );
+ }
+
+ } );
+
+}( mediaWiki, jQuery ) );
diff --git a/resources/src/mediawiki.language/mediawiki.language.init.js b/resources/src/mediawiki.language/mediawiki.language.init.js
new file mode 100644
index 00000000..df95d751
--- /dev/null
+++ b/resources/src/mediawiki.language/mediawiki.language.init.js
@@ -0,0 +1,81 @@
+( function ( mw ) {
+ /**
+ * Base language object with methods related to language support, attempting to mirror some of the
+ * functionality of the Language class in MediaWiki:
+ *
+ * - storing and retrieving language data
+ * - transforming message syntax (`{{PLURAL:}}`, `{{GRAMMAR:}}`, `{{GENDER:}}`)
+ * - formatting numbers
+ *
+ * @class
+ * @singleton
+ */
+ mw.language = {
+ /**
+ * Language-related data (keyed by language, contains instances of mw.Map).
+ * Loaded dynamically (see ResourceLoaderLanguageDataModule class in PHP, registered
+ * as mediawiki.language.data on the client).
+ *
+ * To set data:
+ *
+ * // Override, extend or create the language data object of 'nl'
+ * mw.language.setData( 'nl', 'myKey', 'My value' );
+ *
+ * // Set multiple key/values pairs at once
+ * mw.language.setData( 'nl', { foo: 'X', bar: 'Y' } );
+ *
+ * To get GrammarForms data for language 'nl':
+ *
+ * var grammarForms = mw.language.getData( 'nl', 'grammarForms' );
+ *
+ * Possible data keys:
+ *
+ * - `digitTransformTable`
+ * - `separatorTransformTable`
+ * - `grammarForms`
+ * - `pluralRules`
+ * - `digitGroupingPattern`
+ * - `fallbackLanguages`
+ *
+ * @property
+ */
+ data: {},
+
+ /**
+ * Convenience method for retrieving language data.
+ *
+ * Structured by language code and data key, covering for the potential inexistence of a
+ * data object for this language.
+ *
+ * @param {string} langCode
+ * @param {string} dataKey
+ * @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 = mw.language.data;
+ if ( langData && langData[langCode] instanceof mw.Map ) {
+ return langData[langCode].get( dataKey );
+ }
+ return undefined;
+ },
+
+ /**
+ * Convenience method for setting language data.
+ *
+ * Creates the data mw.Map if there isn't one for the specified language already.
+ *
+ * @param {string} langCode
+ * @param {string|Object} dataKey Key or object of key/values
+ * @param {Mixed} [value] Value for dataKey, omit if dataKey is an object
+ */
+ setData: function ( langCode, dataKey, value ) {
+ var langData = mw.language.data;
+ if ( !( langData[langCode] instanceof mw.Map ) ) {
+ langData[langCode] = new mw.Map();
+ }
+ langData[langCode].set( dataKey, value );
+ }
+ };
+
+}( mediaWiki ) );
diff --git a/resources/src/mediawiki.language/mediawiki.language.js b/resources/src/mediawiki.language/mediawiki.language.js
new file mode 100644
index 00000000..d4f3c69e
--- /dev/null
+++ b/resources/src/mediawiki.language/mediawiki.language.js
@@ -0,0 +1,171 @@
+/*
+ * Methods for transforming message syntax.
+ */
+( function ( mw, $ ) {
+
+/**
+ * @class mw.language
+ */
+$.extend( mw.language, {
+
+ /**
+ * Process the PLURAL template substitution
+ *
+ * @private
+ * @param {Object} template Template object
+ * @param {string} template.title
+ * @param {Array} template.parameters
+ * @return {string}
+ */
+ procPLURAL: function ( template ) {
+ if ( template.title && template.parameters && mw.language.convertPlural ) {
+ // Check if we have forms to replace
+ if ( template.parameters.length === 0 ) {
+ return '';
+ }
+ // Restore the count into a Number ( if it got converted earlier )
+ var count = mw.language.convertNumber( template.title, true );
+ // Do convertPlural call
+ return mw.language.convertPlural( parseInt( count, 10 ), template.parameters );
+ }
+ // Could not process plural return first form or nothing
+ if ( template.parameters[0] ) {
+ return template.parameters[0];
+ }
+ return '';
+ },
+
+ /**
+ * Plural form transformations, needed for some languages.
+ *
+ * @param {number} count Non-localized quantifier
+ * @param {Array} forms List of plural forms
+ * @return {string} Correct form for quantifier in this language
+ */
+ convertPlural: function ( count, forms ) {
+ var pluralRules,
+ formCount,
+ form,
+ index,
+ equalsPosition,
+ pluralFormIndex = 0;
+
+ if ( !forms || forms.length === 0 ) {
+ return '';
+ }
+
+ // Handle for explicit n= forms
+ for ( index = 0; index < forms.length; index++ ) {
+ form = forms[index];
+ if ( /^\d+=/.test( form ) ) {
+ equalsPosition = form.indexOf( '=' );
+ formCount = parseInt( form.slice( 0, equalsPosition ), 10 );
+ if ( formCount === count ) {
+ return form.slice( equalsPosition + 1 );
+ }
+ forms[index] = undefined;
+ }
+ }
+
+ // Remove explicit plural forms from the forms.
+ forms = $.map( forms, function ( form ) {
+ return form;
+ } );
+
+ if ( forms.length === 0 ) {
+ return '';
+ }
+
+ pluralRules = mw.language.getData( mw.config.get( 'wgUserLanguage' ), 'pluralRules' );
+ if ( !pluralRules ) {
+ // default fallback.
+ return ( count === 1 ) ? forms[0] : forms[1];
+ }
+ pluralFormIndex = mw.cldr.getPluralForm( count, pluralRules );
+ pluralFormIndex = Math.min( pluralFormIndex, forms.length - 1 );
+ return forms[pluralFormIndex];
+ },
+
+ /**
+ * Pads an array to a specific length by copying the last one element.
+ *
+ * @private
+ * @param {Array} forms Number of forms given to convertPlural
+ * @param {number} count Number of forms required
+ * @return {Array} Padded array of forms
+ */
+ preConvertPlural: function ( forms, count ) {
+ while ( forms.length < count ) {
+ forms.push( forms[ forms.length - 1 ] );
+ }
+ return forms;
+ },
+
+ /**
+ * Provides an alternative text depending on specified gender.
+ *
+ * Usage in message text: `{{gender:[gender|user object]|masculine|feminine|neutral}}`.
+ * If second or third parameter are not specified, masculine is used.
+ *
+ * These details may be overriden per language.
+ *
+ * @param {string} gender 'male', 'female', or anything else for neutral.
+ * @param {Array} forms List of gender forms
+ * @return string
+ */
+ gender: function ( gender, forms ) {
+ if ( !forms || forms.length === 0 ) {
+ return '';
+ }
+ forms = mw.language.preConvertPlural( forms, 2 );
+ if ( gender === 'male' ) {
+ return forms[0];
+ }
+ if ( gender === 'female' ) {
+ return forms[1];
+ }
+ 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 computed
+ * dynamically by overriding this method per language.
+ *
+ * @param {string} word
+ * @param {string} form
+ * @return {string}
+ */
+ convertGrammar: function ( word, form ) {
+ var grammarForms = mw.language.getData( mw.config.get( 'wgUserLanguage' ), 'grammarForms' );
+ if ( grammarForms && grammarForms[form] ) {
+ return grammarForms[form][word] || word;
+ }
+ return word;
+ },
+
+ /**
+ * Turn a list of string into a simple list using commas and 'and'.
+ *
+ * See Language::listToText in languages/Language.php
+ *
+ * @param {string[]} list
+ * @return {string}
+ */
+ listToText: function ( list ) {
+ var text = '', i = 0;
+ for ( ; i < list.length; i++ ) {
+ text += list[i];
+ if ( list.length - 2 === i ) {
+ text += mw.msg( 'and' ) + mw.msg( 'word-separator' );
+ } else if ( list.length - 1 !== i ) {
+ text += mw.msg( 'comma-separator' );
+ }
+ }
+ return text;
+ }
+} );
+
+}( mediaWiki, jQuery ) );
diff --git a/resources/src/mediawiki.language/mediawiki.language.months.js b/resources/src/mediawiki.language/mediawiki.language.months.js
new file mode 100644
index 00000000..5a1a5cb6
--- /dev/null
+++ b/resources/src/mediawiki.language/mediawiki.language.months.js
@@ -0,0 +1,56 @@
+/*
+ * Transfer of month names from messages into mw.language.
+ *
+ * Loading this module also ensures the availability of appropriate messages via mw.msg.
+ */
+( function ( mw, $ ) {
+ var
+ monthMessages = [
+ 'january', 'february', 'march', 'april',
+ 'may_long', 'june', 'july', 'august',
+ 'september', 'october', 'november', 'december'
+ ],
+ monthGenMessages = [
+ 'january-gen', 'february-gen', 'march-gen', 'april-gen',
+ 'may-gen', 'june-gen', 'july-gen', 'august-gen',
+ 'september-gen', 'october-gen', 'november-gen', 'december-gen'
+ ],
+ monthAbbrevMessages = [
+ 'jan', 'feb', 'mar', 'apr',
+ 'may', 'jun', 'jul', 'aug',
+ 'sep', 'oct', 'nov', 'dec'
+ ];
+
+ // Function suitable for passing to jQuery.map
+ // Can't use mw.msg directly because jQuery.map passes element index as second argument
+ function mwMsgMapper( key ) {
+ return mw.msg( key );
+ }
+
+ /**
+ * Information about month names in current UI language.
+ *
+ * Object keys:
+ *
+ * - `names`: array of month names (in nominative case in languages which have the distinction),
+ * zero-indexed
+ * - `genitive`: array of month names in genitive case, zero-indexed
+ * - `abbrev`: array of three-letter-long abbreviated month names, zero-indexed
+ * - `keys`: object with three keys like the above, containing zero-indexed arrays of message keys
+ * for appropriate messages which can be passed to mw.msg.
+ *
+ * @property
+ * @member mw.language
+ */
+ mw.language.months = {
+ keys: {
+ names: monthMessages,
+ genitive: monthGenMessages,
+ abbrev: monthAbbrevMessages
+ },
+ names: $.map( monthMessages, mwMsgMapper ),
+ genitive: $.map( monthGenMessages, mwMsgMapper ),
+ abbrev: $.map( monthAbbrevMessages, mwMsgMapper )
+ };
+
+}( mediaWiki, jQuery ) );
diff --git a/resources/src/mediawiki.language/mediawiki.language.numbers.js b/resources/src/mediawiki.language/mediawiki.language.numbers.js
new file mode 100644
index 00000000..a0b81410
--- /dev/null
+++ b/resources/src/mediawiki.language/mediawiki.language.numbers.js
@@ -0,0 +1,258 @@
+/*
+ * Number-related utilities for mediawiki.language.
+ */
+( function ( mw, $ ) {
+ /**
+ * @class mw.language
+ */
+
+ /**
+ * Pad a string to guarantee that it is at least `size` length by
+ * filling with the character `ch` at either the start or end of the
+ * string. Pads at the start, by default.
+ *
+ * Example: Fill the string to length 10 with '+' characters on the right.
+ *
+ * pad( 'blah', 10, '+', true ); // => 'blah++++++'
+ *
+ * @private
+ * @param {string} text The string to pad
+ * @param {number} size The length to pad to
+ * @param {string} [ch='0'] Character to pad with
+ * @param {boolean} [end=false] Adds padding at the end if true, otherwise pads at start
+ * @return {string}
+ */
+ function pad( text, size, ch, end ) {
+ if ( !ch ) {
+ ch = '0';
+ }
+
+ var out = String( text ),
+ padStr = replicate( ch, Math.ceil( ( size - out.length ) / ch.length ) );
+
+ return end ? out + padStr : padStr + out;
+ }
+
+ /**
+ * Replicate a string 'n' times.
+ *
+ * @private
+ * @param {string} str The string to replicate
+ * @param {number} num Number of times to replicate the string
+ * @return {string}
+ */
+ function replicate( str, num ) {
+ if ( num <= 0 || !str ) {
+ return '';
+ }
+
+ var buf = [];
+ while ( num-- ) {
+ buf.push( str );
+ }
+ return buf.join( '' );
+ }
+
+ /**
+ * Apply numeric pattern to absolute value using options. Gives no
+ * consideration to local customs.
+ *
+ * Adapted from dojo/number library with thanks
+ * <http://dojotoolkit.org/reference-guide/1.8/dojo/number.html>
+ *
+ * @private
+ * @param {number} value the number to be formatted, ignores sign
+ * @param {string} pattern the number portion of a pattern (e.g. `#,##0.00`)
+ * @param {Object} [options] If provided, both option keys must be present:
+ * @param {string} options.decimal The decimal separator. Defaults to: `'.'`.
+ * @param {string} options.group The group separator. Defaults to: `','`.
+ * @return {string}
+ */
+ function commafyNumber( value, pattern, options ) {
+ options = options || {
+ group: ',',
+ decimal: '.'
+ };
+
+ if ( isNaN( value) ) {
+ return value;
+ }
+
+ var padLength,
+ patternDigits,
+ index,
+ whole,
+ off,
+ remainder,
+ patternParts = pattern.split( '.' ),
+ maxPlaces = ( patternParts[1] || [] ).length,
+ valueParts = String( Math.abs( value ) ).split( '.' ),
+ fractional = valueParts[1] || '',
+ groupSize = 0,
+ groupSize2 = 0,
+ pieces = [];
+
+ if ( patternParts[1] ) {
+ // Pad fractional with trailing zeros
+ padLength = ( patternParts[1] && patternParts[1].lastIndexOf( '0' ) + 1 );
+
+ if ( padLength > fractional.length ) {
+ valueParts[1] = pad( fractional, padLength, '0', true );
+ }
+
+ // Truncate fractional
+ if ( maxPlaces < fractional.length ) {
+ valueParts[1] = fractional.slice( 0, maxPlaces );
+ }
+ } else {
+ if ( valueParts[1] ) {
+ valueParts.pop();
+ }
+ }
+
+ // Pad whole with leading zeros
+ patternDigits = patternParts[0].replace( ',', '' );
+
+ padLength = patternDigits.indexOf( '0' );
+
+ if ( padLength !== -1 ) {
+ padLength = patternDigits.length - padLength;
+
+ if ( padLength > valueParts[0].length ) {
+ valueParts[0] = pad( valueParts[0], padLength );
+ }
+
+ // Truncate whole
+ if ( patternDigits.indexOf( '#' ) === -1 ) {
+ valueParts[0] = valueParts[0].slice( valueParts[0].length - padLength );
+ }
+ }
+
+ // Add group separators
+ index = patternParts[0].lastIndexOf( ',' );
+
+ if ( index !== -1 ) {
+ groupSize = patternParts[0].length - index - 1;
+ remainder = patternParts[0].slice( 0, index );
+ index = remainder.lastIndexOf( ',' );
+ if ( index !== -1 ) {
+ groupSize2 = remainder.length - index - 1;
+ }
+ }
+
+ for ( whole = valueParts[0]; whole; ) {
+ off = groupSize ? whole.length - groupSize : 0;
+ pieces.push( ( off > 0 ) ? whole.slice( off ) : whole );
+ whole = ( off > 0 ) ? whole.slice( 0, off ) : '';
+
+ if ( groupSize2 ) {
+ groupSize = groupSize2;
+ groupSize2 = null;
+ }
+ }
+ valueParts[0] = pieces.reverse().join( options.group );
+
+ return valueParts.join( options.decimal );
+ }
+
+ $.extend( mw.language, {
+
+ /**
+ * Converts a number using #getDigitTransformTable.
+ *
+ * @param {number} num Value to be converted
+ * @param {boolean} [integer=false] Whether to convert the return value to an integer
+ * @return {number|string} Formatted number
+ */
+ convertNumber: function ( num, integer ) {
+ var i, tmp, transformTable, numberString, convertedNumber, pattern;
+
+ pattern = mw.language.getData( mw.config.get( 'wgUserLanguage' ),
+ 'digitGroupingPattern' ) || '#,##0.###';
+
+ // Set the target transform table:
+ transformTable = mw.language.getDigitTransformTable();
+
+ if ( !transformTable ) {
+ return num;
+ }
+
+ // Check if the 'restore' to Latin number flag is set:
+ if ( integer ) {
+ if ( parseInt( num, 10 ) === num ) {
+ return num;
+ }
+ tmp = [];
+ for ( i in transformTable ) {
+ tmp[ transformTable[ i ] ] = i;
+ }
+ transformTable = tmp;
+ numberString = num + '';
+ } else {
+ numberString = mw.language.commafy( num, pattern );
+ }
+
+ convertedNumber = '';
+ for ( i = 0; i < numberString.length; i++ ) {
+ if ( transformTable[ numberString[i] ] ) {
+ convertedNumber += transformTable[numberString[i]];
+ } else {
+ convertedNumber += numberString[i];
+ }
+ }
+ return integer ? parseInt( convertedNumber, 10 ) : convertedNumber;
+ },
+
+ /**
+ * Get the digit transform table for current UI language.
+ * @return {Object|Array}
+ */
+ getDigitTransformTable: function () {
+ return mw.language.getData( mw.config.get( 'wgUserLanguage' ),
+ 'digitTransformTable' ) || [];
+ },
+
+ /**
+ * Get the separator transform table for current UI language.
+ * @return {Object|Array}
+ */
+ getSeparatorTransformTable: function () {
+ return mw.language.getData( mw.config.get( 'wgUserLanguage' ),
+ 'separatorTransformTable' ) || [];
+ },
+
+ /**
+ * Apply pattern to format value as a string.
+ *
+ * Using patterns from [Unicode TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns).
+ *
+ * @param {number} value
+ * @param {string} pattern Pattern string as described by Unicode TR35
+ * @throws {Error} If unable to find a number expression in `pattern`.
+ * @return {string}
+ */
+ commafy: function ( value, pattern ) {
+ var numberPattern,
+ transformTable = mw.language.getSeparatorTransformTable(),
+ group = transformTable[','] || ',',
+ numberPatternRE = /[#0,]*[#0](?:\.0*#*)?/, // not precise, but good enough
+ decimal = transformTable['.'] || '.',
+ patternList = pattern.split( ';' ),
+ positivePattern = patternList[0];
+
+ pattern = patternList[ ( value < 0 ) ? 1 : 0] || ( '-' + positivePattern );
+ numberPattern = positivePattern.match( numberPatternRE );
+
+ if ( !numberPattern ) {
+ throw new Error( 'unable to find a number expression in pattern: ' + pattern );
+ }
+
+ return pattern.replace( numberPatternRE, commafyNumber( value, numberPattern[0], {
+ decimal: decimal,
+ group: group
+ } ) );
+ }
+
+ } );
+
+}( mediaWiki, jQuery ) );