github.com/masterhung0112/hk_server/v5@v5.0.0-20220302090640-ec71aef15e1c/shared/i18n/i18n.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package i18n
     5  
     6  import (
     7  	"fmt"
     8  	"html/template"
     9  	"io/ioutil"
    10  	"net/http"
    11  	"path/filepath"
    12  	"reflect"
    13  	"strings"
    14  
    15  	"github.com/mattermost/go-i18n/i18n"
    16  
    17  	"github.com/masterhung0112/hk_server/v5/shared/mlog"
    18  )
    19  
    20  const defaultLocale = "en"
    21  
    22  // TranslateFunc is the type of the translate functions
    23  type TranslateFunc func(translationID string, args ...interface{}) string
    24  
    25  // T is the translate function using the default server language as fallback language
    26  var T TranslateFunc
    27  
    28  // TDefault is the translate function using english as fallback language
    29  var TDefault TranslateFunc
    30  
    31  var locales map[string]string = make(map[string]string)
    32  var defaultServerLocale string
    33  var defaultClientLocale string
    34  
    35  // TranslationsPreInit loads translations from filesystem if they are not
    36  // loaded already and assigns english while loading server config
    37  func TranslationsPreInit(translationsDir string) error {
    38  	if T != nil {
    39  		return nil
    40  	}
    41  
    42  	// Set T even if we fail to load the translations. Lots of shutdown handling code will
    43  	// segfault trying to handle the error, and the untranslated IDs are strictly better.
    44  	T = tfuncWithFallback(defaultLocale)
    45  	TDefault = tfuncWithFallback(defaultLocale)
    46  
    47  	return initTranslationsWithDir(translationsDir)
    48  }
    49  
    50  // InitTranslations set the defaults configured in the server and initialize
    51  // the T function using the server default as fallback language
    52  func InitTranslations(serverLocale, clientLocale string) error {
    53  	defaultServerLocale = serverLocale
    54  	defaultClientLocale = clientLocale
    55  
    56  	var err error
    57  	T, err = getTranslationsBySystemLocale()
    58  	return err
    59  }
    60  
    61  func initTranslationsWithDir(dir string) error {
    62  	files, _ := ioutil.ReadDir(dir)
    63  	for _, f := range files {
    64  		if filepath.Ext(f.Name()) == ".json" {
    65  			filename := f.Name()
    66  			locales[strings.Split(filename, ".")[0]] = filepath.Join(dir, filename)
    67  
    68  			if err := i18n.LoadTranslationFile(filepath.Join(dir, filename)); err != nil {
    69  				return err
    70  			}
    71  		}
    72  	}
    73  
    74  	return nil
    75  }
    76  
    77  func getTranslationsBySystemLocale() (TranslateFunc, error) {
    78  	locale := defaultServerLocale
    79  	if _, ok := locales[locale]; !ok {
    80  		mlog.Warn("Failed to load system translations for", mlog.String("locale", locale), mlog.String("attempting to fall back to default locale", defaultLocale))
    81  		locale = defaultLocale
    82  	}
    83  
    84  	if locales[locale] == "" {
    85  		return nil, fmt.Errorf("failed to load system translations for '%v'", defaultLocale)
    86  	}
    87  
    88  	translations := tfuncWithFallback(locale)
    89  	if translations == nil {
    90  		return nil, fmt.Errorf("failed to load system translations")
    91  	}
    92  
    93  	mlog.Info("Loaded system translations", mlog.String("for locale", locale), mlog.String("from locale", locales[locale]))
    94  	return translations, nil
    95  }
    96  
    97  // GetUserTranslations get the translation function for an specific locale
    98  func GetUserTranslations(locale string) TranslateFunc {
    99  	if _, ok := locales[locale]; !ok {
   100  		locale = defaultLocale
   101  	}
   102  
   103  	translations := tfuncWithFallback(locale)
   104  	return translations
   105  }
   106  
   107  // GetTranslationsAndLocaleFromRequest return the translation function and the
   108  // locale based on a request headers
   109  func GetTranslationsAndLocaleFromRequest(r *http.Request) (TranslateFunc, string) {
   110  	// This is for checking against locales like pt_BR or zn_CN
   111  	headerLocaleFull := strings.Split(r.Header.Get("Accept-Language"), ",")[0]
   112  	// This is for checking against locales like en, es
   113  	headerLocale := strings.Split(strings.Split(r.Header.Get("Accept-Language"), ",")[0], "-")[0]
   114  	defaultLocale := defaultClientLocale
   115  	if locales[headerLocaleFull] != "" {
   116  		translations := tfuncWithFallback(headerLocaleFull)
   117  		return translations, headerLocaleFull
   118  	} else if locales[headerLocale] != "" {
   119  		translations := tfuncWithFallback(headerLocale)
   120  		return translations, headerLocale
   121  	} else if locales[defaultLocale] != "" {
   122  		translations := tfuncWithFallback(defaultLocale)
   123  		return translations, headerLocale
   124  	}
   125  
   126  	translations := tfuncWithFallback(defaultLocale)
   127  	return translations, defaultLocale
   128  }
   129  
   130  // GetSupportedLocales return a map of locale code and the file path with the
   131  // translations
   132  func GetSupportedLocales() map[string]string {
   133  	return locales
   134  }
   135  
   136  func tfuncWithFallback(pref string) TranslateFunc {
   137  	t, _ := i18n.Tfunc(pref)
   138  	return func(translationID string, args ...interface{}) string {
   139  		if translated := t(translationID, args...); translated != translationID {
   140  			return translated
   141  		}
   142  
   143  		t, _ := i18n.Tfunc(defaultLocale)
   144  		return t(translationID, args...)
   145  	}
   146  }
   147  
   148  // TranslateAsHTML translates the translationID provided and return a
   149  // template.HTML object
   150  func TranslateAsHTML(t TranslateFunc, translationID string, args map[string]interface{}) template.HTML {
   151  	message := t(translationID, escapeForHTML(args))
   152  	message = strings.Replace(message, "[[", "<strong>", -1)
   153  	message = strings.Replace(message, "]]", "</strong>", -1)
   154  	return template.HTML(message)
   155  }
   156  
   157  func escapeForHTML(arg interface{}) interface{} {
   158  	switch typedArg := arg.(type) {
   159  	case string:
   160  		return template.HTMLEscapeString(typedArg)
   161  	case *string:
   162  		return template.HTMLEscapeString(*typedArg)
   163  	case map[string]interface{}:
   164  		safeArg := make(map[string]interface{}, len(typedArg))
   165  		for key, value := range typedArg {
   166  			safeArg[key] = escapeForHTML(value)
   167  		}
   168  		return safeArg
   169  	default:
   170  		mlog.Warn(
   171  			"Unable to escape value for HTML template",
   172  			mlog.Any("html_template", arg),
   173  			mlog.String("template_type", reflect.ValueOf(arg).Type().String()),
   174  		)
   175  		return ""
   176  	}
   177  }
   178  
   179  // IdentityTfunc returns a translation function that don't translate, only
   180  // returns the same id
   181  func IdentityTfunc() TranslateFunc {
   182  	return func(translationID string, args ...interface{}) string {
   183  		return translationID
   184  	}
   185  }