Archive for May, 2011

Facebook Desktop – We’re international, baby!

Some of you might remember a side-project of mine that I announced here which I called Facebook Desktop.  In short, it was a small application that I initially wrote for myself to help me stay in touch with my friends and family through Facebook.  It would sit in my system tray or dock and quietly push messenger-style toast notifications to my screen whenever something happened in my Facebook stream.  Neat, huh?  I thought so.  So, I built it out and eventually released it as a free app for everyone to use!  Since then, it’s actually picked up quite a bit of steam and now enjoys thousands of users from all over the world!

Pleasantly Surprised
I’ve had a couple of releases since I first announced it, but this latest release is special and so I wanted to mention it here.  The popularity of the app, especially overseas, took me by surprise.  Considering this started out as a small utility I built for myself over a weekend, I didn’t anticipate it being used overseas.  But now, it’s actually used in over 20 countries all around the globe!  In fact, the bulk of our users are overseas!  After noticing this, I made the decision to devote the entire next release into adding language support for all of our international users.

Technically, localizing the application wasn’t a problem (I’ve had some experience in the area).  The bigger problem was actually getting all of the text I use in the app translated to all of the various languages I wanted to support.  Then, someone posted a beautiful message on our Facebook application page.

“Are you adding support for German?  If so, I volunteer to translate!”

Shortly after, a comment: “Me too, for Italian!”.  And another: “I can do Hindi”.  That’s when it hit me…crowdsourcing!

The Power of the Community
I’ve never done it before, and there are plenty of ways that it can blow up in my face, but it was obvious to me that our users wanted it, but more importantly, were willing to help make it happen!  So, I took advantage of it!  The next day, I posted a message to our page asking if there is anyone willing to help translate Facebook Desktop in their native language.  The response was amazing!  At the end of the day, we had over 30 responses with people volunteering to translate in over a dozen languages!

I couldn’t ignore this, and so I took everyone up on their offer and started getting all of the text translated.  A couple of months later, a bit of coding, and a lot testing, it’s released!  Facebook Desktop v0.84 went live last night at midnight with support for a whopping SIXTEEN languages.

Together is Better
This version of Facebook Desktop could not have been accomplished without the help of the community.  Plain and simple.  On top of that, engaging with the users in this way made this release that much more fun too!  In the future, I’ll definitely be looking to involve our users more.  It’s better for them, it’s better for me, and it’s better for the app!  To infinity, and beyond!

 

Charles

Relative Time Library for ActionScript

While working on a side-project of mine, I came across the need to display dates and times as relative instead of absolute. You know, saying “two hours ago” instead of “Mon Jan 5 17:24:33 GMT-0700 2011″. Since the web is going real-time these days, it just makes sense. Besides, everyone’s doing it, so it must be good! Anyways, I wrote a library (really, just a simple class) that you can use to do this with ActionScript. It’s really quite simple, but since I couldn’t find it anywhere else, I thought I’d contribute it here for everyone to use too. Here it is in action…

 

If you’re wondering, I’ve tried to follow the convention that Facebook uses for what format to display and when. Twitter has a very similar format, but I decided to go with Facebook’s. Also, if you notice, my library can handle times in the future as well as times in the past. Here’s how to use it…

Usage:

Once you’ve added the library to your project, it’s really quite easy to use. Just call any of the two available APIs and pass in a date or two and you’ll get back the relative time string. Done!

var relativeTime:String = RelativeDate.getRelativeDate(firstDate, secondDate);
var relativeTime:String = RelativeDate.getRelativeDateFromNow(someDate);

Code:
Finally, here is the code in all it’s glory!

package com.adobe.date
{
	import com.adobe.utils.DateUtil;
 
	/**
	 * Utility class to help create human-readable Strings representing
	 * the difference in time from two different dates (e.g. "just now"
	 * or "2 hours ago" or "13 minutes from now").
	 *
	 * @langversion ActionScript 3.0
	 * @playerversion Flash 10.0
	 */
	public class RelativeDate
	{
		private static const SECONDS_PER_MINUTE:uint = 60;
		private static const SECONDS_PER_TWO_MINUTES:uint = 120;
		private static const SECONDS_PER_HOUR:uint = 3600;
		private static const SECONDS_PER_TWO_HOURS:uint = 7200;
		private static const SECONDS_PER_DAY:uint = 86400;
		private static const SECONDS_PER_TWO_DAYS:uint = 172800;
		private static const SECONDS_PER_THREE_DAYS:uint = 259200;
 
		/**
		 * Creates a human-readable String representing the difference
		 * in time from the date provided and now.  This method handles
		 * dates in both the past and the future (e.g. "2 hours ago"
		 * and "2 hours from now".  For any date beyond 3 days difference
		 * from now, then a standard format is returned.
		 *
		 * @param date The date for which to compare against.
		 *
		 * @return Human-readable String representing the time elapsed.
		 */
		public static function getRelativeDateFromNow(date:Date, capitalizeFirstLetter:Boolean = false):String
		{
			return getRelativeDate(date, new Date(), capitalizeFirstLetter);
		}
 
		/**
		 * Creates a human-readable String representing the difference
		 * in time from the first date provided with respect to the
		 * second date provided.  If no second date is provided, then
		 * the relative date will be calcluated with respect to "now".
		 * This method handles dates in both the past and the
		 * future (e.g. "2 hours ago" and "2 hours from now".  For
		 * any date beyond 3 days difference from now, then a
		 * standard format is returned.
		 *
		 * @param firstDate The date for which to compare against.
		 * @param secondDate The date to use as "present" when comparing against firstDate.
		 *
		 * @return Human-readable String representing the time elapsed.
		 */
		public static function getRelativeDate(firstDate:Date, secondDate:Date = null, capitalizeFirstLetter:Boolean = false):String
		{
			var relativeDate:String;
			var isFuture:Boolean = false;
 
			if (secondDate == null)
			{
				secondDate = new Date();
			}
 
			// the difference between the passed-in date and now, in seconds
			var secondsElapsed:Number = (secondDate.getTime() - firstDate.getTime()) / 1000;
 
			if (secondsElapsed < 0)
			{
				isFuture = true;
				secondsElapsed = Math.abs(secondsElapsed);
			}
 
			switch(true)
			{
				case secondsElapsed < SECONDS_PER_MINUTE:
					relativeDate = "just now";
					break;
				case secondsElapsed < SECONDS_PER_TWO_MINUTES:
					relativeDate = "1 minute " + ((isFuture) ? "from now" : "ago");
					break;
				case secondsElapsed < SECONDS_PER_HOUR:
					relativeDate = int(secondsElapsed / SECONDS_PER_MINUTE) + " minutes " + ((isFuture) ? "from now" : "ago");
					break;
				case secondsElapsed < SECONDS_PER_TWO_HOURS:
					relativeDate = "about an hour " + ((isFuture) ? "from now" : "ago");
					break;
				case secondsElapsed < SECONDS_PER_DAY:
					relativeDate = int(secondsElapsed / SECONDS_PER_HOUR) + " hours " + ((isFuture) ? "from now" : "ago");
					break;
				case secondsElapsed < SECONDS_PER_TWO_DAYS:
					relativeDate = ((isFuture) ? "tomorrow" : "yesterday") + " at " + DateUtil.getShortHour(firstDate) + ":" + getMinutesString(firstDate) + DateUtil.getAMPM(firstDate).toLowerCase();
					break;
				case secondsElapsed < SECONDS_PER_THREE_DAYS:
					relativeDate = DateUtil.getFullDayName(firstDate) + " at " + DateUtil.getShortHour(firstDate) + ":" + getMinutesString(firstDate) + DateUtil.getAMPM(firstDate).toLowerCase();
					break;
				default:
					relativeDate = DateUtil.getFullMonthName(firstDate) + " " + firstDate.getDate() + " at " + DateUtil.getShortHour(firstDate) + ":" + getMinutesString(firstDate) + DateUtil.getAMPM(firstDate).toLowerCase()
					break;
			}
 
			return ((capitalizeFirstLetter) ? relativeDate.substring(0, 1).toUpperCase() + relativeDate.substring(1, relativeDate.length) : relativeDate);
		}
 
		/**
		 * @private
		 */
		private static function getMinutesString(date:Date):String
		{
			return ((date.minutes < 10) ? "0" : "") + date.minutes;
		}
	}
}

Hopefully, this can help someone out there looking to do the same! Also, see the code in my GitHub repo!

Until next time, happy coding!

Charles

*Note: Shout-out to Joel Hooks for creating and sharing his awesome date-picker component!