diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
commit | 54b6cfa9a9e5b861a9930af873580d6dc20f773c (patch) | |
tree | 35051494d2af230dce54d6b31c6af8fc24091316 /tools/localize/XLIFFFile.cpp | |
download | frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.zip frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.tar.gz frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.tar.bz2 |
Initial Contribution
Diffstat (limited to 'tools/localize/XLIFFFile.cpp')
-rw-r--r-- | tools/localize/XLIFFFile.cpp | 608 |
1 files changed, 608 insertions, 0 deletions
diff --git a/tools/localize/XLIFFFile.cpp b/tools/localize/XLIFFFile.cpp new file mode 100644 index 0000000..5da05a2 --- /dev/null +++ b/tools/localize/XLIFFFile.cpp @@ -0,0 +1,608 @@ +#include "XLIFFFile.h" + +#include <sys/time.h> +#include <time.h> + +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; + } + + // <file> + vector<XMLNode*> files = root->GetElementsByName(XLIFF_XMLNS, "file"); + for (size_t i=0; i<files.size(); i++) { + XMLNode* file = files[i]; + + string datatype = file->GetAttribute("", "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"; + + // <body> + XMLNode* body = get_unique_node(file, XLIFF_XMLNS, "body", true); + if (body == NULL) continue; + + // <trans-unit> + vector<XMLNode*> transUnits = body->GetElementsByName(XLIFF_XMLNS, "trans-unit"); + for (size_t j=0; j<transUnits.size(); j++) { + XMLNode* transUnit = transUnits[j]; + + string rawID = transUnit->GetAttribute("", "id", ""); + if (rawID == "") { + transUnit->Position().Error("<trans-unit> tag requires an id"); + continue; + } + string id; + int index; + + if (!StringResource::ParseTypedID(rawID, &id, &index)) { + transUnit->Position().Error("<trans-unit> has invalid id '%s'\n", rawID.c_str()); + continue; + } + + // <source> + 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)); + } + + // <target> + 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)); + } + + // <alt-trans> + XMLNode* altTrans = get_unique_node(transUnit, XLIFF_XMLNS, "alt-trans", false); + if (altTrans != NULL) { + // <source> + 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)); + } + + // <target> + 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<string> +XLIFFFile::Files() const +{ + set<string> result; + for (vector<File>::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; i<I; i++) { + if (m_files[i].filename == str.file) { + f = &m_files[i]; + break; + } + } + if (f == NULL) { + File file; + file.filename = str.file; + m_files.push_back(file); + f = &m_files[I]; + } + + const size_t J = f->transUnits.size(); + TransUnit* g = NULL; + for (size_t j=0; j<J; j++) { + if (f->transUnits[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; ix<I; ix++, i--) { + File& file = m_files[i]; + + const size_t J = file.transUnits.size(); + for (size_t jx=0, j=J-1; jx<J; jx++, j--) { + TransUnit& tu = file.transUnits[j]; + + bool keep = func(file.filename, tu, cookie); + if (!keep) { + if (tu.source.id != "") { + m_strings.erase(tu.source); + } + if (tu.target.id != "") { + m_strings.erase(tu.target); + } + if (tu.altSource.id != "") { + m_strings.erase(tu.altSource); + } + if (tu.altTarget.id != "") { + m_strings.erase(tu.altTarget); + } + file.transUnits.erase(file.transUnits.begin()+j); + } + } + if (file.transUnits.size() == 0) { + m_files.erase(m_files.begin()+i); + } + } +} + +void +XLIFFFile::Map(void (*func)(const string&,TransUnit*,void*), void* cookie) +{ + const size_t I = m_files.size(); + for (size_t i=0; i<I; i++) { + File& file = m_files[i]; + + const size_t J = file.transUnits.size(); + for (size_t j=0; j<J; j++) { + func(file.filename, &(file.transUnits[j]), cookie); + } + } +} + +TransUnit* +XLIFFFile::EditTransUnit(const string& filename, const string& id) +{ + const size_t I = m_files.size(); + for (size_t ix=0, i=I-1; ix<I; ix++, i--) { + File& file = m_files[i]; + if (file.filename == filename) { + const size_t J = file.transUnits.size(); + for (size_t jx=0, j=J-1; jx<J; jx++, j--) { + TransUnit& tu = file.transUnits[j]; + if (tu.id == id) { + return &tu; + } + } + } + } + return NULL; +} + +StringResource* +XLIFFFile::find_string_res(TransUnit& g, const StringResource& str) +{ + int index; + if (str.version == CURRENT_VERSION) { + index = 0; + } + else if (str.version == OLD_VERSION) { + index = 2; + } + else { + str.pos.Error("Internal Error %s:%d\n", __FILE__, __LINE__); + return NULL; + } + if (str.config == m_sourceConfig) { + // index += 0; + } + else if (str.config == m_targetConfig) { + index += 1; + } + else { + str.pos.Error("unknown config for string %s: %s", str.id.c_str(), + str.config.ToString().c_str()); + return NULL; + } + switch (index) { + case 0: + return &g.source; + case 1: + return &g.target; + case 2: + return &g.altSource; + case 3: + return &g.altTarget; + } + str.pos.Error("Internal Error %s:%d\n", __FILE__, __LINE__); + return NULL; +} + +int +convert_html_to_xliff(const XMLNode* original, const string& name, XMLNode* addTo, int* phID) +{ + int err = 0; + if (original->Type() == 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<XMLAttribute> attrs; + attrs.push_back(XMLAttribute(XLIFF_XMLNS, "ctype", ctype)); + XMLNode* copy = XMLNode::NewElement(original->Position(), XLIFF_XMLNS, "g", + attrs, XMLNode::EXACT); + + const vector<XMLNode*>& children = original->Children(); + size_t I = children.size(); + for (size_t i=0; i<I; i++) { + err |= convert_html_to_xliff(children[i], name, copy, phID); + } + return err; + } + else { + if (original->Namespace() == 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<XMLAttribute> 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 = "</"; + endText += original->Name(); + endText += ">"; + end->EditChildren().push_back(XMLNode::NewText(original->Position(), + endText, XMLNode::EXACT)); + + addTo->EditChildren().push_back(begin); + + const vector<XMLNode*>& children = original->Children(); + size_t I = children.size(); + for (size_t i=0; i<I; i++) { + err |= convert_html_to_xliff(children[i], name, addTo, phID); + } + + addTo->EditChildren().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<XMLAttribute> attrs; + attrs.push_back(XMLAttribute(XMLNS_XMLNS, "space", "preserve")); + XMLNode* node = XMLNode::NewElement(str.pos, XLIFF_XMLNS, name, attrs, XMLNode::EXACT); + + const vector<XMLNode*>& children = str.value->Children(); + size_t I = children.size(); + int err = 0; + for (size_t i=0; i<I; i++) { + int phID = 0; + err |= convert_html_to_xliff(children[i], name, node, &phID); + } + + if (err != 0) { + delete node; + } + return node; +} + +static bool +compare_id(const TransUnit& lhs, const TransUnit& rhs) +{ + string lid, rid; + int lindex, rindex; + StringResource::ParseTypedID(lhs.id, &lid, &lindex); + StringResource::ParseTypedID(rhs.id, &rid, &rindex); + if (lid < rid) return true; + if (lid == rid && lindex < rindex) return true; + return false; +} + +XMLNode* +XLIFFFile::ToXMLNode() const +{ + XMLNode* root; + size_t N; + + // <xliff> + { + vector<XMLAttribute> 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<TransUnit> groups; + + // <file> + vector<File> files = m_files; + sort(files.begin(), files.end()); + const size_t I = files.size(); + for (size_t i=0; i<I; i++) { + const File& file = files[i]; + + vector<XMLAttribute> 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); + + // <body> + XMLNode* bodyNode = XMLNode::NewElement(GENERATED_POS, XLIFF_XMLNS, "body", + vector<XMLAttribute>(), XMLNode::PRETTY); + fileNode->EditChildren().push_back(bodyNode); + + // <trans-unit> + vector<TransUnit> transUnits = file.transUnits; + sort(transUnits.begin(), transUnits.end(), compare_id); + const size_t J = transUnits.size(); + for (size_t j=0; j<J; j++) { + const TransUnit& transUnit = transUnits[j]; + + vector<XMLAttribute> 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); + + // <extradata> + if (transUnit.source.comment != "") { + vector<XMLAttribute> 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)); + } + + // <source> + if (transUnit.source.id != "") { + transUnitNode->EditChildren().push_back( + create_string_node(transUnit.source, "source")); + } + + // <target> + if (transUnit.target.id != "") { + transUnitNode->EditChildren().push_back( + create_string_node(transUnit.target, "target")); + } + + // <alt-trans> + if (transUnit.altSource.id != "" || transUnit.altTarget.id != "" + || transUnit.rejectComment != "") { + vector<XMLAttribute> altTransAttrs; + XMLNode* altTransNode = XMLNode::NewElement(GENERATED_POS, XLIFF_XMLNS, "alt-trans", + altTransAttrs, XMLNode::PRETTY); + transUnitNode->EditChildren().push_back(altTransNode); + + // <extradata> + if (transUnit.rejectComment != "") { + vector<XMLAttribute> 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)); + } + + // <source> + if (transUnit.altSource.id != "") { + altTransNode->EditChildren().push_back( + create_string_node(transUnit.altSource, "source")); + } + + // <target> + 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 = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\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<File>::const_iterator file=m_files.begin(); file!=m_files.end(); file++) { + stat.toBeTranslated += file->transUnits.size(); + + for (vector<TransUnit>::const_iterator tu=file->transUnits.begin(); + tu!=file->transUnits.end(); tu++) { + if (tu->source.comment == "") { + stat.noComments++; + } + } + } + + stat.totalStrings = stat.toBeTranslated; + + return stat; +} |