package gotext import ( "fmt" "io/ioutil" "os" "strconv" "strings" "sync" ) type translation struct { id string pluralId string trs map[int]string } func newTranslation() *translation { tr := new(translation) tr.trs = make(map[int]string) return tr } func (t *translation) get() string { // Look for translation index 0 if _, ok := t.trs[0]; ok { return t.trs[0] } // Return unstranlated id by default return t.id } func (t *translation) getN(n int) string { // Look for translation index if _, ok := t.trs[n]; ok { return t.trs[n] } // Return unstranlated plural by default return t.pluralId } /* Po parses the content of any PO file and provides all the translation functions needed. It's the base object used by all packafe methods. And it's safe for concurrent use by multiple goroutines by using the sync package for write locking. Example: import "github.com/leonelquinteros/gotext" func main() { // Create po object po := new(gotext.Po) // Parse .po file po.ParseFile("/path/to/po/file/translations.po") // Get translation println(po.Get("Translate this")) } */ type Po struct { // Storage translations map[string]*translation // Sync Mutex sync.RWMutex } // ParseFile tries to read the file by its provided path (f) and parse its content as a .po file. func (po *Po) ParseFile(f string) { // Check if file exists info, err := os.Stat(f) if err != nil { return } // Check that isn't a directory if info.IsDir() { return } // Parse file content data, err := ioutil.ReadFile(f) if err != nil { return } po.Parse(string(data)) } // Parse loads the translations specified in the provided string (str) func (po *Po) Parse(str string) { if po.translations == nil { po.Lock() po.translations = make(map[string]*translation) po.Unlock() } lines := strings.Split(str, "\n") tr := newTranslation() for _, l := range lines { // Trim spaces l = strings.TrimSpace(l) // Skip empty lines if l == "" { continue } // Skip invalid lines if !strings.HasPrefix(l, "msgid") && !strings.HasPrefix(l, "msgid_plural") && !strings.HasPrefix(l, "msgstr") { continue } // Buffer msgid and continue if strings.HasPrefix(l, "msgid") && !strings.HasPrefix(l, "msgid_plural") { // Save current translation buffer. po.Lock() po.translations[tr.id] = tr po.Unlock() // Flush buffer tr = newTranslation() // Set id tr.id, _ = strconv.Unquote(strings.TrimSpace(strings.TrimPrefix(l, "msgid"))) // Loop continue } // Check for plural form if strings.HasPrefix(l, "msgid_plural") { tr.pluralId, _ = strconv.Unquote(strings.TrimSpace(strings.TrimPrefix(l, "msgid_plural"))) // Loop continue } // Save translation if strings.HasPrefix(l, "msgstr") { l = strings.TrimSpace(strings.TrimPrefix(l, "msgstr")) // Check for indexed translation forms if strings.HasPrefix(l, "[") { in := strings.Index(l, "]") if in == -1 { // Skip wrong index formatting continue } // Parse index i, err := strconv.Atoi(l[1:in]) if err != nil { // Skip wrong index formatting continue } // Parse translation string tr.trs[i], _ = strconv.Unquote(strings.TrimSpace(l[in+1:])) // Loop continue } // Save single translation form under 0 index tr.trs[0], _ = strconv.Unquote(l) } } // Save last translation buffer. if tr.id != "" { po.Lock() po.translations[tr.id] = tr po.Unlock() } } // Get retrieves the corresponding translation for the given string. // Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. func (po *Po) Get(str string, vars ...interface{}) string { if po.translations != nil { if _, ok := po.translations[str]; ok { return fmt.Sprintf(po.translations[str].get(), vars...) } } // Return the same we received by default return fmt.Sprintf(str, vars...) } // GetN retrieves the (N)th plural form translation for the given string. // If n == 0, usually the singular form of the string is returned as defined in the PO file. // Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax. func (po *Po) GetN(str, plural string, n int, vars ...interface{}) string { if po.translations != nil { if _, ok := po.translations[str]; ok { return fmt.Sprintf(po.translations[str].getN(n), vars...) } } // Return the plural string we received by default return fmt.Sprintf(plural, vars...) }