Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package jsmessages
import play.api.i18n.Messages
import play.api.libs.json.{JsValue, Json, Writes}
import play.twirl.api.JavaScript
import scala.collection.compat._
/**
* Generate a JavaScript function computing localized messages of a Play application.
*
* Typical usage (from within a Play controller):
*
* {{{
* val jsMessages: JsMessages = ???
*
* val messages = Action { implicit request =>
* Ok(jsMessages(Some("window.Messages")))
* }
* }}}
*
* Then on client-side:
*
* {{{
* console.log(Messages("greeting", "Julien")); // prints "Hello, Julien!"
* }}}
*
* See [[JsMessagesFactory]] to know how to get a `JsMessages` instance.
*
* @param allMessagesData All the messages of the application, as a map of (lang -> map(key -> message pattern)). As it
* is the case in Play, JsMessages assumes that “default” messages are indexed by the `"default"`
* and `"default.play"` language codes.
*/
class JsMessages(allMessagesData: Map[String, Map[String, String]]) {
// Message patterns have to escape quotes using double quotes, here we unescape them because we don’t support using quotes to escape format elements
// TODO Also remove subformats
private val allMessagesUnescaped: Map[String, Map[String, String]] =
allMessagesData.view.mapValues(_.view.mapValues(_.replace("''", "'")).toMap).toMap
/**
* Messages for each available lang of the application.
*
* The message corresponding to a given key is found by searching in the
* following locations, in order: the language (e.g. in the `conf/messages.fr-FR` file), the language
* country (e.g. `conf/messages.fr`), the application default messages (`conf/messages`) and the
* Play default messages.
*/
lazy val allMessages: Map[String, Map[String, String]] = for ((lang, msgs) <- allMessagesUnescaped) yield {
lang match {
// Do not merge with "default" if its "default.play"
case "default.play" => lang -> allMessagesUnescaped.getOrElse("default.play", Map.empty)
case _ => lang -> (
allMessagesUnescaped.getOrElse("default.play", Map.empty) ++
allMessagesUnescaped.getOrElse("default", Map.empty) ++
extractCountry(lang).flatMap(country => allMessagesUnescaped.get(country)).getOrElse(Map.empty) ++
msgs
)
}
}
/**
* Same as `allMessages`, but as a JSON value.
*/
final val allMessagesJson: JsValue = Json.toJson(allMessages)
// Cache of all the messages map as a JSON object
private val allMessagesCache: String = allMessagesJson.toString()
// Per lang cache of the messages
private val messagesCache: Map[String, String] = allMessages.view.mapValues(map => formatMap(map)).toMap
/**
* @param messages Messages instance containing the lang to retrieve messages for
* @return The messages defined for the given language `lang`, as a map
* of (key -> message). The message corresponding to a given key is found by searching in the
* following locations, in order: the language (e.g. in the `conf/messages.fr-FR` file), the language
* country (e.g. `conf/messages.fr`), the application default messages (`conf/messages`) and the
* Play default messages.
*/
def messages(implicit messages: Messages): Map[String, String] = lookupLang(allMessages, messages)
/**
* @param messages Messages instance containing the lang to retrieve messages for
* @return The JSON formatted string of the for the given language `lang`. This is strictly equivalent to
* `Json.toJson(jsMessages.messages).toString`, but may be faster due to the use of caching.
*/
def messagesString(implicit messages: Messages): String = lookupLang(messagesCache, messages)
/**
* Generates a JavaScript function computing localized messages in the given implicit `Lang`.
*
* For example:
*
* {{{
* val messages = Action { implicit request =>
* Ok(jsMessages(Some("window.Messages")))
* }
* }}}
*
* Then use it in your JavaScript code as follows:
*
* {{{
* alert(Messages('greeting', 'World'));
* }}}
*
* Provided you have the following message in your `conf/messages` file:
*
* {{{
* greeting=Hello {0}!
* }}}
*
* Note: This implementation does not handle quotes escaping in patterns and subformats (see
* http://docs.oracle.com/javase/8/docs/api/java/text/MessageFormat.html)
*
* @param namespace Optional JavaScript namespace to use to put the function definition. If not set, this
* function will just generate a function. Otherwise it will generate a function and assign
* it to the given namespace. Note: you can set something like `Some("var Messages")` to use
* a fresh variable.
* @param messages Messages instance defining the language to use. The message corresponding to a given key is found by searching in the
* following locations, in order: the language (e.g. in the `conf/messages.fr-FR` file), the language
* country (e.g. `conf/messages.fr`), the application default messages (`conf/messages`) and the
* Play default messages.
*/
def apply(namespace: Option[String] = None)(implicit messages: Messages): JavaScript = apply(namespace, messagesString)
/**
* Generates a JavaScript function computing localized messages in all the languages of the application.
*
* For example:
*
* {{{
* val messages = Action {
* Ok(jsMessages.all(Some("window.Messages")))
* }
* }}}
*
* Then use it in your JavaScript code as follows:
*
* {{{
* alert(Messages('en', 'greeting', 'World'));
* }}}
*
* Provided you have the following message in your `conf/messages` file:
*
* {{{
* greeting=Hello {0}!
* }}}
*
* Note that, given a message key, the JavaScript function will search the corresponding message in the
* following locations, in order: the language (e.g. in the `conf/messages.fr-FR` file), the language
* country (e.g. `conf/messages.fr`), the application default messages (`conf/messages`) and the
* Play default messages.
*
* Note: This implementation does not handle quotes escaping in patterns and subformats (see
* http://docs.oracle.com/javase/8/docs/api/java/text/MessageFormat.html)
*
* @param namespace Optional JavaScript namespace to use to put the function definition. If not set, this
* function will just generate a function. Otherwise it will generate a function and
* assign it to the given namespace. Note: you can set something like
* `Some("var Messages")` to use a fresh variable.
*/
def all(namespace: Option[String] = None): JavaScript = all(namespace, allMessagesCache)
/**
* @param namespace Optional namespace that will contain the generated function
* @param messages Map of (key -> message) to use, as a JSON literal
* @return a JavaScript function taking a key and eventual arguments and returning a formatted message
*/
private def apply(namespace: Option[String], messages: String): JavaScript = {
JavaScript(s""" #${namespace.map{_ + "="}.getOrElse("")}(function(u){function f(k){
#var m;
#if(typeof k==='object'){
#for(var i=0,l=k.length;i-1) {var lg=l.split('-')[0];r=f.messages[lg] && f.messages[lg][kg];}
#if (r===u) {r=f.messages['default'] && f.messages['default'][kg];}
#if (r===u) {r=f.messages['default.play'] && f.messages['default.play'][kg];}
#return r;
#}
#function h(kh){
#var m;
#if(typeof kh==='object'){
#for(var i=0,le=kh.length;i data.get(country)))
// If none, fallback to default
.orElse(data.get("default"))
// If none, screw that, crash the system! It's your fault for no having a default.
.getOrElse(sys.error(s"Lang $lang is not supported by the application. Consider adding it to your 'application.langs' key in your 'conf/application.conf' file or at least provide a default messages file."))
}
}