| |
1 /* |
| |
2 ******************************************************************************* |
| |
3 * Copyright (C) 2010-2013, International Business Machines Corporation and |
| |
4 * others. All Rights Reserved. |
| |
5 ******************************************************************************* |
| |
6 */ |
| |
7 |
| |
8 #include "unicode/utypes.h" |
| |
9 |
| |
10 #if !UCONFIG_NO_FORMATTING |
| |
11 |
| |
12 #include "unicode/locdspnm.h" |
| |
13 #include "unicode/msgfmt.h" |
| |
14 #include "unicode/ures.h" |
| |
15 #include "unicode/brkiter.h" |
| |
16 |
| |
17 #include "cmemory.h" |
| |
18 #include "cstring.h" |
| |
19 #include "ulocimp.h" |
| |
20 #include "ureslocs.h" |
| |
21 #include "uresimp.h" |
| |
22 |
| |
23 #include <stdarg.h> |
| |
24 |
| |
25 /** |
| |
26 * Concatenate a number of null-terminated strings to buffer, leaving a |
| |
27 * null-terminated string. The last argument should be the null pointer. |
| |
28 * Return the length of the string in the buffer, not counting the trailing |
| |
29 * null. Return -1 if there is an error (buffer is null, or buflen < 1). |
| |
30 */ |
| |
31 static int32_t ncat(char *buffer, uint32_t buflen, ...) { |
| |
32 va_list args; |
| |
33 char *str; |
| |
34 char *p = buffer; |
| |
35 const char* e = buffer + buflen - 1; |
| |
36 |
| |
37 if (buffer == NULL || buflen < 1) { |
| |
38 return -1; |
| |
39 } |
| |
40 |
| |
41 va_start(args, buflen); |
| |
42 while ((str = va_arg(args, char *))) { |
| |
43 char c; |
| |
44 while (p != e && (c = *str++)) { |
| |
45 *p++ = c; |
| |
46 } |
| |
47 } |
| |
48 *p = 0; |
| |
49 va_end(args); |
| |
50 |
| |
51 return p - buffer; |
| |
52 } |
| |
53 |
| |
54 U_NAMESPACE_BEGIN |
| |
55 |
| |
56 //////////////////////////////////////////////////////////////////////////////////////////////////// |
| |
57 |
| |
58 // Access resource data for locale components. |
| |
59 // Wrap code in uloc.c for now. |
| |
60 class ICUDataTable { |
| |
61 const char* path; |
| |
62 Locale locale; |
| |
63 |
| |
64 public: |
| |
65 ICUDataTable(const char* path, const Locale& locale); |
| |
66 ~ICUDataTable(); |
| |
67 |
| |
68 const Locale& getLocale(); |
| |
69 |
| |
70 UnicodeString& get(const char* tableKey, const char* itemKey, |
| |
71 UnicodeString& result) const; |
| |
72 UnicodeString& get(const char* tableKey, const char* subTableKey, const char* itemKey, |
| |
73 UnicodeString& result) const; |
| |
74 |
| |
75 UnicodeString& getNoFallback(const char* tableKey, const char* itemKey, |
| |
76 UnicodeString &result) const; |
| |
77 UnicodeString& getNoFallback(const char* tableKey, const char* subTableKey, const char* itemKey, |
| |
78 UnicodeString &result) const; |
| |
79 }; |
| |
80 |
| |
81 inline UnicodeString & |
| |
82 ICUDataTable::get(const char* tableKey, const char* itemKey, UnicodeString& result) const { |
| |
83 return get(tableKey, NULL, itemKey, result); |
| |
84 } |
| |
85 |
| |
86 inline UnicodeString & |
| |
87 ICUDataTable::getNoFallback(const char* tableKey, const char* itemKey, UnicodeString& result) const { |
| |
88 return getNoFallback(tableKey, NULL, itemKey, result); |
| |
89 } |
| |
90 |
| |
91 ICUDataTable::ICUDataTable(const char* path, const Locale& locale) |
| |
92 : path(NULL), locale(Locale::getRoot()) |
| |
93 { |
| |
94 if (path) { |
| |
95 int32_t len = uprv_strlen(path); |
| |
96 this->path = (const char*) uprv_malloc(len + 1); |
| |
97 if (this->path) { |
| |
98 uprv_strcpy((char *)this->path, path); |
| |
99 this->locale = locale; |
| |
100 } |
| |
101 } |
| |
102 } |
| |
103 |
| |
104 ICUDataTable::~ICUDataTable() { |
| |
105 if (path) { |
| |
106 uprv_free((void*) path); |
| |
107 path = NULL; |
| |
108 } |
| |
109 } |
| |
110 |
| |
111 const Locale& |
| |
112 ICUDataTable::getLocale() { |
| |
113 return locale; |
| |
114 } |
| |
115 |
| |
116 UnicodeString & |
| |
117 ICUDataTable::get(const char* tableKey, const char* subTableKey, const char* itemKey, |
| |
118 UnicodeString &result) const { |
| |
119 UErrorCode status = U_ZERO_ERROR; |
| |
120 int32_t len = 0; |
| |
121 |
| |
122 const UChar *s = uloc_getTableStringWithFallback(path, locale.getName(), |
| |
123 tableKey, subTableKey, itemKey, |
| |
124 &len, &status); |
| |
125 if (U_SUCCESS(status) && len > 0) { |
| |
126 return result.setTo(s, len); |
| |
127 } |
| |
128 return result.setTo(UnicodeString(itemKey, -1, US_INV)); |
| |
129 } |
| |
130 |
| |
131 UnicodeString & |
| |
132 ICUDataTable::getNoFallback(const char* tableKey, const char* subTableKey, const char* itemKey, |
| |
133 UnicodeString& result) const { |
| |
134 UErrorCode status = U_ZERO_ERROR; |
| |
135 int32_t len = 0; |
| |
136 |
| |
137 const UChar *s = uloc_getTableStringWithFallback(path, locale.getName(), |
| |
138 tableKey, subTableKey, itemKey, |
| |
139 &len, &status); |
| |
140 if (U_SUCCESS(status)) { |
| |
141 return result.setTo(s, len); |
| |
142 } |
| |
143 |
| |
144 result.setToBogus(); |
| |
145 return result; |
| |
146 } |
| |
147 |
| |
148 //////////////////////////////////////////////////////////////////////////////////////////////////// |
| |
149 |
| |
150 LocaleDisplayNames::~LocaleDisplayNames() {} |
| |
151 |
| |
152 //////////////////////////////////////////////////////////////////////////////////////////////////// |
| |
153 |
| |
154 #if 0 // currently unused |
| |
155 |
| |
156 class DefaultLocaleDisplayNames : public LocaleDisplayNames { |
| |
157 UDialectHandling dialectHandling; |
| |
158 |
| |
159 public: |
| |
160 // constructor |
| |
161 DefaultLocaleDisplayNames(UDialectHandling dialectHandling); |
| |
162 |
| |
163 virtual ~DefaultLocaleDisplayNames(); |
| |
164 |
| |
165 virtual const Locale& getLocale() const; |
| |
166 virtual UDialectHandling getDialectHandling() const; |
| |
167 |
| |
168 virtual UnicodeString& localeDisplayName(const Locale& locale, |
| |
169 UnicodeString& result) const; |
| |
170 virtual UnicodeString& localeDisplayName(const char* localeId, |
| |
171 UnicodeString& result) const; |
| |
172 virtual UnicodeString& languageDisplayName(const char* lang, |
| |
173 UnicodeString& result) const; |
| |
174 virtual UnicodeString& scriptDisplayName(const char* script, |
| |
175 UnicodeString& result) const; |
| |
176 virtual UnicodeString& scriptDisplayName(UScriptCode scriptCode, |
| |
177 UnicodeString& result) const; |
| |
178 virtual UnicodeString& regionDisplayName(const char* region, |
| |
179 UnicodeString& result) const; |
| |
180 virtual UnicodeString& variantDisplayName(const char* variant, |
| |
181 UnicodeString& result) const; |
| |
182 virtual UnicodeString& keyDisplayName(const char* key, |
| |
183 UnicodeString& result) const; |
| |
184 virtual UnicodeString& keyValueDisplayName(const char* key, |
| |
185 const char* value, |
| |
186 UnicodeString& result) const; |
| |
187 }; |
| |
188 |
| |
189 DefaultLocaleDisplayNames::DefaultLocaleDisplayNames(UDialectHandling dialectHandling) |
| |
190 : dialectHandling(dialectHandling) { |
| |
191 } |
| |
192 |
| |
193 DefaultLocaleDisplayNames::~DefaultLocaleDisplayNames() { |
| |
194 } |
| |
195 |
| |
196 const Locale& |
| |
197 DefaultLocaleDisplayNames::getLocale() const { |
| |
198 return Locale::getRoot(); |
| |
199 } |
| |
200 |
| |
201 UDialectHandling |
| |
202 DefaultLocaleDisplayNames::getDialectHandling() const { |
| |
203 return dialectHandling; |
| |
204 } |
| |
205 |
| |
206 UnicodeString& |
| |
207 DefaultLocaleDisplayNames::localeDisplayName(const Locale& locale, |
| |
208 UnicodeString& result) const { |
| |
209 return result = UnicodeString(locale.getName(), -1, US_INV); |
| |
210 } |
| |
211 |
| |
212 UnicodeString& |
| |
213 DefaultLocaleDisplayNames::localeDisplayName(const char* localeId, |
| |
214 UnicodeString& result) const { |
| |
215 return result = UnicodeString(localeId, -1, US_INV); |
| |
216 } |
| |
217 |
| |
218 UnicodeString& |
| |
219 DefaultLocaleDisplayNames::languageDisplayName(const char* lang, |
| |
220 UnicodeString& result) const { |
| |
221 return result = UnicodeString(lang, -1, US_INV); |
| |
222 } |
| |
223 |
| |
224 UnicodeString& |
| |
225 DefaultLocaleDisplayNames::scriptDisplayName(const char* script, |
| |
226 UnicodeString& result) const { |
| |
227 return result = UnicodeString(script, -1, US_INV); |
| |
228 } |
| |
229 |
| |
230 UnicodeString& |
| |
231 DefaultLocaleDisplayNames::scriptDisplayName(UScriptCode scriptCode, |
| |
232 UnicodeString& result) const { |
| |
233 const char* name = uscript_getName(scriptCode); |
| |
234 if (name) { |
| |
235 return result = UnicodeString(name, -1, US_INV); |
| |
236 } |
| |
237 return result.remove(); |
| |
238 } |
| |
239 |
| |
240 UnicodeString& |
| |
241 DefaultLocaleDisplayNames::regionDisplayName(const char* region, |
| |
242 UnicodeString& result) const { |
| |
243 return result = UnicodeString(region, -1, US_INV); |
| |
244 } |
| |
245 |
| |
246 UnicodeString& |
| |
247 DefaultLocaleDisplayNames::variantDisplayName(const char* variant, |
| |
248 UnicodeString& result) const { |
| |
249 return result = UnicodeString(variant, -1, US_INV); |
| |
250 } |
| |
251 |
| |
252 UnicodeString& |
| |
253 DefaultLocaleDisplayNames::keyDisplayName(const char* key, |
| |
254 UnicodeString& result) const { |
| |
255 return result = UnicodeString(key, -1, US_INV); |
| |
256 } |
| |
257 |
| |
258 UnicodeString& |
| |
259 DefaultLocaleDisplayNames::keyValueDisplayName(const char* /* key */, |
| |
260 const char* value, |
| |
261 UnicodeString& result) const { |
| |
262 return result = UnicodeString(value, -1, US_INV); |
| |
263 } |
| |
264 |
| |
265 #endif // currently unused class DefaultLocaleDisplayNames |
| |
266 |
| |
267 //////////////////////////////////////////////////////////////////////////////////////////////////// |
| |
268 |
| |
269 class LocaleDisplayNamesImpl : public LocaleDisplayNames { |
| |
270 Locale locale; |
| |
271 UDialectHandling dialectHandling; |
| |
272 ICUDataTable langData; |
| |
273 ICUDataTable regionData; |
| |
274 MessageFormat *separatorFormat; |
| |
275 MessageFormat *format; |
| |
276 MessageFormat *keyTypeFormat; |
| |
277 UDisplayContext capitalizationContext; |
| |
278 UnicodeString formatOpenParen; |
| |
279 UnicodeString formatReplaceOpenParen; |
| |
280 UnicodeString formatCloseParen; |
| |
281 UnicodeString formatReplaceCloseParen; |
| |
282 |
| |
283 // Constants for capitalization context usage types. |
| |
284 enum CapContextUsage { |
| |
285 kCapContextUsageLanguage, |
| |
286 kCapContextUsageScript, |
| |
287 kCapContextUsageTerritory, |
| |
288 kCapContextUsageVariant, |
| |
289 kCapContextUsageKey, |
| |
290 kCapContextUsageType, |
| |
291 kCapContextUsageCount |
| |
292 }; |
| |
293 // Capitalization transforms. For each usage type, the first array element indicates |
| |
294 // whether to titlecase for uiListOrMenu context, the second indicates whether to |
| |
295 // titlecase for stand-alone context. |
| |
296 UBool fCapitalization[kCapContextUsageCount][2]; |
| |
297 |
| |
298 public: |
| |
299 // constructor |
| |
300 LocaleDisplayNamesImpl(const Locale& locale, UDialectHandling dialectHandling); |
| |
301 LocaleDisplayNamesImpl(const Locale& locale, UDisplayContext *contexts, int32_t length); |
| |
302 virtual ~LocaleDisplayNamesImpl(); |
| |
303 |
| |
304 virtual const Locale& getLocale() const; |
| |
305 virtual UDialectHandling getDialectHandling() const; |
| |
306 virtual UDisplayContext getContext(UDisplayContextType type) const; |
| |
307 |
| |
308 virtual UnicodeString& localeDisplayName(const Locale& locale, |
| |
309 UnicodeString& result) const; |
| |
310 virtual UnicodeString& localeDisplayName(const char* localeId, |
| |
311 UnicodeString& result) const; |
| |
312 virtual UnicodeString& languageDisplayName(const char* lang, |
| |
313 UnicodeString& result) const; |
| |
314 virtual UnicodeString& scriptDisplayName(const char* script, |
| |
315 UnicodeString& result) const; |
| |
316 virtual UnicodeString& scriptDisplayName(UScriptCode scriptCode, |
| |
317 UnicodeString& result) const; |
| |
318 virtual UnicodeString& regionDisplayName(const char* region, |
| |
319 UnicodeString& result) const; |
| |
320 virtual UnicodeString& variantDisplayName(const char* variant, |
| |
321 UnicodeString& result) const; |
| |
322 virtual UnicodeString& keyDisplayName(const char* key, |
| |
323 UnicodeString& result) const; |
| |
324 virtual UnicodeString& keyValueDisplayName(const char* key, |
| |
325 const char* value, |
| |
326 UnicodeString& result) const; |
| |
327 private: |
| |
328 UnicodeString& localeIdName(const char* localeId, |
| |
329 UnicodeString& result) const; |
| |
330 UnicodeString& appendWithSep(UnicodeString& buffer, const UnicodeString& src) const; |
| |
331 UnicodeString& adjustForUsageAndContext(CapContextUsage usage, UnicodeString& result) const; |
| |
332 void initialize(void); |
| |
333 }; |
| |
334 |
| |
335 LocaleDisplayNamesImpl::LocaleDisplayNamesImpl(const Locale& locale, |
| |
336 UDialectHandling dialectHandling) |
| |
337 : dialectHandling(dialectHandling) |
| |
338 , langData(U_ICUDATA_LANG, locale) |
| |
339 , regionData(U_ICUDATA_REGION, locale) |
| |
340 , separatorFormat(NULL) |
| |
341 , format(NULL) |
| |
342 , keyTypeFormat(NULL) |
| |
343 , capitalizationContext(UDISPCTX_CAPITALIZATION_NONE) |
| |
344 { |
| |
345 initialize(); |
| |
346 } |
| |
347 |
| |
348 LocaleDisplayNamesImpl::LocaleDisplayNamesImpl(const Locale& locale, |
| |
349 UDisplayContext *contexts, int32_t length) |
| |
350 : dialectHandling(ULDN_STANDARD_NAMES) |
| |
351 , langData(U_ICUDATA_LANG, locale) |
| |
352 , regionData(U_ICUDATA_REGION, locale) |
| |
353 , separatorFormat(NULL) |
| |
354 , format(NULL) |
| |
355 , keyTypeFormat(NULL) |
| |
356 , capitalizationContext(UDISPCTX_CAPITALIZATION_NONE) |
| |
357 { |
| |
358 while (length-- > 0) { |
| |
359 UDisplayContext value = *contexts++; |
| |
360 UDisplayContextType selector = (UDisplayContextType)((uint32_t)value >> 8); |
| |
361 switch (selector) { |
| |
362 case UDISPCTX_TYPE_DIALECT_HANDLING: |
| |
363 dialectHandling = (UDialectHandling)value; |
| |
364 break; |
| |
365 case UDISPCTX_TYPE_CAPITALIZATION: |
| |
366 capitalizationContext = value; |
| |
367 break; |
| |
368 default: |
| |
369 break; |
| |
370 } |
| |
371 } |
| |
372 initialize(); |
| |
373 } |
| |
374 |
| |
375 void |
| |
376 LocaleDisplayNamesImpl::initialize(void) { |
| |
377 LocaleDisplayNamesImpl *nonConstThis = (LocaleDisplayNamesImpl *)this; |
| |
378 nonConstThis->locale = langData.getLocale() == Locale::getRoot() |
| |
379 ? regionData.getLocale() |
| |
380 : langData.getLocale(); |
| |
381 |
| |
382 UnicodeString sep; |
| |
383 langData.getNoFallback("localeDisplayPattern", "separator", sep); |
| |
384 if (sep.isBogus()) { |
| |
385 sep = UnicodeString("{0}, {1}", -1, US_INV); |
| |
386 } |
| |
387 UErrorCode status = U_ZERO_ERROR; |
| |
388 separatorFormat = new MessageFormat(sep, status); |
| |
389 |
| |
390 UnicodeString pattern; |
| |
391 langData.getNoFallback("localeDisplayPattern", "pattern", pattern); |
| |
392 if (pattern.isBogus()) { |
| |
393 pattern = UnicodeString("{0} ({1})", -1, US_INV); |
| |
394 } |
| |
395 format = new MessageFormat(pattern, status); |
| |
396 if (pattern.indexOf((UChar)0xFF08) >= 0) { |
| |
397 formatOpenParen.setTo((UChar)0xFF08); // fullwidth ( |
| |
398 formatReplaceOpenParen.setTo((UChar)0xFF3B); // fullwidth [ |
| |
399 formatCloseParen.setTo((UChar)0xFF09); // fullwidth ) |
| |
400 formatReplaceCloseParen.setTo((UChar)0xFF3D); // fullwidth ] |
| |
401 } else { |
| |
402 formatOpenParen.setTo((UChar)0x0028); // ( |
| |
403 formatReplaceOpenParen.setTo((UChar)0x005B); // [ |
| |
404 formatCloseParen.setTo((UChar)0x0029); // ) |
| |
405 formatReplaceCloseParen.setTo((UChar)0x005D); // ] |
| |
406 } |
| |
407 |
| |
408 UnicodeString ktPattern; |
| |
409 langData.get("localeDisplayPattern", "keyTypePattern", ktPattern); |
| |
410 if (ktPattern.isBogus()) { |
| |
411 ktPattern = UnicodeString("{0}={1}", -1, US_INV); |
| |
412 } |
| |
413 keyTypeFormat = new MessageFormat(ktPattern, status); |
| |
414 |
| |
415 uprv_memset(fCapitalization, 0, sizeof(fCapitalization)); |
| |
416 #if !UCONFIG_NO_BREAK_ITERATION |
| |
417 // The following is basically copied from DateFormatSymbols::initializeData |
| |
418 typedef struct { |
| |
419 const char * usageName; |
| |
420 LocaleDisplayNamesImpl::CapContextUsage usageEnum; |
| |
421 } ContextUsageNameToEnum; |
| |
422 const ContextUsageNameToEnum contextUsageTypeMap[] = { |
| |
423 // Entries must be sorted by usageTypeName; entry with NULL name terminates list. |
| |
424 { "key", kCapContextUsageKey }, |
| |
425 { "languages", kCapContextUsageLanguage }, |
| |
426 { "script", kCapContextUsageScript }, |
| |
427 { "territory", kCapContextUsageTerritory }, |
| |
428 { "type", kCapContextUsageType }, |
| |
429 { "variant", kCapContextUsageVariant }, |
| |
430 { NULL, (CapContextUsage)0 }, |
| |
431 }; |
| |
432 int32_t len = 0; |
| |
433 UResourceBundle *localeBundle = ures_open(NULL, locale.getName(), &status); |
| |
434 if (U_SUCCESS(status)) { |
| |
435 UResourceBundle *contextTransforms = ures_getByKeyWithFallback(localeBundle, "contextTransforms", NULL, &status); |
| |
436 if (U_SUCCESS(status)) { |
| |
437 UResourceBundle *contextTransformUsage; |
| |
438 while ( (contextTransformUsage = ures_getNextResource(contextTransforms, NULL, &status)) != NULL ) { |
| |
439 const int32_t * intVector = ures_getIntVector(contextTransformUsage, &len, &status); |
| |
440 if (U_SUCCESS(status) && intVector != NULL && len >= 2) { |
| |
441 const char* usageKey = ures_getKey(contextTransformUsage); |
| |
442 if (usageKey != NULL) { |
| |
443 const ContextUsageNameToEnum * typeMapPtr = contextUsageTypeMap; |
| |
444 int32_t compResult = 0; |
| |
445 // linear search; list is short and we cannot be sure that bsearch is available |
| |
446 while ( typeMapPtr->usageName != NULL && (compResult = uprv_strcmp(usageKey, typeMapPtr->usageName)) > 0 ) { |
| |
447 ++typeMapPtr; |
| |
448 } |
| |
449 if (typeMapPtr->usageName != NULL && compResult == 0) { |
| |
450 fCapitalization[typeMapPtr->usageEnum][0] = intVector[0]; |
| |
451 fCapitalization[typeMapPtr->usageEnum][1] = intVector[1]; |
| |
452 } |
| |
453 } |
| |
454 } |
| |
455 status = U_ZERO_ERROR; |
| |
456 ures_close(contextTransformUsage); |
| |
457 } |
| |
458 ures_close(contextTransforms); |
| |
459 } |
| |
460 ures_close(localeBundle); |
| |
461 } |
| |
462 #endif |
| |
463 } |
| |
464 |
| |
465 LocaleDisplayNamesImpl::~LocaleDisplayNamesImpl() { |
| |
466 delete separatorFormat; |
| |
467 delete format; |
| |
468 delete keyTypeFormat; |
| |
469 } |
| |
470 |
| |
471 const Locale& |
| |
472 LocaleDisplayNamesImpl::getLocale() const { |
| |
473 return locale; |
| |
474 } |
| |
475 |
| |
476 UDialectHandling |
| |
477 LocaleDisplayNamesImpl::getDialectHandling() const { |
| |
478 return dialectHandling; |
| |
479 } |
| |
480 |
| |
481 UDisplayContext |
| |
482 LocaleDisplayNamesImpl::getContext(UDisplayContextType type) const { |
| |
483 switch (type) { |
| |
484 case UDISPCTX_TYPE_DIALECT_HANDLING: |
| |
485 return (UDisplayContext)dialectHandling; |
| |
486 case UDISPCTX_TYPE_CAPITALIZATION: |
| |
487 return capitalizationContext; |
| |
488 default: |
| |
489 break; |
| |
490 } |
| |
491 return (UDisplayContext)0; |
| |
492 } |
| |
493 |
| |
494 UnicodeString& |
| |
495 LocaleDisplayNamesImpl::adjustForUsageAndContext(CapContextUsage usage, |
| |
496 UnicodeString& result) const { |
| |
497 #if !UCONFIG_NO_BREAK_ITERATION |
| |
498 // check to see whether we need to titlecase result |
| |
499 UBool titlecase = FALSE; |
| |
500 switch (capitalizationContext) { |
| |
501 case UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE: |
| |
502 titlecase = TRUE; |
| |
503 break; |
| |
504 case UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU: |
| |
505 titlecase = fCapitalization[usage][0]; |
| |
506 break; |
| |
507 case UDISPCTX_CAPITALIZATION_FOR_STANDALONE: |
| |
508 titlecase = fCapitalization[usage][1]; |
| |
509 break; |
| |
510 default: |
| |
511 // titlecase = FALSE; |
| |
512 break; |
| |
513 } |
| |
514 if (titlecase) { |
| |
515 // TODO: Fix this titlecase hack when we figure out something better to do. |
| |
516 // We don't want to titlecase the whole text, only something like the first word, |
| |
517 // of the first segment long enough to have a complete cluster, whichever is |
| |
518 // shorter. We could have keep a word break iterator around, but I am not sure |
| |
519 // that will do the ight thing for the purposes here. For now we assume that in |
| |
520 // languages for which titlecasing makes a difference, we can stop at non-letter |
| |
521 // characters in 0x0000-0x00FF and only titlecase up to the first occurrence of |
| |
522 // any of those, or to a small number of chars, whichever comes first. |
| |
523 int32_t stopPos, stopPosLimit = 8, len = result.length(); |
| |
524 if ( stopPosLimit > len ) { |
| |
525 stopPosLimit = len; |
| |
526 } |
| |
527 for ( stopPos = 0; stopPos < stopPosLimit; stopPos++ ) { |
| |
528 UChar32 ch = result.char32At(stopPos); |
| |
529 if ( (ch < 0x41) || (ch > 0x5A && ch < 0x61) || (ch > 0x7A && ch < 0xC0) ) { |
| |
530 break; |
| |
531 } |
| |
532 if (ch >= 0x10000) { |
| |
533 stopPos++; |
| |
534 } |
| |
535 } |
| |
536 if ( stopPos > 0 && stopPos < len ) { |
| |
537 UnicodeString firstWord(result, 0, stopPos); |
| |
538 firstWord.toTitle(NULL, locale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT); |
| |
539 result.replaceBetween(0, stopPos, firstWord); |
| |
540 } else { |
| |
541 // no stopPos, titlecase the whole text |
| |
542 result.toTitle(NULL, locale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT); |
| |
543 } |
| |
544 } |
| |
545 #endif |
| |
546 return result; |
| |
547 } |
| |
548 |
| |
549 UnicodeString& |
| |
550 LocaleDisplayNamesImpl::localeDisplayName(const Locale& locale, |
| |
551 UnicodeString& result) const { |
| |
552 UnicodeString resultName; |
| |
553 |
| |
554 const char* lang = locale.getLanguage(); |
| |
555 if (uprv_strlen(lang) == 0) { |
| |
556 lang = "root"; |
| |
557 } |
| |
558 const char* script = locale.getScript(); |
| |
559 const char* country = locale.getCountry(); |
| |
560 const char* variant = locale.getVariant(); |
| |
561 |
| |
562 UBool hasScript = uprv_strlen(script) > 0; |
| |
563 UBool hasCountry = uprv_strlen(country) > 0; |
| |
564 UBool hasVariant = uprv_strlen(variant) > 0; |
| |
565 |
| |
566 if (dialectHandling == ULDN_DIALECT_NAMES) { |
| |
567 char buffer[ULOC_FULLNAME_CAPACITY]; |
| |
568 do { // loop construct is so we can break early out of search |
| |
569 if (hasScript && hasCountry) { |
| |
570 ncat(buffer, ULOC_FULLNAME_CAPACITY, lang, "_", script, "_", country, (char *)0); |
| |
571 localeIdName(buffer, resultName); |
| |
572 if (!resultName.isBogus()) { |
| |
573 hasScript = FALSE; |
| |
574 hasCountry = FALSE; |
| |
575 break; |
| |
576 } |
| |
577 } |
| |
578 if (hasScript) { |
| |
579 ncat(buffer, ULOC_FULLNAME_CAPACITY, lang, "_", script, (char *)0); |
| |
580 localeIdName(buffer, resultName); |
| |
581 if (!resultName.isBogus()) { |
| |
582 hasScript = FALSE; |
| |
583 break; |
| |
584 } |
| |
585 } |
| |
586 if (hasCountry) { |
| |
587 ncat(buffer, ULOC_FULLNAME_CAPACITY, lang, "_", country, (char*)0); |
| |
588 localeIdName(buffer, resultName); |
| |
589 if (!resultName.isBogus()) { |
| |
590 hasCountry = FALSE; |
| |
591 break; |
| |
592 } |
| |
593 } |
| |
594 } while (FALSE); |
| |
595 } |
| |
596 if (resultName.isBogus() || resultName.isEmpty()) { |
| |
597 localeIdName(lang, resultName); |
| |
598 } |
| |
599 |
| |
600 UnicodeString resultRemainder; |
| |
601 UnicodeString temp; |
| |
602 StringEnumeration *e = NULL; |
| |
603 UErrorCode status = U_ZERO_ERROR; |
| |
604 |
| |
605 if (hasScript) { |
| |
606 resultRemainder.append(scriptDisplayName(script, temp)); |
| |
607 } |
| |
608 if (hasCountry) { |
| |
609 appendWithSep(resultRemainder, regionDisplayName(country, temp)); |
| |
610 } |
| |
611 if (hasVariant) { |
| |
612 appendWithSep(resultRemainder, variantDisplayName(variant, temp)); |
| |
613 } |
| |
614 resultRemainder.findAndReplace(formatOpenParen, formatReplaceOpenParen); |
| |
615 resultRemainder.findAndReplace(formatCloseParen, formatReplaceCloseParen); |
| |
616 |
| |
617 e = locale.createKeywords(status); |
| |
618 if (e && U_SUCCESS(status)) { |
| |
619 UnicodeString temp2; |
| |
620 char value[ULOC_KEYWORD_AND_VALUES_CAPACITY]; // sigh, no ULOC_VALUE_CAPACITY |
| |
621 const char* key; |
| |
622 while ((key = e->next((int32_t *)0, status)) != NULL) { |
| |
623 locale.getKeywordValue(key, value, ULOC_KEYWORD_AND_VALUES_CAPACITY, status); |
| |
624 keyDisplayName(key, temp); |
| |
625 temp.findAndReplace(formatOpenParen, formatReplaceOpenParen); |
| |
626 temp.findAndReplace(formatCloseParen, formatReplaceCloseParen); |
| |
627 keyValueDisplayName(key, value, temp2); |
| |
628 temp2.findAndReplace(formatOpenParen, formatReplaceOpenParen); |
| |
629 temp2.findAndReplace(formatCloseParen, formatReplaceCloseParen); |
| |
630 if (temp2 != UnicodeString(value, -1, US_INV)) { |
| |
631 appendWithSep(resultRemainder, temp2); |
| |
632 } else if (temp != UnicodeString(key, -1, US_INV)) { |
| |
633 UnicodeString temp3; |
| |
634 Formattable data[] = { |
| |
635 temp, |
| |
636 temp2 |
| |
637 }; |
| |
638 FieldPosition fpos; |
| |
639 status = U_ZERO_ERROR; |
| |
640 keyTypeFormat->format(data, 2, temp3, fpos, status); |
| |
641 appendWithSep(resultRemainder, temp3); |
| |
642 } else { |
| |
643 appendWithSep(resultRemainder, temp) |
| |
644 .append((UChar)0x3d /* = */) |
| |
645 .append(temp2); |
| |
646 } |
| |
647 } |
| |
648 delete e; |
| |
649 } |
| |
650 |
| |
651 if (!resultRemainder.isEmpty()) { |
| |
652 Formattable data[] = { |
| |
653 resultName, |
| |
654 resultRemainder |
| |
655 }; |
| |
656 FieldPosition fpos; |
| |
657 status = U_ZERO_ERROR; |
| |
658 format->format(data, 2, result, fpos, status); |
| |
659 return adjustForUsageAndContext(kCapContextUsageLanguage, result); |
| |
660 } |
| |
661 |
| |
662 result = resultName; |
| |
663 return adjustForUsageAndContext(kCapContextUsageLanguage, result); |
| |
664 } |
| |
665 |
| |
666 UnicodeString& |
| |
667 LocaleDisplayNamesImpl::appendWithSep(UnicodeString& buffer, const UnicodeString& src) const { |
| |
668 if (buffer.isEmpty()) { |
| |
669 buffer.setTo(src); |
| |
670 } else { |
| |
671 UnicodeString combined; |
| |
672 Formattable data[] = { |
| |
673 buffer, |
| |
674 src |
| |
675 }; |
| |
676 FieldPosition fpos; |
| |
677 UErrorCode status = U_ZERO_ERROR; |
| |
678 separatorFormat->format(data, 2, combined, fpos, status); |
| |
679 if (U_SUCCESS(status)) { |
| |
680 buffer.setTo(combined); |
| |
681 } |
| |
682 } |
| |
683 return buffer; |
| |
684 } |
| |
685 |
| |
686 UnicodeString& |
| |
687 LocaleDisplayNamesImpl::localeDisplayName(const char* localeId, |
| |
688 UnicodeString& result) const { |
| |
689 return localeDisplayName(Locale(localeId), result); |
| |
690 } |
| |
691 |
| |
692 // private |
| |
693 UnicodeString& |
| |
694 LocaleDisplayNamesImpl::localeIdName(const char* localeId, |
| |
695 UnicodeString& result) const { |
| |
696 return langData.getNoFallback("Languages", localeId, result); |
| |
697 } |
| |
698 |
| |
699 UnicodeString& |
| |
700 LocaleDisplayNamesImpl::languageDisplayName(const char* lang, |
| |
701 UnicodeString& result) const { |
| |
702 if (uprv_strcmp("root", lang) == 0 || uprv_strchr(lang, '_') != NULL) { |
| |
703 return result = UnicodeString(lang, -1, US_INV); |
| |
704 } |
| |
705 langData.get("Languages", lang, result); |
| |
706 return adjustForUsageAndContext(kCapContextUsageLanguage, result); |
| |
707 } |
| |
708 |
| |
709 UnicodeString& |
| |
710 LocaleDisplayNamesImpl::scriptDisplayName(const char* script, |
| |
711 UnicodeString& result) const { |
| |
712 langData.get("Scripts", script, result); |
| |
713 return adjustForUsageAndContext(kCapContextUsageScript, result); |
| |
714 } |
| |
715 |
| |
716 UnicodeString& |
| |
717 LocaleDisplayNamesImpl::scriptDisplayName(UScriptCode scriptCode, |
| |
718 UnicodeString& result) const { |
| |
719 const char* name = uscript_getName(scriptCode); |
| |
720 langData.get("Scripts", name, result); |
| |
721 return adjustForUsageAndContext(kCapContextUsageScript, result); |
| |
722 } |
| |
723 |
| |
724 UnicodeString& |
| |
725 LocaleDisplayNamesImpl::regionDisplayName(const char* region, |
| |
726 UnicodeString& result) const { |
| |
727 regionData.get("Countries", region, result); |
| |
728 return adjustForUsageAndContext(kCapContextUsageTerritory, result); |
| |
729 } |
| |
730 |
| |
731 UnicodeString& |
| |
732 LocaleDisplayNamesImpl::variantDisplayName(const char* variant, |
| |
733 UnicodeString& result) const { |
| |
734 langData.get("Variants", variant, result); |
| |
735 return adjustForUsageAndContext(kCapContextUsageVariant, result); |
| |
736 } |
| |
737 |
| |
738 UnicodeString& |
| |
739 LocaleDisplayNamesImpl::keyDisplayName(const char* key, |
| |
740 UnicodeString& result) const { |
| |
741 langData.get("Keys", key, result); |
| |
742 return adjustForUsageAndContext(kCapContextUsageKey, result); |
| |
743 } |
| |
744 |
| |
745 UnicodeString& |
| |
746 LocaleDisplayNamesImpl::keyValueDisplayName(const char* key, |
| |
747 const char* value, |
| |
748 UnicodeString& result) const { |
| |
749 langData.get("Types", key, value, result); |
| |
750 return adjustForUsageAndContext(kCapContextUsageType, result); |
| |
751 } |
| |
752 |
| |
753 //////////////////////////////////////////////////////////////////////////////////////////////////// |
| |
754 |
| |
755 LocaleDisplayNames* |
| |
756 LocaleDisplayNames::createInstance(const Locale& locale, |
| |
757 UDialectHandling dialectHandling) { |
| |
758 return new LocaleDisplayNamesImpl(locale, dialectHandling); |
| |
759 } |
| |
760 |
| |
761 LocaleDisplayNames* |
| |
762 LocaleDisplayNames::createInstance(const Locale& locale, |
| |
763 UDisplayContext *contexts, int32_t length) { |
| |
764 if (contexts == NULL) { |
| |
765 length = 0; |
| |
766 } |
| |
767 return new LocaleDisplayNamesImpl(locale, contexts, length); |
| |
768 } |
| |
769 |
| |
770 U_NAMESPACE_END |
| |
771 |
| |
772 //////////////////////////////////////////////////////////////////////////////////////////////////// |
| |
773 |
| |
774 U_NAMESPACE_USE |
| |
775 |
| |
776 U_CAPI ULocaleDisplayNames * U_EXPORT2 |
| |
777 uldn_open(const char * locale, |
| |
778 UDialectHandling dialectHandling, |
| |
779 UErrorCode *pErrorCode) { |
| |
780 if (U_FAILURE(*pErrorCode)) { |
| |
781 return 0; |
| |
782 } |
| |
783 if (locale == NULL) { |
| |
784 locale = uloc_getDefault(); |
| |
785 } |
| |
786 return (ULocaleDisplayNames *)LocaleDisplayNames::createInstance(Locale(locale), dialectHandling); |
| |
787 } |
| |
788 |
| |
789 U_CAPI ULocaleDisplayNames * U_EXPORT2 |
| |
790 uldn_openForContext(const char * locale, |
| |
791 UDisplayContext *contexts, int32_t length, |
| |
792 UErrorCode *pErrorCode) { |
| |
793 if (U_FAILURE(*pErrorCode)) { |
| |
794 return 0; |
| |
795 } |
| |
796 if (locale == NULL) { |
| |
797 locale = uloc_getDefault(); |
| |
798 } |
| |
799 return (ULocaleDisplayNames *)LocaleDisplayNames::createInstance(Locale(locale), contexts, length); |
| |
800 } |
| |
801 |
| |
802 |
| |
803 U_CAPI void U_EXPORT2 |
| |
804 uldn_close(ULocaleDisplayNames *ldn) { |
| |
805 delete (LocaleDisplayNames *)ldn; |
| |
806 } |
| |
807 |
| |
808 U_CAPI const char * U_EXPORT2 |
| |
809 uldn_getLocale(const ULocaleDisplayNames *ldn) { |
| |
810 if (ldn) { |
| |
811 return ((const LocaleDisplayNames *)ldn)->getLocale().getName(); |
| |
812 } |
| |
813 return NULL; |
| |
814 } |
| |
815 |
| |
816 U_CAPI UDialectHandling U_EXPORT2 |
| |
817 uldn_getDialectHandling(const ULocaleDisplayNames *ldn) { |
| |
818 if (ldn) { |
| |
819 return ((const LocaleDisplayNames *)ldn)->getDialectHandling(); |
| |
820 } |
| |
821 return ULDN_STANDARD_NAMES; |
| |
822 } |
| |
823 |
| |
824 U_CAPI UDisplayContext U_EXPORT2 |
| |
825 uldn_getContext(const ULocaleDisplayNames *ldn, |
| |
826 UDisplayContextType type, |
| |
827 UErrorCode *pErrorCode) { |
| |
828 if (U_FAILURE(*pErrorCode)) { |
| |
829 return (UDisplayContext)0; |
| |
830 } |
| |
831 return ((const LocaleDisplayNames *)ldn)->getContext(type); |
| |
832 } |
| |
833 |
| |
834 U_CAPI int32_t U_EXPORT2 |
| |
835 uldn_localeDisplayName(const ULocaleDisplayNames *ldn, |
| |
836 const char *locale, |
| |
837 UChar *result, |
| |
838 int32_t maxResultSize, |
| |
839 UErrorCode *pErrorCode) { |
| |
840 if (U_FAILURE(*pErrorCode)) { |
| |
841 return 0; |
| |
842 } |
| |
843 if (ldn == NULL || locale == NULL || (result == NULL && maxResultSize > 0) || maxResultSize < 0) { |
| |
844 *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; |
| |
845 return 0; |
| |
846 } |
| |
847 UnicodeString temp(result, 0, maxResultSize); |
| |
848 ((const LocaleDisplayNames *)ldn)->localeDisplayName(locale, temp); |
| |
849 return temp.extract(result, maxResultSize, *pErrorCode); |
| |
850 } |
| |
851 |
| |
852 U_CAPI int32_t U_EXPORT2 |
| |
853 uldn_languageDisplayName(const ULocaleDisplayNames *ldn, |
| |
854 const char *lang, |
| |
855 UChar *result, |
| |
856 int32_t maxResultSize, |
| |
857 UErrorCode *pErrorCode) { |
| |
858 if (U_FAILURE(*pErrorCode)) { |
| |
859 return 0; |
| |
860 } |
| |
861 if (ldn == NULL || lang == NULL || (result == NULL && maxResultSize > 0) || maxResultSize < 0) { |
| |
862 *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; |
| |
863 return 0; |
| |
864 } |
| |
865 UnicodeString temp(result, 0, maxResultSize); |
| |
866 ((const LocaleDisplayNames *)ldn)->languageDisplayName(lang, temp); |
| |
867 return temp.extract(result, maxResultSize, *pErrorCode); |
| |
868 } |
| |
869 |
| |
870 U_CAPI int32_t U_EXPORT2 |
| |
871 uldn_scriptDisplayName(const ULocaleDisplayNames *ldn, |
| |
872 const char *script, |
| |
873 UChar *result, |
| |
874 int32_t maxResultSize, |
| |
875 UErrorCode *pErrorCode) { |
| |
876 if (U_FAILURE(*pErrorCode)) { |
| |
877 return 0; |
| |
878 } |
| |
879 if (ldn == NULL || script == NULL || (result == NULL && maxResultSize > 0) || maxResultSize < 0) { |
| |
880 *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; |
| |
881 return 0; |
| |
882 } |
| |
883 UnicodeString temp(result, 0, maxResultSize); |
| |
884 ((const LocaleDisplayNames *)ldn)->scriptDisplayName(script, temp); |
| |
885 return temp.extract(result, maxResultSize, *pErrorCode); |
| |
886 } |
| |
887 |
| |
888 U_CAPI int32_t U_EXPORT2 |
| |
889 uldn_scriptCodeDisplayName(const ULocaleDisplayNames *ldn, |
| |
890 UScriptCode scriptCode, |
| |
891 UChar *result, |
| |
892 int32_t maxResultSize, |
| |
893 UErrorCode *pErrorCode) { |
| |
894 return uldn_scriptDisplayName(ldn, uscript_getName(scriptCode), result, maxResultSize, pErrorCode); |
| |
895 } |
| |
896 |
| |
897 U_CAPI int32_t U_EXPORT2 |
| |
898 uldn_regionDisplayName(const ULocaleDisplayNames *ldn, |
| |
899 const char *region, |
| |
900 UChar *result, |
| |
901 int32_t maxResultSize, |
| |
902 UErrorCode *pErrorCode) { |
| |
903 if (U_FAILURE(*pErrorCode)) { |
| |
904 return 0; |
| |
905 } |
| |
906 if (ldn == NULL || region == NULL || (result == NULL && maxResultSize > 0) || maxResultSize < 0) { |
| |
907 *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; |
| |
908 return 0; |
| |
909 } |
| |
910 UnicodeString temp(result, 0, maxResultSize); |
| |
911 ((const LocaleDisplayNames *)ldn)->regionDisplayName(region, temp); |
| |
912 return temp.extract(result, maxResultSize, *pErrorCode); |
| |
913 } |
| |
914 |
| |
915 U_CAPI int32_t U_EXPORT2 |
| |
916 uldn_variantDisplayName(const ULocaleDisplayNames *ldn, |
| |
917 const char *variant, |
| |
918 UChar *result, |
| |
919 int32_t maxResultSize, |
| |
920 UErrorCode *pErrorCode) { |
| |
921 if (U_FAILURE(*pErrorCode)) { |
| |
922 return 0; |
| |
923 } |
| |
924 if (ldn == NULL || variant == NULL || (result == NULL && maxResultSize > 0) || maxResultSize < 0) { |
| |
925 *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; |
| |
926 return 0; |
| |
927 } |
| |
928 UnicodeString temp(result, 0, maxResultSize); |
| |
929 ((const LocaleDisplayNames *)ldn)->variantDisplayName(variant, temp); |
| |
930 return temp.extract(result, maxResultSize, *pErrorCode); |
| |
931 } |
| |
932 |
| |
933 U_CAPI int32_t U_EXPORT2 |
| |
934 uldn_keyDisplayName(const ULocaleDisplayNames *ldn, |
| |
935 const char *key, |
| |
936 UChar *result, |
| |
937 int32_t maxResultSize, |
| |
938 UErrorCode *pErrorCode) { |
| |
939 if (U_FAILURE(*pErrorCode)) { |
| |
940 return 0; |
| |
941 } |
| |
942 if (ldn == NULL || key == NULL || (result == NULL && maxResultSize > 0) || maxResultSize < 0) { |
| |
943 *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; |
| |
944 return 0; |
| |
945 } |
| |
946 UnicodeString temp(result, 0, maxResultSize); |
| |
947 ((const LocaleDisplayNames *)ldn)->keyDisplayName(key, temp); |
| |
948 return temp.extract(result, maxResultSize, *pErrorCode); |
| |
949 } |
| |
950 |
| |
951 U_CAPI int32_t U_EXPORT2 |
| |
952 uldn_keyValueDisplayName(const ULocaleDisplayNames *ldn, |
| |
953 const char *key, |
| |
954 const char *value, |
| |
955 UChar *result, |
| |
956 int32_t maxResultSize, |
| |
957 UErrorCode *pErrorCode) { |
| |
958 if (U_FAILURE(*pErrorCode)) { |
| |
959 return 0; |
| |
960 } |
| |
961 if (ldn == NULL || key == NULL || value == NULL || (result == NULL && maxResultSize > 0) |
| |
962 || maxResultSize < 0) { |
| |
963 *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; |
| |
964 return 0; |
| |
965 } |
| |
966 UnicodeString temp(result, 0, maxResultSize); |
| |
967 ((const LocaleDisplayNames *)ldn)->keyValueDisplayName(key, value, temp); |
| |
968 return temp.extract(result, maxResultSize, *pErrorCode); |
| |
969 } |
| |
970 |
| |
971 #endif |