package web import ( "fmt" "strings" "gitgud.io/mike/mpv-manager/internal/assets" "gitgud.io/mike/mpv-manager/pkg/locale" ) // LanguageOption holds language display data for web UI selection // This struct includes JSON tags for API responses type LanguageOption struct { Locale string `json:"locale"` // Full locale string (e.g., "ja-JP") LanguageName string `json:"language_name"` // English language name (e.g., "Japanese") LanguageLocal string `json:"language_local"` // Native language name (e.g., "日本語") FlagEmoji string `json:"flag_emoji"` // Flag emoji (e.g., "🇯🇵") CountryCode string `json:"country_code"` // ISO country code (e.g., "JP") CountryName string `json:"country_name"` // Country name (e.g., "Japan") FullLocale string `json:"full_locale"` // Full locale code for display (e.g., "ES-419") DisplayText string `json:"display_text"` // Formatted display text (e.g., "🇯🇵 JP - 日本語 (Japanese)") } // LanguageFilterOptions defines filtering options for language search // This struct is kept for API compatibility with existing web code type LanguageFilterOptions struct { CommonOnly bool // Show only common languages SearchTerm string // Search by language name or country code Limit int // Maximum number of results (0 = no limit) } // ConvertToWebOption converts a locale.LanguageOption to a web.LanguageOption // This preserves the JSON tags needed for API responses func ConvertToWebOption(localeOpt locale.LanguageOption) LanguageOption { flagEmoji := extractFlagEmoji(localeOpt) return LanguageOption{ Locale: localeOpt.Locale, LanguageName: localeOpt.LanguageName, LanguageLocal: localeOpt.LanguageLocal, FlagEmoji: flagEmoji, CountryCode: localeOpt.CountryCode, CountryName: localeOpt.CountryName, FullLocale: localeOpt.FullLocale, DisplayText: localeOpt.DisplayText, } } // ConvertToWebOptionSlice converts a slice of locale.LanguageOption to []web.LanguageOption func ConvertToWebOptionSlice(localeOpts []locale.LanguageOption) []LanguageOption { webOpts := make([]LanguageOption, len(localeOpts)) for i, opt := range localeOpts { webOpts[i] = ConvertToWebOption(opt) } return webOpts } // ConvertToWebOptionSliceWithEntries converts a slice of locale.LanguageOption to []web.LanguageOption // using the entries to look up flag emojis from regions func ConvertToWebOptionSliceWithEntries(localeOpts []locale.LanguageOption, entries []locale.LocaleEntry) []LanguageOption { webOpts := make([]LanguageOption, len(localeOpts)) for i, opt := range localeOpts { webOpts[i] = ConvertToWebOptionWithEntries(opt, entries) } return webOpts } // ConvertToWebOptionWithEntries converts a locale.LanguageOption to a web.LanguageOption // using the entries to look up the flag emoji from the region func ConvertToWebOptionWithEntries(localeOpt locale.LanguageOption, entries []locale.LocaleEntry) LanguageOption { flagEmoji := extractFlagEmojiFromEntries(localeOpt, entries) return LanguageOption{ Locale: localeOpt.Locale, LanguageName: localeOpt.LanguageName, LanguageLocal: localeOpt.LanguageLocal, FlagEmoji: flagEmoji, CountryCode: localeOpt.CountryCode, CountryName: localeOpt.CountryName, FullLocale: localeOpt.FullLocale, DisplayText: localeOpt.DisplayText, } } // extractFlagEmojiFromEntries extracts the flag emoji from entries for a given locale option func extractFlagEmojiFromEntries(localeOpt locale.LanguageOption, entries []locale.LocaleEntry) string { // Try to find the region by locale for _, entry := range entries { for _, region := range entry.Regions { if region.Locale == localeOpt.Locale { if region.Flag != "" { return region.Flag } } } } // Fallback to language code flag parts := strings.Split(localeOpt.Locale, "-") if len(parts) > 0 { langCode := parts[0] return locale.GetFlagForLanguageCode(langCode) } return "🏳️" } // extractFlagEmoji extracts the flag emoji from a locale option // This is a simpler version that doesn't require entries func extractFlagEmoji(localeOpt locale.LanguageOption) string { // Extract language code from locale parts := strings.Split(localeOpt.Locale, "-") if len(parts) > 0 { langCode := parts[0] return locale.GetFlagForLanguageCode(langCode) } return "🏳️" } // GetFlagImageURL returns the URL path for a flag image based on locale string // Returns "/static/flags/JP.png" for "ja-JP" locale // This is web-specific and not available in the locale package func GetFlagImageURL(localeStr string) string { parts := strings.Split(localeStr, "-") if len(parts) == 2 { // Region code is the second part (e.g., "JP" from "ja-JP") return fmt.Sprintf("/static/flags/%s.png", parts[1]) } // Try to find entry by language code and use first region entries, err := assets.ReadLocales() if err != nil { return "" } entry := locale.FindByLanguageCode(localeStr, entries) if entry != nil && len(entry.Regions) > 0 { return fmt.Sprintf("/static/flags/%s.png", entry.Regions[0].CountryCode) } return "" } // GetCommonLanguageOptions returns a list of common language options for quick selection func GetCommonLanguageOptions() ([]LanguageOption, error) { entries, err := assets.ReadLocales() if err != nil { return nil, fmt.Errorf("failed to load languages: %w", err) } localeOpts := locale.GetCommonLanguages(entries) return ConvertToWebOptionSliceWithEntries(localeOpts, entries), nil } // GetAllLanguageOptions returns all language options sorted alphabetically func GetAllLanguageOptions() ([]LanguageOption, error) { entries, err := assets.ReadLocales() if err != nil { return nil, fmt.Errorf("failed to load languages: %w", err) } localeOpts := locale.GetAllLanguages(entries) return ConvertToWebOptionSliceWithEntries(localeOpts, entries), nil } // GetLanguagesByCountry returns all languages available for a specific country func GetLanguagesByCountry(countryCode string) ([]LanguageOption, error) { entries, err := assets.ReadLocales() if err != nil { return nil, fmt.Errorf("failed to load languages: %w", err) } localeOpts := locale.GetLanguagesByCountry(entries, countryCode) return ConvertToWebOptionSliceWithEntries(localeOpts, entries), nil } // FindLanguageByLocale finds a specific language option by locale string func FindLanguageByLocale(localeStr string) (*LanguageOption, error) { entries, err := assets.ReadLocales() if err != nil { return nil, fmt.Errorf("failed to load languages: %w", err) } localeOpt, err := locale.FindLanguageByLocale(entries, localeStr) if err != nil { return nil, err } webOpt := ConvertToWebOptionWithEntries(*localeOpt, entries) return &webOpt, nil } // IsCommonLanguage checks if a language code is in the common languages list func IsCommonLanguage(languageCode string) bool { return locale.IsCommonLanguage(languageCode) } // GetCommonLanguageCodes returns the list of common language codes func GetCommonLanguageCodes() []string { return locale.GetCommonLanguageCodes() } // SearchLanguages searches and filters languages based on provided options // This is kept for API compatibility but delegates to locale.SearchLanguages func SearchLanguages(opts LanguageFilterOptions) ([]LanguageOption, error) { entries, err := assets.ReadLocales() if err != nil { return nil, fmt.Errorf("failed to load languages: %w", err) } localeOpts := locale.SearchLanguages(entries, locale.LanguageFilterOptions{ CommonOnly: opts.CommonOnly, SearchTerm: opts.SearchTerm, Limit: opts.Limit, }) return ConvertToWebOptionSliceWithEntries(localeOpts, entries), nil }