diff --git a/internal/cli/cli.go b/internal/cli/cli.go index ca4f47bd..fc074717 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -13,7 +13,6 @@ import ( "miniflux.app/v2/internal/config" "miniflux.app/v2/internal/database" - "miniflux.app/v2/internal/locale" "miniflux.app/v2/internal/storage" "miniflux.app/v2/internal/ui/static" "miniflux.app/v2/internal/version" @@ -153,10 +152,6 @@ func Parse() { slog.Info("The default value for DATABASE_URL is used") } - if err := locale.LoadCatalogMessages(); err != nil { - printErrorAndExit(fmt.Errorf("unable to load translations: %v", err)) - } - if err := static.CalculateBinaryFileChecksums(); err != nil { printErrorAndExit(fmt.Errorf("unable to calculate binary file checksums: %v", err)) } diff --git a/internal/locale/catalog.go b/internal/locale/catalog.go index 61f5f27d..8ecdab74 100644 --- a/internal/locale/catalog.go +++ b/internal/locale/catalog.go @@ -12,17 +12,26 @@ import ( type translationDict map[string]interface{} type catalog map[string]translationDict -var defaultCatalog catalog +var defaultCatalog = make(catalog, len(AvailableLanguages)) //go:embed translations/*.json var translationFiles embed.FS +func GetTranslationDict(language string) (translationDict, error) { + if _, ok := defaultCatalog[language]; !ok { + var err error + if defaultCatalog[language], err = loadTranslationFile(language); err != nil { + return nil, err + } + } + return defaultCatalog[language], nil +} + // LoadCatalogMessages loads and parses all translations encoded in JSON. func LoadCatalogMessages() error { var err error - defaultCatalog = make(catalog, len(AvailableLanguages())) - for language := range AvailableLanguages() { + for language := range AvailableLanguages { defaultCatalog[language], err = loadTranslationFile(language) if err != nil { return err diff --git a/internal/locale/catalog_test.go b/internal/locale/catalog_test.go index 75537911..687b1de2 100644 --- a/internal/locale/catalog_test.go +++ b/internal/locale/catalog_test.go @@ -39,7 +39,7 @@ func TestLoadCatalog(t *testing.T) { } func TestAllKeysHaveValue(t *testing.T) { - for language := range AvailableLanguages() { + for language := range AvailableLanguages { messages, err := loadTranslationFile(language) if err != nil { t.Fatalf(`Unable to load translation messages for language %q`, language) @@ -71,7 +71,7 @@ func TestMissingTranslations(t *testing.T) { t.Fatal(`Unable to parse reference language`) } - for language := range AvailableLanguages() { + for language := range AvailableLanguages { if language == refLang { continue } @@ -110,7 +110,7 @@ func TestTranslationFilePluralForms(t *testing.T) { "uk_UA": 3, "id_ID": 1, } - for language := range AvailableLanguages() { + for language := range AvailableLanguages { messages, err := loadTranslationFile(language) if err != nil { t.Fatalf(`Unable to load translation messages for language %q`, language) diff --git a/internal/locale/locale.go b/internal/locale/locale.go index a5a1010b..aa6165b8 100644 --- a/internal/locale/locale.go +++ b/internal/locale/locale.go @@ -3,26 +3,24 @@ package locale // import "miniflux.app/v2/internal/locale" -// AvailableLanguages returns the list of available languages. -func AvailableLanguages() map[string]string { - return map[string]string{ - "en_US": "English", - "es_ES": "Español", - "fr_FR": "Français", - "de_DE": "Deutsch", - "pl_PL": "Polski", - "pt_BR": "Português Brasileiro", - "zh_CN": "简体中文", - "zh_TW": "繁體中文", - "nl_NL": "Nederlands", - "ru_RU": "Русский", - "it_IT": "Italiano", - "ja_JP": "日本語", - "tr_TR": "Türkçe", - "el_EL": "Ελληνικά", - "fi_FI": "Suomi", - "hi_IN": "हिन्दी", - "uk_UA": "Українська", - "id_ID": "Bahasa Indonesia", - } +// AvailableLanguages is the list of available languages. +var AvailableLanguages = map[string]string{ + "en_US": "English", + "es_ES": "Español", + "fr_FR": "Français", + "de_DE": "Deutsch", + "pl_PL": "Polski", + "pt_BR": "Português Brasileiro", + "zh_CN": "简体中文", + "zh_TW": "繁體中文", + "nl_NL": "Nederlands", + "ru_RU": "Русский", + "it_IT": "Italiano", + "ja_JP": "日本語", + "tr_TR": "Türkçe", + "el_EL": "Ελληνικά", + "fi_FI": "Suomi", + "hi_IN": "हिन्दी", + "uk_UA": "Українська", + "id_ID": "Bahasa Indonesia", } diff --git a/internal/locale/locale_test.go b/internal/locale/locale_test.go index 86b52820..32f6a40f 100644 --- a/internal/locale/locale_test.go +++ b/internal/locale/locale_test.go @@ -6,7 +6,7 @@ package locale // import "miniflux.app/v2/internal/locale" import "testing" func TestAvailableLanguages(t *testing.T) { - results := AvailableLanguages() + results := AvailableLanguages for k, v := range results { if k == "" { t.Errorf(`Empty language key detected`) diff --git a/internal/locale/printer.go b/internal/locale/printer.go index f85960fa..d997c1a7 100644 --- a/internal/locale/printer.go +++ b/internal/locale/printer.go @@ -11,9 +11,11 @@ type Printer struct { } func (p *Printer) Print(key string) string { - if str, ok := defaultCatalog[p.language][key]; ok { - if translation, ok := str.(string); ok { - return translation + if dict, err := GetTranslationDict(p.language); err == nil { + if str, ok := dict[key]; ok { + if translation, ok := str.(string); ok { + return translation + } } } return key @@ -21,16 +23,16 @@ func (p *Printer) Print(key string) string { // Printf is like fmt.Printf, but using language-specific formatting. func (p *Printer) Printf(key string, args ...interface{}) string { - var translation string + translation := key - str, found := defaultCatalog[p.language][key] - if !found { - translation = key - } else { - var valid bool - translation, valid = str.(string) - if !valid { - translation = key + if dict, err := GetTranslationDict(p.language); err == nil { + str, found := dict[key] + if found { + var valid bool + translation, valid = str.(string) + if !valid { + translation = key + } } } @@ -39,9 +41,12 @@ func (p *Printer) Printf(key string, args ...interface{}) string { // Plural returns the translation of the given key by using the language plural form. func (p *Printer) Plural(key string, n int, args ...interface{}) string { - choices, found := defaultCatalog[p.language][key] + dict, err := GetTranslationDict(p.language) + if err != nil { + return key + } - if found { + if choices, found := dict[key]; found { var plurals []string switch v := choices.(type) { diff --git a/internal/ui/settings_show.go b/internal/ui/settings_show.go index 179b9802..eae72a7f 100644 --- a/internal/ui/settings_show.go +++ b/internal/ui/settings_show.go @@ -71,7 +71,7 @@ func (h *handler) showSettingsPage(w http.ResponseWriter, r *http.Request) { "MarkAsReadOnlyOnPlayerCompletion": form.MarkAsReadOnlyOnPlayerCompletion, }) view.Set("themes", model.Themes()) - view.Set("languages", locale.AvailableLanguages()) + view.Set("languages", locale.AvailableLanguages) view.Set("timezones", timezones) view.Set("menu", "settings") view.Set("user", user) diff --git a/internal/ui/settings_update.go b/internal/ui/settings_update.go index be99adb5..5610a9a9 100644 --- a/internal/ui/settings_update.go +++ b/internal/ui/settings_update.go @@ -44,7 +44,7 @@ func (h *handler) updateSettings(w http.ResponseWriter, r *http.Request) { view := view.New(h.tpl, r, sess) view.Set("form", settingsForm) view.Set("themes", model.Themes()) - view.Set("languages", locale.AvailableLanguages()) + view.Set("languages", locale.AvailableLanguages) view.Set("timezones", timezones) view.Set("menu", "settings") view.Set("user", loggedUser) diff --git a/internal/validator/user.go b/internal/validator/user.go index 2e79785b..b461f912 100644 --- a/internal/validator/user.go +++ b/internal/validator/user.go @@ -155,7 +155,7 @@ func validateTheme(theme string) *locale.LocalizedError { } func validateLanguage(language string) *locale.LocalizedError { - languages := locale.AvailableLanguages() + languages := locale.AvailableLanguages if _, found := languages[language]; !found { return locale.NewLocalizedError("error.invalid_language") }