]> granicus.if.org Git - icu/commitdiff
ICU-10553 Implement context-sensitive capitalization for relative dates (J)
authorPeter Edberg <pedberg@unicode.org>
Wed, 15 Jan 2014 08:12:16 +0000 (08:12 +0000)
committerPeter Edberg <pedberg@unicode.org>
Wed, 15 Jan 2014 08:12:16 +0000 (08:12 +0000)
X-SVN-Rev: 34896

icu4j/main/classes/core/src/com/ibm/icu/impl/RelativeDateFormat.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/DateFormatTest.java

index 6821f4d35e836fc2ba2ced493769dba2596c8eae..8db8958c1dd4e2b8c78626f7284b70aea93c099f 100644 (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;
     }
index 86bd4e2bd803800bd2766ddb6f33e8fc7cbeb566..f19c87a06789aeb86aa2f7d93357cc2f405212a4 100644 (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);
             }
         }