2019-01-02 06:16:15 +00:00
|
|
|
package data
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"strconv"
|
|
|
|
)
|
|
|
|
|
|
|
|
// WH40K has basic text internationalisation capabilities based on a <lang>.dta
|
|
|
|
// file that maps string IDs to messages
|
|
|
|
type I18n struct {
|
|
|
|
Name string
|
2020-03-26 22:09:26 +00:00
|
|
|
mapping map[int]Entry
|
2019-01-02 06:16:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME: this should be put into the config file maybe, or detected from a list
|
|
|
|
// of possibilities?
|
|
|
|
const (
|
|
|
|
I18nFile = "USEng.dta"
|
|
|
|
)
|
|
|
|
|
2020-03-26 22:09:26 +00:00
|
|
|
// I18n entries may have 1 or 2 items. If two, the first is a menu item and the
|
|
|
|
// second is a help string for that menu item.
|
|
|
|
//
|
|
|
|
// The text or help may contain some magic strings, as mentioned in USEng.data
|
|
|
|
type Entry struct {
|
|
|
|
Text string
|
|
|
|
Help string
|
|
|
|
}
|
|
|
|
|
2019-01-02 06:16:15 +00:00
|
|
|
func LoadI18n(filename string) (*I18n, error) {
|
|
|
|
f, err := os.Open(filename)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
out := &I18n{
|
|
|
|
Name: filepath.Base(filename),
|
2020-03-26 22:09:26 +00:00
|
|
|
mapping: make(map[int]Entry),
|
2019-01-02 06:16:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
scanner := bufio.NewScanner(f)
|
|
|
|
for i := 0; scanner.Scan(); i++ {
|
|
|
|
// Remove comments from lines
|
|
|
|
line := bytes.TrimSpace(bytes.SplitN(scanner.Bytes(), []byte("//"), 2)[0])
|
|
|
|
|
|
|
|
// Ignore empty lines
|
|
|
|
if len(line) == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// Lines are expected to be in this format:
|
|
|
|
// <number> "text that may include \" or not"
|
|
|
|
parts := bytes.SplitN(line, []byte(" "), 2)
|
|
|
|
if len(parts) != 2 {
|
|
|
|
return nil, fmt.Errorf("Bad line in %v at %v: %q", filename, i, scanner.Text())
|
|
|
|
}
|
|
|
|
|
|
|
|
num, err := strconv.Atoi(string(parts[0]))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cut off the leading and trailing quote characters of the string
|
|
|
|
val := parts[1]
|
|
|
|
val = val[1 : len(val)-1]
|
|
|
|
|
|
|
|
// TODO: Replace certain escape characters with their literals?
|
|
|
|
|
2020-03-26 22:09:26 +00:00
|
|
|
if entry, ok := out.mapping[num]; !ok { // first time we've seen this
|
|
|
|
out.mapping[num] = Entry{Text: string(val)}
|
|
|
|
} else { // Second time, the item is help text
|
|
|
|
entry.Help = string(val)
|
|
|
|
out.mapping[num] = entry
|
|
|
|
}
|
2019-01-02 06:16:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := scanner.Err(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *I18n) Len() int {
|
|
|
|
return len(n.mapping)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Puts the internationalized string into `out` if `in` matches a known ID
|
2020-03-26 22:09:26 +00:00
|
|
|
func (n *I18n) ReplaceText(in int, out *string) {
|
|
|
|
if str, ok := n.mapping[in]; ok {
|
|
|
|
*out = str.Text
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *I18n) ReplaceHelp(in int, out *string) {
|
2019-01-02 06:16:15 +00:00
|
|
|
if str, ok := n.mapping[in]; ok {
|
2020-03-26 22:09:26 +00:00
|
|
|
*out = str.Help
|
2019-01-02 06:16:15 +00:00
|
|
|
}
|
|
|
|
}
|