Многоязычный режим
Вы должны определить доступные языки в разделе languages
в конфигурации Вашего сайта.
Смотрите также Мультиязычный Хьюго Часть 1: Перевод контента
Настройка языков
Ниже приведен пример конфигурации сайта для многоязычного проекта Hugo:
copyright: Everything is mine
defaultContentLanguage: en
languages:
ar:
languagedirection: rtl
title: مدونتي
weight: 2
en:
params:
linkedin: https://linkedin.com/whoever
title: My blog
weight: 1
fr:
params:
linkedin: https://linkedin.com/fr/whoever
navigation:
help: Aide
title: Mon blogue
weight: 2
pt-pt:
title: O meu blog
weight: 3
params:
navigation:
help: Help
copyright = "Everything is mine"
defaultContentLanguage = "en"
[languages]
[languages.ar]
languagedirection = "rtl"
title = "مدونتي"
weight = 2
[languages.en]
title = "My blog"
weight = 1
[languages.en.params]
linkedin = "https://linkedin.com/whoever"
[languages.fr]
title = "Mon blogue"
weight = 2
[languages.fr.params]
linkedin = "https://linkedin.com/fr/whoever"
[languages.fr.params.navigation]
help = "Aide"
[languages.pt-pt]
title = "O meu blog"
weight = 3
[params]
[params.navigation]
help = "Help"
{
"copyright": "Everything is mine",
"defaultContentLanguage": "en",
"languages": {
"ar": {
"languagedirection": "rtl",
"title": "مدونتي",
"weight": 2
},
"en": {
"params": {
"linkedin": "https://linkedin.com/whoever"
},
"title": "My blog",
"weight": 1
},
"fr": {
"params": {
"linkedin": "https://linkedin.com/fr/whoever",
"navigation": {
"help": "Aide"
}
},
"title": "Mon blogue",
"weight": 2
},
"pt-pt": {
"title": "O meu blog",
"weight": 3
}
},
"params": {
"navigation": {
"help": "Help"
}
}
}
Все, что не определено в блоке languages
, вернется к глобальному значению для этого ключа (например, copyright
для английского языка en
). Это также работает для params
, как показано выше с помощью help
: Вы получите значение Aide
на французском языке и Help
на всех языках без этого набора параметров.
С приведенной выше конфигурацией все содержимое, карта сайта, RSS-каналы, пагинации и страницы таксономии будут отображаться под символом /
на английском языке (Ваш язык содержимого по умолчанию), а затем под символом /fr
на французском языке.
При работе с заголовком Params
в single page templates, опускайте params
в ключе для перевода.
defaultContentLanguage
устанавливает язык проекта по умолчанию. Если не установлен, языком по умолчанию будет en
.
Если язык по умолчанию должен отображаться под кодом своего собственного языка (/en
), как и другие, установите defaultContentLanguageInSubdir: true
.
Только очевидные неглобальные параметры могут быть переопределены для каждого языка. Примеры глобальных параметров: baseURL
, buildDrafts
и т.д.
Обратите внимание: используйте строчные коды языков даже при использовании региональных языков (например, используйте pt-pt вместо pt-PT). В настоящее время внутренние строчные коды языка Hugo могут вызывать конфликты с такими настройками, как `defaultContentLanguage, которые не в нижнем регистре. Проследите за развитием этой проблемы в трекере проблем репозитория Hugo
Отключить язык
Вы можете отключить один или несколько языков. Это может быть полезно при работе над новым переводом.
disableLanguages = ["fr", "ja"]
Обратите внимание, что Вы не можете отключить язык содержимого по умолчанию.
Мы сохранили это как отдельную настройку, чтобы упростить настройку с помощью OS environment:
HUGO_DISABLELANGUAGES="fr ja" hugo
Если у Вас уже есть список отключенных языков в config.toml
, Вы можете включить их в процессе разработки следующим образом:
HUGO_DISABLELANGUAGES=" " hugo server
Настроить многоязычный мультихост
Начиная с Hugo 0.31, мы поддерживаем несколько языков в многоузловой конфигурации. Смотрите эту проблему для получения подробной информации.
Это означает, что теперь Вы можете настроить baseURL
для каждого языка language
:
Если
baseURL
установлен на уровнеlanguage
, тогда он должен быть у всех языков, и все они должны быть разными.
Пример:
languages:
en:
baseURL: https://example.com
languageName: English
title: In English
weight: 2
fr:
baseURL: https://example.fr
languageName: Français
title: En Français
weight: 1
[languages]
[languages.en]
baseURL = "https://example.com"
languageName = "English"
title = "In English"
weight = 2
[languages.fr]
baseURL = "https://example.fr"
languageName = "Français"
title = "En Français"
weight = 1
{
"languages": {
"en": {
"baseURL": "https://example.com",
"languageName": "English",
"title": "In English",
"weight": 2
},
"fr": {
"baseURL": "https://example.fr",
"languageName": "Français",
"title": "En Français",
"weight": 1
}
}
}
С учетом вышеизложенного, два сайта будут сгенерированы как public
с их собственным корнем:
public
├── en
└── fr
Все URL-адреса (например, .Permalink
и т.д.) будут созданы из этого корня. Таким образом, на английской домашней странице выше для параметра .Permalink
установлено значение https://example.com/
.
Когда Вы запускаете hugo server
, мы запускаем несколько HTTP-серверов. Обычно в консоли Вы увидите что-то вроде этого:
Web Server is available at 127.0.0.1:1313 (bind address 127.0.0.1)
Web Server is available at 127.0.0.1:1314 (bind address 127.0.0.1)
Press Ctrl+C to stop
Живая перезагрузка и --navigateToChanged
между серверами работают должным образом.
Таксономии и Blackfriday
Таксономии и конфигурация Blackfriday также можно установить для каждого языка:
Taxonomies:
tag: tags
blackfriday:
angledQuotes: true
hrefTargetBlank: true
languages:
en:
blackfriday:
angledQuotes: false
title: English
weight: 1
fr:
Taxonomies:
plaque: plaques
title: Français
weight: 2
[Taxonomies]
tag = "tags"
[blackfriday]
angledQuotes = true
hrefTargetBlank = true
[languages]
[languages.en]
title = "English"
weight = 1
[languages.en.blackfriday]
angledQuotes = false
[languages.fr]
title = "Français"
weight = 2
[languages.fr.Taxonomies]
plaque = "plaques"
{
"Taxonomies": {
"tag": "tags"
},
"blackfriday": {
"angledQuotes": true,
"hrefTargetBlank": true
},
"languages": {
"en": {
"blackfriday": {
"angledQuotes": false
},
"title": "English",
"weight": 1
},
"fr": {
"Taxonomies": {
"plaque": "plaques"
},
"title": "Français",
"weight": 2
}
}
}
Переведите свой контент
Есть два способа управлять переводами Вашего контента. Оба гарантируют, что каждой странице назначен язык и она связана с соответствующими переводами.
Перевод по имени файла
Рассмотрим следующий пример:
/content/about.en.md
/content/about.fr.md
Первому файлу назначается английский язык и он связан со вторым. Второму файлу назначен французский язык и он связан с первым.
Их язык назначен в соответствии с кодом языка, добавленным в виде суффикса к имени файла.
Имея одинаковый путь и базовое имя файла, части содержимого связаны вместе как переведенные страницы.
Перевод по каталогу контента
Эта система использует разные каталоги контента для каждого из языков. Каталог содержимого каждого языка устанавливается с помощью параметра contentDir
.
languages:
en:
contentDir: content/english
languageName: English
weight: 10
fr:
contentDir: content/french
languageName: Français
weight: 20
[languages]
[languages.en]
contentDir = "content/english"
languageName = "English"
weight = 10
[languages.fr]
contentDir = "content/french"
languageName = "Français"
weight = 20
{
"languages": {
"en": {
"contentDir": "content/english",
"languageName": "English",
"weight": 10
},
"fr": {
"contentDir": "content/french",
"languageName": "Français",
"weight": 20
}
}
}
Значением contentDir
может быть любой допустимый путь - даже абсолютные ссылки на путь. Единственное ограничение - каталоги содержимого не могут перекрываться.
Рассмотрим следующий пример в сочетании с приведенной выше конфигурацией:
/content/english/about.md
/content/french/about.md
Первому файлу назначается английский язык и он связан со вторым. Второму файлу назначен французский язык и он связан с первым.
Их язык назначен в соответствии с каталогом контента, в котором они находятся.
Имея одинаковый путь и базовое имя (относительно каталога их языкового контента), части контента связаны вместе как переведенные страницы.
Обход ссылки по умолчанию
Любые страницы, использующие один и тот же ключ перевода translationKey
, установленный в начале, будут связаны как переведенные страницы независимо от базового имени или местоположения.
Рассмотрим следующий пример:
/content/about-us.en.md
/content/om.nn.md
/content/presentation/a-propos.fr.md
# установить на всех трех страницах
translationKey: "about"
Если установить для параметра translationKey
значение about
на всех трех страницах, они будут связаны как переведенные страницы.
Локализация постоянных ссылок
Поскольку для обработки ссылок используются пути и имена файлов, все переведенные страницы будут иметь один и тот же URL-адрес (кроме подкаталога языка).
Чтобы локализовать URL-адреса, slug
или url
параметр главного сообщения может быть установлен в любом языковом файле, отличном от заданного по умолчанию.
Например, французский перевод (content/about.fr.md
) может иметь свой собственный локализованный слаг.
Title: A Propos
slug: a-propos
Title = "A Propos"
slug = "a-propos"
{
"Title": "A Propos",
"slug": "a-propos"
}
Во время рендеринга Hugo построит как /about/
и /fr/a-propos/
, сохраняя при этом их перевод ссылки.
Пакеты страниц
Чтобы избежать дублирования файлов, каждый пакет страниц наследует ресурсы пакетов связанных переведенных страниц, за исключением файлов содержимого (файлы разметки, файлы HTML и т.д.).
Следовательно, из шаблона страница будет иметь доступ к файлам из всех пакетов связанных страниц.
Если в связанных пакетах два или более файла имеют одно и то же базовое имя, только один будет включен и выбран следующим образом:
- Файл из текущего языкового пакета, если он есть.
- Первый файл найден в пакетах в порядке языка
Weight
.
Ссылка на переведенный контент
Чтобы создать список ссылок на переведенный контент, используйте шаблон, подобный следующему:
{{ if .IsTranslated }}
<h4>{{ i18n "translations" }}</h4>
<ul>
{{ range .Translations }}
<li>
<a href="{{ .Permalink }}">{{ .Lang }}: {{ .Title }}{{ if .IsPage }} ({{ i18n "wordCount" . }}){{ end }}</a>
</li>
{{ end }}
</ul>
{{ end }}
Вышеупомянутое может быть помещено в partial
(т.е. внутри layouts/partials/
) и включено в любой шаблон, будь то отдельная страница содержимого или домашняя страница. Он не будет ничего печатать, если для данной страницы нет переводов.
Вышеупомянутое также использует функцию i18n
, описанную в следующем разделе.
Список всех доступных языков
.AllTranslations
на Page
может использоваться для вывода списка всех переводов, включая саму страницу. На домашней странице его можно использовать для создания языкового навигатора:
<ul>
{{ range $.Site.Home.AllTranslations }}
<li><a href="{{ .Permalink }}">{{ .Language.LanguageName }}</a></li>
{{ end }}
</ul>
Перевод строк
Хьюго использует go-i18n для поддержки строковых переводов. Смотрите репозиторий исходного кода проекта, чтобы найти инструменты, которые помогут Вам управлять рабочими процессами перевода.
Переводы собираются из папки themes/<THEME>/i18n/
(встроенной в тему), а также переводов, находящихся в i18n/
в корне Вашего проекта. В i18n
переводы будут объединены и будут иметь приоритет над тем, что находится в папке темы. Языковые файлы должны быть названы в соответствии с RFC 5646 такими именами, как en-US.toml
, fr.toml
и т.д.
Запрос базового перевода
Изнутри ваших шаблонов используйте функцию i18n
следующим образом:
{{ i18n "home" }}
Функция будет искать идентификатор "home"
в файле i18n/en-US.toml
:
[home]
other = "Home"
Результат будет
Home
Запрос на гибкий перевод с переменными
Часто Вы захотите использовать переменные страницы в строках переводов. Для этого передайте контекст .
при вызове i18n
:
{{ i18n "wordCount" . }}
Функция передаст контекст .
идентификатору "wordCount"
в файле i18n/en-US.toml
:
[wordCount]
other = "This article has {{ .WordCount }} words."
Предположим, что в контексте .WordCount
значение равно 101. Результатом будет:
This article has 101 words.
Запрос на перевод в единственном/множественном числе
Чтобы соответствовать требованиям единственного/множественного числа, Вы должны передать словарь (карту) с числовым свойством .Count
в функцию i18n
. В приведенном ниже примере используется переменная .ReadingTime
, которая имеет встроенное свойство .Count
.
{{ i18n "readingTime" .ReadingTime }}
Функция будет читать .Count
из .ReadingTime
и оценивать, где число является единственным (one
) или множественным (other
). После этого он перейдет к идентификатору readingTime
в файле i18n/en-US.toml
:
[readingTime]
one = "One minute to read"
other = "{{.Count}} minutes to read"
Предположим, что .ReadingTime.Count
в контексте имеет значение 525600. Результат будет:
525600 minutes to read
Если .ReadingTime.Count
в контексте имеет значение 1. Результат:
One minutes to read
Если Вам нужно передать пользовательские данные: ((dict "Count" 25)
является минимальным требованием)
{{ i18n "readingTime" (dict "Count" 25 "FirstArgument" true "SecondArgument" false "Etc" "so on, so far") }}
Настройка дат
На момент написания этой статьи в Go еще не было поддержки интернационализированных локалей для дат, но если Вы поработаете, Вы можете смоделировать это. Например, если Вы хотите использовать названия месяцев на французском языке, Вы можете добавить файл данных, например data/mois.yaml
, с таким содержимым:
1: "janvier"
2: "février"
3: "mars"
4: "avril"
5: "mai"
6: "juin"
7: "juillet"
8: "août"
9: "septembre"
10: "octobre"
11: "novembre"
12: "décembre"
…затем проиндексируйте неанглийские имена дат в своих шаблонах следующим образом:
<time class="post-date" datetime="{{ .Date.Format '2006-01-02T15:04:05Z07:00' | safeHTML }}">
Article publié le {{ .Date.Day }} {{ index $.Site.Data.mois (printf "%d" .Date.Month) }} {{ .Date.Year }} (dernière modification le {{ .Lastmod.Day }} {{ index $.Site.Data.mois (printf "%d" .Lastmod.Month) }} {{ .Lastmod.Year }})
</time>
Этот метод извлекает день, месяц и год, указывая .Date.Day
, .Date.Month
и .Date.Year
и использует номер месяца в качестве ключа при индексировании файл данных названия месяца.
Меню
Вы можете определять свои меню для каждого языка независимо. Создание многоязычных меню работает так же, как создание обычных меню, за исключением того, что они определены в специфичных для языка блоках в файле конфигурации:
defaultContentLanguage = "en"
[languages.en]
weight = 0
languageName = "English"
[[languages.en.menu.main]]
url = "/"
name = "Home"
weight = 0
[languages.de]
weight = 10
languageName = "Deutsch"
[[languages.de.menu.main]]
url = "/"
name = "Startseite"
weight = 0
Рендеринг основной навигации работает как обычно. .Site.Menus
просто будет содержать меню на текущем языке. Обратите внимание, что приведенный ниже absLangURL
будет ссылаться на правильный языковой стандарт Вашего веб-сайта. Без него пункты меню на всех языках будут ссылаться на английскую версию, поскольку это язык содержимого по умолчанию, который находится в корневом каталоге.
<ul>
{{- $currentPage := . -}}
{{ range .Site.Menus.main -}}
<li class="{{ if $currentPage.IsMenuCurrent "main" . }}active{{ end }}">
<a href="{{ .URL | absLangURL }}">{{ .Name }}</a>
</li>
{{- end }}
</ul>
Отсутствующие переводы
Если строка не имеет перевода для текущего языка, Hugo будет использовать значение из языка по умолчанию. Если значение по умолчанию не установлено, будет показана пустая строка.
При переводе веб-сайта Hugo может быть удобно иметь визуальный индикатор отсутствующих переводов. Параметр конфигурации enableMissingTranslationPlaceholders
помечает все непереведенные строки меткой-заполнителем [i18n] identifier
, где identifier
это идентификатор отсутствующего перевода.
Для слияния контента с других языков (т.е. отсутствующих переводов контента) смотрите lang.Merge.
Чтобы отследить недостающие строки перевода, запустите Hugo с флагом --i18n-warnings
:
hugo --i18n-warnings | grep i18n
i18n|MISSING_TRANSLATION|en|wordCount
Поддержка многоязычных тем
Для поддержки многоязычного режима в Ваших темах необходимо принять во внимание некоторые URL-адреса в шаблонах. Если существует более одного языка, URL-адреса должны соответствовать следующим критериям:
- Заходите из встроенного
.Permalink
или.RelPermalink
- Быть построенным с помощью функции шаблона
relLangURL
или функции шаблонаabsLangURL
ИЛИ иметь префикс{{ .LanguagePrefix }}
Если определено более одного языка, переменная LanguagePrefix
будет равна /en
(или любому другому вашему CurrentLanguage
). Если не включен, это будет пустая строка (и поэтому безвредна для одноязычных веб-сайтов Hugo).