You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
373 lines
13 KiB
373 lines
13 KiB
/*==LICENSE==* |
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools |
|
Copyright (C) 2011 Cyan Worlds, Inc. |
|
|
|
This program is free software: you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation, either version 3 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
|
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com |
|
or by snail mail at: |
|
Cyan Worlds, Inc. |
|
14617 N Newport Hwy |
|
Mead, WA 99021 |
|
|
|
*==LICENSE==*/ |
|
////////////////////////////////////////////////////////////////////// |
|
// |
|
// pfLocalizedString - a small class to handle localized strings and |
|
// which can take parameters (like %s or %1s) and |
|
// also can be easily translated to XML format |
|
// |
|
////////////////////////////////////////////////////////////////////// |
|
|
|
#include "hsTypes.h" |
|
#include "hsUtils.h" |
|
|
|
#include "pfLocalizedString.h" |
|
|
|
#if HS_BUILD_FOR_MAC |
|
#include <bxwchar.h> |
|
#endif |
|
|
|
// MinGW sucks |
|
#if defined(_WIN32) && !defined(_MSC_VER) |
|
# define swprintf _snwprintf |
|
#endif |
|
|
|
////////////////////////////////////////////////////////////////////// |
|
//// pfLocalizedString functions ///////////////////////////////////// |
|
////////////////////////////////////////////////////////////////////// |
|
|
|
//// Constructors //////////////////////////////////////////////////// |
|
|
|
pfLocalizedString::pfLocalizedString(const wchar_t *plainText) |
|
{ |
|
fNumArguments = 0; |
|
IConvertFromPlainText(plainText); |
|
} |
|
|
|
pfLocalizedString::pfLocalizedString(const std::wstring & plainText) |
|
{ |
|
fNumArguments = 0; |
|
IConvertFromPlainText(plainText); |
|
} |
|
|
|
//// IConvertFromPlainText /////////////////////////////////////////// |
|
|
|
void pfLocalizedString::IConvertFromPlainText(const std::wstring & plainText) |
|
{ |
|
textBlock curTextBlock; |
|
fText.clear(); |
|
fPlainTextRep = plainText; |
|
fNumArguments = 0; // reset the argument count |
|
int curParameter = 0; |
|
|
|
for (std::wstring::size_type curIndex = 0; curIndex < plainText.size(); curIndex++) |
|
{ |
|
wchar_t curChar = plainText[curIndex]; |
|
bool isLastChar = (curIndex == (plainText.length() - 1)); |
|
switch (curChar) |
|
{ |
|
case L'\\': |
|
if (!isLastChar) |
|
{ |
|
// we need to see the next character |
|
curIndex++; |
|
wchar_t nextChar = plainText[curIndex]; |
|
if ((nextChar == L'%')||(nextChar == L'\\')) |
|
{ |
|
// we recognize it as an escaped character, so add it to the text |
|
curTextBlock.fText += nextChar; |
|
} |
|
// otherwise we don't recognize it and it will be skipped |
|
} |
|
// if it's the last char, just drop it |
|
break; |
|
case L'%': |
|
if (!isLastChar) |
|
{ |
|
// we need to grab the trailing s character |
|
std::wstring::size_type endArgPos = plainText.find(L"s", curIndex); |
|
if (endArgPos != std::wstring::npos) // make sure the s exists |
|
{ |
|
if (endArgPos == (curIndex + 1)) // no number specifier |
|
{ |
|
fText.push_back(curTextBlock); |
|
curTextBlock.fIsParam = true; |
|
curTextBlock.fParamIndex = curParameter; |
|
curParameter++; |
|
curTextBlock.fText = L""; |
|
fText.push_back(curTextBlock); |
|
curTextBlock.fIsParam = false; |
|
curTextBlock.fParamIndex = 0; |
|
} |
|
else // number specified |
|
{ |
|
fText.push_back(curTextBlock); |
|
curTextBlock.fIsParam = true; |
|
curTextBlock.fText = L""; |
|
|
|
std::wstring number = plainText.substr(curIndex + 1, (endArgPos - (curIndex + 1))); |
|
curTextBlock.fParamIndex = (UInt8)wcstol(number.c_str(), NULL, 10) - 1; // args are 1-based, vectors are 0-based |
|
fText.push_back(curTextBlock); |
|
|
|
curTextBlock.fIsParam = false; |
|
curTextBlock.fParamIndex = 0; |
|
} |
|
fNumArguments++; // increment our argument count |
|
curIndex = endArgPos; // update our position |
|
} |
|
// if s didn't exist, we just skip this % sign |
|
} |
|
// if it was the last char, we just skip this % sign |
|
break; |
|
default: |
|
curTextBlock.fText += curChar; |
|
break; |
|
} |
|
} |
|
fText.push_back(curTextBlock); |
|
|
|
IUpdateXML(); |
|
} |
|
|
|
//// IUpdatePlainText //////////////////////////////////////////////// |
|
|
|
void pfLocalizedString::IUpdatePlainText() |
|
{ |
|
fPlainTextRep = L""; |
|
for (std::vector<std::wstring>::size_type curIndex = 0; curIndex < fText.size(); curIndex++) |
|
{ |
|
textBlock curTextBlock = fText[curIndex]; |
|
|
|
if (curTextBlock.fIsParam) |
|
{ |
|
std::wstring paramStr = L"%"; |
|
wchar_t buff[256]; |
|
swprintf(buff, 256, L"%d", curTextBlock.fParamIndex + 1); |
|
paramStr += buff; |
|
paramStr += L"s"; |
|
fPlainTextRep += paramStr; |
|
} |
|
else |
|
{ |
|
// otherwise, we need to copy all the text over, making sure that % and \ are properly escaped |
|
for (std::wstring::size_type curChar = 0; curChar < curTextBlock.fText.size(); curChar++) |
|
{ |
|
if ((curTextBlock.fText[curChar] == L'\\')||(curTextBlock.fText[curChar] == L'%')) |
|
fPlainTextRep += L"\\"; |
|
fPlainTextRep += curTextBlock.fText[curChar]; |
|
} |
|
} |
|
} |
|
} |
|
|
|
//// IConvertFromXML ///////////////////////////////////////////////// |
|
|
|
void pfLocalizedString::IConvertFromXML(const std::wstring & xml) |
|
{ |
|
textBlock curTextBlock; |
|
fText.clear(); |
|
fNumArguments = 0; // reset the argument counter |
|
int curParameter = 0; |
|
|
|
for (std::wstring::size_type curIndex = 0; curIndex < xml.length(); curIndex++) |
|
{ |
|
wchar_t curChar = xml[curIndex]; |
|
bool isLastChar = (curIndex == (xml.length() - 1)); |
|
switch (curChar) |
|
{ // expat handles the > < and so on stuff for us |
|
case L'\\': // but we want to be able to escape the % sign and the \ character |
|
if (!isLastChar) |
|
{ |
|
// we need to see the next character |
|
curIndex++; |
|
wchar_t nextChar = xml[curIndex]; |
|
if ((nextChar == L'%')||(nextChar == L'\\')) |
|
{ |
|
// we recognize it as an escaped character, so add it to the text |
|
curTextBlock.fText += nextChar; |
|
} |
|
// otherwise we don't recognize it and it will be skipped |
|
} |
|
// if it's the last char, just drop it |
|
break; |
|
case L'%': |
|
if (!isLastChar) |
|
{ |
|
// we need to grab the trailing s character |
|
std::wstring::size_type endArgPos = xml.find(L"s", curIndex); |
|
if (endArgPos != std::wstring::npos) // make sure the s exists |
|
{ |
|
if (endArgPos == (curIndex + 1)) // no number specifier |
|
{ |
|
fText.push_back(curTextBlock); |
|
curTextBlock.fIsParam = true; |
|
curTextBlock.fParamIndex = curParameter; |
|
curParameter++; |
|
curTextBlock.fText = L""; |
|
fText.push_back(curTextBlock); |
|
curTextBlock.fIsParam = false; |
|
curTextBlock.fParamIndex = 0; |
|
} |
|
else // number specified |
|
{ |
|
fText.push_back(curTextBlock); |
|
curTextBlock.fIsParam = true; |
|
curTextBlock.fText = L""; |
|
|
|
std::wstring number = xml.substr(curIndex + 1, (endArgPos - (curIndex + 1))); |
|
curTextBlock.fParamIndex = (UInt8)wcstol(number.c_str(), nil, 10) - 1; // args are 1-based, vectors are 0-based |
|
fText.push_back(curTextBlock); |
|
|
|
curTextBlock.fIsParam = false; |
|
curTextBlock.fParamIndex = 0; |
|
} |
|
fNumArguments++; // increment the number of arguments |
|
curIndex = endArgPos; // update our position |
|
} |
|
// if s didn't exist, we just skip this % sign |
|
} |
|
// if it was the last char, we just skip this % sign |
|
break; |
|
default: |
|
curTextBlock.fText += curChar; |
|
break; |
|
} |
|
} |
|
fText.push_back(curTextBlock); |
|
|
|
IUpdatePlainText(); |
|
IUpdateXML(); // we don't really get pure xml from the parser (since it auto translates all the &x; stuff) |
|
} |
|
|
|
//// IUpdateXML ////////////////////////////////////////////////////// |
|
|
|
void pfLocalizedString::IUpdateXML() |
|
{ |
|
fXMLRep = L""; |
|
for (std::vector<std::wstring>::size_type curIndex = 0; curIndex < fText.size(); curIndex++) |
|
{ |
|
textBlock curTextBlock = fText[curIndex]; |
|
|
|
if (curTextBlock.fIsParam) |
|
{ |
|
std::wstring paramStr = L"%"; |
|
wchar_t buff[256]; |
|
swprintf(buff, 256, L"%d", curTextBlock.fParamIndex + 1); |
|
paramStr += buff; |
|
paramStr += L"s"; |
|
fXMLRep += paramStr; |
|
} |
|
else |
|
{ |
|
// otherwise, we need to copy all the text over, making sure that %, &, <, and > are properly converted |
|
for (std::wstring::size_type curChar = 0; curChar < curTextBlock.fText.size(); curChar++) |
|
{ |
|
if (curTextBlock.fText[curChar] == L'%') |
|
fXMLRep += L"\\%"; |
|
else if (curTextBlock.fText[curChar] == L'&') |
|
fXMLRep += L"&"; |
|
else if (curTextBlock.fText[curChar] == L'<') |
|
fXMLRep += L"<"; |
|
else if (curTextBlock.fText[curChar] == L'>') |
|
fXMLRep += L">"; |
|
else |
|
fXMLRep += curTextBlock.fText[curChar]; |
|
} |
|
} |
|
} |
|
} |
|
|
|
//// FromXML ///////////////////////////////////////////////////////// |
|
|
|
void pfLocalizedString::FromXML(const std::wstring & xml) |
|
{ |
|
IConvertFromXML(xml); |
|
} |
|
|
|
//// Operators /////////////////////////////////////////////////////// |
|
|
|
bool pfLocalizedString::operator<(pfLocalizedString &obj) |
|
{ |
|
return (fPlainTextRep < obj.fPlainTextRep); |
|
} |
|
|
|
bool pfLocalizedString::operator>(pfLocalizedString &obj) |
|
{ |
|
return (fPlainTextRep > obj.fPlainTextRep); |
|
} |
|
|
|
bool pfLocalizedString::operator==(pfLocalizedString &obj) |
|
{ |
|
return (fPlainTextRep == obj.fPlainTextRep); |
|
} |
|
|
|
bool pfLocalizedString::operator<=(pfLocalizedString &obj) |
|
{ |
|
return (fPlainTextRep <= obj.fPlainTextRep); |
|
} |
|
|
|
bool pfLocalizedString::operator>=(pfLocalizedString &obj) |
|
{ |
|
return (fPlainTextRep >= obj.fPlainTextRep); |
|
} |
|
|
|
bool pfLocalizedString::operator!=(pfLocalizedString &obj) |
|
{ |
|
return (fPlainTextRep != obj.fPlainTextRep); |
|
} |
|
|
|
pfLocalizedString pfLocalizedString::operator+(pfLocalizedString &obj) |
|
{ |
|
fPlainTextRep += obj.fPlainTextRep; |
|
IConvertFromPlainText(fPlainTextRep); |
|
return *this; |
|
} |
|
|
|
pfLocalizedString &pfLocalizedString::operator+=(pfLocalizedString &obj) |
|
{ |
|
fPlainTextRep += obj.fPlainTextRep; |
|
IConvertFromPlainText(fPlainTextRep); |
|
return *this; |
|
} |
|
|
|
pfLocalizedString &pfLocalizedString::operator=(const std::wstring & plainText) |
|
{ |
|
IConvertFromPlainText(plainText); |
|
return *this; |
|
} |
|
|
|
pfLocalizedString &pfLocalizedString::operator=(const wchar_t *plainText) |
|
{ |
|
IConvertFromPlainText(plainText); |
|
return *this; |
|
} |
|
|
|
std::wstring pfLocalizedString::operator%(const std::vector<std::wstring> & arguments) |
|
{ |
|
std::wstring retVal = L""; |
|
for (std::vector<std::wstring>::size_type curIndex = 0; curIndex < fText.size(); curIndex++) |
|
{ |
|
if (fText[curIndex].fIsParam) |
|
{ |
|
int curParam = fText[curIndex].fParamIndex; |
|
if (curParam < arguments.size()) |
|
retVal += arguments[curParam]; |
|
} |
|
else |
|
retVal += fText[curIndex].fText; |
|
} |
|
return retVal; |
|
}
|
|
|