package locale import ( "context" "strings" "sync/atomic" "golang.org/x/text/language" ) var ( localeRegistry = map[string]*Locale{ "en": defaultLocale, } localeMatcher = language.NewMatcher( []language.Tag{ language.English, language.SimplifiedChinese, language.TraditionalChinese, language.Persian, language.Russian, }, language.PreferSameScript(true), ) localeNames = []string{"en", "zh-Hans", "zh-Hant", "fa", "ru"} current atomic.Pointer[Locale] ) type Locale struct { Locale string DeprecatedMessage string DeprecatedMessageNoLink string InsecureFeatureMessage string ExternalPathFeature string TailscaleInitializing string TailscaleInUse string TailscaleNeedsLogin string TailscaleNeedsApproval string TailscaleStopped string TailscaleStarting string TailscaleRunning string TaildropReceiving string TaildropReceived string TaildropSendCanceled string VPNConnecting string VPNAuthentication string VPNConnected string VPNError string Unknown string } var defaultLocale = &Locale{ Locale: "en", DeprecatedMessage: "%s is deprecated in sing-box %s and will be removed in sing-box %s. Please check the documentation for migration.", DeprecatedMessageNoLink: "%s is deprecated in sing-box %s and will be removed in sing-box %s.", InsecureFeatureMessage: "%s is considered insecure in the graphical client for sing-box on %s. Enable Insecure Mode in `Settings - Core - Insecure Mode` to use it.", ExternalPathFeature: "Access to %s (outside of the working directory) is considered insecure in the graphical client for sing-box on %s. Enable Insecure Mode in `Settings - Core - Insecure Mode` to use it.", TailscaleInitializing: "Initializing", TailscaleInUse: "In use by another user", TailscaleNeedsLogin: "Needs login", TailscaleNeedsApproval: "Needs approval", TailscaleStopped: "Stopped", TailscaleStarting: "Starting", TailscaleRunning: "Running", TaildropReceiving: "Receiving %s from %s", TaildropReceived: "%s received from %s", TaildropSendCanceled: "Sending %s canceled by receiver", VPNConnecting: "Connecting", VPNAuthentication: "Authentication required", VPNConnected: "Connected", VPNError: "Error", Unknown: "Unknown", } type contextKey struct{} func init() { current.Store(defaultLocale) } func Current() *Locale { return current.Load() } func ContextWithLocale(ctx context.Context, localeID string) (context.Context, bool) { selectedLocale, loaded := selectLocale(localeID) if !loaded { return ctx, false } return context.WithValue(ctx, contextKey{}, selectedLocale), true } func FromContext(ctx context.Context) *Locale { selectedLocale, loaded := ctx.Value(contextKey{}).(*Locale) if loaded { return selectedLocale } return Current() } func selectLocale(localeID string) (*Locale, bool) { localeName, loaded := match(localeID) if !loaded { return nil, false } selectedLocale, loaded := localeRegistry[localeName] return selectedLocale, loaded } func Set(localeID string) bool { selectedLocale, loaded := selectLocale(localeID) if !loaded { return false } current.Store(selectedLocale) return true } func match(localeID string) (string, bool) { localeEntries := strings.Split(localeID, ",") for i, localeEntry := range localeEntries { languageID, options, hasOptions := strings.Cut(localeEntry, ";") languageID, _, _ = strings.Cut(strings.TrimSpace(languageID), "@") languageID = strings.ReplaceAll(languageID, "_", "-") if !hasOptions { languageID, _, _ = strings.Cut(languageID, ".") } switch { case strings.EqualFold(languageID, "C"), strings.EqualFold(languageID, "POSIX"): languageID = "en" case strings.EqualFold(languageID, "zh-CHS"): languageID = "zh-Hans" case strings.EqualFold(languageID, "zh-CHT"): languageID = "zh-Hant" } localeEntries[i] = languageID if hasOptions { localeEntries[i] += ";" + options } } localeID = strings.Join(localeEntries, ",") tags, _, err := language.ParseAcceptLanguage(localeID) if err != nil || len(tags) == 0 { return "", false } for i, tag := range tags { base, script, region := tag.Raw() if base.String() != "zh" && base.String() != "cmn" { continue } if script.String() == "Hans" || script.String() == "Hant" { continue } languageID := "zh-Hans" if region.String() == "TW" || region.String() == "HK" || region.String() == "MO" { languageID = "zh-Hant" } if region.String() != "ZZ" { languageID += "-" + region.String() } tags[i] = language.MustParse(languageID) } _, localeIndex, _ := localeMatcher.Match(tags...) return localeNames[localeIndex], true } func (l *Locale) TailscaleStateText(state string) string { switch state { case "NoState": return l.TailscaleInitializing case "InUseOtherUser": return l.TailscaleInUse case "NeedsLogin": return l.TailscaleNeedsLogin case "NeedsMachineAuth": return l.TailscaleNeedsApproval case "Stopped": return l.TailscaleStopped case "Starting": return l.TailscaleStarting case "Running": return l.TailscaleRunning default: return l.Unknown } } func (l *Locale) VPNStateText(state string) string { switch state { case "connecting": return l.VPNConnecting case "auth-pending": return l.VPNAuthentication case "connected": return l.VPNConnected case "error": return l.VPNError default: return l.Unknown } }