#include "XLIFFFile.h" #include #include #include const char* const XLIFF_XMLNS = "urn:oasis:names:tc:xliff:document:1.2"; const char *const NS_MAP[] = { "", XLIFF_XMLNS, "xml", XMLNS_XMLNS, NULL, NULL }; const XMLNamespaceMap XLIFF_NAMESPACES(NS_MAP); int XLIFFFile::File::Compare(const XLIFFFile::File& that) const { if (filename != that.filename) { return filename < that.filename ? -1 : 1; } return 0; } // ===================================================================================== XLIFFFile::XLIFFFile() { } XLIFFFile::~XLIFFFile() { } static XMLNode* get_unique_node(const XMLNode* parent, const string& ns, const string& name, bool required) { size_t count = parent->CountElementsByName(ns, name); if (count == 1) { return parent->GetElementByNameAt(ns, name, 0); } else { if (required) { SourcePos pos = count == 0 ? parent->Position() : parent->GetElementByNameAt(XLIFF_XMLNS, name, 1)->Position(); pos.Error("<%s> elements must contain exactly one <%s> element", parent->Name().c_str(), name.c_str()); } return NULL; } } XLIFFFile* XLIFFFile::Parse(const string& filename) { XLIFFFile* result = new XLIFFFile(); XMLNode* root = NodeHandler::ParseFile(filename, XMLNode::PRETTY); if (root == NULL) { return NULL; } // vector files = root->GetElementsByName(XLIFF_XMLNS, "file"); for (size_t i=0; iGetAttribute("", "datatype", ""); string originalFile = file->GetAttribute("", "original", ""); Configuration sourceConfig; sourceConfig.locale = file->GetAttribute("", "source-language", ""); result->m_sourceConfig = sourceConfig; Configuration targetConfig; targetConfig.locale = file->GetAttribute("", "target-language", ""); result->m_targetConfig = targetConfig; result->m_currentVersion = file->GetAttribute("", "build-num", ""); result->m_oldVersion = "old"; // XMLNode* body = get_unique_node(file, XLIFF_XMLNS, "body", true); if (body == NULL) continue; // vector transUnits = body->GetElementsByName(XLIFF_XMLNS, "trans-unit"); for (size_t j=0; jGetAttribute("", "id", ""); if (rawID == "") { transUnit->Position().Error(" tag requires an id"); continue; } string id; int index; if (!StringResource::ParseTypedID(rawID, &id, &index)) { transUnit->Position().Error(" has invalid id '%s'\n", rawID.c_str()); continue; } // XMLNode* source = get_unique_node(transUnit, XLIFF_XMLNS, "source", false); if (source != NULL) { XMLNode* node = source->Clone(); node->SetPrettyRecursive(XMLNode::EXACT); result->AddStringResource(StringResource(source->Position(), originalFile, sourceConfig, id, index, node, CURRENT_VERSION, result->m_currentVersion)); } // XMLNode* target = get_unique_node(transUnit, XLIFF_XMLNS, "target", false); if (target != NULL) { XMLNode* node = target->Clone(); node->SetPrettyRecursive(XMLNode::EXACT); result->AddStringResource(StringResource(target->Position(), originalFile, targetConfig, id, index, node, CURRENT_VERSION, result->m_currentVersion)); } // XMLNode* altTrans = get_unique_node(transUnit, XLIFF_XMLNS, "alt-trans", false); if (altTrans != NULL) { // XMLNode* altSource = get_unique_node(altTrans, XLIFF_XMLNS, "source", false); if (altSource != NULL) { XMLNode* node = altSource->Clone(); node->SetPrettyRecursive(XMLNode::EXACT); result->AddStringResource(StringResource(altSource->Position(), originalFile, sourceConfig, id, index, node, OLD_VERSION, result->m_oldVersion)); } // XMLNode* altTarget = get_unique_node(altTrans, XLIFF_XMLNS, "target", false); if (altTarget != NULL) { XMLNode* node = altTarget->Clone(); node->SetPrettyRecursive(XMLNode::EXACT); result->AddStringResource(StringResource(altTarget->Position(), originalFile, targetConfig, id, index, node, OLD_VERSION, result->m_oldVersion)); } } } } delete root; return result; } XLIFFFile* XLIFFFile::Create(const Configuration& sourceConfig, const Configuration& targetConfig, const string& currentVersion) { XLIFFFile* result = new XLIFFFile(); result->m_sourceConfig = sourceConfig; result->m_targetConfig = targetConfig; result->m_currentVersion = currentVersion; return result; } set XLIFFFile::Files() const { set result; for (vector::const_iterator f = m_files.begin(); f != m_files.end(); f++) { result.insert(f->filename); } return result; } void XLIFFFile::AddStringResource(const StringResource& str) { string id = str.TypedID(); File* f = NULL; const size_t I = m_files.size(); for (size_t i=0; itransUnits.size(); TransUnit* g = NULL; for (size_t j=0; jtransUnits[j].id == id) { g = &f->transUnits[j]; } } if (g == NULL) { TransUnit group; group.id = id; f->transUnits.push_back(group); g = &f->transUnits[J]; } StringResource* res = find_string_res(*g, str); if (res == NULL) { return ; } if (res->id != "") { str.pos.Error("Duplicate string resource: %s", res->id.c_str()); res->pos.Error("Previous definition here"); return ; } *res = str; m_strings.insert(str); } void XLIFFFile::Filter(bool (*func)(const string&,const TransUnit&,void*), void* cookie) { const size_t I = m_files.size(); for (size_t ix=0, i=I-1; ixType() == XMLNode::TEXT) { addTo->EditChildren().push_back(original->Clone()); return 0; } else { string ctype; if (original->Namespace() == "") { if (original->Name() == "b") { ctype = "bold"; } else if (original->Name() == "i") { ctype = "italic"; } else if (original->Name() == "u") { ctype = "underline"; } } if (ctype != "") { vector attrs; attrs.push_back(XMLAttribute(XLIFF_XMLNS, "ctype", ctype)); XMLNode* copy = XMLNode::NewElement(original->Position(), XLIFF_XMLNS, "g", attrs, XMLNode::EXACT); const vector& children = original->Children(); size_t I = children.size(); for (size_t i=0; iNamespace() == XLIFF_XMLNS) { addTo->EditChildren().push_back(original->Clone()); return 0; } else { if (original->Namespace() == "") { // flatten out the tag into ph tags -- but only if there is no namespace // that's still unsupported because propagating the xmlns attribute is hard. vector attrs; char idStr[30]; (*phID)++; sprintf(idStr, "id-%d", *phID); attrs.push_back(XMLAttribute(XLIFF_XMLNS, "id", idStr)); if (original->Children().size() == 0) { XMLNode* ph = XMLNode::NewElement(original->Position(), XLIFF_XMLNS, "ph", attrs, XMLNode::EXACT); ph->EditChildren().push_back( XMLNode::NewText(original->Position(), original->ToString(XLIFF_NAMESPACES), XMLNode::EXACT)); addTo->EditChildren().push_back(ph); } else { XMLNode* begin = XMLNode::NewElement(original->Position(), XLIFF_XMLNS, "bpt", attrs, XMLNode::EXACT); begin->EditChildren().push_back( XMLNode::NewText(original->Position(), original->OpenTagToString(XLIFF_NAMESPACES, XMLNode::EXACT), XMLNode::EXACT)); XMLNode* end = XMLNode::NewElement(original->Position(), XLIFF_XMLNS, "ept", attrs, XMLNode::EXACT); string endText = "Name(); endText += ">"; end->EditChildren().push_back(XMLNode::NewText(original->Position(), endText, XMLNode::EXACT)); addTo->EditChildren().push_back(begin); const vector& children = original->Children(); size_t I = children.size(); for (size_t i=0; iEditChildren().push_back(end); } return err; } else { original->Position().Error("invalid <%s> element in <%s> tag\n", original->Name().c_str(), name.c_str()); return 1; } } } } } XMLNode* create_string_node(const StringResource& str, const string& name) { vector attrs; attrs.push_back(XMLAttribute(XMLNS_XMLNS, "space", "preserve")); XMLNode* node = XMLNode::NewElement(str.pos, XLIFF_XMLNS, name, attrs, XMLNode::EXACT); const vector& children = str.value->Children(); size_t I = children.size(); int err = 0; for (size_t i=0; i { vector attrs; XLIFF_NAMESPACES.AddToAttributes(&attrs); attrs.push_back(XMLAttribute(XLIFF_XMLNS, "version", "1.2")); root = XMLNode::NewElement(GENERATED_POS, XLIFF_XMLNS, "xliff", attrs, XMLNode::PRETTY); } vector groups; // vector files = m_files; sort(files.begin(), files.end()); const size_t I = files.size(); for (size_t i=0; i fileAttrs; fileAttrs.push_back(XMLAttribute(XLIFF_XMLNS, "datatype", "x-android-res")); fileAttrs.push_back(XMLAttribute(XLIFF_XMLNS, "original", file.filename)); struct timeval tv; struct timezone tz; gettimeofday(&tv, &tz); fileAttrs.push_back(XMLAttribute(XLIFF_XMLNS, "date", trim_string(ctime(&tv.tv_sec)))); fileAttrs.push_back(XMLAttribute(XLIFF_XMLNS, "source-language", m_sourceConfig.locale)); fileAttrs.push_back(XMLAttribute(XLIFF_XMLNS, "target-language", m_targetConfig.locale)); fileAttrs.push_back(XMLAttribute(XLIFF_XMLNS, "build-num", m_currentVersion)); XMLNode* fileNode = XMLNode::NewElement(GENERATED_POS, XLIFF_XMLNS, "file", fileAttrs, XMLNode::PRETTY); root->EditChildren().push_back(fileNode); // XMLNode* bodyNode = XMLNode::NewElement(GENERATED_POS, XLIFF_XMLNS, "body", vector(), XMLNode::PRETTY); fileNode->EditChildren().push_back(bodyNode); // vector transUnits = file.transUnits; sort(transUnits.begin(), transUnits.end(), compare_id); const size_t J = transUnits.size(); for (size_t j=0; j tuAttrs; // strings start with string: tuAttrs.push_back(XMLAttribute(XLIFF_XMLNS, "id", transUnit.id)); XMLNode* transUnitNode = XMLNode::NewElement(GENERATED_POS, XLIFF_XMLNS, "trans-unit", tuAttrs, XMLNode::PRETTY); bodyNode->EditChildren().push_back(transUnitNode); // if (transUnit.source.comment != "") { vector extradataAttrs; XMLNode* extraNode = XMLNode::NewElement(GENERATED_POS, XLIFF_XMLNS, "extradata", extradataAttrs, XMLNode::EXACT); transUnitNode->EditChildren().push_back(extraNode); extraNode->EditChildren().push_back( XMLNode::NewText(GENERATED_POS, transUnit.source.comment, XMLNode::PRETTY)); } // if (transUnit.source.id != "") { transUnitNode->EditChildren().push_back( create_string_node(transUnit.source, "source")); } // if (transUnit.target.id != "") { transUnitNode->EditChildren().push_back( create_string_node(transUnit.target, "target")); } // if (transUnit.altSource.id != "" || transUnit.altTarget.id != "" || transUnit.rejectComment != "") { vector altTransAttrs; XMLNode* altTransNode = XMLNode::NewElement(GENERATED_POS, XLIFF_XMLNS, "alt-trans", altTransAttrs, XMLNode::PRETTY); transUnitNode->EditChildren().push_back(altTransNode); // if (transUnit.rejectComment != "") { vector extradataAttrs; XMLNode* extraNode = XMLNode::NewElement(GENERATED_POS, XLIFF_XMLNS, "extradata", extradataAttrs, XMLNode::EXACT); altTransNode->EditChildren().push_back(extraNode); extraNode->EditChildren().push_back( XMLNode::NewText(GENERATED_POS, transUnit.rejectComment, XMLNode::PRETTY)); } // if (transUnit.altSource.id != "") { altTransNode->EditChildren().push_back( create_string_node(transUnit.altSource, "source")); } // if (transUnit.altTarget.id != "") { altTransNode->EditChildren().push_back( create_string_node(transUnit.altTarget, "target")); } } } } return root; } string XLIFFFile::ToString() const { XMLNode* xml = ToXMLNode(); string s = "\n"; s += xml->ToString(XLIFF_NAMESPACES); delete xml; s += '\n'; return s; } Stats XLIFFFile::GetStats(const string& config) const { Stats stat; stat.config = config; stat.files = m_files.size(); stat.toBeTranslated = 0; stat.noComments = 0; for (vector::const_iterator file=m_files.begin(); file!=m_files.end(); file++) { stat.toBeTranslated += file->transUnits.size(); for (vector::const_iterator tu=file->transUnits.begin(); tu!=file->transUnits.end(); tu++) { if (tu->source.comment == "") { stat.noComments++; } } } stat.totalStrings = stat.toBeTranslated; return stat; }