OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "base/i18n/rtl.h" |
| 6 |
| 7 #include "base/file_path.h" |
| 8 #include "base/logging.h" |
| 9 #include "base/string_util.h" |
| 10 #include "base/utf_string_conversions.h" |
| 11 #include "base/sys_string_conversions.h" |
| 12 #include "unicode/coll.h" |
| 13 #include "unicode/locid.h" |
| 14 #include "unicode/uchar.h" |
| 15 #include "unicode/uscript.h" |
| 16 |
| 17 #if defined(TOOLKIT_GTK) |
| 18 #include <gtk/gtk.h> |
| 19 #endif |
| 20 |
| 21 namespace base { |
| 22 namespace i18n { |
| 23 |
| 24 // Represents the locale-specific ICU text direction. |
| 25 static TextDirection g_icu_text_direction = UNKNOWN_DIRECTION; |
| 26 |
| 27 void GetLanguageAndRegionFromOS(std::string* lang, std::string* region) { |
| 28 // Later we may have to change this to be OS-dependent so that |
| 29 // it's not affected by ICU's default locale. It's all right |
| 30 // to do this way because SetICUDefaultLocale is internal |
| 31 // to this file and we know that it's not yet called when this function |
| 32 // is called. |
| 33 icu::Locale locale = icu::Locale::getDefault(); |
| 34 const char* language = locale.getLanguage(); |
| 35 const char* country = locale.getCountry(); |
| 36 DCHECK(language); |
| 37 *lang = language; |
| 38 *region = country; |
| 39 } |
| 40 |
| 41 // Convert Chrome locale name to ICU locale name |
| 42 std::string ICULocaleName(const std::string& locale_string) { |
| 43 // If not Spanish, just return it. |
| 44 if (locale_string.substr(0, 2) != "es") |
| 45 return locale_string; |
| 46 // Expand es to es-ES. |
| 47 if (LowerCaseEqualsASCII(locale_string, "es")) |
| 48 return "es-ES"; |
| 49 // Map es-419 (Latin American Spanish) to es-FOO depending on the system |
| 50 // locale. If it's es-RR other than es-ES, map to es-RR. Otherwise, map |
| 51 // to es-MX (the most populous in Spanish-speaking Latin America). |
| 52 if (LowerCaseEqualsASCII(locale_string, "es-419")) { |
| 53 std::string lang, region; |
| 54 GetLanguageAndRegionFromOS(&lang, ®ion); |
| 55 if (LowerCaseEqualsASCII(lang, "es") && |
| 56 !LowerCaseEqualsASCII(region, "es")) { |
| 57 lang.append("-"); |
| 58 lang.append(region); |
| 59 return lang; |
| 60 } |
| 61 return "es-MX"; |
| 62 } |
| 63 // Currently, Chrome has only "es" and "es-419", but later we may have |
| 64 // more specific "es-RR". |
| 65 return locale_string; |
| 66 } |
| 67 |
| 68 void SetICUDefaultLocale(const std::string& locale_string) { |
| 69 icu::Locale locale(ICULocaleName(locale_string).c_str()); |
| 70 UErrorCode error_code = U_ZERO_ERROR; |
| 71 icu::Locale::setDefault(locale, error_code); |
| 72 // This return value is actually bogus because Locale object is |
| 73 // an ID and setDefault seems to always succeed (regardless of the |
| 74 // presence of actual locale data). However, |
| 75 // it does not hurt to have it as a sanity check. |
| 76 DCHECK(U_SUCCESS(error_code)); |
| 77 g_icu_text_direction = UNKNOWN_DIRECTION; |
| 78 } |
| 79 |
| 80 TextDirection GetICUTextDirection() { |
| 81 if (g_icu_text_direction == UNKNOWN_DIRECTION) { |
| 82 const icu::Locale& locale = icu::Locale::getDefault(); |
| 83 g_icu_text_direction = GetTextDirectionForLocale(locale.getName()); |
| 84 } |
| 85 return g_icu_text_direction; |
| 86 } |
| 87 |
| 88 TextDirection GetTextDirection() { |
| 89 #if defined(TOOLKIT_GTK) |
| 90 GtkTextDirection gtk_dir = gtk_widget_get_default_direction(); |
| 91 return (gtk_dir == GTK_TEXT_DIR_LTR) ? LEFT_TO_RIGHT : RIGHT_TO_LEFT; |
| 92 #else |
| 93 return GetICUTextDirection(); |
| 94 #endif |
| 95 } |
| 96 |
| 97 bool IsRTL() { |
| 98 return GetTextDirection() == RIGHT_TO_LEFT; |
| 99 } |
| 100 |
| 101 TextDirection GetTextDirectionForLocale(const char* locale_name) { |
| 102 UErrorCode status = U_ZERO_ERROR; |
| 103 ULayoutType layout_dir = uloc_getCharacterOrientation(locale_name, &status); |
| 104 DCHECK(U_SUCCESS(status)); |
| 105 // Treat anything other than RTL as LTR. |
| 106 return (layout_dir != ULOC_LAYOUT_RTL) ? LEFT_TO_RIGHT : RIGHT_TO_LEFT; |
| 107 } |
| 108 |
| 109 TextDirection GetFirstStrongCharacterDirection(const std::wstring& text) { |
| 110 #if defined(WCHAR_T_IS_UTF32) |
| 111 string16 text_utf16 = WideToUTF16(text); |
| 112 const UChar* string = text_utf16.c_str(); |
| 113 #else |
| 114 const UChar* string = text.c_str(); |
| 115 #endif |
| 116 size_t length = text.length(); |
| 117 size_t position = 0; |
| 118 while (position < length) { |
| 119 UChar32 character; |
| 120 size_t next_position = position; |
| 121 U16_NEXT(string, next_position, length, character); |
| 122 |
| 123 // Now that we have the character, we use ICU in order to query for the |
| 124 // appropriate Unicode BiDi character type. |
| 125 int32_t property = u_getIntPropertyValue(character, UCHAR_BIDI_CLASS); |
| 126 if ((property == U_RIGHT_TO_LEFT) || |
| 127 (property == U_RIGHT_TO_LEFT_ARABIC) || |
| 128 (property == U_RIGHT_TO_LEFT_EMBEDDING) || |
| 129 (property == U_RIGHT_TO_LEFT_OVERRIDE)) { |
| 130 return RIGHT_TO_LEFT; |
| 131 } else if ((property == U_LEFT_TO_RIGHT) || |
| 132 (property == U_LEFT_TO_RIGHT_EMBEDDING) || |
| 133 (property == U_LEFT_TO_RIGHT_OVERRIDE)) { |
| 134 return LEFT_TO_RIGHT; |
| 135 } |
| 136 |
| 137 position = next_position; |
| 138 } |
| 139 |
| 140 return LEFT_TO_RIGHT; |
| 141 } |
| 142 |
| 143 bool AdjustStringForLocaleDirection(const std::wstring& text, |
| 144 std::wstring* localized_text) { |
| 145 if (GetTextDirection() == LEFT_TO_RIGHT || text.length() == 0) |
| 146 return false; |
| 147 |
| 148 // Marking the string as LTR if the locale is RTL and the string does not |
| 149 // contain strong RTL characters. Otherwise, mark the string as RTL. |
| 150 *localized_text = text; |
| 151 bool has_rtl_chars = StringContainsStrongRTLChars(text); |
| 152 if (!has_rtl_chars) |
| 153 WrapStringWithLTRFormatting(localized_text); |
| 154 else |
| 155 WrapStringWithRTLFormatting(localized_text); |
| 156 |
| 157 return true; |
| 158 } |
| 159 |
| 160 bool StringContainsStrongRTLChars(const std::wstring& text) { |
| 161 #if defined(WCHAR_T_IS_UTF32) |
| 162 string16 text_utf16 = WideToUTF16(text); |
| 163 const UChar* string = text_utf16.c_str(); |
| 164 #else |
| 165 const UChar* string = text.c_str(); |
| 166 #endif |
| 167 size_t length = text.length(); |
| 168 size_t position = 0; |
| 169 while (position < length) { |
| 170 UChar32 character; |
| 171 size_t next_position = position; |
| 172 U16_NEXT(string, next_position, length, character); |
| 173 |
| 174 // Now that we have the character, we use ICU in order to query for the |
| 175 // appropriate Unicode BiDi character type. |
| 176 int32_t property = u_getIntPropertyValue(character, UCHAR_BIDI_CLASS); |
| 177 if ((property == U_RIGHT_TO_LEFT) || (property == U_RIGHT_TO_LEFT_ARABIC)) |
| 178 return true; |
| 179 |
| 180 position = next_position; |
| 181 } |
| 182 |
| 183 return false; |
| 184 } |
| 185 |
| 186 void WrapStringWithLTRFormatting(std::wstring* text) { |
| 187 // Inserting an LRE (Left-To-Right Embedding) mark as the first character. |
| 188 text->insert(0, 1, static_cast<wchar_t>(kLeftToRightEmbeddingMark)); |
| 189 |
| 190 // Inserting a PDF (Pop Directional Formatting) mark as the last character. |
| 191 text->push_back(static_cast<wchar_t>(kPopDirectionalFormatting)); |
| 192 } |
| 193 |
| 194 void WrapStringWithRTLFormatting(std::wstring* text) { |
| 195 // Inserting an RLE (Right-To-Left Embedding) mark as the first character. |
| 196 text->insert(0, 1, static_cast<wchar_t>(kRightToLeftEmbeddingMark)); |
| 197 |
| 198 // Inserting a PDF (Pop Directional Formatting) mark as the last character. |
| 199 text->push_back(static_cast<wchar_t>(kPopDirectionalFormatting)); |
| 200 } |
| 201 |
| 202 void WrapPathWithLTRFormatting(const FilePath& path, |
| 203 string16* rtl_safe_path) { |
| 204 // Wrap the overall path with LRE-PDF pair which essentialy marks the |
| 205 // string as a Left-To-Right string. |
| 206 // Inserting an LRE (Left-To-Right Embedding) mark as the first character. |
| 207 rtl_safe_path->push_back(kLeftToRightEmbeddingMark); |
| 208 #if defined(OS_MACOSX) |
| 209 rtl_safe_path->append(UTF8ToUTF16(path.value())); |
| 210 #elif defined(OS_WIN) |
| 211 rtl_safe_path->append(path.value()); |
| 212 #else // defined(OS_POSIX) && !defined(OS_MACOSX) |
| 213 std::wstring wide_path = base::SysNativeMBToWide(path.value()); |
| 214 rtl_safe_path->append(WideToUTF16(wide_path)); |
| 215 #endif |
| 216 // Inserting a PDF (Pop Directional Formatting) mark as the last character. |
| 217 rtl_safe_path->push_back(kPopDirectionalFormatting); |
| 218 } |
| 219 |
| 220 std::wstring GetDisplayStringInLTRDirectionality(std::wstring* text) { |
| 221 if (GetTextDirection() == RIGHT_TO_LEFT) |
| 222 WrapStringWithLTRFormatting(text); |
| 223 return *text; |
| 224 } |
| 225 |
| 226 } // namespace i18n |
| 227 } // namespace base |
| 228 |
OLD | NEW |