404 in Multilingual site

Theme: Hugoplate.

I have 404.html and 404.fr.html in /layouts.

My site doesn’t have all content translated so when the user goes to example.com/fr/foo and there’s no content, he gets a 404 page. However, the 404 is an English 404 (404.html), not the 404.fr.html. Of course this being an English 404, the menu changes to English.

What am I missing here?

In public/fr/404.html it is the correct, French 404 page but it’s not being served when the user triggers a 404 page from under /fr path.

2 Likes

On my website I found that I need to use single 404.html for all languages and whatever need to be translated in it, I am using with i18n like:

<h1 class="e404">404</h1>
          <p class="e404">{{ T "404"}}</p>

Example for en and pl of the same file.

ps. file is places directly in /layouts folder.

For some reason I haven’t figured out why sometimes 404.en.html was not working and as i18 worked, I sticked with single file with translations that way.

1 Like

This approach didn’t work for me. I even checked with
<p>Current language: {{ .Site.Language.Lang }}</p>
and it returns en even if the 404 is example.com/fr/foo.

This is a known issue:
https://github.com/gohugoio/hugo/issues/10400

There’s a longer story behind this, but for now either:

  1. Use one template (layouts/404.html), and localize with the i18n function
  2. Create a content page (content/xx/errors/404.md) for each language, with permalinks in site config to publish them to the right place.
1 Like

Can you please explain a bit more about your 2nd option? First one’s not working for me.

The first approach works great. Try it:

git clone --single-branch -b hugo-forum-topic-49273 https://github.com/jmooring/hugo-testing hugo-forum-topic-49273
cd hugo-forum-topic-49273
hugo
cat public/de/404.html
cat public/en/404.html
1 Like

For the second approach…

content/
└── errors/
    ├── 404.de.md
    └── 404.en.md

In the above, add other error pages as needed (400, 401, 403, etc.).

content/errors/404.de.md

+++
title = '404'
url = '404.html' 
+++
This is content/errors/404.de.md

config

[[cascade]]
[cascade.build]
list = 'never'
render = 'never'
[cascade._target]
path = '/errors'

[[cascade]]
[cascade.build]
list = 'never'
[cascade._target]
path = '/errors/**'
1 Like

I know i18n should work, but it doesn’t (does work for other variables on the site). Maybe I’m not seeing something obvious.

en.yaml:

- id: error404
  translation: Page not found

- id: error404Message
  translation: The page you are looking for might have been removed, had its name changed, or is temporarily unavailable.

fr.yaml:

- id: error404
  translation: Page non trouvée

- id: error404Message
  translation: La page que vous recherchez a peut-être été supprimée, son nom a changé ou est temporairement indisponible.

In layouts/404.html:

<h1 class="h2 mb-4">{{ i18n "error404" }}</h1>
  <div class="content">
  <p>{{ i18n "error404Message" }}</p>
</div>

This construct obviously works fine on the example site I posted, and I updated the example to match your description:

git clone --single-branch -b hugo-forum-topic-49273 https://github.com/jmooring/hugo-testing hugo-forum-topic-49273
cd hugo-forum-topic-49273
rm -rf public/ && hugo && cat public/{en,fr}/404.html

We’d have to look at your source to figure out what’s different.

Also, you can reduce your i18n files to:

error404: Page non trouvée
error404Message: La page que vous recherchez a peut-être été supprimée, son nom a changé ou est temporairement indisponible.

See YAML examples:

Hi,
I know that you may received answer on that, but I will extend mine approach.
Its working on mine with {{ .Site.Language.Lang }} perfectly well but I got some other things that may be crucial

To check it in Local build I got created file config/development/server.html that contain:

[[redirects]]
	from = '/en/**'
	to = '/en/404.html'
	status = 404

[[redirects]]  # Default language should be last.
	from = '/**'
	to = '/404.html'
	status = 404

For my production (I am hosting on Netlify) I set following entry in _redirects file:

/en/* /en/404.html 404

So it returning right language for right page.

It works if you directly go to the 404 page url (http://localhost:1313/fr/404.html). but if you try to go to a non-existed page like http://localhost:1313/fr/foo, it will redirect to a default 404. you can try it.

1 Like

I’ve setup a repo with a plain vanilla theme I’m using and it’s being hosted on GitHub Pages to demo what I mean.

When you switch to French on the home page, click on Get a Quote button. This page doesn’t exist in French but it shows an English 404 even though the URL is:

https://ruskinik.github.io/hugoplate-404/fr/contact

While on this page, if you switch to French now, it will show the correct 404.

This is not how users behave though. When they land on a 404, last thing they want is to translate this page.

The redirect is the problem.

Would be nice if example.com/fr/foo served a French 404, not the default one.

Live working example

http://dariusz.wieckiewicz.org/en/non-existed-page

Will refer to 404 page that belong to English language

Where this

https://dariusz.wieckiewicz.org/non-existed-page will refer to 404 in Polish.

You sure you set all correctly in your environment and where you host site?

GitHub Pages may handle 404 approach differently than Netlify hence you need to look at documentation for GP.

1 Like

I used Github Pages just to replicate the problem and allow others to have a look at the source code.

Here’s what I did:

git clone --recurse-submodules https://github.com/ruskinik/hugoplate-404
cd hugoplate-404/
npm install
hugo

When I do this:

grep Page public/404.html 

I get this:

<h1 class="h2 mb-4">Page not found</h1>

When I do this:

grep Page public/fr/404.html 

I get this:

<h1 class="h2 mb-4">Page non trouvée</h1>

So the i18n approach to multilingual 404 pages works fine. I suspect, as @idarek described, that your production server or hosting environment either is not or cannot be configured to handle this. For example, with GitHub Pages, you can have only one error file: a 404.html file at the server root.

@tfsomrat

There are two distinct issues here.

1) Creating a 404 page for each language

As I mentioned above, https://github.com/gohugoio/hugo/issues/10400 prevents you from creating language-specific 404 templates. Instead, use either of the approaches I described: call the i18n function, or create error page(s) in content.

2) Configuring your server or hosting environment

See comments from @idarek above.

For development, see https://gohugo.io/getting-started/configuration/#configure-server.

config/development/server.toml
# Assumes defaultContentLanguageInSubdir is true

[[redirects]]
from = '/fr/**'
to = '/fr/404.html'
status = 404

[[redirects]] # Default language must be last.
from = '/**'
to = '/en/404.html'
status = 404

For production, refer to the documentation for your server or hosting environment. Some providers allow you to configure error pages and/or redirects. GitHub Pages does not have provisions for either.

1 Like

Kicking a dead horse here, but I just tested https://github.com/gohugoio/hugo/issues/10400 again.

The issue was resolved with v0.123.0, so you can do this now:

layouts/
├── _default/
│   ├── baseof.html
│   ├── home.html
│   ├── list.html
│   └── single.html
├── 404.en.html
└── 404.fr.html

I’ve updated the example site:

git clone --single-branch -b hugo-forum-topic-49273 https://github.com/jmooring/hugo-testing hugo-forum-topic-49273
cd hugo-forum-topic-49273
hugo server
1 Like

Solution that worked for me (main language English + 4 other languages).

  • Single 404.html in layouts
  • Added config/development/server.toml for local redirects
  • In server.toml (French as an example):
[[redirects]]
	from = '/fr/**'
	to = '/fr/404.html'
	status = 404

[[redirects]]  # Default language should be last.
	from = '/**'
	to = '/404.html'
	status = 404
  • For redirects on Netlify, in netlify.toml (German as an example):
[[redirects]]
from = "/de/*"
to = "/de/404.html"
status = 404

Thanks to @idarek, @tfsomrat, and @jmooring for help.

1 Like

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.