HUGO v163

How I implemented a multilingual Hugo site with one file per article, no prefix for the default language, and assets alongside content.

After several experiments, I finally simplified the site structure to make it truly maintainable. Now, all multilingual content lives in a single .md file per article, assets (images, PDFs) sit next to the article, and the default language —Spanish— is served without a URL prefix.

Article organization

No more per-year folders. Each article is an .md file placed directly under content/blog/, or a folder containing an index.md and its resources. For example:

content/blog/
├── 20250601_moxon_antenna.md        # article without images
└── 20250701_bc348_restoration/      # article with photos and documents
    ├── index.md
    ├── fotos/
    │   ├── before.jpg
    │   └── after.jpg
    └── doc/
        └── schematic.pdf

If the article has no images, a single .md file is enough. If it needs attachments, create a folder with the article slug and put the files inside.

Frictionless multilingual

Hugo handles languages, but the traditional approach forced content duplication in per-language folders. On this site:

  • The default language is Spanish, served at the root (/post/).
  • Other languages are served with a prefix: /en/post/ and /pt-br/post/.
  • All translated content lives in the same .md file as front-matter fields content_es, content_en, content_pt-br. Titles, descriptions, and leads can also be set per language.

Example front matter:

content_es: |
  Este es el artículo en español.

content_en: |
  This is the article in English.

content_pt-br: |
  Este é o artigo em português.

Templates simply pick the field that matches the active language. If a translation is missing, the Spanish content is used as a fallback.

Goodbye data files

In a previous version I used external YAML files under /data/ to store translations. It turned out to be cumbersome: two files per article, extra lookups, and data files don’t appear in Hugo’s search results. I went back to putting everything in the .md file: one article, one file, all languages.

Assets next to the article

I removed static/post-images/. Now each article’s images and documents are kept in a subfolder (fotos/, doc/, etc.) inside the article folder. Benefits:

  • Resources travel with the article (easy to move, rename, or delete).
  • Relative paths in Markdown are simpler: ![before](fotos/before.jpg).
  • Images from different articles no longer mix in a single huge directory.

Single rendering template

A common renderer reads the .Page (whether a simple page or a section’s _index.md), picks the content field for the current language, and pipes it through markdownify. Summaries also grab the translated lead or the first paragraphs of the corresponding content.

Outcome

With this scheme:

  • Publishing a new article is just creating a file and writing.
  • Adding images means dropping them into a subfolder inside the article.
  • Adding a language means copying and translating a YAML block inside the same file.
  • URLs stay clean and canonical: /my-post/, /en/my-post/, /pt-br/meu-post/.

A lean, self-contained structure with no external dependencies, easy to maintain and scale.