Files
RenewCMS/internal/domain/localization/localizationRules.go
T

180 lines
4.7 KiB
Go

package localization
import (
"RenewCMS/internal/domain/language"
"regexp"
"slices"
"strings"
)
var socialLocalePattern = regexp.MustCompile(`^([A-Za-z]{2,3})_([A-Za-z]{2})$`)
var regionPattern = regexp.MustCompile(`^[A-Za-z]{2}$`)
func NormalizeSocialLocale(tag string, social *string) (*string, error) {
if social == nil {
return nil, nil
}
parts := socialLocalePattern.FindStringSubmatch(*social)
if parts == nil {
return nil, ErrInvalidSocialLocale
}
tagLanguage, _, _ := strings.Cut(tag, "-")
if !strings.EqualFold(parts[1], tagLanguage) {
return nil, ErrInvalidSocialLocale
}
normalized := strings.ToLower(parts[1]) + "_" + strings.ToUpper(parts[2])
return &normalized, nil
}
func SocialLocaleState(row Row, siteLocale *string) string {
if row.SocialLocale != nil {
return SocialLocaleSet
}
if EffectiveSocialLocale(row, siteLocale) != nil {
return SocialLocaleDerived
}
return SocialLocaleNotSet
}
func EffectiveSocialLocale(row Row, siteLocale *string) *string {
if row.SocialLocale != nil {
return row.SocialLocale
}
if _, region, found := strings.Cut(row.Tag, "-"); found && region != "" {
derived := strings.Replace(row.Tag, "-", "_", 1)
return &derived
}
return borrowedSocialLocale(row.Tag, siteLocale)
}
func borrowedSocialLocale(tag string, siteLocale *string) *string {
if siteLocale == nil {
return nil
}
tagLanguage, _, _ := strings.Cut(tag, "-")
parts := strings.Split(*siteLocale, "-")
if len(parts) < 2 || !strings.EqualFold(parts[0], tagLanguage) || !regionPattern.MatchString(parts[1]) {
return nil
}
borrowed := strings.ToLower(parts[0]) + "_" + strings.ToUpper(parts[1])
return &borrowed
}
func normalizeEntries(entries []Entry) ([]Entry, error) {
normalized := make([]Entry, 0, len(entries))
seen := map[string]bool{}
for _, entry := range entries {
tag, err := language.Normalize(entry.Tag)
if err != nil {
return nil, err
}
if seen[tag] {
return nil, ErrDuplicateLanguage
}
seen[tag] = true
social, err := NormalizeSocialLocale(tag, entry.SocialLocale)
if err != nil {
return nil, err
}
normalized = append(normalized, Entry{Tag: tag, Enabled: entry.Enabled, SocialLocale: social})
}
return normalized, nil
}
func Plan(current []Row, counts map[string]VersionCount, change Change) ([]Row, error) {
entries, err := normalizeEntries(change.Languages)
if err != nil {
return nil, err
}
if !slices.ContainsFunc(entries, func(entry Entry) bool { return entry.Enabled }) {
return nil, ErrNoEnabledLanguage
}
defaultTag, err := language.Normalize(change.Default)
if err != nil || !slices.ContainsFunc(entries, func(entry Entry) bool { return entry.Tag == defaultTag && entry.Enabled }) {
return nil, ErrDefaultNotEnabled
}
confirmed := map[string]bool{}
for _, row := range current {
confirmed[row.Tag] = row.Confirmed
if !slices.ContainsFunc(entries, func(entry Entry) bool { return entry.Tag == row.Tag }) && counts[row.Tag].Total+counts[row.Tag].Trashed > 0 {
return nil, ErrLanguageInUse
}
}
next := make([]Row, 0, len(entries))
for _, entry := range entries {
isDefault := entry.Tag == defaultTag
next = append(next, Row{Tag: entry.Tag, IsDefault: isDefault, Enabled: entry.Enabled, Confirmed: confirmed[entry.Tag] || isDefault, SocialLocale: entry.SocialLocale})
}
return next, nil
}
func EnabledTag(rows []Row, tag string) bool {
return slices.ContainsFunc(rows, func(row Row) bool { return row.Tag == tag && row.Enabled })
}
func HasRow(rows []Row, tag string) bool {
return slices.ContainsFunc(rows, func(row Row) bool { return row.Tag == tag })
}
func MissingTags(rows []Row, tags []string) []string {
missing := []string{}
for _, tag := range tags {
if tag != language.Unassigned && !EnabledTag(rows, tag) && !slices.Contains(missing, tag) {
missing = append(missing, tag)
}
}
slices.Sort(missing)
return missing
}
func ConfirmedDefaultTag(rows []Row) string {
if NeedsSetup(rows) {
return language.Unassigned
}
return DefaultTag(rows)
}
func SiteTextAllowed(defaultTag string, tag string) bool {
return tag == language.Unassigned || tag == defaultTag
}
func PublicTag(rows []Row, tag string) bool {
return tag != language.Unassigned && EnabledTag(rows, tag)
}
func RequireSingleDefault(rows []Row) error {
defaults := 0
for _, row := range rows {
if row.IsDefault {
defaults++
}
}
if defaults != 1 {
return ErrSingleDefault
}
return nil
}
func DefaultTag(rows []Row) string {
for _, row := range rows {
if row.IsDefault {
return row.Tag
}
}
return language.Unassigned
}
func CompareTags(defaultTag string, left string, right string) int {
if left == right {
return 0
}
if left == defaultTag {
return -1
}
if right == defaultTag {
return 1
}
return strings.Compare(left, right)
}