jenkins-bot has submitted this change and it was merged.
Change subject: Use jquery.i18n for standalone i18n
......................................................................
Use jquery.i18n for standalone i18n
VisualEditor.php:
* Make jquery.i18n a dependency of ext.visualEditor.standalone
makeStaticLoader.php:
* Remove ve.init.platform.addMessages() call with PHP-generated messages
* Add fake module for jquery.i18n
** Needed because the module might come from MW core
** Also add special treatment for fallbacks.js and language scripts
ve.init.sa.Platform.js:
* Remove basic message system, replace with jquery.i18n
* Add initialize method that loads messages for current language and
fallbacks
ve.init.sa.Target.js:
* Wait for the platform to initialize before actually doing things
* Add .setup() method to allow callers to short-circuit this process
** This is convenient for callers of ve.init.sa.Target in the test suite
ve.ce.test.js:
* Use existing ve.test.utils function for creating a surface
ve.test.utils.js:
* Call .setup() on the target so we can get a surface synchronously
ve.init.Platform.test.js:
* Make these tests async, wait for the platform to initialize
* Allow for missing messages to be output either as <foo> (MW)
or foo (jquery.i18n)
* Get rid of message clearing code, namespace test messages instead
Change-Id: Iac7dfd327eadf9b503a61510574d35d748faac92
---
M VisualEditor.php
M demos/ve/index.php
M maintenance/makeStaticLoader.php
M modules/ve/init/sa/ve.init.sa.Platform.js
M modules/ve/init/sa/ve.init.sa.Target.js
M modules/ve/init/ve.init.Platform.js
M modules/ve/test/ce/ve.ce.test.js
M modules/ve/test/index.php
M modules/ve/test/init/ve.init.Platform.test.js
M modules/ve/test/ve.test.utils.js
10 files changed, 203 insertions(+), 118 deletions(-)
Approvals:
Krinkle: Looks good to me, approved
jenkins-bot: Verified
diff --git a/VisualEditor.php b/VisualEditor.php
index f5e74df..92c3308 100644
--- a/VisualEditor.php
+++ b/VisualEditor.php
@@ -266,6 +266,7 @@
),
'dependencies' => array(
'ext.visualEditor.base',
+ 'jquery.i18n',
),
),
diff --git a/demos/ve/index.php b/demos/ve/index.php
index 8bf2983..1e3f766 100644
--- a/demos/ve/index.php
+++ b/demos/ve/index.php
@@ -101,16 +101,33 @@
<script
src="../../modules/ve/init/ve.init.Platform.js"></script>
<script src="../../modules/ve/init/ve.init.Target.js"></script>
<script src="../../modules/ve/ve.debug.js"></script>
+ <!-- jquery.i18n -->
+ <script
src="../../modules/jquery.i18n/src/jquery.i18n.js"></script>
+ <script
src="../../modules/jquery.i18n/src/jquery.i18n.messagestore.js"></script>
+ <script
src="../../modules/jquery.i18n/src/jquery.i18n.parser.js"></script>
+ <script
src="../../modules/jquery.i18n/src/jquery.i18n.emitter.js"></script>
+ <script
src="../../modules/jquery.i18n/src/jquery.i18n.language.js"></script>
+ <script
src="../../modules/jquery.i18n/src/jquery.i18n.fallbacks.js"></script>
+ <script
src="../../modules/jquery.i18n/src/languages/bs.js"></script>
+ <script
src="../../modules/jquery.i18n/src/languages/dsb.js"></script>
+ <script
src="../../modules/jquery.i18n/src/languages/fi.js"></script>
+ <script
src="../../modules/jquery.i18n/src/languages/ga.js"></script>
+ <script
src="../../modules/jquery.i18n/src/languages/he.js"></script>
+ <script
src="../../modules/jquery.i18n/src/languages/hsb.js"></script>
+ <script
src="../../modules/jquery.i18n/src/languages/hu.js"></script>
+ <script
src="../../modules/jquery.i18n/src/languages/hy.js"></script>
+ <script
src="../../modules/jquery.i18n/src/languages/la.js"></script>
+ <script
src="../../modules/jquery.i18n/src/languages/ml.js"></script>
+ <script
src="../../modules/jquery.i18n/src/languages/os.js"></script>
+ <script
src="../../modules/jquery.i18n/src/languages/ru.js"></script>
+ <script
src="../../modules/jquery.i18n/src/languages/sl.js"></script>
+ <script
src="../../modules/jquery.i18n/src/languages/uk.js"></script>
<!-- ext.visualEditor.standalone -->
<script src="../../modules/ve/init/sa/ve.init.sa.js"></script>
<script
src="../../modules/ve/init/sa/ve.init.sa.Platform.js"></script>
<script
src="../../modules/ve/init/sa/ve.init.sa.Target.js"></script>
<!-- Standalone init -->
<script>
- <?php
- require
'../../modules/../VisualEditor.i18n.php';
- echo 've.init.platform.addMessages( ' .
json_encode( $messages['en'] ) . " );\n";
- ?>
ve.init.platform.setModulesUrl( '../../modules' );
</script>
<!-- rangy -->
diff --git a/maintenance/makeStaticLoader.php b/maintenance/makeStaticLoader.php
index aeb27d0..98a33ee 100644
--- a/maintenance/makeStaticLoader.php
+++ b/maintenance/makeStaticLoader.php
@@ -158,13 +158,36 @@
}
</script>',
'bodyAdd' => '<script>
- <?php
- require ' . var_export( $i18nScript, true ) . ';
- echo \'ve.init.platform.addMessages( \' . json_encode(
$messages[\'en\'] ) . " );\n";
- ?>
ve.init.platform.setModulesUrl( \'' . $vePath . '\' );
</script>'
),
+ 'jquery.i18n' => array(
+ 'scripts' => array(
+ 'jquery.i18n/src/jquery.i18n.js',
+
'jquery.i18n/src/jquery.i18n.messagestore.js',
+ 'jquery.i18n/src/jquery.i18n.parser.js',
+
'jquery.i18n/src/jquery.i18n.emitter.js',
+
'jquery.i18n/src/jquery.i18n.language.js',
+ // fallbacks.js is not in the real RL
module, but we need it in the standalone
+
'jquery.i18n/src/jquery.i18n.fallbacks.js',
+ // We can't use languageScripts here
because we don't know what the language
+ // will be in advance, so just include
all of them
+ 'jquery.i18n/src/languages/bs.js',
+ 'jquery.i18n/src/languages/dsb.js',
+ 'jquery.i18n/src/languages/fi.js',
+ 'jquery.i18n/src/languages/ga.js',
+ 'jquery.i18n/src/languages/he.js',
+ 'jquery.i18n/src/languages/hsb.js',
+ 'jquery.i18n/src/languages/hu.js',
+ 'jquery.i18n/src/languages/hy.js',
+ 'jquery.i18n/src/languages/la.js',
+ 'jquery.i18n/src/languages/ml.js',
+ 'jquery.i18n/src/languages/os.js',
+ 'jquery.i18n/src/languages/ru.js',
+ 'jquery.i18n/src/languages/sl.js',
+ 'jquery.i18n/src/languages/uk.js',
+ ),
+ )
);
$modules = array(
@@ -175,6 +198,7 @@
'unicodejs.wordbreak',
// Dependencies for ext.visualEditor.standalone:
'ext.visualEditor.base',
+ 'jquery.i18n',
'ext.visualEditor.standalone',
'Standalone init',
// Dependencies for ext.visualEditor.core:
diff --git a/modules/ve/init/sa/ve.init.sa.Platform.js
b/modules/ve/init/sa/ve.init.sa.Platform.js
index 9e14e38..80f04d1 100644
--- a/modules/ve/init/sa/ve.init.sa.Platform.js
+++ b/modules/ve/init/sa/ve.init.sa.Platform.js
@@ -20,7 +20,6 @@
// Properties
this.externalLinkUrlProtocolsRegExp = /^https?\:\/\//;
this.modulesUrl = 'extensions/VisualEditor/modules';
- this.messages = {};
this.parsedMessages = {};
};
@@ -51,23 +50,14 @@
/** @inheritdoc */
ve.init.sa.Platform.prototype.addMessages = function ( messages ) {
- for ( var key in messages ) {
- this.messages[key] = messages[key];
- }
+ $.i18n().load( messages, $.i18n().locale );
};
-/** @inheritdoc */
-ve.init.sa.Platform.prototype.getMessage = function ( key ) {
- if ( key in this.messages ) {
- // Simple message parser, does $N replacement and nothing else.
- var parameters = Array.prototype.slice.call( arguments, 1 );
- return this.messages[key].replace( /\$(\d+)/g, function ( str,
match ) {
- var index = parseInt( match, 10 ) - 1;
- return parameters[index] !== undefined ?
parameters[index] : '$' + match;
- } );
- }
- return '<' + key + '>';
-};
+/**
+ * @method
+ * @inheritdoc
+ */
+ve.init.sa.Platform.prototype.getMessage = $.i18n;
/** @inheritdoc */
ve.init.sa.Platform.prototype.addParsedMessages = function ( messages ) {
@@ -121,6 +111,52 @@
return langs;
};
+ve.init.sa.Platform.prototype.initialize = function () {
+ var i, len, partialLocale, localeParts, deferred,
+ path = this.getModulesUrl(),
+ locale = $.i18n().locale,
+ languages = [ locale, 'en' ], // Always use 'en' as the final
fallback
+ languagesCovered = {},
+ promises = [],
+ fallbacks = $.i18n.fallbacks[locale];
+
+ if ( !fallbacks ) {
+ // Try to find something that has fallbacks (which means it's a
language we know about)
+ // by stripping things from the end. But collect all the
intermediate ones in case we
+ // go past languages that don't have fallbacks but do exist.
+ localeParts = locale.split( '-' );
+ localeParts.pop();
+ while ( localeParts.length && !fallbacks ) {
+ partialLocale = localeParts.join( '-' );
+ languages.push( partialLocale );
+ fallbacks = $.i18n.fallbacks[partialLocale];
+ localeParts.pop();
+ }
+ }
+
+ if ( fallbacks ) {
+ languages = languages.concat( fallbacks );
+ }
+
+ for ( i = 0, len = languages.length; i < len; i++ ) {
+ if ( languagesCovered[languages[i]] ) {
+ continue;
+ }
+ languagesCovered[languages[i]] = true;
+
+ deferred = $.Deferred();
+ $.i18n().load( path + '/ve/i18n/' + languages[i] + '.json',
languages[i] )
+ .always( deferred.resolve );
+ promises.push( deferred.promise() );
+
+ deferred = $.Deferred();
+ $.i18n().load( path + '/oojs-ui/i18n/' + languages[i] +
'.json', languages[i] )
+ .always( deferred.resolve );
+ promises.push( deferred.promise() );
+ }
+ return $.when.apply( $, promises );
+};
+
/* Initialization */
ve.init.platform = new ve.init.sa.Platform();
diff --git a/modules/ve/init/sa/ve.init.sa.Target.js
b/modules/ve/init/sa/ve.init.sa.Target.js
index f68fab6..5b93f48 100644
--- a/modules/ve/init/sa/ve.init.sa.Target.js
+++ b/modules/ve/init/sa/ve.init.sa.Target.js
@@ -24,8 +24,25 @@
// Parent constructor
ve.init.Target.call( this, $container );
+ this.document = doc;
+
+ ve.init.platform.getInitializedPromise().done( ve.bind( this.setup,
this ) );
+};
+
+/* Inheritance */
+
+OO.inheritClass( ve.init.sa.Target, ve.init.Target );
+
+/* Methods */
+
+ve.init.sa.Target.prototype.setup = function () {
+ if ( this.setupDone ) {
+ return;
+ }
+
// Properties
- this.surface = new ve.ui.Surface( doc );
+ this.setupDone = true;
+ this.surface = new ve.ui.Surface( this.document );
this.toolbar = new ve.ui.TargetToolbar( this, this.surface, { 'shadow':
true } );
// Initialization
@@ -40,7 +57,3 @@
this.surface.setPasteRules( this.constructor.static.pasteRules );
this.surface.initialize();
};
-
-/* Inheritance */
-
-OO.inheritClass( ve.init.sa.Target, ve.init.Target );
diff --git a/modules/ve/init/ve.init.Platform.js
b/modules/ve/init/ve.init.Platform.js
index cbf4fdf..b594ff8 100644
--- a/modules/ve/init/ve.init.Platform.js
+++ b/modules/ve/init/ve.init.Platform.js
@@ -64,7 +64,7 @@
* @abstract
* @param {string} key Message key
* @param {Mixed...} [args] List of arguments which will be injected at $1,
$2, etc. in the messaage
- * @returns {string} Localized message
+ * @returns {string} Localized message, or key or '<' + key + '>' if message
not found
*/
ve.init.Platform.prototype.getMessage = function () {
throw new Error( 've.init.Platform.getMessage must be overridden in
subclass' );
diff --git a/modules/ve/test/ce/ve.ce.test.js b/modules/ve/test/ce/ve.ce.test.js
index 27e85e4..6460f37 100644
--- a/modules/ve/test/ce/ve.ce.test.js
+++ b/modules/ve/test/ce/ve.ce.test.js
@@ -171,7 +171,7 @@
} );
QUnit.test( 'fakeImes', function ( assert ) {
- var i, ilen, j, jlen, dom, target, testRunner, testName, testActions,
seq, testInfo,
+ var i, ilen, j, jlen, surface, testRunner, testName, testActions, seq,
testInfo,
action, args, count, foundEndLoop, fakePreventDefault;
// count tests
@@ -211,9 +211,8 @@
foundEndLoop = false;
// First element is the testInfo
testInfo = testActions[0];
- dom = ve.createDocumentFromHtml( testInfo.startDom || '' );
- target = new ve.init.sa.Target( $( '<div>' ).appendTo(
'#qunit-fixture' ), dom );
- testRunner = new ve.ce.TestRunner( target.surface );
+ surface = ve.test.utils.createSurfaceFromHtml(
testInfo.startDom || '' );
+ testRunner = new ve.ce.TestRunner( surface );
// start at 1 to omit the testInfo
for ( j = 1, jlen = testActions.length; j < jlen; j++ ) {
action = testActions[j].action;
@@ -233,6 +232,6 @@
}
// Test that there is at least one endLoop
assert.equal( foundEndLoop, true, testName + ' found at least
one endLoop' );
- target.surface.destroy();
+ surface.destroy();
}
} );
diff --git a/modules/ve/test/index.php b/modules/ve/test/index.php
index 12bebd1..1704373 100644
--- a/modules/ve/test/index.php
+++ b/modules/ve/test/index.php
@@ -53,16 +53,33 @@
<script src="../../ve/init/ve.init.Platform.js"></script>
<script src="../../ve/init/ve.init.Target.js"></script>
<script src="../../ve/ve.debug.js"></script>
+ <!-- jquery.i18n -->
+ <script src="../../jquery.i18n/src/jquery.i18n.js"></script>
+ <script
src="../../jquery.i18n/src/jquery.i18n.messagestore.js"></script>
+ <script
src="../../jquery.i18n/src/jquery.i18n.parser.js"></script>
+ <script
src="../../jquery.i18n/src/jquery.i18n.emitter.js"></script>
+ <script
src="../../jquery.i18n/src/jquery.i18n.language.js"></script>
+ <script
src="../../jquery.i18n/src/jquery.i18n.fallbacks.js"></script>
+ <script src="../../jquery.i18n/src/languages/bs.js"></script>
+ <script src="../../jquery.i18n/src/languages/dsb.js"></script>
+ <script src="../../jquery.i18n/src/languages/fi.js"></script>
+ <script src="../../jquery.i18n/src/languages/ga.js"></script>
+ <script src="../../jquery.i18n/src/languages/he.js"></script>
+ <script src="../../jquery.i18n/src/languages/hsb.js"></script>
+ <script src="../../jquery.i18n/src/languages/hu.js"></script>
+ <script src="../../jquery.i18n/src/languages/hy.js"></script>
+ <script src="../../jquery.i18n/src/languages/la.js"></script>
+ <script src="../../jquery.i18n/src/languages/ml.js"></script>
+ <script src="../../jquery.i18n/src/languages/os.js"></script>
+ <script src="../../jquery.i18n/src/languages/ru.js"></script>
+ <script src="../../jquery.i18n/src/languages/sl.js"></script>
+ <script src="../../jquery.i18n/src/languages/uk.js"></script>
<!-- ext.visualEditor.standalone -->
<script src="../../ve/init/sa/ve.init.sa.js"></script>
<script src="../../ve/init/sa/ve.init.sa.Platform.js"></script>
<script src="../../ve/init/sa/ve.init.sa.Target.js"></script>
<!-- Standalone init -->
<script>
- <?php
- require '../../../VisualEditor.i18n.php';
- echo 've.init.platform.addMessages( ' .
json_encode( $messages['en'] ) . " );\n";
- ?>
ve.init.platform.setModulesUrl( '../..' );
</script>
<!-- rangy -->
diff --git a/modules/ve/test/init/ve.init.Platform.test.js
b/modules/ve/test/init/ve.init.Platform.test.js
index 27515f8..15dfba5 100644
--- a/modules/ve/test/init/ve.init.Platform.test.js
+++ b/modules/ve/test/init/ve.init.Platform.test.js
@@ -5,94 +5,71 @@
* @license The MIT License (MIT); see LICENSE.txt
*/
-QUnit.module( 've.init.Platform', {
- setup: function () {
- this.platform = ve.init.platform;
+QUnit.module( 've.init.Platform' );
- // Clone and deference instance
- ve.init.platform = ve.cloneObject( ve.init.platform );
-
- // Reset internal properties needed to keep tests atomic
- ve.init.platform.parsedMessages = {};
-
- // Platform specific properties
- // TODO: Perhaps this warrants a clearMessages() method?
- if ( ve.init.sa && ve.init.platform instanceof
ve.init.sa.Platform ) {
- ve.init.platform.messages = {};
- } else if ( ve.init.mw && ve.init.platform instanceof
ve.init.mw.Platform ) {
- /*global mw */
- this.mwMessagesValues = ve.copy( mw.messages.values );
- }
- },
- teardown: function () {
- ve.init.platform = this.platform;
- if ( ve.init.mw && ve.init.platform instanceof
ve.init.mw.Platform ) {
- /*global mw */
- mw.messages.values = this.mwMessagesValues;
- }
- }
-} );
-
-QUnit.test( 'messages', 4, function ( assert ) {
+QUnit.asyncTest( 'messages', 4, function ( assert ) {
var platform = ve.init.platform;
- assert.strictEqual(
- platform.getMessage( 'foo' ),
- '<foo>',
- 'return bracket-wrapped key as fallback'
- );
+ platform.getInitializedPromise().done( function () {
+ QUnit.start();
+ assert.ok(
+ /^<?platformtest-foo\>?$/.test( platform.getMessage(
'platformtest-foo' ) ),
+ 'return plain key as fallback, possibly wrapped in
brackets'
+ );
- platform.addMessages( {
- foo: 'Foo & Bar <quux action="followed">by</quux>!',
- bacon: 'Bacon <&> Ipsum: $1'
+ platform.addMessages( {
+ 'platformtest-foo': 'Foo & Bar <quux
action="followed">by</quux>!',
+ 'platformtest-bacon': 'Bacon <&> Ipsum: $1'
+ } );
+
+ assert.strictEqual(
+ platform.getMessage( 'platformtest-foo' ),
+ 'Foo & Bar <quux action="followed">by</quux>!',
+ 'return plain message'
+ );
+
+ assert.strictEqual(
+ platform.getMessage( 'platformtest-bacon', 10 ),
+ 'Bacon <&> Ipsum: 10',
+ 'return plain message with $# replacements'
+ );
+
+ assert.ok(
+ /^<?platformtest-quux\>?$/.test( platform.getMessage(
'platformtest-quux' ) ),
+ 'return plain key as fallback, possibly wrapped in
brackets (after set up)'
+ );
} );
-
- assert.strictEqual(
- platform.getMessage( 'foo' ),
- 'Foo & Bar <quux action="followed">by</quux>!',
- 'return plain message'
- );
-
- assert.strictEqual(
- platform.getMessage( 'bacon', 10 ),
- 'Bacon <&> Ipsum: 10',
- 'return plain message with $# replacements'
- );
-
- assert.strictEqual(
- platform.getMessage( 'quux' ),
- '<quux>',
- 'return bracket-wrapped key as fallback (after set up)'
- );
} );
-QUnit.test( 'parsedMessage', 3, function ( assert ) {
+QUnit.asyncTest( 'parsedMessage', 3, function ( assert ) {
var platform = ve.init.platform;
- assert.strictEqual(
- platform.getParsedMessage( 'foo' ),
- '<foo>',
- 'return html escaped brackets in wrapped-key fallback'
- );
+ platform.getInitializedPromise().done( function () {
+ QUnit.start();
+ assert.ok(
+ /^(<)?platformtest-quux(>)?$/.test(
platform.getMessage( 'platformtest-quux' ) ),
+ 'any brackets in fallbacks are HTML-escaped'
+ );
- platform.addMessages( {
- foo: 'Foo & Bar <quux action="followed">by</quux>!',
- bacon: 'Bacon <&> Ipsum: $1'
+ platform.addMessages( {
+ 'platformtest-foo': 'Foo & Bar <quux
action="followed">by</quux>!',
+ 'platformtest-bacon': 'Bacon <&> Ipsum: $1'
+ } );
+
+ platform.addParsedMessages( {
+ 'platformtest-foo': 'Foo <quux><html></quux>'
+ } );
+
+ assert.strictEqual(
+ platform.getParsedMessage( 'platformtest-foo' ),
+ 'Foo <quux><html></quux>',
+ 'prefer value from parsedMessage store'
+ );
+
+ assert.strictEqual(
+ platform.getParsedMessage( 'platformtest-bacon', 10 ),
+ 'Bacon <&> Ipsum: $1',
+ 'fall back to html-escaped version of plain message, no
$# replacements'
+ );
} );
-
- platform.addParsedMessages( {
- foo: 'Foo <quux><html></quux>'
- } );
-
- assert.strictEqual(
- platform.getParsedMessage( 'foo' ),
- 'Foo <quux><html></quux>',
- 'prefer value from parsedMessage store'
- );
-
- assert.strictEqual(
- platform.getParsedMessage( 'bacon', 10 ),
- 'Bacon <&> Ipsum: $1',
- 'fall back to html-escaped version of plain message, no $#
replacements'
- );
} );
diff --git a/modules/ve/test/ve.test.utils.js b/modules/ve/test/ve.test.utils.js
index 8057887..d11e017 100644
--- a/modules/ve/test/ve.test.utils.js
+++ b/modules/ve/test/ve.test.utils.js
@@ -136,5 +136,6 @@
*/
ve.test.utils.createSurfaceFromDocument = function ( doc ) {
var target = new ve.init.sa.Target( $( '#qunit-fixture' ), doc );
+ target.setup();
return target.surface;
};
--
To view, visit https://gerrit.wikimedia.org/r/100503
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: Iac7dfd327eadf9b503a61510574d35d748faac92
Gerrit-PatchSet: 12
Gerrit-Project: mediawiki/extensions/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Catrope <[email protected]>
Gerrit-Reviewer: Catrope <[email protected]>
Gerrit-Reviewer: Esanders <[email protected]>
Gerrit-Reviewer: Krinkle <[email protected]>
Gerrit-Reviewer: jenkins-bot
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits