androidlocalization

How to create a localized time ago String on Android


I was reviewing the Google I/O Session 2012 app and came across this TODO

public static String getTimeAgo(long time, Context ctx) {
    if (time < 1000000000000L) {
        // if timestamp given in seconds, convert to millis
        time *= 1000;
    }

    long now = getCurrentTime(ctx);
    if (time > now || time <= 0) {
        return null;
    }

    // TODO: localize
    final long diff = now - time;
    if (diff < MINUTE_MILLIS) {
        return "just now";
    } else if (diff < 2 * MINUTE_MILLIS) {
        return "a minute ago";
    } else if (diff < 50 * MINUTE_MILLIS) {
        return diff / MINUTE_MILLIS + " minutes ago";
    } else if (diff < 90 * MINUTE_MILLIS) {
        return "an hour ago";
    } else if (diff < 24 * HOUR_MILLIS) {
        return diff / HOUR_MILLIS + " hours ago";
    } else if (diff < 48 * HOUR_MILLIS) {
        return "yesterday";
    } else {
        return diff / DAY_MILLIS + " days ago";
    }
}

Which made me wonder what would be the steps to localizing it.


Solution

  • From Ian Lake's answer:

    final CharSequence relativeTimeSpan = DateUtils.getRelativeTimeSpanString(time, now, 0);
    

    DateUtils.getRelativeTimeSpanString produces things such as the following:

    Alternatively, this would allow translations to be provided through the Android res framework using the given Context, useful if your desired messaging difference from what you can get out of the framework method.

    public static String getTimeAgo(long time, Context context) {
        if (time < 1000000000000L)
            // if timestamp given in seconds, convert to millis
            time *= 1000;
    
        final long now = getCurrentTime(context);
        if (time > now || time <= 0) return "";
    
    
        final Resources res = context.getResources();
        final long time_difference = now - time;
        if (time_difference < _A_MINUTE)
            return res.getString(R.string.just_now);
        else if (time_difference < 60 * _A_MINUTE)
            return res.getString(R.string.time_ago,
                                 res.getQuantityString(R.plurals.minutes, (int) time_difference / _A_MINUTE, time_difference / _A_MINUTE));
        else if (time_difference < 24 * _AN_HOUR)
            return res.getString(R.string.time_ago,
                                 res.getQuantityString(R.plurals.hours, (int) time_difference / _AN_HOUR, time_difference / _AN_HOUR));
        else if (time_difference < 48 * _AN_HOUR)
            return res.getString(R.string.yesterday);
        else
            return res.getString(R.string.time_ago,
                                 res.getQuantityString(R.plurals.days, (int) time_difference / _A_DAY, time_difference / _A_DAY));
    }
    

    where I defined the constants as:

    /** One second (in milliseconds) */
    private static final int _A_SECOND = 1000;
    /** One minute (in milliseconds) */
    private static final int _A_MINUTE = 60 * _A_SECOND;
    /** One hour (in milliseconds) */
    private static final int _AN_HOUR = 60 * _A_MINUTE;
    /** One day (in milliseconds) */
    private static final int _A_DAY = 24 * _AN_HOUR;
    

    Then the rest of the work is in the structure of the plurals and string resources.

    My default local is en, so res/values/strings.xml:

    <!-- time ago strings -->
    <string name="just_now">just now</string>
    <string name="time_ago">%s ago</string>
    <string name="yesterday">yesterday</string>
    

    Then res/values/plurals.xml:

    <plurals name="minutes">
        <item quantity="one">a minute</item>
        <item quantity="other">%d minutes</item>
    </plurals>
    
    <plurals name="hours">
        <item quantity="one">an hour</item>
        <item quantity="other">%d hours</item>
    </plurals>
    
    <plurals name="days">
        <item quantity="one">a day</item>
        <item quantity="other">%d days</item>
    </plurals>
    

    This approach should allow you to localize on the vocabulary and the grammar of the language, as different languages not only have different words for words like "minute", but they also have different rules about how to pluralize words based on the quantity. So the plurals resource utilizes the Android frameworks support of localizing on pluralizations. All that is left is to provide the translations.