This source file includes following definitions.
- extractLanguageCode
- removeLastComponent
- ensureNameToLCIDMap
- convertLocaleNameToLCID
- LCIDFromLocaleInternal
- LCIDFromLocale
- create
- m_defaultsForLocale
- create
- getLocaleInfoString
- getLocaleInfo
- ensureShortMonthLabels
- countContinuousLetters
- commitLiteralToken
- convertWindowsDateTimeFormat
- ensureMonthLabels
- ensureWeekDayShortLabels
- monthLabels
- weekDayShortLabels
- firstDayOfWeek
- isRTL
- dateFormat
- dateFormat
- monthFormat
- shortMonthFormat
- timeFormat
- shortTimeFormat
- dateTimeFormatWithSeconds
- dateTimeFormatWithoutSeconds
- shortMonthLabels
- standAloneMonthLabels
- shortStandAloneMonthLabels
- timeAMPMLabels
- initializeLocaleData
#include "config.h"
#include "platform/text/LocaleWin.h"
#include <limits>
#include "platform/DateComponents.h"
#include "platform/Language.h"
#include "platform/LayoutTestSupport.h"
#include "platform/text/DateTimeFormat.h"
#include "wtf/CurrentTime.h"
#include "wtf/DateMath.h"
#include "wtf/HashMap.h"
#include "wtf/OwnPtr.h"
#include "wtf/PassOwnPtr.h"
#include "wtf/text/StringBuffer.h"
#include "wtf/text/StringBuilder.h"
#include "wtf/text/StringHash.h"
using namespace std;
namespace WebCore {
typedef LCID (WINAPI* LocaleNameToLCIDPtr)(LPCWSTR, DWORD);
typedef HashMap<String, LCID> NameToLCIDMap;
static String extractLanguageCode(const String& locale)
{
size_t dashPosition = locale.find('-');
if (dashPosition == kNotFound)
return locale;
return locale.left(dashPosition);
}
static String removeLastComponent(const String& name)
{
size_t lastSeparator = name.reverseFind('-');
if (lastSeparator == kNotFound)
return emptyString();
return name.left(lastSeparator);
}
static void ensureNameToLCIDMap(NameToLCIDMap& map)
{
if (!map.isEmpty())
return;
map.add("ar", 0x0001);
map.add("ar-eg", 0x0C01);
map.add("de", 0x0007);
map.add("de-de", 0x0407);
map.add("el", 0x0008);
map.add("el-gr", 0x0408);
map.add("en", 0x0009);
map.add("en-gb", 0x0809);
map.add("en-us", 0x0409);
map.add("fr", 0x000C);
map.add("fr-fr", 0x040C);
map.add("he", 0x000D);
map.add("he-il", 0x040D);
map.add("hi", 0x0039);
map.add("hi-in", 0x0439);
map.add("ja", 0x0011);
map.add("ja-jp", 0x0411);
map.add("ko", 0x0012);
map.add("ko-kr", 0x0412);
map.add("ru", 0x0019);
map.add("ru-ru", 0x0419);
map.add("zh-cn", 0x0804);
map.add("zh-tw", 0x0404);
}
static LCID WINAPI convertLocaleNameToLCID(LPCWSTR name, DWORD)
{
if (!name || !name[0])
return LOCALE_USER_DEFAULT;
DEFINE_STATIC_LOCAL(NameToLCIDMap, map, ());
ensureNameToLCIDMap(map);
String localeName = String(name).replace('_', '-');
localeName = localeName.lower();
do {
NameToLCIDMap::const_iterator iterator = map.find(localeName);
if (iterator != map.end())
return iterator->value;
localeName = removeLastComponent(localeName);
} while (!localeName.isEmpty());
return LOCALE_USER_DEFAULT;
}
static LCID LCIDFromLocaleInternal(LCID userDefaultLCID, const String& userDefaultLanguageCode, LocaleNameToLCIDPtr localeNameToLCID, const String& locale)
{
String localeLanguageCode = extractLanguageCode(locale);
if (equalIgnoringCase(localeLanguageCode, userDefaultLanguageCode))
return userDefaultLCID;
return localeNameToLCID(locale.charactersWithNullTermination().data(), 0);
}
static LCID LCIDFromLocale(const String& locale, bool defaultsForLocale)
{
LocaleNameToLCIDPtr localeNameToLCID = reinterpret_cast<LocaleNameToLCIDPtr>(::GetProcAddress(::GetModuleHandle(L"kernel32"), "LocaleNameToLCID"));
if (!localeNameToLCID)
localeNameToLCID = convertLocaleNameToLCID;
const size_t languageCodeBufferSize = 9;
WCHAR lowercaseLanguageCode[languageCodeBufferSize];
::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME | (defaultsForLocale ? LOCALE_NOUSEROVERRIDE : 0), lowercaseLanguageCode, languageCodeBufferSize);
String userDefaultLanguageCode = String(lowercaseLanguageCode);
LCID lcid = LCIDFromLocaleInternal(LOCALE_USER_DEFAULT, userDefaultLanguageCode, localeNameToLCID, locale);
if (!lcid)
lcid = LCIDFromLocaleInternal(LOCALE_USER_DEFAULT, userDefaultLanguageCode, localeNameToLCID, defaultLanguage());
return lcid;
}
PassOwnPtr<Locale> Locale::create(const String& locale)
{
bool defaultsForLocale = isRunningLayoutTest();
return LocaleWin::create(LCIDFromLocale(locale, defaultsForLocale), defaultsForLocale);
}
inline LocaleWin::LocaleWin(LCID lcid, bool defaultsForLocale)
: m_lcid(lcid)
, m_didInitializeNumberData(false)
, m_defaultsForLocale(defaultsForLocale)
{
DWORD value = 0;
getLocaleInfo(LOCALE_IFIRSTDAYOFWEEK | (defaultsForLocale ? LOCALE_NOUSEROVERRIDE : 0), value);
m_firstDayOfWeek = (value + 1) % 7;
}
PassOwnPtr<LocaleWin> LocaleWin::create(LCID lcid, bool defaultsForLocale)
{
return adoptPtr(new LocaleWin(lcid, defaultsForLocale));
}
LocaleWin::~LocaleWin()
{
}
String LocaleWin::getLocaleInfoString(LCTYPE type)
{
int bufferSizeWithNUL = ::GetLocaleInfo(m_lcid, type | (m_defaultsForLocale ? LOCALE_NOUSEROVERRIDE : 0), 0, 0);
if (bufferSizeWithNUL <= 0)
return String();
StringBuffer<UChar> buffer(bufferSizeWithNUL);
::GetLocaleInfo(m_lcid, type | (m_defaultsForLocale ? LOCALE_NOUSEROVERRIDE : 0), buffer.characters(), bufferSizeWithNUL);
buffer.shrink(bufferSizeWithNUL - 1);
return String::adopt(buffer);
}
void LocaleWin::getLocaleInfo(LCTYPE type, DWORD& result)
{
::GetLocaleInfo(m_lcid, type | LOCALE_RETURN_NUMBER, reinterpret_cast<LPWSTR>(&result), sizeof(DWORD) / sizeof(TCHAR));
}
void LocaleWin::ensureShortMonthLabels()
{
if (!m_shortMonthLabels.isEmpty())
return;
const LCTYPE types[12] = {
LOCALE_SABBREVMONTHNAME1,
LOCALE_SABBREVMONTHNAME2,
LOCALE_SABBREVMONTHNAME3,
LOCALE_SABBREVMONTHNAME4,
LOCALE_SABBREVMONTHNAME5,
LOCALE_SABBREVMONTHNAME6,
LOCALE_SABBREVMONTHNAME7,
LOCALE_SABBREVMONTHNAME8,
LOCALE_SABBREVMONTHNAME9,
LOCALE_SABBREVMONTHNAME10,
LOCALE_SABBREVMONTHNAME11,
LOCALE_SABBREVMONTHNAME12,
};
m_shortMonthLabels.reserveCapacity(WTF_ARRAY_LENGTH(types));
for (unsigned i = 0; i < WTF_ARRAY_LENGTH(types); ++i) {
m_shortMonthLabels.append(getLocaleInfoString(types[i]));
if (m_shortMonthLabels.last().isEmpty()) {
m_shortMonthLabels.shrink(0);
m_shortMonthLabels.reserveCapacity(WTF_ARRAY_LENGTH(WTF::monthName));
for (unsigned m = 0; m < WTF_ARRAY_LENGTH(WTF::monthName); ++m)
m_shortMonthLabels.append(WTF::monthName[m]);
return;
}
}
}
static unsigned countContinuousLetters(const String& format, unsigned index)
{
unsigned count = 1;
UChar reference = format[index];
while (index + 1 < format.length()) {
if (format[++index] != reference)
break;
++count;
}
return count;
}
static void commitLiteralToken(StringBuilder& literalBuffer, StringBuilder& converted)
{
if (literalBuffer.length() <= 0)
return;
DateTimeFormat::quoteAndAppendLiteral(literalBuffer.toString(), converted);
literalBuffer.clear();
}
static String convertWindowsDateTimeFormat(const String& format)
{
StringBuilder converted;
StringBuilder literalBuffer;
bool inQuote = false;
bool lastQuoteCanBeLiteral = false;
for (unsigned i = 0; i < format.length(); ++i) {
UChar ch = format[i];
if (inQuote) {
if (ch == '\'') {
inQuote = false;
ASSERT(i);
if (lastQuoteCanBeLiteral && format[i - 1] == '\'') {
literalBuffer.append('\'');
lastQuoteCanBeLiteral = false;
} else {
lastQuoteCanBeLiteral = true;
}
} else {
literalBuffer.append(ch);
}
continue;
}
if (ch == '\'') {
inQuote = true;
if (lastQuoteCanBeLiteral && i > 0 && format[i - 1] == '\'') {
literalBuffer.append(ch);
lastQuoteCanBeLiteral = false;
} else {
lastQuoteCanBeLiteral = true;
}
} else if (isASCIIAlpha(ch)) {
commitLiteralToken(literalBuffer, converted);
unsigned symbolStart = i;
unsigned count = countContinuousLetters(format, i);
i += count - 1;
if (ch == 'h' || ch == 'H' || ch == 'm' || ch == 's' || ch == 'M' || ch == 'y') {
converted.append(format, symbolStart, count);
} else if (ch == 'd') {
if (count <= 2)
converted.append(format, symbolStart, count);
else if (count == 3)
converted.append("EEE");
else
converted.append("EEEE");
} else if (ch == 'g') {
if (count == 1) {
converted.append('G');
} else {
}
} else if (ch == 't') {
converted.append('a');
} else {
literalBuffer.append(format, symbolStart, count);
}
} else {
literalBuffer.append(ch);
}
}
commitLiteralToken(literalBuffer, converted);
return converted.toString();
}
void LocaleWin::ensureMonthLabels()
{
if (!m_monthLabels.isEmpty())
return;
const LCTYPE types[12] = {
LOCALE_SMONTHNAME1,
LOCALE_SMONTHNAME2,
LOCALE_SMONTHNAME3,
LOCALE_SMONTHNAME4,
LOCALE_SMONTHNAME5,
LOCALE_SMONTHNAME6,
LOCALE_SMONTHNAME7,
LOCALE_SMONTHNAME8,
LOCALE_SMONTHNAME9,
LOCALE_SMONTHNAME10,
LOCALE_SMONTHNAME11,
LOCALE_SMONTHNAME12,
};
m_monthLabels.reserveCapacity(WTF_ARRAY_LENGTH(types));
for (unsigned i = 0; i < WTF_ARRAY_LENGTH(types); ++i) {
m_monthLabels.append(getLocaleInfoString(types[i]));
if (m_monthLabels.last().isEmpty()) {
m_monthLabels.shrink(0);
m_monthLabels.reserveCapacity(WTF_ARRAY_LENGTH(WTF::monthFullName));
for (unsigned m = 0; m < WTF_ARRAY_LENGTH(WTF::monthFullName); ++m)
m_monthLabels.append(WTF::monthFullName[m]);
return;
}
}
}
void LocaleWin::ensureWeekDayShortLabels()
{
if (!m_weekDayShortLabels.isEmpty())
return;
const LCTYPE types[7] = {
LOCALE_SABBREVDAYNAME7,
LOCALE_SABBREVDAYNAME1,
LOCALE_SABBREVDAYNAME2,
LOCALE_SABBREVDAYNAME3,
LOCALE_SABBREVDAYNAME4,
LOCALE_SABBREVDAYNAME5,
LOCALE_SABBREVDAYNAME6
};
m_weekDayShortLabels.reserveCapacity(WTF_ARRAY_LENGTH(types));
for (unsigned i = 0; i < WTF_ARRAY_LENGTH(types); ++i) {
m_weekDayShortLabels.append(getLocaleInfoString(types[i]));
if (m_weekDayShortLabels.last().isEmpty()) {
m_weekDayShortLabels.shrink(0);
m_weekDayShortLabels.reserveCapacity(WTF_ARRAY_LENGTH(WTF::weekdayName));
for (unsigned w = 0; w < WTF_ARRAY_LENGTH(WTF::weekdayName); ++w) {
m_weekDayShortLabels.append(WTF::weekdayName[(w + 6) % 7]);
}
return;
}
}
}
const Vector<String>& LocaleWin::monthLabels()
{
ensureMonthLabels();
return m_monthLabels;
}
const Vector<String>& LocaleWin::weekDayShortLabels()
{
ensureWeekDayShortLabels();
return m_weekDayShortLabels;
}
unsigned LocaleWin::firstDayOfWeek()
{
return m_firstDayOfWeek;
}
bool LocaleWin::isRTL()
{
WTF::Unicode::Direction dir = WTF::Unicode::direction(monthLabels()[0][0]);
return dir == WTF::Unicode::RightToLeft || dir == WTF::Unicode::RightToLeftArabic;
}
String LocaleWin::dateFormat()
{
if (m_dateFormat.isNull())
m_dateFormat = convertWindowsDateTimeFormat(getLocaleInfoString(LOCALE_SSHORTDATE));
return m_dateFormat;
}
String LocaleWin::dateFormat(const String& windowsFormat)
{
return convertWindowsDateTimeFormat(windowsFormat);
}
String LocaleWin::monthFormat()
{
if (m_monthFormat.isNull())
m_monthFormat = convertWindowsDateTimeFormat(getLocaleInfoString(LOCALE_SYEARMONTH));
return m_monthFormat;
}
String LocaleWin::shortMonthFormat()
{
if (m_shortMonthFormat.isNull())
m_shortMonthFormat = convertWindowsDateTimeFormat(getLocaleInfoString(LOCALE_SYEARMONTH)).replace("MMMM", "MMM");
return m_shortMonthFormat;
}
String LocaleWin::timeFormat()
{
if (m_timeFormatWithSeconds.isNull())
m_timeFormatWithSeconds = convertWindowsDateTimeFormat(getLocaleInfoString(LOCALE_STIMEFORMAT));
return m_timeFormatWithSeconds;
}
String LocaleWin::shortTimeFormat()
{
if (!m_timeFormatWithoutSeconds.isNull())
return m_timeFormatWithoutSeconds;
String format = getLocaleInfoString(LOCALE_SSHORTTIME);
if (format.isEmpty()) {
format = getLocaleInfoString(LOCALE_STIMEFORMAT);
StringBuilder builder;
builder.append(getLocaleInfoString(LOCALE_STIME));
builder.append("ss");
size_t pos = format.reverseFind(builder.toString());
if (pos != kNotFound)
format.remove(pos, builder.length());
}
m_timeFormatWithoutSeconds = convertWindowsDateTimeFormat(format);
return m_timeFormatWithoutSeconds;
}
String LocaleWin::dateTimeFormatWithSeconds()
{
if (!m_dateTimeFormatWithSeconds.isNull())
return m_dateTimeFormatWithSeconds;
StringBuilder builder;
builder.append(dateFormat());
builder.append(' ');
builder.append(timeFormat());
m_dateTimeFormatWithSeconds = builder.toString();
return m_dateTimeFormatWithSeconds;
}
String LocaleWin::dateTimeFormatWithoutSeconds()
{
if (!m_dateTimeFormatWithoutSeconds.isNull())
return m_dateTimeFormatWithoutSeconds;
StringBuilder builder;
builder.append(dateFormat());
builder.append(' ');
builder.append(shortTimeFormat());
m_dateTimeFormatWithoutSeconds = builder.toString();
return m_dateTimeFormatWithoutSeconds;
}
const Vector<String>& LocaleWin::shortMonthLabels()
{
ensureShortMonthLabels();
return m_shortMonthLabels;
}
const Vector<String>& LocaleWin::standAloneMonthLabels()
{
return monthLabels();
}
const Vector<String>& LocaleWin::shortStandAloneMonthLabels()
{
return shortMonthLabels();
}
const Vector<String>& LocaleWin::timeAMPMLabels()
{
if (m_timeAMPMLabels.isEmpty()) {
m_timeAMPMLabels.append(getLocaleInfoString(LOCALE_S1159));
m_timeAMPMLabels.append(getLocaleInfoString(LOCALE_S2359));
}
return m_timeAMPMLabels;
}
void LocaleWin::initializeLocaleData()
{
if (m_didInitializeNumberData)
return;
Vector<String, DecimalSymbolsSize> symbols;
enum DigitSubstitution {
DigitSubstitutionContext = 0,
DigitSubstitution0to9 = 1,
DigitSubstitutionNative = 2,
};
DWORD digitSubstitution = DigitSubstitution0to9;
getLocaleInfo(LOCALE_IDIGITSUBSTITUTION, digitSubstitution);
if (digitSubstitution == DigitSubstitution0to9) {
symbols.append("0");
symbols.append("1");
symbols.append("2");
symbols.append("3");
symbols.append("4");
symbols.append("5");
symbols.append("6");
symbols.append("7");
symbols.append("8");
symbols.append("9");
} else {
String digits = getLocaleInfoString(LOCALE_SNATIVEDIGITS);
ASSERT(digits.length() >= 10);
for (unsigned i = 0; i < 10; ++i)
symbols.append(digits.substring(i, 1));
}
ASSERT(symbols.size() == DecimalSeparatorIndex);
symbols.append(getLocaleInfoString(LOCALE_SDECIMAL));
ASSERT(symbols.size() == GroupSeparatorIndex);
symbols.append(getLocaleInfoString(LOCALE_STHOUSAND));
ASSERT(symbols.size() == DecimalSymbolsSize);
String negativeSign = getLocaleInfoString(LOCALE_SNEGATIVESIGN);
enum NegativeFormat {
NegativeFormatParenthesis = 0,
NegativeFormatSignPrefix = 1,
NegativeFormatSignSpacePrefix = 2,
NegativeFormatSignSuffix = 3,
NegativeFormatSpaceSignSuffix = 4,
};
DWORD negativeFormat = NegativeFormatSignPrefix;
getLocaleInfo(LOCALE_INEGNUMBER, negativeFormat);
String negativePrefix = emptyString();
String negativeSuffix = emptyString();
switch (negativeFormat) {
case NegativeFormatParenthesis:
negativePrefix = "(";
negativeSuffix = ")";
break;
case NegativeFormatSignSpacePrefix:
negativePrefix = negativeSign + " ";
break;
case NegativeFormatSignSuffix:
negativeSuffix = negativeSign;
break;
case NegativeFormatSpaceSignSuffix:
negativeSuffix = " " + negativeSign;
break;
case NegativeFormatSignPrefix:
default:
negativePrefix = negativeSign;
break;
}
m_didInitializeNumberData = true;
setLocaleData(symbols, emptyString(), emptyString(), negativePrefix, negativeSuffix);
}
}