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.
17
MoMagicLittleEndian = 0x950412de
18
MoMagicBigEndian = 0xde120495
20
EotSeparator = "\x04" // msgctxt and msgid separator
21
NulSeparator = "\x00" // msgid and msgstr separator
24
// File represents an MO File.
26
// See http://www.gnu.org/software/gettext/manual/html_node/MO-Files.html
40
// Load loads a named mo file.
41
func Load(name string) (*File, error) {
42
data, err := ioutil.ReadFile(name)
49
// LoadData loads mo file format data.
50
func LoadData(data []byte) (*File, error) {
51
r := bytes.NewReader(data)
53
var magicNumber uint32
54
if err := binary.Read(r, binary.LittleEndian, &magicNumber); err != nil {
55
return nil, fmt.Errorf("gettext: %v", err)
57
var bo binary.ByteOrder
59
case MoMagicLittleEndian:
60
bo = binary.LittleEndian
61
case MoMagicBigEndian:
64
return nil, fmt.Errorf("gettext: %v", "invalid magic number")
76
if err := binary.Read(r, bo, &header); err != nil {
77
return nil, fmt.Errorf("gettext: %v", err)
79
if v := header.MajorVersion; v != 0 && v != 1 {
80
return nil, fmt.Errorf("gettext: %v", "invalid version number")
82
if v := header.MinorVersion; v != 0 && v != 1 {
83
return nil, fmt.Errorf("gettext: %v", "invalid version number")
86
msgIdStart := make([]uint32, header.MsgIdCount)
87
msgIdLen := make([]uint32, header.MsgIdCount)
88
if _, err := r.Seek(int64(header.MsgIdOffset), 0); err != nil {
89
return nil, fmt.Errorf("gettext: %v", err)
91
for i := 0; i < int(header.MsgIdCount); i++ {
92
if err := binary.Read(r, bo, &msgIdLen[i]); err != nil {
93
return nil, fmt.Errorf("gettext: %v", err)
95
if err := binary.Read(r, bo, &msgIdStart[i]); err != nil {
96
return nil, fmt.Errorf("gettext: %v", err)
100
msgStrStart := make([]int32, header.MsgIdCount)
101
msgStrLen := make([]int32, header.MsgIdCount)
102
if _, err := r.Seek(int64(header.MsgStrOffset), 0); err != nil {
103
return nil, fmt.Errorf("gettext: %v", err)
105
for i := 0; i < int(header.MsgIdCount); i++ {
106
if err := binary.Read(r, bo, &msgStrLen[i]); err != nil {
107
return nil, fmt.Errorf("gettext: %v", err)
109
if err := binary.Read(r, bo, &msgStrStart[i]); err != nil {
110
return nil, fmt.Errorf("gettext: %v", err)
115
MagicNumber: magicNumber,
116
MajorVersion: header.MajorVersion,
117
MinorVersion: header.MinorVersion,
118
MsgIdCount: header.MsgIdCount,
119
MsgIdOffset: header.MsgIdOffset,
120
MsgStrOffset: header.MsgStrOffset,
121
HashSize: header.HashSize,
122
HashOffset: header.HashOffset,
124
for i := 0; i < int(header.MsgIdCount); i++ {
125
if _, err := r.Seek(int64(msgIdStart[i]), 0); err != nil {
126
return nil, fmt.Errorf("gettext: %v", err)
128
msgIdData := make([]byte, msgIdLen[i])
129
if _, err := r.Read(msgIdData); err != nil {
130
return nil, fmt.Errorf("gettext: %v", err)
133
if _, err := r.Seek(int64(msgStrStart[i]), 0); err != nil {
134
return nil, fmt.Errorf("gettext: %v", err)
136
msgStrData := make([]byte, msgStrLen[i])
137
if _, err := r.Read(msgStrData); err != nil {
138
return nil, fmt.Errorf("gettext: %v", err)
141
if len(msgIdData) == 0 {
143
MsgId: string(msgIdData),
144
MsgStr: string(msgStrData),
146
file.MimeHeader.fromMessage(&msg)
149
MsgId: string(msgIdData),
150
MsgStr: string(msgStrData),
152
// Is this a context message?
153
if idx := strings.Index(msg.MsgId, EotSeparator); idx != -1 {
154
msg.MsgContext, msg.MsgId = msg.MsgId[:idx], msg.MsgId[idx+1:]
156
// Is this a plural message?
157
if idx := strings.Index(msg.MsgId, NulSeparator); idx != -1 {
158
msg.MsgId, msg.MsgIdPlural = msg.MsgId[:idx], msg.MsgId[idx+1:]
159
msg.MsgStrPlural = strings.Split(msg.MsgStr, NulSeparator)
162
file.Messages = append(file.Messages, msg)
169
// Save saves a mo file.
170
func (f *File) Save(name string) error {
171
return ioutil.WriteFile(name, f.Data(), 0666)
174
// Save returns a mo file format data.
175
func (f *File) Data() []byte {
179
// String returns the po format file string.
180
func (f *File) String() string {
182
fmt.Fprintf(&buf, "# version: %d.%d\n", f.MajorVersion, f.MinorVersion)
183
fmt.Fprintf(&buf, "%s\n", f.MimeHeader.String())
184
fmt.Fprintf(&buf, "\n")
186
for k, v := range f.Messages {
187
fmt.Fprintf(&buf, `msgid "%v"`+"\n", k)
188
fmt.Fprintf(&buf, `msgstr "%s"`+"\n", v.MsgStr)
189
fmt.Fprintf(&buf, "\n")