From 9adf6283b75a187b96796b892fd128e300f429a4 Mon Sep 17 00:00:00 2001
From: James Moger <james.moger@gitblit.com>
Date: Fri, 15 Jun 2012 16:01:30 -0400
Subject: [PATCH] Revised TimeUtils for localization

---
 src/com/gitblit/wicket/pages/LuceneSearchPage.java              |    2 
 src/com/gitblit/utils/TimeUtils.java                            |   85 +++++++++++++----
 src/com/gitblit/client/DateCellRenderer.java                    |    4 
 src/com/gitblit/wicket/panels/RepositoriesPanel.java            |    5 
 tests/com/gitblit/tests/TimeUtilsTest.java                      |   74 +++++++-------
 src/com/gitblit/wicket/pages/ReviewProposalPage.java            |    2 
 src/com/gitblit/wicket/panels/FederationProposalsPanel.java     |    2 
 src/com/gitblit/wicket/pages/TicketsPage.java                   |    2 
 src/com/gitblit/wicket/panels/TagsPanel.java                    |    2 
 src/com/gitblit/wicket/WicketUtils.java                         |   22 ++--
 src/com/gitblit/wicket/pages/CommitPage.java                    |    6 
 src/com/gitblit/wicket/pages/FederationRegistrationPage.java    |    4 
 src/com/gitblit/wicket/panels/FederationRegistrationsPanel.java |    4 
 src/com/gitblit/wicket/pages/MetricsPage.java                   |    3 
 src/com/gitblit/wicket/pages/TicketPage.java                    |    4 
 src/com/gitblit/wicket/panels/BasePanel.java                    |   17 +++
 src/com/gitblit/wicket/panels/HistoryPanel.java                 |    2 
 src/com/gitblit/wicket/panels/ActivityPanel.java                |    4 
 src/com/gitblit/client/Translation.java                         |   10 ++
 src/com/gitblit/wicket/pages/BasePage.java                      |   21 ++++
 src/com/gitblit/wicket/panels/SearchPanel.java                  |    2 
 src/com/gitblit/wicket/panels/BranchesPanel.java                |    2 
 src/com/gitblit/client/StatusPanel.java                         |    5 
 src/com/gitblit/wicket/panels/CommitHeaderPanel.java            |    2 
 src/com/gitblit/wicket/panels/LogPanel.java                     |    2 
 src/com/gitblit/models/IssueModel.java                          |    4 
 src/com/gitblit/wicket/pages/SummaryPage.java                   |    5 
 src/com/gitblit/wicket/pages/TagPage.java                       |    2 
 28 files changed, 195 insertions(+), 104 deletions(-)

diff --git a/src/com/gitblit/client/DateCellRenderer.java b/src/com/gitblit/client/DateCellRenderer.java
index 3d0ab15..954dad2 100644
--- a/src/com/gitblit/client/DateCellRenderer.java
+++ b/src/com/gitblit/client/DateCellRenderer.java
@@ -37,7 +37,7 @@
 	private static final long serialVersionUID = 1L;
 
 	private final String pattern;
-
+	
 	public DateCellRenderer(String pattern, Color foreground) {
 		this.pattern = (pattern == null ? "yyyy-MM-dd HH:mm" : pattern);
 		setForeground(foreground);
@@ -55,7 +55,7 @@
 				title = "--";
 				dateString = "never";
 			} else {
-				title = TimeUtils.timeAgo(date);
+				title = Translation.getTimeUtils().timeAgo(date);
 				dateString = new SimpleDateFormat(pattern).format((Date) value);
 			}
 
diff --git a/src/com/gitblit/client/StatusPanel.java b/src/com/gitblit/client/StatusPanel.java
index f22d69c..7506dfe 100644
--- a/src/com/gitblit/client/StatusPanel.java
+++ b/src/com/gitblit/client/StatusPanel.java
@@ -35,7 +35,6 @@
 import com.gitblit.Constants.RpcRequest;
 import com.gitblit.models.ServerStatus;
 import com.gitblit.utils.ByteFormat;
-import com.gitblit.utils.TimeUtils;
 
 /**
  * This panel displays the server status.
@@ -155,8 +154,8 @@
 		ServerStatus status = gitblit.getStatus();
 		header.setText(Translation.get("gb.status"));
 		version.setText(Constants.NAME + (status.isGO ? " GO v" : " WAR v") + status.version);
-		releaseDate.setText(status.releaseDate);
-		bootDate.setText(status.bootDate.toString() + " (" + TimeUtils.timeAgo(status.bootDate)
+		releaseDate.setText(status.releaseDate);		
+		bootDate.setText(status.bootDate.toString() + " (" + Translation.getTimeUtils().timeAgo(status.bootDate)
 				+ ")");
 		url.setText(gitblit.url);
 		servletContainer.setText(status.servletContainer);
diff --git a/src/com/gitblit/client/Translation.java b/src/com/gitblit/client/Translation.java
index 2e7b5bb..16ef20d 100644
--- a/src/com/gitblit/client/Translation.java
+++ b/src/com/gitblit/client/Translation.java
@@ -18,6 +18,8 @@
 import java.util.MissingResourceException;
 import java.util.ResourceBundle;
 
+import com.gitblit.utils.TimeUtils;
+
 /**
  * Loads the Gitblit language resource file.
  * 
@@ -27,6 +29,8 @@
 public class Translation {
 
 	private final static ResourceBundle translation;
+	
+	private final static TimeUtils timeUtils;
 
 	static {
 		ResourceBundle bundle;
@@ -38,6 +42,8 @@
 			bundle = ResourceBundle.getBundle("GitBlitWebApp");
 		}
 		translation = bundle;
+		
+		timeUtils = new TimeUtils(translation);
 	}
 
 	public static String get(String key) {
@@ -46,4 +52,8 @@
 		}
 		return key;
 	}
+	
+	public static TimeUtils getTimeUtils() {
+		return timeUtils;
+	}
 }
diff --git a/src/com/gitblit/models/IssueModel.java b/src/com/gitblit/models/IssueModel.java
index 3c191e2..c903891 100644
--- a/src/com/gitblit/models/IssueModel.java
+++ b/src/com/gitblit/models/IssueModel.java
@@ -297,8 +297,8 @@
 
 		@Override
 		public String toString() {
-			StringBuilder sb = new StringBuilder();
-			sb.append(TimeUtils.timeAgo(created));
+			StringBuilder sb = new StringBuilder();			
+			sb.append(new TimeUtils().timeAgo(created));
 			switch (code) {
 			case '+':
 				sb.append(" created by ");
diff --git a/src/com/gitblit/utils/TimeUtils.java b/src/com/gitblit/utils/TimeUtils.java
index 662025b..7f69562 100644
--- a/src/com/gitblit/utils/TimeUtils.java
+++ b/src/com/gitblit/utils/TimeUtils.java
@@ -15,9 +15,11 @@
  */
 package com.gitblit.utils;
 
+import java.text.MessageFormat;
 import java.text.SimpleDateFormat;
 import java.util.Calendar;
 import java.util.Date;
+import java.util.ResourceBundle;
 
 /**
  * Utility class of time functions.
@@ -35,6 +37,16 @@
 	public static final long ONEDAY = ONEHOUR * 24L;
 
 	public static final long ONEYEAR = ONEDAY * 365L;
+	
+	private final ResourceBundle translation;
+	
+	public TimeUtils() {
+		this(null);
+	}
+	
+	public TimeUtils(ResourceBundle translation) {
+		this.translation = translation;
+	}
 
 	/**
 	 * Returns true if date is today.
@@ -67,21 +79,21 @@
 	 * @param days
 	 * @return duration as string in days, months, and/or years
 	 */
-	public static String duration(int days) {
+	public String duration(int days) {
 		if (days <= 60) {
-			return days + (days > 1 ? " days" : " day");
+			return (days > 1 ? translate(days, "gb.duration.days", "{0} days") : translate("gb.duration.oneDay", "1 day"));
 		} else if (days < 365) {
 			int rem = days % 30;
-			return ((days / 30) + (rem >= 15 ? 1 : 0)) + " months";
+			return translate(((days / 30) + (rem >= 15 ? 1 : 0)), "gb.duration.months", "{0} months");
 		} else {
 			int years = days / 365;
 			int rem = days % 365;
-			String yearsString = years + (years > 1 ? " years" : " year");
+			String yearsString = (years > 1 ? translate(years, "gb.duration.years", "{0} years") : translate("gb.duration.oneYear", "1 year"));
 			if (rem < 30) {
 				if (rem == 0) {
 					return yearsString;
 				} else {
-					return yearsString + (rem >= 15 ? ", 1 month" : "");
+					return yearsString + (rem >= 15 ? (", " + translate("gb.duration.oneMonth", "1 month")): "");
 				}
 			} else {
 				int months = rem / 30;
@@ -89,8 +101,8 @@
 				if (remDays >= 15) {
 					months++;
 				}
-				String monthsString = yearsString + ", " + months
-						+ (months > 1 ? " months" : " month");
+				String monthsString = yearsString + ", "
+						+ (months > 1 ? translate(months, "gb.duration.months", "{0} months") : translate("gb.duration.oneMonth", "1 month"));
 				return monthsString;
 			}
 		}
@@ -155,6 +167,14 @@
 		return days;
 	}
 
+	public String today() {
+		return translate("gb.time.today", "today");
+	}
+
+	public String yesterday() {
+		return translate("gb.time.yesterday", "yesterday");
+	}
+
 	/**
 	 * Returns the string representation of the duration between now and the
 	 * date.
@@ -162,7 +182,7 @@
 	 * @param date
 	 * @return duration as a string
 	 */
-	public static String timeAgo(Date date) {
+	public String timeAgo(Date date) {
 		return timeAgo(date, false);
 	}
 
@@ -172,7 +192,7 @@
 	 * @param date
 	 * @return the css class
 	 */
-	public static String timeAgoCss(Date date) {
+	public String timeAgoCss(Date date) {
 		return timeAgo(date, true);
 	}
 
@@ -184,7 +204,7 @@
 	 * @param css
 	 * @return the string representation of the duration OR the css class
 	 */
-	private static String timeAgo(Date date, boolean css) {
+	private String timeAgo(Date date, boolean css) {
 		if (isToday(date) || isYesterday(date)) {
 			int mins = minutesAgo(date, true);
 			if (mins >= 120) {
@@ -193,15 +213,18 @@
 				}
 				int hours = hoursAgo(date, true);
 				if (hours > 23) {
-					return "yesterday";
+					return yesterday();
 				} else {
-					return hours + " hours ago";
+					return translate(hours, "gb.time.hoursAgo", "{0} hours ago");
 				}
 			}
 			if (css) {
 				return "age0";
 			}
-			return mins + " min" + (mins > 1 ? "s" : "") + " ago";
+			if (mins > 2) {
+				return translate(mins, "gb.time.minsAgo", "{0} mins ago");
+			}
+			return translate("gb.time.justNow", "just now");
 		} else {
 			int days = daysAgo(date);
 			if (css) {
@@ -215,13 +238,13 @@
 			}
 			if (days < 365) {
 				if (days <= 30) {
-					return days + " days ago";
+					return translate(days, "gb.time.daysAgo", "{0} days ago");
 				} else if (days <= 90) {
 					int weeks = days / 7;
 					if (weeks == 12) {
-						return "3 months ago";
+						return translate(3, "gb.time.monthsAgo", "{0} months ago");
 					} else {
-						return weeks + " weeks ago";
+						return translate(weeks, "gb.time.weeksAgo", "{0} weeks ago");
 					}
 				}
 				int months = days / 30;
@@ -229,21 +252,43 @@
 				if (weeks >= 2) {
 					months++;
 				}
-				return months + " months ago";
+				return translate(months, "gb.time.monthsAgo", "{0} months ago");
 			} else if (days == 365) {
-				return "1 year ago";
+				return translate("gb.time.oneYearAgo", "1 year ago");
 			} else {
 				int yr = days / 365;
 				days = days % 365;
 				int months = (yr * 12) + (days / 30);
 				if (months > 23) {
-					return yr + " years ago";
+					return translate(yr, "gb.time.yearsAgo", "{0} years ago");
 				} else {
-					return months + " months ago";
+					return translate(months, "gb.time.monthsAgo", "{0} months ago");
 				}
 			}
 		}
 	}
+	
+	private String translate(String key, String defaultValue) {
+		String value = defaultValue;
+		if (translation != null && translation.containsKey(key)) {
+			String aValue = translation.getString(key);
+			if (!StringUtils.isEmpty(aValue)) {
+				value = aValue;
+			}
+		}
+		return value;
+	}
+	
+	private String translate(int val, String key, String defaultPattern) {
+		String pattern = defaultPattern;
+		if (translation != null && translation.containsKey(key)) {
+			String aValue = translation.getString(key);
+			if (!StringUtils.isEmpty(aValue)) {
+				pattern = aValue;
+			}
+		}
+		return MessageFormat.format(pattern, val);
+	}
 
 	/**
 	 * Convert a frequency string into minutes.
diff --git a/src/com/gitblit/wicket/WicketUtils.java b/src/com/gitblit/wicket/WicketUtils.java
index 864ebe6..34a14a1 100644
--- a/src/com/gitblit/wicket/WicketUtils.java
+++ b/src/com/gitblit/wicket/WicketUtils.java
@@ -418,7 +418,7 @@
 		return params.getString("n", "");
 	}
 
-	public static Label createDateLabel(String wicketId, Date date, TimeZone timeZone) {
+	public static Label createDateLabel(String wicketId, Date date, TimeZone timeZone, TimeUtils timeUtils) {
 		String format = GitBlit.getString(Keys.web.datestampShortFormat, "MM/dd/yy");
 		DateFormat df = new SimpleDateFormat(format);
 		if (timeZone == null) {
@@ -434,7 +434,7 @@
 		String title = null;
 		if (date.getTime() <= System.currentTimeMillis()) {
 			// past
-			title = TimeUtils.timeAgo(date);
+			title = timeUtils.timeAgo(date);
 		}
 		if ((System.currentTimeMillis() - date.getTime()) < 10 * 24 * 60 * 60 * 1000L) {
 			String tmp = dateString;
@@ -442,14 +442,14 @@
 			title = tmp;
 		}
 		Label label = new Label(wicketId, dateString);
-		WicketUtils.setCssClass(label, TimeUtils.timeAgoCss(date));
+		WicketUtils.setCssClass(label, timeUtils.timeAgoCss(date));
 		if (!StringUtils.isEmpty(title)) {
 			WicketUtils.setHtmlTooltip(label, title);
 		}
 		return label;
 	}
 
-	public static Label createTimeLabel(String wicketId, Date date, TimeZone timeZone) {
+	public static Label createTimeLabel(String wicketId, Date date, TimeZone timeZone, TimeUtils timeUtils) {
 		String format = GitBlit.getString(Keys.web.timeFormat, "HH:mm");
 		DateFormat df = new SimpleDateFormat(format);
 		if (timeZone == null) {
@@ -462,7 +462,7 @@
 		} else {
 			timeString = df.format(date);
 		}
-		String title = TimeUtils.timeAgo(date);
+		String title = timeUtils.timeAgo(date);
 		Label label = new Label(wicketId, timeString);
 		if (!StringUtils.isEmpty(title)) {
 			WicketUtils.setHtmlTooltip(label, title);
@@ -470,7 +470,7 @@
 		return label;
 	}
 
-	public static Label createDatestampLabel(String wicketId, Date date, TimeZone timeZone) {
+	public static Label createDatestampLabel(String wicketId, Date date, TimeZone timeZone, TimeUtils timeUtils) {
 		String format = GitBlit.getString(Keys.web.datestampLongFormat, "EEEE, MMMM d, yyyy");
 		DateFormat df = new SimpleDateFormat(format);
 		if (timeZone == null) {
@@ -485,12 +485,12 @@
 		}
 		String title = null;
 		if (TimeUtils.isToday(date)) {
-			title = "today";
+			title = timeUtils.today();
 		} else if (TimeUtils.isYesterday(date)) {
-				title = "yesterday";
+				title = timeUtils.yesterday();
 		} else if (date.getTime() <= System.currentTimeMillis()) {
 			// past
-			title = TimeUtils.timeAgo(date);
+			title = timeUtils.timeAgo(date);
 		}
 		if ((System.currentTimeMillis() - date.getTime()) < 10 * 24 * 60 * 60 * 1000L) {
 			String tmp = dateString;
@@ -504,7 +504,7 @@
 		return label;
 	}
 
-	public static Label createTimestampLabel(String wicketId, Date date, TimeZone timeZone) {
+	public static Label createTimestampLabel(String wicketId, Date date, TimeZone timeZone, TimeUtils timeUtils) {
 		String format = GitBlit.getString(Keys.web.datetimestampLongFormat,
 				"EEEE, MMMM d, yyyy HH:mm Z");
 		DateFormat df = new SimpleDateFormat(format);
@@ -521,7 +521,7 @@
 		String title = null;
 		if (date.getTime() <= System.currentTimeMillis()) {
 			// past
-			title = TimeUtils.timeAgo(date);
+			title = timeUtils.timeAgo(date);
 		}
 		Label label = new Label(wicketId, dateString);
 		if (!StringUtils.isEmpty(title)) {
diff --git a/src/com/gitblit/wicket/pages/BasePage.java b/src/com/gitblit/wicket/pages/BasePage.java
index 31da3a6..aadab87 100644
--- a/src/com/gitblit/wicket/pages/BasePage.java
+++ b/src/com/gitblit/wicket/pages/BasePage.java
@@ -17,6 +17,7 @@
 
 import java.util.LinkedHashMap;
 import java.util.Map;
+import java.util.ResourceBundle;
 import java.util.TimeZone;
 
 import javax.servlet.http.Cookie;
@@ -47,6 +48,7 @@
 import com.gitblit.Keys;
 import com.gitblit.models.RepositoryModel;
 import com.gitblit.models.UserModel;
+import com.gitblit.utils.TimeUtils;
 import com.gitblit.wicket.GitBlitWebSession;
 import com.gitblit.wicket.WicketUtils;
 import com.gitblit.wicket.panels.LinkPanel;
@@ -54,6 +56,8 @@
 public abstract class BasePage extends WebPage {
 
 	private final Logger logger;
+	
+	private transient TimeUtils timeUtils;
 
 	public BasePage() {
 		super();
@@ -75,6 +79,23 @@
 		}
 	}
 	
+	protected String getLanguageCode() {
+		return GitBlitWebSession.get().getLocale().getLanguage();
+	}
+	
+	protected TimeUtils getTimeUtils() {
+		if (timeUtils == null) {
+			ResourceBundle bundle;		
+			try {
+				bundle = ResourceBundle.getBundle("com.gitblit.wicket.GitBlitWebApp", GitBlitWebSession.get().getLocale());
+			} catch (Throwable t) {
+				bundle = ResourceBundle.getBundle("com.gitblit.wicket.GitBlitWebApp");
+			}
+			timeUtils = new TimeUtils(bundle);
+		}
+		return timeUtils;
+	}
+	
 	@Override
 	protected void onBeforeRender() {
 		if (GitBlit.isDebugMode()) {
diff --git a/src/com/gitblit/wicket/pages/CommitPage.java b/src/com/gitblit/wicket/pages/CommitPage.java
index 0e250b8..f3b3265 100644
--- a/src/com/gitblit/wicket/pages/CommitPage.java
+++ b/src/com/gitblit/wicket/pages/CommitPage.java
@@ -81,12 +81,12 @@
 		// author
 		add(createPersonPanel("commitAuthor", c.getAuthorIdent(), Constants.SearchType.AUTHOR));
 		add(WicketUtils.createTimestampLabel("commitAuthorDate", c.getAuthorIdent().getWhen(),
-				getTimeZone()));
+				getTimeZone(), getTimeUtils()));
 		
 		// committer
 		add(createPersonPanel("commitCommitter", c.getCommitterIdent(), Constants.SearchType.COMMITTER));
 		add(WicketUtils.createTimestampLabel("commitCommitterDate",
-				c.getCommitterIdent().getWhen(), getTimeZone()));
+				c.getCommitterIdent().getWhen(), getTimeZone(), getTimeUtils()));
 
 		add(new Label("commitId", c.getName()));
 
@@ -129,7 +129,7 @@
 						Constants.SearchType.AUTHOR));
 				item.add(new GravatarImage("noteAuthorAvatar", entry.notesRef.getAuthorIdent()));
 				item.add(WicketUtils.createTimestampLabel("authorDate", entry.notesRef
-						.getAuthorIdent().getWhen(), getTimeZone()));
+						.getAuthorIdent().getWhen(), getTimeZone(), getTimeUtils()));
 				item.add(new Label("noteContent", GitBlit.self().processCommitMessage(
 						repositoryName, entry.content)).setEscapeModelStrings(false));
 			}
diff --git a/src/com/gitblit/wicket/pages/FederationRegistrationPage.java b/src/com/gitblit/wicket/pages/FederationRegistrationPage.java
index 751dac8..19c30a5 100644
--- a/src/com/gitblit/wicket/pages/FederationRegistrationPage.java
+++ b/src/com/gitblit/wicket/pages/FederationRegistrationPage.java
@@ -52,8 +52,8 @@
 		add(new Label("frequency", registration.frequency));
 		add(new Label("folder", registration.folder));
 		add(new Label("token", showAdmin ? registration.token : "--"));
-		add(WicketUtils.createTimestampLabel("lastPull", registration.lastPull, getTimeZone()));
-		add(WicketUtils.createTimestampLabel("nextPull", registration.nextPull, getTimeZone()));
+		add(WicketUtils.createTimestampLabel("lastPull", registration.lastPull, getTimeZone(), getTimeUtils()));
+		add(WicketUtils.createTimestampLabel("nextPull", registration.nextPull, getTimeZone(), getTimeUtils()));
 
 		StringBuilder inclusions = new StringBuilder();
 		for (String inc : registration.inclusions) {
diff --git a/src/com/gitblit/wicket/pages/LuceneSearchPage.java b/src/com/gitblit/wicket/pages/LuceneSearchPage.java
index 2894add..54e27ce 100644
--- a/src/com/gitblit/wicket/pages/LuceneSearchPage.java
+++ b/src/com/gitblit/wicket/pages/LuceneSearchPage.java
@@ -234,7 +234,7 @@
 					item.add(new LinkPanel("branch", "branch", StringUtils.getRelativePath(Constants.R_HEADS, sr.branch), LogPage.class, WicketUtils.newObjectParameter(sr.repository, sr.branch)));
 				}
 				item.add(new Label("author", sr.author));
-				item.add(WicketUtils.createDatestampLabel("date", sr.date, getTimeZone()));
+				item.add(WicketUtils.createDatestampLabel("date", sr.date, getTimeZone(), getTimeUtils()));
 			}
 		};
 		add(resultsView.setVisible(results.size() > 0));
diff --git a/src/com/gitblit/wicket/pages/MetricsPage.java b/src/com/gitblit/wicket/pages/MetricsPage.java
index f6df7c5..5904a64 100644
--- a/src/com/gitblit/wicket/pages/MetricsPage.java
+++ b/src/com/gitblit/wicket/pages/MetricsPage.java
@@ -41,7 +41,6 @@
 import com.gitblit.models.Metric;
 import com.gitblit.utils.MetricUtils;
 import com.gitblit.utils.StringUtils;
-import com.gitblit.utils.TimeUtils;
 import com.gitblit.wicket.WicketUtils;
 
 public class MetricsPage extends RepositoryPage {
@@ -62,7 +61,7 @@
 		} else {
 			add(new Label("branchStats",
 					MessageFormat.format(getString("gb.branchStats"), metricsTotal.count,
-							metricsTotal.tag, TimeUtils.duration(metricsTotal.duration))));
+							metricsTotal.tag, getTimeUtils().duration(metricsTotal.duration))));
 		}
 		insertLinePlot("commitsChart", metrics);
 		insertBarPlot("dayOfWeekChart", getDayOfWeekMetrics(r, objectId));
diff --git a/src/com/gitblit/wicket/pages/ReviewProposalPage.java b/src/com/gitblit/wicket/pages/ReviewProposalPage.java
index 25ceb65..df7b1bc 100644
--- a/src/com/gitblit/wicket/pages/ReviewProposalPage.java
+++ b/src/com/gitblit/wicket/pages/ReviewProposalPage.java
@@ -54,7 +54,7 @@
 
 		add(new Label("url", proposal.url));
 		add(new Label("message", proposal.message));
-		add(WicketUtils.createTimestampLabel("received", proposal.received, getTimeZone()));
+		add(WicketUtils.createTimestampLabel("received", proposal.received, getTimeZone(), getTimeUtils()));
 		add(new Label("token", proposal.token));
 		add(new Label("tokenType", proposal.tokenType.name()));
 		
diff --git a/src/com/gitblit/wicket/pages/SummaryPage.java b/src/com/gitblit/wicket/pages/SummaryPage.java
index ef2ee55..cb507d2 100644
--- a/src/com/gitblit/wicket/pages/SummaryPage.java
+++ b/src/com/gitblit/wicket/pages/SummaryPage.java
@@ -49,7 +49,6 @@
 import com.gitblit.utils.JGitUtils;
 import com.gitblit.utils.MarkdownUtils;
 import com.gitblit.utils.StringUtils;
-import com.gitblit.utils.TimeUtils;
 import com.gitblit.wicket.WicketUtils;
 import com.gitblit.wicket.panels.BranchesPanel;
 import com.gitblit.wicket.panels.LogPanel;
@@ -84,13 +83,13 @@
 		add(new Label("repositoryOwner", getRepositoryModel().owner));
 
 		add(WicketUtils.createTimestampLabel("repositoryLastChange",
-				JGitUtils.getLastChange(r), getTimeZone()));
+				JGitUtils.getLastChange(r), getTimeZone(), getTimeUtils()));
 		if (metricsTotal == null) {
 			add(new Label("branchStats", ""));
 		} else {
 			add(new Label("branchStats",
 					MessageFormat.format(getString("gb.branchStats"), metricsTotal.count,
-							metricsTotal.tag, TimeUtils.duration(metricsTotal.duration))));
+							metricsTotal.tag, getTimeUtils().duration(metricsTotal.duration))));
 		}
 		add(new BookmarkablePageLink<Void>("metrics", MetricsPage.class,
 				WicketUtils.newRepositoryParameter(repositoryName)));
diff --git a/src/com/gitblit/wicket/pages/TagPage.java b/src/com/gitblit/wicket/pages/TagPage.java
index b7a5374..91c913d 100644
--- a/src/com/gitblit/wicket/pages/TagPage.java
+++ b/src/com/gitblit/wicket/pages/TagPage.java
@@ -87,7 +87,7 @@
 		if (tagRef.getAuthorIdent() != null) {
 			when = tagRef.getAuthorIdent().getWhen();
 		}
-		add(WicketUtils.createTimestampLabel("tagDate", when, getTimeZone()));
+		add(WicketUtils.createTimestampLabel("tagDate", when, getTimeZone(), getTimeUtils()));
 
 		addFullText("fullMessage", tagRef.getFullMessage(), true);
 	}
diff --git a/src/com/gitblit/wicket/pages/TicketPage.java b/src/com/gitblit/wicket/pages/TicketPage.java
index 48db1ce..5723386 100644
--- a/src/com/gitblit/wicket/pages/TicketPage.java
+++ b/src/com/gitblit/wicket/pages/TicketPage.java
@@ -42,7 +42,7 @@
 		add(new Label("ticketTitle", t.title));
 		add(new Label("ticketId", t.id));
 		add(new Label("ticketHandler", t.handler.toLowerCase()));
-		add(WicketUtils.createTimestampLabel("ticketOpenDate", t.date, getTimeZone()));
+		add(WicketUtils.createTimestampLabel("ticketOpenDate", t.date, getTimeZone(), getTimeUtils()));
 		Label stateLabel = new Label("ticketState", t.state);
 		WicketUtils.setTicketCssClass(stateLabel, t.state);
 		add(stateLabel);
@@ -56,7 +56,7 @@
 			public void populateItem(final Item<Comment> item) {
 				final Comment entry = item.getModelObject();
 				item.add(WicketUtils.createDateLabel("commentDate", entry.date, GitBlitWebSession
-						.get().getTimezone()));
+						.get().getTimezone(), getTimeUtils()));
 				item.add(new Label("commentAuthor", entry.author.toLowerCase()));
 				item.add(new Label("commentText", prepareComment(entry.text))
 						.setEscapeModelStrings(false));
diff --git a/src/com/gitblit/wicket/pages/TicketsPage.java b/src/com/gitblit/wicket/pages/TicketsPage.java
index 0ac8114..b68b7e4 100644
--- a/src/com/gitblit/wicket/pages/TicketsPage.java
+++ b/src/com/gitblit/wicket/pages/TicketsPage.java
@@ -52,7 +52,7 @@
 				WicketUtils.setTicketCssClass(stateLabel, entry.state);
 				item.add(stateLabel);
 				item.add(WicketUtils.createDateLabel("ticketDate", entry.date, GitBlitWebSession
-						.get().getTimezone()));
+						.get().getTimezone(), getTimeUtils()));
 				item.add(new Label("ticketHandler", StringUtils.trimString(
 						entry.handler.toLowerCase(), 30)));
 				item.add(new LinkPanel("ticketTitle", "list subject", StringUtils.trimString(
diff --git a/src/com/gitblit/wicket/panels/ActivityPanel.java b/src/com/gitblit/wicket/panels/ActivityPanel.java
index f75cf7d..e86a689 100644
--- a/src/com/gitblit/wicket/panels/ActivityPanel.java
+++ b/src/com/gitblit/wicket/panels/ActivityPanel.java
@@ -56,7 +56,7 @@
 
 			public void populateItem(final Item<Activity> activityItem) {
 				final Activity entry = activityItem.getModelObject();
-				activityItem.add(WicketUtils.createDatestampLabel("title", entry.startDate, getTimeZone()));
+				activityItem.add(WicketUtils.createDatestampLabel("title", entry.startDate, getTimeZone(), getTimeUtils()));
 
 				// display the commits in chronological order
 				DataView<RepositoryCommit> commits = new DataView<RepositoryCommit>("commit",
@@ -68,7 +68,7 @@
 
 						// commit time of day
 						commitItem.add(WicketUtils.createTimeLabel("time", commit.getCommitterIdent()
-								.getWhen(), getTimeZone()));
+								.getWhen(), getTimeZone(), getTimeUtils()));
 
 						// avatar
 						commitItem.add(new GravatarImage("avatar", commit.getAuthorIdent(), 36));
diff --git a/src/com/gitblit/wicket/panels/BasePanel.java b/src/com/gitblit/wicket/panels/BasePanel.java
index 3606dd0..ec87917 100644
--- a/src/com/gitblit/wicket/panels/BasePanel.java
+++ b/src/com/gitblit/wicket/panels/BasePanel.java
@@ -15,6 +15,7 @@
  */
 package com.gitblit.wicket.panels;
 
+import java.util.ResourceBundle;
 import java.util.TimeZone;
 
 import org.apache.wicket.AttributeModifier;
@@ -25,12 +26,15 @@
 import com.gitblit.Constants;
 import com.gitblit.GitBlit;
 import com.gitblit.Keys;
+import com.gitblit.utils.TimeUtils;
 import com.gitblit.wicket.GitBlitWebSession;
 import com.gitblit.wicket.WicketUtils;
 
 public abstract class BasePanel extends Panel {
 
 	private static final long serialVersionUID = 1L;
+	
+	private transient TimeUtils timeUtils;
 
 	public BasePanel(String wicketId) {
 		super(wicketId);
@@ -40,6 +44,19 @@
 		return GitBlit.getBoolean(Keys.web.useClientTimezone, false) ? GitBlitWebSession.get()
 				.getTimezone() : GitBlit.getTimezone();
 	}
+	
+	protected TimeUtils getTimeUtils() {
+		if (timeUtils == null) {
+			ResourceBundle bundle;		
+			try {
+				bundle = ResourceBundle.getBundle("com.gitblit.wicket.GitBlitWebApp", GitBlitWebSession.get().getLocale());
+			} catch (Throwable t) {
+				bundle = ResourceBundle.getBundle("com.gitblit.wicket.GitBlitWebApp");
+			}
+			timeUtils = new TimeUtils(bundle);
+		}
+		return timeUtils;
+	}
 
 	protected void setPersonSearchTooltip(Component component, String value, Constants.SearchType searchType) {
 		if (searchType.equals(Constants.SearchType.AUTHOR)) {
diff --git a/src/com/gitblit/wicket/panels/BranchesPanel.java b/src/com/gitblit/wicket/panels/BranchesPanel.java
index 5f1b058..8bbab9a 100644
--- a/src/com/gitblit/wicket/panels/BranchesPanel.java
+++ b/src/com/gitblit/wicket/panels/BranchesPanel.java
@@ -83,7 +83,7 @@
 			public void populateItem(final Item<RefModel> item) {
 				final RefModel entry = item.getModelObject();
 
-				item.add(WicketUtils.createDateLabel("branchDate", entry.getDate(), getTimeZone()));
+				item.add(WicketUtils.createDateLabel("branchDate", entry.getDate(), getTimeZone(), getTimeUtils()));
 
 				item.add(new LinkPanel("branchName", "list name", StringUtils.trimString(
 						entry.displayName, 28), LogPage.class, WicketUtils.newObjectParameter(
diff --git a/src/com/gitblit/wicket/panels/CommitHeaderPanel.java b/src/com/gitblit/wicket/panels/CommitHeaderPanel.java
index f411397..bb960cc 100644
--- a/src/com/gitblit/wicket/panels/CommitHeaderPanel.java
+++ b/src/com/gitblit/wicket/panels/CommitHeaderPanel.java
@@ -42,7 +42,7 @@
 				WicketUtils.newObjectParameter(repositoryName, c.getName())));
 		add(new Label("commitid", c.getName()));
 		add(new Label("author", c.getAuthorIdent().getName()));
-		add(WicketUtils.createDateLabel("date", c.getAuthorIdent().getWhen(), getTimeZone()));
+		add(WicketUtils.createDateLabel("date", c.getAuthorIdent().getWhen(), getTimeZone(), getTimeUtils()));
 		add(new GravatarImage("authorAvatar", c.getAuthorIdent()));
 	}
 }
\ No newline at end of file
diff --git a/src/com/gitblit/wicket/panels/FederationProposalsPanel.java b/src/com/gitblit/wicket/panels/FederationProposalsPanel.java
index 11f3f5c..3e70cce 100644
--- a/src/com/gitblit/wicket/panels/FederationProposalsPanel.java
+++ b/src/com/gitblit/wicket/panels/FederationProposalsPanel.java
@@ -56,7 +56,7 @@
 				final FederationProposal entry = item.getModelObject();
 				item.add(new LinkPanel("url", "list", entry.url, ReviewProposalPage.class,
 						WicketUtils.newTokenParameter(entry.token)));
-				item.add(WicketUtils.createDateLabel("received", entry.received, getTimeZone()));
+				item.add(WicketUtils.createDateLabel("received", entry.received, getTimeZone(), getTimeUtils()));
 				item.add(new Label("tokenType", entry.tokenType.name()));
 				item.add(new LinkPanel("token", "list", entry.token, ReviewProposalPage.class,
 						WicketUtils.newTokenParameter(entry.token)));
diff --git a/src/com/gitblit/wicket/panels/FederationRegistrationsPanel.java b/src/com/gitblit/wicket/panels/FederationRegistrationsPanel.java
index a2bfd8c..ff94717 100644
--- a/src/com/gitblit/wicket/panels/FederationRegistrationsPanel.java
+++ b/src/com/gitblit/wicket/panels/FederationRegistrationsPanel.java
@@ -66,9 +66,9 @@
 
 				item.add(WicketUtils.getRegistrationImage("typeIcon", entry, this));
 
-				item.add(WicketUtils.createDateLabel("lastPull", entry.lastPull, getTimeZone()));
+				item.add(WicketUtils.createDateLabel("lastPull", entry.lastPull, getTimeZone(), getTimeUtils()));
 				item.add(WicketUtils
-						.createTimestampLabel("nextPull", entry.nextPull, getTimeZone()));
+						.createTimestampLabel("nextPull", entry.nextPull, getTimeZone(), getTimeUtils()));
 				item.add(new Label("frequency", entry.frequency));
 				WicketUtils.setAlternatingBackground(item, counter);
 				counter++;
diff --git a/src/com/gitblit/wicket/panels/HistoryPanel.java b/src/com/gitblit/wicket/panels/HistoryPanel.java
index 15a4aa2..befd701 100644
--- a/src/com/gitblit/wicket/panels/HistoryPanel.java
+++ b/src/com/gitblit/wicket/panels/HistoryPanel.java
@@ -103,7 +103,7 @@
 				final RevCommit entry = item.getModelObject();
 				final Date date = JGitUtils.getCommitDate(entry);
 
-				item.add(WicketUtils.createDateLabel("commitDate", date, getTimeZone()));
+				item.add(WicketUtils.createDateLabel("commitDate", date, getTimeZone(), getTimeUtils()));
 
 				// author search link
 				String author = entry.getAuthorIdent().getName();
diff --git a/src/com/gitblit/wicket/panels/LogPanel.java b/src/com/gitblit/wicket/panels/LogPanel.java
index 5ced2f6..f441ba5 100644
--- a/src/com/gitblit/wicket/panels/LogPanel.java
+++ b/src/com/gitblit/wicket/panels/LogPanel.java
@@ -91,7 +91,7 @@
 				final RevCommit entry = item.getModelObject();
 				final Date date = JGitUtils.getCommitDate(entry);
 
-				item.add(WicketUtils.createDateLabel("commitDate", date, getTimeZone()));
+				item.add(WicketUtils.createDateLabel("commitDate", date, getTimeZone(), getTimeUtils()));
 
 				// author search link
 				String author = entry.getAuthorIdent().getName();
diff --git a/src/com/gitblit/wicket/panels/RepositoriesPanel.java b/src/com/gitblit/wicket/panels/RepositoriesPanel.java
index 3b42972..9664e05 100644
--- a/src/com/gitblit/wicket/panels/RepositoriesPanel.java
+++ b/src/com/gitblit/wicket/panels/RepositoriesPanel.java
@@ -49,7 +49,6 @@
 import com.gitblit.models.RepositoryModel;
 import com.gitblit.models.UserModel;
 import com.gitblit.utils.StringUtils;
-import com.gitblit.utils.TimeUtils;
 import com.gitblit.wicket.GitBlitWebSession;
 import com.gitblit.wicket.WicketUtils;
 import com.gitblit.wicket.pages.BasePage;
@@ -242,11 +241,11 @@
 				if (entry.lastChange.getTime() == 0) {
 					lastChange = "--";
 				} else {
-					lastChange = TimeUtils.timeAgo(entry.lastChange);
+					lastChange = getTimeUtils().timeAgo(entry.lastChange);
 				}
 				Label lastChangeLabel = new Label("repositoryLastChange", lastChange);
 				row.add(lastChangeLabel);
-				WicketUtils.setCssClass(lastChangeLabel, TimeUtils.timeAgoCss(entry.lastChange));
+				WicketUtils.setCssClass(lastChangeLabel, getTimeUtils().timeAgoCss(entry.lastChange));
 
 				boolean showOwner = user != null && user.username.equalsIgnoreCase(entry.owner);
 				if (showAdmin) {
diff --git a/src/com/gitblit/wicket/panels/SearchPanel.java b/src/com/gitblit/wicket/panels/SearchPanel.java
index 4d195c5..eab3aea 100644
--- a/src/com/gitblit/wicket/panels/SearchPanel.java
+++ b/src/com/gitblit/wicket/panels/SearchPanel.java
@@ -87,7 +87,7 @@
 				final RevCommit entry = item.getModelObject();
 				final Date date = JGitUtils.getCommitDate(entry);
 
-				item.add(WicketUtils.createDateLabel("commitDate", date, getTimeZone()));
+				item.add(WicketUtils.createDateLabel("commitDate", date, getTimeZone(), getTimeUtils()));
 
 				// author search link
 				String author = entry.getAuthorIdent().getName();
diff --git a/src/com/gitblit/wicket/panels/TagsPanel.java b/src/com/gitblit/wicket/panels/TagsPanel.java
index a06c1c2..2bee6a6 100644
--- a/src/com/gitblit/wicket/panels/TagsPanel.java
+++ b/src/com/gitblit/wicket/panels/TagsPanel.java
@@ -69,7 +69,7 @@
 			public void populateItem(final Item<RefModel> item) {
 				RefModel entry = item.getModelObject();
 
-				item.add(WicketUtils.createDateLabel("tagDate", entry.getDate(), getTimeZone()));
+				item.add(WicketUtils.createDateLabel("tagDate", entry.getDate(), getTimeZone(), getTimeUtils()));
 
 				Class<? extends RepositoryPage> linkClass;
 				switch (entry.getReferencedObjectType()) {
diff --git a/tests/com/gitblit/tests/TimeUtilsTest.java b/tests/com/gitblit/tests/TimeUtilsTest.java
index 9d94df8..f9d5d83 100644
--- a/tests/com/gitblit/tests/TimeUtilsTest.java
+++ b/tests/com/gitblit/tests/TimeUtilsTest.java
@@ -53,50 +53,52 @@
 
 	@Test
 	public void testDurations() throws Exception {
-		assertEquals("1 day", TimeUtils.duration(1));
-		assertEquals("5 days", TimeUtils.duration(5));
-		assertEquals("3 months", TimeUtils.duration(75));
-		assertEquals("12 months", TimeUtils.duration(364));
-		assertEquals("1 year", TimeUtils.duration(365 + 0));
-		assertEquals("1 year", TimeUtils.duration(365 + 10));
-		assertEquals("1 year, 1 month", TimeUtils.duration(365 + 15));
-		assertEquals("1 year, 1 month", TimeUtils.duration(365 + 30));
-		assertEquals("1 year, 1 month", TimeUtils.duration(365 + 44));
-		assertEquals("1 year, 2 months", TimeUtils.duration(365 + 45));
-		assertEquals("1 year, 2 months", TimeUtils.duration(365 + 60));
+		TimeUtils timeUtils = new TimeUtils();
+		assertEquals("1 day", timeUtils.duration(1));
+		assertEquals("5 days", timeUtils.duration(5));
+		assertEquals("3 months", timeUtils.duration(75));
+		assertEquals("12 months", timeUtils.duration(364));
+		assertEquals("1 year", timeUtils.duration(365 + 0));
+		assertEquals("1 year", timeUtils.duration(365 + 10));
+		assertEquals("1 year, 1 month", timeUtils.duration(365 + 15));
+		assertEquals("1 year, 1 month", timeUtils.duration(365 + 30));
+		assertEquals("1 year, 1 month", timeUtils.duration(365 + 44));
+		assertEquals("1 year, 2 months", timeUtils.duration(365 + 45));
+		assertEquals("1 year, 2 months", timeUtils.duration(365 + 60));
 
-		assertEquals("2 years", TimeUtils.duration(2 * 365 + 0));
-		assertEquals("2 years", TimeUtils.duration(2 * 365 + 10));
-		assertEquals("2 years, 1 month", TimeUtils.duration(2 * 365 + 15));
-		assertEquals("2 years, 1 month", TimeUtils.duration(2 * 365 + 30));
-		assertEquals("2 years, 1 month", TimeUtils.duration(2 * 365 + 44));
-		assertEquals("2 years, 2 months", TimeUtils.duration(2 * 365 + 45));
-		assertEquals("2 years, 2 months", TimeUtils.duration(2 * 365 + 60));
+		assertEquals("2 years", timeUtils.duration(2 * 365 + 0));
+		assertEquals("2 years", timeUtils.duration(2 * 365 + 10));
+		assertEquals("2 years, 1 month", timeUtils.duration(2 * 365 + 15));
+		assertEquals("2 years, 1 month", timeUtils.duration(2 * 365 + 30));
+		assertEquals("2 years, 1 month", timeUtils.duration(2 * 365 + 44));
+		assertEquals("2 years, 2 months", timeUtils.duration(2 * 365 + 45));
+		assertEquals("2 years, 2 months", timeUtils.duration(2 * 365 + 60));
 	}
 
 	@Test
 	public void testTimeAgo() throws Exception {
 		// standard time ago tests
-		assertEquals("1 min ago", TimeUtils.timeAgo(offset(1 * TimeUtils.MIN)));
-		assertEquals("60 mins ago", TimeUtils.timeAgo(offset(60 * TimeUtils.MIN)));
-		assertEquals("2 hours ago", TimeUtils.timeAgo(offset(120 * TimeUtils.MIN)));
-		assertEquals("15 hours ago", TimeUtils.timeAgo(offset(15 * TimeUtils.ONEHOUR)));
-		assertEquals("yesterday", TimeUtils.timeAgo(offset(24 * TimeUtils.ONEHOUR)));
-		assertEquals("2 days ago", TimeUtils.timeAgo(offset(2 * TimeUtils.ONEDAY)));
-		assertEquals("5 weeks ago", TimeUtils.timeAgo(offset(35 * TimeUtils.ONEDAY)));
-		assertEquals("3 months ago", TimeUtils.timeAgo(offset(84 * TimeUtils.ONEDAY)));
-		assertEquals("3 months ago", TimeUtils.timeAgo(offset(95 * TimeUtils.ONEDAY)));
-		assertEquals("4 months ago", TimeUtils.timeAgo(offset(104 * TimeUtils.ONEDAY)));
-		assertEquals("1 year ago", TimeUtils.timeAgo(offset(365 * TimeUtils.ONEDAY)));
-		assertEquals("13 months ago", TimeUtils.timeAgo(offset(395 * TimeUtils.ONEDAY)));
-		assertEquals("2 years ago", TimeUtils.timeAgo(offset((2 * 365 + 30) * TimeUtils.ONEDAY)));
+		TimeUtils timeUtils = new TimeUtils();
+		assertEquals("just now", timeUtils.timeAgo(offset(1 * TimeUtils.MIN)));
+		assertEquals("60 mins ago", timeUtils.timeAgo(offset(60 * TimeUtils.MIN)));
+		assertEquals("2 hours ago", timeUtils.timeAgo(offset(120 * TimeUtils.MIN)));
+		assertEquals("15 hours ago", timeUtils.timeAgo(offset(15 * TimeUtils.ONEHOUR)));
+		assertEquals("yesterday", timeUtils.timeAgo(offset(24 * TimeUtils.ONEHOUR)));
+		assertEquals("2 days ago", timeUtils.timeAgo(offset(2 * TimeUtils.ONEDAY)));
+		assertEquals("5 weeks ago", timeUtils.timeAgo(offset(35 * TimeUtils.ONEDAY)));
+		assertEquals("3 months ago", timeUtils.timeAgo(offset(84 * TimeUtils.ONEDAY)));
+		assertEquals("3 months ago", timeUtils.timeAgo(offset(95 * TimeUtils.ONEDAY)));
+		assertEquals("4 months ago", timeUtils.timeAgo(offset(104 * TimeUtils.ONEDAY)));
+		assertEquals("1 year ago", timeUtils.timeAgo(offset(365 * TimeUtils.ONEDAY)));
+		assertEquals("13 months ago", timeUtils.timeAgo(offset(395 * TimeUtils.ONEDAY)));
+		assertEquals("2 years ago", timeUtils.timeAgo(offset((2 * 365 + 30) * TimeUtils.ONEDAY)));
 
 		// css class tests
-		assertEquals("age0", TimeUtils.timeAgoCss(offset(1 * TimeUtils.MIN)));
-		assertEquals("age0", TimeUtils.timeAgoCss(offset(60 * TimeUtils.MIN)));
-		assertEquals("age1", TimeUtils.timeAgoCss(offset(120 * TimeUtils.MIN)));
-		assertEquals("age1", TimeUtils.timeAgoCss(offset(24 * TimeUtils.ONEHOUR)));
-		assertEquals("age2", TimeUtils.timeAgoCss(offset(2 * TimeUtils.ONEDAY)));
+		assertEquals("age0", timeUtils.timeAgoCss(offset(1 * TimeUtils.MIN)));
+		assertEquals("age0", timeUtils.timeAgoCss(offset(60 * TimeUtils.MIN)));
+		assertEquals("age1", timeUtils.timeAgoCss(offset(120 * TimeUtils.MIN)));
+		assertEquals("age1", timeUtils.timeAgoCss(offset(24 * TimeUtils.ONEHOUR)));
+		assertEquals("age2", timeUtils.timeAgoCss(offset(2 * TimeUtils.ONEDAY)));
 	}
 
 	@Test

--
Gitblit v1.9.1