package localization import ( "RenewCMS/internal/domain/language" "regexp" "slices" "strings" ) var socialLocalePattern = regexp.MustCompile(`^([A-Za-z]{2,3})_([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) string { if row.SocialLocale != nil { return SocialLocaleSet } if _, region, found := strings.Cut(row.Tag, "-"); found && region != "" { return SocialLocaleDerived } return SocialLocaleNotSet } func EffectiveSocialLocale(row Row) *string { switch SocialLocaleState(row) { case SocialLocaleSet: return row.SocialLocale case SocialLocaleDerived: derived := strings.Replace(row.Tag, "-", "_", 1) return &derived } return nil } 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 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) }