Replaced the `i18n` and `toml` dependencies with Fyne's built-in language system for localization management. Updated the `Localizer` implementation to handle translations using JSON files and embed functionality. Simplified language selection and persisted settings via Fyne's preferences API.
195 lines
4.5 KiB
Go
195 lines
4.5 KiB
Go
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
|
|
}
|