I18n: A way to force page generation even if it isn't translated?

So, I have a bi-lingual site with English as the default language and some content:

content/
      page1.md
      page1.ru.md
      page2.md

Hugo generates me a nice site with Russian version in /ru/.

English version has a homepage, page1 and page2, all neat and clean, with menus and footers in English.

Russian version has a homepage and page1, all nice and clean, with menus and footers in Russian.

What I want is for Russian version to have a page2, too; with the content borrowed from English version untranslated, but with menus and footer in Russian.

I have a feeling it is doable with lang.Merge, but I can’t really understand how exactly.

I’ll start off by admitting I hadn’t used the I18n functionality but had read through it a few times in the past, but I did just whip up a test to make sure it worked as I thought.

Short Answer: I don’t think you can do exactly what you are hoping.

The reason is that Hugo won’t generate a file without an existing file in the content/ directory. It’s one-to-one in my understanding (came up recently). So without a page2.ru.md file, no corresponding page2/index.html file will be created (assuming you’re using prettyURLs).

Workarounds: A few ideas come to mind.

Basic Option
What lang.Merge can do for you is automatically create links to the EN pages for any missing RU pages. But they would have english menus etc. So with your given files

    {{ range .Site.RegularPages }}
        <li><a href="{{.Permalink}}">{{.Title}}</a></li>
    {{ end }}

Will output 2 links on an en list page, but only 1 on the same ru list page. If you changed it to be

    {{ $pages := .Site.RegularPages }}
    {{ range .Site.Home.Translations }}
      {{ $pages = $pages | lang.Merge .Site.RegularPages }}
      {{ range $pages }}
        <li><a href="{{.Permalink}}">{{.Title}}</a></li>
      {{ end }}
    {{ end }}

The ru list page would show two links, one to the ru page1, and the second to the en page2. It does not automatically create a ru page2 with the content from page1.md. That’s probably the confusion from the documentation, and not what you were trying to achieve.

Better Option (probably)
What you could do is have a script outside hugo which creates a link to the missing file, which hugo would then see and process correctly.

On the command line, I created a link (an alias) from page2.md to page2.ru.md and hugo successfully created the page2.ru/index.html file correctly. I tested this on macOS using both hard (ln page2.md page2.ru.md) and symbolic (ln -s page2.md page2.ru.md) commands and they worked.

What didn’t work is an alias created via the Finder either with File → Make Alias or option-command-dragging. This is probably because those are not unix based links, they are actual files containing some info the Finder interprets as an alias, but hugo doesn’t.

The easiest way I see to do this is use a script outside hugo. The basic steps would be:

get a list of all items recursively of the content directory
  for each item:
    if it's a .md file AND a matching .ru.md file DOES NOT exist:
      create a link

Honestly fairly trivial unix type script, thought my shell scripting skills are not strong, there’s lots of examples of this type of script. Run it before building hugo, and you’re all set. You could probably easily add a step to log any links it finds to a file so you have a list of content that isn’t translated if there was a process to work through them.

[Edit] Just had another thought. The script could copy the files instead of creating the link. Advantages:

  • you have real files ready to be translated if you wanted

Cons:

  • if the content in the en version changes, they will become out of sync
  • loose the ability to easily list the files that are missing translations unless the script also echoed some parameter into the front matter marking them as a copy. Then grep from the command line or a “hidden” list page in hugo could find all content with that parameter.

I’d use a link, but your workflow/use case might benefit from the other.

Alternative
One last option comes to mind, but this seems extremely impractical. Instead of file based translations, hugo supports individual string translations. Although I don’t have a i18n translation file to test with, the docs are pretty clear:

If a string does not have a translation for the current language, Hugo will use the value from the default language.

But to make use of this, the entire content of the missing pages would have to be managed in the i18n strings file, which seems extremely cumbersome, and maybe not even possible since I can’t test. You’d have to get the content from i18n and then process it for markdown, etc etc.

Ok, thought of one other option, but I’d only give it a Good rating, think the links outside hugo will be simplest.

You could use the .Page.Translations to get the linked translation page associated with the current page, and use .GetPage and .Page.Contents to get it’s content and render it. (all this is untested)

{{ if .IsTranslated }}
{{ range .Translations }}

This would also STILL require you to create the missing page2.ru.md files so Hugo has something to process. And then you’d have to modify your templates to look for pages with no content and a translation, and in those cases pull the content from the other page.

Linking would be much easier, but added this for completeness.

Thank you for your thorough and comprehensive answers!

I was rather hoping to avoid having to generate files for empty page translations, but you’ve confirmed my suspicion: there needs to be a page in the language in question for Hugo to even consider generating it.

Your brilliant idea about automatically creating symlinks for all the missing pages somehow just didn’t cross my mind at all. I is indeed fairly simple to script, and in my workflow can even be done on deployment phase, leaving the Git repository nice and clean.

The other options you’ve outlined are indeed less clean and more cumbersome, at least for my needs, but I can fathom some examples where those may end up preferable. For my use case, a script for automatic symlink generation should be exactly what is asked for.

Thank you!

Actually there is no default hugo solution. I’ve spend a lot of time to figure out how to reach the goal and don’t copy data content all the time for all languages, because it’s is a way to difficult to controll different versions.

So I simply check if it’s an internal link and the page under the current language exists and if so then I create a link for this language if no then I simply assign a default one.

{{ $link := .link }}
{{ if and ( $.Site.GetPage ( substr ( $link ) 0 -1 ) ) ( ne ( $.Site.BaseURL | relLangURL ) "/" ) ( not ( in $link "://" ) ) }}
    {{ $link = ( print ( $.Site.BaseURL | relLangURL ) $link ) }}
{{ end }}

All my links have / symbol in the end, so if your links look like /blog and not as /blog/ then just change ( substr ( $link ) 0 -1 ) to just $link .

I’m a hugo newbie, sorry if it’s not elegant solution but it works for me so far.