hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

translationProvider.go (3823B)

    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) Update(d *deps.Deps) error {
   49 	spec := source.NewSourceSpec(d.PathSpec, nil, nil)
   50 
   51 	bundle := i18n.NewBundle(language.English)
   52 	bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal)
   53 	bundle.RegisterUnmarshalFunc("yaml", yaml.Unmarshal)
   54 	bundle.RegisterUnmarshalFunc("yml", yaml.Unmarshal)
   55 	bundle.RegisterUnmarshalFunc("json", json.Unmarshal)
   56 
   57 	// The source dirs are ordered so the most important comes first. Since this is a
   58 	// last key win situation, we have to reverse the iteration order.
   59 	dirs := d.BaseFs.I18n.Dirs
   60 	for i := len(dirs) - 1; i >= 0; i-- {
   61 		dir := dirs[i]
   62 		src := spec.NewFilesystemFromFileMetaInfo(dir)
   63 		files, err := src.Files()
   64 		if err != nil {
   65 			return err
   66 		}
   67 		for _, file := range files {
   68 			if err := addTranslationFile(bundle, file); err != nil {
   69 				return err
   70 			}
   71 		}
   72 	}
   73 
   74 	tp.t = NewTranslator(bundle, d.Cfg, d.Log)
   75 
   76 	d.Translate = tp.t.Func(d.Language.Lang)
   77 
   78 	return nil
   79 }
   80 
   81 const artificialLangTagPrefix = "art-x-"
   82 
   83 func addTranslationFile(bundle *i18n.Bundle, r source.File) error {
   84 	f, err := r.FileInfo().Meta().Open()
   85 	if err != nil {
   86 		return fmt.Errorf("failed to open translations file %q:: %w", r.LogicalName(), err)
   87 	}
   88 
   89 	b := helpers.ReaderToBytes(f)
   90 	f.Close()
   91 
   92 	name := r.LogicalName()
   93 	lang := paths.Filename(name)
   94 	tag := language.Make(lang)
   95 	if tag == language.Und {
   96 		try := artificialLangTagPrefix + lang
   97 		_, err = language.Parse(try)
   98 		if err != nil {
   99 			return fmt.Errorf("%q: %s", try, err)
  100 		}
  101 		name = artificialLangTagPrefix + name
  102 	}
  103 
  104 	_, err = bundle.ParseMessageFileBytes(b, name)
  105 	if err != nil {
  106 		if strings.Contains(err.Error(), "no plural rule") {
  107 			// https://github.com/gohugoio/hugo/issues/7798
  108 			name = artificialLangTagPrefix + name
  109 			_, err = bundle.ParseMessageFileBytes(b, name)
  110 			if err == nil {
  111 				return nil
  112 			}
  113 		}
  114 		return errWithFileContext(fmt.Errorf("failed to load translations: %w", err), r)
  115 	}
  116 
  117 	return nil
  118 }
  119 
  120 // Clone sets the language func for the new language.
  121 func (tp *TranslationProvider) Clone(d *deps.Deps) error {
  122 	d.Translate = tp.t.Func(d.Language.Lang)
  123 
  124 	return nil
  125 }
  126 
  127 func errWithFileContext(inerr error, r source.File) error {
  128 	fim, ok := r.FileInfo().(hugofs.FileMetaInfo)
  129 	if !ok {
  130 		return inerr
  131 	}
  132 
  133 	meta := fim.Meta()
  134 	realFilename := meta.Filename
  135 	f, err := meta.Open()
  136 	if err != nil {
  137 		return inerr
  138 	}
  139 	defer f.Close()
  140 
  141 	return herrors.NewFileErrorFromName(inerr, realFilename).UpdateContent(f, nil)
  142 
  143 }