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