import com.ibm.icu.lang.UCharacter;
import com.ibm.icu.lang.UScript;
+import com.ibm.icu.text.BreakIterator;
import com.ibm.icu.text.DisplayContext;
import com.ibm.icu.text.LocaleDisplayNames;
import com.ibm.icu.text.MessageFormat;
KEYVALUE
}
/**
- * Capitalization transforms. For each usage type, the first array element indicates
- * whether to titlecase for uiListOrMenu context, the second indicates whether to
- * titlecase for stand-alone context.
+ * Capitalization transforms. For each usage type, indicates whether to titlecase for
+ * the context specified in capitalization (which we know at construction time).
*/
- private Map<CapitalizationContextUsage,boolean[]> capitalizationUsage = null;
+ private boolean[] capitalizationUsage = null;
/**
* Map from resource key to CapitalizationContextUsage value
*/
contextUsageTypeMap.put("key", CapitalizationContextUsage.KEY);
contextUsageTypeMap.put("keyValue", CapitalizationContextUsage.KEYVALUE);
}
+ /**
+ * BreakIterator to use for capitalization
+ */
+ private BreakIterator capitalizationBrkIter = null;
+
public static LocaleDisplayNames getInstance(ULocale locale, DialectHandling dialectHandling) {
synchronized (cache) {
}
this.keyTypeFormat = new MessageFormat(keyTypePattern);
- // Get values from the contextTransforms data
- // (copied from DateFormatSymbols)
+ // Get values from the contextTransforms data if we need them
+ // Also check whether we will need a break iterator (depends on the data)
+ boolean needBrkIter = false;
if (capitalization == DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU ||
capitalization == DisplayContext.CAPITALIZATION_FOR_STANDALONE) {
- capitalizationUsage = new HashMap<CapitalizationContextUsage,boolean[]>();
- boolean[] noTransforms = new boolean[2];
- noTransforms[0] = false;
- noTransforms[1] = false;
- CapitalizationContextUsage allUsages[] = CapitalizationContextUsage.values();
- for (CapitalizationContextUsage usage: allUsages) {
- capitalizationUsage.put(usage, noTransforms);
- }
+ capitalizationUsage = new boolean[CapitalizationContextUsage.values().length]; // initialized to all false
ICUResourceBundle rb = (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, locale);
UResourceBundle contextTransformsBundle = null;
try {
String usageKey = contextTransformUsage.getKey();
CapitalizationContextUsage usage = contextUsageTypeMap.get(usageKey);
if (usage != null) {
- boolean[] transforms = new boolean[2];
- transforms[0] = (intVector[0] != 0);
- transforms[1] = (intVector[1] != 0);
- capitalizationUsage.put(usage, transforms);
+ int titlecaseInt = (capitalization == DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU)?
+ intVector[0]: intVector[1];
+ if (titlecaseInt != 0) {
+ capitalizationUsage[usage.ordinal()] = true;
+ needBrkIter = true;
+ }
}
}
}
}
}
+ // Get a sentence break iterator if we will need it
+ if (needBrkIter || capitalization == DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE) {
+ capitalizationBrkIter = BreakIterator.getSentenceInstance(locale);
+ }
}
@Override
}
private String adjustForUsageAndContext(CapitalizationContextUsage usage, String name) {
- String result = name;
- boolean titlecase = false;
- switch (capitalization) {
- case CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE:
- titlecase = true;
- break;
- case CAPITALIZATION_FOR_UI_LIST_OR_MENU:
- case CAPITALIZATION_FOR_STANDALONE:
- if (capitalizationUsage != null) {
- boolean[] transforms = capitalizationUsage.get(usage);
- titlecase = (capitalization==DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU)?
- transforms[0]: transforms[1];
- }
- break;
- default:
- break;
- }
- if (titlecase) {
- // TODO: Fix this titlecase hack when we figure out something better to do.
- // We don't want to titlecase the whole text, only something like the first word,
- // of the first segment long enough to have a complete cluster, whichever is
- // shorter. We could have keep a word break iterator around, but I am not sure
- // that will do the ight thing for the purposes here. For now we assume that in
- // languages for which titlecasing makes a difference, we can stop at non-letter
- // characters in 0x0000-0x00FF and only titlecase up to the first occurrence of
- // any of those, or to a small number of chars, whichever comes first.
- int stopPos, stopPosLimit = 8, len = name.length();
- if ( stopPosLimit > len ) {
- stopPosLimit = len;
- }
- for ( stopPos = 0; stopPos < stopPosLimit; stopPos++ ) {
- int ch = name.codePointAt(stopPos);
- if ( (ch < 0x41) || (ch > 0x5A && ch < 0x61) || (ch > 0x7A && ch < 0xC0) ) {
- break;
- }
- if (ch >= 0x10000) {
- stopPos++;
- }
- }
- if ( stopPos > 0 && stopPos < len ) {
- String firstWord = name.substring(0, stopPos);
- String firstWordTitleCase = UCharacter.toTitleCase(locale, firstWord, null,
- UCharacter.TITLECASE_NO_LOWERCASE | UCharacter.TITLECASE_NO_BREAK_ADJUSTMENT);
- result = firstWordTitleCase.concat(name.substring(stopPos));
- } else {
- // no stopPos, titlecase the whole text
- result = UCharacter.toTitleCase(locale, name, null,
- UCharacter.TITLECASE_NO_LOWERCASE | UCharacter.TITLECASE_NO_BREAK_ADJUSTMENT);
- }
- }
- return result;
+ if (name != null && name.length() > 0 && UCharacter.isLowerCase(name.codePointAt(0)) &&
+ capitalizationBrkIter != null &&
+ (capitalization==DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE ||
+ (capitalizationUsage != null && capitalizationUsage[usage.ordinal()]) )) {
+ // Note, won't have capitalizationUsage != null && capitalizationUsage[usage.ordinal()]
+ // unless capitalization is CAPITALIZATION_FOR_UI_LIST_OR_MENU or CAPITALIZATION_FOR_STANDALONE
+ return UCharacter.toTitleCase(locale, name, capitalizationBrkIter,
+ UCharacter.TITLECASE_NO_LOWERCASE | UCharacter.TITLECASE_NO_BREAK_ADJUSTMENT);
+ }
+ return name;
}
@Override
import com.ibm.icu.lang.UCharacter;
import com.ibm.icu.lang.UProperty;
+import com.ibm.icu.text.BreakIterator;
import com.ibm.icu.text.DateFormat;
import com.ibm.icu.text.DisplayContext;
import com.ibm.icu.text.MessageFormat;
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);
- }
+ // capitalize relativeDayString according to context for relative, set formatter no context
+ if ( relativeDayString.length() > 0 && UCharacter.isLowerCase(relativeDayString.codePointAt(0)) &&
+ capitalizationBrkIter != null &&
+ (capitalizationContext == DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE ||
+ (capitalizationContext == DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU && capitalizationOfRelativeUnitsForListOrMenu) ||
+ (capitalizationContext == DisplayContext.CAPITALIZATION_FOR_STANDALONE && capitalizationOfRelativeUnitsForStandAlone) )) {
+ relativeDayString = UCharacter.toTitleCase(fLocale, relativeDayString, capitalizationBrkIter,
+ UCharacter.TITLECASE_NO_LOWERCASE | UCharacter.TITLECASE_NO_BREAK_ADJUSTMENT);
}
fDateTimeFormat.setContext(DisplayContext.CAPITALIZATION_NONE);
} else {
throw new UnsupportedOperationException("Relative Date parse is not implemented yet");
}
+ /* (non-Javadoc)
+ * @see com.ibm.icu.text.DateFormat#setContext(com.ibm.icu.text.DisplayContext)
+ * Here we override the DateFormat implementation in order to
+ * lazily initialize relevant items
+ */
+ public void setContext(DisplayContext context) {
+ super.setContext(context);
+ if (!capitalizationInfoIsSet &&
+ (context==DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU || context==DisplayContext.CAPITALIZATION_FOR_STANDALONE)) {
+ initCapitalizationContextInfo(fLocale);
+ capitalizationInfoIsSet = true;
+ }
+ if (capitalizationBrkIter == null && (context==DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE ||
+ (context==DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU && capitalizationOfRelativeUnitsForListOrMenu) ||
+ (context==DisplayContext.CAPITALIZATION_FOR_STANDALONE && capitalizationOfRelativeUnitsForStandAlone) )) {
+ capitalizationBrkIter = BreakIterator.getSentenceInstance(fLocale);
+ }
+ }
+
private DateFormat fDateFormat; // keep for serialization compatibility
@SuppressWarnings("unused")
private DateFormat fTimeFormat; // now unused, keep for serialization compatibility
private transient URelativeString fDates[] = null; // array of strings
- private transient boolean capitalizationForRelativeUnitsListOrMenu = false;
- private transient boolean capitalizationForRelativeUnitsStandAlone = false;
- private transient boolean combinedFormatHasDateAtStart = false;
+ private boolean combinedFormatHasDateAtStart = false;
+ private boolean capitalizationInfoIsSet = false;
+ private boolean capitalizationOfRelativeUnitsForListOrMenu = false;
+ private boolean capitalizationOfRelativeUnitsForStandAlone = false;
+ private BreakIterator capitalizationBrkIter = null;
/**
datesSet.add(rs);
}
fDates = datesSet.toArray(new URelativeString[0]);
-
+ }
+
+ /**
+ * Set capitalizationOfRelativeUnitsForListOrMenu, capitalizationOfRelativeUnitsForStandAlone
+ */
+ private void initCapitalizationContextInfo(ULocale locale) {
+ ICUResourceBundle rb = (ICUResourceBundle) UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, locale);
try {
- rdb = rb.getWithFallback("contextTransforms/relative");
+ ICUResourceBundle rdb = rb.getWithFallback("contextTransforms/relative");
int[] intVector = rdb.getIntVector();
if (intVector.length >= 2) {
- capitalizationForRelativeUnitsListOrMenu = (intVector[0] != 0);
- capitalizationForRelativeUnitsStandAlone = (intVector[1] != 0);
+ capitalizationOfRelativeUnitsForListOrMenu = (intVector[0] != 0);
+ capitalizationOfRelativeUnitsForStandAlone = (intVector[1] != 0);
}
} catch (MissingResourceException e) {
// use default
}
}
-
+
/**
* @return the number of days in "until-now"
*/
import com.ibm.icu.impl.PatternProps;
import com.ibm.icu.impl.SimpleCache;
import com.ibm.icu.lang.UCharacter;
+import com.ibm.icu.text.BreakIterator;
import com.ibm.icu.text.TimeZoneFormat.Style;
import com.ibm.icu.text.TimeZoneFormat.TimeType;
import com.ibm.icu.util.BasicTimeZone;
*/
private volatile TimeZoneFormat tzFormat;
+ /**
+ * BreakIterator to use for capitalization
+ */
+ private BreakIterator capitalizationBrkIter = null;
+
/*
* Capitalization setting, introduced in ICU 50
* Special serialization, see writeObject & readObject below
return getDefaultCenturyStart();
}
+ /**
+ * {@icu} Set a particular DisplayContext value in the formatter,
+ * such as CAPITALIZATION_FOR_STANDALONE. Note: For getContext, see
+ * DateFormat.
+ *
+ * @param context The DisplayContext value to set.
+ * @draft ICU 53
+ * @provisional This API might change or be removed in a future release.
+ */
+ // Here we override the DateFormat implementation in order to lazily initialize relevant items
+ public void setContext(DisplayContext context) {
+ super.setContext(context);
+ if (capitalizationBrkIter == null && (context==DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE ||
+ context==DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU ||
+ context==DisplayContext.CAPITALIZATION_FOR_STANDALONE)) {
+ capitalizationBrkIter = BreakIterator.getSentenceInstance(locale);
+ }
+ }
+
/**
* Formats a date or time, which is the standard millis
* since January 1, 1970, 00:00:00 GMT.
break;
} // switch (patternCharIndex)
- if (fieldNum == 0) {
+ if (fieldNum == 0 && capitalizationContext != null && UCharacter.isLowerCase(buf.codePointAt(bufstart)) &&
+ capitalizationBrkIter != null) {
boolean titlecase = false;
- if (capitalizationContext != null) {
- switch (capitalizationContext) {
- case CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE:
- titlecase = true;
- break;
- case CAPITALIZATION_FOR_UI_LIST_OR_MENU:
- case CAPITALIZATION_FOR_STANDALONE:
- if (formatData.capitalization != null) {
- boolean[] transforms = formatData.capitalization.get(capContextUsageType);
- titlecase = (capitalizationContext==DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU)?
- transforms[0]: transforms[1];
- }
- break;
- default:
- break;
- }
+ switch (capitalizationContext) {
+ case CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE:
+ titlecase = true;
+ break;
+ case CAPITALIZATION_FOR_UI_LIST_OR_MENU:
+ case CAPITALIZATION_FOR_STANDALONE:
+ if (formatData.capitalization != null) {
+ boolean[] transforms = formatData.capitalization.get(capContextUsageType);
+ titlecase = (capitalizationContext==DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU)?
+ transforms[0]: transforms[1];
+ }
+ break;
+ default:
+ break;
}
if (titlecase) {
String firstField = buf.substring(bufstart); // bufstart or beginOffset, should be the same
- String firstFieldTitleCase = UCharacter.toTitleCase(locale, firstField, null,
+ String firstFieldTitleCase = UCharacter.toTitleCase(locale, firstField, capitalizationBrkIter,
UCharacter.TITLECASE_NO_LOWERCASE | UCharacter.TITLECASE_NO_BREAK_ADJUSTMENT);
buf.replace(bufstart, buf.length(), firstFieldTitleCase);
}
new TestContextItem( "da", DisplayContext.STANDARD_NAMES, DisplayContext.CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE, "en", "engelsk" ),
new TestContextItem( "da", DisplayContext.STANDARD_NAMES, DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE, "en", "Engelsk" ),
new TestContextItem( "da", DisplayContext.STANDARD_NAMES, DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU, "en", "Engelsk" ),
+ new TestContextItem( "da", DisplayContext.STANDARD_NAMES, DisplayContext.CAPITALIZATION_FOR_STANDALONE, "en", "engelsk" ),
new TestContextItem( "da", DisplayContext.STANDARD_NAMES, DisplayContext.CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE, "en_US", "engelsk (USA)" ),
new TestContextItem( "da", DisplayContext.STANDARD_NAMES, DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE, "en_US", "Engelsk (USA)" ),
new TestContextItem( "da", DisplayContext.STANDARD_NAMES, DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU, "en_US", "Engelsk (USA)" ),
+ new TestContextItem( "da", DisplayContext.STANDARD_NAMES, DisplayContext.CAPITALIZATION_FOR_STANDALONE, "en_US", "engelsk (USA)" ),
new TestContextItem( "da", DisplayContext.DIALECT_NAMES, DisplayContext.CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE, "en_US", "amerikansk engelsk" ),
new TestContextItem( "da", DisplayContext.DIALECT_NAMES, DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE, "en_US", "Amerikansk engelsk" ),
new TestContextItem( "da", DisplayContext.DIALECT_NAMES, DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU, "en_US", "Amerikansk engelsk" ),
+ new TestContextItem( "da", DisplayContext.DIALECT_NAMES, DisplayContext.CAPITALIZATION_FOR_STANDALONE, "en_US", "amerikansk engelsk" ),
new TestContextItem( "es", DisplayContext.STANDARD_NAMES, DisplayContext.CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE, "en", "ingl\u00E9s" ),
new TestContextItem( "es", DisplayContext.STANDARD_NAMES, DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE, "en", "Ingl\u00E9s" ),
new TestContextItem( "es", DisplayContext.STANDARD_NAMES, DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU, "en", "Ingl\u00E9s" ),
+ new TestContextItem( "es", DisplayContext.STANDARD_NAMES, DisplayContext.CAPITALIZATION_FOR_STANDALONE, "en", "Ingl\u00E9s" ),
new TestContextItem( "es", DisplayContext.STANDARD_NAMES, DisplayContext.CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE, "en_US", "ingl\u00E9s (Estados Unidos)" ),
new TestContextItem( "es", DisplayContext.STANDARD_NAMES, DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE, "en_US", "Ingl\u00E9s (Estados Unidos)" ),
new TestContextItem( "es", DisplayContext.STANDARD_NAMES, DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU, "en_US", "Ingl\u00E9s (Estados Unidos)" ),
+ new TestContextItem( "es", DisplayContext.STANDARD_NAMES, DisplayContext.CAPITALIZATION_FOR_STANDALONE, "en_US", "Ingl\u00E9s (Estados Unidos)" ),
new TestContextItem( "es", DisplayContext.DIALECT_NAMES, DisplayContext.CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE, "en_US", "ingl\u00E9s estadounidense" ),
new TestContextItem( "es", DisplayContext.DIALECT_NAMES, DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE, "en_US", "Ingl\u00E9s estadounidense" ),
new TestContextItem( "es", DisplayContext.DIALECT_NAMES, DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU, "en_US", "Ingl\u00E9s estadounidense" ),
+ new TestContextItem( "es", DisplayContext.DIALECT_NAMES, DisplayContext.CAPITALIZATION_FOR_STANDALONE, "en_US", "Ingl\u00E9s estadounidense" ),
};
for (TestContextItem item: items) {
ULocale locale = new ULocale(item.displayLocale);