Esempio n. 1
0
        /// <summary>
        /// Loads translations to the specified catalog using specified MO file parser.
        /// </summary>
        /// <param name="parsedMoFile"></param>
        /// <param name="catalog"></param>
        protected virtual void Load(MoFile parsedMoFile, Catalog catalog)
        {
            foreach (var translation in parsedMoFile.Translations)
            {
                catalog.Translations.Add(translation.Key, translation.Value);
            }

            if (parsedMoFile.Headers.ContainsKey("Plural-Forms"))
            {
                var generator = this.PluralRuleGenerator as IPluralRuleTextParser;
                if (generator != null)
                {
                    generator.SetPluralRuleText(parsedMoFile.Headers["Plural-Forms"]);
                }
            }
            catalog.PluralRule = this.PluralRuleGenerator.CreateRule(catalog.CultureInfo);
        }
        /// <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();
            }
        }