package kernel import ( "embed" "encoding/json" "fyne.io/fyne/v2" "fyne.io/fyne/v2/lang" "golang.org/x/text/language" ) //go:embed translations var translations embed.FS var supportedLanguages = map[string]Lang{ "ru": {Code: "ru", Title: "Русский"}, "kk": {Code: "kk", Title: "Қазақ Тілі"}, "en": {Code: "en", Title: "English"}, } type LocalizerContract interface { IsStartWithLanguageSelection() bool GetMessage(key string, data ...any) string GetLanguages() []Lang GetCurrentLanguage() Lang SetCurrentLanguage(selectLang Lang, isSaveSetting bool) error AddChangeCallback(messageID string, callback func(text string)) } type Lang struct { Code string Title string } type changeCallback struct { messageID string callback func(text string) } type Localizer struct { setting SettingLanguageContract currentLang Lang changeCallbacks map[int]*changeCallback isStartWithLanguageSelection bool } func newLocalizer(app fyne.App) (*Localizer, error) { setting := newSettingLanguage(app) currentLanguage, isLanguageNotSupported := setting.GetLang() localizer := &Localizer{ setting: setting, changeCallbacks: map[int]*changeCallback{}, isStartWithLanguageSelection: isLanguageNotSupported, } err := localizer.SetCurrentLanguage(currentLanguage, false) if err != nil { return nil, err } return localizer, nil } func (l *Localizer) IsStartWithLanguageSelection() bool { return l.isStartWithLanguageSelection } func (l *Localizer) GetMessage(key string, data ...any) string { return lang.L(key, data...) } func (l *Localizer) GetLanguages() []Lang { return getLanguages() } func (l *Localizer) GetCurrentLanguage() Lang { return l.currentLang } func (l *Localizer) SetCurrentLanguage(selectLang Lang, isSaveSetting bool) error { l.currentLang = selectLang translationsData, err := l.getTranslations(selectLang) if err != nil { return err } name := lang.SystemLocale().LanguageString() err = lang.AddTranslations(fyne.NewStaticResource(name+".json", translationsData)) if err != nil { return err } if isSaveSetting { l.setting.SetLang(selectLang) } l.eventSetCurrentLanguage() return nil } func (l *Localizer) AddChangeCallback(messageID string, callback func(text string)) { l.changeCallbacks[len(l.changeCallbacks)] = &changeCallback{messageID: messageID, callback: callback} } func (l *Localizer) eventSetCurrentLanguage() { for _, changeCallback := range l.changeCallbacks { text := l.GetMessage(changeCallback.messageID) changeCallback.callback(text) } } func (l *Localizer) getTranslations(language Lang) ([]byte, error) { baseJson, err := translations.ReadFile("translations/base." + language.Code + ".json") if err != nil { return nil, err } appJson, err := translations.ReadFile("translations/app." + language.Code + ".json") if err != nil { return nil, err } return l.mergeTranslations(baseJson, appJson) } func (l *Localizer) mergeTranslations(baseJson []byte, appJson []byte) ([]byte, error) { base := map[string]interface{}{} custom := map[string]interface{}{} err := json.Unmarshal(baseJson, &base) if err != nil { return nil, err } err = json.Unmarshal(appJson, &custom) if err != nil { return nil, err } for k, v := range custom { base[k] = v } return json.Marshal(base) } func getLanguages() []Lang { items := []Lang{} for _, item := range supportedLanguages { items = append(items, item) } return items } type SettingLanguageContract interface { GetLang() (currentLang Lang, isLanguageNotSupported bool) SetLang(language Lang) } type SettingLanguage struct { app fyne.App } func newSettingLanguage(app fyne.App) *SettingLanguage { return &SettingLanguage{ app: app, } } func (s *SettingLanguage) GetLang() (currentLang Lang, isLanguageNotSupported bool) { languageCode := s.app.Preferences().String("language") currentLang = supportedLanguages["ru"] if languageCode == "" { languageTag, err := language.Parse(lang.SystemLocale().LanguageString()) if err != nil { return currentLang, true } base, _ := languageTag.Base() languageCode = base.String() } if findLang, ok := findSupportedLanguage(languageCode); ok { return findLang, false } return currentLang, true } func (s *SettingLanguage) SetLang(language Lang) { s.app.Preferences().SetString("language", language.Code) } func findSupportedLanguage(code string) (Lang, bool) { lang, ok := supportedLanguages[code] return lang, ok }