...

Source file src/github.com/chai2010/gettext-go/mo/file.go

Documentation: github.com/chai2010/gettext-go/mo

     1  // Copyright 2013 ChaiShushan <chaishushan{AT}gmail.com>. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package mo
     6  
     7  import (
     8  	"bytes"
     9  	"encoding/binary"
    10  	"fmt"
    11  	"io/ioutil"
    12  	"strings"
    13  )
    14  
    15  const (
    16  	MoHeaderSize        = 28
    17  	MoMagicLittleEndian = 0x950412de
    18  	MoMagicBigEndian    = 0xde120495
    19  
    20  	EotSeparator = "\x04" // msgctxt and msgid separator
    21  	NulSeparator = "\x00" // msgid and msgstr separator
    22  )
    23  
    24  // File represents an MO File.
    25  //
    26  // See http://www.gnu.org/software/gettext/manual/html_node/MO-Files.html
    27  type File struct {
    28  	MagicNumber  uint32
    29  	MajorVersion uint16
    30  	MinorVersion uint16
    31  	MsgIdCount   uint32
    32  	MsgIdOffset  uint32
    33  	MsgStrOffset uint32
    34  	HashSize     uint32
    35  	HashOffset   uint32
    36  	MimeHeader   Header
    37  	Messages     []Message
    38  }
    39  
    40  // Load loads mo file format data.
    41  func Load(data []byte) (*File, error) {
    42  	return loadData(data)
    43  }
    44  
    45  // Load loads a named mo file.
    46  func LoadFile(path string) (*File, error) {
    47  	data, err := ioutil.ReadFile(path)
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  	return loadData(data)
    52  }
    53  
    54  func loadData(data []byte) (*File, error) {
    55  	r := bytes.NewReader(data)
    56  
    57  	var magicNumber uint32
    58  	if err := binary.Read(r, binary.LittleEndian, &magicNumber); err != nil {
    59  		return nil, fmt.Errorf("gettext: %v", err)
    60  	}
    61  	var bo binary.ByteOrder
    62  	switch magicNumber {
    63  	case MoMagicLittleEndian:
    64  		bo = binary.LittleEndian
    65  	case MoMagicBigEndian:
    66  		bo = binary.BigEndian
    67  	default:
    68  		return nil, fmt.Errorf("gettext: %v", "invalid magic number")
    69  	}
    70  
    71  	var header struct {
    72  		MajorVersion uint16
    73  		MinorVersion uint16
    74  		MsgIdCount   uint32
    75  		MsgIdOffset  uint32
    76  		MsgStrOffset uint32
    77  		HashSize     uint32
    78  		HashOffset   uint32
    79  	}
    80  	if err := binary.Read(r, bo, &header); err != nil {
    81  		return nil, fmt.Errorf("gettext: %v", err)
    82  	}
    83  	if v := header.MajorVersion; v != 0 && v != 1 {
    84  		return nil, fmt.Errorf("gettext: %v", "invalid version number")
    85  	}
    86  	if v := header.MinorVersion; v != 0 && v != 1 {
    87  		return nil, fmt.Errorf("gettext: %v", "invalid version number")
    88  	}
    89  
    90  	msgIdStart := make([]uint32, header.MsgIdCount)
    91  	msgIdLen := make([]uint32, header.MsgIdCount)
    92  	if _, err := r.Seek(int64(header.MsgIdOffset), 0); err != nil {
    93  		return nil, fmt.Errorf("gettext: %v", err)
    94  	}
    95  	for i := 0; i < int(header.MsgIdCount); i++ {
    96  		if err := binary.Read(r, bo, &msgIdLen[i]); err != nil {
    97  			return nil, fmt.Errorf("gettext: %v", err)
    98  		}
    99  		if err := binary.Read(r, bo, &msgIdStart[i]); err != nil {
   100  			return nil, fmt.Errorf("gettext: %v", err)
   101  		}
   102  	}
   103  
   104  	msgStrStart := make([]int32, header.MsgIdCount)
   105  	msgStrLen := make([]int32, header.MsgIdCount)
   106  	if _, err := r.Seek(int64(header.MsgStrOffset), 0); err != nil {
   107  		return nil, fmt.Errorf("gettext: %v", err)
   108  	}
   109  	for i := 0; i < int(header.MsgIdCount); i++ {
   110  		if err := binary.Read(r, bo, &msgStrLen[i]); err != nil {
   111  			return nil, fmt.Errorf("gettext: %v", err)
   112  		}
   113  		if err := binary.Read(r, bo, &msgStrStart[i]); err != nil {
   114  			return nil, fmt.Errorf("gettext: %v", err)
   115  		}
   116  	}
   117  
   118  	file := &File{
   119  		MagicNumber:  magicNumber,
   120  		MajorVersion: header.MajorVersion,
   121  		MinorVersion: header.MinorVersion,
   122  		MsgIdCount:   header.MsgIdCount,
   123  		MsgIdOffset:  header.MsgIdOffset,
   124  		MsgStrOffset: header.MsgStrOffset,
   125  		HashSize:     header.HashSize,
   126  		HashOffset:   header.HashOffset,
   127  	}
   128  	for i := 0; i < int(header.MsgIdCount); i++ {
   129  		if _, err := r.Seek(int64(msgIdStart[i]), 0); err != nil {
   130  			return nil, fmt.Errorf("gettext: %v", err)
   131  		}
   132  		msgIdData := make([]byte, msgIdLen[i])
   133  		if _, err := r.Read(msgIdData); err != nil {
   134  			return nil, fmt.Errorf("gettext: %v", err)
   135  		}
   136  
   137  		if _, err := r.Seek(int64(msgStrStart[i]), 0); err != nil {
   138  			return nil, fmt.Errorf("gettext: %v", err)
   139  		}
   140  		msgStrData := make([]byte, msgStrLen[i])
   141  		if _, err := r.Read(msgStrData); err != nil {
   142  			return nil, fmt.Errorf("gettext: %v", err)
   143  		}
   144  
   145  		if len(msgIdData) == 0 {
   146  			var msg = Message{
   147  				MsgId:  string(msgIdData),
   148  				MsgStr: string(msgStrData),
   149  			}
   150  			file.MimeHeader.fromMessage(&msg)
   151  		} else {
   152  			var msg = Message{
   153  				MsgId:  string(msgIdData),
   154  				MsgStr: string(msgStrData),
   155  			}
   156  			// Is this a context message?
   157  			if idx := strings.Index(msg.MsgId, EotSeparator); idx != -1 {
   158  				msg.MsgContext, msg.MsgId = msg.MsgId[:idx], msg.MsgId[idx+1:]
   159  			}
   160  			// Is this a plural message?
   161  			if idx := strings.Index(msg.MsgId, NulSeparator); idx != -1 {
   162  				msg.MsgId, msg.MsgIdPlural = msg.MsgId[:idx], msg.MsgId[idx+1:]
   163  				msg.MsgStrPlural = strings.Split(msg.MsgStr, NulSeparator)
   164  				msg.MsgStr = ""
   165  			}
   166  			file.Messages = append(file.Messages, msg)
   167  		}
   168  	}
   169  
   170  	return file, nil
   171  }
   172  
   173  // Save saves a mo file.
   174  func (f *File) Save(name string) error {
   175  	return ioutil.WriteFile(name, f.Data(), 0666)
   176  }
   177  
   178  // Save returns a mo file format data.
   179  func (f *File) Data() []byte {
   180  	return encodeFile(f)
   181  }
   182  
   183  // String returns the po format file string.
   184  func (f *File) String() string {
   185  	var buf bytes.Buffer
   186  	fmt.Fprintf(&buf, "# version: %d.%d\n", f.MajorVersion, f.MinorVersion)
   187  	fmt.Fprintf(&buf, "%s\n", f.MimeHeader.String())
   188  	fmt.Fprintf(&buf, "\n")
   189  
   190  	for k, v := range f.Messages {
   191  		fmt.Fprintf(&buf, `msgid "%v"`+"\n", k)
   192  		fmt.Fprintf(&buf, `msgstr "%s"`+"\n", v.MsgStr)
   193  		fmt.Fprintf(&buf, "\n")
   194  	}
   195  
   196  	return buf.String()
   197  }
   198  

View as plain text