Skip to content

Commit

Permalink
Rework the multilingual docs
Browse files Browse the repository at this point in the history
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
  • Loading branch information
bep committed Aug 4, 2016
1 parent 17c8714 commit c01d519
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 161 deletions.
2 changes: 1 addition & 1 deletion commands/multilingual.go
Expand Up @@ -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")))
Expand Down
197 changes: 38 additions & 159 deletions docs/content/content/multilingual.md
Expand Up @@ -9,21 +9,19 @@ 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"
params:
linkedin: "english-link"
fr:
weight: 2
title: "Mon blog"
params:
linkedin: "lien-francais"
Expand All @@ -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:

Expand All @@ -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.

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.
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`.

### Language switching links
By having the same _base file name_, the content pieces are linked together as translated pieces.

Here is a simple example if all your pages are translated:
### Link to translated content

```
{{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:
To create a list of links to translated content, use a template similar to this:

```
{{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}}
{{ $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 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"
```


### 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:
Often you will want to use to the page variables in the translations strings. To do that, pass on the "." context when calling `18n`:

```
<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>
{{ i18n "wordCount" . }}
```

An even simpler version will always redirect your users to a given language:

This uses a definition like this one in `i18n/en-US.yaml`:
```
<html><head>
<meta http-equiv="refresh" content="0;url=/en" />
</head><body></body></html>
- id: wordCount
translation: "This article has {{ .WordCount }} words."
```

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.

### Multilingual Themes support

### Sitemaps
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 }}`.

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:
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.

```
<?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.
4 changes: 4 additions & 0 deletions hugolib/node.go
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion hugolib/site.go
Expand Up @@ -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"),
Expand Down

0 comments on commit c01d519

Please sign in to comment.