Files
Mark D Horn 59302346d6 linter: enable whitespace
Signed-off-by: Mark D Horn <mark.d.horn@intel.com>
2020-04-24 14:53:17 -07:00

243 lines
5.5 KiB
Go

// Copyright © 2020 Intel Corporation
//
// SPDX-License-Identifier: GPL-3.0-only
package language
import (
"bytes"
"fmt"
"os"
"path/filepath"
"sort"
"strings"
"github.com/clearlinux/clr-installer/cmd"
"github.com/clearlinux/clr-installer/log"
"golang.org/x/text/language"
"golang.org/x/text/language/display"
)
// Language represents a system language, containing the locale code and lang tag representation
type Language struct {
Code string
Tag language.Tag
userDefined bool
}
const (
// DefaultLanguage is the default language string
DefaultLanguage = "en_US.UTF-8"
// RequiredBundle the bundle needed to set language other than the default
RequiredBundle = "glibc-locale"
)
// validLanguages stores the list of localized languages
var localizedLanguages = map[string]bool{ // TODO: Remove this when localization is applied for all languages
"en_US.UTF-8": false,
"zh_CN.UTF-8": false,
"es_MX.UTF-8": false,
}
// validLanguages stores the list of all valid, known languages
var validLanguages []*Language
// displayLanguage is The default language to display all language value
var displayLanguage *display.Dictionary
func init() {
displayLanguage = display.English
}
// SetSelectionLanguage changes the default display language
func SetSelectionLanguage(code string) {
// The language package can show the language choices
// in different languages, but it generic per language
// aka no country specific values
parts := strings.Split(code, "_")
shortCode := strings.ToLower(parts[0])
switch shortCode {
case "de":
displayLanguage = display.German
case "en":
displayLanguage = display.English
case "es":
displayLanguage = display.Spanish
case "fr":
displayLanguage = display.French
case "ja":
displayLanguage = display.Japanese
case "pt":
displayLanguage = display.Portuguese
case "zh":
displayLanguage = display.Chinese
default:
displayLanguage = display.English
}
}
// IsUserDefined returns true if the configuration was interactively
// defined by the user
func (l *Language) IsUserDefined() bool {
return l.userDefined
}
// GetConfValues converts a Language to display string which can
// be displayed as the configured value
func (l *Language) GetConfValues() (string, string) {
saveDefault := displayLanguage
SetSelectionLanguage(l.Code)
en := displayLanguage.Tags()
name := en.Name(l.Tag)
displayLanguage = saveDefault
return name, l.Code
}
// MarshalYAML marshals Language into YAML format
func (l *Language) MarshalYAML() (interface{}, error) {
return l.Code, nil
}
// UnmarshalYAML unmarshals Language from YAML format
func (l *Language) UnmarshalYAML(unmarshal func(interface{}) error) error {
var code string
if err := unmarshal(&code); err != nil {
return err
}
l.Code = code
l.userDefined = false
if tag, err := language.Parse(code); err != nil {
l.Tag = tag
}
return nil
}
// Equals compares tow Language instances
func (l *Language) Equals(comp *Language) bool {
if comp == nil {
return false
}
return l.Code == comp.Code
}
func getLangName(tagName string) *Language {
langDict := displayLanguage.Languages()
var lang *Language
if tag, err := language.Parse(tagName); err != nil {
saveTag := tag
simple := strings.Replace(tagName, ".UTF-8", "", -1)
// In case the MustParse fails, recover and fallback
defer func() {
if r := recover(); r != nil {
tag = saveTag
}
}()
tag = language.MustParse(simple)
langName := langDict.Name(tag)
tagString := tag.String()
if tagString != "" && tagString != "und" && langName != "" {
lang = &Language{
Code: tagName,
Tag: tag,
}
} else {
log.Debug("Unable to use language locale '%s'", tagName)
}
} else {
log.Debug("Unable to parse language locale '%s'", tagName)
}
return lang
}
// Load uses localectl to load the currently available locales/Languages
func Load() ([]*Language, error) {
if validLanguages != nil {
return validLanguages, nil
}
validLanguages = []*Language{}
uniqLang := make(map[string]*Language)
w := bytes.NewBuffer(nil)
err := cmd.Run(w, "locale", "-a")
if err != nil {
return nil, err
}
tks := strings.Split(w.String(), "\n")
for _, curr := range tks {
if curr == "" {
continue
}
if lang := getLangName(curr); lang != nil {
uniqLang[curr] = lang
}
}
// Create a sorted order list of keys
sortedKeys := make([]string, 0, len(uniqLang))
for k := range uniqLang {
// TODO: Remove this when localization is applied for all languages
if _, found := localizedLanguages[k]; found {
sortedKeys = append(sortedKeys, k)
}
}
sort.Strings(sortedKeys)
for _, value := range sortedKeys {
validLanguages = append(validLanguages, uniqLang[value])
}
return validLanguages, nil
}
// IsValidLanguage verifies if the given language is valid
func IsValidLanguage(l *Language) bool {
var result = false
allLanguages, err := Load()
if err != nil {
return result
}
for _, curr := range allLanguages {
if curr.Equals(l) {
result = true
}
}
return result
}
// SetTargetLanguage creates a locale locale.conf on the target
func SetTargetLanguage(rootDir string, language string) error {
targetLocaleFile := filepath.Join(rootDir, "/etc/locale.conf")
filehandle, err := os.OpenFile(targetLocaleFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
return fmt.Errorf("Could not create locale file")
}
defer func() {
_ = filehandle.Close()
}()
if _, err := filehandle.Write([]byte("LANG=" + language + "\n")); err != nil {
return fmt.Errorf("Could not write keyboard file")
}
return nil
}