Step 3
: Create the content adapter.
{{< code file=content/books/_content.gotmpl copy=true >}}
{{/* Get remote data. */}}
{{ $data := dict }}
{{ $url := "https://gohugo.io/shared/examples/data/books.json" }}
{{ with resources.GetRemote $url }}
{{ with .Err }}
{{ errorf "Unable to get remote resource %s: %s" $url . }}
{{ else }}
{{ $data = . | transform.Unmarshal }}
{{ end }}
{{ else }}
{{ errorf "Unable to get remote resource %s" $url }}
{{ end }}
{{/* Add pages and page resources. */}}
{{ range $data }}
{{/* Add page. */}}
{{ $content := dict "mediaType" "text/markdown" "value" .summary }}
{{ $dates := dict "date" (time.AsTime .date) }}
{{ $params := dict "author" .author "isbn" .isbn "rating" .rating "tags" .tags }}
{{ $page := dict
"content" $content
"dates" $dates
"kind" "page"
"params" $params
"path" .title
"title" .title
}}
{{ $.AddPage $page }}
{{/* Add page resource. */}}
{{ $item := . }}
{{ with $url := $item.cover }}
{{ with resources.GetRemote $url }}
{{ with .Err }}
{{ errorf "Unable to get remote resource %s: %s" $url . }}
{{ else }}
{{ $content := dict "mediaType" .MediaType.Type "value" .Content }}
{{ $params := dict "alt" $item.title }}
{{ $resource := dict
"content" $content
"params" $params
"path" (printf "%s/cover.%s" $item.title .MediaType.SubType)
}}
{{ $.AddResource $resource }}
{{ end }}
{{ else }}
{{ errorf "Unable to get remote resource %s" $url }}
{{ end }}
{{ end }}
{{ end }}
{{< /code >}}
Step 4
: Create a single page template to render each book review.
{{< code file=layouts/books/single.html copy=true >}}
{{ define "main" }}
{{ .Title }}
{{ with .Resources.GetMatch "cover.*" }}
{{ end }}
Author: {{ .Params.author }}
ISBN: {{ .Params.isbn }}
Rating: {{ .Params.rating }}
Review date: {{ .Date | time.Format ":date_long" }}
{{ with .GetTerms "tags" }}
Tags:
{{ end }}
{{ .Content }}
{{ end }}
{{< /code >}}
## Multilingual sites
With multilingual sites you can:
1. Create one content adapter for all languages using the [`EnableAllLanguages`](#enablealllanguages) method as described above.
2. Create content adapters unique to each language. See the examples below.
### Translations by file name
With this site configuration:
{{< code-toggle file=hugo >}}
[languages.en]
weight = 1
[languages.de]
weight = 2
{{< /code-toggle >}}
Include a language designator in the content adapter's file name.
```text
content/
└── books/
├── _content.de.gotmpl
├── _content.en.gotmpl
├── _index.de.md
└── _index.en.md
```
### Translations by content directory
With this site configuration:
{{< code-toggle file=hugo >}}
[languages.en]
contentDir = 'content/en'
weight = 1
[languages.de]
contentDir = 'content/de'
weight = 2
{{< /code-toggle >}}
Create a single content adapter in each directory:
```text
content/
├── de/
│ └── books/
│ ├── _content.gotmpl
│ └── _index.md
└── en/
└── books/
├── _content.gotmpl
└── _index.md
```
## Page collisions
Two or more pages collide when they have the same publication path. Due to concurrency, the content of the published page is indeterminate. Consider this example:
```text
content/
└── books/
├── _content.gotmpl <-- content adapter
├── _index.md
└── the-hunchback-of-notre-dame.md
```
If the content adapter also creates books/the-hunchback-of-notre-dame, the content of the published page is indeterminate. You can not define the processing order.
To detect page collisions, use the `--printPathWarnings` flag when building your site.
[content formats]: /content-management/formats/#classification
[front matter field]: /content-management/front-matter/#fields
[logical path]: /getting-started/glossary/#logical-path
[media type]: https://en.wikipedia.org/wiki/Media_type
[page kind]: /getting-started/glossary/#page-kind
[syntax]: /templates/introduction/
[template functions]: /functions/