gopkg.in/ubuntu-core/snappy.v0@v0.0.0-20210902073436-25a8614f10a6/i18n/i18n.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2014-2015 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package i18n 21 22 //go:generate update-pot 23 24 import ( 25 "fmt" 26 "os" 27 "path/filepath" 28 "strings" 29 30 "github.com/snapcore/go-gettext" 31 32 "github.com/snapcore/snapd/dirs" 33 "github.com/snapcore/snapd/osutil" 34 ) 35 36 // TEXTDOMAIN is the message domain used by snappy; see dgettext(3) 37 // for more information. 38 var ( 39 TEXTDOMAIN = "snappy" 40 locale gettext.Catalog 41 translations gettext.Translations 42 ) 43 44 func init() { 45 bindTextDomain(TEXTDOMAIN, "/usr/share/locale") 46 setLocale("") 47 } 48 49 func langpackResolver(baseRoot string, locale string, domain string) string { 50 // first check for the real locale (e.g. de_DE) 51 // then try to simplify the locale (e.g. de_DE -> de) 52 locales := []string{locale, strings.SplitN(locale, "_", 2)[0]} 53 for _, locale := range locales { 54 r := filepath.Join(locale, "LC_MESSAGES", fmt.Sprintf("%s.mo", domain)) 55 56 // look into the core snaps first for translations, 57 // then the main system 58 candidateDirs := []string{ 59 filepath.Join(dirs.SnapMountDir, "/core/current/", baseRoot), 60 baseRoot, 61 } 62 for _, root := range candidateDirs { 63 // ubuntu uses /usr/lib/locale-langpack and patches the glibc gettext 64 // implementation 65 langpack := filepath.Join(root, "..", "locale-langpack", r) 66 if osutil.FileExists(langpack) { 67 return langpack 68 } 69 70 regular := filepath.Join(root, r) 71 if osutil.FileExists(regular) { 72 return regular 73 } 74 } 75 } 76 77 return "" 78 } 79 80 func bindTextDomain(domain, dir string) { 81 translations = gettext.NewTranslations(dir, domain, langpackResolver) 82 } 83 84 func setLocale(loc string) { 85 if loc == "" { 86 loc = localeFromEnv() 87 } 88 89 locale = translations.Locale(simplifyLocale(loc)) 90 } 91 92 func simplifyLocale(loc string) string { 93 // de_DE.UTF-8, de_DE@euro all need to get simplified 94 loc = strings.Split(loc, "@")[0] 95 loc = strings.Split(loc, ".")[0] 96 97 return loc 98 } 99 100 func localeFromEnv() string { 101 loc := os.Getenv("LC_MESSAGES") 102 if loc == "" { 103 loc = os.Getenv("LANG") 104 } 105 106 return loc 107 } 108 109 // CurrentLocale returns the current locale without encoding or variants. 110 func CurrentLocale() string { 111 return simplifyLocale(localeFromEnv()) 112 } 113 114 // G is the shorthand for Gettext 115 func G(msgid string) string { 116 return locale.Gettext(msgid) 117 } 118 119 // https://www.gnu.org/software/gettext/manual/html_node/Plural-forms.html 120 // (search for 1000) 121 func ngn(d int) uint32 { 122 const max = 1000000 123 if d < 0 { 124 d = -d 125 } 126 if d > max { 127 return uint32((d % max) + max) 128 } 129 return uint32(d) 130 } 131 132 // NG is the shorthand for NGettext 133 func NG(msgid string, msgidPlural string, n int) string { 134 return locale.NGettext(msgid, msgidPlural, ngn(n)) 135 }