github.com/cozy/cozy-stack@v0.0.0-20240603063001-31110fa4cae1/pkg/i18n/i18n.go (about) 1 package i18n 2 3 import ( 4 "fmt" 5 "html/template" 6 "regexp" 7 "strings" 8 "time" 9 10 "github.com/cozy/cozy-stack/pkg/consts" 11 "github.com/cozy/cozy-stack/pkg/logger" 12 "github.com/goodsign/monday" 13 "github.com/leonelquinteros/gotext" 14 ) 15 16 var translations = make(map[string]*gotext.Po) 17 18 // LoadLocale creates the translation object for a locale from the content of a .po file 19 func LoadLocale(locale, contextName string, rawPO []byte) { 20 po := gotext.NewPo() 21 po.Parse(rawPO) 22 identifier := locale 23 if contextName != "" { 24 identifier = contextName + "/" + locale 25 } 26 translations[identifier] = po 27 } 28 29 // Translator returns a translation function of the locale specified 30 func Translator(locale, contextName string) func(key string, vars ...interface{}) string { 31 return func(key string, vars ...interface{}) string { 32 return Translate(key, locale, contextName, vars...) 33 } 34 } 35 36 var boldRegexp = regexp.MustCompile(`\*\*(.*?)\*\*`) 37 var newlineRegexp = regexp.MustCompile(`(\n)`) 38 39 // TranslatorHTML returns a translation function of the locale specified, which 40 // allow simple markup like **bold**. 41 func TranslatorHTML(locale, contextName string) func(key string, vars ...interface{}) template.HTML { 42 return func(key string, vars ...interface{}) template.HTML { 43 translated := Translate(key, locale, contextName, vars...) 44 escaped := template.HTMLEscapeString(translated) 45 replaced := boldRegexp.ReplaceAllString(escaped, "<strong>$1</strong>") 46 replaced = newlineRegexp.ReplaceAllString(replaced, "<br />") 47 return template.HTML(replaced) 48 } 49 } 50 51 // Translate translates the given key on the specified locale. 52 func Translate(key, locale, contextName string, vars ...interface{}) string { 53 if po, ok := translations[contextName+"/"+locale]; ok { 54 translated := po.Get(key) 55 if translated != key && translated != "" { 56 if len(vars) > 0 { 57 return fmt.Sprintf(translated, vars...) 58 } 59 return translated 60 } 61 } 62 if po, ok := translations[locale]; ok { 63 translated := po.Get(key) 64 if translated != key && translated != "" { 65 if len(vars) > 0 { 66 return fmt.Sprintf(translated, vars...) 67 } 68 return translated 69 } 70 } 71 if po, ok := translations[consts.DefaultLocale]; ok { 72 translated := po.Get(key, vars...) 73 if translated != key && translated != "" { 74 return translated 75 } 76 } 77 logger.WithNamespace("i18n"). 78 Infof("Translation not found for key %q on locale %q", key, locale) 79 if strings.HasPrefix(key, " Permissions ") { 80 key = strings.Replace(key, "Permissions ", "", 1) 81 } 82 return fmt.Sprintf(key, vars...) 83 } 84 85 // LocalizeTime transforms a date+time in a string for the given locale. 86 // The layout is in the same format as the one given to time.Format. 87 func LocalizeTime(t time.Time, locale, layout string) string { 88 return monday.Format(t, layout, mondayLocale(locale)) 89 } 90 91 func mondayLocale(locale string) monday.Locale { 92 switch locale { 93 case "de", "de_DE": 94 return monday.LocaleDeDE 95 case "es", "es_ES": 96 return monday.LocaleEsES 97 case "fr", "fr_FR": 98 return monday.LocaleFrFR 99 case "it", "it_IT": 100 return monday.LocaleItIT 101 case "ja", "ja_JP": 102 return monday.LocaleJaJP 103 case "nl", "nl_NL": 104 return monday.LocaleNlNL 105 case "pt", "pt_PT": 106 return monday.LocalePtPT 107 case "ru", "ru_RU": 108 return monday.LocaleRuRU 109 default: 110 return monday.LocaleEnUS 111 } 112 }