Shivaram Lingamneni пре 5 година
родитељ
комит
c6b9fe0218
7 измењених фајлова са 223 додато и 242 уклоњено
  1. 1
    1
      irc/client.go
  2. 5
    117
      irc/config.go
  3. 22
    3
      irc/getters.go
  4. 16
    22
      irc/handlers.go
  5. 37
    26
      irc/help.go
  6. 131
    39
      irc/languages/languages.go
  7. 11
    34
      irc/server.go

+ 1
- 1
irc/client.go Прегледај датотеку

@@ -131,6 +131,7 @@ func NewClient(server *Server, conn net.Conn, isTLS bool) {
131 131
 		channels:     make(ChannelSet),
132 132
 		ctime:        now,
133 133
 		flags:        modes.NewModeSet(),
134
+		languages:    server.Languages().Default(),
134 135
 		loginThrottle: connection_limits.GenericThrottle{
135 136
 			Duration: config.Accounts.LoginThrottling.Duration,
136 137
 			Limit:    config.Accounts.LoginThrottling.MaxAttempts,
@@ -143,7 +144,6 @@ func NewClient(server *Server, conn net.Conn, isTLS bool) {
143 144
 		nickMaskString: "*", // * is used until actual nick is given
144 145
 		history:        history.NewHistoryBuffer(config.History.ClientLength),
145 146
 	}
146
-	client.languages = server.languages.Default()
147 147
 
148 148
 	remoteAddr := conn.RemoteAddr()
149 149
 	client.realIP = utils.AddrToIP(remoteAddr)

+ 5
- 117
irc/config.go Прегледај датотеку

@@ -7,13 +7,11 @@ package irc
7 7
 
8 8
 import (
9 9
 	"crypto/tls"
10
-	"encoding/json"
11 10
 	"fmt"
12 11
 	"io/ioutil"
13 12
 	"log"
14 13
 	"net"
15 14
 	"os"
16
-	"path/filepath"
17 15
 	"regexp"
18 16
 	"strings"
19 17
 	"time"
@@ -283,9 +281,10 @@ type Config struct {
283 281
 		Enabled bool
284 282
 		Path    string
285 283
 		Default string
286
-		Data    map[string]languages.LangData
287 284
 	}
288 285
 
286
+	languageManager *languages.Manager
287
+
289 288
 	Datastore struct {
290 289
 		Path        string
291 290
 		AutoUpgrade bool
@@ -638,120 +637,9 @@ func LoadConfig(filename string) (config *Config, err error) {
638 637
 	}
639 638
 	config.Server.MaxSendQBytes = int(maxSendQBytes)
640 639
 
641
-	// get language files
642
-	config.Languages.Data = make(map[string]languages.LangData)
643
-	if config.Languages.Enabled {
644
-		files, err := ioutil.ReadDir(config.Languages.Path)
645
-		if err != nil {
646
-			return nil, fmt.Errorf("Could not load language files: %s", err.Error())
647
-		}
648
-
649
-		for _, f := range files {
650
-			// skip dirs
651
-			if f.IsDir() {
652
-				continue
653
-			}
654
-
655
-			// only load core .lang.yaml file, and ignore help/irc files
656
-			name := f.Name()
657
-			lowerName := strings.ToLower(name)
658
-			if !strings.HasSuffix(lowerName, ".lang.yaml") {
659
-				continue
660
-			}
661
-			// don't load our example files in practice
662
-			if strings.HasPrefix(lowerName, "example") {
663
-				continue
664
-			}
665
-
666
-			// load core info file
667
-			data, err = ioutil.ReadFile(filepath.Join(config.Languages.Path, name))
668
-			if err != nil {
669
-				return nil, fmt.Errorf("Could not load language file [%s]: %s", name, err.Error())
670
-			}
671
-
672
-			var langInfo languages.LangData
673
-			err = yaml.Unmarshal(data, &langInfo)
674
-			if err != nil {
675
-				return nil, fmt.Errorf("Could not parse language file [%s]: %s", name, err.Error())
676
-			}
677
-			langInfo.Translations = make(map[string]string)
678
-
679
-			// load actual translation files
680
-			var tlList map[string]string
681
-
682
-			// load irc strings file
683
-			ircName := strings.TrimSuffix(name, ".lang.yaml") + "-irc.lang.json"
684
-
685
-			data, err = ioutil.ReadFile(filepath.Join(config.Languages.Path, ircName))
686
-			if err == nil {
687
-				err = json.Unmarshal(data, &tlList)
688
-				if err != nil {
689
-					return nil, fmt.Errorf("Could not parse language's irc file [%s]: %s", ircName, err.Error())
690
-				}
691
-
692
-				for key, value := range tlList {
693
-					// because of how crowdin works, this is how we skip untranslated lines
694
-					if key == value || value == "" {
695
-						continue
696
-					}
697
-					langInfo.Translations[key] = value
698
-				}
699
-			}
700
-
701
-			// load help strings file
702
-			helpName := strings.TrimSuffix(name, ".lang.yaml") + "-help.lang.json"
703
-
704
-			data, err = ioutil.ReadFile(filepath.Join(config.Languages.Path, helpName))
705
-			if err == nil {
706
-				err = json.Unmarshal(data, &tlList)
707
-				if err != nil {
708
-					return nil, fmt.Errorf("Could not parse language's help file [%s]: %s", helpName, err.Error())
709
-				}
710
-
711
-				for key, value := range tlList {
712
-					// because of how crowdin works, this is how we skip untranslated lines
713
-					if key == value || value == "" {
714
-						continue
715
-					}
716
-					langInfo.Translations[key] = value
717
-				}
718
-			}
719
-
720
-			// confirm that values are correct
721
-			if langInfo.Code == "en" {
722
-				return nil, fmt.Errorf("Cannot have language file with code 'en' (this is the default language using strings inside the server code). If you're making an English variant, name it with a more specific code")
723
-			}
724
-
725
-			if len(langInfo.Translations) == 0 {
726
-				// skip empty translations
727
-				continue
728
-			}
729
-
730
-			if langInfo.Code == "" || langInfo.Name == "" || langInfo.Contributors == "" {
731
-				return nil, fmt.Errorf("Code, name or contributors is empty in language file [%s]", name)
732
-			}
733
-
734
-			// check for duplicate languages
735
-			_, exists := config.Languages.Data[strings.ToLower(langInfo.Code)]
736
-			if exists {
737
-				return nil, fmt.Errorf("Language code [%s] defined twice", langInfo.Code)
738
-			}
739
-
740
-			// and insert into lang info
741
-			config.Languages.Data[strings.ToLower(langInfo.Code)] = langInfo
742
-		}
743
-
744
-		// confirm that default language exists
745
-		if config.Languages.Default == "" {
746
-			config.Languages.Default = "en"
747
-		} else {
748
-			config.Languages.Default = strings.ToLower(config.Languages.Default)
749
-		}
750
-
751
-		_, exists := config.Languages.Data[config.Languages.Default]
752
-		if config.Languages.Default != "en" && !exists {
753
-			return nil, fmt.Errorf("Cannot find default language [%s]", config.Languages.Default)
754
-		}
640
+	config.languageManager, err = languages.NewManager(config.Languages.Enabled, config.Languages.Path, config.Languages.Default)
641
+	if err != nil {
642
+		return nil, fmt.Errorf("Could not load languages: %s", err.Error())
755 643
 	}
756 644
 
757 645
 	// RecoverFromErrors defaults to true

+ 22
- 3
irc/getters.go Прегледај датотеку

@@ -5,13 +5,15 @@ package irc
5 5
 
6 6
 import (
7 7
 	"github.com/oragono/oragono/irc/isupport"
8
+	"github.com/oragono/oragono/irc/languages"
8 9
 	"github.com/oragono/oragono/irc/modes"
9 10
 )
10 11
 
11
-func (server *Server) Config() *Config {
12
+func (server *Server) Config() (config *Config) {
12 13
 	server.configurableStateMutex.RLock()
13
-	defer server.configurableStateMutex.RUnlock()
14
-	return server.config
14
+	config = server.config
15
+	server.configurableStateMutex.RUnlock()
16
+	return
15 17
 }
16 18
 
17 19
 func (server *Server) ISupport() *isupport.List {
@@ -58,6 +60,10 @@ func (server *Server) GetOperator(name string) (oper *Oper) {
58 60
 	return server.config.operators[name]
59 61
 }
60 62
 
63
+func (server *Server) Languages() (lm *languages.Manager) {
64
+	return server.Config().languageManager
65
+}
66
+
61 67
 func (client *Client) Nick() string {
62 68
 	client.stateMutex.RLock()
63 69
 	defer client.stateMutex.RUnlock()
@@ -191,6 +197,19 @@ func (client *Client) SetAccountName(account string) (changed bool) {
191 197
 	return
192 198
 }
193 199
 
200
+func (client *Client) Languages() (languages []string) {
201
+	client.stateMutex.RLock()
202
+	languages = client.languages
203
+	client.stateMutex.RUnlock()
204
+	return languages
205
+}
206
+
207
+func (client *Client) SetLanguages(languages []string) {
208
+	client.stateMutex.Lock()
209
+	client.languages = languages
210
+	client.stateMutex.Unlock()
211
+}
212
+
194 213
 func (client *Client) HasMode(mode modes.Mode) bool {
195 214
 	// client.flags has its own synch
196 215
 	return client.flags.HasMode(mode)

+ 16
- 22
irc/handlers.go Прегледај датотеку

@@ -988,11 +988,7 @@ Get an explanation of <argument>, or "index" for a list of help topics.`), rb)
988 988
 
989 989
 	// handle index
990 990
 	if argument == "index" {
991
-		if client.HasMode(modes.Operator) {
992
-			client.sendHelp("HELP", GetHelpIndex(client.languages, HelpIndexOpers), rb)
993
-		} else {
994
-			client.sendHelp("HELP", GetHelpIndex(client.languages, HelpIndex), rb)
995
-		}
991
+		client.sendHelp("HELP", server.helpIndexManager.GetIndex(client.Languages(), client.HasMode(modes.Operator)), rb)
996 992
 		return false
997 993
 	}
998 994
 
@@ -1088,7 +1084,7 @@ func infoHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
1088 1084
 		rb.Add(nil, server.name, RPL_INFO, client.nick, line)
1089 1085
 	}
1090 1086
 	// show translators for languages other than good ole' regular English
1091
-	tlines := server.languages.Translators()
1087
+	tlines := server.Languages().Translators()
1092 1088
 	if 0 < len(tlines) {
1093 1089
 		rb.Add(nil, server.name, RPL_INFO, client.nick, client.t("Translators:"))
1094 1090
 		for _, line := range tlines {
@@ -1422,12 +1418,14 @@ func klineHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Res
1422 1418
 
1423 1419
 // LANGUAGE <code>{ <code>}
1424 1420
 func languageHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
1421
+	nick := client.Nick()
1425 1422
 	alreadyDoneLanguages := make(map[string]bool)
1426 1423
 	var appliedLanguages []string
1427 1424
 
1428
-	supportedLanguagesCount := server.languages.Count()
1425
+	lm := server.Languages()
1426
+	supportedLanguagesCount := lm.Count()
1429 1427
 	if supportedLanguagesCount < len(msg.Params) {
1430
-		rb.Add(nil, client.server.name, ERR_TOOMANYLANGUAGES, client.nick, strconv.Itoa(supportedLanguagesCount), client.t("You specified too many languages"))
1428
+		rb.Add(nil, client.server.name, ERR_TOOMANYLANGUAGES, nick, strconv.Itoa(supportedLanguagesCount), client.t("You specified too many languages"))
1431 1429
 		return false
1432 1430
 	}
1433 1431
 
@@ -1441,9 +1439,9 @@ func languageHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *
1441 1439
 			continue
1442 1440
 		}
1443 1441
 
1444
-		_, exists := server.languages.Info[value]
1442
+		_, exists := lm.Languages[value]
1445 1443
 		if !exists {
1446
-			rb.Add(nil, client.server.name, ERR_NOLANGUAGE, client.nick, client.t("Languages are not supported by this server"))
1444
+			rb.Add(nil, client.server.name, ERR_NOLANGUAGE, nick, fmt.Sprintf(client.t("Language %s is not supported by this server"), value))
1447 1445
 			return false
1448 1446
 		}
1449 1447
 
@@ -1456,20 +1454,16 @@ func languageHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *
1456 1454
 		appliedLanguages = append(appliedLanguages, value)
1457 1455
 	}
1458 1456
 
1459
-	client.stateMutex.Lock()
1460
-	if len(appliedLanguages) == 1 && appliedLanguages[0] == "en" {
1461
-		// premature optimisation ahoy!
1462
-		client.languages = []string{}
1463
-	} else {
1464
-		client.languages = appliedLanguages
1457
+	var langsToSet []string
1458
+	if !(len(appliedLanguages) == 1 && appliedLanguages[0] == "en") {
1459
+		langsToSet = appliedLanguages
1465 1460
 	}
1466
-	client.stateMutex.Unlock()
1461
+	client.SetLanguages(langsToSet)
1467 1462
 
1468
-	params := []string{client.nick}
1469
-	for _, lang := range appliedLanguages {
1470
-		params = append(params, lang)
1471
-	}
1472
-	params = append(params, client.t("Language preferences have been set"))
1463
+	params := make([]string, len(appliedLanguages)+2)
1464
+	params[0] = nick
1465
+	copy(params[1:], appliedLanguages)
1466
+	params[len(params)-1] = client.t("Language preferences have been set")
1473 1467
 
1474 1468
 	rb.Add(nil, client.server.name, RPL_YOURLANGUAGESARE, params...)
1475 1469
 

+ 37
- 26
irc/help.go Прегледај датотеку

@@ -7,6 +7,7 @@ import (
7 7
 	"fmt"
8 8
 	"sort"
9 9
 	"strings"
10
+	"sync"
10 11
 
11 12
 	"github.com/oragono/oragono/irc/languages"
12 13
 )
@@ -603,13 +604,15 @@ func modesTextGenerator(client *Client) string {
603 604
 	return client.t(cmodeHelpText) + "\n\n" + client.t(umodeHelpText)
604 605
 }
605 606
 
606
-// HelpIndex contains the list of all help topics for regular users.
607
-var HelpIndex map[string]string
607
+type HelpIndexManager struct {
608
+	sync.RWMutex // tier 1
608 609
 
609
-// HelpIndexOpers contains the list of all help topics for opers.
610
-var HelpIndexOpers map[string]string
610
+	langToIndex     map[string]string
611
+	langToOperIndex map[string]string
612
+}
611 613
 
612 614
 // GenerateHelpIndex is used to generate HelpIndex.
615
+// Returns: a map from language code to the help index in that language.
613 616
 func GenerateHelpIndex(lm *languages.Manager, forOpers bool) map[string]string {
614 617
 	// generate the help entry lists
615 618
 	var commands, isupport, information []string
@@ -658,10 +661,7 @@ Information:
658 661
 
659 662
 	newHelpIndex["en"] = fmt.Sprintf(defaultHelpIndex, commandsString, isupportString, informationString)
660 663
 
661
-	lm.RLock()
662
-	defer lm.RUnlock()
663
-
664
-	for langCode := range lm.Info {
664
+	for langCode := range lm.Languages {
665 665
 		translatedHelpIndex := lm.Translate([]string{langCode}, defaultHelpIndex)
666 666
 		if translatedHelpIndex != defaultHelpIndex {
667 667
 			newHelpIndex[langCode] = fmt.Sprintf(translatedHelpIndex, commandsString, isupportString, informationString)
@@ -671,22 +671,16 @@ Information:
671 671
 	return newHelpIndex
672 672
 }
673 673
 
674
-// GenerateHelpIndices generates our help indexes and confirms we have HELP entries for every command.
675
-func GenerateHelpIndices(lm *languages.Manager) error {
676
-	// startup check that we have HELP entries for every command
677
-	if len(HelpIndex) == 0 && len(HelpIndexOpers) == 0 {
678
-		for name := range Commands {
679
-			_, exists := Help[strings.ToLower(name)]
680
-			if !exists {
681
-				return fmt.Errorf("Help entry does not exist for command %s", name)
682
-			}
683
-		}
684
-	}
685
-
674
+// GenerateIndices regenerates our help indexes for each currently enabled language.
675
+func (hm *HelpIndexManager) GenerateIndices(lm *languages.Manager) {
686 676
 	// generate help indexes
687
-	HelpIndex = GenerateHelpIndex(lm, false)
688
-	HelpIndexOpers = GenerateHelpIndex(lm, true)
689
-	return nil
677
+	langToIndex := GenerateHelpIndex(lm, false)
678
+	langToOperIndex := GenerateHelpIndex(lm, true)
679
+
680
+	hm.Lock()
681
+	defer hm.Unlock()
682
+	hm.langToIndex = langToIndex
683
+	hm.langToOperIndex = langToOperIndex
690 684
 }
691 685
 
692 686
 // sendHelp sends the client help of the given string.
@@ -709,13 +703,30 @@ func (client *Client) sendHelp(name string, text string, rb *ResponseBuffer) {
709 703
 }
710 704
 
711 705
 // GetHelpIndex returns the help index for the given language.
712
-func GetHelpIndex(languages []string, helpIndex map[string]string) string {
706
+func (hm *HelpIndexManager) GetIndex(languages []string, oper bool) string {
707
+	hm.RLock()
708
+	langToIndex := hm.langToIndex
709
+	if oper {
710
+		langToIndex = hm.langToOperIndex
711
+	}
712
+	hm.RUnlock()
713
+
713 714
 	for _, lang := range languages {
714
-		index, exists := helpIndex[lang]
715
+		index, exists := langToIndex[lang]
715 716
 		if exists {
716 717
 			return index
717 718
 		}
718 719
 	}
719 720
 	// 'en' always exists
720
-	return helpIndex["en"]
721
+	return langToIndex["en"]
722
+}
723
+
724
+func init() {
725
+	// startup check that we have HELP entries for every command
726
+	for name := range Commands {
727
+		_, exists := Help[strings.ToLower(name)]
728
+		if !exists {
729
+			panic(fmt.Sprintf("Help entry does not exist for command %s", name))
730
+		}
731
+	}
721 732
 }

+ 131
- 39
irc/languages/languages.go Прегледај датотеку

@@ -4,10 +4,25 @@
4 4
 package languages
5 5
 
6 6
 import (
7
+	"encoding/json"
7 8
 	"fmt"
9
+	"io/ioutil"
10
+	"path/filepath"
8 11
 	"sort"
12
+	"strconv"
9 13
 	"strings"
10
-	"sync"
14
+
15
+	"gopkg.in/yaml.v2"
16
+)
17
+
18
+const (
19
+	// for a language (e.g., `fi-FI`) to be supported
20
+	// it must have a metadata file named, e.g., `fi-FI.lang.yaml`
21
+	metadataFileSuffix = ".lang.yaml"
22
+)
23
+
24
+var (
25
+	stringsFileSuffixes = []string{"-irc.lang.json", "-help.lang.json", "-nickserv.lang.json", "-hostserv.lang.json", "-chanserv.lang.json"}
11 26
 )
12 27
 
13 28
 // LangData is the data contained in a language file.
@@ -16,76 +31,144 @@ type LangData struct {
16 31
 	Code         string
17 32
 	Contributors string
18 33
 	Incomplete   bool
19
-	Translations map[string]string
20 34
 }
21 35
 
22 36
 // Manager manages our languages and provides translation abilities.
23 37
 type Manager struct {
24
-	sync.RWMutex
25
-	Info         map[string]LangData
38
+	Languages    map[string]LangData
26 39
 	translations map[string]map[string]string
27 40
 	defaultLang  string
28 41
 }
29 42
 
30 43
 // NewManager returns a new Manager.
31
-func NewManager(defaultLang string, languageData map[string]LangData) *Manager {
32
-	lm := Manager{
33
-		Info:         make(map[string]LangData),
44
+func NewManager(enabled bool, path string, defaultLang string) (lm *Manager, err error) {
45
+	lm = &Manager{
46
+		Languages:    make(map[string]LangData),
34 47
 		translations: make(map[string]map[string]string),
35 48
 		defaultLang:  defaultLang,
36 49
 	}
37 50
 
38 51
 	// make fake "en" info
39
-	lm.Info["en"] = LangData{
52
+	lm.Languages["en"] = LangData{
40 53
 		Code:         "en",
41 54
 		Name:         "English",
42 55
 		Contributors: "Oragono contributors and the IRC community",
43 56
 	}
44 57
 
45
-	// load language data
46
-	for name, data := range languageData {
47
-		lm.Info[name] = data
58
+	if enabled {
59
+		err = lm.loadData(path)
60
+		if err == nil {
61
+			// successful load, check that defaultLang is sane
62
+			_, ok := lm.Languages[lm.defaultLang]
63
+			if !ok {
64
+				err = fmt.Errorf("Cannot find default language [%s]", lm.defaultLang)
65
+			}
66
+		}
67
+	} else {
68
+		lm.defaultLang = "en"
69
+	}
70
+
71
+	return
72
+}
73
+
74
+func (lm *Manager) loadData(path string) (err error) {
75
+	files, err := ioutil.ReadDir(path)
76
+	if err != nil {
77
+		return
78
+	}
79
+
80
+	// 1. for each language that has a ${langcode}.lang.yaml in the languages path
81
+	// 2. load ${langcode}.lang.yaml
82
+	// 3. load ${langcode}-irc.lang.json and friends as the translations
83
+	for _, f := range files {
84
+		if f.IsDir() {
85
+			continue
86
+		}
87
+		// glob up *.lang.yaml in the directory
88
+		name := f.Name()
89
+		if !strings.HasSuffix(name, metadataFileSuffix) {
90
+			continue
91
+		}
92
+		prefix := strings.TrimSuffix(name, metadataFileSuffix)
93
+
94
+		// load, e.g., `zh-CN.lang.yaml`
95
+		var data []byte
96
+		data, err = ioutil.ReadFile(filepath.Join(path, name))
97
+		if err != nil {
98
+			return
99
+		}
100
+		var langInfo LangData
101
+		err = yaml.Unmarshal(data, &langInfo)
102
+		if err != nil {
103
+			return err
104
+		}
105
+
106
+		if langInfo.Code == "en" {
107
+			return fmt.Errorf("Cannot have language file with code 'en' (this is the default language using strings inside the server code). If you're making an English variant, name it with a more specific code")
108
+		}
109
+
110
+		// check for duplicate languages
111
+		_, exists := lm.Languages[strings.ToLower(langInfo.Code)]
112
+		if exists {
113
+			return fmt.Errorf("Language code [%s] defined twice", langInfo.Code)
114
+		}
115
+
116
+		// slurp up all translation files with `prefix` into a single translation map
117
+		translations := make(map[string]string)
118
+		for _, translationSuffix := range stringsFileSuffixes {
119
+			stringsFilePath := filepath.Join(path, prefix+translationSuffix)
120
+			data, err = ioutil.ReadFile(stringsFilePath)
121
+			if err != nil {
122
+				continue // skip missing paths
123
+			}
124
+			var tlList map[string]string
125
+			err = json.Unmarshal(data, &tlList)
126
+			if err != nil {
127
+				return fmt.Errorf("invalid json for translation file %s: %s", stringsFilePath, err.Error())
128
+			}
48 129
 
49
-		// make sure we don't include empty translations
50
-		lm.translations[name] = make(map[string]string)
51
-		for key, value := range data.Translations {
52
-			if strings.TrimSpace(value) == "" {
53
-				continue
130
+			for key, value := range tlList {
131
+				// because of how crowdin works, this is how we skip untranslated lines
132
+				if key == value || strings.TrimSpace(value) == "" {
133
+					continue
134
+				}
135
+				translations[key] = value
54 136
 			}
55
-			lm.translations[name][key] = value
56 137
 		}
138
+
139
+		if len(translations) == 0 {
140
+			// skip empty translations
141
+			continue
142
+		}
143
+
144
+		// sanity check the language definition from the yaml file
145
+		if langInfo.Code == "" || langInfo.Name == "" || langInfo.Contributors == "" {
146
+			return fmt.Errorf("Code, name or contributors is empty in language file [%s]", name)
147
+		}
148
+
149
+		key := strings.ToLower(langInfo.Code)
150
+		lm.Languages[key] = langInfo
151
+		lm.translations[key] = translations
57 152
 	}
58 153
 
59
-	return &lm
154
+	return nil
60 155
 }
61 156
 
62 157
 // Default returns the default languages.
63 158
 func (lm *Manager) Default() []string {
64
-	lm.RLock()
65
-	defer lm.RUnlock()
66
-
67
-	if lm.defaultLang == "" {
68
-		return []string{}
69
-	}
70 159
 	return []string{lm.defaultLang}
71 160
 }
72 161
 
73 162
 // Count returns how many languages we have.
74 163
 func (lm *Manager) Count() int {
75
-	lm.RLock()
76
-	defer lm.RUnlock()
77
-
78
-	return len(lm.Info)
164
+	return len(lm.Languages)
79 165
 }
80 166
 
81 167
 // Translators returns the languages we have and the translators.
82 168
 func (lm *Manager) Translators() []string {
83
-	lm.RLock()
84
-	defer lm.RUnlock()
85
-
86 169
 	var tlist sort.StringSlice
87 170
 
88
-	for _, info := range lm.Info {
171
+	for _, info := range lm.Languages {
89 172
 		if info.Code == "en" {
90 173
 			continue
91 174
 		}
@@ -98,12 +181,9 @@ func (lm *Manager) Translators() []string {
98 181
 
99 182
 // Codes returns the proper language codes for the given casefolded language codes.
100 183
 func (lm *Manager) Codes(codes []string) []string {
101
-	lm.RLock()
102
-	defer lm.RUnlock()
103
-
104 184
 	var newCodes []string
105 185
 	for _, code := range codes {
106
-		info, exists := lm.Info[code]
186
+		info, exists := lm.Languages[code]
107 187
 		if exists {
108 188
 			newCodes = append(newCodes, info.Code)
109 189
 		}
@@ -123,9 +203,6 @@ func (lm *Manager) Translate(languages []string, originalString string) string {
123 203
 		return originalString
124 204
 	}
125 205
 
126
-	lm.RLock()
127
-	defer lm.RUnlock()
128
-
129 206
 	for _, lang := range languages {
130 207
 		lang = strings.ToLower(lang)
131 208
 		if lang == "en" {
@@ -149,3 +226,18 @@ func (lm *Manager) Translate(languages []string, originalString string) string {
149 226
 	// didn't find any translation
150 227
 	return originalString
151 228
 }
229
+
230
+func (lm *Manager) CapValue() string {
231
+	langCodes := make([]string, len(lm.Languages)+1)
232
+	langCodes[0] = strconv.Itoa(len(lm.Languages))
233
+	i := 1
234
+	for _, info := range lm.Languages {
235
+		codeToken := info.Code
236
+		if info.Incomplete {
237
+			codeToken = "~" + info.Code
238
+		}
239
+		langCodes[i] = codeToken
240
+		i += 1
241
+	}
242
+	return strings.Join(langCodes, ",")
243
+}

+ 11
- 34
irc/server.go Прегледај датотеку

@@ -25,7 +25,6 @@ import (
25 25
 	"github.com/oragono/oragono/irc/caps"
26 26
 	"github.com/oragono/oragono/irc/connection_limits"
27 27
 	"github.com/oragono/oragono/irc/isupport"
28
-	"github.com/oragono/oragono/irc/languages"
29 28
 	"github.com/oragono/oragono/irc/logger"
30 29
 	"github.com/oragono/oragono/irc/modes"
31 30
 	"github.com/oragono/oragono/irc/sno"
@@ -76,9 +75,9 @@ type Server struct {
76 75
 	connectionThrottler    *connection_limits.Throttler
77 76
 	ctime                  time.Time
78 77
 	dlines                 *DLineManager
78
+	helpIndexManager       HelpIndexManager
79 79
 	isupport               *isupport.List
80 80
 	klines                 *KLineManager
81
-	languages              *languages.Manager
82 81
 	listeners              map[string]*ListenerWrapper
83 82
 	logger                 *logger.Manager
84 83
 	monitorManager         *MonitorManager
@@ -119,7 +118,6 @@ func NewServer(config *Config, logger *logger.Manager) (*Server, error) {
119 118
 		clients:             NewClientManager(),
120 119
 		connectionLimiter:   connection_limits.NewLimiter(),
121 120
 		connectionThrottler: connection_limits.NewThrottler(),
122
-		languages:           languages.NewManager(config.Languages.Default, config.Languages.Data),
123 121
 		listeners:           make(map[string]*ListenerWrapper),
124 122
 		logger:              logger,
125 123
 		monitorManager:      NewMonitorManager(),
@@ -137,11 +135,6 @@ func NewServer(config *Config, logger *logger.Manager) (*Server, error) {
137 135
 		return nil, err
138 136
 	}
139 137
 
140
-	// generate help info
141
-	if err := GenerateHelpIndices(server.languages); err != nil {
142
-		return nil, err
143
-	}
144
-
145 138
 	// Attempt to clean up when receiving these signals.
146 139
 	signal.Notify(server.signals, ServerExitSignals...)
147 140
 	signal.Notify(server.rehashSignal, syscall.SIGHUP)
@@ -468,11 +461,9 @@ func (server *Server) tryRegister(c *Client) {
468 461
 
469 462
 // t returns the translated version of the given string, based on the languages configured by the client.
470 463
 func (client *Client) t(originalString string) string {
471
-	// grab this mutex to protect client.languages
472
-	client.stateMutex.RLock()
473
-	defer client.stateMutex.RUnlock()
474
-
475
-	return client.server.languages.Translate(client.languages, originalString)
464
+	// TODO(slingamn) investigate a fast path for this, using an atomic load to see if translation is disabled
465
+	languages := client.Languages()
466
+	return client.server.Languages().Translate(languages, originalString)
476 467
 }
477 468
 
478 469
 // MOTD serves the Message of the Day.
@@ -536,9 +527,10 @@ func (client *Client) getWhoisOf(target *Client, rb *ResponseBuffer) {
536 527
 		rb.Add(nil, client.server.name, RPL_WHOISBOT, cnick, tnick, ircfmt.Unescape(fmt.Sprintf(client.t("is a $bBot$b on %s"), client.server.Config().Network.Name)))
537 528
 	}
538 529
 
539
-	if 0 < len(target.languages) {
530
+	tLanguages := target.Languages()
531
+	if 0 < len(tLanguages) {
540 532
 		params := []string{cnick, tnick}
541
-		for _, str := range client.server.languages.Codes(target.languages) {
533
+		for _, str := range client.server.Languages().Codes(tLanguages) {
542 534
 			params = append(params, str)
543 535
 		}
544 536
 		params = append(params, client.t("can speak these languages"))
@@ -655,31 +647,16 @@ func (server *Server) applyConfig(config *Config, initial bool) (err error) {
655 647
 	updatedCaps := caps.NewSet()
656 648
 
657 649
 	// Translations
658
-	currentLanguageValue, _ := CapValues.Get(caps.Languages)
659
-
660
-	langCodes := []string{strconv.Itoa(len(config.Languages.Data) + 1), "en"}
661
-	for _, info := range config.Languages.Data {
662
-		if info.Incomplete {
663
-			langCodes = append(langCodes, "~"+info.Code)
664
-		} else {
665
-			langCodes = append(langCodes, info.Code)
666
-		}
667
-	}
668
-	newLanguageValue := strings.Join(langCodes, ",")
669
-	server.logger.Debug("server", "Languages:", newLanguageValue)
650
+	server.logger.Debug("server", "Regenerating HELP indexes for new languages")
651
+	server.helpIndexManager.GenerateIndices(config.languageManager)
670 652
 
653
+	currentLanguageValue, _ := CapValues.Get(caps.Languages)
654
+	newLanguageValue := config.languageManager.CapValue()
671 655
 	if currentLanguageValue != newLanguageValue {
672 656
 		updatedCaps.Add(caps.Languages)
673 657
 		CapValues.Set(caps.Languages, newLanguageValue)
674 658
 	}
675 659
 
676
-	lm := languages.NewManager(config.Languages.Default, config.Languages.Data)
677
-
678
-	server.logger.Debug("server", "Regenerating HELP indexes for new languages")
679
-	GenerateHelpIndices(lm)
680
-
681
-	server.languages = lm
682
-
683 660
 	// SASL
684 661
 	authPreviouslyEnabled := oldConfig != nil && oldConfig.Accounts.AuthenticationEnabled
685 662
 	if config.Accounts.AuthenticationEnabled && !authPreviouslyEnabled {

Loading…
Откажи
Сачувај