libpod/container_internal: Split locale at the first dot, etc.

We're going to feed this into Go's BCP 47 language parser.  Language
tags have the form [1]:

  language
  ["-" script]
  ["-" region]
  *("-" variant)
  *("-" extension)
  ["-" privateuse]

and locales have the form [2]:

  [language[_territory][.codeset][@modifier]]

The modifier is useful for collation, but Go's language-based API
[3] does not provide a way for us to supply it.  This code converts
our locale to a BCP 47 language by stripping the dot and later and
replacing the first underscore, if any, with a hyphen.  This will
avoid errors like [4]:

  WARN[0000] failed to parse language "en_US.UTF-8": language: tag is not well-formed

when feeding language.Parse(...).

[1]: https://tools.ietf.org/html/bcp47#section-2.1
[2]: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_02
[3]: https://github.com/golang/go/issues/25340
[4]: https://github.com/containers/libpod/issues/2494

Signed-off-by: W. Trevor King <wking@tremily.us>
This commit is contained in:
W. Trevor King
2019-03-05 21:15:01 -08:00
parent 40f7843945
commit 69cb8639b4
2 changed files with 61 additions and 7 deletions

View File

@ -34,8 +34,8 @@ const (
)
var (
// localeToLanguage maps from locale values to language tags.
localeToLanguage = map[string]string{
// localeToLanguageMap maps from locale values to language tags.
localeToLanguageMap = map[string]string{
"": "und-u-va-posix",
"c": "und-u-va-posix",
"posix": "und-u-va-posix",
@ -1281,6 +1281,16 @@ func (c *Container) saveSpec(spec *spec.Spec) error {
return nil
}
// localeToLanguage translates POSIX locale strings to BCP 47 language tags.
func localeToLanguage(locale string) string {
locale = strings.Replace(strings.SplitN(locale, ".", 2)[0], "_", "-", 1)
langString, ok := localeToLanguageMap[strings.ToLower(locale)]
if !ok {
langString = locale
}
return langString
}
// Warning: precreate hooks may alter 'config' in place.
func (c *Container) setupOCIHooks(ctx context.Context, config *spec.Spec) (extensionStageHooks map[string][]spec.Hook, err error) {
var locale string
@ -1296,11 +1306,7 @@ func (c *Container) setupOCIHooks(ctx context.Context, config *spec.Spec) (exten
}
}
langString, ok := localeToLanguage[strings.ToLower(locale)]
if !ok {
langString = locale
}
langString := localeToLanguage(locale)
lang, err := language.Parse(langString)
if err != nil {
logrus.Warnf("failed to parse language %q: %s", langString, err)

View File

@ -17,6 +17,54 @@ import (
// hookPath is the path to an example hook executable.
var hookPath string
func TestLocaleToLanguage(t *testing.T) {
for _, testCase := range []struct {
locale string
language string
}{
{
locale: "",
language: "und-u-va-posix",
},
{
locale: "C",
language: "und-u-va-posix",
},
{
locale: "POSIX",
language: "und-u-va-posix",
},
{
locale: "c",
language: "und-u-va-posix",
},
{
locale: "en",
language: "en",
},
{
locale: "en_US",
language: "en-US",
},
{
locale: "en.UTF-8",
language: "en",
},
{
locale: "en_US.UTF-8",
language: "en-US",
},
{
locale: "does-not-exist",
language: "does-not-exist",
},
} {
t.Run(testCase.locale, func(t *testing.T) {
assert.Equal(t, testCase.language, localeToLanguage(testCase.locale))
})
}
}
func TestPostDeleteHooks(t *testing.T) {
ctx := context.Background()
dir, err := ioutil.TempDir("", "libpod_test_")