Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c583d0991b | ||
|
|
4d0fbfd720 | ||
|
|
d0a0759bca | ||
|
|
972f5685e7 | ||
|
|
36b0166de9 | ||
|
|
b44b8cf3f4 | ||
|
|
3f452f14f2 |
32
.github/ISSUE_TEMPLATE.md
vendored
Normal file
32
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# Please describe your issue
|
||||||
|
|
||||||
|
## Is this a bug, an improvement, a proposal or something else?
|
||||||
|
|
||||||
|
- [ ] Bug
|
||||||
|
- [ ] Improvement
|
||||||
|
- [ ] Proposal
|
||||||
|
- [ ] Something else
|
||||||
|
|
||||||
|
|
||||||
|
## Briefly explain your issue
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
## What's the expected behaviour?
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
## What's the actual behaviour?
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
## What are the steps to reproduce the actual behaviour?
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
## Comments
|
||||||
|
|
||||||
31
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
31
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# Before creating your Pull Request...
|
||||||
|
|
||||||
|
- New Pull Requests should include a good description of what's being merged.
|
||||||
|
- Ideally, all Pull Requests are preceded by a discussion initiated in an Issue on this repository.
|
||||||
|
- For bug fixes is mandatory to have tests that cover and fail when the bug is present and will pass after this Pull Request.
|
||||||
|
- For changes and improvements, new tests have to be provided to cover the new features.
|
||||||
|
|
||||||
|
|
||||||
|
## What does this change implement/fix?
|
||||||
|
|
||||||
|
... *answer here*
|
||||||
|
|
||||||
|
## Is this a fix or an improvement?
|
||||||
|
|
||||||
|
- [ ] Fix
|
||||||
|
- [ ] Improvement
|
||||||
|
|
||||||
|
## Have discussed this change in an issue?
|
||||||
|
|
||||||
|
- [ ] Yes
|
||||||
|
- [ ] No
|
||||||
|
|
||||||
|
## Was some test failing because of this issue or change needed?
|
||||||
|
|
||||||
|
- [ ] Yes
|
||||||
|
- [ ] No
|
||||||
|
|
||||||
|
## Are you including tests to cover this change?
|
||||||
|
|
||||||
|
- [ ] Yes
|
||||||
|
- [ ] No
|
||||||
46
CODE_OF_CONDUCT.md
Normal file
46
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to creating a positive environment include:
|
||||||
|
|
||||||
|
* Using welcoming and inclusive language
|
||||||
|
* Being respectful of differing viewpoints and experiences
|
||||||
|
* Gracefully accepting constructive criticism
|
||||||
|
* Focusing on what is best for the community
|
||||||
|
* Showing empathy towards other community members
|
||||||
|
|
||||||
|
Examples of unacceptable behavior by participants include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||||
|
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||||
|
|
||||||
|
## Our Responsibilities
|
||||||
|
|
||||||
|
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||||
|
|
||||||
|
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at leonel.quinteros@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||||
|
|
||||||
|
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
||||||
|
|
||||||
|
[homepage]: http://contributor-covenant.org
|
||||||
|
[version]: http://contributor-covenant.org/version/1/4/
|
||||||
19
CONTRIBUTING.md
Normal file
19
CONTRIBUTING.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# CONTRIBUTING
|
||||||
|
|
||||||
|
This open source project welcomes everybody that wants to contribute to it by implementing new features, fixing bugs, testing, creating documentation or simply talk about it.
|
||||||
|
|
||||||
|
Most contributions will start by creating a new Issue to discuss what is the contribution about and to agree on the steps to move forward.
|
||||||
|
|
||||||
|
## Issues
|
||||||
|
|
||||||
|
All issues reports are welcome. Open a new Issue whenever you want to report a bug, request a change or make a proposal.
|
||||||
|
|
||||||
|
This should be your start point of contribution.
|
||||||
|
|
||||||
|
|
||||||
|
## Pull Requests
|
||||||
|
|
||||||
|
If you have any changes that can be merged, feel free to send a Pull Request.
|
||||||
|
|
||||||
|
Usually, you'd want to create a new Issue to discuss about the change you want to merge and why it's needed or what it solves.
|
||||||
|
|
||||||
108
gotext.go
108
gotext.go
@@ -22,68 +22,110 @@ For quick/simple translations you can use the package level functions directly.
|
|||||||
*/
|
*/
|
||||||
package gotext
|
package gotext
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
// Global environment variables
|
// Global environment variables
|
||||||
var (
|
type config struct {
|
||||||
|
sync.RWMutex
|
||||||
|
|
||||||
// Default domain to look at when no domain is specified. Used by package level functions.
|
// Default domain to look at when no domain is specified. Used by package level functions.
|
||||||
domain = "default"
|
domain string
|
||||||
|
|
||||||
// Language set.
|
// Language set.
|
||||||
language = "en_US"
|
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 = "/usr/local/share/locale"
|
library string
|
||||||
|
|
||||||
// Storage for package level methods
|
// Storage for package level methods
|
||||||
storage *Locale
|
storage *Locale
|
||||||
)
|
}
|
||||||
|
|
||||||
|
var globalConfig *config
|
||||||
|
|
||||||
|
// Init default configuration
|
||||||
|
func init() {
|
||||||
|
globalConfig = &config{
|
||||||
|
domain: "default",
|
||||||
|
language: "en_US",
|
||||||
|
library: "/usr/local/share/locale",
|
||||||
|
storage: nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// loadStorage creates a new Locale object at package level based on the Global variables settings.
|
// loadStorage creates a new Locale object at package level based on the Global variables settings.
|
||||||
// It's called automatically when trying to use Get or GetD methods.
|
// It's called automatically when trying to use Get or GetD methods.
|
||||||
func loadStorage(force bool) {
|
func loadStorage(force bool) {
|
||||||
if storage == nil || force {
|
globalConfig.Lock()
|
||||||
storage = NewLocale(library, language)
|
|
||||||
|
if globalConfig.storage == nil || force {
|
||||||
|
globalConfig.storage = NewLocale(globalConfig.library, globalConfig.language)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := storage.domains[domain]; !ok || force {
|
if _, ok := globalConfig.storage.domains[globalConfig.domain]; !ok || force {
|
||||||
storage.AddDomain(domain)
|
globalConfig.storage.AddDomain(globalConfig.domain)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
globalConfig.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDomain is the domain getter for the package configuration
|
// GetDomain is the domain getter for the package configuration
|
||||||
func GetDomain() string {
|
func GetDomain() string {
|
||||||
return domain
|
globalConfig.RLock()
|
||||||
|
dom := globalConfig.domain
|
||||||
|
globalConfig.RUnlock()
|
||||||
|
|
||||||
|
return dom
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetDomain sets the name for the domain to be used at package level.
|
// 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) {
|
func SetDomain(dom string) {
|
||||||
domain = dom
|
globalConfig.Lock()
|
||||||
|
globalConfig.domain = dom
|
||||||
|
globalConfig.Unlock()
|
||||||
|
|
||||||
loadStorage(true)
|
loadStorage(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLanguage is the language getter for the package configuration
|
// GetLanguage is the language getter for the package configuration
|
||||||
func GetLanguage() string {
|
func GetLanguage() string {
|
||||||
return language
|
globalConfig.RLock()
|
||||||
|
lang := globalConfig.language
|
||||||
|
globalConfig.RUnlock()
|
||||||
|
|
||||||
|
return lang
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetLanguage sets the language code to be used at package level.
|
// 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) {
|
func SetLanguage(lang string) {
|
||||||
language = lang
|
globalConfig.Lock()
|
||||||
|
globalConfig.language = lang
|
||||||
|
globalConfig.Unlock()
|
||||||
|
|
||||||
loadStorage(true)
|
loadStorage(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLibrary is the library getter for the package configuration
|
// GetLibrary is the library getter for the package configuration
|
||||||
func GetLibrary() string {
|
func GetLibrary() string {
|
||||||
return library
|
globalConfig.RLock()
|
||||||
|
lib := globalConfig.library
|
||||||
|
globalConfig.RUnlock()
|
||||||
|
|
||||||
|
return lib
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetLibrary sets the root path for the loale directories and files to be used at package level.
|
// 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) {
|
func SetLibrary(lib string) {
|
||||||
library = lib
|
globalConfig.Lock()
|
||||||
|
globalConfig.library = lib
|
||||||
|
globalConfig.Unlock()
|
||||||
|
|
||||||
loadStorage(true)
|
loadStorage(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,9 +134,13 @@ func SetLibrary(lib string) {
|
|||||||
// This function is recommended to be used when changing more than one setting,
|
// 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) {
|
func Configure(lib, lang, dom string) {
|
||||||
library = lib
|
globalConfig.Lock()
|
||||||
language = lang
|
|
||||||
domain = dom
|
globalConfig.library = lib
|
||||||
|
globalConfig.language = lang
|
||||||
|
globalConfig.domain = dom
|
||||||
|
|
||||||
|
globalConfig.Unlock()
|
||||||
|
|
||||||
loadStorage(true)
|
loadStorage(true)
|
||||||
}
|
}
|
||||||
@@ -102,13 +148,13 @@ func Configure(lib, lang, dom string) {
|
|||||||
// 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.
|
// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax.
|
||||||
func Get(str string, vars ...interface{}) string {
|
func Get(str string, vars ...interface{}) string {
|
||||||
return GetD(domain, str, vars...)
|
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.
|
// 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 {
|
func GetN(str, plural string, n int, vars ...interface{}) string {
|
||||||
return GetND("default", str, plural, n, vars...)
|
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.
|
||||||
@@ -124,19 +170,23 @@ func GetND(dom, str, plural string, n int, vars ...interface{}) string {
|
|||||||
loadStorage(false)
|
loadStorage(false)
|
||||||
|
|
||||||
// Return translation
|
// Return translation
|
||||||
return storage.GetND(dom, str, plural, n, vars...)
|
globalConfig.RLock()
|
||||||
|
tr := globalConfig.storage.GetND(dom, str, plural, n, vars...)
|
||||||
|
globalConfig.RUnlock()
|
||||||
|
|
||||||
|
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.
|
// 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 {
|
func GetC(str, ctx string, vars ...interface{}) string {
|
||||||
return GetDC(domain, str, ctx, vars...)
|
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.
|
// 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 {
|
func GetNC(str, plural string, n int, ctx string, vars ...interface{}) string {
|
||||||
return GetNDC("default", str, plural, n, ctx, vars...)
|
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.
|
||||||
@@ -152,7 +202,11 @@ func GetNDC(dom, str, plural string, n int, ctx string, vars ...interface{}) str
|
|||||||
loadStorage(false)
|
loadStorage(false)
|
||||||
|
|
||||||
// Return translation
|
// Return translation
|
||||||
return storage.GetNDC(dom, str, plural, n, ctx, vars...)
|
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.
|
// printf applies text formatting only when needed to parse variables.
|
||||||
|
|||||||
140
gotext_test.go
140
gotext_test.go
@@ -82,14 +82,14 @@ msgstr[1] ""
|
|||||||
`
|
`
|
||||||
|
|
||||||
// Create Locales directory on default location
|
// Create Locales directory on default location
|
||||||
dirname := path.Clean("/tmp" + string(os.PathSeparator) + "en_US")
|
dirname := path.Join("/tmp", "en_US")
|
||||||
err := os.MkdirAll(dirname, os.ModePerm)
|
err := os.MkdirAll(dirname, os.ModePerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Can't create test directory: %s", err.Error())
|
t.Fatalf("Can't create test directory: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write PO content to default domain file
|
// Write PO content to default domain file
|
||||||
filename := path.Clean(dirname + string(os.PathSeparator) + "default.po")
|
filename := path.Join(dirname, "default.po")
|
||||||
|
|
||||||
f, err := os.Create(filename)
|
f, err := os.Create(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -161,14 +161,14 @@ msgstr[1] ""
|
|||||||
`
|
`
|
||||||
|
|
||||||
// Create Locales directory on default location
|
// Create Locales directory on default location
|
||||||
dirname := path.Clean("/tmp" + string(os.PathSeparator) + "en_US")
|
dirname := path.Join("/tmp", "en_US")
|
||||||
err := os.MkdirAll(dirname, os.ModePerm)
|
err := os.MkdirAll(dirname, os.ModePerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Can't create test directory: %s", err.Error())
|
t.Fatalf("Can't create test directory: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write PO content to default domain file
|
// Write PO content to default domain file
|
||||||
filename := path.Clean(dirname + string(os.PathSeparator) + "default.po")
|
filename := path.Join(dirname, "default.po")
|
||||||
|
|
||||||
f, err := os.Create(filename)
|
f, err := os.Create(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -214,6 +214,108 @@ msgstr[1] ""
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDomains(t *testing.T) {
|
||||||
|
// Set PO content
|
||||||
|
strDefault := `
|
||||||
|
msgid ""
|
||||||
|
msgstr "Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
|
||||||
|
msgid "Default text"
|
||||||
|
msgid_plural "Default texts"
|
||||||
|
msgstr[0] "Default translation"
|
||||||
|
msgstr[1] "Default translations"
|
||||||
|
|
||||||
|
msgctxt "Ctx"
|
||||||
|
msgid "Default context"
|
||||||
|
msgid_plural "Default contexts"
|
||||||
|
msgstr[0] "Default ctx translation"
|
||||||
|
msgstr[1] "Default ctx translations"
|
||||||
|
`
|
||||||
|
|
||||||
|
strCustom := `
|
||||||
|
msgid ""
|
||||||
|
msgstr "Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
|
||||||
|
msgid "Custom text"
|
||||||
|
msgid_plural "Custom texts"
|
||||||
|
msgstr[0] "Custom translation"
|
||||||
|
msgstr[1] "Custom translations"
|
||||||
|
|
||||||
|
msgctxt "Ctx"
|
||||||
|
msgid "Custom context"
|
||||||
|
msgid_plural "Custom contexts"
|
||||||
|
msgstr[0] "Custom ctx translation"
|
||||||
|
msgstr[1] "Custom ctx translations"
|
||||||
|
`
|
||||||
|
|
||||||
|
// Create Locales directory and files on temp location
|
||||||
|
dirname := path.Join("/tmp", "en_US")
|
||||||
|
err := os.MkdirAll(dirname, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Can't create test directory: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
fDefault, err := os.Create(path.Join(dirname, "default.po"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Can't create test file: %s", err.Error())
|
||||||
|
}
|
||||||
|
defer fDefault.Close()
|
||||||
|
|
||||||
|
fCustom, err := os.Create(path.Join(dirname, "custom.po"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Can't create test file: %s", err.Error())
|
||||||
|
}
|
||||||
|
defer fCustom.Close()
|
||||||
|
|
||||||
|
_, err = fDefault.WriteString(strDefault)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Can't write to test file: %s", err.Error())
|
||||||
|
}
|
||||||
|
_, err = fCustom.WriteString(strCustom)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Can't write to test file: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
Configure("/tmp", "en_US", "default")
|
||||||
|
|
||||||
|
// Check default domain translation
|
||||||
|
SetDomain("default")
|
||||||
|
tr := Get("Default text")
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
tr = GetNC("Default context", "Default contexts", 23, "Ctx")
|
||||||
|
if tr != "Default ctx translations" {
|
||||||
|
t.Errorf("Expected 'Default ctx translations'. Got '%s'", tr)
|
||||||
|
}
|
||||||
|
|
||||||
|
SetDomain("custom")
|
||||||
|
tr = Get("Custom text")
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
tr = GetNC("Custom context", "Custom contexts", 23, "Ctx")
|
||||||
|
if tr != "Custom ctx translations" {
|
||||||
|
t.Errorf("Expected 'Custom ctx translations'. Got '%s'", tr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestPackageRace(t *testing.T) {
|
func TestPackageRace(t *testing.T) {
|
||||||
// Set PO content
|
// Set PO content
|
||||||
str := `# Some comment
|
str := `# Some comment
|
||||||
@@ -230,17 +332,21 @@ msgstr[0] "This one is the singular: %s"
|
|||||||
msgstr[1] "This one is the plural: %s"
|
msgstr[1] "This one is the plural: %s"
|
||||||
msgstr[2] "And this is the second plural form: %s"
|
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"
|
||||||
|
|
||||||
`
|
`
|
||||||
|
|
||||||
// Create Locales directory on default location
|
// Create Locales directory on default location
|
||||||
dirname := path.Clean(library + string(os.PathSeparator) + "en_US")
|
dirname := path.Join("/tmp", "en_US")
|
||||||
err := os.MkdirAll(dirname, os.ModePerm)
|
err := os.MkdirAll(dirname, os.ModePerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Can't create test directory: %s", err.Error())
|
t.Fatalf("Can't create test directory: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write PO content to default domain file
|
// Write PO content to default domain file
|
||||||
filename := path.Clean(dirname + string(os.PathSeparator) + domain + ".po")
|
filename := path.Join("/tmp", GetDomain()+".po")
|
||||||
|
|
||||||
f, err := os.Create(filename)
|
f, err := os.Create(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -255,26 +361,24 @@ msgstr[2] "And this is the second plural form: %s"
|
|||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
for i := 0; i < 100; i++ {
|
for i := 0; i < 1000; i++ {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
// Test translations
|
// Test translations
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
|
GetLibrary()
|
||||||
|
SetLibrary(path.Join("/tmp", "gotextlib"))
|
||||||
|
GetDomain()
|
||||||
|
SetDomain("default")
|
||||||
|
GetLanguage()
|
||||||
|
SetLanguage("en_US")
|
||||||
|
Configure("/tmp", "en_US", "default")
|
||||||
|
|
||||||
Get("My text")
|
Get("My text")
|
||||||
GetN("One with var: %s", "Several with vars: %s", 0, "test")
|
GetN("One with var: %s", "Several with vars: %s", 0, "test")
|
||||||
|
GetC("Some random in a context", "Ctx")
|
||||||
}()
|
}()
|
||||||
|
|
||||||
wg.Add(1)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
|
|
||||||
Get("My text")
|
|
||||||
GetN("One with var: %s", "Several with vars: %s", 1, "test")
|
|
||||||
}()
|
|
||||||
|
|
||||||
Get("My text")
|
|
||||||
GetN("One with var: %s", "Several with vars: %s", 2, "test")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|||||||
Reference in New Issue
Block a user