infusion
Version:
Infusion is an application framework for developing flexible stuff with JavaScript
138 lines (128 loc) • 6.02 kB
JavaScript
/*
Copyright The Infusion copyright holders
See the AUTHORS.md file at the top-level directory of this distribution and at
https://github.com/fluid-project/infusion/raw/main/AUTHORS.md.
Licensed under the Educational Community License (ECL), Version 2.0 or the New
BSD license. You may not use this file except in compliance with one these
Licenses.
You may obtain a copy of the ECL 2.0 License and BSD License at
https://github.com/fluid-project/infusion/raw/main/Infusion-LICENSE.txt
*/
"use strict";
fluid.defaults("fluid.messageResolver", {
gradeNames: ["fluid.component"],
mergePolicy: {
messageBase: "nomerge",
parents: "nomerge"
},
resolveFunc: fluid.stringTemplate,
parseFunc: fluid.identity,
messageBase: {},
members: {
messageBase: "@expand:{that}.options.parseFunc({that}.options.messageBase)"
},
invokers: {
lookup: "fluid.messageResolver.lookup({that}, {arguments}.0)", // messagecodes
resolve: "fluid.messageResolver.resolve({that}, {arguments}.0, {arguments}.1)" // messagecodes, args
},
parents: []
});
/**
* Look up the first matching message template, starting with the current grade and working up through its parents.
* Returns both the template for the message and the function used to resolve the localised value. By default
* the resolve function is `fluid.stringTemplate`, and the template returned uses its syntax.
*
* @param {Object} that - The component itself.
* @param {String[]} messagecodes - One or more message codes to look up templates for.
* @return {Object} - An object that contains `template` and `resolveFunc` members (see above).
*/
fluid.messageResolver.lookup = function (that, messagecodes) {
var resolved = fluid.messageResolver.resolveOne(that.messageBase, messagecodes);
if (resolved === undefined) {
return fluid.find(that.options.parents, function (parent) {
return parent ? parent.lookup(messagecodes) : undefined;
});
} else {
return {template: resolved, resolveFunc: that.options.resolveFunc};
}
};
/**
* Look up the first message that corresponds to a message code found in `messageCodes`. Then, resolve its
* localised value. By default, supports variable substitutions using `fluid.stringTemplate`.
*
* @param {Object} that - The component itself.
* @param {Array} messagecodes - A list of message codes to look for.
* @param {Object} args - A map of variables that may potentially be used as part of the final output.
* @return {String} - The final message, localised, with any variables found in `args`.
*/
fluid.messageResolver.resolve = function (that, messagecodes, args) {
if (!messagecodes) {
return "[No messagecodes provided]";
}
messagecodes = fluid.makeArray(messagecodes);
var looked = that.lookup(messagecodes);
return looked ? looked.resolveFunc(looked.template, args) :
"[Message string for key " + messagecodes[0] + " not found]";
};
// unsupported, NON-API function
fluid.messageResolver.resolveOne = function (messageBase, messagecodes) {
// Use this strategy to fake up the possibility of indirecting into a dynamically reloaded resource (e.g.
// via relocalisation) without having to push it through a component model
if (messageBase instanceof fluid.fetchResources.FetchOne) {
messageBase = fluid.fetchResources.resolveFetchOne(messageBase);
};
for (var i = 0; i < messagecodes.length; ++i) {
var code = messagecodes[i];
var message = messageBase[code];
// Bless people who have resolved individual messages asynchronously as well. In practice this will
// eventually have to give way to an approach for general asynchrony in options, or the much more
// efficient "immutable application" model for models, or both
if (message instanceof fluid.fetchResources.FetchOne) {
message = fluid.fetchResources.resolveFetchOne(message);
}
if (message !== undefined) {
return message;
}
}
};
/**
* Converts a data structure consisting of a mapping of keys to message strings, into a "messageLocator" function
* which maps an array of message codes, to be tried in sequence until a key is found, and an array of substitution
* arguments, into a substituted message string.
*
* @param {Object} messageBase - A body of messages to wrap in a resolver function.
* @param {Function} [resolveFunc] - (optional) A "resolver" function to use instead of the default `fluid.stringTemplate`.
* @return {Function} - A "messageLocator" function (see above).
*/
fluid.messageLocator = function (messageBase, resolveFunc) {
var resolver = fluid.messageResolver({messageBase: messageBase, resolveFunc: resolveFunc});
return function (messagecodes, args) {
return resolver.resolve(messagecodes, args);
};
};
/**
* Resolve a "message source", which is either itself a resolver, or an object representing a bundle of messages
* and the associated resolution function.
*
* When passing a "data" object, it is expected to have a `type` element that is set to `data`, and to have a
* `messages` array and a `resolveFunc` function that can be used to resolve messages.
*
* A "resolver" is expected to be an object with a `type` element that is set to `resolver` that exposes a `resolve`
* function.
*
* @param {Object} messageSource - See above.
* @return {Function|String|undefined} - A resolve function or a `String` representing the final resolved output.
*/
fluid.resolveMessageSource = function (messageSource) {
if (messageSource.type === "data") {
if (messageSource.url === undefined) {
return fluid.messageLocator(messageSource.messages, messageSource.resolveFunc);
}
else {
// TODO: fetch via AJAX, and convert format if necessary
}
}
else if (messageSource.type === "resolver") {
return messageSource.resolver.resolve;
}
};