Create MO parser
Refactored a bit too, so we can use interfaces to take Mo and Po files added fixtures found that the parser for Po files have a bug... but it works... so not touched
This commit is contained in:
parent
8c36835ece
commit
cd46239477
23 changed files with 1726 additions and 234 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -3,6 +3,9 @@
|
|||
.settings
|
||||
.buildpath
|
||||
|
||||
# golang jetbrains shit
|
||||
.idea
|
||||
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
|
|
BIN
fixtures/de/default.mo
Normal file
BIN
fixtures/de/default.mo
Normal file
Binary file not shown.
77
fixtures/de/default.po
Normal file
77
fixtures/de/default.po
Normal file
|
@ -0,0 +1,77 @@
|
|||
msgid ""
|
||||
msgstr ""
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Project-Id-Version: \n"
|
||||
"POT-Creation-Date: \n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Last-Translator: Josef Fröhle <froehle@b1-systems.de>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: de\n"
|
||||
"X-Generator: Poedit 2.0.6\n"
|
||||
"X-Poedit-SourceCharset: UTF-8\n"
|
||||
|
||||
# Initial comment
|
||||
# Headers below
|
||||
msgid "language"
|
||||
msgstr "de"
|
||||
|
||||
# Some comment
|
||||
msgid "My text"
|
||||
msgstr "Translated text"
|
||||
|
||||
# More comments
|
||||
msgid "Another string"
|
||||
msgstr ""
|
||||
|
||||
# Multi-line msgid
|
||||
msgid ""
|
||||
"multi\n"
|
||||
"line\n"
|
||||
"id"
|
||||
msgstr "id with multiline content"
|
||||
|
||||
# Multi-line msgid_plural
|
||||
msgid ""
|
||||
"multi\n"
|
||||
"line\n"
|
||||
"plural\n"
|
||||
"id"
|
||||
msgstr "plural id with multiline content"
|
||||
|
||||
# Multi-line string
|
||||
msgid "Multi-line"
|
||||
msgstr ""
|
||||
"Multi \n"
|
||||
"line"
|
||||
|
||||
msgid "One with var: %s"
|
||||
msgid_plural "Several with vars: %s"
|
||||
msgstr[0] "This one is the singular: %s"
|
||||
msgstr[1] "This one is the plural: %s"
|
||||
|
||||
msgctxt "Ctx"
|
||||
msgid "One with var: %s"
|
||||
msgid_plural "Several with vars: %s"
|
||||
msgstr[0] "This one is the singular in a Ctx context: %s"
|
||||
msgstr[1] "This one is the plural in a Ctx context: %s"
|
||||
|
||||
msgid "Some random"
|
||||
msgstr "Some random translation"
|
||||
|
||||
msgctxt "Ctx"
|
||||
msgid "Some random in a context"
|
||||
msgstr "Some random translation in a context"
|
||||
|
||||
msgid "Empty translation"
|
||||
msgstr ""
|
||||
|
||||
msgid "Empty plural form singular"
|
||||
msgid_plural "Empty plural form"
|
||||
msgstr[0] "Singular translated"
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "More"
|
||||
msgstr "More translation"
|
BIN
fixtures/de_DE/LC_MESSAGES/default.mo
Normal file
BIN
fixtures/de_DE/LC_MESSAGES/default.mo
Normal file
Binary file not shown.
77
fixtures/de_DE/LC_MESSAGES/default.po
Normal file
77
fixtures/de_DE/LC_MESSAGES/default.po
Normal file
|
@ -0,0 +1,77 @@
|
|||
msgid ""
|
||||
msgstr ""
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Project-Id-Version: \n"
|
||||
"POT-Creation-Date: \n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Last-Translator: Josef Fröhle <froehle@b1-systems.de>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: de_DE\n"
|
||||
"X-Generator: Poedit 2.0.6\n"
|
||||
"X-Poedit-SourceCharset: UTF-8\n"
|
||||
|
||||
# Initial comment
|
||||
# Headers below
|
||||
msgid "language"
|
||||
msgstr "de_DE"
|
||||
|
||||
# Some comment
|
||||
msgid "My text"
|
||||
msgstr "Translated text"
|
||||
|
||||
# More comments
|
||||
msgid "Another string"
|
||||
msgstr ""
|
||||
|
||||
# Multi-line msgid
|
||||
msgid ""
|
||||
"multi\n"
|
||||
"line\n"
|
||||
"id"
|
||||
msgstr "id with multiline content"
|
||||
|
||||
# Multi-line msgid_plural
|
||||
msgid ""
|
||||
"multi\n"
|
||||
"line\n"
|
||||
"plural\n"
|
||||
"id"
|
||||
msgstr "plural id with multiline content"
|
||||
|
||||
# Multi-line string
|
||||
msgid "Multi-line"
|
||||
msgstr ""
|
||||
"Multi \n"
|
||||
"line"
|
||||
|
||||
msgid "One with var: %s"
|
||||
msgid_plural "Several with vars: %s"
|
||||
msgstr[0] "This one is the singular: %s"
|
||||
msgstr[1] "This one is the plural: %s"
|
||||
|
||||
msgctxt "Ctx"
|
||||
msgid "One with var: %s"
|
||||
msgid_plural "Several with vars: %s"
|
||||
msgstr[0] "This one is the singular in a Ctx context: %s"
|
||||
msgstr[1] "This one is the plural in a Ctx context: %s"
|
||||
|
||||
msgid "Some random"
|
||||
msgstr "Some random translation"
|
||||
|
||||
msgctxt "Ctx"
|
||||
msgid "Some random in a context"
|
||||
msgstr "Some random translation in a context"
|
||||
|
||||
msgid "Empty translation"
|
||||
msgstr ""
|
||||
|
||||
msgid "Empty plural form singular"
|
||||
msgid_plural "Empty plural form"
|
||||
msgstr[0] "Singular translated"
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "More"
|
||||
msgstr "More translation"
|
68
fixtures/en_AU/default.po
Normal file
68
fixtures/en_AU/default.po
Normal file
|
@ -0,0 +1,68 @@
|
|||
msgid ""
|
||||
msgstr ""
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Project-Id-Version: \n"
|
||||
"POT-Creation-Date: \n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Last-Translator: Josef Fröhle <froehle@b1-systems.de>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: en_US\n"
|
||||
"X-Generator: Poedit 2.0.6\n"
|
||||
"X-Poedit-SourceCharset: UTF-8\n"
|
||||
|
||||
# Initial comment
|
||||
# Headers below
|
||||
msgid "language"
|
||||
msgstr "en_AU"
|
||||
|
||||
# Some comment
|
||||
msgid "My text"
|
||||
msgstr "Translated text"
|
||||
|
||||
# More comments
|
||||
msgid "Another string"
|
||||
msgstr ""
|
||||
|
||||
# Multi-line msgid
|
||||
msgid "multilineid"
|
||||
msgstr "id with multiline content"
|
||||
|
||||
# Multi-line msgid_plural
|
||||
msgid "multilinepluralid"
|
||||
msgstr "plural id with multiline content"
|
||||
|
||||
# Multi-line string
|
||||
msgid "Multi-line"
|
||||
msgstr "Multi line"
|
||||
|
||||
msgid "One with var: %s"
|
||||
msgid_plural "Several with vars: %s"
|
||||
msgstr[0] "This one is the singular: %s"
|
||||
msgstr[1] "This one is the plural: %s"
|
||||
|
||||
msgctxt "Ctx"
|
||||
msgid "One with var: %s"
|
||||
msgid_plural "Several with vars: %s"
|
||||
msgstr[0] "This one is the singular in a Ctx context: %s"
|
||||
msgstr[1] "This one is the plural in a Ctx context: %s"
|
||||
|
||||
msgid "Some random"
|
||||
msgstr "Some random translation"
|
||||
|
||||
msgctxt "Ctx"
|
||||
msgid "Some random in a context"
|
||||
msgstr "Some random translation in a context"
|
||||
|
||||
msgid "Empty translation"
|
||||
msgstr ""
|
||||
|
||||
msgid "Empty plural form singular"
|
||||
msgid_plural "Empty plural form"
|
||||
msgstr[0] "Singular translated"
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "More"
|
||||
msgstr "More translation"
|
BIN
fixtures/en_GB/default.mo
Normal file
BIN
fixtures/en_GB/default.mo
Normal file
Binary file not shown.
BIN
fixtures/en_US/default.mo
Normal file
BIN
fixtures/en_US/default.mo
Normal file
Binary file not shown.
68
fixtures/en_US/default.po
Normal file
68
fixtures/en_US/default.po
Normal file
|
@ -0,0 +1,68 @@
|
|||
msgid ""
|
||||
msgstr ""
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Project-Id-Version: \n"
|
||||
"POT-Creation-Date: \n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Last-Translator: Josef Fröhle <froehle@b1-systems.de>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: en_US\n"
|
||||
"X-Generator: Poedit 2.0.6\n"
|
||||
"X-Poedit-SourceCharset: UTF-8\n"
|
||||
|
||||
# Initial comment
|
||||
# Headers below
|
||||
msgid "language"
|
||||
msgstr "en_US"
|
||||
|
||||
# Some comment
|
||||
msgid "My text"
|
||||
msgstr "Translated text"
|
||||
|
||||
# More comments
|
||||
msgid "Another string"
|
||||
msgstr ""
|
||||
|
||||
# Multi-line msgid
|
||||
msgid "multilineid"
|
||||
msgstr "id with multiline content"
|
||||
|
||||
# Multi-line msgid_plural
|
||||
msgid "multilinepluralid"
|
||||
msgstr "plural id with multiline content"
|
||||
|
||||
# Multi-line string
|
||||
msgid "Multi-line"
|
||||
msgstr "Multi line"
|
||||
|
||||
msgid "One with var: %s"
|
||||
msgid_plural "Several with vars: %s"
|
||||
msgstr[0] "This one is the singular: %s"
|
||||
msgstr[1] "This one is the plural: %s"
|
||||
|
||||
msgctxt "Ctx"
|
||||
msgid "One with var: %s"
|
||||
msgid_plural "Several with vars: %s"
|
||||
msgstr[0] "This one is the singular in a Ctx context: %s"
|
||||
msgstr[1] "This one is the plural in a Ctx context: %s"
|
||||
|
||||
msgid "Some random"
|
||||
msgstr "Some random translation"
|
||||
|
||||
msgctxt "Ctx"
|
||||
msgid "Some random in a context"
|
||||
msgstr "Some random translation in a context"
|
||||
|
||||
msgid "Empty translation"
|
||||
msgstr ""
|
||||
|
||||
msgid "Empty plural form singular"
|
||||
msgid_plural "Empty plural form"
|
||||
msgstr[0] "Singular translated"
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "More"
|
||||
msgstr "More translation"
|
BIN
fixtures/fr/LC_MESSAGES/default.mo
Normal file
BIN
fixtures/fr/LC_MESSAGES/default.mo
Normal file
Binary file not shown.
77
fixtures/fr/LC_MESSAGES/default.po
Normal file
77
fixtures/fr/LC_MESSAGES/default.po
Normal file
|
@ -0,0 +1,77 @@
|
|||
msgid ""
|
||||
msgstr ""
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
"Project-Id-Version: \n"
|
||||
"POT-Creation-Date: \n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Last-Translator: Josef Fröhle <froehle@b1-systems.de>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: fr\n"
|
||||
"X-Generator: Poedit 2.0.6\n"
|
||||
"X-Poedit-SourceCharset: UTF-8\n"
|
||||
|
||||
# Initial comment
|
||||
# Headers below
|
||||
msgid "language"
|
||||
msgstr "fr"
|
||||
|
||||
# Some comment
|
||||
msgid "My text"
|
||||
msgstr "Translated text"
|
||||
|
||||
# More comments
|
||||
msgid "Another string"
|
||||
msgstr ""
|
||||
|
||||
# Multi-line msgid
|
||||
msgid ""
|
||||
"multi\n"
|
||||
"line\n"
|
||||
"id"
|
||||
msgstr "id with multiline content"
|
||||
|
||||
# Multi-line msgid_plural
|
||||
msgid ""
|
||||
"multi\n"
|
||||
"line\n"
|
||||
"plural\n"
|
||||
"id"
|
||||
msgstr "plural id with multiline content"
|
||||
|
||||
# Multi-line string
|
||||
msgid "Multi-line"
|
||||
msgstr ""
|
||||
"Multi \n"
|
||||
"line"
|
||||
|
||||
msgid "One with var: %s"
|
||||
msgid_plural "Several with vars: %s"
|
||||
msgstr[0] "This one is the singular: %s"
|
||||
msgstr[1] "This one is the plural: %s"
|
||||
|
||||
msgctxt "Ctx"
|
||||
msgid "One with var: %s"
|
||||
msgid_plural "Several with vars: %s"
|
||||
msgstr[0] "This one is the singular in a Ctx context: %s"
|
||||
msgstr[1] "This one is the plural in a Ctx context: %s"
|
||||
|
||||
msgid "Some random"
|
||||
msgstr "Some random translation"
|
||||
|
||||
msgctxt "Ctx"
|
||||
msgid "Some random in a context"
|
||||
msgstr "Some random translation in a context"
|
||||
|
||||
msgid "Empty translation"
|
||||
msgstr ""
|
||||
|
||||
msgid "Empty plural form singular"
|
||||
msgid_plural "Empty plural form"
|
||||
msgstr[0] "Singular translated"
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "More"
|
||||
msgstr "More translation"
|
60
gotext.go
60
gotext.go
|
@ -23,7 +23,6 @@ For quick/simple translations you can use the package level functions directly.
|
|||
package gotext
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
|
@ -37,7 +36,7 @@ type config struct {
|
|||
// Language set.
|
||||
language string
|
||||
|
||||
// Path to library directory where all locale directories and translation files are.
|
||||
// Path to library directory where all locale directories and Translation files are.
|
||||
library string
|
||||
|
||||
// Storage for package level methods
|
||||
|
@ -65,7 +64,7 @@ func loadStorage(force bool) {
|
|||
globalConfig.storage = NewLocale(globalConfig.library, globalConfig.language)
|
||||
}
|
||||
|
||||
if _, ok := globalConfig.storage.domains[globalConfig.domain]; !ok || force {
|
||||
if _, ok := globalConfig.storage.Domains[globalConfig.domain]; !ok || force {
|
||||
globalConfig.storage.AddDomain(globalConfig.domain)
|
||||
}
|
||||
|
||||
|
@ -74,18 +73,27 @@ func loadStorage(force bool) {
|
|||
|
||||
// GetDomain is the domain getter for the package configuration
|
||||
func GetDomain() string {
|
||||
var dom string
|
||||
globalConfig.RLock()
|
||||
dom := globalConfig.domain
|
||||
if globalConfig.storage != nil {
|
||||
dom = globalConfig.storage.GetDomain()
|
||||
}
|
||||
if dom == "" {
|
||||
dom = globalConfig.domain
|
||||
}
|
||||
globalConfig.RUnlock()
|
||||
|
||||
return dom
|
||||
}
|
||||
|
||||
// SetDomain sets the name for the domain to be used at package level.
|
||||
// It reloads the corresponding translation file.
|
||||
// It reloads the corresponding Translation file.
|
||||
func SetDomain(dom string) {
|
||||
globalConfig.Lock()
|
||||
globalConfig.domain = dom
|
||||
if globalConfig.storage != nil {
|
||||
globalConfig.storage.SetDomain(dom)
|
||||
}
|
||||
globalConfig.Unlock()
|
||||
|
||||
loadStorage(true)
|
||||
|
@ -101,10 +109,10 @@ func GetLanguage() string {
|
|||
}
|
||||
|
||||
// SetLanguage sets the language code to be used at package level.
|
||||
// It reloads the corresponding translation file.
|
||||
// It reloads the corresponding Translation file.
|
||||
func SetLanguage(lang string) {
|
||||
globalConfig.Lock()
|
||||
globalConfig.language = lang
|
||||
globalConfig.language = SimplifiedLocale(lang)
|
||||
globalConfig.Unlock()
|
||||
|
||||
loadStorage(true)
|
||||
|
@ -120,7 +128,7 @@ func GetLibrary() string {
|
|||
}
|
||||
|
||||
// SetLibrary sets the root path for the loale directories and files to be used at package level.
|
||||
// It reloads the corresponding translation file.
|
||||
// It reloads the corresponding Translation file.
|
||||
func SetLibrary(lib string) {
|
||||
globalConfig.Lock()
|
||||
globalConfig.library = lib
|
||||
|
@ -129,47 +137,48 @@ func SetLibrary(lib string) {
|
|||
loadStorage(true)
|
||||
}
|
||||
|
||||
// Configure sets all configuration variables to be used at package level and reloads the corresponding translation file.
|
||||
// Configure sets all configuration variables to be used at package level and reloads the corresponding Translation file.
|
||||
// It receives the library path, language code and domain name.
|
||||
// This function is recommended to be used when changing more than one setting,
|
||||
// as using each setter will introduce a I/O overhead because the translation file will be loaded after each set.
|
||||
// as using each setter will introduce a I/O overhead because the Translation file will be loaded after each set.
|
||||
func Configure(lib, lang, dom string) {
|
||||
globalConfig.Lock()
|
||||
|
||||
globalConfig.library = lib
|
||||
globalConfig.language = lang
|
||||
globalConfig.language = SimplifiedLocale(lang)
|
||||
globalConfig.domain = dom
|
||||
globalConfig.storage.SetDomain(dom)
|
||||
|
||||
globalConfig.Unlock()
|
||||
|
||||
loadStorage(true)
|
||||
}
|
||||
|
||||
// Get uses the default domain globally set to return the corresponding translation of a given string.
|
||||
// Get uses the default domain globally set to return the corresponding Translation of a given string.
|
||||
// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax.
|
||||
func Get(str string, vars ...interface{}) string {
|
||||
return GetD(GetDomain(), str, vars...)
|
||||
}
|
||||
|
||||
// GetN retrieves the (N)th plural form of translation for the given string in the default domain.
|
||||
// GetN retrieves the (N)th plural form of Translation for the given string in the default domain.
|
||||
// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax.
|
||||
func GetN(str, plural string, n int, vars ...interface{}) string {
|
||||
return GetND(GetDomain(), str, plural, n, vars...)
|
||||
}
|
||||
|
||||
// GetD returns the corresponding translation in the given domain for a given string.
|
||||
// GetD returns the corresponding Translation in the given domain for a given string.
|
||||
// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax.
|
||||
func GetD(dom, str string, vars ...interface{}) string {
|
||||
return GetND(dom, str, str, 1, vars...)
|
||||
}
|
||||
|
||||
// GetND retrieves the (N)th plural form of translation in the given domain for a given string.
|
||||
// GetND retrieves the (N)th plural form of Translation in the given domain for a given string.
|
||||
// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax.
|
||||
func GetND(dom, str, plural string, n int, vars ...interface{}) string {
|
||||
// Try to load default package Locale storage
|
||||
loadStorage(false)
|
||||
|
||||
// Return translation
|
||||
// Return Translation
|
||||
globalConfig.RLock()
|
||||
tr := globalConfig.storage.GetND(dom, str, plural, n, vars...)
|
||||
globalConfig.RUnlock()
|
||||
|
@ -177,43 +186,34 @@ func GetND(dom, str, plural string, n int, vars ...interface{}) string {
|
|||
return tr
|
||||
}
|
||||
|
||||
// GetC uses the default domain globally set to return the corresponding translation of the given string in the given context.
|
||||
// GetC uses the default domain globally set to return the corresponding Translation of the given string in the given context.
|
||||
// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax.
|
||||
func GetC(str, ctx string, vars ...interface{}) string {
|
||||
return GetDC(GetDomain(), str, ctx, vars...)
|
||||
}
|
||||
|
||||
// GetNC retrieves the (N)th plural form of translation for the given string in the given context in the default domain.
|
||||
// GetNC retrieves the (N)th plural form of Translation for the given string in the given context in the default domain.
|
||||
// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax.
|
||||
func GetNC(str, plural string, n int, ctx string, vars ...interface{}) string {
|
||||
return GetNDC(GetDomain(), str, plural, n, ctx, vars...)
|
||||
}
|
||||
|
||||
// GetDC returns the corresponding translation in the given domain for the given string in the given context.
|
||||
// GetDC returns the corresponding Translation in the given domain for the given string in the given context.
|
||||
// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax.
|
||||
func GetDC(dom, str, ctx string, vars ...interface{}) string {
|
||||
return GetNDC(dom, str, str, 1, ctx, vars...)
|
||||
}
|
||||
|
||||
// GetNDC retrieves the (N)th plural form of translation in the given domain for a given string.
|
||||
// GetNDC retrieves the (N)th plural form of Translation in the given domain for a given string.
|
||||
// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax.
|
||||
func GetNDC(dom, str, plural string, n int, ctx string, vars ...interface{}) string {
|
||||
// Try to load default package Locale storage
|
||||
loadStorage(false)
|
||||
|
||||
// Return translation
|
||||
// Return Translation
|
||||
globalConfig.RLock()
|
||||
tr := globalConfig.storage.GetNDC(dom, str, plural, n, ctx, vars...)
|
||||
globalConfig.RUnlock()
|
||||
|
||||
return tr
|
||||
}
|
||||
|
||||
// printf applies text formatting only when needed to parse variables.
|
||||
func printf(str string, vars ...interface{}) string {
|
||||
if len(vars) > 0 {
|
||||
return fmt.Sprintf(str, vars...)
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package gotext
|
|||
import (
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
@ -65,14 +66,14 @@ msgstr[0] "This one is the singular in a Ctx context: %s"
|
|||
msgstr[1] "This one is the plural in a Ctx context: %s"
|
||||
|
||||
msgid "Some random"
|
||||
msgstr "Some random translation"
|
||||
msgstr "Some random Translation"
|
||||
|
||||
msgctxt "Ctx"
|
||||
msgid "Some random in a context"
|
||||
msgstr "Some random translation in a context"
|
||||
msgstr "Some random Translation in a context"
|
||||
|
||||
msgid "More"
|
||||
msgstr "More translation"
|
||||
msgstr "More Translation"
|
||||
|
||||
msgid "Untranslated"
|
||||
msgid_plural "Several untranslated"
|
||||
|
@ -95,13 +96,15 @@ msgstr[1] ""
|
|||
if err != nil {
|
||||
t.Fatalf("Can't create test file: %s", err.Error())
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
_, err = f.WriteString(str)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't write to test file: %s", err.Error())
|
||||
}
|
||||
|
||||
// Move file close to write the file, so we can use it in the next step
|
||||
f.Close()
|
||||
|
||||
// Set package configuration
|
||||
Configure("/tmp", "en_US", "default")
|
||||
|
||||
|
@ -125,8 +128,8 @@ msgstr[1] ""
|
|||
|
||||
// Test context translations
|
||||
tr = GetC("Some random in a context", "Ctx")
|
||||
if tr != "Some random translation in a context" {
|
||||
t.Errorf("Expected 'Some random translation in a context' but got '%s'", tr)
|
||||
if tr != "Some random Translation in a context" {
|
||||
t.Errorf("Expected 'Some random Translation in a context' but got '%s'", tr)
|
||||
}
|
||||
|
||||
v = "Variable"
|
||||
|
@ -214,6 +217,38 @@ msgstr[1] ""
|
|||
}
|
||||
}
|
||||
|
||||
func TestMoAndPoTranslator(t *testing.T) {
|
||||
|
||||
fixPath, _ := filepath.Abs("./fixtures/")
|
||||
|
||||
Configure(fixPath, "en_GB", "default")
|
||||
|
||||
// Check default domain Translation
|
||||
SetDomain("default")
|
||||
tr := Get("My text")
|
||||
if tr != "Translated text" {
|
||||
t.Errorf("Expected 'Translated text'. Got '%s'", tr)
|
||||
}
|
||||
tr = Get("language")
|
||||
if tr != "en_GB" {
|
||||
t.Errorf("Expected 'en_GB'. Got '%s'", tr)
|
||||
}
|
||||
|
||||
// Change Language (locale)
|
||||
SetLanguage("en_AU")
|
||||
|
||||
// Check default domain Translation
|
||||
SetDomain("default")
|
||||
tr = Get("My text")
|
||||
if tr != "Translated text" {
|
||||
t.Errorf("Expected 'Translated text'. Got '%s'", tr)
|
||||
}
|
||||
tr = Get("language")
|
||||
if tr != "en_AU" {
|
||||
t.Errorf("Expected 'en_AU'. Got '%s'", tr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDomains(t *testing.T) {
|
||||
// Set PO content
|
||||
strDefault := `
|
||||
|
@ -222,13 +257,13 @@ msgstr "Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
|||
|
||||
msgid "Default text"
|
||||
msgid_plural "Default texts"
|
||||
msgstr[0] "Default translation"
|
||||
msgstr[0] "Default Translation"
|
||||
msgstr[1] "Default translations"
|
||||
|
||||
msgctxt "Ctx"
|
||||
msgid "Default context"
|
||||
msgid_plural "Default contexts"
|
||||
msgstr[0] "Default ctx translation"
|
||||
msgstr[0] "Default ctx Translation"
|
||||
msgstr[1] "Default ctx translations"
|
||||
`
|
||||
|
||||
|
@ -238,13 +273,13 @@ msgstr "Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
|||
|
||||
msgid "Custom text"
|
||||
msgid_plural "Custom texts"
|
||||
msgstr[0] "Custom translation"
|
||||
msgstr[0] "Custom Translation"
|
||||
msgstr[1] "Custom translations"
|
||||
|
||||
msgctxt "Ctx"
|
||||
msgid "Custom context"
|
||||
msgid_plural "Custom contexts"
|
||||
msgstr[0] "Custom ctx translation"
|
||||
msgstr[0] "Custom ctx Translation"
|
||||
msgstr[1] "Custom ctx translations"
|
||||
`
|
||||
|
||||
|
@ -278,19 +313,19 @@ msgstr[1] "Custom ctx translations"
|
|||
|
||||
Configure("/tmp", "en_US", "default")
|
||||
|
||||
// Check default domain translation
|
||||
// Check default domain Translation
|
||||
SetDomain("default")
|
||||
tr := Get("Default text")
|
||||
if tr != "Default translation" {
|
||||
t.Errorf("Expected 'Default translation'. Got '%s'", tr)
|
||||
if tr != "Default Translation" {
|
||||
t.Errorf("Expected 'Default Translation'. Got '%s'", tr)
|
||||
}
|
||||
tr = GetN("Default text", "Default texts", 23)
|
||||
if tr != "Default translations" {
|
||||
t.Errorf("Expected 'Default translations'. Got '%s'", tr)
|
||||
}
|
||||
tr = GetC("Default context", "Ctx")
|
||||
if tr != "Default ctx translation" {
|
||||
t.Errorf("Expected 'Default ctx translation'. Got '%s'", tr)
|
||||
if tr != "Default ctx Translation" {
|
||||
t.Errorf("Expected 'Default ctx Translation'. Got '%s'", tr)
|
||||
}
|
||||
tr = GetNC("Default context", "Default contexts", 23, "Ctx")
|
||||
if tr != "Default ctx translations" {
|
||||
|
@ -299,16 +334,16 @@ msgstr[1] "Custom ctx translations"
|
|||
|
||||
SetDomain("custom")
|
||||
tr = Get("Custom text")
|
||||
if tr != "Custom translation" {
|
||||
t.Errorf("Expected 'Custom translation'. Got '%s'", tr)
|
||||
if tr != "Custom Translation" {
|
||||
t.Errorf("Expected 'Custom Translation'. Got '%s'", tr)
|
||||
}
|
||||
tr = GetN("Custom text", "Custom texts", 23)
|
||||
if tr != "Custom translations" {
|
||||
t.Errorf("Expected 'Custom translations'. Got '%s'", tr)
|
||||
}
|
||||
tr = GetC("Custom context", "Ctx")
|
||||
if tr != "Custom ctx translation" {
|
||||
t.Errorf("Expected 'Custom ctx translation'. Got '%s'", tr)
|
||||
if tr != "Custom ctx Translation" {
|
||||
t.Errorf("Expected 'Custom ctx Translation'. Got '%s'", tr)
|
||||
}
|
||||
tr = GetNC("Custom context", "Custom contexts", 23, "Ctx")
|
||||
if tr != "Custom ctx translations" {
|
||||
|
@ -334,7 +369,7 @@ msgstr[2] "And this is the second plural form: %s"
|
|||
|
||||
msgctxt "Ctx"
|
||||
msgid "Some random in a context"
|
||||
msgstr "Some random translation in a context"
|
||||
msgstr "Some random Translation in a context"
|
||||
|
||||
`
|
||||
|
||||
|
|
85
helper.go
Normal file
85
helper.go
Normal file
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright (c) 2018 DeineAgentur UG https://www.deineagentur.com. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
*/
|
||||
|
||||
package gotext
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var re = regexp.MustCompile(`%\(([a-zA-Z0-9_]+)\)[.0-9]*[xsvTtbcdoqXxUeEfFgGp]`)
|
||||
|
||||
func SimplifiedLocale(lang string) string {
|
||||
// en_US/en_US.UTF-8/zh_CN/zh_TW/el_GR@euro/...
|
||||
if idx := strings.Index(lang, ":"); idx != -1 {
|
||||
lang = lang[:idx]
|
||||
}
|
||||
if idx := strings.Index(lang, "@"); idx != -1 {
|
||||
lang = lang[:idx]
|
||||
}
|
||||
if idx := strings.Index(lang, "."); idx != -1 {
|
||||
lang = lang[:idx]
|
||||
}
|
||||
return strings.TrimSpace(lang)
|
||||
}
|
||||
|
||||
// printf applies text formatting only when needed to parse variables.
|
||||
func Printf(str string, vars ...interface{}) string {
|
||||
if len(vars) > 0 {
|
||||
return fmt.Sprintf(str, vars...)
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
|
||||
// NPrintf support named format
|
||||
func NPrintf(format string, params map[string]interface{}) {
|
||||
f, p := parseSprintf(format, params)
|
||||
fmt.Printf(f, p...)
|
||||
}
|
||||
|
||||
// Sprintf support named format
|
||||
// Sprintf("%(name)s is Type %(type)s", map[string]interface{}{"name": "Gotext", "type": "struct"})
|
||||
func Sprintf(format string, params map[string]interface{}) string {
|
||||
f, p := parseSprintf(format, params)
|
||||
return fmt.Sprintf(f, p...)
|
||||
}
|
||||
|
||||
func parseSprintf(format string, params map[string]interface{}) (string, []interface{}) {
|
||||
f, n := reformatSprintf(format)
|
||||
var p []interface{}
|
||||
for _, v := range n {
|
||||
p = append(p, params[v])
|
||||
}
|
||||
return f, p
|
||||
}
|
||||
|
||||
func reformatSprintf(f string) (string, []string) {
|
||||
m := re.FindAllStringSubmatch(f, -1)
|
||||
i := re.FindAllStringSubmatchIndex(f, -1)
|
||||
|
||||
ord := []string{}
|
||||
for _, v := range m {
|
||||
ord = append(ord, v[1])
|
||||
}
|
||||
|
||||
pair := []int{0}
|
||||
for _, v := range i {
|
||||
pair = append(pair, v[2]-1)
|
||||
pair = append(pair, v[3]+1)
|
||||
}
|
||||
pair = append(pair, len(f))
|
||||
plen := len(pair)
|
||||
|
||||
out := ""
|
||||
for n := 0; n < plen; n += 2 {
|
||||
out += f[pair[n]:pair[n+1]]
|
||||
}
|
||||
|
||||
return out, ord
|
||||
}
|
112
helper_test.go
Normal file
112
helper_test.go
Normal file
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* Copyright (c) 2018 DeineAgentur UG https://www.deineagentur.com. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
*/
|
||||
|
||||
package gotext
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSimplifiedLocale(t *testing.T) {
|
||||
tr :=SimplifiedLocale("de_DE@euro")
|
||||
if tr != "de_DE" {
|
||||
t.Errorf("Expected 'de_DE' but got '%s'", tr)
|
||||
}
|
||||
|
||||
tr =SimplifiedLocale("de_DE.UTF-8")
|
||||
if tr != "de_DE" {
|
||||
t.Errorf("Expected 'de_DE' but got '%s'", tr)
|
||||
}
|
||||
|
||||
tr =SimplifiedLocale("de_DE:latin1")
|
||||
if tr != "de_DE" {
|
||||
t.Errorf("Expected 'de_DE' but got '%s'", tr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReformattingSingleNamedPattern(t *testing.T) {
|
||||
pat := "%(name_me)x"
|
||||
|
||||
f, n := reformatSprintf(pat)
|
||||
|
||||
if f != "%x" {
|
||||
t.Errorf("pattern should be %%x but %v", f)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(n, []string{"name_me"}) {
|
||||
t.Errorf("named var should be {name_me} but %v", n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReformattingMultipleNamedPattern(t *testing.T) {
|
||||
pat := "%(name_me)x and %(another_name)v"
|
||||
|
||||
f, n := reformatSprintf(pat)
|
||||
|
||||
if f != "%x and %v" {
|
||||
t.Errorf("pattern should be %%x and %%v but %v", f)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(n, []string{"name_me", "another_name"}) {
|
||||
t.Errorf("named var should be {name_me, another_name} but %v", n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReformattingRepeatedNamedPattern(t *testing.T) {
|
||||
pat := "%(name_me)x and %(another_name)v and %(name_me)v"
|
||||
|
||||
f, n := reformatSprintf(pat)
|
||||
|
||||
if f != "%x and %v and %v" {
|
||||
t.Errorf("pattern should be %%x and %%v and %%v but %v", f)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(n, []string{"name_me", "another_name", "name_me"}) {
|
||||
t.Errorf("named var should be {name_me, another_name, name_me} but %v", n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSprintf(t *testing.T) {
|
||||
pat := "%(brother)s loves %(sister)s. %(sister)s also loves %(brother)s."
|
||||
params := map[string]interface{}{
|
||||
"sister": "Susan",
|
||||
"brother": "Louis",
|
||||
}
|
||||
|
||||
s := Sprintf(pat, params)
|
||||
|
||||
if s != "Louis loves Susan. Susan also loves Louis." {
|
||||
t.Errorf("result should be Louis loves Susan. Susan also love Louis. but %v", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNPrintf(t *testing.T) {
|
||||
pat := "%(brother)s loves %(sister)s. %(sister)s also loves %(brother)s.\n"
|
||||
params := map[string]interface{}{
|
||||
"sister": "Susan",
|
||||
"brother": "Louis",
|
||||
}
|
||||
|
||||
NPrintf(pat, params)
|
||||
|
||||
}
|
||||
|
||||
func TestSprintfFloatsWithPrecision(t *testing.T) {
|
||||
pat := "%(float)f / %(floatprecision).1f / %(long)g / %(longprecision).3g"
|
||||
params := map[string]interface{}{
|
||||
"float": 5.034560,
|
||||
"floatprecision": 5.03456,
|
||||
"long": 5.03456,
|
||||
"longprecision": 5.03456,
|
||||
}
|
||||
|
||||
s := Sprintf(pat, params)
|
||||
|
||||
expectedresult := "5.034560 / 5.0 / 5.03456 / 5.03"
|
||||
if s != expectedresult {
|
||||
t.Errorf("result should be (%v) but is (%v)", expectedresult, s)
|
||||
}
|
||||
}
|
128
locale.go
128
locale.go
|
@ -1,3 +1,8 @@
|
|||
/*
|
||||
* Copyright (c) 2018 DeineAgentur UG https://www.deineagentur.com. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
*/
|
||||
|
||||
package gotext
|
||||
|
||||
import (
|
||||
|
@ -22,13 +27,13 @@ Example:
|
|||
// Create Locale with library path and language code
|
||||
l := gotext.NewLocale("/path/to/i18n/dir", "en_US")
|
||||
|
||||
// Load domain '/path/to/i18n/dir/en_US/LC_MESSAGES/default.po'
|
||||
// Load domain '/path/to/i18n/dir/en_US/LC_MESSAGES/default.{po,mo}'
|
||||
l.AddDomain("default")
|
||||
|
||||
// Translate text from default domain
|
||||
fmt.Println(l.Get("Translate this"))
|
||||
|
||||
// Load different domain ('/path/to/i18n/dir/en_US/LC_MESSAGES/extras.po')
|
||||
// Load different domain ('/path/to/i18n/dir/en_US/LC_MESSAGES/extras.{po,mo}')
|
||||
l.AddDomain("extras")
|
||||
|
||||
// Translate text from domain
|
||||
|
@ -43,8 +48,11 @@ type Locale struct {
|
|||
// Language for this Locale
|
||||
lang string
|
||||
|
||||
// List of available domains for this locale.
|
||||
domains map[string]*Po
|
||||
// List of available Domains for this locale.
|
||||
Domains map[string]Translator
|
||||
|
||||
// First AddDomain is default Domain
|
||||
defaultDomain string
|
||||
|
||||
// Sync Mutex
|
||||
sync.RWMutex
|
||||
|
@ -55,124 +63,162 @@ type Locale struct {
|
|||
func NewLocale(p, l string) *Locale {
|
||||
return &Locale{
|
||||
path: p,
|
||||
lang: l,
|
||||
domains: make(map[string]*Po),
|
||||
lang: SimplifiedLocale(l),
|
||||
Domains: make(map[string]Translator),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Locale) findPO(dom string) string {
|
||||
filename := path.Join(l.path, l.lang, "LC_MESSAGES", dom+".po")
|
||||
func (l *Locale) findExt(dom, ext string) string {
|
||||
filename := path.Join(l.path, l.lang, "LC_MESSAGES", dom+"."+ext)
|
||||
if _, err := os.Stat(filename); err == nil {
|
||||
return filename
|
||||
}
|
||||
|
||||
if len(l.lang) > 2 {
|
||||
filename = path.Join(l.path, l.lang[:2], "LC_MESSAGES", dom+".po")
|
||||
filename = path.Join(l.path, l.lang[:2], "LC_MESSAGES", dom+"."+ext)
|
||||
if _, err := os.Stat(filename); err == nil {
|
||||
return filename
|
||||
}
|
||||
}
|
||||
|
||||
filename = path.Join(l.path, l.lang, dom+".po")
|
||||
filename = path.Join(l.path, l.lang, dom+"."+ext)
|
||||
if _, err := os.Stat(filename); err == nil {
|
||||
return filename
|
||||
}
|
||||
|
||||
if len(l.lang) > 2 {
|
||||
filename = path.Join(l.path, l.lang[:2], dom+".po")
|
||||
filename = path.Join(l.path, l.lang[:2], dom+"."+ext)
|
||||
if _, err := os.Stat(filename); err == nil {
|
||||
return filename
|
||||
}
|
||||
}
|
||||
|
||||
return filename
|
||||
return ""
|
||||
}
|
||||
|
||||
// AddDomain creates a new domain for a given locale object and initializes the Po object.
|
||||
// If the domain exists, it gets reloaded.
|
||||
func (l *Locale) AddDomain(dom string) {
|
||||
po := new(Po)
|
||||
var poObj Translator
|
||||
|
||||
// Parse file.
|
||||
po.ParseFile(l.findPO(dom))
|
||||
file := l.findExt(dom, "po")
|
||||
if file != "" {
|
||||
poObj = new(Po)
|
||||
// Parse file.
|
||||
poObj.ParseFile(file)
|
||||
} else {
|
||||
file = l.findExt(dom, "mo")
|
||||
if file != "" {
|
||||
poObj = new(Mo)
|
||||
// Parse file.
|
||||
poObj.ParseFile(file)
|
||||
} else {
|
||||
// fallback return if no file found with
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Save new domain
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
|
||||
if l.domains == nil {
|
||||
l.domains = make(map[string]*Po)
|
||||
if l.Domains == nil {
|
||||
l.Domains = make(map[string]Translator)
|
||||
}
|
||||
l.domains[dom] = po
|
||||
if l.defaultDomain == "" {
|
||||
l.defaultDomain = dom
|
||||
}
|
||||
l.Domains[dom] = poObj
|
||||
|
||||
// Unlock "Save new domain"
|
||||
l.Unlock()
|
||||
}
|
||||
|
||||
// Get uses a domain "default" to return the corresponding translation of a given string.
|
||||
// GetDomain is the domain getter for the package configuration
|
||||
func (l *Locale) GetDomain() string {
|
||||
l.RLock()
|
||||
dom := l.defaultDomain
|
||||
l.RUnlock()
|
||||
return dom
|
||||
}
|
||||
|
||||
// SetDomain sets the name for the domain to be used at package level.
|
||||
// It reloads the corresponding Translation file.
|
||||
func (l *Locale) SetDomain(dom string) {
|
||||
l.Lock()
|
||||
l.defaultDomain = dom
|
||||
l.Unlock()
|
||||
}
|
||||
|
||||
// Get uses a domain "default" to return the corresponding Translation of a given string.
|
||||
// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax.
|
||||
func (l *Locale) Get(str string, vars ...interface{}) string {
|
||||
return l.GetD(GetDomain(), str, vars...)
|
||||
return l.GetD(l.defaultDomain, str, vars...)
|
||||
}
|
||||
|
||||
// GetN retrieves the (N)th plural form of translation for the given string in the "default" domain.
|
||||
// GetN retrieves the (N)th plural form of Translation for the given string in the "default" domain.
|
||||
// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax.
|
||||
func (l *Locale) GetN(str, plural string, n int, vars ...interface{}) string {
|
||||
return l.GetND(GetDomain(), str, plural, n, vars...)
|
||||
return l.GetND(l.defaultDomain, str, plural, n, vars...)
|
||||
}
|
||||
|
||||
// GetD returns the corresponding translation in the given domain for the given string.
|
||||
// GetD returns the corresponding Translation in the given domain for the given string.
|
||||
// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax.
|
||||
func (l *Locale) GetD(dom, str string, vars ...interface{}) string {
|
||||
return l.GetND(dom, str, str, 1, vars...)
|
||||
}
|
||||
|
||||
// GetND retrieves the (N)th plural form of translation in the given domain for the given string.
|
||||
// GetND retrieves the (N)th plural form of Translation in the given domain for the given string.
|
||||
// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax.
|
||||
func (l *Locale) GetND(dom, str, plural string, n int, vars ...interface{}) string {
|
||||
// Sync read
|
||||
l.RLock()
|
||||
defer l.RUnlock()
|
||||
|
||||
if l.domains != nil {
|
||||
if _, ok := l.domains[dom]; ok {
|
||||
if l.domains[dom] != nil {
|
||||
return l.domains[dom].GetN(str, plural, n, vars...)
|
||||
if l.Domains != nil {
|
||||
if _, ok := l.Domains[dom]; ok {
|
||||
if l.Domains[dom] != nil {
|
||||
return l.Domains[dom].GetN(str, plural, n, vars...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the same we received by default
|
||||
return printf(plural, vars...)
|
||||
return Printf(plural, vars...)
|
||||
}
|
||||
|
||||
// GetC uses a domain "default" to return the corresponding translation of the given string in the given context.
|
||||
// GetC uses a domain "default" to return the corresponding Translation of the given string in the given context.
|
||||
// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax.
|
||||
func (l *Locale) GetC(str, ctx string, vars ...interface{}) string {
|
||||
return l.GetDC(GetDomain(), str, ctx, vars...)
|
||||
return l.GetDC(l.defaultDomain, str, ctx, vars...)
|
||||
}
|
||||
|
||||
// GetNC retrieves the (N)th plural form of translation for the given string in the given context in the "default" domain.
|
||||
// GetNC retrieves the (N)th plural form of Translation for the given string in the given context in the "default" domain.
|
||||
// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax.
|
||||
func (l *Locale) GetNC(str, plural string, n int, ctx string, vars ...interface{}) string {
|
||||
return l.GetNDC(GetDomain(), str, plural, n, ctx, vars...)
|
||||
return l.GetNDC(l.defaultDomain, str, plural, n, ctx, vars...)
|
||||
}
|
||||
|
||||
// GetDC returns the corresponding translation in the given domain for the given string in the given context.
|
||||
// GetDC returns the corresponding Translation in the given domain for the given string in the given context.
|
||||
// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax.
|
||||
func (l *Locale) GetDC(dom, str, ctx string, vars ...interface{}) string {
|
||||
return l.GetNDC(dom, str, str, 1, ctx, vars...)
|
||||
}
|
||||
|
||||
// GetNDC retrieves the (N)th plural form of translation in the given domain for the given string in the given context.
|
||||
// GetNDC retrieves the (N)th plural form of Translation in the given domain for the given string in the given context.
|
||||
// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax.
|
||||
func (l *Locale) GetNDC(dom, str, plural string, n int, ctx string, vars ...interface{}) string {
|
||||
// Sync read
|
||||
l.RLock()
|
||||
defer l.RUnlock()
|
||||
|
||||
if l.domains != nil {
|
||||
if _, ok := l.domains[dom]; ok {
|
||||
if l.domains[dom] != nil {
|
||||
return l.domains[dom].GetNC(str, plural, n, ctx, vars...)
|
||||
if l.Domains != nil {
|
||||
if _, ok := l.Domains[dom]; ok {
|
||||
if l.Domains[dom] != nil {
|
||||
return l.Domains[dom].GetNC(str, plural, n, ctx, vars...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the same we received by default
|
||||
return printf(plural, vars...)
|
||||
return Printf(plural, vars...)
|
||||
}
|
||||
|
|
169
locale_test.go
169
locale_test.go
|
@ -1,3 +1,8 @@
|
|||
/*
|
||||
* Copyright (c) 2018 DeineAgentur UG https://www.deineagentur.com. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
*/
|
||||
|
||||
package gotext
|
||||
|
||||
import (
|
||||
|
@ -45,14 +50,14 @@ msgstr[0] "This one is the singular in a Ctx context: %s"
|
|||
msgstr[1] "This one is the plural in a Ctx context: %s"
|
||||
|
||||
msgid "Some random"
|
||||
msgstr "Some random translation"
|
||||
msgstr "Some random Translation"
|
||||
|
||||
msgctxt "Ctx"
|
||||
msgid "Some random in a context"
|
||||
msgstr "Some random translation in a context"
|
||||
msgstr "Some random Translation in a context"
|
||||
|
||||
msgid "More"
|
||||
msgstr "More translation"
|
||||
msgstr "More Translation"
|
||||
|
||||
`
|
||||
|
||||
|
@ -81,14 +86,11 @@ msgstr "More translation"
|
|||
l := NewLocale("/tmp", "en_US")
|
||||
|
||||
// Force nil domain storage
|
||||
l.domains = nil
|
||||
l.Domains = nil
|
||||
|
||||
// Add domain
|
||||
l.AddDomain("my_domain")
|
||||
|
||||
// Set global domain
|
||||
SetDomain("my_domain")
|
||||
|
||||
// Test translations
|
||||
tr := l.GetD("my_domain", "My text")
|
||||
if tr != "Translated text" {
|
||||
|
@ -109,8 +111,8 @@ msgstr "More translation"
|
|||
|
||||
// Test context translations
|
||||
tr = l.GetC("Some random in a context", "Ctx")
|
||||
if tr != "Some random translation in a context" {
|
||||
t.Errorf("Expected 'Some random translation in a context'. Got '%s'", tr)
|
||||
if tr != "Some random Translation in a context" {
|
||||
t.Errorf("Expected 'Some random Translation in a context'. Got '%s'", tr)
|
||||
}
|
||||
|
||||
v = "Test"
|
||||
|
@ -130,10 +132,10 @@ msgstr "More translation"
|
|||
t.Errorf("Expected 'This one is the plural in a Ctx context: Test' but got '%s'", tr)
|
||||
}
|
||||
|
||||
// Test last translation
|
||||
// Test last Translation
|
||||
tr = l.GetD("my_domain", "More")
|
||||
if tr != "More translation" {
|
||||
t.Errorf("Expected 'More translation' but got '%s'", tr)
|
||||
if tr != "More Translation" {
|
||||
t.Errorf("Expected 'More Translation' but got '%s'", tr)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -178,14 +180,14 @@ msgstr[0] "This one is the singular in a Ctx context: %s"
|
|||
msgstr[1] "This one is the plural in a Ctx context: %s"
|
||||
|
||||
msgid "Some random"
|
||||
msgstr "Some random translation"
|
||||
msgstr "Some random Translation"
|
||||
|
||||
msgctxt "Ctx"
|
||||
msgid "Some random in a context"
|
||||
msgstr "Some random translation in a context"
|
||||
msgstr "Some random Translation in a context"
|
||||
|
||||
msgid "More"
|
||||
msgstr "More translation"
|
||||
msgstr "More Translation"
|
||||
|
||||
`
|
||||
|
||||
|
@ -214,16 +216,28 @@ msgstr "More translation"
|
|||
l := NewLocale("/tmp", "en_US")
|
||||
|
||||
// Force nil domain storage
|
||||
l.domains = nil
|
||||
l.Domains = nil
|
||||
|
||||
// Add domain
|
||||
l.AddDomain("my_domain")
|
||||
|
||||
// Set default domain to make it fail
|
||||
SetDomain("default")
|
||||
// Test non-existent "default" domain responses
|
||||
tr := l.GetDomain()
|
||||
if tr != "my_domain" {
|
||||
t.Errorf("Expected 'my_domain' but got '%s'", tr)
|
||||
}
|
||||
|
||||
// Test non-existent "deafult" domain responses
|
||||
tr := l.Get("My text")
|
||||
// Set default domain to make it fail
|
||||
l.SetDomain("default")
|
||||
|
||||
// Test non-existent "default" domain responses
|
||||
tr = l.GetDomain()
|
||||
if tr != "default" {
|
||||
t.Errorf("Expected 'default' but got '%s'", tr)
|
||||
}
|
||||
|
||||
// Test non-existent "default" domain responses
|
||||
tr = l.Get("My text")
|
||||
if tr != "My text" {
|
||||
t.Errorf("Expected 'My text' but got '%s'", tr)
|
||||
}
|
||||
|
@ -255,6 +269,121 @@ msgstr "More translation"
|
|||
if tr != "This are tests" {
|
||||
t.Errorf("Expected 'Plural index' but got '%s'", tr)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Create Locale with full language code
|
||||
l = NewLocale("/tmp", "golem")
|
||||
|
||||
// Force nil domain storage
|
||||
l.Domains = nil
|
||||
|
||||
// Add domain
|
||||
l.SetDomain("my_domain")
|
||||
|
||||
// Test non-existent "default" domain responses
|
||||
tr = l.GetDomain()
|
||||
if tr != "my_domain" {
|
||||
t.Errorf("Expected 'my_domain' but got '%s'", tr)
|
||||
}
|
||||
|
||||
// Test syntax error parsed translations
|
||||
tr = l.Get("This one has invalid syntax translations")
|
||||
if tr != "This one has invalid syntax translations" {
|
||||
t.Errorf("Expected 'This one has invalid syntax translations' but got '%s'", tr)
|
||||
}
|
||||
|
||||
tr = l.GetN("This one has invalid syntax translations", "This are tests", 1)
|
||||
if tr != "This are tests" {
|
||||
t.Errorf("Expected 'Plural index' but got '%s'", tr)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Create Locale with full language code
|
||||
l = NewLocale("fixtures/", "fr_FR")
|
||||
|
||||
// Force nil domain storage
|
||||
l.Domains = nil
|
||||
|
||||
// Add domain
|
||||
l.SetDomain("default")
|
||||
|
||||
// Test non-existent "default" domain responses
|
||||
tr = l.GetDomain()
|
||||
if tr != "default" {
|
||||
t.Errorf("Expected 'my_domain' but got '%s'", tr)
|
||||
}
|
||||
|
||||
// Test syntax error parsed translations
|
||||
tr = l.Get("This one has invalid syntax translations")
|
||||
if tr != "This one has invalid syntax translations" {
|
||||
t.Errorf("Expected 'This one has invalid syntax translations' but got '%s'", tr)
|
||||
}
|
||||
|
||||
tr = l.GetN("This one has invalid syntax translations", "This are tests", 1)
|
||||
if tr != "This are tests" {
|
||||
t.Errorf("Expected 'Plural index' but got '%s'", tr)
|
||||
}
|
||||
|
||||
// Create Locale with full language code
|
||||
l = NewLocale("fixtures/", "de_DE")
|
||||
|
||||
// Force nil domain storage
|
||||
l.Domains = nil
|
||||
|
||||
// Add domain
|
||||
l.SetDomain("default")
|
||||
|
||||
// Test non-existent "default" domain responses
|
||||
tr = l.GetDomain()
|
||||
if tr != "default" {
|
||||
t.Errorf("Expected 'my_domain' but got '%s'", tr)
|
||||
}
|
||||
|
||||
// Test syntax error parsed translations
|
||||
tr = l.Get("This one has invalid syntax translations")
|
||||
if tr != "This one has invalid syntax translations" {
|
||||
t.Errorf("Expected 'This one has invalid syntax translations' but got '%s'", tr)
|
||||
}
|
||||
|
||||
tr = l.GetN("This one has invalid syntax translations", "This are tests", 1)
|
||||
if tr != "This are tests" {
|
||||
t.Errorf("Expected 'Plural index' but got '%s'", tr)
|
||||
}
|
||||
|
||||
|
||||
// Create Locale with full language code
|
||||
l = NewLocale("fixtures/", "de_AT")
|
||||
|
||||
// Force nil domain storage
|
||||
l.Domains = nil
|
||||
|
||||
// Add domain
|
||||
l.SetDomain("default")
|
||||
|
||||
// Test non-existent "default" domain responses
|
||||
tr = l.GetDomain()
|
||||
if tr != "default" {
|
||||
t.Errorf("Expected 'my_domain' but got '%s'", tr)
|
||||
}
|
||||
|
||||
// Test syntax error parsed translations
|
||||
tr = l.Get("This one has invalid syntax translations")
|
||||
if tr != "This one has invalid syntax translations" {
|
||||
t.Errorf("Expected 'This one has invalid syntax translations' but got '%s'", tr)
|
||||
}
|
||||
|
||||
// Test syntax error parsed translations
|
||||
tr = l.GetNDC("mega", "This one has invalid syntax translations","plural",2,"ctx")
|
||||
if tr != "plural" {
|
||||
t.Errorf("Expected 'plural' but got '%s'", tr)
|
||||
}
|
||||
|
||||
tr = l.GetN("This one has invalid syntax translations", "This are tests", 1)
|
||||
if tr != "This are tests" {
|
||||
t.Errorf("Expected 'Plural index' but got '%s'", tr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLocaleRace(t *testing.T) {
|
||||
|
|
421
mo.go
Normal file
421
mo.go
Normal file
|
@ -0,0 +1,421 @@
|
|||
/*
|
||||
* Copyright (c) 2018 DeineAgentur UG https://www.deineagentur.com. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
*/
|
||||
|
||||
package gotext
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io/ioutil"
|
||||
"net/textproto"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/leonelquinteros/gotext/plurals"
|
||||
)
|
||||
|
||||
const (
|
||||
MoMagicLittleEndian = 0x950412de
|
||||
MoMagicBigEndian = 0xde120495
|
||||
|
||||
EotSeparator = "\x04" // msgctxt and msgid separator
|
||||
NulSeparator = "\x00" // msgid and msgstr separator
|
||||
)
|
||||
/*
|
||||
Mo parses the content of any MO file and provides all the Translation functions needed.
|
||||
It's the base object used by all package methods.
|
||||
And it's safe for concurrent use by multiple goroutines by using the sync package for locking.
|
||||
|
||||
Example:
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Create po object
|
||||