/*==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 . 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 #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 = _wtoi(number.c_str()) - 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::size_type curIndex = 0; curIndex < fText.size(); curIndex++) { textBlock curTextBlock = fText[curIndex]; if (curTextBlock.fIsParam) { std::wstring paramStr = L"%"; wchar_t buff[256]; _itow(curTextBlock.fParamIndex + 1, buff, 10); 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 = _wtoi(number.c_str()) - 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::size_type curIndex = 0; curIndex < fText.size(); curIndex++) { textBlock curTextBlock = fText[curIndex]; if (curTextBlock.fIsParam) { std::wstring paramStr = L"%"; wchar_t buff[256]; _itow(curTextBlock.fParamIndex + 1, buff, 10); 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 & arguments) { std::wstring retVal = L""; for (std::vector::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; }