/// <summary>
        /// Parses a GNU MO file from the given stream and loads all available data.
        /// </summary>
        /// <remarks>
        ///	http://www.gnu.org/software/gettext/manual/html_node/MO-Files.html
        /// </remarks>
        /// <param name="stream">Stream that contain binary data in the MO file format</param>
        public void Parse(Stream stream)
        {
            this._Init();
            Trace.WriteLine("Trying to parse a MO file stream...", "NGettext");

            if (stream == null || stream.Length < 20)
            {
                throw new ArgumentException("Stream can not be null of less than 20 bytes long.");
            }

            var reader = new BinaryReader(new ReadOnlyStreamWrapper(stream));
            try
            {
                var magicNumber = reader.ReadUInt32();
                if (magicNumber != MO_FILE_MAGIC)
                {
                    // System.IO.BinaryReader does not respect machine endianness and always uses little-endian
                    // So we need to detect and read big-endian files by ourselves
                    if (_ReverseBytes(magicNumber) == MO_FILE_MAGIC)
                    {
                        Trace.WriteLine("Big Endian file detected. Switching readers...", "NGettext");
                        this.IsBigEndian = true;
            #if DNXCORE50
                        reader.Dispose();
            #else
                        reader.Close();
            #endif
                        reader = new BigEndianBinaryReader(new ReadOnlyStreamWrapper(stream));
                    }
                    else
                    {
                        throw new ArgumentException("Invalid stream: can not find MO file magic number.");
                    }
                }

                var revision = reader.ReadInt32();
                this.FormatRevision = new Version(revision >> 16, revision & 0xffff);

                Trace.WriteLine(String.Format("MO File Revision: {0}.{1}.", this.FormatRevision.Major, this.FormatRevision.Minor), "NGettext");

                if (this.FormatRevision.Major > MAX_SUPPORTED_VERSION)
                {
                    throw new Exception(String.Format("Unsupported MO file major revision: {0}.", this.FormatRevision.Major));
                }

                var stringCount = reader.ReadInt32();
                var originalTableOffset = reader.ReadInt32();
                var translationTableOffset = reader.ReadInt32();

                // We don't support hash tables and system dependent segments.

                Trace.WriteLine(String.Format("MO File contains {0} strings.", stringCount), "NGettext");

                var originalTable = new StringOffsetTable[stringCount];
                var translationTable = new StringOffsetTable[stringCount];

                Trace.WriteLine(String.Format("Trying to parse strings using encoding \"{0}\"...", this.Encoding), "NGettext");

                reader.BaseStream.Seek(originalTableOffset, SeekOrigin.Begin);
                for (int i = 0; i < stringCount; i++)
                {
                    originalTable[i].Length = reader.ReadInt32();
                    originalTable[i].Offset = reader.ReadInt32();
                }

                reader.BaseStream.Seek(translationTableOffset, SeekOrigin.Begin);
                for (int i = 0; i < stringCount; i++)
                {
                    translationTable[i].Length = reader.ReadInt32();
                    translationTable[i].Offset = reader.ReadInt32();
                }

                for (int i = 0; i < stringCount; i++)
                {
                    var originalStrings = this._ReadStrings(reader, originalTable[i].Offset, originalTable[i].Length);
                    var translatedStrings = this._ReadStrings(reader, translationTable[i].Offset, translationTable[i].Length);

                    if (originalStrings.Length == 0 || translatedStrings.Length == 0) continue;

                    if (originalStrings[0].Length == 0)
                    {
                        // MO file metadata processing
                        foreach (var headerText in translatedStrings[0].Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries))
                        {
                            var header = headerText.Split(new[] { ':' }, 2);
                            if (header.Length == 2)
                            {
                                this.Headers.Add(header[0], header[1].Trim());
                            }
                        }

                        if (this.DetectEncoding && this.Headers.ContainsKey("Content-Type"))
                        {
                            try
                            {
                                var contentType = new ContentType(this.Headers["Content-Type"]);
                                if (!String.IsNullOrEmpty(contentType.CharSet))
                                {
                                    this.Encoding = Encoding.GetEncoding(contentType.CharSet);
                                    Trace.WriteLine(String.Format("File encoding switched to \"{0}\" (\"{1}\" requested).", this.Encoding, contentType.CharSet), "NGettext");
                                }
                            }
                            catch (Exception exception)
                            {
                                Trace.WriteLine(String.Format("Unable to change parser encoding using the Content-Type header: \"{0}\".", exception.Message), "NGettext");
                            }
                        }
                        if (this.Headers.ContainsKey("Plural-Forms"))
                        {
                            //TODO: Plural forms parsing.
                        }
                    }

                    this.Translations.Add(originalStrings[0], translatedStrings);
                }

                Trace.WriteLine("String parsing completed.", "NGettext");

            }
            finally
            {
            #if DNXCORE50
                reader.Dispose();
            #else
                reader.Close();
            #endif
            }
        }
Exemple #2
0
        /// <summary>
        /// Parses a GNU MO file from the given stream and loads all available data.
        /// </summary>
        /// <remarks>
        ///	http://www.gnu.org/software/gettext/manual/html_node/MO-Files.html
        /// </remarks>
        /// <param name="stream">Stream that contain binary data in the MO file format</param>
        /// <returns>Parsed file data.</returns>
        public MoFile Parse(Stream stream)
        {
            #if !NETSTANDARD1_0
            Trace.WriteLine("Trying to parse a MO file stream...", "NGettext");
            #endif

            if (stream == null || stream.Length < 20)
            {
                throw new ArgumentException("Stream can not be null of less than 20 bytes long.");
            }

            var bigEndian = false;
            var reader = new BinaryReader(new ReadOnlyStreamWrapper(stream));
            try
            {
                var magicNumber = reader.ReadUInt32();
                if (magicNumber != MO_FILE_MAGIC)
                {
                    // System.IO.BinaryReader does not respect machine endianness and always uses LittleEndian
                    // So we need to detect and read BigEendian files by ourselves
                    if (_ReverseBytes(magicNumber) == MO_FILE_MAGIC)
                    {
            #if !NETSTANDARD1_0
                        Trace.WriteLine("BigEndian file detected. Switching readers...", "NGettext");
            #endif
                        bigEndian = true;
                        ((IDisposable)reader).Dispose();
                        reader = new BigEndianBinaryReader(new ReadOnlyStreamWrapper(stream));
                    }
                    else
                    {
                        throw new ArgumentException("Invalid stream: can not find MO file magic number.");
                    }
                }

                var revision = reader.ReadInt32();
                var parsedFile = new MoFile(new Version(revision >> 16, revision & 0xffff), this.DefaultEncoding, bigEndian);

            #if !NETSTANDARD1_0
                Trace.WriteLine(String.Format("MO File Revision: {0}.{1}.", parsedFile.FormatRevision.Major, parsedFile.FormatRevision.Minor), "NGettext");
            #endif

                if (parsedFile.FormatRevision.Major > MAX_SUPPORTED_VERSION)
                {
                    throw new CatalogLoadingException(String.Format("Unsupported MO file major revision: {0}.", parsedFile.FormatRevision.Major));
                }

                var stringCount = reader.ReadInt32();
                var originalTableOffset = reader.ReadInt32();
                var translationTableOffset = reader.ReadInt32();

                // We don't support hash tables and system dependent segments.

            #if !NETSTANDARD1_0
                Trace.WriteLine(String.Format("MO File contains {0} strings.", stringCount), "NGettext");
            #endif

                var originalTable = new StringOffsetTable[stringCount];
                var translationTable = new StringOffsetTable[stringCount];

            #if !NETSTANDARD1_0
                Trace.WriteLine(String.Format("Trying to parse strings using encoding \"{0}\"...", parsedFile.Encoding), "NGettext");
            #endif

                reader.BaseStream.Seek(originalTableOffset, SeekOrigin.Begin);
                for (int i = 0; i < stringCount; i++)
                {
                    originalTable[i].Length = reader.ReadInt32();
                    originalTable[i].Offset = reader.ReadInt32();
                }

                reader.BaseStream.Seek(translationTableOffset, SeekOrigin.Begin);
                for (int i = 0; i < stringCount; i++)
                {
                    translationTable[i].Length = reader.ReadInt32();
                    translationTable[i].Offset = reader.ReadInt32();
                }

                for (int i = 0; i < stringCount; i++)
                {
                    var originalStrings = this._ReadStrings(reader, originalTable[i].Offset, originalTable[i].Length, parsedFile.Encoding);
                    var translatedStrings = this._ReadStrings(reader, translationTable[i].Offset, translationTable[i].Length, parsedFile.Encoding);

                    if (originalStrings.Length == 0 || translatedStrings.Length == 0) continue;

                    if (originalStrings[0].Length == 0)
                    {
                        // MO file meta data processing
                        foreach (var headerText in translatedStrings[0].Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries))
                        {
                            var separatorIndex = headerText.IndexOf(':');
                            if (separatorIndex > 0)
                            {
                                var headerName = headerText.Substring(0, separatorIndex);
                                var headerValue = headerText.Substring(separatorIndex + 1).Trim();
                                parsedFile.Headers.Add(headerName, headerValue.Trim());
                            }
                        }

                        if (this.AutoDetectEncoding && parsedFile.Headers.ContainsKey("Content-Type"))
                        {
                            try
                            {
                                var contentType = new ContentType(parsedFile.Headers["Content-Type"]);
                                if (!String.IsNullOrEmpty(contentType.CharSet))
                                {
                                    parsedFile.Encoding = Encoding.GetEncoding(contentType.CharSet);
            #if !NETSTANDARD1_0
                                    Trace.WriteLine(String.Format("File encoding switched to \"{0}\" (\"{1}\" requested).", parsedFile.Encoding, contentType.CharSet), "NGettext");
            #endif
                                }
                            }
                            catch (Exception exception)
                            {
                                throw new CatalogLoadingException(String.Format("Unable to change parser encoding using the Content-Type header: \"{0}\".", exception.Message), exception);
                            }
                        }
                    }

                    parsedFile.Translations.Add(originalStrings[0], translatedStrings);
                }

            #if !NETSTANDARD1_0
                Trace.WriteLine("String parsing completed.", "NGettext");
            #endif
                return parsedFile;
            }
            finally
            {
                ((IDisposable)reader).Dispose();
            }
        }