| 
									
										
										
										
											2023-05-20 15:11:08 +00:00
										 |  |  | package locale | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"embed" | 
					
						
							|  |  |  | 	"io/fs" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2024-03-10 21:31:24 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-20 15:11:08 +00:00
										 |  |  | 	"x-ui/logger" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-20 15:16:34 +00:00
										 |  |  | 	"github.com/gin-gonic/gin" | 
					
						
							| 
									
										
										
										
											2023-05-20 15:11:08 +00:00
										 |  |  | 	"github.com/nicksnyder/go-i18n/v2/i18n" | 
					
						
							|  |  |  | 	"github.com/pelletier/go-toml/v2" | 
					
						
							|  |  |  | 	"golang.org/x/text/language" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-10 21:31:24 +00:00
										 |  |  | var ( | 
					
						
							|  |  |  | 	i18nBundle   *i18n.Bundle | 
					
						
							|  |  |  | 	LocalizerWeb *i18n.Localizer | 
					
						
							|  |  |  | 	LocalizerBot *i18n.Localizer | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2023-05-20 15:11:08 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | type I18nType string | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	Bot I18nType = "bot" | 
					
						
							|  |  |  | 	Web I18nType = "web" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type SettingService interface { | 
					
						
							|  |  |  | 	GetTgLang() (string, error) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func InitLocalizer(i18nFS embed.FS, settingService SettingService) error { | 
					
						
							|  |  |  | 	// set default bundle to english
 | 
					
						
							| 
									
										
										
										
											2023-06-16 09:31:34 +00:00
										 |  |  | 	i18nBundle = i18n.NewBundle(language.MustParse("en-US")) | 
					
						
							| 
									
										
										
										
											2023-05-20 15:11:08 +00:00
										 |  |  | 	i18nBundle.RegisterUnmarshalFunc("toml", toml.Unmarshal) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// parse files
 | 
					
						
							|  |  |  | 	if err := parseTranslationFiles(i18nFS, i18nBundle); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-20 15:29:04 +00:00
										 |  |  | 	// setup bot locale
 | 
					
						
							|  |  |  | 	if err := initTGBotLocalizer(settingService); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-20 15:11:08 +00:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-12 19:13:51 +00:00
										 |  |  | func createTemplateData(params []string, seperator ...string) map[string]any { | 
					
						
							| 
									
										
										
										
											2023-05-20 15:11:08 +00:00
										 |  |  | 	var sep string = "==" | 
					
						
							|  |  |  | 	if len(seperator) > 0 { | 
					
						
							|  |  |  | 		sep = seperator[0] | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-12 19:13:51 +00:00
										 |  |  | 	templateData := make(map[string]any) | 
					
						
							| 
									
										
										
										
											2023-05-20 15:11:08 +00:00
										 |  |  | 	for _, param := range params { | 
					
						
							|  |  |  | 		parts := strings.SplitN(param, sep, 2) | 
					
						
							|  |  |  | 		templateData[parts[0]] = parts[1] | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return templateData | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func I18n(i18nType I18nType, key string, params ...string) string { | 
					
						
							|  |  |  | 	var localizer *i18n.Localizer | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch i18nType { | 
					
						
							|  |  |  | 	case "bot": | 
					
						
							|  |  |  | 		localizer = LocalizerBot | 
					
						
							|  |  |  | 	case "web": | 
					
						
							|  |  |  | 		localizer = LocalizerWeb | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		logger.Errorf("Invalid type for I18n: %s", i18nType) | 
					
						
							|  |  |  | 		return "" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	templateData := createTemplateData(params) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	msg, err := localizer.Localize(&i18n.LocalizeConfig{ | 
					
						
							|  |  |  | 		MessageID:    key, | 
					
						
							|  |  |  | 		TemplateData: templateData, | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		logger.Errorf("Failed to localize message: %v", err) | 
					
						
							|  |  |  | 		return "" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return msg | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-20 15:29:04 +00:00
										 |  |  | func initTGBotLocalizer(settingService SettingService) error { | 
					
						
							|  |  |  | 	botLang, err := settingService.GetTgLang() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	LocalizerBot = i18n.NewLocalizer(i18nBundle, botLang) | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-20 15:16:34 +00:00
										 |  |  | func LocalizerMiddleware() gin.HandlerFunc { | 
					
						
							|  |  |  | 	return func(c *gin.Context) { | 
					
						
							|  |  |  | 		var lang string | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if cookie, err := c.Request.Cookie("lang"); err == nil { | 
					
						
							|  |  |  | 			lang = cookie.Value | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			lang = c.GetHeader("Accept-Language") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		LocalizerWeb = i18n.NewLocalizer(i18nBundle, lang) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		c.Set("localizer", LocalizerWeb) | 
					
						
							|  |  |  | 		c.Set("I18n", I18n) | 
					
						
							|  |  |  | 		c.Next() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-20 15:11:08 +00:00
										 |  |  | func parseTranslationFiles(i18nFS embed.FS, i18nBundle *i18n.Bundle) error { | 
					
						
							|  |  |  | 	err := fs.WalkDir(i18nFS, "translation", | 
					
						
							|  |  |  | 		func(path string, d fs.DirEntry, err error) error { | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if d.IsDir() { | 
					
						
							|  |  |  | 				return nil | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			data, err := i18nFS.ReadFile(path) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			_, err = i18nBundle.ParseMessageFileBytes(data, path) | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } |