Source: src/main/javascript/oracle/oj/ojcore/Translations.js

Oracle® JavaScript Extension Toolkit (JET)
3.2.0

E87541-01

/*
** Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
**
**34567890123456789012345678901234567890123456789012345678901234567890123456789
*/

/*global ojt:false*/

/**
 * @class Services for Retrieving Translated Resources
 * @export
 */
oj.Translations = {};

/**
 * Sets the translation bundle used by JET
 * If an AMD loader (such as Require.js) is not present, this method should be called by the application to provide
 * translated strings for JET.
 * This method may also be used by an application that wants to completely replace the resource bundle that is automatically
 * fetched by an AMD loader.
 * @param {Object} bundle resource bundle that should be used by the framework
 * @export
 */
oj.Translations.setBundle = function(bundle)
{
  oj.Translations._bundle = bundle;
};

/**
 * Retrives a translated resource for a given key
 * @param {string} key resource key The dot character (.) within the key string
 * is interpreted as a separator for the name of a sub-section within the bundle.
 * For example, 'components.chart', would be read as the 'chart' section within
 * 'components'. Thus the key name for an individual section should never contain a dot.
 * @return {Object|string|null} resource associated with the key or null if none was found
 * @export
 */
oj.Translations.getResource = function(key)
{
  return oj.Translations._getResourceString(key);
};

/**
 * Applies parameters to a format pattern
 * @param {string} pattern pattern that may contain tokens like {0}, {1}, {name}. These tokens
 * will be used to define string keys for retrieving values from the parameters
 * object. Token strings should not contain comma (,) 
 * or space characters, since they are reserved for future format type enhancements. 
 * The reserved characters within a pattern are:
 * $ { } [ ]  
 * These characters will not appear in the formatted output unless they are escaped
 * with a dollar character ('$').
 * 
 * @param {Object|Array} parameters parameters to be inserted into the string. Both arrays and
 * Javascript objects with string keys are accepted.
 * 
 * @return formatted message or null if the pattern argument was null
 * @export
 */
oj.Translations.applyParameters = function(pattern, parameters)
{
  return (pattern == null) ? null : oj.Translations._format(pattern, parameters);
};

/**
 * Retrieves a translated string after inserting optional parameters. 
 * The method uses a key to retrieve a format pattern from the resource bundle.
 * Tokens like {0}, {1}, {name} within the pattern will be used to define placement
 * for the optional parameters.  Token strings should not contain comma (,) 
 * or space characters, since they are reserved for future format type enhancements.
 * The reserved characters within a pattern are:
 * $ { } [ ]  
 * These characters will not appear in the formatted output unless they are escaped
 * with a dollar character ('$').
 * @param {string} key  translations resource key. The dot character (.) within the key string
 * is interpreted as a separator for the name of a sub-section within the bundle.
 * For example, 'components.chart', would be read as the 'chart' section within
 * 'components'. Thus the key name for an individual section should never contain a dot.
 * 
 * @param {...string|Object|Array} var_args  - optional parameters to be inserted into the 
 * translated pattern.
 * 
 * If more than one var_args arguments are passed, they will be treated as an array 
 * for replacing positional tokens like {0}, {1}, etc.
 * If a single argument is passed, it will be treated as a Javascript Object whose
 * keys will be matched to tokens within the pattern. Note that an Array is just
 * a special kind of such an Object.
 * 
 * For backward compatibility, a var_args argument whose type is neither 
 * Object or Array will be used to replace {0} in the pattern.
 * 
 * @return formatted translated string
 * @export
 */
oj.Translations.getTranslatedString = function(key, var_args)
{  
  var val = oj.Translations._getResourceString(key);
  
  if (val == null)
  {
    return key;
  }
  
  var params = {};
  
  if (arguments.length > 2)
  {
    params = Array.prototype.slice.call(arguments, 1);
  }
  else if (arguments.length == 2)
  {
    params = arguments[1];
    if (typeof params !== 'object' && !(params instanceof Array))
    {
      params = [params];
    }
      
  }
  
  return oj.Translations.applyParameters(val, params);
};


/**
 * Provides a key-to-value map of the translated resources for a given component name
 * @param {string} componentName name of the component
 * @return a map of translated resources
 * @export
 */
oj.Translations.getComponentTranslations = function(componentName)
{
  var bundle = oj.Translations._getBundle()[componentName], translations, k;
  
  if (bundle == null)
  {
    return {};
  }
  
  // Assume that the set of keys remains constant regardless of the current locale
  translations = {};
  for(k in bundle)
  {
    if (bundle.hasOwnProperty(k)) {
        translations[k] = bundle[k];
    }
  }
  return translations;
};

/**
 * Retrives a translated resource for a given key, accounting for nested keys
 * @param {string} key
 * @return {string|null} resource associated with the key or null if none was found
 * @private
 */
oj.Translations._getResourceString = function(key)
{
  // Account for dot separated nested keys
  var keys = key ? key.split(".") : [], bundle = oj.Translations._getBundle(), 
          iteration = keys.length, index = 0, subkey = keys[index];
  oj.Assert.assertObject(bundle);
  
  // even though we start with a valid bundle it's possible that part or all of the key is invalid, 
  // so check we have a valid bundle in the while loop
  while (--iteration > 0 && bundle) 
  {
    // if we have a key like a.b.c
    bundle = bundle[subkey];
    index++;
    subkey = keys[index];
  }
    
  return bundle ? (bundle[subkey] || null) : null;
};

oj.Translations._format = function(formatString, parameters)
{
  var formatLength = formatString.length;
  
  // Use the javascript StringBuffer technique.
  var buffer = [];
  
  var token = null;
  
  
  var escaped = false;
  var isToken = false;
  var isGroup = false;
  var isExcluded = false;
  
  var tokenTerminated; // this will be set to true when a comma or space is 
                       // encountered in teh token
  var i;
  
  for (i = 0; i < formatLength; i++)
  {
    var ch = formatString.charAt(i);
    
    var accumulate = false;
    
    if (!escaped)
    {
      switch(ch)
      {
        case '$':
          escaped = true;
          break;
          
        case '{':
          if (!isExcluded)
          {
            if (!isToken)
            {
              tokenTerminated = false;
              token = [];
            }
            isToken = true;
          }
          break;
          
        case '}':
          if (isToken && token.length > 0)
          {
            var val = parameters[token.join('')];
            buffer.push((val === undefined) ? "null" : val);
          }
          isToken = false;
          break;
          
        case '[':
          if (!isToken)
          {
            if (isGroup)
            {
              isExcluded = true;
            }
            else
            {
              isGroup = true;
            }
          }
          break;
          
        case ']':
          if (isExcluded)
          {
            isExcluded = false;
          }
          else
          {
            isGroup = false;
          }
          break;
        
        default:
          accumulate = true;
      }
    }
    else
    {
      accumulate = true;
      escaped = false;  
    }
    
    if (accumulate)
    {
      if (isToken)
      {
        if (ch == ',' || ch ==' ')
        {
          tokenTerminated = true;
        }
        else if (!tokenTerminated)
        {
          token.push(ch);
        }
      }
      else if (!isExcluded)
      {
        buffer.push(ch);
      }
    } 
  }

  // Use the javascript StringBuffer technique for toString()
  return buffer.join("");
};


oj.Translations._getBundle = function()
{
  var b = oj.Translations._bundle;
  if (b)
  {
    return b;
  }
  
  if (oj.__isAmdLoaderPresent()) {
    oj.Assert.assert(ojt !== undefined, "ojtranslations module must be defined");
    return ojt;
  }
  return {};
};