2017-05-27 19:08:47 +02:00
package publicSettings
2017-05-07 02:32:32 +02:00
import (
2017-05-14 21:45:50 +02:00
"errors"
2017-05-09 01:44:41 +02:00
"fmt"
2017-05-13 22:52:10 +02:00
"html/template"
"path"
"path/filepath"
2017-07-11 21:34:06 +02:00
"strings"
2017-05-13 22:52:10 +02:00
2017-07-06 21:53:13 +02:00
"sort"
2017-05-17 07:58:40 +02:00
"github.com/NyaaPantsu/nyaa/config"
This is a prelimenary work
Showing how we can remove services, preventing cyclic imports and lessing the number of imports.
Now db is in models. Db and models are highly tightened, according to go standards, you should put them in the same package.
In models, there are folders separating the different methods used to modify the models. For example, if you want to create a user, you have to use /models (for the user struct) and /models/user (for creating a user.
However, if you want to delete a torrent, you just have to import /models and do torrent.Delete(definitely bool).
By the way packages in models are the plural name of a model. For example, you have torrent.go for a torrent model and its package torrents for db stuff related functions (Find, Create, Some helpers)
2017-06-29 00:44:07 +02:00
"github.com/NyaaPantsu/nyaa/models"
2017-06-28 13:42:38 +02:00
"github.com/gin-gonic/gin"
2017-05-11 15:12:19 +02:00
"github.com/nicksnyder/go-i18n/i18n"
2017-05-13 00:17:34 +02:00
"github.com/nicksnyder/go-i18n/i18n/language"
2017-07-11 21:34:06 +02:00
glang "golang.org/x/text/language"
"golang.org/x/text/language/display"
2017-05-07 23:05:41 +02:00
)
2017-05-07 02:32:32 +02:00
2017-05-26 12:12:52 +02:00
// UserRetriever : this interface is required to prevent a cyclic import between the languages and userService package.
2017-05-14 21:45:50 +02:00
type UserRetriever interface {
2017-07-03 00:59:59 +02:00
RetrieveCurrentUser ( c * gin . Context ) ( * models . User , error )
2017-05-14 21:45:50 +02:00
}
2017-07-06 21:53:13 +02:00
type Language struct {
Name string
Code string
2017-07-11 21:34:06 +02:00
Tag string
2017-07-06 21:53:13 +02:00
}
type Languages [ ] Language
2017-05-26 12:12:52 +02:00
// TemplateTfunc : T func used in template
2017-05-21 00:38:28 +02:00
type TemplateTfunc func ( string , ... interface { } ) template . HTML
2017-05-14 21:45:50 +02:00
var (
2017-07-10 14:11:05 +02:00
defaultLanguage = config . Get ( ) . I18n . DefaultLanguage
2017-05-26 12:12:52 +02:00
userRetriever UserRetriever
2017-07-06 21:53:13 +02:00
languages Languages
2017-05-14 21:45:50 +02:00
)
2017-05-26 12:12:52 +02:00
// InitI18n : Initialize the languages translation
2017-05-14 21:45:50 +02:00
func InitI18n ( conf config . I18nConfig , retriever UserRetriever ) error {
defaultLanguage = conf . DefaultLanguage
userRetriever = retriever
2017-05-31 04:21:57 +02:00
defaultFilepath := path . Join ( conf . Directory , defaultLanguage + ".all.json" )
2017-05-13 22:52:10 +02:00
err := i18n . LoadTranslationFile ( defaultFilepath )
if err != nil {
panic ( fmt . Sprintf ( "failed to load default translation file '%s': %v" , defaultFilepath , err ) )
}
2017-05-31 04:21:57 +02:00
paths , err := filepath . Glob ( path . Join ( conf . Directory , "*.json" ) )
2017-05-13 22:52:10 +02:00
if err != nil {
return fmt . Errorf ( "failed to get translation files: %v" , err )
}
2017-05-14 21:45:50 +02:00
for _ , file := range paths {
err := i18n . LoadTranslationFile ( file )
2017-05-13 22:52:10 +02:00
if err != nil {
2017-05-14 21:45:50 +02:00
return fmt . Errorf ( "failed to load translation file '%s': %v" , file , err )
2017-05-13 22:52:10 +02:00
}
}
return nil
}
2017-05-26 12:12:52 +02:00
// GetDefaultLanguage : returns the default language from config
2017-05-14 21:45:50 +02:00
func GetDefaultLanguage ( ) string {
return defaultLanguage
}
2017-05-26 12:12:52 +02:00
// TfuncAndLanguageWithFallback : When go-i18n finds a language with >0 translations, it uses it as the Tfunc
2017-05-09 20:24:37 +02:00
// However, if said language has a missing translation, it won't fallback to the "main" language
2017-05-13 00:17:34 +02:00
func TfuncAndLanguageWithFallback ( language string , languages ... string ) ( i18n . TranslateFunc , * language . Language , error ) {
2017-05-19 04:55:59 +02:00
fallbackLanguage := GetDefaultLanguage ( )
2017-05-09 20:24:37 +02:00
2017-05-19 04:55:59 +02:00
tFunc , tLang , err1 := i18n . TfuncAndLanguage ( language , languages ... )
// If fallbackLanguage fails, it will give the "id" field so we don't
// care about the error
fallbackT , fallbackTlang , _ := i18n . TfuncAndLanguage ( fallbackLanguage )
2017-05-09 20:24:37 +02:00
2017-05-19 04:55:59 +02:00
translateFunction := func ( translationID string , args ... interface { } ) string {
if translated := tFunc ( translationID , args ... ) ; translated != translationID {
2017-05-09 20:24:37 +02:00
return translated
}
return fallbackT ( translationID , args ... )
2017-05-19 04:55:59 +02:00
}
if err1 != nil {
tLang = fallbackTlang
}
return translateFunction , tLang , err1
2017-05-09 20:24:37 +02:00
}
2017-07-06 21:53:13 +02:00
// GetAvailableLanguages : Get languages available on the website, languages are parsed once at runtime
func GetAvailableLanguages ( ) Languages {
if len ( languages ) > 0 {
return languages
}
// Need this to sort out languages alphabetically by language tag
var codes [ ] string
2017-05-10 21:45:39 +02:00
for _ , languageTag := range i18n . LanguageTags ( ) {
2017-07-06 21:53:13 +02:00
codes = append ( codes , languageTag )
}
2017-07-11 21:34:06 +02:00
languages = ParseLanguages ( codes )
return languages
}
2017-07-06 21:53:13 +02:00
2017-07-13 00:20:43 +02:00
// GetParentTag returns the highest parent of a language (e.g. fr-fr -> fr)
func GetParentTag ( languageTag string ) glang . Tag {
2017-07-11 21:34:06 +02:00
lang := glang . Make ( languageTag )
for ! lang . Parent ( ) . IsRoot ( ) {
lang = lang . Parent ( )
}
return lang
}
// ParseLanguages takes a list of language codes and convert them in languages object
func ParseLanguages ( codes [ ] string ) Languages {
var langs Languages
sort . Strings ( codes )
2017-07-06 21:53:13 +02:00
// Now build languages array
for _ , languageTag := range codes {
2017-07-13 00:20:43 +02:00
lang := GetParentTag ( languageTag )
2017-07-11 21:34:06 +02:00
langs = append ( langs , Language { strings . Title ( display . Self . Name ( glang . Make ( languageTag ) ) ) , lang . String ( ) , languageTag } )
2017-05-10 21:45:39 +02:00
}
2017-07-11 21:34:06 +02:00
return langs
2017-05-10 21:45:39 +02:00
}
2017-05-26 12:12:52 +02:00
// GetDefaultTfunc : Gets T func from default language
2017-05-14 21:45:50 +02:00
func GetDefaultTfunc ( ) ( i18n . TranslateFunc , error ) {
return i18n . Tfunc ( defaultLanguage )
}
2017-05-26 12:12:52 +02:00
// GetTfuncAndLanguageFromRequest : Gets the T func and chosen language from the request
2017-06-28 13:42:38 +02:00
func GetTfuncAndLanguageFromRequest ( c * gin . Context ) ( T i18n . TranslateFunc , Tlang * language . Language ) {
2017-05-10 21:45:39 +02:00
userLanguage := ""
2017-06-28 13:42:38 +02:00
user , _ := getCurrentUser ( c )
2017-07-03 01:22:54 +02:00
if user . ID > 0 {
2017-05-11 15:12:19 +02:00
userLanguage = user . Language
2017-05-10 21:45:39 +02:00
}
2017-06-28 13:42:38 +02:00
cookie , err := c . Cookie ( "lang" )
2017-05-07 23:05:41 +02:00
cookieLanguage := ""
if err == nil {
2017-06-28 13:42:38 +02:00
cookieLanguage = cookie
2017-05-07 23:05:41 +02:00
}
2017-05-10 21:45:39 +02:00
2017-05-19 04:55:59 +02:00
// go-i18n supports the format of the Accept-Language header
2017-06-28 13:42:38 +02:00
headerLanguage := c . Request . Header . Get ( "Accept-Language" )
2017-05-19 04:55:59 +02:00
T , Tlang , _ = TfuncAndLanguageWithFallback ( userLanguage , cookieLanguage , headerLanguage )
2017-05-13 00:17:34 +02:00
return
}
2017-05-26 12:12:52 +02:00
// GetTfuncFromRequest : Gets the T func from the request
2017-06-28 13:42:38 +02:00
func GetTfuncFromRequest ( c * gin . Context ) TemplateTfunc {
T , _ := GetTfuncAndLanguageFromRequest ( c )
2017-05-21 00:38:28 +02:00
return func ( id string , args ... interface { } ) template . HTML {
return template . HTML ( fmt . Sprintf ( T ( id ) , args ... ) )
}
2017-05-07 23:05:41 +02:00
}
2017-05-14 21:45:50 +02:00
2017-07-11 21:34:06 +02:00
// GetThemeFromRequest : Gets the user selected theme from the request
2017-06-28 13:42:38 +02:00
func GetThemeFromRequest ( c * gin . Context ) string {
user , _ := getCurrentUser ( c )
2017-07-03 01:22:54 +02:00
if user . ID > 0 {
2017-05-27 19:08:47 +02:00
return user . Theme
}
2017-06-28 13:42:38 +02:00
cookie , err := c . Cookie ( "theme" )
2017-05-27 19:08:47 +02:00
if err == nil {
2017-06-28 13:42:38 +02:00
return cookie
2017-05-27 19:08:47 +02:00
}
return ""
}
2017-07-11 21:34:06 +02:00
// GetMascotFromRequest : Gets the user selected theme from the request
2017-06-28 13:42:38 +02:00
func GetMascotFromRequest ( c * gin . Context ) string {
user , _ := getCurrentUser ( c )
2017-07-03 01:22:54 +02:00
if user . ID > 0 {
2017-05-31 04:21:35 +02:00
return user . Mascot
}
2017-06-28 13:42:38 +02:00
cookie , err := c . Cookie ( "mascot" )
2017-05-31 04:21:35 +02:00
if err == nil {
2017-06-28 13:42:38 +02:00
return cookie
2017-05-31 04:21:35 +02:00
}
return "show"
}
2017-07-11 21:34:06 +02:00
// GetMascotUrlFromRequest : Get the user selected mascot url from the request.
2017-06-05 16:32:48 +02:00
// Returns an empty string if not set.
2017-06-28 13:42:38 +02:00
func GetMascotUrlFromRequest ( c * gin . Context ) string {
user , _ := getCurrentUser ( c )
2017-07-03 01:22:54 +02:00
if user . ID > 0 {
2017-06-05 16:32:48 +02:00
return user . MascotURL
}
2017-06-28 13:42:38 +02:00
cookie , err := c . Cookie ( "mascot_url" )
2017-06-05 16:32:48 +02:00
if err == nil {
2017-06-28 13:42:38 +02:00
return cookie
2017-06-05 16:32:48 +02:00
}
return ""
}
2017-05-31 04:21:35 +02:00
2017-07-03 00:59:59 +02:00
func getCurrentUser ( c * gin . Context ) ( * models . User , error ) {
2017-05-14 21:45:50 +02:00
if userRetriever == nil {
2017-07-03 00:59:59 +02:00
return & models . User { } , errors . New ( "failed to get current user: no user retriever set" )
2017-05-14 21:45:50 +02:00
}
2017-06-28 13:42:38 +02:00
return userRetriever . RetrieveCurrentUser ( c )
2017-05-14 21:45:50 +02:00
}
2017-07-06 21:53:13 +02:00
2017-07-11 21:34:06 +02:00
// Exist evaluate if a language exists or not (language code or language name)
2017-07-06 21:53:13 +02:00
func ( langs Languages ) Exist ( name string ) bool {
for _ , language := range langs {
if language . Code == name || language . Name == name {
return true
}
}
return false
}
2017-07-11 21:34:06 +02:00
// Translate accepts a languageCode in string and translate the language to the language from the language code
func ( lang * Language ) Translate ( languageCode template . HTML ) string {
2017-07-11 22:25:52 +02:00
return Translate ( lang . Tag , string ( languageCode ) )
}
// Translate accepts a languageCode in string and translate the language to the language from the language code in to
func Translate ( languageCode string , to string ) string {
2017-07-13 00:20:43 +02:00
langTranslate := display . Tags ( GetParentTag ( to ) )
2017-07-11 23:34:59 +02:00
translated := langTranslate . Name ( glang . Make ( languageCode ) )
if translated == "Root" {
return ""
}
return translated
2017-07-11 21:34:06 +02:00
}
// Flag reads the language's country code and return the country's flag if national true or the international flag for the language
func ( lang * Language ) Flag ( national bool ) string {
2017-07-13 18:34:19 +02:00
if ! national {
if ! strings . Contains ( lang . Tag , "," ) {
return Flag ( lang . Tag , false )
}
return Flag ( lang . Code , false )
2017-07-11 21:34:06 +02:00
}
return lang . Code
}
2017-07-11 23:34:59 +02:00
// Flag reads the language's country code and return the country's flag if national true or the international flag for the language
2017-07-13 13:33:37 +02:00
func Flag ( languageCode string , parent bool ) string {
lang := glang . Make ( languageCode )
if parent {
lang = GetParentTag ( languageCode )
}
languageSplit := strings . Split ( lang . String ( ) , "-" )
2017-07-11 23:34:59 +02:00
if len ( languageSplit ) > 1 {
return languageSplit [ 1 ]
}
2017-07-13 13:33:37 +02:00
return lang . String ( )
2017-07-11 23:34:59 +02:00
}