-
Notifications
You must be signed in to change notification settings - Fork 22
/
Copy pathginI18n.go
256 lines (227 loc) · 8.52 KB
/
ginI18n.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
// Package i18n ginI18nImpl is an implementation of the GinI18n interface, providing
// localization support for Gin applications. It uses the go-i18n library
// to manage and retrieve localized messages.
//
// Fields:
// - bundle: The i18n.Bundle containing the localization messages.
// - localizerByLng: A map of language tags to their corresponding localizers.
// - defaultLanguage: The default language tag to use for localization.
// - getLngHandler: A handler function to retrieve the language tag from the Gin context.
//
// Methods:
// - GetMessage: Retrieves a localized message based on the provided context and parameter.
// - MustGetMessage: Retrieves a localized message and returns an empty string if retrieval fails.
// - HasLang: Checks if a specific language is supported.
// - GetCurrentLanguage: Retrieves the current language based on the Gin context..
// - GetDefaultLanguage: Retrieves the default language
// - SetBundle: Sets the i18n.Bundle configuration.
// - SetGetLngHandler: Sets the handler function to retrieve the language tag from the Gin context.
// - loadMessageFiles: Loads all localization files into the bundle.
// - loadMessageFile: Loads a single localization file into the bundle.
// - setLocalizerByLng: Sets the localizers for each accepted language.
// - newLocalizer: Creates a new localizer for a given language.
// - getLocalizerByLng: Retrieves the localizer for a given language.
// - getLocalizeConfig: Converts the parameter into an i18n.LocalizeConfig.
package i18n
import (
"errors"
"fmt"
"path"
"github.com/gin-gonic/gin"
"github.com/nicksnyder/go-i18n/v2/i18n"
"golang.org/x/text/language"
)
// GinI18n is an interface that defines methods for internationalization (i18n) in a Gin web framework context.
// It provides methods to get localized messages and configure the i18n bundle and language handler.
var _ GinI18n = (*ginI18nImpl)(nil)
type ginI18nImpl struct {
bundle *i18n.Bundle
localizerByLng map[string]*i18n.Localizer
defaultLanguage language.Tag
getLngHandler GetLngHandler
}
// GetDefaultLanguage retrieves the default language tag for the application.
//
// This method returns the default language tag that is used when no specific
// language is specified by the client or in the context.
//
// Parameters:
// - ctx: The Gin context from which to retrieve the message.
//
// Returns:
// - language.Tag: The default language tag.
func (i *ginI18nImpl) GetDefaultLanguage() language.Tag {
return i.defaultLanguage
}
// GetCurrentLanguage retrieves the current language tag from the Gin context.
//
// This method extracts the language tag from the Gin context using the provided
// `getLngHandler` function. It uses this handler to obtain the language tag for
// the current request. If the language is not provided, it returns the default language.
//
// Parameters:
// - ctx: The Gin context from which to retrieve the message.
//
// Returns:
// - language.Tag: The language tag based on the context (either the specified language or the default language).
func (i *ginI18nImpl) GetCurrentLanguage(context *gin.Context) language.Tag {
return language.Make(i.getLngHandler(context, i.defaultLanguage.String()))
}
// HasLang checks whether the specified language is supported by the application.
//
// This method checks if a language tag is available in the localizer map (`localizerByLng`),
// which stores localizers for all supported languages. If the language is supported,
// it returns `true`; otherwise, it returns `false`.
//
// Parameters:
// - ctx: The Gin context from which to retrieve the message.
// - language (string): The language tag (e.g., "en", "zh") to check.
//
// Returns:
// - bool: `true` if the language is supported, otherwise `false`.
func (i *ginI18nImpl) HasLang(language string) bool {
if _, exist := i.localizerByLng[language]; exist {
return true
}
return false
}
// GetMessage retrieves a localized message based on the provided context and parameter.
// If the message cannot be retrieved, it returns an empty string.
//
// Parameters:
// - ctx: The Gin context from which to retrieve the message.
// - param: The parameter used to fetch the localized message.
//
// Returns:
// - string: The localized message or an empty string if retrieval fails.
// - error: An error if the message retrieval fails.
func (i *ginI18nImpl) GetMessage(ctx *gin.Context, param interface{}) (string, error) {
lng := i.getLngHandler(ctx, i.defaultLanguage.String())
localizer := i.getLocalizerByLng(lng)
localizeConfig, err := i.getLocalizeConfig(param)
if err != nil {
return "", err
}
message, err := localizer.Localize(localizeConfig)
if err != nil {
return "", err
}
return message, nil
}
// MustGetMessage retrieves a localized message based on the provided context and parameter.
// If the message cannot be retrieved, it returns an empty string.
// This method panics if the message retrieval fails.
//
// Parameters:
// - ctx: The Gin context from which to retrieve the message.
// - param: The parameter used to fetch the localized message.
//
// Returns:
// - string: The localized message or an empty string if retrieval fails.
func (i *ginI18nImpl) MustGetMessage(ctx *gin.Context, param interface{}) string {
message, _ := i.GetMessage(ctx, param)
return message
}
// SetBundle initializes the i18n bundle with the provided configuration.
// It sets the default language, registers the unmarshal function for the bundle files,
// loads the message files, and sets the localizer based on the accepted languages.
//
// Parameters:
// - cfg: A pointer to a BundleCfg struct that contains the configuration for the bundle.
func (i *ginI18nImpl) SetBundle(cfg *BundleCfg) {
bundle := i18n.NewBundle(cfg.DefaultLanguage)
bundle.RegisterUnmarshalFunc(cfg.FormatBundleFile, cfg.UnmarshalFunc)
i.bundle = bundle
i.defaultLanguage = cfg.DefaultLanguage
i.loadMessageFiles(cfg)
i.setLocalizerByLng(cfg.AcceptLanguage)
}
// SetGetLngHandler sets the handler function that will be used to get the language.
// The handler should be a function that implements the GetLngHandler interface.
//
// Parameters:
//
// handler - a function that implements the GetLngHandler interface
func (i *ginI18nImpl) SetGetLngHandler(handler GetLngHandler) {
i.getLngHandler = handler
}
// loadMessageFiles load all file localize to bundle
func (i *ginI18nImpl) loadMessageFiles(config *BundleCfg) {
for _, lng := range config.AcceptLanguage {
src := path.Join(config.RootPath, lng.String()) + "." + config.FormatBundleFile
if err := i.loadMessageFile(config, src); err != nil {
panic(err)
}
}
}
func (i *ginI18nImpl) loadMessageFile(config *BundleCfg, src string) error {
buf, err := config.Loader.LoadMessage(src)
if err != nil {
return err
}
if _, err = i.bundle.ParseMessageFileBytes(buf, src); err != nil {
return err
}
return nil
}
// setLocalizerByLng set localizer by language
func (i *ginI18nImpl) setLocalizerByLng(acceptLanguage []language.Tag) {
i.localizerByLng = map[string]*i18n.Localizer{}
for _, lng := range acceptLanguage {
lngStr := lng.String()
i.localizerByLng[lngStr] = i.newLocalizer(lngStr)
}
// set defaultLanguage if it isn't exist
defaultLng := i.defaultLanguage.String()
if _, hasDefaultLng := i.localizerByLng[defaultLng]; !hasDefaultLng {
i.localizerByLng[defaultLng] = i.newLocalizer(defaultLng)
}
}
// newLocalizer create a localizer by language
func (i *ginI18nImpl) newLocalizer(lng string) *i18n.Localizer {
lngDefault := i.defaultLanguage.String()
lngs := []string{
lng,
}
if lng != lngDefault {
lngs = append(lngs, lngDefault)
}
localizer := i18n.NewLocalizer(
i.bundle,
lngs...,
)
return localizer
}
// getLocalizerByLng get localizer by language
func (i *ginI18nImpl) getLocalizerByLng(lng string) *i18n.Localizer {
localizer, hasValue := i.localizerByLng[lng]
if hasValue {
return localizer
}
return i.localizerByLng[i.defaultLanguage.String()]
}
func (i *ginI18nImpl) getLocalizeConfig(param interface{}) (*i18n.LocalizeConfig, error) {
switch paramValue := param.(type) {
case string:
localizeConfig := &i18n.LocalizeConfig{
MessageID: paramValue,
}
return localizeConfig, nil
case *i18n.Message:
localizeConfig := &i18n.LocalizeConfig{
DefaultMessage: paramValue,
}
return localizeConfig, nil
case i18n.Message:
localizeConfig := &i18n.LocalizeConfig{
DefaultMessage: ¶mValue,
}
return localizeConfig, nil
case *i18n.LocalizeConfig:
return paramValue, nil
case i18n.LocalizeConfig:
return ¶mValue, nil
}
msg := fmt.Sprintf("un supported localize param: %v", param)
return nil, errors.New(msg)
}