github.com/anakojm/hugo-katex@v0.0.0-20231023141351-42d6f5de9c0b/langs/i18n/translationProvider.go (about) 1 // Copyright 2017 The Hugo Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package i18n 15 16 import ( 17 "encoding/json" 18 "fmt" 19 "strings" 20 21 "github.com/gohugoio/hugo/common/paths" 22 23 "github.com/gohugoio/hugo/common/herrors" 24 "golang.org/x/text/language" 25 yaml "gopkg.in/yaml.v2" 26 27 "github.com/gohugoio/go-i18n/v2/i18n" 28 "github.com/gohugoio/hugo/helpers" 29 toml "github.com/pelletier/go-toml/v2" 30 31 "github.com/gohugoio/hugo/deps" 32 "github.com/gohugoio/hugo/hugofs" 33 "github.com/gohugoio/hugo/source" 34 ) 35 36 // TranslationProvider provides translation handling, i.e. loading 37 // of bundles etc. 38 type TranslationProvider struct { 39 t Translator 40 } 41 42 // NewTranslationProvider creates a new translation provider. 43 func NewTranslationProvider() *TranslationProvider { 44 return &TranslationProvider{} 45 } 46 47 // Update updates the i18n func in the provided Deps. 48 func (tp *TranslationProvider) NewResource(dst *deps.Deps) error { 49 spec := source.NewSourceSpec(dst.PathSpec, nil, nil) 50 51 var defaultLangTag, err = language.Parse(dst.Conf.DefaultContentLanguage()) 52 if err != nil { 53 defaultLangTag = language.English 54 } 55 bundle := i18n.NewBundle(defaultLangTag) 56 57 bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal) 58 bundle.RegisterUnmarshalFunc("yaml", yaml.Unmarshal) 59 bundle.RegisterUnmarshalFunc("yml", yaml.Unmarshal) 60 bundle.RegisterUnmarshalFunc("json", json.Unmarshal) 61 62 // The source dirs are ordered so the most important comes first. Since this is a 63 // last key win situation, we have to reverse the iteration order. 64 dirs := dst.BaseFs.I18n.Dirs 65 for i := len(dirs) - 1; i >= 0; i-- { 66 dir := dirs[i] 67 src := spec.NewFilesystemFromFileMetaInfo(dir) 68 files, err := src.Files() 69 if err != nil { 70 return err 71 } 72 for _, file := range files { 73 if err := addTranslationFile(bundle, file); err != nil { 74 return err 75 } 76 } 77 } 78 79 tp.t = NewTranslator(bundle, dst.Conf, dst.Log) 80 81 dst.Translate = tp.t.Func(dst.Conf.Language().Lang) 82 83 return nil 84 85 } 86 87 const artificialLangTagPrefix = "art-x-" 88 89 func addTranslationFile(bundle *i18n.Bundle, r source.File) error { 90 f, err := r.FileInfo().Meta().Open() 91 if err != nil { 92 return fmt.Errorf("failed to open translations file %q:: %w", r.LogicalName(), err) 93 } 94 95 b := helpers.ReaderToBytes(f) 96 f.Close() 97 98 name := r.LogicalName() 99 lang := paths.Filename(name) 100 tag := language.Make(lang) 101 if tag == language.Und { 102 try := artificialLangTagPrefix + lang 103 _, err = language.Parse(try) 104 if err != nil { 105 return fmt.Errorf("%q: %s", try, err) 106 } 107 name = artificialLangTagPrefix + name 108 } 109 110 _, err = bundle.ParseMessageFileBytes(b, name) 111 if err != nil { 112 if strings.Contains(err.Error(), "no plural rule") { 113 // https://github.com/gohugoio/hugo/issues/7798 114 name = artificialLangTagPrefix + name 115 _, err = bundle.ParseMessageFileBytes(b, name) 116 if err == nil { 117 return nil 118 } 119 } 120 return errWithFileContext(fmt.Errorf("failed to load translations: %w", err), r) 121 } 122 123 return nil 124 } 125 126 // CloneResource sets the language func for the new language. 127 func (tp *TranslationProvider) CloneResource(dst, src *deps.Deps) error { 128 dst.Translate = tp.t.Func(dst.Conf.Language().Lang) 129 return nil 130 } 131 132 func errWithFileContext(inerr error, r source.File) error { 133 fim, ok := r.FileInfo().(hugofs.FileMetaInfo) 134 if !ok { 135 return inerr 136 } 137 138 meta := fim.Meta() 139 realFilename := meta.Filename 140 f, err := meta.Open() 141 if err != nil { 142 return inerr 143 } 144 defer f.Close() 145 146 return herrors.NewFileErrorFromName(inerr, realFilename).UpdateContent(f, nil) 147 148 }