Play Framework: локализация Date.since()

Play framework позволяет в шаблонах groovy использовать extension functions - т.е прицеплять методы на ходу к объектам.

В частности в play имеет расширение экземпляров класса Date - since(), который возвращает сколько времени прошло от указанной даты. Например "13 minutes ago"

С since() все хорошо, пока не требуется интернационализация. Если переопределить соответствующие ключи в файлах перевода, то для русского он возвращает нечто вроде 3 дняs назад. Другими словами этот метод не может отображать количество пройденного времени на любом языке, кроме английского.

Метод since() описан в классе JavaExtentions, как и многое в play он статический, поэтому перегрузить его не получиться. Остаётся только описать свою extension function, которая будет честно переводить на язык пользователя "13 minutes ago" -> "13 минут назад". Для этого нужно создать класс ext.UponExtensions


package ext;

import java.util.Date;

import play.i18n.Lang;
import play.i18n.Messages;
import play.templates.JavaExtensions;

public class UponExtensions extends JavaExtensions {
public static String upon(Date date) {
return upon(date, false);
}

/**
* Return localized since
* @param date Date to format
* @param stopAtMonth show real date if delta month
*/
public static String upon(Date date, Boolean stopAtMonth) {
if (Lang.get().equals("ru")) return russianUpon(date, stopAtMonth);
return since(date, stopAtMonth);
}

/**
* Only for Russian
*/
private static String russianUpon (Date date, Boolean stopAtMonth) {
Date now = new Date();
long d = (now.getTime() - date.getTime()) / 1000;

if (stopAtMonth && d > 30 * 24 * 60 * 60) {
return asdate(date.getTime(), Messages.get("since.format"));
}

String words[] = null;

if (d < 60) {
words = PLURAL_RU_SECONDS;
} else if (d < 60 * 60) {
d = d / 60;
words = PLURAL_RU_MINUTES;
} else if (d < 24 * 60 * 60) {
d = d / (60 * 60);
words = PLURAL_RU_HOURS;
} else if (d < 30 * 24 * 60 * 60) {
d = d / (24 * 60 * 60);
words = PLURAL_RU_DAYS;
} else if (d < 365 * 24 * 60 * 60) {
d = d / (30 * 24 * 60 * 60);
words = PLURAL_RU_MONTH;
} else {
d = d / (365 * 24 * 60 * 60);
words = PLURAL_RU_YEARS;
}
String word = words[d%10==1 && d%100!=11 ? 0 : (d%10>=2 && d%10<=4 && (d%100<10 || d%100>=20) ? 1 : 2)];

return "" + d + " " + word + " назад";
}

private static final String PLURAL_RU_SECONDS[] = new String[] {"секунда", "секунды", "секунд"};
private static final String PLURAL_RU_MINUTES[] = new String[] {"минута", "минуты", "минут"};;
private static final String PLURAL_RU_HOURS[] = new String[] {"час", "часа", "часов"};
private static final String PLURAL_RU_DAYS[] = new String[] {"день", "дня", "дней"};
private static final String PLURAL_RU_MONTH[] = new String[] {"месяц", "месяца", "месяцев"};
private static final String PLURAL_RU_YEARS[] = new String[] {"год", "года", "лет"};
}

По аналогии можно создавать переводы и для других языков.

Для использования нужно везде в шаблонах заменить since() на upon():


&{'label.lastSynchronization'} ${new Date(lastSync).upon()}

Получим

Последняя синхронизация 19 минут назад

Last synchronized 19 minutes ago

Ещё примеры

3 sec: 3 seconds ago | 3 секунды назад
33 sec: 33 seconds ago | 33 секунды назад
2 min: 2 minutes ago | 2 минуты назад
21 min: 21 minutes ago | 21 минута назад
1 min: 1 minute ago | 1 минута назад
55 min: 55 minutes ago | 55 минут назад
4 h: 4 hours ago | 4 часа назад
15 h: 15 hours ago | 15 часов назад
22 h: 22 hours ago | 22 часа назад
1 d: 1 day ago | 1 день назад
11 d: 11 days ago | 11 дней назад
20 d: 20 days ago | 20 дней назад
53 d: 1 month ago | 1 месяц назад
1 mon: 1 month ago | 1 месяц назад
2 mon: 2 months ago | 2 месяца назад
5 mon: 5 months ago | 5 месяцев назад
1 y: 1 year ago | 1 год назад
2 y: 2 years ago | 2 года назад
31 y: 31 years ago | 31 год назад
30 y: 30 years ago | 30 лет назад
5 y: 5 years ago | 5 лет назад

Пытался заменить тело метода JavaExtentions/since() с помощью cglib(который очень активно используется play framework), но так и не получилось. Кто нибудь умеет?