diff options
author | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:29:28 +0200 |
---|---|---|
committer | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:29:28 +0200 |
commit | 75c07011b7c4d06acd7b45dabdcd60ab9d80f385 (patch) | |
tree | 8853c3978dd152ef377e652239448b1352320206 /lib/parse_lng.h | |
parent | 5.22 (diff) | |
download | FreeFileSync-75c07011b7c4d06acd7b45dabdcd60ab9d80f385.tar.gz FreeFileSync-75c07011b7c4d06acd7b45dabdcd60ab9d80f385.tar.bz2 FreeFileSync-75c07011b7c4d06acd7b45dabdcd60ab9d80f385.zip |
5.23
Diffstat (limited to 'lib/parse_lng.h')
-rw-r--r-- | lib/parse_lng.h | 706 |
1 files changed, 0 insertions, 706 deletions
diff --git a/lib/parse_lng.h b/lib/parse_lng.h deleted file mode 100644 index 19a8e751..00000000 --- a/lib/parse_lng.h +++ /dev/null @@ -1,706 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef PARSE_LNG_HEADER_INCLUDED -#define PARSE_LNG_HEADER_INCLUDED - -#include <algorithm> -#include <cctype> -#include <functional> -#include <memory> -#include <map> -#include <set> -#include <sstream> -#include <stdexcept> -#include <string> -#include <vector> -#include <list> -#include <zen/utf.h> -#include <zen/string_tools.h> -#include "parse_plural.h" -//#include <zen/perf.h> - -namespace lngfile -{ -//singular forms -typedef std::map <std::string, std::string> TranslationMap; //orig |-> translation - -//plural forms -typedef std::pair<std::string, std::string> SingularPluralPair; //1 house| n houses -typedef std::vector<std::string> PluralForms; //1 dom | 2 domy | 5 domów -typedef std::map<SingularPluralPair, PluralForms> TranslationPluralMap; //(sing/plu) |-> pluralforms - -struct TransHeader -{ - TransHeader() : pluralCount(0) {} - std::string languageName; //display name: "English (UK)" - std::string translatorName; //"Zenju" - std::string localeName; //ISO 639 language code + ISO 3166 country code, e.g. "en_GB", or "en_US" - std::string flagFile; //"england.png" - int pluralCount; //2 - std::string pluralDefinition; //"n == 1 ? 0 : 1" -}; - - -struct ParsingError -{ - ParsingError(const std::wstring& msg, size_t row, size_t col) : msg_(msg), row_(row), col_(col) {} - std::wstring msg_; //parser error message - size_t row_; //starting with 0 - size_t col_; // -}; -void parseLng(const std::string& fileStream, TransHeader& header, TranslationMap& out, TranslationPluralMap& pluralOut); //throw ParsingError -void parseHeader(const std::string& fileStream, TransHeader& header); //throw ParsingError - -class TranslationUnorderedList; //unordered list of unique translation items -std::string generateLng(const TranslationUnorderedList& in, const TransHeader& header); - - - - - - - - - - - - - - - - - - - -//--------------------------- implementation --------------------------- -enum class TranslationNewItemPos -{ - REL, - TOP -}; - -class TranslationUnorderedList //unordered list of unique translation items -{ -public: - TranslationUnorderedList(TranslationNewItemPos newItemPos, TranslationMap&& transOld, TranslationPluralMap&& transPluralOld) : - newItemPos_(newItemPos), transOld_(std::move(transOld)), transPluralOld_(std::move(transPluralOld)) {} - - void addItem(const std::string& orig) - { - if (!transUnique.insert(orig).second) return; - auto it = transOld_.find(orig); - if (it != transOld_.end() && !it->second.empty()) //preserve old translation from .lng file if existing - sequence.push_back(std::make_shared<RegularItem>(std::make_pair(orig, it->second))); - else - switch (newItemPos_) - { - case TranslationNewItemPos::REL: - sequence.push_back(std::make_shared<RegularItem>(std::make_pair(orig, std::string()))); - break; - case TranslationNewItemPos::TOP: - sequence.push_front(std::make_shared<RegularItem>(std::make_pair(orig, std::string()))); //put untranslated items to the front of the .lng filebreak; - break; - } - } - - void addItem(const SingularPluralPair& orig) - { - if (!pluralUnique.insert(orig).second) return; - auto it = transPluralOld_.find(orig); - if (it != transPluralOld_.end() && !it->second.empty()) //preserve old translation from .lng file if existing - sequence.push_back(std::make_shared<PluralItem>(std::make_pair(orig, it->second))); - else - switch (newItemPos_) - { - case TranslationNewItemPos::REL: - sequence.push_back(std::make_shared<PluralItem>(std::make_pair(orig, PluralForms()))); - break; - case TranslationNewItemPos::TOP: - sequence.push_front(std::make_shared<PluralItem>(std::make_pair(orig, PluralForms()))); //put untranslated items to the front of the .lng file - break; - } - } - - bool untranslatedTextExists() const { return std::any_of(sequence.begin(), sequence.end(), [](const std::shared_ptr<Item>& item) { return !item->hasTranslation(); }); } - - template <class Function, class Function2> - void visitItems(Function onTrans, Function2 onPluralTrans) const //onTrans takes (const TranslationMap::value_type&), onPluralTrans takes (const TranslationPluralMap::value_type&) - { - for (const auto& item : sequence) - if (auto regular = dynamic_cast<const RegularItem*>(item.get())) - onTrans(regular->value); - else if (auto plural = dynamic_cast<const PluralItem*>(item.get())) - onPluralTrans(plural->value); - else assert(false); - } - -private: - struct Item { virtual ~Item() {} virtual bool hasTranslation() const = 0; }; - struct RegularItem : public Item { RegularItem(const TranslationMap ::value_type& val) : value(val) {} virtual bool hasTranslation() const { return !value.second.empty(); } TranslationMap ::value_type value; }; - struct PluralItem : public Item { PluralItem (const TranslationPluralMap::value_type& val) : value(val) {} virtual bool hasTranslation() const { return !value.second.empty(); } TranslationPluralMap::value_type value; }; - - const TranslationNewItemPos newItemPos_; - std::list<std::shared_ptr<Item>> sequence; //ordered list of translation elements - - std::set<TranslationMap ::key_type> transUnique; //check uniqueness - std::set<TranslationPluralMap::key_type> pluralUnique; // - - const TranslationMap transOld_; //reuse existing translation - const TranslationPluralMap transPluralOld_; // -}; - - -struct Token -{ - enum Type - { - //header information - TK_HEADER_BEGIN, - TK_HEADER_END, - TK_LANG_NAME_BEGIN, - TK_LANG_NAME_END, - TK_TRANS_NAME_BEGIN, - TK_TRANS_NAME_END, - TK_LOCALE_NAME_BEGIN, - TK_LOCALE_NAME_END, - TK_FLAG_FILE_BEGIN, - TK_FLAG_FILE_END, - TK_PLURAL_COUNT_BEGIN, - TK_PLURAL_COUNT_END, - TK_PLURAL_DEF_BEGIN, - TK_PLURAL_DEF_END, - - //item level - TK_SRC_BEGIN, - TK_SRC_END, - TK_TRG_BEGIN, - TK_TRG_END, - TK_TEXT, - TK_PLURAL_BEGIN, - TK_PLURAL_END, - TK_END - }; - - Token(Type t) : type(t) {} - Type type; - - std::string text; -}; - - -class KnownTokens -{ -public: - typedef std::map<Token::Type, std::string> TokenMap; - - static const TokenMap& getList() - { - static KnownTokens inst; - return inst.tokens; - } - - static std::string text(Token::Type t) - { - auto it = getList().find(t); - return it != getList().end() ? it->second : std::string(); - } - -private: - KnownTokens() - { - //header information - tokens.insert(std::make_pair(Token::TK_HEADER_BEGIN, "<header>")); - tokens.insert(std::make_pair(Token::TK_HEADER_END, "</header>")); - tokens.insert(std::make_pair(Token::TK_LANG_NAME_BEGIN, "<language>")); - tokens.insert(std::make_pair(Token::TK_LANG_NAME_END, "</language>")); - tokens.insert(std::make_pair(Token::TK_TRANS_NAME_BEGIN, "<translator>")); - tokens.insert(std::make_pair(Token::TK_TRANS_NAME_END, "</translator>")); - tokens.insert(std::make_pair(Token::TK_LOCALE_NAME_BEGIN, "<locale>")); - tokens.insert(std::make_pair(Token::TK_LOCALE_NAME_END, "</locale>")); - tokens.insert(std::make_pair(Token::TK_FLAG_FILE_BEGIN, "<image>")); - tokens.insert(std::make_pair(Token::TK_FLAG_FILE_END, "</image>")); - tokens.insert(std::make_pair(Token::TK_PLURAL_COUNT_BEGIN, "<plural_count>")); - tokens.insert(std::make_pair(Token::TK_PLURAL_COUNT_END, "</plural_count>")); - tokens.insert(std::make_pair(Token::TK_PLURAL_DEF_BEGIN, "<plural_definition>")); - tokens.insert(std::make_pair(Token::TK_PLURAL_DEF_END, "</plural_definition>")); - - //item level - tokens.insert(std::make_pair(Token::TK_SRC_BEGIN, "<source>")); - tokens.insert(std::make_pair(Token::TK_SRC_END, "</source>")); - tokens.insert(std::make_pair(Token::TK_TRG_BEGIN, "<target>")); - tokens.insert(std::make_pair(Token::TK_TRG_END, "</target>")); - tokens.insert(std::make_pair(Token::TK_PLURAL_BEGIN, "<pluralform>")); - tokens.insert(std::make_pair(Token::TK_PLURAL_END, "</pluralform>")); - } - TokenMap tokens; -}; - - -class Scanner -{ -public: - Scanner(const std::string& fileStream) : stream(fileStream), pos(stream.begin()) - { - if (zen::startsWith(stream, zen::BYTE_ORDER_MARK_UTF8)) - pos += zen::strLength(zen::BYTE_ORDER_MARK_UTF8); - } - - Token nextToken() - { - //skip whitespace - pos = std::find_if(pos, stream.end(), [](char c) { return !zen::isWhiteSpace(c); }); - - if (pos == stream.end()) - return Token(Token::TK_END); - - for (const auto& token : KnownTokens::getList()) - if (startsWith(token.second)) - { - pos += token.second.size(); - return Token(token.first); - } - - //rest must be "text" - auto itBegin = pos; - while (pos != stream.end() && !startsWithKnownTag()) - pos = std::find(pos + 1, stream.end(), '<'); - - std::string text(itBegin, pos); - - normalize(text); //remove whitespace from end ect. - - if (text.empty() && pos == stream.end()) - return Token(Token::TK_END); - - Token out(Token::TK_TEXT); - out.text = text; - return out; - } - - size_t posRow() const //current row beginning with 0 - { - //count line endings - const size_t crSum = std::count(stream.begin(), pos, '\r'); //carriage returns - const size_t nlSum = std::count(stream.begin(), pos, '\n'); //new lines - assert(crSum == 0 || nlSum == 0 || crSum == nlSum); - return std::max(crSum, nlSum); //be compatible with Linux/Mac/Win - } - - size_t posCol() const //current col beginning with 0 - { - //seek beginning of line - for (auto it = pos; it != stream.begin(); ) - { - --it; - if (*it == '\r' || *it == '\n') - return pos - it - 1; - } - return pos - stream.begin(); - } - -private: - bool startsWithKnownTag() const - { - return std::any_of(KnownTokens::getList().begin(), KnownTokens::getList().end(), - [&](const KnownTokens::TokenMap::value_type& p) { return startsWith(p.second); }); - } - - bool startsWith(const std::string& prefix) const - { - if (stream.end() - pos < static_cast<ptrdiff_t>(prefix.size())) - return false; - return std::equal(prefix.begin(), prefix.end(), pos); - } - - static void normalize(std::string& text) - { - zen::trim(text); //remove whitespace from both ends - - //Delimiter: - //---------- - //Linux: 0xA \n - //Mac: 0xD \r - //Win: 0xD 0xA \r\n <- language files are in Windows format - zen::replace(text, "\r\n", '\n'); // - zen::replace(text, "\r", '\n'); //ensure c-style line breaks - } - - const std::string stream; - std::string::const_iterator pos; -}; - - -class LngParser -{ -public: - LngParser(const std::string& fileStream) : scn(fileStream), tk(scn.nextToken()) {} - - void parse(TranslationMap& out, TranslationPluralMap& pluralOut, TransHeader& header) - { - parseHeader(header); - - try - { - parse_plural::PluralFormInfo pi(header.pluralDefinition, header.pluralCount); - - //items - while (token().type != Token::TK_END) - parseRegular(out, pluralOut, pi); - } - catch (const parse_plural::InvalidPluralForm&) - { - throw ParsingError(L"Invalid plural form definition", scn.posRow(), scn.posCol()); - } - } - - void parseHeader(TransHeader& header) - { - consumeToken(Token::TK_HEADER_BEGIN); - - consumeToken(Token::TK_LANG_NAME_BEGIN); - header.languageName = tk.text; - consumeToken(Token::TK_TEXT); - consumeToken(Token::TK_LANG_NAME_END); - - consumeToken(Token::TK_TRANS_NAME_BEGIN); - header.translatorName = tk.text; - consumeToken(Token::TK_TEXT); - consumeToken(Token::TK_TRANS_NAME_END); - - consumeToken(Token::TK_LOCALE_NAME_BEGIN); - header.localeName = tk.text; - consumeToken(Token::TK_TEXT); - consumeToken(Token::TK_LOCALE_NAME_END); - - consumeToken(Token::TK_FLAG_FILE_BEGIN); - header.flagFile = tk.text; - consumeToken(Token::TK_TEXT); - consumeToken(Token::TK_FLAG_FILE_END); - - consumeToken(Token::TK_PLURAL_COUNT_BEGIN); - header.pluralCount = zen::stringTo<int>(tk.text); - consumeToken(Token::TK_TEXT); - consumeToken(Token::TK_PLURAL_COUNT_END); - - consumeToken(Token::TK_PLURAL_DEF_BEGIN); - header.pluralDefinition = tk.text; - consumeToken(Token::TK_TEXT); - consumeToken(Token::TK_PLURAL_DEF_END); - - consumeToken(Token::TK_HEADER_END); - } - -private: - void parseRegular(TranslationMap& out, TranslationPluralMap& pluralOut, const parse_plural::PluralFormInfo& pluralInfo) - { - consumeToken(Token::TK_SRC_BEGIN); - - if (token().type == Token::TK_PLURAL_BEGIN) - return parsePlural(pluralOut, pluralInfo); - - if (token().type != Token::TK_TEXT) - throw ParsingError(L"Source text empty", scn.posRow(), scn.posCol()); - std::string original = tk.text; - nextToken(); - - consumeToken(Token::TK_SRC_END); - - consumeToken(Token::TK_TRG_BEGIN); - std::string translation; - if (token().type == Token::TK_TEXT) - { - translation = token().text; - nextToken(); - } - consumeToken(Token::TK_TRG_END); - - validateTranslation(original, translation); //throw throw ParsingError - out.insert(std::make_pair(original, translation)); - } - - void parsePlural(TranslationPluralMap& pluralOut, const parse_plural::PluralFormInfo& pluralInfo) - { - //Token::TK_SRC_BEGIN already consumed - - consumeToken(Token::TK_PLURAL_BEGIN); - std::string engSingular = tk.text; - consumeToken(Token::TK_TEXT); - consumeToken(Token::TK_PLURAL_END); - - consumeToken(Token::TK_PLURAL_BEGIN); - std::string engPlural = tk.text; - consumeToken(Token::TK_TEXT); - consumeToken(Token::TK_PLURAL_END); - - consumeToken(Token::TK_SRC_END); - - consumeToken(Token::TK_TRG_BEGIN); - - PluralForms pluralList; - while (token().type == Token::TK_PLURAL_BEGIN) - { - consumeToken(Token::TK_PLURAL_BEGIN); - std::string pluralForm = tk.text; - consumeToken(Token::TK_TEXT); - consumeToken(Token::TK_PLURAL_END); - pluralList.push_back(pluralForm); - } - - consumeToken(Token::TK_TRG_END); - - const SingularPluralPair original(engSingular, engPlural); - validateTranslation(original, pluralList, pluralInfo); - pluralOut.insert(std::make_pair(original, pluralList)); - } - - void validateTranslation(const std::string& original, const std::string& translation) //throw ParsingError - { - if (original.empty()) - throw ParsingError(L"Source translation is empty", scn.posRow(), scn.posCol()); - - if (!translation.empty()) - { - //if original contains placeholder, so should translation! - auto checkPlaceholder = [&](const std::string& placeholder) - { - if (zen::contains(original, placeholder) && - !zen::contains(translation, placeholder)) - throw ParsingError(zen::replaceCpy<std::wstring>(L"Placeholder %x missing in translation", L"%x", zen::utfCvrtTo<std::wstring>(placeholder)), scn.posRow(), scn.posCol()); - }; - checkPlaceholder("%x"); - checkPlaceholder("%y"); - checkPlaceholder("%z"); - - //if source contains ampersand to mark menu accellerator key, so must translation - if (hasSingleAmpersand(original) && !hasSingleAmpersand(translation)) - throw ParsingError(L"Translation is missing the & character to mark an access key for the menu item", scn.posRow(), scn.posCol()); - } - } - - void validateTranslation(const SingularPluralPair& original, const PluralForms& translation, const parse_plural::PluralFormInfo& pluralInfo) //throw ParsingError - { - using namespace zen; - //check the primary placeholder is existing at least for the second english text - if (!contains(original.second, "%x")) - throw ParsingError(L"Plural form source does not contain %x placeholder", scn.posRow(), scn.posCol()); - - if (!translation.empty()) - { - //check for invalid number of plural forms - if (pluralInfo.getCount() != static_cast<int>(translation.size())) - throw ParsingError(replaceCpy(replaceCpy<std::wstring>(L"Invalid number of plural forms; actual: %x, expected: %y", L"%x", numberTo<std::wstring>(translation.size())), L"%y", numberTo<std::wstring>(pluralInfo.getCount())), scn.posRow(), scn.posCol()); - - //check for duplicate plural form translations (catch copy & paste errors for single-number form translations) - for (auto it = translation.begin(); it != translation.end(); ++it) - if (!contains(*it, "%x")) - { - auto it2 = std::find(it + 1, translation.end(), *it); - if (it2 != translation.end()) - throw ParsingError(replaceCpy<std::wstring>(L"Duplicate plural form translation at index position %x", L"%x", numberTo<std::wstring>(it2 - translation.begin())), scn.posRow(), scn.posCol()); - } - - for (int pos = 0; pos < static_cast<int>(translation.size()); ++pos) - if (pluralInfo.isSingleNumberForm(pos)) - { - //translation needs to use decimal number if english source does so (e.g. frequently changing text like statistics) - if (contains(original.first, "%x") || - contains(original.first, "1")) - { - const int firstNumber = pluralInfo.getFirstNumber(pos); - if (!(contains(translation[pos], "%x") || - contains(translation[pos], numberTo<std::string>(firstNumber)))) - throw ParsingError(replaceCpy<std::wstring>(replaceCpy<std::wstring>(L"Plural form translation at index position %y needs to use the decimal number %z or the %x placeholder", - L"%y", numberTo<std::wstring>(pos)), L"%z", numberTo<std::wstring>(firstNumber)), scn.posRow(), scn.posCol()); - } - } - else - { - //ensure the placeholder is used when needed - if (!contains(translation[pos], "%x")) - throw ParsingError(replaceCpy<std::wstring>(L"Plural form at index position %y is missing the %x placeholder", L"%y", numberTo<std::wstring>(pos)), scn.posRow(), scn.posCol()); - } - - auto checkSecondaryPlaceholder = [&](const std::string& placeholder) - { - //make sure secondary placeholder is used in both source texts (or none) - if (zen::contains(original.first, placeholder) || - zen::contains(original.second, placeholder)) - { - if (!zen::contains(original.first, placeholder) || - !zen::contains(original.second, placeholder)) - throw ParsingError(zen::replaceCpy<std::wstring>(L"Placeholder %x missing in plural form source", L"%x", zen::utfCvrtTo<std::wstring>(placeholder)), scn.posRow(), scn.posCol()); - - //secondary placeholder is required for all plural forms - if (!std::all_of(translation.begin(), translation.end(), [&](const std::string& pform) { return zen::contains(pform, placeholder); })) - throw ParsingError(zen::replaceCpy<std::wstring>(L"Placeholder %x missing in plural form translation", L"%x", zen::utfCvrtTo<std::wstring>(placeholder)), scn.posRow(), scn.posCol()); - } - }; - - checkSecondaryPlaceholder("%y"); - checkSecondaryPlaceholder("%z"); - } - } - - static bool hasSingleAmpersand(const std::string& str) - { - size_t pos = 0; - for (;;) - { - pos = str.find('&', pos); - if (pos == std::string::npos) - return false; - - bool freeBefore = pos == 0 || str[pos - 1] != '&'; - bool freeAfter = pos >= str.size() - 1 || str[pos + 1] != '&'; //str.size() > 0 here! - - if (freeBefore && freeAfter) //make sure to not catch && which windows resolves as just one & for display! - return true; - ++pos; - } - } - - void nextToken() { tk = scn.nextToken(); } - const Token& token() const { return tk; } - - void consumeToken(Token::Type t) //throw ParsingError - { - expectToken(t); //throw ParsingError - nextToken(); - } - - void expectToken(Token::Type t) //throw ParsingError - { - if (token().type != t) - throw ParsingError(L"Unexpected token", scn.posRow(), scn.posCol()); - } - - Scanner scn; - Token tk; -}; - - -inline -void parseLng(const std::string& fileStream, TransHeader& header, TranslationMap& out, TranslationPluralMap& pluralOut) //throw ParsingError -{ - out.clear(); - pluralOut.clear(); - - LngParser(fileStream).parse(out, pluralOut, header); -} - - -inline -void parseHeader(const std::string& fileStream, TransHeader& header) //throw ParsingError -{ - LngParser(fileStream).parseHeader(header); -} - - -inline -void formatMultiLineText(std::string& text) -{ - assert(!zen::contains(text, "\r\n")); - - if (text.find('\n') != std::string::npos) //multiple lines - { - if (*text.begin() != '\n') - text = '\n' + text; - if (*text.rbegin() != '\n') - text += '\n'; - } -} - - -std::string generateLng(const TranslationUnorderedList& in, const TransHeader& header) -{ - std::string out; - //header - out += KnownTokens::text(Token::TK_HEADER_BEGIN) + '\n'; - - out += '\t' + KnownTokens::text(Token::TK_LANG_NAME_BEGIN); - out += header.languageName; - out += KnownTokens::text(Token::TK_LANG_NAME_END) + '\n'; - - out += '\t' + KnownTokens::text(Token::TK_TRANS_NAME_BEGIN); - out += header.translatorName; - out += KnownTokens::text(Token::TK_TRANS_NAME_END) + '\n'; - - out += '\t' + KnownTokens::text(Token::TK_LOCALE_NAME_BEGIN); - out += header.localeName; - out += KnownTokens::text(Token::TK_LOCALE_NAME_END) + '\n'; - - out += '\t' + KnownTokens::text(Token::TK_FLAG_FILE_BEGIN); - out += header.flagFile; - out += KnownTokens::text(Token::TK_FLAG_FILE_END) + '\n'; - - out += '\t' + KnownTokens::text(Token::TK_PLURAL_COUNT_BEGIN); - out += zen::numberTo<std::string>(header.pluralCount); - out += KnownTokens::text(Token::TK_PLURAL_COUNT_END) + '\n'; - - out += '\t' + KnownTokens::text(Token::TK_PLURAL_DEF_BEGIN); - out += header.pluralDefinition; - out += KnownTokens::text(Token::TK_PLURAL_DEF_END) + '\n'; - - out += KnownTokens::text(Token::TK_HEADER_END) + '\n'; - - out += '\n'; - - - in.visitItems([&](const TranslationMap::value_type& trans) - { - std::string original = trans.first; - std::string translation = trans.second; - - formatMultiLineText(original); - formatMultiLineText(translation); - - out += KnownTokens::text(Token::TK_SRC_BEGIN); - out += original; - out += KnownTokens::text(Token::TK_SRC_END) + '\n'; - - out += KnownTokens::text(Token::TK_TRG_BEGIN); - out += translation; - out += KnownTokens::text(Token::TK_TRG_END) + '\n' + '\n'; - }, - [&](const TranslationPluralMap::value_type& transPlural) - { - std::string engSingular = transPlural.first.first; - std::string engPlural = transPlural.first.second; - const PluralForms& forms = transPlural.second; - - formatMultiLineText(engSingular); - formatMultiLineText(engPlural); - - out += KnownTokens::text(Token::TK_SRC_BEGIN) + '\n'; - out += KnownTokens::text(Token::TK_PLURAL_BEGIN); - out += engSingular; - out += KnownTokens::text(Token::TK_PLURAL_END) + '\n'; - out += KnownTokens::text(Token::TK_PLURAL_BEGIN); - out += engPlural; - out += KnownTokens::text(Token::TK_PLURAL_END) + '\n'; - out += KnownTokens::text(Token::TK_SRC_END) + '\n'; - - out += KnownTokens::text(Token::TK_TRG_BEGIN); - out += '\n'; - - for (std::string plForm : forms) - { - formatMultiLineText(plForm); - - out += KnownTokens::text(Token::TK_PLURAL_BEGIN); - out += plForm; - out += KnownTokens::text(Token::TK_PLURAL_END) + '\n'; - } - out += KnownTokens::text(Token::TK_TRG_END) + '\n' + '\n'; - }); - - assert(!zen::contains(out, "\r\n") && !zen::contains(out, "\r")); - return zen::replaceCpy(out, '\n', "\r\n"); //back to win line endings -} -} - -#endif //PARSE_LNG_HEADER_INCLUDED |