21#include <simgear/debug/logstream.hxx>
22#include <simgear/props/props.hxx>
23#include <simgear/misc/strutils.hxx>
29namespace strutils = simgear::strutils;
33 _languageId(languageId), _domain(domain)
51 std::string tag(
name);
55 }
else if (tag ==
"file") {
56 const char* origName_c = atts.getValue(
"original");
58 if (origName_c && !std::strcmp(origName_c,
"Obsolete_PO_entries")) {
61 }
else if (tag ==
"trans-unit") {
62 startTransUnitElement(atts);
63 }
else if (tag ==
"group") {
64 const char* resType_c = atts.getValue(
"restype");
66 if (resType_c && !std::strcmp(resType_c,
67 "x-trolltech-linguist-context")) {
68 startContextGroup(atts.getValue(
"resname"));
69 }
else if (resType_c && !std::strcmp(resType_c,
"x-gettext-plurals")) {
70 startPluralGroup(atts.getValue(
"id"));
75void XLIFFParser::startTransUnitElement(
const XMLAttributes &atts)
77 const char* id_c = atts.getValue(
"id");
80 "Error while parsing a .xlf file",
81 "<trans-unit> element with no 'id' attribute.",
82 "Illegal <trans-unit> element with no 'id' attribute at " +
83 std::to_string(getLine()) +
" of " + getPath() +
".");
86 if (!_pluralGroupId.empty()) {
87 checkIdOfPluralTransUnit(id_c);
88 _expectedPluralFormIndex++;
90 std::tie(_resource, _basicId, _index) = parseSimpleTransUnitId(id_c);
94void XLIFFParser::checkIdOfPluralTransUnit(std::string transUnitId)
96 const std::string expectedId =
97 _pluralGroupId +
"[" + std::to_string(_expectedPluralFormIndex) +
"]";
99 if (transUnitId != expectedId) {
101 "Error while parsing a .xlf file",
102 "Unexpected value '" + transUnitId +
"' for 'id' attribute of "
103 "<trans-unit> element found inside plural group with id='" +
104 _pluralGroupId +
"' (expected: '" + expectedId +
"') at " +
105 std::to_string(getLine()) +
" of " + getPath() +
".");
109std::tuple<std::string, std::string, int>
110XLIFFParser::parseSimpleTransUnitId(
const std::string&
id)
112 std::regex simpleIdRegexp(R
"(^([^/:]+)/([^/:]+):(\d+)$)");
115 if (std::regex_match(
id, results, simpleIdRegexp)) {
116 const int elementIdx = strutils::readNonNegativeInt<int>(results.str(3));
117 return std::make_tuple(results.str(1), results.str(2), elementIdx);
120 "Error while parsing a .xlf file",
121 "Unexpected 'id' attribute value in a <trans-unit> or <group>.",
122 "Unexpected syntax for a <trans-unit> or "
123 "<group restype=\"x-gettext-plurals\" ...> 'id' attribute: '" +
124 id +
"' at " + std::to_string(getLine()) +
" of " + getPath() +
".");
130 std::string tag(
name);
133 _skipElements =
false;
134 }
else if (_skipElements) {
136 }
else if (tag ==
"source") {
138 }
else if (tag ==
"target") {
139 _targetTexts.push_back(std::move(_text));
140 }
else if (tag ==
"trans-unit") {
141 if (_pluralGroupId.empty()) {
142 finishTransUnit(
false );
144 }
else if (tag ==
"group") {
145 assert(_groupsStack.size() > 0);
147 switch (_groupsStack.top()->type) {
148 case GroupType::context:
151 case GroupType::plural:
160void XLIFFParser::startContextGroup(
const char* resname_c)
162 if (resname_c ==
nullptr) {
163 SG_LOG(SG_GENERAL, SG_WARN,
164 "XLIFF group with restype=\"x-trolltech-linguist-context\" has "
165 "no 'resname' attribute: line " << getLine() <<
" of " <<
170 const std::string resname{resname_c};
172 if (resname.empty()) {
173 SG_LOG(SG_GENERAL, SG_WARN,
174 "XLIFF group with restype=\"x-trolltech-linguist-context\" has "
175 "an empty 'resname' attribute: line " << getLine() <<
" of " <<
183 _currentResource = _domain->getOrCreateResource(resname);
184 _groupsStack.push(std::make_unique<ContextGroup>(resname));
187void XLIFFParser::startPluralGroup(
const char* id_c)
189 if (id_c ==
nullptr) {
190 SG_LOG(SG_GENERAL, SG_WARN,
191 "XLIFF group with restype=\"x-gettext-plurals\" has "
192 "no 'id' attribute: at line " << getLine() <<
" of " <<
198 std::string resource;
199 std::tie(resource, _basicId, _index) = parseSimpleTransUnitId(id_c);
201 if (resource != _resource) {
203 "Error while parsing a .xlf file",
204 "Unexpected 'id' attribute value in a <group "
205 "restype=\"x-gettext-plurals\" ...> element.",
206 "Attribute 'id' of a <group restype=\"x-gettext-plurals\" ...> "
207 "element inside plural group '" + _pluralGroupId +
208 "' specifies resource '" + resource +
"' whereas "
209 "the current context group declares resname='" + _resource +
"' "
210 "(attribute id='" + std::string(id_c) +
"' at line " +
211 std::to_string(getLine()) +
" of " + getPath() +
").");
214 _pluralGroupId = id_c;
215 _expectedPluralFormIndex = 0;
216 _groupsStack.push(std::make_unique<PluralGroup>(id_c));
219void XLIFFParser::endContextGroup()
221 assert(
dynamic_cast<ContextGroup*
>(_groupsStack.top().get())->name
226 _currentResource.reset();
229void XLIFFParser::endPluralGroup()
231 assert(
dynamic_cast<PluralGroup*
>(_groupsStack.top().get())->id
234 finishTransUnit(
true );
237 _pluralGroupId.clear();
240void XLIFFParser::finishTransUnit(
bool hasPlural)
242 if (!_currentResource) {
243 SG_LOG(SG_GENERAL, SG_WARN,
244 "XLIFF trans-unit without enclosing resource <group>: at line "
245 << getLine() <<
" of " << getPath());
246 }
else if (hasPlural) {
247 checkNumberOfPluralForms(_targetTexts.size());
248 _currentResource->setTargetTexts(std::move(_basicId), _index,
249 std::move(_targetTexts));
250 }
else if (!_targetTexts.empty()) {
251 _currentResource->setFirstTargetText(std::move(_basicId), _index,
252 std::move(_targetTexts[0]));
256 _targetTexts.clear();
259void XLIFFParser::checkNumberOfPluralForms(std::size_t nbPluralFormsInTransUnit)
261 const std::size_t nbPluralFormsInCode =
264 if (nbPluralFormsInTransUnit != nbPluralFormsInCode) {
266 "Error while parsing a .xlf file",
267 "Mismatch between the number of plural forms found in a "
268 "group with restype=\"x-gettext-plurals\" and the number of "
269 "plural forms declared in LanguageInfo.cxx for language '" +
271 "Found a plural group with " +
272 std::to_string(nbPluralFormsInTransUnit) +
" plural forms, however "
273 "the number of plural forms for this language as set in "
274 "LanguageInfo.cxx is " + std::to_string(nbPluralFormsInCode) +
275 " (at " + std::to_string(getLine()) +
" of " + getPath() +
").");
281 _text += std::string(s,
static_cast<size_t>(len));
293 SG_LOG(SG_GENERAL, SG_WARN,
"Warning: " << message <<
" (" << line <<
',' << column <<
')');
297XLIFFParser::Group::Group(GroupType type_) : type(type_)
300XLIFFParser::ContextGroup::ContextGroup(
const std::string& name_)
301 : Group(GroupType::context),
name(name_)
304XLIFFParser::PluralGroup::PluralGroup(
const std::string& id_)
305 : Group(GroupType::plural), id(id_)
Information on plural forms for the supported languages.
Parse an XLIFF 1.2 XML file.
static std::size_t getNumberOfPluralForms(const std::string &languageId)
Return the number of plural forms in the specified language.
Class that holds translation resources within a domain.
void data(const char *s, int len) override
void pi(const char *target, const char *data) override
XLIFFParser(const std::string &languageId, TranslationDomain *domain)
void endElement(const char *name) override
void startElement(const char *name, const XMLAttributes &atts) override
void warning(const char *message, int line, int column) override
FlightPlan.hxx - defines a full flight-plan object, including departure, cruise, arrival information ...
void fatalMessageBoxThenExit(const std::string &caption, const std::string &msg, const std::string &moreText, int exitStatus, bool reportToSentry)