From 3ceb8b61ba1ba5688609ea81889bec0eea20853e Mon Sep 17 00:00:00 2001 From: Rafael Xavier de Souza Date: Mon, 18 Jul 2016 09:49:01 -0300 Subject: [PATCH] Message: Provide Bidi structured text support (2/2) Closes #539 Fixes #599 --- README.md | 6 +-- doc/api/message/message-formatter.md | 44 +++++++++----------- src/message-runtime.js | 9 ++-- src/message.js | 20 ++++----- test/functional/message/format-message.js | 16 +++---- test/functional/message/message-formatter.js | 18 ++++---- 6 files changed, 52 insertions(+), 61 deletions(-) diff --git a/README.md b/README.md index c64cc5a90..37fc60317 100644 --- a/README.md +++ b/README.md @@ -509,7 +509,7 @@ Read more details about locale at [UTS#35 locale][]. [Read more...](doc/api/message/load-messages.md) -- **`.messageFormatter( path ) ➡ function( [variables] )`** +- **`.messageFormatter( path [, options] ) ➡ function( [variables] )`** Return a function that formats a message (using ICU message format pattern) given its path and a set of variables into a user-readable string. It supports @@ -525,9 +525,9 @@ Read more details about locale at [UTS#35 locale][]. [Read more...](doc/api/message/message-formatter.md) -- **`.formatMessage( path [, variables ] )`** +- **`.formatMessage( path [, variables, options ] )`** - Alias for `.messageFormatter( path )([ variables ])`. + Alias for `.messageFormatter( path [, options] )([ variables ])`. ### Number module diff --git a/doc/api/message/message-formatter.md b/doc/api/message/message-formatter.md index 203a3c3dc..58f6fac8b 100644 --- a/doc/api/message/message-formatter.md +++ b/doc/api/message/message-formatter.md @@ -1,4 +1,4 @@ -## .messageFormatter( path, options ) ➡ function([ variables ]) +## .messageFormatter( path [, options] ) ➡ function([ variables ]) Return a function that formats a message (using ICU message format pattern) given its path and a set of variables into a user-readable string. It supports @@ -16,22 +16,22 @@ String or Array containing the path of the message content, eg. **options** (optional) -Options should be an Objects, where each property can be referenced by name. -The possible property having name recognizable by messageFormatter is "setBiDiSupport". -It should have Boolean value indicating whether Bidi structuring is to be imposed on formatted -message (like {"setBiDiSupport": true}). -Special Unicode Bidi marks are inserted depending on locale in order to preserve the text -flow of structured message which corresponds give local (from right-to-left for Bidi scripts -like Arabic, Hebrew or Pharsi and left-to-right otherwise. -For more info on Bidi structured text see: -http://cldr.unicode.org/development/development-process/design-proposals/bidi-handling-of-structured-text +A JSON object including none or any of the following options. + +> **setBiDiSupport** Optional +> +> Boolean (default `false`) enable or disable the addition of Unicode BiDi +> control characters to all input to preserve the the integrity of the output +> when mixing LTR and RTL text, e.g., `{ setBiDiSupport: true }`. +> +> For more information on BiDi structured text see: +> http://cldr.unicode.org/development/development-process/design-proposals/bidi-handling-of-structured-text **variables** (optional) -Variables can be Objects, where each property can be referenced by name inside a -message; or Arrays, where each entry of the Array can be used inside a message, -using numeric indices. When passing one or more arguments of other types, -they're converted to an Array and used as such. +A JSON object or an Array. When it's a JSON object, each key corresponds to a +message variable. When it's an Array, each item corresponds to a message +variable using numeric indices. ### Example @@ -84,10 +84,6 @@ formatter = Globalize( "en" ).messageFormatter( "hello" ); formatter([ "Wolfgang", "Amadeus", "Mozart" ]); // > "Hello, Wolfgang Amadeus Mozart" -// Numbered variables using function arguments. -formatter( "Wolfgang", "Amadeus", "Mozart" ); -// > "Hello, Wolfgang Amadeus Mozart" - // Named variables using Object key-value pairs. formatter = Globalize( "en" ).messageFormatter( "hey" ); formatter({ @@ -224,16 +220,16 @@ likeFormatter( 2 ); likeFormatter( 3 ); // > "You and 2 others liked this" ``` -#### Bidi structured meessage - Globalize.loadMessages({ - ar: { breadcrumb: "{0} >> {1} >> {2}" } - }); -bidiFormatter = Globalize( "ar" ).messageFormatter( "breadcrumb", {"setBiDiSupport": true} ); + +#### BiDi structured meessage +Globalize.loadMessages({ + ar: { breadcrumb: "{0} >> {1} >> {2}" } +}); +bidiFormatter = Globalize( "ar" ).messageFormatter( "breadcrumb", { setBiDiSupport: true } ); bidiFormatter( "First", "Second", "Third" ); // > "Third << Second << First" - Read on [SlexAxton/messageFormatter.js][] for more information on regard of ICU MessageFormat. diff --git a/src/message-runtime.js b/src/message-runtime.js index eae8bc9de..fc5533426 100644 --- a/src/message-runtime.js +++ b/src/message-runtime.js @@ -13,15 +13,16 @@ Globalize._messageFormat = new messageFormatterRuntime(); // TODO setStrictNumbe Globalize._validateParameterTypeMessageVariables = validateParameterTypeMessageVariables; Globalize.messageFormatter = -Globalize.prototype.messageFormatter = function( /* path */ ) { +Globalize.prototype.messageFormatter = function( path, options ) { + options = options || {}; return Globalize[ - runtimeKey( "messageFormatter", this._locale, [].slice.call( arguments, 0 ) ) + runtimeKey( "messageFormatter", this._locale, [ path, options ] ) ]; }; Globalize.formatMessage = -Globalize.prototype.formatMessage = function( path /* , variables */ ) { - return this.messageFormatter( path ).apply( {}, [].slice.call( arguments, 1 ) ); +Globalize.prototype.formatMessage = function( path, variables, options ) { + return this.messageFormatter( path, options ).apply( {}, [ variables ] ); }; return Globalize; diff --git a/src/message.js b/src/message.js index d9bfd34c4..d4a0892a3 100644 --- a/src/message.js +++ b/src/message.js @@ -24,8 +24,6 @@ define([ messageCompiler, messageFormatterRuntime, messageFormatterFn, messageFormatterRuntimeBind, alwaysArray ) { -var slice = [].slice; - /** * .loadMessages( json ) * @@ -64,19 +62,21 @@ Globalize.loadMessages = function( json ) { */ Globalize.messageFormatter = Globalize.prototype.messageFormatter = function( path, options ) { - var cldr, formatter, message, pluralGenerator, returnFn, - args = slice.call( arguments, 0 ); + var args, cldr, formatter, message, pluralGenerator, returnFn; validateParameterPresence( path, "path" ); validateParameterType( path, "path", typeof path === "string" || Array.isArray( path ), "a String nor an Array" ); - path = alwaysArray( path ); cldr = this.cldr; + options = options || {}; validateDefaultLocale( cldr ); validateMessageBundle( cldr ); + args = [ path, options ]; + path = alwaysArray( path ); + message = cldr.get( [ "globalize-messages/{bundle}" ].concat( path ) ); validateMessagePresence( path, message ); @@ -144,19 +144,19 @@ Globalize.prototype.messageFormatter = function( path, options ) { }; /** - * .formatMessage( path [, variables] ) + * .formatMessage( path [, variables, options] ) * * @path [String or Array] * * @variables [Number, String, Array or Object] * + * @options [object] + * * Format a message given its path. */ Globalize.formatMessage = -Globalize.prototype.formatMessage = function( path /* , variables */ ) { - return ( arguments[ 1 ] && arguments[ 1 ].setBiDiSupport === true ) ? - this.messageFormatter( path, arguments[ 1 ] ).apply( {}, slice.call( arguments, 2 ) ) : - this.messageFormatter( path ).apply( {}, slice.call( arguments, 1 ) ); +Globalize.prototype.formatMessage = function( path, variables, options ) { + return this.messageFormatter( path, options ).apply( {}, [ variables ] ); }; return Globalize; diff --git a/test/functional/message/format-message.js b/test/functional/message/format-message.js index 0f374c618..fda612964 100644 --- a/test/functional/message/format-message.js +++ b/test/functional/message/format-message.js @@ -1,4 +1,4 @@ -define([ +define([ "globalize", "json!cldr-data/supplemental/likelySubtags.json", "json!cldr-data/supplemental/plurals.json", @@ -52,15 +52,11 @@ QUnit.test( "should format a message", function( assert ) { }), "Hello, Beethoven" ); }); -QUnit.test( "should format a message", function( assert ) { - assert.equal( Globalize( "en" ).formatMessage( "greetings/hello", { - name: "Beethoven" - }), "Hello, Beethoven" ); -}); - -QUnit.test( "should support Bidi structured text", function( assert ) { - assert.equal( Globalize( "he" ).formatMessage( "breadcrumb", {"setBiDiSupport": true}, - [ "Mozart", "Bethoven", "Dvorzak" ] +QUnit.test( "should support BiDi structured text", function( assert ) { + assert.equal( Globalize( "he" ).formatMessage( + "breadcrumb", + [ "Mozart", "Bethoven", "Dvorzak" ], + { setBiDiSupport: true } ), "\u200FMozart\u200F >> \u200FBethoven\u200F >> \u200FDvorzak\u200F" ); }); diff --git a/test/functional/message/message-formatter.js b/test/functional/message/message-formatter.js index 8658dc0bd..4f5d184da 100644 --- a/test/functional/message/message-formatter.js +++ b/test/functional/message/message-formatter.js @@ -152,10 +152,6 @@ QUnit.test( "should support ICU message format", function( assert ) { assert.messageFormatter( "en", "greetings/helloArray", "Beethoven", "Hello, Beethoven" ); assert.messageFormatter( "en", "greetings/helloArray2", [ "Beethoven", "Mozart" ], "Hello, Beethoven and Mozart" ); - assert.equal( - Globalize( "en" ).messageFormatter( "greetings/helloArray2" )( "Beethoven", "Mozart" ), - "Hello, Beethoven and Mozart" - ); assert.messageFormatter( "en", "greetings/helloName", { name: "Beethoven" }, "Hello, Beethoven" ); @@ -201,9 +197,11 @@ QUnit.test( "should support ICU message format", function( assert ) { }, "4th category" ); }); -QUnit.test( "should support Bidi structured text", function( assert ) { +QUnit.test( "should support BiDi structured text", function( assert ) { assert.equal( - Globalize( "he" ).messageFormatter( "helloArray", {"setBiDiSupport": true} )( "Beethoven", "Mozart" ), + Globalize( "he" ).messageFormatter( "helloArray", { + setBiDiSupport: true + })( "Beethoven", "Mozart" ), "Hello, \u200FBeethoven\u200F & \u200FMozart\u200F" ); }); @@ -228,8 +226,8 @@ QUnit.test( "should allow for runtime compilation", function( assert ) { util.assertRuntimeBind( assert, Globalize( "en" ).messageFormatter( "amen" ), - "b639686813", - "Globalize(\"en\").messageFormatter(\"amen\")", + "b141291319", + "Globalize(\"en\").messageFormatter(\"amen\",{})", function( runtimeArgs ) { assert.equal( runtimeArgs[ 0 ].toString(), @@ -241,8 +239,8 @@ QUnit.test( "should allow for runtime compilation", function( assert ) { util.assertRuntimeBind( assert, Globalize( "en" ).messageFormatter( "like" ), - "b328290139", - "Globalize(\"en\").messageFormatter(\"like\")", + "b452335545", + "Globalize(\"en\").messageFormatter(\"like\",{})", function( runtimeArgs ) { assert.equal( runtimeArgs[ 0 ].toString(),