multilingual.md (18103B)
1 ---
2 title: Multilingual Mode
3 linktitle: Multilingual
4 description: Hugo supports the creation of websites with multiple languages side by side.
5 date: 2017-01-10
6 publishdate: 2017-01-10
7 lastmod: 2017-01-10
8 categories: [content management]
9 keywords: [multilingual,i18n, internationalization]
10 menu:
11 docs:
12 parent: "content-management"
13 weight: 150
14 weight: 150 #rem
15 draft: false
16 aliases: [/content/multilingual/,/tutorials/create-a-multilingual-site/]
17 toc: true
18 ---
19
20 You should define the available languages in a `languages` section in your site configuration.
21
22 > Also See [Hugo Multilingual Part 1: Content translation]
23
24 ## Configure Languages
25
26 The following is an example of a site configuration for a multilingual Hugo project:
27
28 {{< code-toggle file="config" >}}
29 defaultContentLanguage = "en"
30 copyright = "Everything is mine"
31
32 [params]
33 [params.navigation]
34 help = "Help"
35
36 [languages]
37 [languages.en]
38 title = "My blog"
39 weight = 1
40 [languages.en.params]
41 linkedin = "https://linkedin.com/whoever"
42
43 [languages.fr]
44 title = "Mon blogue"
45 weight = 2
46 [languages.fr.params]
47 linkedin = "https://linkedin.com/fr/whoever"
48 [languages.fr.params.navigation]
49 help = "Aide"
50
51 [languages.ar]
52 title = "مدونتي"
53 weight = 2
54 languagedirection = "rtl"
55
56 [languages.pt-pt]
57 title = "O meu blog"
58 weight = 3
59 {{< /code-toggle >}}
60
61 Anything not defined in a `languages` block will fall back to the global value for that key (e.g., `copyright` for the English `en` language). This also works for `params`, as demonstrated with `help` above: You will get the value `Aide` in French and `Help` in all the languages without this parameter set.
62
63 With the configuration above, all content, sitemap, RSS feeds, paginations,
64 and taxonomy pages will be rendered below `/` in English (your default content language) and then below `/fr` in French.
65
66 When working with front matter `Params` in [single page templates], omit the `params` in the key for the translation.
67
68 `defaultContentLanguage` sets the project's default language. If not set, the default language will be `en`.
69
70 If the default language needs to be rendered below its own language code (`/en`) like the others, set `defaultContentLanguageInSubdir: true`.
71
72 Only the obvious non-global options can be overridden per language. Examples of global options are `baseURL`, `buildDrafts`, etc.
73
74 **Please note:** use lowercase language codes, even when using regional languages (ie. use pt-pt instead of pt-PT). Currently Hugo language internals lowercase language codes, which can cause conflicts with settings like `defaultContentLanguage` which are not lowercased. Please track the evolution of this issue in [Hugo repository issue tracker](https://github.com/gohugoio/hugo/issues/7344)
75
76 ### Disable a Language
77
78 You can disable one or more languages. This can be useful when working on a new translation.
79
80 {{< code-toggle file="config" >}}
81 disableLanguages = ["fr", "ja"]
82 {{< /code-toggle >}}
83
84 Note that you cannot disable the default content language.
85
86 We kept this as a standalone setting to make it easier to set via [OS environment]:
87
88 ```bash
89 HUGO_DISABLELANGUAGES="fr ja" hugo
90 ```
91
92 If you have already a list of disabled languages in `config.toml`, you can enable them in development like this:
93
94 ```bash
95 HUGO_DISABLELANGUAGES=" " hugo server
96 ```
97
98 ### Configure Multilingual Multihost
99
100 From **Hugo 0.31** we support multiple languages in a multihost configuration. See [this issue](https://github.com/gohugoio/hugo/issues/4027) for details.
101
102 This means that you can now configure a `baseURL` per `language`:
103
104 > If a `baseURL` is set on the `language` level, then all languages must have one and they must all be different.
105
106 Example:
107
108 {{< code-toggle file="config" >}}
109 [languages]
110 [languages.fr]
111 baseURL = "https://example.fr"
112 languageName = "Français"
113 weight = 1
114 title = "En Français"
115
116 [languages.en]
117 baseURL = "https://example.com"
118 languageName = "English"
119 weight = 2
120 title = "In English"
121 {{</ code-toggle >}}
122
123 With the above, the two sites will be generated into `public` with their own root:
124
125 ```text
126 public
127 ├── en
128 └── fr
129 ```
130
131 **All URLs (i.e `.Permalink` etc.) will be generated from that root. So the English home page above will have its `.Permalink` set to `https://example.com/`.**
132
133 When you run `hugo server` we will start multiple HTTP servers. You will typically see something like this in the console:
134
135 ```text
136 Web Server is available at 127.0.0.1:1313 (bind address 127.0.0.1)
137 Web Server is available at 127.0.0.1:1314 (bind address 127.0.0.1)
138 Press Ctrl+C to stop
139 ```
140
141 Live reload and `--navigateToChanged` between the servers work as expected.
142
143
144 ## Translate Your Content
145
146 There are two ways to manage your content translations. Both ensure each page is assigned a language and is linked to its counterpart translations.
147
148 ### Translation by filename
149
150 Considering the following example:
151
152 1. `/content/about.en.md`
153 2. `/content/about.fr.md`
154
155 The first file is assigned the English language and is linked to the second.
156 The second file is assigned the French language and is linked to the first.
157
158 Their language is __assigned__ according to the language code added as a __suffix to the filename__.
159
160 By having the same **path and base filename**, the content pieces are __linked__ together as translated pages.
161
162 {{< note >}}
163 If a file has no language code, it will be assigned the default language.
164 {{</ note >}}
165
166 ### Translation by content directory
167
168 This system uses different content directories for each of the languages. Each language's content directory is set using the `contentDir` param.
169
170 {{< code-toggle file="config" >}}
171 languages:
172 en:
173 weight: 10
174 languageName: "English"
175 contentDir: "content/english"
176 fr:
177 weight: 20
178 languageName: "Français"
179 contentDir: "content/french"
180 {{< /code-toggle >}}
181
182 The value of `contentDir` can be any valid path -- even absolute path references. The only restriction is that the content directories cannot overlap.
183
184 Considering the following example in conjunction with the configuration above:
185
186 1. `/content/english/about.md`
187 2. `/content/french/about.md`
188
189 The first file is assigned the English language and is linked to the second.
190 The second file is assigned the French language and is linked to the first.
191
192 Their language is __assigned__ according to the content directory they are __placed__ in.
193
194 By having the same **path and basename** (relative to their language content directory), the content pieces are __linked__ together as translated pages.
195
196 ### Bypassing default linking
197
198 Any pages sharing the same `translationKey` set in front matter will be linked as translated pages regardless of basename or location.
199
200 Considering the following example:
201
202 1. `/content/about-us.en.md`
203 2. `/content/om.nn.md`
204 3. `/content/presentation/a-propos.fr.md`
205
206 {{< code-toggle >}}
207 translationKey: "about"
208 {{< /code-toggle >}}
209
210 By setting the `translationKey` front matter param to `about` in all three pages, they will be __linked__ as translated pages.
211
212 ### Localizing permalinks
213
214 Because paths and filenames are used to handle linking, all translated pages will share the same URL (apart from the language subdirectory).
215
216 To localize the URLs, the [`slug`]({{< ref "/content-management/organization/index.md#slug" >}}) or [`url`]({{< ref "/content-management/organization/index.md#url" >}}) front matter param can be set in any of the non-default language file.
217
218 For example, a French translation (`content/about.fr.md`) can have its own localized slug.
219
220 {{< code-toggle >}}
221 Title: A Propos
222 slug: "a-propos"
223 {{< /code-toggle >}}
224
225 At render, Hugo will build both `/about/` and `/fr/a-propos/` while maintaining their translation linking.
226
227 {{% note %}}
228 If using `url`, remember to include the language part as well: `/fr/compagnie/a-propos/`.
229 {{%/ note %}}
230
231 ### Page Bundles
232
233 To avoid the burden of having to duplicate files, each Page Bundle inherits the resources of its linked translated pages' bundles except for the content files (markdown files, html files etc...).
234
235 Therefore, from within a template, the page will have access to the files from all linked pages' bundles.
236
237 If, across the linked bundles, two or more files share the same basename, only one will be included and chosen as follows:
238
239 * File from current language bundle, if present.
240 * First file found across bundles by order of language `Weight`.
241
242 {{% note %}}
243 Page Bundle resources follow the same language assignment logic as content files, both by filename (`image.jpg`, `image.fr.jpg`) and by directory (`english/about/header.jpg`, `french/about/header.jpg`).
244 {{%/ note %}}
245
246 ## Reference the Translated Content
247
248 To create a list of links to translated content, use a template similar to the following:
249
250 {{< code file="layouts/partials/i18nlist.html" >}}
251 {{ if .IsTranslated }}
252 <h4>{{ i18n "translations" }}</h4>
253 <ul>
254 {{ range .Translations }}
255 <li>
256 <a href="{{ .Permalink }}">{{ .Lang }}: {{ .Title }}{{ if .IsPage }} ({{ i18n "wordCount" . }}){{ end }}</a>
257 </li>
258 {{ end }}
259 </ul>
260 {{ end }}
261 {{< /code >}}
262
263 The above can be put in a `partial` (i.e., inside `layouts/partials/`) and included in any template, whether a [single content page][contenttemplate] or the [homepage]. It will not print anything if there are no translations for a given page.
264
265 The above also uses the [`i18n` function][i18func] described in the next section.
266
267 ### List All Available Languages
268
269 `.AllTranslations` on a `Page` can be used to list all translations, including the page itself. On the home page it can be used to build a language navigator:
270
271 {{< code file="layouts/partials/allLanguages.html" >}}
272 <ul>
273 {{ range $.Site.Home.AllTranslations }}
274 <li><a href="{{ .Permalink }}">{{ .Language.LanguageName }}</a></li>
275 {{ end }}
276 </ul>
277 {{< /code >}}
278
279 ## Translation of Strings
280
281 Hugo uses [go-i18n] to support string translations. [See the project's source repository][go-i18n-source] to find tools that will help you manage your translation workflows.
282
283 Translations are collected from the `themes/<THEME>/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.toml`, `fr.toml`, etc.
284
285 Artificial languages with private use subtags as defined in [RFC 5646 § 2.2.7](https://datatracker.ietf.org/doc/html/rfc5646#section-2.2.7) are also supported. You may omit the `art-x-` prefix for brevity. For example:
286
287 ```text
288 art-x-hugolang
289 hugolang
290 ```
291
292 Private use subtags must not exceed 8 alphanumeric characters.
293
294 ### Query basic translation
295
296 From within your templates, use the `i18n` function like this:
297
298 ```go-html-template
299 {{ i18n "home" }}
300 ```
301
302 The function will search for the `"home"` id:
303
304 {{< code-toggle file="i18n/en-US" >}}
305 [home]
306 other = "Home"
307 {{< /code-toggle >}}
308
309 The result will be
310
311 ```text
312 Home
313 ```
314
315 ### Query a flexible translation with variables
316
317 Often you will want to use the page variables in the translation strings. To do so, pass the `.` context when calling `i18n`:
318
319 ```go-html-template
320 {{ i18n "wordCount" . }}
321 ```
322
323 The function will pass the `.` context to the `"wordCount"` id:
324
325 {{< code-toggle file="i18n/en-US" >}}
326 [wordCount]
327 other = "This article has {{ .WordCount }} words."
328 {{< /code-toggle >}}
329
330 Assume `.WordCount` in the context has value is 101. The result will be:
331
332 ```text
333 This article has 101 words.
334 ```
335
336 ### Query a singular/plural translation
337
338 In order to meet singular/plural requirement, you must pass a dictionary (map) with a numeric `.Count` property to the `i18n` function. The below example uses `.ReadingTime` variable which has a built-in `.Count` property.
339
340 ```go-html-template
341 {{ i18n "readingTime" .ReadingTime }}
342 ```
343
344 The function will read `.Count` from `.ReadingTime` and evaluate where the number is singular (`one`) or plural (`other`). After that, it will pass to `readingTime` id:
345
346 {{< code-toggle file="i18n/en-US" >}}
347 [readingTime]
348 one = "One minute to read"
349 other = "{{.Count}} minutes to read"
350 {{< /code-toggle >}}
351
352 Assume `.ReadingTime.Count` in the context has value of 525600. The result will be:
353
354 ```text
355 525600 minutes to read
356 ```
357
358 If `.ReadingTime.Count` in the context has value is 1. The result is:
359
360 ```text
361 One minute to read
362 ```
363
364 In case you need to pass custom data: (`(dict "Count" 25)` is minimum requirement)
365
366 ```go-html-template
367 {{ i18n "readingTime" (dict "Count" 25 "FirstArgument" true "SecondArgument" false "Etc" "so on, so far") }}
368 ```
369
370 ## Localization
371
372 The following localization examples assume your site's primary language is English, with translations to French and German.
373
374 {{< code-toggle file="config" >}}
375 defaultContentLang = 'en'
376
377 [languages]
378 [languages.en]
379 contentDir = 'content/en'
380 languageName = 'English'
381 weight = 1
382 [languages.fr]
383 contentDir = 'content/fr'
384 languageName = 'Français'
385 weight = 2
386 [languages.de]
387 contentDir = 'content/de'
388 languageName = 'Deutsch'
389 weight = 3
390
391 {{< /code-toggle >}}
392
393 ### Dates
394
395 With this front matter:
396
397 {{< code-toggle >}}
398 date = 2021-11-03T12:34:56+01:00
399 {{< /code-toggle >}}
400
401 And this template code:
402
403 ```go-html-template
404 {{ .Date | time.Format ":date_full" }}
405 ```
406
407 The rendered page displays:
408
409 Language|Value
410 :--|:--
411 English|Wednesday, November 3, 2021
412 Français|mercredi 3 novembre 2021
413 Deutsch|Mittwoch, 3. November 2021
414
415 See [time.Format] for details.
416
417 ### Currency
418
419 With this template code:
420
421 ```go-html-template
422 {{ 512.5032 | lang.FormatCurrency 2 "USD" }}
423 ```
424
425 The rendered page displays:
426
427 Language|Value
428 :--|:--
429 English|$512.50
430 Français|512,50 $US
431 Deutsch|512,50 $
432
433 See [lang.FormatCurrency] and [lang.FormatAccounting] for details.
434
435 ### Numbers
436
437 With this template code:
438
439 ```go-html-template
440 {{ 512.5032 | lang.FormatNumber 2 }}
441 ```
442
443 The rendered page displays:
444
445 Language|Value
446 :--|:--
447 English|512.50
448 Français|512,50
449 Deutsch|512,50
450
451 See [lang.FormatNumber] and [lang.FormatNumberCustom] for details.
452
453 ### Percentages
454
455 With this template code:
456
457 ```go-html-template
458 {{ 512.5032 | lang.FormatPercent 2 }} ---> 512.50%
459 ```
460
461 The rendered page displays:
462
463 Language|Value
464 :--|:--
465 English|512.50%
466 Français|512,50 %
467 Deutsch|512,50 %
468
469 See [lang.FormatPercent] for details.
470
471 ## Menus
472
473 You can define your menus for each language independently. Creating multilingual menus works just like [creating regular menus][menus], except they're defined in language-specific blocks in the configuration file:
474
475 {{< code-toggle file="config" >}}
476 defaultContentLanguage = "en"
477
478 [languages.en]
479 weight = 0
480 languageName = "English"
481
482 [[languages.en.menu.main]]
483 url = "/"
484 name = "Home"
485 weight = 0
486
487 [languages.de]
488 weight = 10
489 languageName = "Deutsch"
490
491 [[languages.de.menu.main]]
492 url = "/"
493 name = "Startseite"
494 weight = 0
495 {{< /code-toggle >}}
496
497 The rendering of the main navigation works as usual. `.Site.Menus` will just contain the menu in the current language. Note that `absLangURL` below will link to the correct locale of your website. Without it, menu entries in all languages would link to the English version, since it's the default content language that resides in the root directory.
498
499 ```go-html-template
500 <ul>
501 {{- $currentPage := . -}}
502 {{ range .Site.Menus.main -}}
503 <li class="{{ if $currentPage.IsMenuCurrent "main" . }}active{{ end }}">
504 <a href="{{ .URL | absLangURL }}">{{ .Name }}</a>
505 </li>
506 {{- end }}
507 </ul>
508 ```
509
510 ## Missing Translations
511
512 If a string does not have a translation for the current language, Hugo will use the value from the default language. If no default value is set, an empty string will be shown.
513
514 While translating a Hugo website, it can be handy to have a visual indicator of missing translations. The [`enableMissingTranslationPlaceholders` configuration option][config] will flag all untranslated strings with the placeholder `[i18n] identifier`, where `identifier` is the id of the missing translation.
515
516 {{% note %}}
517 Hugo will generate your website with these missing translation placeholders. It might not be suitable for production environments.
518 {{% /note %}}
519
520 For merging of content from other languages (i.e. missing content translations), see [lang.Merge].
521
522 To track down missing translation strings, run Hugo with the `--printI18nWarnings` flag:
523
524 ```bash
525 hugo --printI18nWarnings | grep i18n
526 i18n|MISSING_TRANSLATION|en|wordCount
527 ```
528
529 ## Multilingual Themes support
530
531 To support Multilingual mode in your themes, some considerations must be taken for the URLs in the templates. If there is more than one language, URLs must meet the following criteria:
532
533 * Come from the built-in `.Permalink` or `.RelPermalink`
534 * Be constructed with the [`relLangURL` template function][rellangurl] or the [`absLangURL` template function][abslangurl] **OR** be prefixed with `{{ .LanguagePrefix }}`
535
536 If there is 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 (and is therefore harmless for single-language Hugo websites).
537
538 [abslangurl]: /functions/abslangurl
539 [config]: /getting-started/configuration/
540 [contenttemplate]: /templates/single-page-templates/
541 [go-i18n-source]: https://github.com/nicksnyder/go-i18n
542 [go-i18n]: https://github.com/nicksnyder/go-i18n
543 [homepage]: /templates/homepage/
544 [Hugo Multilingual Part 1: Content translation]: https://regisphilibert.com/blog/2018/08/hugo-multilingual-part-1-managing-content-translation/
545 [i18func]: /functions/i18n/
546 [lang.FormatAccounting]: /functions/lang/#langformataccounting
547 [lang.FormatCurrency]: /functions/lang/#langformatcurrency
548 [lang.FormatNumber]: /functions/lang/#langformatnumber
549 [lang.FormatNumberCustom]: /functions/lang/#langformatnumbercustom
550 [lang.FormatPercent]: /functions/lang/#langformatpercent
551 [lang.Merge]: /functions/lang.merge/
552 [menus]: /content-management/menus/
553 [OS environment]: /getting-started/configuration/#configure-with-environment-variables
554 [rellangurl]: /functions/rellangurl
555 [RFC 5646]: https://tools.ietf.org/html/rfc5646
556 [single page templates]: /templates/single-page-templates/
557 [time.Format]: /functions/dateformat