summaryrefslogtreecommitdiff
path: root/resources/src/jquery/jquery.localize.js
blob: f5932b246fd446b62c66aba0d53058b7410f7615 (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
/**
 * @class jQuery.plugin.localize
 */
( function ( $, mw ) {

/**
 * Gets a localized message, using parameters from options if present.
 *
 * @ignore
 * @param {Object} options
 * @param {string} key
 * @return {string} Localized message
 */
function msg( options, key ) {
	var args = options.params[ key ] || [];
	// Format: mw.msg( key [, p1, p2, ...] )
	args.unshift( options.prefix + ( options.keys[ key ] || key ) );
	return mw.msg.apply( mw, args );
}

/**
 * Localizes a DOM selection by replacing <html:msg /> elements with localized text and adding
 * localized title and alt attributes to elements with title-msg and alt-msg attributes
 * respectively.
 *
 * Call on a selection of HTML which contains `<html:msg key="message-key" />` elements or elements
 * with title-msg="message-key", alt-msg="message-key" or placeholder-msg="message-key" attributes.
 * `<html:msg />` elements will be replaced with localized text, *-msg attributes will be replaced
 * with attributes that do not have the "-msg" suffix and contain a localized message.
 *
 * Example:
 *     // Messages: { 'title': 'Awesome', 'desc': 'Cat doing backflip' 'search' contains 'Search' }
 *     var html = '\
 *         <p>\
 *             <html:msg key="title" />\
 *             <img src="something.jpg" title-msg="title" alt-msg="desc" />\
 *             <input type="text" placeholder-msg="search" />\
 *         </p>';
 *     $( 'body' ).append( $( html ).localize() );
 *
 * Appends something like this to the body...
 *     <p>
 *         Awesome
 *         <img src="something.jpg" title="Awesome" alt="Cat doing backflip" />
 *         <input type="text" placeholder="Search" />
 *     </p>
 *
 * Arguments can be passed into uses of a message using the params property of the options object
 * given to .localize(). Multiple messages can be given parameters, because the params property is
 * an object keyed by the message key to apply the parameters to, each containing an array of
 * parameters to use. The limitation is that you can not use different parameters to individual uses
 * of a message in the same selection being localized - they will all recieve the same parameters.
 *
 * Example:
 *     // Messages: { 'easy-as': 'Easy as $1 $2 $3.' }
 *     var html = '<p><html:msg key="easy-as" /></p>';
 *     $( 'body' ).append( $( html ).localize( { 'params': { 'easy-as': ['a', 'b', 'c'] } } ) );
 *
 * Appends something like this to the body...
 *     <p>Easy as a, b, c</p>
 *
 * Raw HTML content can be used, instead of it being escaped as text. To do this, just use the raw
 * attribute on a msg element.
 *
 * Example:
 *     // Messages: { 'hello': '<b><i>Hello</i> $1!</b>' }
 *     var html = '\
 *         <p>\
 *             <!-- escaped: --><html:msg key="hello" />\
 *             <!-- raw: --><html:msg key="hello" raw />\
 *         </p>';
 *     $( 'body' ).append( $( html ).localize( { 'params': { 'hello': ['world'] } } ) );
 *
 * Appends something like this to the body...
 *     <p>
 *         <!-- escaped: -->&lt;b&gt;&lt;i&gt;Hello&lt;/i&gt; world!&lt;/b&gt;
 *         <!-- raw: --><b><i>Hello</i> world!</b>
 *     </p>
 *
 * Message keys can also be remapped, allowing the same generic template to be used with a variety
 * of messages. This is important for improving re-usability of templates.
 *
 * Example:
 *     // Messages: { 'good-afternoon': 'Good afternoon' }
 *     var html = '<p><html:msg key="greeting" /></p>';
 *     $( 'body' ).append( $( html ).localize( { 'keys': { 'greeting': 'good-afternoon' } } ) );
 *
 * Appends something like this to the body...
 *     <p>Good afternoon</p>
 *
 * Message keys can also be prefixed globally, which is handy when writing extensions, where by
 * convention all messages are prefixed with the extension's name.
 *
 * Example:
 *     // Messages: { 'teleportation-warning': 'You may not get there all in one piece.' }
 *     var html = '<p><html:msg key="warning" /></p>';
 *     $( 'body' ).append( $( html ).localize( { 'prefix': 'teleportation-' } ) );
 *
 * Appends something like this to the body...
 *     <p>You may not get there all in one piece.</p>
 *
 * @param {Object} options Map of options to be used while localizing
 * @param {string} options.prefix String to prepend to all message keys
 * @param {Object} options.keys Message key aliases, used for remapping keys to a template
 * @param {Object} options.params Lists of parameters to use with certain message keys
 * @return {jQuery}
 * @chainable
 */
$.fn.localize = function ( options ) {
	var $target = this,
		attributes = [ 'title', 'alt', 'placeholder' ];

	// Extend options
	options = $.extend( {
		prefix: '',
		keys: {},
		params: {}
	}, options );

	// Elements
	// Ok, so here's the story on this selector. In IE 6/7, searching for 'msg' turns up the
	// 'html:msg', but searching for 'html:msg' doesn't. In later IE and other browsers, searching
	// for 'html:msg' turns up the 'html:msg', but searching for 'msg' doesn't. So searching for
	// both 'msg' and 'html:msg' seems to get the job done. This feels pretty icky, though.
	$target.find( 'msg,html\\:msg' ).each( function () {
		var $el = $( this );
		// Escape by default
		if ( $el.attr( 'raw' ) ) {
			$el.html( msg( options, $el.attr( 'key' ) ) );
		} else {
			$el.text( msg( options, $el.attr( 'key' ) ) );
		}
		// Remove wrapper
		$el.replaceWith( $el.html() );
	} );

	// Attributes
	// Note: there's no way to prevent escaping of values being injected into attributes, this is
	// on purpose, not a design flaw.
	$.each( attributes, function ( i, attr ) {
		var msgAttr = attr + '-msg';
		$target.find( '[' + msgAttr + ']' ).each( function () {
			var $el = $( this );
			$el.attr( attr, msg( options, $el.attr( msgAttr ) ) ).removeAttr( msgAttr );
		} );
	} );

	// HTML, Text for elements which cannot have children e.g. OPTION
	$target.find( '[data-msg-text]' ).each( function () {
		var $el = $( this );
		$el.text( msg( options, $el.attr( 'data-msg-text' ) ) );
	} );

	$target.find( '[data-msg-html]' ).each( function () {
		var $el = $( this );
		$el.html( msg( options, $el.attr( 'data-msg-html' ) ) );
	} );

	return $target;
};

// Let IE know about the msg tag before it's used...
document.createElement( 'msg' );

/**
 * @class jQuery
 * @mixins jQuery.plugin.localize
 */

}( jQuery, mediaWiki ) );