Esempio n. 1
0
        /// <summary>
        /// Updates this XLIFF document with the source data from the given translatable document.
        /// </summary>
        /// <returns>True if any changes were made to this document.</returns>
        public bool Update(TranslatableDocument sourceDocument, string sourceDocumentId)
        {
            bool changed   = false;
            var  nodesById = new Dictionary <string, TranslatableNode>();

            foreach (var node in sourceDocument.Nodes)
            {
                if (nodesById.ContainsKey(node.Id))
                {
                    throw new BuildErrorException($"The document '{sourceDocumentId}' has a duplicate node '{node.Id}'.");
                }

                nodesById.Add(node.Id, node);
            }

            XElement   fileElement       = _document.Root.Element(File);
            XAttribute originalAttribute = fileElement.Attribute("original");

            if (originalAttribute.Value != sourceDocumentId)
            {
                // update original path in case where user has renaned source file and corresponding xlf
                originalAttribute.Value = sourceDocumentId;
                changed = true;
            }

            XElement bodyElement  = fileElement.Element(Body);
            XElement groupElement = bodyElement.Element(Group);

            if (groupElement != null && !groupElement.Elements().Any())
            {
                // remove unnecessary empty group added by older tool. We don't want to bother keeping that unnecessary id up-to-date.
                groupElement.Remove();
                changed = true;
            }

            foreach (XElement unitElement in bodyElement.Descendants(TransUnit).ToList())
            {
                string id     = unitElement.GetId();
                string state  = unitElement.GetTargetState();
                string source = unitElement.GetSourceValue();
                string note   = unitElement.GetNoteValue();

                // delete node in document that has been removed from source.
                if (!nodesById.TryGetValue(id, out TranslatableNode sourceNode))
                {
                    unitElement.Remove();
                    changed = true;
                    continue;
                }

                // update trans-unit state if either the source text or associated note has change.
                if (source != sourceNode.Source || (sourceNode.Note != null && note != sourceNode.Note))
                {
                    unitElement.SetSourceValue(sourceNode.Source);

                    // if sourceNode.Note is null, it indicates that the source format can't have notes, in which case
                    // they may be applied directly to the xlf by the user and we should not revert that on update
                    if (sourceNode.Note != null)
                    {
                        unitElement.SetNoteValue(sourceNode.Note);
                    }

                    switch (state)
                    {
                    case "new":
                        // when a new string gets modified before it has been translated,
                        // update untranslated target to match the new source
                        unitElement.SetTargetValue(sourceNode.Source);
                        break;

                    case "translated":
                        // flag strings that have been modified after translation for review/re-translation
                        unitElement.SetTargetState("needs-review-translation");
                        break;
                    }

                    changed = true;
                }

                // If the source and target require different numbers of formatting items then reset
                // the target string completely. This avoids problems when the source has been updated
                // to remove formatting items--when formatting the target string we won't have as many
                // replacement items as it calls for, leading to an exception.
                // And if the source string is updated to use _more_ items then formatting with the
                // target string is likely to produce misleading (or outright meaningless) text. In
                // either case we lose nothing by just reverting the string until it can be localized
                // again.
                // Note we don't limit this check to when the source has changed in the original
                // document because we also want to catch errors introduced during translation.
                var sourceReplacementCount = unitElement.GetSourceValue().GetReplacementCount();
                var targetReplacementCount = unitElement.GetTargetValue().GetReplacementCount();

                if (targetReplacementCount != sourceReplacementCount)
                {
                    unitElement.SetTargetValue(sourceNode.Source);
                    unitElement.SetTargetState("new");

                    changed = true;
                }

                // signal to loop below that this node is not new
                nodesById.Remove(id);
            }

            // Add new trans-units
            foreach (TranslatableNode sourceNode in sourceDocument.Nodes)
            {
                // Nodes that have been removed from nodesById table are not new and have already been handled.
                // Do not refactor this check away by iterating over dictionary values as the document order must be maintained deterministically.
                if (!nodesById.ContainsKey(sourceNode.Id))
                {
                    continue;
                }

                XElement newTransUnit =
                    new XElement(TransUnit,
                                 new XAttribute("id", sourceNode.Id),
                                 new XElement(Source, sourceNode.Source),
                                 new XElement(Target, new XAttribute("state", "new"), sourceNode.Source),
                                 new XElement(Note, sourceNode.Note == "" ? null : sourceNode.Note));

                bool inserted = false;
                foreach (var transUnit in bodyElement.Elements(TransUnit))
                {
                    if (StringComparer.Ordinal.Compare(newTransUnit.GetId(), transUnit.GetId()) < 0)
                    {
                        transUnit.AddBeforeSelf(newTransUnit);
                        inserted = true;
                        break;
                    }
                }

                if (!inserted)
                {
                    bodyElement.Add(newTransUnit);
                }

                changed = true;
            }

            return(changed);
        }
Esempio n. 2
0
        /// <summary>
        /// Updates this XLIFF document with the source data from the given translatable document.
        /// </summary>
        /// <returns>True if any changes were made to this document.</returns>
        public bool Update(TranslatableDocument sourceDocument, string sourceDocumentId)
        {
            bool changed = false;
            Dictionary <string, TranslatableNode> nodesById = sourceDocument.Nodes.ToDictionary(n => n.Id);
            XNamespace ns = _document.Root.Name.Namespace;

            XElement   fileElement       = _document.Root.Element(ns + "file");
            XAttribute originalAttribute = fileElement.Attribute("original");

            if (originalAttribute.Value != sourceDocumentId)
            {
                // update original path in case where user has renaned source file and corresponding xlf
                originalAttribute.Value = sourceDocumentId;
                changed = true;
            }

            XElement bodyElement  = fileElement.Element(ns + "body");
            XElement groupElement = bodyElement.Element(ns + "group");

            if (groupElement != null && !groupElement.Elements().Any())
            {
                // remove unnecessary empty group added by older tool. We don't want to bother keeping that unnecessary id up-to-date.
                groupElement.Remove();
                changed = true;
            }

            foreach (XElement unitElement in bodyElement.Descendants(ns + "trans-unit").ToList())
            {
                XElement   sourceElement  = unitElement.Element(ns + "source");
                XElement   targetElement  = unitElement.Element(ns + "target");
                XElement   noteElement    = unitElement.Element(ns + "note");
                XAttribute idAttribute    = unitElement.Attribute("id");
                XAttribute stateAttribute = targetElement.Attribute("state");

                string id     = idAttribute.Value;
                string state  = stateAttribute.Value;
                string source = sourceElement.Value;
                string note   = noteElement.Value;

                // delete node in document that has been removed from source.
                if (!nodesById.TryGetValue(id, out TranslatableNode sourceNode))
                {
                    unitElement.Remove();
                    changed = true;
                    continue;
                }

                // update trans-unit state if either the source text or associated note has change.
                if (source != sourceNode.Source || (sourceNode.Note != null && note != sourceNode.Note))
                {
                    sourceElement.Value = sourceNode.Source;

                    // if sourceNode.Note is null, it indicates that the source format can't have notes, in which case
                    // they may be applied directly to the xlf by the user and we should not revert that on update
                    if (sourceNode.Note != null)
                    {
                        noteElement.Value = sourceNode.Note;
                        noteElement.SelfCloseIfPossible();
                    }

                    switch (state)
                    {
                    case "new":
                        // when a new string gets modified before it has been translated,
                        // update untranslated target to match the new source
                        targetElement.Value = sourceNode.Source;
                        break;

                    case "translated":
                        // flag strings that have been modified after translation for review/re-translation
                        stateAttribute.Value = "needs-review-translation";
                        break;
                    }

                    changed = true;
                }

                // signal to loop below that this node is not new
                nodesById.Remove(id);
            }

            // Add new trans-units
            foreach (TranslatableNode sourceNode in sourceDocument.Nodes)
            {
                // Nodes that have been removed from nodesById table are not new and have already been handled.
                // Do not refactor this check away by iterating over dictionary values as the document order must be maintained deterministically.
                if (!nodesById.ContainsKey(sourceNode.Id))
                {
                    continue;
                }

                bodyElement.Add(
                    new XElement(ns + "trans-unit",
                                 new XAttribute("id", sourceNode.Id),
                                 new XElement(ns + "source", sourceNode.Source),
                                 new XElement(ns + "target", new XAttribute("state", "new"), sourceNode.Source),
                                 new XElement(ns + "note", sourceNode.Note == "" ? null : sourceNode.Note)));

                changed = true;
            }

            return(changed);
        }