package i18n
-//go:generate update-pot
-
import (
- "fmt"
"os"
- "path/filepath"
"strings"
-
- "github.com/snapcore/go-gettext"
-
- "github.com/snapcore/snapd/dirs"
- "github.com/snapcore/snapd/osutil"
)
-// TEXTDOMAIN is the message domain used by snappy; see dgettext(3)
-// for more information.
-var (
- TEXTDOMAIN = "snappy"
- locale gettext.Catalog
- translations gettext.Translations
-)
-
-func init() {
- bindTextDomain(TEXTDOMAIN, "/usr/share/locale")
- setLocale("")
-}
-
-func langpackResolver(baseRoot string, locale string, domain string) string {
- // first check for the real locale (e.g. de_DE)
- // then try to simplify the locale (e.g. de_DE -> de)
- locales := []string{locale, strings.SplitN(locale, "_", 2)[0]}
- for _, locale := range locales {
- r := filepath.Join(locale, "LC_MESSAGES", fmt.Sprintf("%s.mo", domain))
-
- // look into the core snaps first for translations,
- // then the main system
- candidateDirs := []string{
- filepath.Join(dirs.SnapMountDir, "/core/current/", baseRoot),
- baseRoot,
- }
- for _, root := range candidateDirs {
- // ubuntu uses /usr/lib/locale-langpack and patches the glibc gettext
- // implementation
- langpack := filepath.Join(root, "..", "locale-langpack", r)
- if osutil.FileExists(langpack) {
- return langpack
- }
-
- regular := filepath.Join(root, r)
- if osutil.FileExists(regular) {
- return regular
- }
- }
- }
-
- return ""
-}
-
-func bindTextDomain(domain, dir string) {
- translations = gettext.NewTranslations(dir, domain, langpackResolver)
-}
-
-func setLocale(loc string) {
- if loc == "" {
- loc = localeFromEnv()
- }
-
- locale = translations.Locale(simplifyLocale(loc))
-}
-
func simplifyLocale(loc string) string {
// de_DE.UTF-8, de_DE@euro all need to get simplified
loc = strings.Split(loc, "@")[0]
return loc
}
-// CurrentLocale returns the current locale without encoding or variants.
func CurrentLocale() string {
return simplifyLocale(localeFromEnv())
}
// G is the shorthand for Gettext
func G(msgid string) string {
- return locale.Gettext(msgid)
-}
-
-// https://www.gnu.org/software/gettext/manual/html_node/Plural-forms.html
-// (search for 1000)
-func ngn(d int) uint32 {
- const max = 1000000
- if d < 0 {
- d = -d
- }
- if d > max {
- return uint32((d % max) + max)
- }
- return uint32(d)
+ return msgid
}
// NG is the shorthand for NGettext
func NG(msgid string, msgidPlural string, n int) string {
- return locale.NGettext(msgid, msgidPlural, ngn(n))
+ if n == 1 {
+ return msgid
+ } else {
+ return msgidPlural
+ }
}
package i18n
import (
- "os"
- "os/exec"
- "path/filepath"
"testing"
. "gopkg.in/check.v1"
-
- "github.com/snapcore/snapd/dirs"
)
// Hook up check.v1 into the "go test" runner
func Test(t *testing.T) { TestingT(t) }
-var mockLocalePo = []byte(`
-msgid ""
-msgstr ""
-"Project-Id-Version: snappy-test\n"
-"Report-Msgid-Bugs-To: snappy-devel@lists.ubuntu.com\n"
-"POT-Creation-Date: 2015-06-16 09:08+0200\n"
-"Language: en_DK\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=n != 1;>\n"
-
-msgid "plural_1"
-msgid_plural "plural_2"
-msgstr[0] "translated plural_1"
-msgstr[1] "translated plural_2"
-
-msgid "singular"
-msgstr "translated singular"
-`)
-
-func makeMockTranslations(c *C, localeDir string) {
- fullLocaleDir := filepath.Join(localeDir, "en_DK", "LC_MESSAGES")
- err := os.MkdirAll(fullLocaleDir, 0755)
- c.Assert(err, IsNil)
-
- po := filepath.Join(fullLocaleDir, "snappy-test.po")
- mo := filepath.Join(fullLocaleDir, "snappy-test.mo")
- err = os.WriteFile(po, mockLocalePo, 0644)
- c.Assert(err, IsNil)
-
- cmd := exec.Command("msgfmt", po, "--output-file", mo)
- cmd.Stdout = os.Stdout
- cmd.Stderr = os.Stderr
- err = cmd.Run()
- c.Assert(err, IsNil)
-}
-
-type i18nTestSuite struct {
- origLang string
- origLcMessages string
-}
+type i18nTestSuite struct{}
var _ = Suite(&i18nTestSuite{})
-func (s *i18nTestSuite) SetUpTest(c *C) {
- // this dir contains a special hand-crafted en_DK/snappy-test.mo
- // file
- localeDir := c.MkDir()
- makeMockTranslations(c, localeDir)
-
- // we use a custom test mo file
- TEXTDOMAIN = "snappy-test"
-
- s.origLang = os.Getenv("LANG")
- s.origLcMessages = os.Getenv("LC_MESSAGES")
-
- bindTextDomain("snappy-test", localeDir)
- os.Setenv("LANG", "en_DK.UTF-8")
- setLocale("")
-}
-
-func (s *i18nTestSuite) TearDownTest(c *C) {
- os.Setenv("LANG", s.origLang)
- os.Setenv("LC_MESSAGES", s.origLcMessages)
-}
-
func (s *i18nTestSuite) TestTranslatedSingular(c *C) {
// no G() to avoid adding the test string to snappy-pot
var Gtest = G
- c.Assert(Gtest("singular"), Equals, "translated singular")
+ c.Assert(Gtest("singular"), Equals, "singular")
}
func (s *i18nTestSuite) TestTranslatesPlural(c *C) {
// no NG() to avoid adding the test string to snappy-pot
var NGtest = NG
- c.Assert(NGtest("plural_1", "plural_2", 1), Equals, "translated plural_1")
-}
-
-func (s *i18nTestSuite) TestTranslatedMissingLangNoCrash(c *C) {
- setLocale("invalid")
-
- // no G() to avoid adding the test string to snappy-pot
- var Gtest = G
- c.Assert(Gtest("singular"), Equals, "singular")
-}
-
-func (s *i18nTestSuite) TestInvalidTextDomainDir(c *C) {
- bindTextDomain("snappy-test", "/random/not/existing/dir")
- setLocale("invalid")
-
- // no G() to avoid adding the test string to snappy-pot
- var Gtest = G
- c.Assert(Gtest("singular"), Equals, "singular")
-}
-
-func (s *i18nTestSuite) TestLangpackResolverFromLangpack(c *C) {
- root := c.MkDir()
- localeDir := filepath.Join(root, "/usr/share/locale")
- err := os.MkdirAll(localeDir, 0755)
- c.Assert(err, IsNil)
-
- d := filepath.Join(root, "/usr/share/locale-langpack")
- makeMockTranslations(c, d)
- bindTextDomain("snappy-test", localeDir)
- setLocale("")
-
- // no G() to avoid adding the test string to snappy-pot
- var Gtest = G
- c.Assert(Gtest("singular"), Equals, "translated singular", Commentf("test with %q failed", d))
-}
-
-func (s *i18nTestSuite) TestLangpackResolverFromCore(c *C) {
- origSnapMountDir := dirs.SnapMountDir
- defer func() { dirs.SnapMountDir = origSnapMountDir }()
- dirs.SnapMountDir = c.MkDir()
-
- d := filepath.Join(dirs.SnapMountDir, "/core/current/usr/share/locale")
- makeMockTranslations(c, d)
- bindTextDomain("snappy-test", "/usr/share/locale")
- setLocale("")
-
- // no G() to avoid adding the test string to snappy-pot
- var Gtest = G
- c.Assert(Gtest("singular"), Equals, "translated singular", Commentf("test with %q failed", d))
+ c.Assert(NGtest("plural_1", "plural_2", 0), Equals, "plural_2")
+ c.Assert(NGtest("plural_1", "plural_2", 1), Equals, "plural_1")
+ c.Assert(NGtest("plural_1", "plural_2", 2), Equals, "plural_2")
}