ICU-10553 Implement context-sensitive capitalization for relative dates (J)

X-SVN-Rev: 34896
This commit is contained in:
Peter Edberg 2014-01-15 08:12:16 +00:00
parent f12f43b80e
commit fce9b6ba8c
2 changed files with 123 additions and 5 deletions

View file

@ -1,6 +1,6 @@
/*
*******************************************************************************
* Copyright (C) 2007-2013, International Business Machines Corporation and *
* Copyright (C) 2007-2014, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
@ -14,7 +14,10 @@ import java.util.MissingResourceException;
import java.util.Set;
import java.util.TreeSet;
import com.ibm.icu.lang.UCharacter;
import com.ibm.icu.lang.UProperty;
import com.ibm.icu.text.DateFormat;
import com.ibm.icu.text.DisplayContext;
import com.ibm.icu.text.MessageFormat;
import com.ibm.icu.text.SimpleDateFormat;
import com.ibm.icu.util.Calendar;
@ -102,6 +105,8 @@ public class RelativeDateFormat extends DateFormat {
FieldPosition fieldPosition) {
String relativeDayString = null;
DisplayContext capitalizationContext = getContext(DisplayContext.Type.CAPITALIZATION);
if (fDateStyle != DateFormat.NONE) {
// calculate the difference, in days, between 'cal' and now.
int dayDiff = dayDifference(cal);
@ -110,6 +115,47 @@ public class RelativeDateFormat extends DateFormat {
relativeDayString = getStringForDay(dayDiff);
}
if ( relativeDayString != null && fDatePattern != null &&
(fTimePattern == null || fCombinedFormat == null || combinedFormatHasDateAtStart) ) {
// capitalize relativeDayString according to context for tense, set formatter no context
if ( capitalizationContext == DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE ||
(capitalizationContext == DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU && capitalizationForRelativeUnitsListOrMenu) ||
(capitalizationContext == DisplayContext.CAPITALIZATION_FOR_STANDALONE && capitalizationForRelativeUnitsStandAlone) ) {
// titlecase first word of relativeDayString, do like LocaleDisplayNamesImpl.adjustForUsageAndContext
// Note that for languages that *have* case, using the word break properties here works reasonably
// well (CLDR word break tailoring is for non-casing languages, and the POSIX locale).
int stopPos, stopPosLimit = 8, len = relativeDayString.length();
if ( stopPosLimit > len ) {
stopPosLimit = len;
}
for ( stopPos = 0; stopPos < stopPosLimit; stopPos++ ) {
int ch = relativeDayString.codePointAt(stopPos);
int wb = UCharacter.getIntPropertyValue(ch, UProperty.WORD_BREAK);
if ( !(UCharacter.isLowerCase(ch) || wb==UCharacter.WordBreak.EXTEND || wb==UCharacter.WordBreak.SINGLE_QUOTE ||
wb==UCharacter.WordBreak.MIDNUMLET || wb==UCharacter.WordBreak.MIDLETTER) ) {
break;
}
if (ch >= 0x10000) {
stopPos++;
}
}
if ( stopPos > 0 && stopPos < len ) {
String firstWord = relativeDayString.substring(0, stopPos);
firstWord = UCharacter.toTitleCase(fLocale, firstWord, null,
UCharacter.TITLECASE_NO_LOWERCASE | UCharacter.TITLECASE_NO_BREAK_ADJUSTMENT);
relativeDayString = firstWord.concat(relativeDayString.substring(stopPos));
} else {
// no stopPos, titlecase the whole text
relativeDayString = UCharacter.toTitleCase(fLocale, relativeDayString, null,
UCharacter.TITLECASE_NO_LOWERCASE | UCharacter.TITLECASE_NO_BREAK_ADJUSTMENT);
}
}
fDateTimeFormat.setContext(DisplayContext.CAPITALIZATION_NONE);
} else {
// set our context for the formatter
fDateTimeFormat.setContext(capitalizationContext);
}
if (fDateTimeFormat != null && (fDatePattern != null || fTimePattern != null)) {
// The new way
if (fDatePattern == null) {
@ -169,6 +215,10 @@ public class RelativeDateFormat extends DateFormat {
private transient URelativeString fDates[] = null; // array of strings
private transient boolean capitalizationForRelativeUnitsListOrMenu = false;
private transient boolean capitalizationForRelativeUnitsStandAlone = false;
private transient boolean combinedFormatHasDateAtStart = false;
/**
* Get the string at a specific offset.
@ -216,6 +266,17 @@ public class RelativeDateFormat extends DateFormat {
datesSet.add(rs);
}
fDates = datesSet.toArray(new URelativeString[0]);
try {
rdb = rb.getWithFallback("contextTransforms/tense");
int[] intVector = rdb.getIntVector();
if (intVector.length >= 2) {
capitalizationForRelativeUnitsListOrMenu = (intVector[0] != 0);
capitalizationForRelativeUnitsStandAlone = (intVector[1] != 0);
}
} catch (MissingResourceException e) {
// use default
}
}
/**
@ -284,6 +345,7 @@ public class RelativeDateFormat extends DateFormat {
} catch (MissingResourceException e) {
// use default
}
combinedFormatHasDateAtStart = pattern.startsWith("{1}");
fCombinedFormat = new MessageFormat(pattern, locale);
return fCombinedFormat;
}

View file

@ -1,6 +1,6 @@
/*
*******************************************************************************
* Copyright (C) 2001-2013, International Business Machines Corporation and *
* Copyright (C) 2001-2014, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
@ -4200,6 +4200,32 @@ public class DateFormatTest extends com.ibm.icu.dev.test.TestFmwk {
new TestContextItem( "cs", "LLLL y", DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU, "\u010Cervenec 2008" ),
new TestContextItem( "cs", "LLLL y", DisplayContext.CAPITALIZATION_FOR_STANDALONE, "\u010Dervenec 2008" ),
};
class TestRelativeContextItem {
public String locale;
public DisplayContext capitalizationContext;
public String expectedFormatToday;
public String expectedFormatYesterday;
// Simple constructor
public TestRelativeContextItem(String loc, DisplayContext capCtxt, String expFmtToday, String expFmtYesterday) {
locale = loc;
capitalizationContext = capCtxt;
expectedFormatToday = expFmtToday;
expectedFormatYesterday = expFmtYesterday;
}
};
final TestRelativeContextItem[] relItems = {
new TestRelativeContextItem( "en", DisplayContext.CAPITALIZATION_NONE, "today", "yesterday" ),
new TestRelativeContextItem( "en", DisplayContext.CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE, "today", "yesterday" ),
new TestRelativeContextItem( "en", DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE, "Today", "Yesterday" ),
new TestRelativeContextItem( "en", DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU, "Today", "Yesterday" ),
new TestRelativeContextItem( "en", DisplayContext.CAPITALIZATION_FOR_STANDALONE, "Today", "Yesterday" ),
new TestRelativeContextItem( "nb", DisplayContext.CAPITALIZATION_NONE, "i dag", "i g\u00E5r" ),
new TestRelativeContextItem( "nb", DisplayContext.CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE, "i dag", "i g\u00E5r" ),
new TestRelativeContextItem( "nb", DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE, "I dag", "I g\u00E5r" ),
new TestRelativeContextItem( "nb", DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU, "i dag", "i g\u00E5r" ),
new TestRelativeContextItem( "nb", DisplayContext.CAPITALIZATION_FOR_STANDALONE, "I dag", "I g\u00E5r" ),
};
Calendar cal = new GregorianCalendar(2008, Calendar.JULY, 2);
for (TestContextItem item: items) {
ULocale locale = new ULocale(item.locale);
@ -4211,14 +4237,44 @@ public class DateFormatTest extends com.ibm.icu.dev.test.TestFmwk {
FieldPosition fpos2 = new FieldPosition(0);
sdfmt.format(cal, result2, fpos2);
if (result2.toString().compareTo(item.expectedFormat) != 0) {
errln("FAIL: format (default context) for locale " + item.locale + ", capitalizationContext " + item.capitalizationContext +
errln("FAIL: format for locale " + item.locale + ", capitalizationContext " + item.capitalizationContext +
", expected \"" + item.expectedFormat + "\", got \"" + result2 + "\"");
}
// now read back context, make sure it is what we set
// now read back context, make sure it is what we set (testing with DateFormat subclass)
DisplayContext capitalizationContext = sdfmt.getContext(DisplayContext.Type.CAPITALIZATION);
if (capitalizationContext != item.capitalizationContext) {
errln("FAIL: getDefaultContext for locale " + item.locale + ", capitalizationContext " + item.capitalizationContext +
errln("FAIL: getContext for locale " + item.locale + ", capitalizationContext " + item.capitalizationContext +
", but got context " + capitalizationContext);
}
}
for (TestRelativeContextItem relItem: relItems) {
ULocale locale = new ULocale(relItem.locale);
DateFormat dfmt = DateFormat.getDateInstance(DateFormat.RELATIVE_LONG, locale);
Date today = new Date();
// now try context & standard format call
dfmt.setContext(relItem.capitalizationContext);
cal.setTime(today);
StringBuffer result2 = new StringBuffer();
FieldPosition fpos2 = new FieldPosition(0);
dfmt.format(cal, result2, fpos2);
if (result2.toString().compareTo(relItem.expectedFormatToday) != 0) {
errln("FAIL: format today for locale " + relItem.locale + ", capitalizationContext " + relItem.capitalizationContext +
", expected \"" + relItem.expectedFormatToday + "\", got \"" + result2 + "\"");
}
cal.add(Calendar.DATE, -1);
result2.setLength(0);
dfmt.format(cal, result2, fpos2);
if (result2.toString().compareTo(relItem.expectedFormatYesterday) != 0) {
errln("FAIL: format yesterday for locale " + relItem.locale + ", capitalizationContext " + relItem.capitalizationContext +
", expected \"" + relItem.expectedFormatYesterday + "\", got \"" + result2 + "\"");
}
// now read back context, make sure it is what we set (testing with DateFormat itself)
DisplayContext capitalizationContext = dfmt.getContext(DisplayContext.Type.CAPITALIZATION);
if (capitalizationContext != relItem.capitalizationContext) {
errln("FAIL: getContext for locale " + relItem.locale + ", capitalizationContext " + relItem.capitalizationContext +
", but got context " + capitalizationContext);
}
}