package locales import ( "git.perx.ru/perxis/perxis-go/pkg/data" "git.perx.ru/perxis/perxis-go/pkg/errors" pb "git.perx.ru/perxis/perxis-go/proto/locales" "golang.org/x/text/language" ) const ( DefaultID = "default" // DefaultID идентификатор локали по умолчанию DefaultDirection = "ltr" // DefaultDirection направление письма по умолчанию ) type Locale struct { ID string `json:"id" bson:"_id"` // Идентификатор локали, генерируется автоматически. Для локали по умолчанию устанавливается как "default". SpaceID string `json:"spaceId" bson:"-"` // Идентификатор пространства. Name string `json:"name" bson:"name"` // Название локали. Опционально, заполняется автоматически (Пример: russian, english) NativeName string `json:"native_name" bson:"native_name"` // Название локали на языке локали. Опционально, заполняется автоматически (Пример: Русский, English) Code string `json:"code" bson:"code"` // Код локали https://en.wikipedia.org/wiki/IETF_language_tag Fallback string `json:"fallback" bson:"fallback"` // Идентификатор локали, который будет использоваться при отсутствии перевода Direction string `json:"direction" bson:"direction"` // Направление письма - слева направо (ltr) или справа налево (rtl). По умолчанию устанавливается ltr. Weight int `json:"weight" bson:"weight"` // Вес локали. NoPublish bool `json:"no_publish" bson:"no_publish"` // Не публиковать контент данной локали. Не будет доступен контент через Delivery API. (кроме default) Disabled bool `json:"disabled" bson:"disabled"` // Запретить использование локали. Нельзя создавать и редактировать контент для данной локали (кроме default) } func (locale *Locale) Clone() *Locale { return &Locale{ ID: locale.ID, SpaceID: locale.SpaceID, Name: locale.Name, NativeName: locale.NativeName, Code: locale.Code, Fallback: locale.Fallback, Direction: locale.Direction, Weight: locale.Weight, NoPublish: locale.NoPublish, Disabled: locale.Disabled, } } func (locale *Locale) IsDefault() bool { return locale.ID == DefaultID } func (locale *Locale) Equal(other *Locale) bool { return locale == other || locale != nil && other != nil && *locale == *other } func (locale *Locale) Key() string { if locale == nil { return "" } return locale.ID } // Возвращает язык локали, например "en", "ru" func (locale *Locale) GetLanguage() string { lang, err := language.Parse(locale.Code) if err != nil { return "" } base, _ := lang.Base() return base.String() } func GetIDs(locales []*Locale) []string { result := make([]string, 0, len(locales)) for _, locale := range locales { result = append(result, locale.ID) } return result } func LocaleToProto(locale *Locale) *pb.Locale { if locale == nil { return nil } protoLocale := &pb.Locale{ Id: locale.ID, SpaceId: locale.SpaceID, Name: locale.Name, NativeName: locale.NativeName, Code: locale.Code, Fallback: locale.Fallback, Direction: locale.Direction, Weight: int64(locale.Weight), NoPublish: locale.NoPublish, Disabled: locale.Disabled, } return protoLocale } func LocaleFromProto(protoLocale *pb.Locale) *Locale { if protoLocale == nil { return nil } locale := &Locale{ ID: protoLocale.Id, SpaceID: protoLocale.SpaceId, Name: protoLocale.Name, NativeName: protoLocale.NativeName, Code: protoLocale.Code, Fallback: protoLocale.Fallback, Direction: protoLocale.Direction, Weight: int(protoLocale.Weight), NoPublish: protoLocale.NoPublish, Disabled: protoLocale.Disabled, } return locale } func FallbackSort(locales []*Locale) ([]*Locale, error) { if len(locales) == 0 { return locales, nil } var ( unsorted = data.SliceToMap(locales) sorted = make([]*Locale, 0, len(locales)) visited = make(map[string]bool, len(locales)) ) for { changed := false for _, node := range locales { if visited[node.ID] { continue } if _, exist := unsorted[node.Fallback]; !exist && node.Fallback != "" { return nil, errors.Wrap(ErrNotFound, node.Fallback) } if node.IsDefault() { sorted = append([]*Locale{node}, sorted...) visited[node.ID] = true changed = true continue } if node.Fallback == "" || visited[node.Fallback] { sorted = append(sorted, node) visited[node.ID] = true changed = true } } // Все локали перенесены в список отсортированных if len(sorted) == len(locales) { break } // Если не произошло изменений, но не все локали отсортированы - обнаружены циклические зависимости if !changed { return nil, ErrCircularDependency } } return sorted, nil }