diff --git a/.htmltest.yml b/.htmltest.yml
index 53e80a8d75fb..b68ed6e7d7fb 100644
--- a/.htmltest.yml
+++ b/.htmltest.yml
@@ -72,3 +72,5 @@ IgnoreURLs: # list of regexs of paths or URLs to be ignored
- ^https://wikipedia.org/wiki/(S.M.A.R.T|Hop_)
# TODO move into content/en/blog/2023/contributing-to-otel/index.md once https://github.com/open-telemetry/opentelemetry.io/issues/3889 is implemented
- ^https://shorturl.at/vLYZ0$
+ # TODO remove the following temporary ignore rule (@chalin)
+ - ^/en/docs/specs$
diff --git a/content/ja/docs/specs/_index.md b/content/ja/docs/specs/_index.md
new file mode 100644
index 000000000000..63087076a600
--- /dev/null
+++ b/content/ja/docs/specs/_index.md
@@ -0,0 +1,12 @@
+---
+title: Specifications
+linkTitle: Specs ↗
+description: _Redirect page_
+weight: 960
+# _build: { render: link }
+redirect: /en/docs/specs 301!
+redirects: [{ from: '*', to: '/en/docs/specs/:splat' }]
+default_lang_commit: 3b44fbfa49ced919daea01123abfaed836d2d0ec
+---
+
+Netlify redirect target: [{{% param "title" %}}]({{% param "redirect" %}}).
diff --git a/layouts/index.redirects b/layouts/index.redirects
index 280f6762074b..7e7f4ac8073a 100644
--- a/layouts/index.redirects
+++ b/layouts/index.redirects
@@ -72,3 +72,7 @@
{{ end }}
{{ return $result }}
{{ end }}
+
+{{/* Multilingual support */ -}}
+
+{{ partial "redirects/sites.redirects" . | partial "func/trim-lines.html" -}}
diff --git a/layouts/partials/func/trim-lines.html b/layouts/partials/func/trim-lines.html
new file mode 100644
index 000000000000..db3b92212962
--- /dev/null
+++ b/layouts/partials/func/trim-lines.html
@@ -0,0 +1,3 @@
+{{ range split . "\n" -}}
+{{ trim . " \t" }}
+{{ end -}}
diff --git a/layouts/partials/redirects/sites.redirects b/layouts/partials/redirects/sites.redirects
new file mode 100644
index 000000000000..70aa516cb9b5
--- /dev/null
+++ b/layouts/partials/redirects/sites.redirects
@@ -0,0 +1,41 @@
+{{/* Redirect for default language when .LanguagePrefix is empty. */ -}}
+
+{{ $defaultLang := "" -}}
+{{ with .Site.Sites.Default -}}
+ {{ if eq .LanguagePrefix "" -}}
+ {{ with .Language.Lang -}}
+ {{ $defaultLang = . -}}
+ /{{ . }} /
+ /{{ . }}/* /:splat
+ {{ end -}}
+ {{ end -}}
+{{ end -}}
+
+{{/* Process non-default languages. */ -}}
+
+/ja/docs/specs /en/docs/specs/ 301!
+
+{{ range after 1 .Sites -}}
+
+ {{ $siteLang := .Language.Lang -}}
+
+ # Site localization {{ $siteLang }}
+ {{ range $p := .Pages -}}
+
+ {{ range $p.Params.redirects -}}
+ {{ $fallbackPage := partial "i18n/fallback-page.html" $p -}}
+ {{ if or (eq $siteLang $defaultLang) (not $fallbackPage) -}}
+ {{ $from := cond (strings.HasPrefix .from "/")
+ .from
+ (print $p.RelPermalink .from) -}}
+ {{ $to := cond (strings.HasPrefix .to "/")
+ .to
+ (print $p.RelPermalink .to) -}}
+ {{ $from | printf "%-35s" }} {{ $to }}
+ {{ else -}}
+ {{/* # {{ $p.RelPermalink }} */ -}}
+ {{ end -}}
+ {{ end -}}
+ {{ end -}}
+
+{{ end -}}