[Golang] Convert PO file to JSON Format


Introduction

Write a Go program to convert PO files to JSON format. The data of JSON format can be passed to front-end by web servers to translate a text string into the user's native language. You can use the JSON data from PO files to implement gettext function in browsers.

Sample PO files

In this example, we support two locale, zh_TW (Traditional Chinese) and vi_VN (Vietnamese). The zh_TW PO file are located at locale/zh_TW/LC_MESSAGES/messages.po and vi_VN PO file are located at locale/vi_VN/LC_MESSAGES/messages.po.

zh_TW PO file locale/zh_TW/LC_MESSAGES/messages.po:

messages.po | repository | view raw
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# Chinese translations for PACKAGE package.
# Copyright (C) 2013 THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# Automatically generated, 2013.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-06-04 10:20+0800\n"
"PO-Revision-Date: 2013-03-10 05:19+0800\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: zh_TW\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"

msgid "Home"
msgstr "首頁"

msgid "Canon"
msgstr "經典"

msgid "About"
msgstr "關於"

msgid "Setting"
msgstr "設定"

msgid "Translation"
msgstr "翻譯"

vi_VN PO file locale/vi_VN/LC_MESSAGES/messages.po:

messages.po | repository | view raw
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# Vietnamese translations for PACKAGE package.
# Copyright (C) 2013 THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# Automatically generated, 2013.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-06-06 23:05+0800\n"
"PO-Revision-Date: 2013-06-06 22:50+0800\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: vi\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"

msgid "Home"
msgstr "Trang chính"

msgid "Canon"
msgstr "Kinh điển"

msgid "About"
msgstr "Giới thiệu"

msgid "Setting"
msgstr "Thiết lập"

msgid "Translation"
msgstr "Dịch"

Source Code

Convert PO files to JSON format:

po2json.go | repository | view raw
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package po2json

import "regexp"
import "io/ioutil"
import "encoding/json"

type msgIdStrPairs map[string]string
type localesMsg map[string]msgIdStrPairs

const pattern = `msgid "(.+)"\nmsgstr "(.+)"`

func getPOPath(locale, domain, localedir string) string {
	return localedir + "/" + locale + "/LC_MESSAGES/" + domain + ".po"
}

func extractFromPOFile(popath string) msgIdStrPairs {
	buf, err := ioutil.ReadFile(popath)
	if err != nil {
		panic(err)
	}

	re := regexp.MustCompile(pattern)
	matches := re.FindAllStringSubmatch(string(buf), -1)

	pairs := msgIdStrPairs{}
	for _, array := range matches {
		pairs[array[1]] = array[2]
	}
	return pairs
}

func PO2JSON(locales []string, domain, localedir string) string {
	// create PO-like json data for i18n
	obj := localesMsg{}
	for _, locale := range locales {
		// English is default language
		if locale == "en_US" {
			continue
		}

		obj[locale] = extractFromPOFile(getPOPath(locale, domain, localedir))
	}

	b, err := json.Marshal(obj)
	if err != nil {
		panic(err)
	}

	return string(b)
}
po2json_test.go | repository | view raw
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package po2json

import "testing"

func TestAll(t *testing.T) {
	locales := []string{"zh_TW", "vi_VN"}
	const domain = "messages"
	const localedir = "locale"

	t.Log(getPOPath(locales[0], domain, localedir))
	t.Log(getPOPath(locales[1], domain, localedir))

	t.Log(PO2JSON(locales, domain, localedir))
}

Output of Demo

=== RUN   TestAll
--- PASS: TestAll (0.00s)
        po2json_test.go:10: locale/zh_TW/LC_MESSAGES/messages.po
        po2json_test.go:11: locale/vi_VN/LC_MESSAGES/messages.po
        po2json_test.go:13: {"vi_VN":{"About":"Giới thiệu","Canon":"Kinh điển","Home":"Trang chính","Setting":"Thiết lập","Translation":"Dịch"},"zh_TW":{"About":"關於","Canon":"經典","Home":"首頁","Setting":"設定","Translation":"翻譯"}}
PASS
ok      command-line-arguments  0.003s

Tested on: Ubuntu Linux 15.10, Go 1.5.3.


References:

[1]golang regular expression
[2]regexp - The Go Programming Language
[3]golang read file to string
[4]go - How Can i read a whole file into a string variable in golang? - Stack Overflow
[5]json - The Go Programming Language
[6]Online regex tester and debugger: PHP, PCRE, Python, Golang and JavaScript