Rework the multilingual docs

And in the same go adjusted some minor parts of the language API:

Add LanguagePrefix alias to Node and rename the Multilingual config section to Languages.

See #2309
This commit is contained in:
Bjørn Erik Pedersen 2016-08-04 12:06:29 +02:00
parent ed0985404d
commit f0b91852ea
5 changed files with 51 additions and 168 deletions

View file

@ -13,7 +13,7 @@ import (
func readMultilingualConfiguration() (*hugolib.HugoSites, error) {
sites := make([]*hugolib.Site, 0)
multilingual := viper.GetStringMap("Multilingual")
multilingual := viper.GetStringMap("Languages")
if len(multilingual) == 0 {
// TODO(bep) multilingo langConfigsList = append(langConfigsList, hugolib.NewLanguage("en"))
sites = append(sites, hugolib.NewSite(hugolib.NewLanguage("en")))

View file

@ -9,13 +9,12 @@ title: Multilingual Mode
weight: 68
toc: true
---
Hugo supports multiple languages side-by-side (added in `Hugo 0.17`). Define the available languages in a `Languages` section in your top-level `config.yaml` (or equivalent).
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:
Example:
```
Multilingual:
Languages:
en:
weight: 1
title: "My blog"
@ -23,7 +22,6 @@ Multilingual:
linkedin: "english-link"
fr:
weight: 2
title: "Mon blog"
params:
linkedin: "lien-francais"
@ -33,21 +31,16 @@ 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).
value for that key (like `copyright` for the English (`en`) language 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).
and taxonomy pages will be rendered below `/en` in English, and below `/fr` in French.
Only the obvious non-global options can be overridden per language. Examples of global options are `BaseURL`, `BuildDrafts`, etc.
### Translating your content
Translated articles are picked up by the name of the content files.
Translated articles are identified by the name of the content file.
Example of translated articles:
@ -59,180 +52,66 @@ 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.
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`.
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.
By having the same _base file name_, the content pieces are linked together as translated pieces.
### Link to translated content
### Language switching links
Here is a simple example if all your pages are translated:
To create a list of links to translated content, use a template similar to this:
```
{{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}}
{{ $translations := .Translations }}
{{ if gt (len $translations) 0 }}
<h4>{{ i18n "translations" }}</h4>
<ul>
{{ range $translations }}
<li>
<a href="{{ .Permalink }}">{{ .Lang }}: {{ .Title }}</a>
</li>
{{ end}}
</ul>
{{ end }}
```
The above can be put in a `partial` and included in any template, be it for a content page or the home page. It will not print anything if there are no translations for a given page, or if it is -- in the case of the home page, section listing etc. -- a site with only one language.
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.
The above also uses the `i8n` func, see [Translation of strings](#translation-of-strings).
### 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.
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..
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. Language files should be named according to RFC 5646 with names such as `en-US.yaml`, `fr.yaml`, etc.
From within your templates, use the `i18n` function as such:
From within your templates, use the `i18n` function like this:
```
{{ i18n "home" }}
```
to use a definition like this one in `i18n/en-US.yaml`:
This uses a definition like this one in `i18n/en-US.yaml`:
```
- id: home
translation: "Home"
```
Often you will want to use to the page variables in the translations strings. To do that, pass on the "." context when calling `18n`:
```
{{ i18n "wordCount" . }}
```
This uses a definition like this one in `i18n/en-US.yaml`:
```
- id: wordCount
translation: "This article has {{ .WordCount }} words."
```
### 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.
To support Multilingual mode in your themes, some considerations must be taken for the URLs in the templates. If there are more than one language, URLs must either come from the built-in `.Permalink` or `.URL`, be constructed with `relURL` or `absURL` -- or prefixed with `{{.LanguagePrefix }}`.
If there are more than one language defined, 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 single-language 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.

View file

@ -48,7 +48,7 @@ func testCommonResetState() {
}
func _TestMultiSites(t *testing.T) {
func TestMultiSites(t *testing.T) {
sites := createMultiTestSites(t)
@ -65,7 +65,7 @@ func _TestMultiSites(t *testing.T) {
if len(enSite.Pages) != 3 {
t.Fatal("Expected 3 english pages")
}
assert.Len(t, enSite.Source.Files(), 6, "should have 6 source files")
assert.Len(t, enSite.Source.Files(), 11, "should have 11 source files")
assert.Len(t, enSite.AllPages, 6, "should have 6 total pages (including translations)")
doc1en := enSite.Pages[0]

View file

@ -201,6 +201,10 @@ func (n *Node) Lang() string {
return n.lang
}
func (n *Node) LanguagePrefix() string {
return n.Site.LanguagePrefix
}
// AllTranslations returns all translations, including the current Node.
// Note that this and the one below is kind of a temporary hack before #2297 is solved.
func (n *Node) AllTranslations() Nodes {

View file

@ -866,7 +866,7 @@ func (s *Site) initializeSiteInfo() {
CurrentLanguage: lang.Lang,
LanguagePrefix: languagePrefix,
Languages: languages,
GoogleAnalytics: viper.GetString("GoogleAnalytics"),
GoogleAnalytics: lang.GetString("GoogleAnalytics"),
RSSLink: permalinkStr(viper.GetString("RSSUri")),
BuildDrafts: viper.GetBool("BuildDrafts"),
canonifyURLs: viper.GetBool("CanonifyURLs"),