summaryrefslogtreecommitdiff
path: root/www/wiki/resources/src/mediawiki.language/mediawiki.language.js
blob: 45863a3e33c58e3ab439991eaf1e1639de95edc0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
/*
 * 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 ) {
			var count;
			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 )
				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
		 * @param {Object} [explicitPluralForms] List of explicit plural forms
		 * @return {string} Correct form for quantifier in this language
		 */
		convertPlural: function ( count, forms, explicitPluralForms ) {
			var pluralRules,
				pluralFormIndex = 0;

			if ( explicitPluralForms && ( explicitPluralForms[ count ] !== undefined ) ) {
				return explicitPluralForms[ count ];
			}

			if ( !forms || 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 overridden 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:case|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 userLanguage, forms, transformations,
				patterns, i, rule, sourcePattern, regexp, replacement;

			userLanguage = mw.config.get( 'wgUserLanguage' );

			forms = mw.language.getData( userLanguage, 'grammarForms' );
			if ( forms && forms[ form ] ) {
				return forms[ form ][ word ];
			}

			transformations = mediaWiki.language.getData( userLanguage, 'grammarTransformations' );

			if ( !( transformations && transformations[ form ] ) ) {
				return word;
			}

			patterns = transformations[ form ];

			// Some names of grammar rules are aliases for other rules.
			// In such cases the value is a string rather than object,
			// so load the actual rules.
			if ( typeof patterns === 'string' ) {
				patterns = transformations[ patterns ];
			}

			for ( i = 0; i < patterns.length; i++ ) {
				rule = patterns[ i ];
				sourcePattern = rule[ 0 ];

				if ( sourcePattern === '@metadata' ) {
					continue;
				}

				regexp = new RegExp( sourcePattern );
				replacement = rule[ 1 ];

				if ( word.match( regexp ) ) {
					return word.replace( regexp, replacement );
				}
			}

			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;
		},

		setSpecialCharacters: function ( data ) {
			this.specialCharacters = data;
		},

		/**
		 * Formats language tags according the BCP47 standard.
		 * See LanguageCode::bcp47 for the PHP implementation.
		 *
		 * @param {string} languageTag Well-formed language tag
		 * @return {string}
		 */
		bcp47: function ( languageTag ) {
			var formatted,
				isFirstSegment = true,
				isPrivate = false,
				segments = languageTag.split( '-' );

			formatted = segments.map( function ( segment ) {
				var newSegment;

				// when previous segment is x, it is a private segment and should be lc
				if ( isPrivate ) {
					newSegment = segment.toLowerCase();
				// ISO 3166 country code
				} else if ( segment.length === 2 && !isFirstSegment ) {
					newSegment = segment.toUpperCase();
				// ISO 15924 script code
				} else if ( segment.length === 4 && !isFirstSegment ) {
					newSegment = segment.charAt( 0 ).toUpperCase() + segment.substring( 1 ).toLowerCase();
				// Use lowercase for other cases
				} else {
					newSegment = segment.toLowerCase();
				}

				isPrivate = segment.toLowerCase() === 'x';
				isFirstSegment = false;

				return newSegment;
			} );

			return formatted.join( '-' );
		}
	} );

}( mediaWiki, jQuery ) );