These classes moved from flash-utils back into narya.
Not generally useful to your everyday flash coder. MessageBundle and MessageManager are built on top of Flex Resources, and were already being excluded from our asdocs, and TimeUtil uses MessageBundle. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@5932 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -0,0 +1,437 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Flash Utils library - general purpose ActionScript utility code
|
||||
// Copyright (C) 2007-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/ooolib/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
import mx.resources.IResourceBundle;
|
||||
import mx.resources.ResourceManager;
|
||||
import mx.resources.IResourceManager;
|
||||
|
||||
/**
|
||||
* A message bundle provides an easy mechanism by which to obtain
|
||||
* translated message strings from a resource bundle. It uses the {@link
|
||||
* MessageFormat} class to substitute arguments into the translation
|
||||
* strings. Message bundles would generally be obtained via the {@link
|
||||
* MessageManager}, but could be constructed individually if so desired.
|
||||
*/
|
||||
public class MessageBundle
|
||||
{
|
||||
/**
|
||||
* Initializes the message bundle which will obtain localized messages
|
||||
* from the supplied resource bundle. The path is provided purely for
|
||||
* reporting purposes.
|
||||
*/
|
||||
public function init (msgmgr :MessageManager, path :String, parent :MessageBundle) :void
|
||||
{
|
||||
_msgmgr = msgmgr;
|
||||
_path = path;
|
||||
_parent = parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to this bundle.
|
||||
*/
|
||||
public function getPath () :String
|
||||
{
|
||||
return _path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if we have a translation mapping for the supplied key,
|
||||
* false if not.
|
||||
*/
|
||||
public function exists (key :String) :Boolean
|
||||
{
|
||||
return getResourceString(key, false) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the messages that begin with the specified prefix.
|
||||
*
|
||||
* @param includeParent if true, messages from our parent bundle (and its
|
||||
* parent bundle, all the way up the chain will be included).
|
||||
*/
|
||||
public function getAll (prefix :String, includeParent :Boolean = true) :Array
|
||||
{
|
||||
var messages :Array = []
|
||||
for each (var value :Object in getAllMapped(prefix, includeParent)) {
|
||||
messages.push(value);
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the messages and their keys that begin with the specified prefix.
|
||||
*/
|
||||
public function getAllMapped (prefix :String, includeParent :Boolean = true) :Object
|
||||
{
|
||||
var mgr :IResourceManager = ResourceManager.getInstance();
|
||||
|
||||
// search all locales, using the first one first, but including any
|
||||
// non-overridden keys from backing locales
|
||||
var messages :Object = {};
|
||||
var key :String;
|
||||
for each (var locale :String in mgr.getLocales()) {
|
||||
var bundle :IResourceBundle = mgr.getResourceBundle(locale, _path);
|
||||
if (bundle != null) {
|
||||
for (key in bundle.content) {
|
||||
// preserve the message found in an earlier locale
|
||||
if (StringUtil.startsWith(key, prefix) && !(key in messages)) {
|
||||
messages[key] = bundle.content[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (includeParent && _parent != null) {
|
||||
// don't let parent messages overwrite any we've found
|
||||
var parentMap :Object = _parent.getAllMapped(prefix, includeParent);
|
||||
for (key in parentMap) {
|
||||
if (!(key in messages)) {
|
||||
messages[key] = parentMap[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a String from the resource bundle, or null if there was an
|
||||
* error.
|
||||
*
|
||||
* @param key the resource key.
|
||||
* @param reportMissing whether or not the method should log an error
|
||||
* if the resource didn't exist.
|
||||
*/
|
||||
protected function getResourceString (key :String, reportMissing :Boolean = true) :String
|
||||
{
|
||||
var value :String = ResourceManager.getInstance().getString(_path, key);
|
||||
|
||||
if (value == null) {
|
||||
// if we have a parent, try getting the string from them
|
||||
if (_parent != null) {
|
||||
value = _parent.getResourceString(key, false);
|
||||
}
|
||||
// if we didn't find it in our parent, we want to fall
|
||||
// through and report missing appropriately
|
||||
if (value == null && reportMissing) {
|
||||
Log.getLog(this).warning("Missing translation message",
|
||||
"bundle", _path, "key", key);
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the translation for the specified message key. The
|
||||
* specified arguments are substituted into the translated string.
|
||||
*
|
||||
* <p> If the first argument in the array is an {@link Integer}
|
||||
* object, a translation will be selected accounting for plurality in
|
||||
* the following manner. Assume a message key of
|
||||
* <code>m.widgets</code>, the following translations should be
|
||||
* defined:
|
||||
* <pre>
|
||||
* m.widgets.0 = no widgets.
|
||||
* m.widgets.1 = {0} widget.
|
||||
* m.widgets.n = {0} widgets.
|
||||
* </pre>
|
||||
*
|
||||
* The specified argument is substituted into the translated string as
|
||||
* appropriate. Consider using:
|
||||
*
|
||||
* <pre>
|
||||
* m.widgets.n = {0,number,integer} widgets.
|
||||
* </pre>
|
||||
*
|
||||
* to obtain proper insertion of commas and dots as appropriate for
|
||||
* the locale.
|
||||
*
|
||||
* <p> See {@link MessageFormat} for more information on how the
|
||||
* substitution is performed. If a translation message does not exist
|
||||
* for the specified key, an error is logged and the key itself (plus
|
||||
* the arguments) is returned so that the caller need not worry about
|
||||
* handling a null response.
|
||||
*/
|
||||
public function get (key :String, ... args) :String
|
||||
{
|
||||
// if this string is tainted, we don't translate it, instead we
|
||||
// simply remove the taint character and return it to the caller
|
||||
if (key.charAt(0) === TAINT_CHAR) {
|
||||
return key.substring(1);
|
||||
}
|
||||
|
||||
args = Util.unfuckVarargs(args);
|
||||
|
||||
// if this is a qualified key, we need to pass the buck to the
|
||||
// appropriate message bundle
|
||||
if (key.indexOf(QUAL_PREFIX) == 0) {
|
||||
var qbundle :MessageBundle = _msgmgr.getBundle(getBundle(key));
|
||||
return qbundle.get(getUnqualifiedKey(key), args);
|
||||
}
|
||||
|
||||
// Select the proper suffix if our first argument can be coaxed into an integer
|
||||
var suffix :String = getSuffix(args);
|
||||
var msg :String = getResourceString(key + suffix, false);
|
||||
|
||||
if (msg == null) {
|
||||
if (suffix != "") {
|
||||
// Try the original key
|
||||
msg = getResourceString(key, false);
|
||||
}
|
||||
|
||||
// if the msg is still missing, we have a problem
|
||||
if (msg == null) {
|
||||
Log.getLog(this).warning("Missing translation message",
|
||||
"bundle", _path, "key", key);
|
||||
|
||||
// return something bogus
|
||||
return (key + args);
|
||||
}
|
||||
}
|
||||
|
||||
return StringUtil.substitute(msg, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper function for {@link #get(String,Object[])} that allows us
|
||||
* to automatically perform plurality processing if our first argument
|
||||
* can be coaxed to an {@link Integer}.
|
||||
*/
|
||||
protected function getSuffix (args :Array) :String
|
||||
{
|
||||
if (args.length > 0 && args[0] != null) {
|
||||
try {
|
||||
var count :int = (args[0] is int) ? int(args[0]) :
|
||||
StringUtil.parseInteger(String(args[0]));
|
||||
switch (count) {
|
||||
case 0: return ".0";
|
||||
case 1: return ".1";
|
||||
default: return ".n";
|
||||
}
|
||||
} catch (err :ArgumentError) {
|
||||
// fall out
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the translation for the specified compound message key. A
|
||||
* compound key contains the message key followed by a tab separated
|
||||
* list of message arguments which will be subsituted into the
|
||||
* translation string.
|
||||
*
|
||||
* <p> See {@link MessageFormat} for more information on how the
|
||||
* substitution is performed. If a translation message does not exist
|
||||
* for the specified key, an error is logged and the key itself (plus
|
||||
* the arguments) is returned so that the caller need not worry about
|
||||
* handling a null response.
|
||||
*/
|
||||
public function xlate (compoundKey :String) :String
|
||||
{
|
||||
// if this is a qualified key, we need to pass the buck to the
|
||||
// appropriate message bundle; we have to do it here because we
|
||||
// want the compound arguments of this key to be translated in the
|
||||
// context of the containing message bundle qualification
|
||||
if (compoundKey.indexOf(QUAL_PREFIX) == 0) {
|
||||
var qbundle :MessageBundle = _msgmgr.getBundle(
|
||||
getBundle(compoundKey));
|
||||
return qbundle.xlate(getUnqualifiedKey(compoundKey));
|
||||
}
|
||||
|
||||
// to be more efficient about creating unnecessary objects, we
|
||||
// do some checking before splitting
|
||||
var tidx :int = compoundKey.indexOf("|");
|
||||
if (tidx == -1) {
|
||||
return get(compoundKey);
|
||||
|
||||
} else {
|
||||
var key :String = compoundKey.substring(0, tidx);
|
||||
var argstr :String = compoundKey.substring(tidx+1);
|
||||
var args :Array = argstr.split("|");
|
||||
// unescape and translate the arguments
|
||||
for (var ii :int = 0; ii < args.length; ii++) {
|
||||
// if the argument is tainted, do no further translation
|
||||
// (it might contain |s or other fun stuff)
|
||||
if (args[ii].indexOf(TAINT_CHAR) == 0) {
|
||||
args[ii] = unescape(args[ii].substring(1));
|
||||
} else {
|
||||
args[ii] = xlate(unescape(args[ii]));
|
||||
}
|
||||
}
|
||||
return get(key, args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this to "taint" any string that has been entered by an entity
|
||||
* outside the application so that the translation code knows not to
|
||||
* attempt to translate this string when doing recursive translations
|
||||
* (see {@link #xlate}).
|
||||
*/
|
||||
public static function taint (text :Object) :String
|
||||
{
|
||||
return TAINT_CHAR + text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Composes a message key with an array of arguments. The message can
|
||||
* subsequently be translated in a single call using {@link #xlate}.
|
||||
*/
|
||||
public static function compose (key :String, ... args) :String
|
||||
{
|
||||
args = Util.unfuckVarargs(args);
|
||||
|
||||
var buf :StringBuilder = new StringBuilder();
|
||||
buf.append(key, "|");
|
||||
for (var ii :int = 0; ii < args.length; ii++) {
|
||||
if (ii > 0) {
|
||||
buf.append("|");
|
||||
}
|
||||
var arg :String = String(args[ii]);
|
||||
for (var p :int = 0; p < arg.length; p++) {
|
||||
var ch :String = arg.charAt(p);
|
||||
if (ch == "|") {
|
||||
buf.append("\\!");
|
||||
} else if (ch == "\\") {
|
||||
buf.append("\\\\");
|
||||
} else {
|
||||
buf.append(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method for calling {@link #compose(String,Object[])}
|
||||
* with a single argument that will be automatically tainted (see
|
||||
* {@link #taint}).
|
||||
*/
|
||||
public static function tcompose (key :String, ... args) :String
|
||||
{
|
||||
args = Util.unfuckVarargs(args);
|
||||
|
||||
for (var ii :int = 0; ii < args.length; ii++) {
|
||||
args[ii] = taint(args[ii]);
|
||||
}
|
||||
return compose(key, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a fully qualified message key which, when translated by
|
||||
* some other bundle, will know to resolve and utilize the supplied
|
||||
* bundle to translate this particular key.
|
||||
*/
|
||||
public static function qualify (bundle :String, key :String) :String
|
||||
{
|
||||
if (bundle.indexOf(QUAL_PREFIX) != -1 ||
|
||||
bundle.indexOf(QUAL_SEP) != -1) {
|
||||
throw new Error("Message bundle may not contain " + QUAL_PREFIX +
|
||||
" or " + QUAL_SEP);
|
||||
}
|
||||
|
||||
return QUAL_PREFIX + bundle + QUAL_SEP + key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bundle name from a fully qualified message key.
|
||||
*
|
||||
* @see #qualify
|
||||
*/
|
||||
public static function getBundle (qualifiedKey :String) :String
|
||||
{
|
||||
if (qualifiedKey.indexOf(QUAL_PREFIX) != 0) {
|
||||
throw new Error(qualifiedKey +
|
||||
" is not a fully qualified message key.");
|
||||
}
|
||||
var qsidx :int = qualifiedKey.indexOf(QUAL_SEP);
|
||||
if (qsidx == -1) {
|
||||
throw new Error(qualifiedKey +
|
||||
" is not a valid fully qualified key.");
|
||||
}
|
||||
|
||||
return qualifiedKey.substring(QUAL_PREFIX.length, qsidx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unqualified portion of the key from a fully qualified
|
||||
* message key.
|
||||
*
|
||||
* @see #qualify
|
||||
*/
|
||||
public static function getUnqualifiedKey (qualifiedKey :String) :String
|
||||
{
|
||||
if (qualifiedKey.indexOf(QUAL_PREFIX) != 0) {
|
||||
throw new Error(qualifiedKey +
|
||||
" is not a fully qualified message key.");
|
||||
}
|
||||
var qsidx :int = qualifiedKey.indexOf(QUAL_SEP);
|
||||
if (qsidx == -1) {
|
||||
throw new Error(qualifiedKey +
|
||||
" is not a fully qualified message key.");
|
||||
}
|
||||
return qualifiedKey.substring(qsidx + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unescapes characters that are escaped in a call to compose.
|
||||
*/
|
||||
public static function unescape (val :String) :String
|
||||
{
|
||||
var bsidx :int = val.indexOf("\\");
|
||||
if (bsidx == -1) {
|
||||
return val;
|
||||
}
|
||||
|
||||
var buf :StringBuilder = new StringBuilder();
|
||||
for (var ii :int = 0; ii < val.length; ii++) {
|
||||
var ch :String = val.charAt(ii);
|
||||
if (ch != "\\" || ii == val.length-1) {
|
||||
buf.append(ch);
|
||||
} else {
|
||||
// look at the next character
|
||||
ch = val.charAt(++ii);
|
||||
buf.append((ch == "!") ? "|" : ch);
|
||||
}
|
||||
}
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/** The message manager via whom we'll resolve fully qualified
|
||||
* translation strings. */
|
||||
protected var _msgmgr :MessageManager;
|
||||
|
||||
/** The path that identifies the resource bundle we are using to
|
||||
* obtain our messages. */
|
||||
protected var _path :String;
|
||||
|
||||
/** Our parent bundle if we're not the global bundle. */
|
||||
protected var _parent :MessageBundle;
|
||||
|
||||
protected static const TAINT_CHAR :String = "~";
|
||||
protected static const QUAL_PREFIX :String = "%";
|
||||
protected static const QUAL_SEP :String = ":";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Flash Utils library - general purpose ActionScript utility code
|
||||
// Copyright (C) 2007-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/ooolib/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
import mx.resources.IResourceBundle;
|
||||
import mx.resources.Locale;
|
||||
import mx.resources.ResourceManager;
|
||||
|
||||
/**
|
||||
* The message manager provides a thin wrapper around Java's built-in
|
||||
* localization support, supporting a policy of dividing up localization
|
||||
* resources into logical units, all of the translations for which are
|
||||
* contained in a single messages file.
|
||||
*
|
||||
* <p> The message manager assumes that the locale remains constant for
|
||||
* the duration of its operation. If the locale were to change during the
|
||||
* operation of the client, a call to {@link #setLocale} should be made to
|
||||
* inform the message manager of the new locale (which will clear the
|
||||
* message bundle cache).
|
||||
*/
|
||||
public class MessageManager
|
||||
{
|
||||
/** The name of the global resource bundle (which other bundles revert
|
||||
* to if they can't locate a message within themselves). It must be
|
||||
* named <code>global.properties</code> and live at the top of the
|
||||
* bundle hierarchy. */
|
||||
public static const GLOBAL_BUNDLE :String = "global";
|
||||
|
||||
/**
|
||||
* Constructs a message manager with the supplied resource prefix and
|
||||
* the default locale. The prefix will be prepended to the path of all
|
||||
* resource bundles prior to their resolution. For example, if a
|
||||
* prefix of <code>rsrc.messages</code> was provided and a message
|
||||
* bundle with the name <code>game.chess</code> was later requested,
|
||||
* the message manager would attempt to load a resource bundle with
|
||||
* the path <code>rsrc.messages.game.chess</code> and would eventually
|
||||
* search for a file in the classpath with the path
|
||||
* <code>rsrc/messages/game/chess.properties</code>.
|
||||
*
|
||||
* <p> See the documentation for {@link
|
||||
* ResourceBundle#getBundle(String,Locale,ClassLoader)} for a more
|
||||
* detailed explanation of how resource bundle paths are resolved.
|
||||
*/
|
||||
public function MessageManager ()
|
||||
{
|
||||
// load up the global bundle
|
||||
_global = getBundle(GLOBAL_BUNDLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the message bundle for the specified path. If no bundle can
|
||||
* be located with the specified path, a special bundle is returned
|
||||
* that returns the untranslated message identifiers instead of an
|
||||
* associated translation. This is done so that error code to handle a
|
||||
* failed bundle load need not be replicated wherever bundles are
|
||||
* used. Instead an error will be logged and the requesting service
|
||||
* can continue to function in an impaired state.
|
||||
*/
|
||||
public function getBundle (path :String) :MessageBundle
|
||||
{
|
||||
// first look in the cache
|
||||
var bundle :MessageBundle = (_cache.get(path) as MessageBundle);
|
||||
if (bundle != null) {
|
||||
return bundle;
|
||||
}
|
||||
|
||||
// see if we should use a custom class for the bundle
|
||||
var mbclass :String = ResourceManager.getInstance().getString(path, MBUNDLE_CLASS_KEY);
|
||||
if (mbclass != null) {
|
||||
try {
|
||||
var clazz :Class = ClassUtil.getClassByName(String(mbclass));
|
||||
bundle = new clazz();
|
||||
} catch (ee :Error) {
|
||||
Log.getLog(this).warning("Failure instantiating custom message bundle",
|
||||
"mbclass", mbclass, ee);
|
||||
}
|
||||
}
|
||||
// if there was no custom class, or we failed to instantiate the
|
||||
// custom class, use a standard message bundle
|
||||
if (bundle == null) {
|
||||
bundle = new MessageBundle();
|
||||
}
|
||||
|
||||
// initialize our message bundle, cache it and return it (if we
|
||||
// couldn't resolve the bundle, the message bundle will cope with
|
||||
// it's null resource bundle)
|
||||
bundle.init(this, path, _global);
|
||||
_cache.put(path, bundle);
|
||||
return bundle;
|
||||
}
|
||||
|
||||
/** The locale for which we're obtaining message bundles. */
|
||||
protected var _locale :String;
|
||||
|
||||
/** A cache of instantiated message bundles. */
|
||||
protected var _cache :Map = Maps.newMapOf(String);
|
||||
|
||||
/** Our top-level message bundle, from which others obtain messages if
|
||||
* they can't find them within themselves. */
|
||||
protected var _global :MessageBundle;
|
||||
|
||||
/** A key that can contain the classname of a custom message bundle
|
||||
* class to be used to handle messages for a particular bundle. */
|
||||
protected static const MBUNDLE_CLASS_KEY :String = "msgbundle_class";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Flash Utils library - general purpose ActionScript utility code
|
||||
// Copyright (C) 2007-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/ooolib/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
/**
|
||||
* Utility for times.
|
||||
*/
|
||||
public class TimeUtil
|
||||
{
|
||||
/** Time unit constant. */
|
||||
public static const MILLISECOND :int = 0;
|
||||
|
||||
/** Time unit constant. */
|
||||
public static const SECOND :int = 1;
|
||||
|
||||
/** Time unit constant. */
|
||||
public static const MINUTE :int = 2;
|
||||
|
||||
/** Time unit constant. */
|
||||
public static const HOUR :int = 3;
|
||||
|
||||
/** Time unit constant. */
|
||||
public static const DAY :int = 4;
|
||||
|
||||
// TODO: Weeks?, months?
|
||||
protected static const MAX_UNIT :int = DAY;
|
||||
|
||||
/**
|
||||
* Get a translatable string specifying the magnitude of the specified
|
||||
* duration. Results will be between "1 second" and "X hours", with
|
||||
* all times rounded to the nearest unit. "0 units" will never be
|
||||
* displayed, the minimum is 1.
|
||||
*/
|
||||
public static function getTimeOrderString (
|
||||
duration :Number, minUnit :int, maxUnit :int = -1) :String
|
||||
{
|
||||
// enforce sanity
|
||||
if (maxUnit == -1) {
|
||||
maxUnit = MAX_UNIT;
|
||||
}
|
||||
minUnit = Math.min(minUnit, maxUnit);
|
||||
maxUnit = Math.min(maxUnit, MAX_UNIT);
|
||||
|
||||
for (var uu :int = MILLISECOND; uu <= MAX_UNIT; uu++) {
|
||||
var quantity :int = getQuantityPerUnit(uu);
|
||||
if ((minUnit <= uu) && (duration < quantity || maxUnit == uu)) {
|
||||
duration = Math.max(1, duration);
|
||||
return MessageBundle.tcompose(getTransKey(uu),
|
||||
String(duration));
|
||||
}
|
||||
duration = Math.round(duration / quantity);
|
||||
}
|
||||
|
||||
// will not happen, because eventually uu will be MAX_UNIT
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a translatable string specifying the duration, down to the
|
||||
* minimum granularity.
|
||||
*/
|
||||
public static function getTimeString (duration :Number, minUnit :int) :String
|
||||
{
|
||||
// sanity
|
||||
minUnit = Math.min(minUnit, MAX_UNIT);
|
||||
duration = Math.abs(duration);
|
||||
|
||||
var list :Array = new Array();
|
||||
var parts :int = 0; // how many parts are in the translation string?
|
||||
for (var uu :int = MILLISECOND; uu <= MAX_UNIT; uu++) {
|
||||
var quantity :int = getQuantityPerUnit(uu);
|
||||
if (minUnit <= uu) {
|
||||
list.push(MessageBundle.tcompose(getTransKey(uu), String(duration % quantity)));
|
||||
parts++;
|
||||
}
|
||||
duration /= quantity;
|
||||
if (duration <= 0 && parts > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (parts == 1) {
|
||||
return (list[0] as String);
|
||||
} else {
|
||||
return MessageBundle.compose("m.times_" + parts, list);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to get the quantity for the specified unit.
|
||||
* (Not very OO)
|
||||
*/
|
||||
protected static function getQuantityPerUnit (unit :int) :int
|
||||
{
|
||||
switch (unit) {
|
||||
case MILLISECOND: return 1000;
|
||||
case SECOND: case MINUTE: return 60;
|
||||
case HOUR: return 24;
|
||||
case DAY: return int.MAX_VALUE;
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to get the translation key for the specified unit.
|
||||
* (Not very OO)
|
||||
*/
|
||||
protected static function getTransKey (unit :int) :String
|
||||
{
|
||||
switch (unit) {
|
||||
case MILLISECOND: return "m.millisecond";
|
||||
case SECOND: return "m.second";
|
||||
case MINUTE: return "m.minute";
|
||||
case HOUR: return "m.hour";
|
||||
case DAY: return "m.day";
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user