summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--commands/benchmark.go4
-rw-r--r--commands/hugo.go73
-rw-r--r--commands/list.go6
-rw-r--r--commands/multilingual.go41
-rw-r--r--docs/content/content/multilingual.md238
-rw-r--r--docs/content/taxonomies/displaying.md8
-rw-r--r--docs/content/taxonomies/ordering.md4
-rw-r--r--docs/content/templates/functions.md26
-rw-r--r--docs/content/templates/terms.md8
-rw-r--r--docs/content/templates/variables.md27
-rw-r--r--helpers/path.go6
-rw-r--r--hugolib/embedded_shortcodes_test.go4
-rw-r--r--hugolib/i18n.go36
-rw-r--r--hugolib/menu_test.go8
-rw-r--r--hugolib/multilingual.go48
-rw-r--r--hugolib/page.go67
-rw-r--r--hugolib/permalinks.go2
-rw-r--r--hugolib/planner.go2
-rw-r--r--hugolib/robotstxt_test.go8
-rw-r--r--hugolib/rss_test.go8
-rw-r--r--hugolib/site.go226
-rw-r--r--hugolib/site_test.go242
-rw-r--r--hugolib/site_url_test.go7
-rw-r--r--hugolib/sitemap_test.go8
-rw-r--r--hugolib/taxonomy_test.go12
-rw-r--r--hugolib/translations.go59
-rw-r--r--source/file.go28
-rw-r--r--tpl/template_funcs.go2
-rw-r--r--tpl/template_i18n.go47
29 files changed, 1013 insertions, 242 deletions
diff --git a/commands/benchmark.go b/commands/benchmark.go
index 3f5aa6ef3..53d2c8e9e 100644
--- a/commands/benchmark.go
+++ b/commands/benchmark.go
@@ -57,7 +57,7 @@ func benchmark(cmd *cobra.Command, args []string) error {
return err
}
for i := 0; i < benchmarkTimes; i++ {
- MainSite = nil
+ MainSites = nil
_ = buildSite()
}
pprof.WriteHeapProfile(f)
@@ -76,7 +76,7 @@ func benchmark(cmd *cobra.Command, args []string) error {
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
for i := 0; i < benchmarkTimes; i++ {
- MainSite = nil
+ MainSites = nil
_ = buildSite()
}
}
diff --git a/commands/hugo.go b/commands/hugo.go
index 7afd78a9d..57a426458 100644
--- a/commands/hugo.go
+++ b/commands/hugo.go
@@ -46,10 +46,10 @@ import (
"github.com/spf13/viper"
)
-// MainSite represents the Hugo site to build. This variable is exported as it
+// MainSites represents the Hugo sites to build. This variable is exported as it
// is used by at least one external library (the Hugo caddy plugin). We should
// provide a cleaner external API, but until then, this is it.
-var MainSite *hugolib.Site
+var MainSites map[string]*hugolib.Site
// Reset resets Hugo ready for a new full build. This is mainly only useful
// for benchmark testing etc. via the CLI commands.
@@ -287,6 +287,7 @@ func loadDefaultSettings() {
viper.SetDefault("ArchetypeDir", "archetypes")
viper.SetDefault("PublishDir", "public")
viper.SetDefault("DataDir", "data")
+ viper.SetDefault("I18nDir", "i18n")
viper.SetDefault("ThemesDir", "themes")
viper.SetDefault("DefaultLayout", "post")
viper.SetDefault("BuildDrafts", false)
@@ -323,6 +324,8 @@ func loadDefaultSettings() {
viper.SetDefault("EnableEmoji", false)
viper.SetDefault("PygmentsCodeFencesGuessSyntax", false)
viper.SetDefault("UseModTimeAsFallback", false)
+ viper.SetDefault("Multilingual", false)
+ viper.SetDefault("DefaultContentLanguage", "en")
}
// InitializeConfig initializes a config file with sensible default configuration flags.
@@ -490,6 +493,8 @@ func InitializeConfig(subCmdVs ...*cobra.Command) error {
helpers.HugoReleaseVersion(), minVersion)
}
+ readMultilingualConfiguration()
+
return nil
}
@@ -506,7 +511,7 @@ func watchConfig() {
viper.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("Config file changed:", e.Name)
// Force a full rebuild
- MainSite = nil
+ MainSites = nil
utils.CheckErr(buildSite(true))
if !viper.GetBool("DisableLiveReload") {
// Will block forever trying to write to a channel that nobody is reading if livereload isn't initialized
@@ -632,6 +637,7 @@ func copyStatic() error {
func getDirList() []string {
var a []string
dataDir := helpers.AbsPathify(viper.GetString("DataDir"))
+ i18nDir := helpers.AbsPathify(viper.GetString("I18nDir"))
layoutDir := helpers.AbsPathify(viper.GetString("LayoutDir"))
staticDir := helpers.AbsPathify(viper.GetString("StaticDir"))
walker := func(path string, fi os.FileInfo, err error) error {
@@ -639,8 +645,13 @@ func getDirList() []string {
if path == dataDir && os.IsNotExist(err) {
jww.WARN.Println("Skip DataDir:", err)
return nil
+ }
+ if path == i18nDir && os.IsNotExist(err) {
+ jww.WARN.Println("Skip I18nDir:", err)
+ return nil
}
+
if path == layoutDir && os.IsNotExist(err) {
jww.WARN.Println("Skip LayoutDir:", err)
return nil
@@ -684,6 +695,7 @@ func getDirList() []string {
helpers.SymbolicWalk(hugofs.Source(), dataDir, walker)
helpers.SymbolicWalk(hugofs.Source(), helpers.AbsPathify(viper.GetString("ContentDir")), walker)
+ helpers.SymbolicWalk(hugofs.Source(), i18nDir, walker)
helpers.SymbolicWalk(hugofs.Source(), helpers.AbsPathify(viper.GetString("LayoutDir")), walker)
helpers.SymbolicWalk(hugofs.Source(), staticDir, walker)
if helpers.ThemeSet() {
@@ -695,31 +707,52 @@ func getDirList() []string {
func buildSite(watching ...bool) (err error) {
fmt.Println("Started building site")
- startTime := time.Now()
- if MainSite == nil {
- MainSite = new(hugolib.Site)
- }
- if len(watching) > 0 && watching[0] {
- MainSite.RunMode.Watching = true
+ t0 := time.Now()
+
+ if MainSites == nil {
+ MainSites = make(map[string]*hugolib.Site)
}
- err = MainSite.Build()
- if err != nil {
- return err
+
+ for _, lang := range langConfigsList {
+ t1 := time.Now()
+ mainSite, present := MainSites[lang]
+ if !present {
+ mainSite = new(hugolib.Site)
+ MainSites[lang] = mainSite
+ mainSite.SetMultilingualConfig(lang, langConfigsList, langConfigs)
+ }
+
+ if len(watching) > 0 && watching[0] {
+ mainSite.RunMode.Watching = true
+ }
+
+ if err := mainSite.Build(); err != nil {
+ return err
+ }
+
+ mainSite.Stats(lang, t1)
}
- MainSite.Stats()
- jww.FEEDBACK.Printf("in %v ms\n", int(1000*time.Since(startTime).Seconds()))
+
+ jww.FEEDBACK.Printf("total in %v ms\n", int(1000*time.Since(t0).Seconds()))
return nil
}
func rebuildSite(events []fsnotify.Event) error {
- startTime := time.Now()
- err := MainSite.ReBuild(events)
- if err != nil {
- return err
+ t0 := time.Now()
+
+ for _, lang := range langConfigsList {
+ t1 := time.Now()
+ mainSite := MainSites[lang]
+
+ if err := mainSite.ReBuild(events); err != nil {
+ return err
+ }
+
+ mainSite.Stats(lang, t1)
}
- MainSite.Stats()
- jww.FEEDBACK.Printf("in %v ms\n", int(1000*time.Since(startTime).Seconds()))
+
+ jww.FEEDBACK.Printf("total in %v ms\n", int(1000*time.Since(t0).Seconds()))
return nil
}
diff --git a/commands/list.go b/commands/list.go
index 5267a4f8b..bc5bb557a 100644
--- a/commands/list.go
+++ b/commands/list.go
@@ -57,7 +57,7 @@ var listDraftsCmd = &cobra.Command{
return newSystemError("Error Processing Source Content", err)
}
- for _, p := range site.Pages {
+ for _, p := range site.AllPages {
if p.IsDraft() {
fmt.Println(filepath.Join(p.File.Dir(), p.File.LogicalName()))
}
@@ -88,7 +88,7 @@ posted in the future.`,
return newSystemError("Error Processing Source Content", err)
}
- for _, p := range site.Pages {
+ for _, p := range site.AllPages {
if p.IsFuture() {
fmt.Println(filepath.Join(p.File.Dir(), p.File.LogicalName()))
}
@@ -119,7 +119,7 @@ expired.`,
return newSystemError("Error Processing Source Content", err)
}
- for _, p := range site.Pages {
+ for _, p := range site.AllPages {
if p.IsExpired() {
fmt.Println(filepath.Join(p.File.Dir(), p.File.LogicalName()))
}
diff --git a/commands/multilingual.go b/commands/multilingual.go
new file mode 100644
index 000000000..68da7c96d
--- /dev/null
+++ b/commands/multilingual.go
@@ -0,0 +1,41 @@
+package commands
+
+import (
+ "sort"
+
+ "github.com/spf13/cast"
+ "github.com/spf13/viper"
+)
+
+var langConfigs map[string]interface{}
+var langConfigsList langConfigsSortable
+
+func readMultilingualConfiguration() {
+ multilingual := viper.GetStringMap("Multilingual")
+ if len(multilingual) == 0 {
+ langConfigsList = append(langConfigsList, "")
+ return
+ }
+
+ langConfigs = make(map[string]interface{})
+ for lang, config := range multilingual {
+ langConfigs[lang] = config
+ langConfigsList = append(langConfigsList, lang)
+ }
+ sort.Sort(langConfigsList)
+}
+
+type langConfigsSortable []string
+
+func (p langConfigsSortable) Len() int { return len(p) }
+func (p langConfigsSortable) Less(i, j int) bool { return weightForLang(p[i]) < weightForLang(p[j]) }
+func (p langConfigsSortable) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
+
+func weightForLang(lang string) int {
+ conf := langConfigs[lang]
+ if conf == nil {
+ return 0
+ }
+ m := cast.ToStringMap(conf)
+ return cast.ToInt(m["weight"])
+}
diff --git a/docs/content/content/multilingual.md b/docs/content/content/multilingual.md
new file mode 100644
index 000000000..8edc6a600
--- /dev/null
+++ b/docs/content/content/multilingual.md
@@ -0,0 +1,238 @@
+---
+date: 2016-01-02T21:21:00Z
+menu:
+ main:
+ parent: content
+next: /content/example
+prev: /content/summaries
+title: Multilingual Mode
+weight: 68
+toc: true
+---
+
+Since version 0.17, Hugo supports a native Multilingual mode. In your
+top-level `config.yaml` (or equivalent), you define the available
+languages in a `Multilingual` section such as:
+
+```
+Multilingual:
+ en:
+ weight: 1
+ title: "My blog"
+ params:
+ linkedin: "english-link"
+ fr:
+ weight: 2
+
+ title: "Mon blog"
+ params:
+ linkedin: "lien-francais"
+ copyright: "Tout est miens"
+
+copyright: "Everything is mine"
+```
+
+Anything not defined in a `[lang]:` block will fall back to the global
+value for that key (like `copyright` for the `en` lang in this
+example).
+
+With the config above, all content, sitemap, RSS feeds, paginations
+and taxonomy pages will be rendered under `/en` in English, and under
+`/fr` in French.
+
+Only those keys are read under `Multilingual`: `weight`, `title`,
+`author`, `social`, `languageCode`, `copyright`, `disqusShortname`,
+`params` (which can contain a map of several other keys).
+
+
+### Translating your content
+
+Translated articles are picked up by the name of the content files.
+
+Example of translated articles:
+
+1. `/content/about.en.md`
+2. `/content/about.fr.md`
+
+You can also have:
+
+1. `/content/about.md`
+2. `/content/about.fr.md`
+
+in which case the config variable `DefaultContentLanguage` will be
+used to affect the default language `about.md`. This way, you can
+slowly start to translate your current content without having to
+rename everything.
+
+If left unspecified, the value for `DefaultContentLanguage` defaults
+to `en`.
+
+By having the same _base file name_, the content pieces are linked
+together as translated pieces. Only the content pieces in the language
+defined by **.Site.CurrentLanguage** will be rendered in a run of
+`hugo`. The translated content will be available in the
+`.Page.Translations` so you can create links to the corresponding
+translated pieces.
+
+
+### Language switching links
+
+Here is a simple example if all your pages are translated:
+
+```
+{{if .IsPage}}
+ {{ range $txLang := .Site.Languages }}
+ {{if isset $.Translations $txLang}}
+ <a href="{{ (index $.Translations $txLang).Permalink }}">{{ i18n ( printf "language_switcher_%s" $txLang ) }}</a>
+ {{end}}
+ {{end}}
+{{end}}
+
+{{if .IsNode}}
+ {{ range $txLang := .Site.Languages }}
+ <a href="/{{$txLang}}">{{ i18n ( printf "language_switcher_%s" $txLang ) }}</a>
+ {{end}}
+{{end}}
+```
+
+This is a more complete example. It handles missing translations and will support non-multilingual sites. Better for theme authors:
+
+```
+{{if .Site.Multilingual}}
+ {{if .IsPage}}
+ {{ range $txLang := .Site.Languages }}
+ {{if isset $.Translations $txLang}}
+ <a href="{{ (index $.Translations $txLang).Permalink }}">{{ i18n ( printf "language_switcher_%s" $txLang ) }}</a>
+ {{else}}
+ <a href="/{{$txLang}}">{{ i18n ( printf "language_switcher_%s" $txLang ) }}</a>
+ {{end}}
+ {{end}}
+ {{end}}
+
+ {{if .IsNode}}
+ {{ range $txLang := .Site.Languages }}
+ <a href="/{{$txLang}}">{{ i18n ( printf "language_switcher_%s" $txLang ) }}</a>
+ {{end}}
+ {{end}}
+{{end}}
+```
+
+This makes use of the **.Site.Languages** variable to create links to
+the other available languages. The order in which the languages are
+listed is defined by the `weight` attribute in each language under
+`Multilingual`.
+
+This will also require you to have some content in your `i18n/` files
+(see below) that would look like:
+
+```
+- id: language_switcher_en
+ translation: "English"
+- id: language_switcher_fr
+ translation: "Français"
+```
+
+and a copy of this in translations for each language.
+
+As you might notice, node pages link to the root of the other
+available translations (`/en`), as those pages do not necessarily have
+a translated counterpart.
+
+Taxonomies (tags, categories) are completely segregated between
+translations and will have their own tag clouds and list views.
+
+
+### Translation of strings
+
+Hugo uses [go-i18n](https://github.com/nicksnyder/go-i18n) to support
+string translations. Follow the link to find tools to manage your
+translation workflows.
+
+Translations are collected from the `themes/[name]/i18n/` folder
+(built into the theme), as well as translations present in `i18n/` at
+the root of your project. In the `i18n`, the translations will be
+merged and take precedence over what is in the theme folder. Files in
+there follow RFC 5646 and should be named something like `en-US.yaml`,
+`fr.yaml`, etc..
+
+From within your templates, use the `i18n` function as such:
+
+```
+{{ i18n "home" }}
+```
+
+to use a definition like this one in `i18n/en-US.yaml`:
+
+```
+- id: home
+ translation: "Home"
+```
+
+
+### Multilingual Themes support
+
+To support Multilingual mode in your themes, you only need to make
+sure URLs defined manually (those not using `.Permalink` or `.URL`
+variables) in your templates are prefixed with `{{
+.Site.LanguagePrefix }}`. If `Multilingual` mode is enabled, the
+`LanguagePrefix` variable will equal `"/en"` (or whatever your
+`CurrentLanguage` is). If not enabled, it will be an empty string, so
+it is harmless for non-multilingual sites.
+
+
+### Multilingual index.html and 404.html
+
+To redirect your users to their closest language, drop an `index.html`
+in `/static` of your site, with the following content (tailored to
+your needs) to redirect based on their browser's language:
+
+```
+<html><head>
+<meta http-equiv="refresh" content="1;url=/en" /><!-- just in case JS doesn't work -->
+<script>
+lang = window.navigator.language.substr(0, 2);
+if (lang == "fr") {
+ window.location = "/fr";
+} else {
+ window.location = "/en";
+}
+
+/* or simply:
+window.location = "/en";
+*/
+</script></head><body></body></html>
+```
+
+An even simpler version will always redirect your users to a given language:
+
+```
+<html><head>
+<meta http-equiv="refresh" content="0;url=/en" />
+</head><body></body></html>
+```
+
+You can do something similar with your `404.html` page, as you don't
+know the language of someone arriving at a non-existing page. You
+could inspect the prefix of the navigator path in Javascript or use
+the browser's language detection like above.
+
+
+### Sitemaps
+
+As sitemaps are generated once per language and live in
+`[lang]/sitemap.xml`. Write this content in `static/sitemap.xml` to
+link all your sitemaps together:
+
+```
+<?xml version="1.0" encoding="UTF-8"?>
+<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
+ <sitemap>
+ <loc>https://example.com/en/sitemap.xml</loc>
+ </sitemap>
+ <sitemap>
+ <loc>https://example.com/fr/sitemap.xml</loc>
+ </sitemap>
+</sitemapindex>
+```
+
+and explicitly list all the languages you want referenced.
diff --git a/docs/content/taxonomies/displaying.md b/docs/content/taxonomies/displaying.md
index 8719807f9..c66c3de56 100644
--- a/docs/content/taxonomies/displaying.md
+++ b/docs/content/taxonomies/displaying.md
@@ -38,7 +38,7 @@ each content piece are located in the usual place
<ul id="tags">
{{ range .Params.tags }}
- <li><a href="{{ "/tags/" | relURL }}{{ . | urlize }}">{{ . }}</a> </li>
+ <li><a href="{{ "/tags/" | relLangURL }}{{ . | urlize }}">{{ . }}</a> </li>
{{ end }}
</ul>
@@ -110,7 +110,8 @@ The following example displays all tag keys:
<ul id="all-tags">
{{ range $name, $taxonomy := .Site.Taxonomies.tags }}
- <li><a href="{{ "/tags/" | relURL }}{{ $name | urlize }}">{{ $name }}</a></li>
+<<<<<<< HEAD
+ <li><a href="{{ "/tags/" | relLangURL }}{{ $name | urlize }}">{{ $name }}</a></li>
{{ end }}
</ul>
@@ -120,7 +121,7 @@ This example will list all taxonomies, each of their keys and all the content as
<section>
<ul>
{{ range $taxonomyname, $taxonomy := .Site.Taxonomies }}
- <li><a href="{{ "/" | relURL}}{{ $taxonomyname | urlize }}">{{ $taxonomyname }}</a>
+ <li><a href="{{ "/" | relLangURL}}{{ $taxonomyname | urlize }}">{{ $taxonomyname }}</a>
<ul>
{{ range $key, $value := $taxonomy }}
<li> {{ $key }} </li>
@@ -135,4 +136,3 @@ This example will list all taxonomies, each of their keys and all the content as
{{ end }}
</ul>
</section>
-
diff --git a/docs/content/taxonomies/ordering.md b/docs/content/taxonomies/ordering.md
index baecd7b4c..ac86bc69d 100644
--- a/docs/content/taxonomies/ordering.md
+++ b/docs/content/taxonomies/ordering.md
@@ -29,7 +29,7 @@ Taxonomies can be ordered by either alphabetical key or by the number of content
<ul>
{{ $data := .Data }}
{{ range $key, $value := .Data.Taxonomy.Alphabetical }}
- <li><a href="{{ $data.Plural }}/{{ $value.Name | urlize }}"> {{ $value.Name }} </a> {{ $value.Count }} </li>
+ <li><a href="{{ .Site.LanguagePrefix }}/{{ $data.Plural }}/{{ $value.Name | urlize }}"> {{ $value.Name }} </a> {{ $value.Count }} </li>
{{ end }}
</ul>
@@ -38,7 +38,7 @@ Taxonomies can be ordered by either alphabetical key or by the number of content
<ul>
{{ $data := .Data }}
{{ range $key, $value := .Data.Taxonomy.ByCount }}
- <li><a href="{{ $data.Plural }}/{{ $value.Name | urlize }}"> {{ $value.Name }} </a> {{ $value.Count }} </li>
+ <li><a href="{{ .Site.LanguagePrefix }}/{{ $data.Plural }}/{{ $value.Name | urlize }}"> {{ $value.Name }} </a> {{ $value.Count }} </li>
{{ end }}
</ul>
diff --git a/docs/content/templates/functions.md b/docs/content/templates/functions.md
index cd212b85d..aa55ced4f 100644
--- a/docs/content/templates/functions.md
+++ b/docs/content/templates/functions.md
@@ -435,6 +435,13 @@ e.g.
## Strings
+### printf
+
+Format a string using the standard `fmt.Sprintf` function. See [the go
+doc](https://golang.org/pkg/fmt/) for reference.
+
+e.g., `{{ i18n ( printf "combined_%s" $var ) }}` or `{{ printf "formatted %.2f" 3.1416 }}`
+
### chomp
Removes any trailing newline characters. Useful in a pipeline to remove newlines added by other processing (including `markdownify`).
@@ -726,7 +733,6 @@ CJK-like languages.
<!-- outputs a content length of 8 runes. -->
```
-
### md5
`md5` hashes the given input and returns its MD5 checksum.
@@ -752,6 +758,23 @@ This can be useful if you want to use Gravatar for generating a unique avatar:
<!-- returns the string "c8b5b0e33d408246e30f53e32b8f7627a7a649d4" -->
```
+## Internationalization
+
+### i18n
+
+This translates a piece of content based on your `i18n/en-US.yaml`
+(and friends) files. You can use the
+[go-i18n](https://github.com/nicksnyder/go-i18n) tools to manage your
+translations. The translations can exist in both the theme and at the
+root of your repository.
+
+e.g.: `{{ i18n "translation_id" }}`
+
+
+### T
+
+`T` is an alias to `i18n`. E.g. `{{ T "translation_id" }}`.
+>>>>>>> Add multilingual support in Hugo
## Times
@@ -763,7 +786,6 @@ This can be useful if you want to use Gravatar for generating a unique avatar:
* `{{ (time "2016-05-28").YearDay }}` → 149
* `{{ mul 1000 (time "2016-05-28T10:30:00.00+10:00").Unix }}` → 1464395400000 (Unix time in milliseconds)
-
## URLs
### absURL, relURL
diff --git a/docs/content/templates/terms.md b/docs/content/templates/terms.md
index 3e6b43878..3711fd8aa 100644
--- a/docs/content/templates/terms.md
+++ b/docs/content/templates/terms.md
@@ -89,7 +89,7 @@ content tagged with each tag.
<ul>
{{ $data := .Data }}
{{ range $key, $value := .Data.Terms }}
- <li><a href="{{ $data.Plural }}/{{ $key | urlize }}">{{ $key }}</a> {{ len $value }}</li>
+ <li><a href="{{ .Site.LanguagePrefix }}/{{ $data.Plural }}/{{ $key | urlize }}">{{ $key }}</a> {{ len $value }}</li>
{{ end }}
</ul>
</div>
@@ -109,7 +109,7 @@ Another example listing the content for each term (ordered by Date):
{{ $data := .Data }}
{{ range $key,$value := .Data.Terms.ByCount }}
- <h2><a href="{{ $data.Plural }}/{{ $value.Name | urlize }}">{{ $value.Name }}</a> {{ $value.Count }}</h2>
+ <h2><a href="{{ .Site.LanguagePrefix }}/{{ $data.Plural }}/{{ $value.Name | urlize }}">{{ $value.Name }}</a> {{ $value.Count }}</h2>
<ul>
{{ range $value.Pages.ByDate }}
<li><a href="{{ .Permalink }}">{{ .Title }}</a></li>
@@ -140,7 +140,7 @@ Hugo can order the meta data in two different ways. It can be ordered:
<ul>
{{ $data := .Data }}
{{ range $key, $value := .Data.Terms.Alphabetical }}
- <li><a href="{{ $data.Plural }}/{{ $value.Name | urlize }}">{{ $value.Name }}</a> {{ $value.Count }}</li>
+ <li><a href="{{ .Site.LanguagePrefix }}/{{ $data.Plural }}/{{ $value.Name | urlize }}">{{ $value.Name }}</a> {{ $value.Count }}</li>
{{ end }}
</ul>
</div>
@@ -158,7 +158,7 @@ Hugo can order the meta data in two different ways. It can be ordered:
<ul>
{{ $data := .Data }}
{{ range $key, $value := .Data.Terms.ByCount }}
- <li><a href="{{ $data.Plural }}/{{ $value.Name | urlize }}">{{ $value.Name }}</a> {{ $value.Count }}</li>
+ <li><a href="{{ .Site.LanguagePrefix }}/{{ $data.Plural }}/{{ $value.Name | urlize }}">{{ $value.Name }}</a> {{ $value.Count }}</li>
{{ end }}
</ul>
</div>
diff --git a/docs/content/templates/variables.md b/docs/content/templates/variables.md
index 953ba5cb6..8f37dd19f 100644
--- a/docs/content/templates/variables.md
+++ b/docs/content/templates/variables.md
@@ -58,6 +58,8 @@ matter, content or derived from file location.
**.IsPage** Always true for page.<br>
**.Site** See [Site Variables]({{< relref "#site-variables" >}}) below.<br>
**.Hugo** See [Hugo Variables]({{< relref "#hugo-variables" >}}) below.<br>
+**.Translations** A map to other pages wi