Add support for a content dir set per language

A sample config:

```toml
defaultContentLanguage = "en"
defaultContentLanguageInSubdir = true

[Languages]
[Languages.en]
weight = 10
title = "In English"
languageName = "English"
contentDir = "content/english"

[Languages.nn]
weight = 20
title = "På Norsk"
languageName = "Norsk"
contentDir = "content/norwegian"
```

The value of `contentDir` can be any valid path, even absolute path references. The only restriction is that the content dirs cannot overlap.

The content files will be assigned a language by

1. The placement: `content/norwegian/post/my-post.md` will be read as Norwegian content.
2. The filename: `content/english/post/my-post.nn.md` will be read as Norwegian even if it lives in the English content folder.

The content directories will be merged into a big virtual filesystem with one simple rule: The most specific language file will win.
This means that if both `content/norwegian/post/my-post.md` and `content/english/post/my-post.nn.md` exists, they will be considered duplicates and the version inside `content/norwegian` will win.

Note that translations will be automatically assigned by Hugo by the content file's relative placement, so `content/norwegian/post/my-post.md` will be a translation of `content/english/post/my-post.md`.

If this does not work for you, you can connect the translations together by setting a `translationKey` in the content files' front matter.

Fixes #4523
Fixes #4552
Fixes #4553
This commit is contained in:
Bjørn Erik Pedersen
2018-03-21 17:21:46 +01:00
parent f27977809c
commit eb42774e58
66 changed files with 1819 additions and 556 deletions

View File

@@ -746,9 +746,7 @@ func (s *Site) processPartial(events []fsnotify.Event) (whatChanged, error) {
}
}
if removed && isContentFile(ev.Name) {
path, _ := helpers.GetRelativePath(ev.Name, s.getContentDir(ev.Name))
h.removePageByPath(path)
h.removePageByFilename(ev.Name)
}
sourceReallyChanged = append(sourceReallyChanged, ev)
@@ -890,7 +888,7 @@ func (s *Site) handleDataFile(r source.ReadableFile) error {
func (s *Site) readData(f source.ReadableFile) (interface{}, error) {
file, err := f.Open()
if err != nil {
return nil, err
return nil, fmt.Errorf("readData: failed to open data file: %s", err)
}
defer file.Close()
content := helpers.ReaderToBytes(file)
@@ -1295,9 +1293,9 @@ func (c *contentCaptureResultHandler) handleBundles(d *bundleDirs) {
}
}
func (c *contentCaptureResultHandler) handleCopyFiles(filenames ...string) {
func (c *contentCaptureResultHandler) handleCopyFiles(files ...pathLangFile) {
for _, proc := range c.contentProcessors {
proc.processAssets(filenames)
proc.processAssets(files)
}
}
@@ -1305,15 +1303,16 @@ func (s *Site) readAndProcessContent(filenames ...string) error {
ctx := context.Background()
g, ctx := errgroup.WithContext(ctx)
sourceSpec := source.NewSourceSpec(s.owner.Cfg, s.Fs)
baseDir := s.absContentDir()
defaultContentLanguage := s.SourceSpec.DefaultContentLanguage
contentProcessors := make(map[string]*siteContentProcessor)
var defaultContentProcessor *siteContentProcessor
sites := s.owner.langSite()
for k, v := range sites {
proc := newSiteContentProcessor(ctx, baseDir, len(filenames) > 0, v)
if v.Language.Disabled {
continue
}
proc := newSiteContentProcessor(ctx, len(filenames) > 0, v)
contentProcessors[k] = proc
if k == defaultContentLanguage {
defaultContentProcessor = proc
@@ -1330,6 +1329,8 @@ func (s *Site) readAndProcessContent(filenames ...string) error {
mainHandler := &contentCaptureResultHandler{contentProcessors: contentProcessors, defaultContentProcessor: defaultContentProcessor}
sourceSpec := source.NewSourceSpec(s.PathSpec, s.BaseFs.ContentFs)
if s.running() {
// Need to track changes.
bundleMap = s.owner.ContentChanges
@@ -1339,7 +1340,7 @@ func (s *Site) readAndProcessContent(filenames ...string) error {
handler = mainHandler
}
c := newCapturer(s.Log, sourceSpec, handler, bundleMap, baseDir, filenames...)
c := newCapturer(s.Log, sourceSpec, handler, bundleMap, filenames...)
err1 := c.capture()