Sitio multilenguaje: un solo árbol de contenido
Como implementar um site multilíngue em Hugo usando uma única árvore de conteúdo e data files
Depois de mudar o site para HUGO no ano passado e entender melhor esta tecnologia/framework, quis resolver alguns problemas e ver se é possível servir conteúdo multilíngue de forma simplificada, ou seja, a partir de uma única árvore de arquivos, sem multiplicar os ramos de conteúdo.
A Forma Convencional
Por padrão, o HUGO propõe fazer uma árvore de conteúdo para cada idioma. Isso implica criar uma estrutura similar para cada um:
content/
├── es/
│ ├── posts/
│ └── series/
├── en/
│ ├── posts/
│ └── series/
└── pt-br/
├── posts/
└── series/
Nos casos em que o conteúdo é muito diferente entre idiomas, essa abordagem é adequada. Não é o meu caso.
A maior penalidade vem de manter 3 versões de cada arquivo.
As Séries
Outra coisa que eu queria fazer é criar séries simples de artigos, algo que ajude a manter uma navegação e fluxo de informação coerente entre um conjunto de artigos relacionados a um tema. Este post faz parte de uma série, e vou falar sobre isso em outro artigo mais adiante.
Data Files + Templates Inteligentes
Para resolver o conteúdo multilíngue, finalmente implementei um sistema híbrido que usa arquivos de dados YAML para conteúdo multilíngue e templates Hugo que selecionam automaticamente o idioma correto.
Estrutura Final
content/
└── 20250810_meu_post.md # Apenas metadados
data/
└── 20250810_meu_post.yaml # Conteúdo em 3 idiomas
layouts/
├── _default/
│ ├── single.html # Renderiza a partir de data files
│ └── summary.html # Trechos multilíngues
└── partials/
└── content-renderer.html # Lógica de seleção de idioma
Arquivo de Conteúdo (.md)
O arquivo .md contém apenas metadados e uma referência:
---
title: 'Antena Moxon para 6m'
date: 2025-08-10
categories: ["Radio"]
tags: ["Antena", "VHF"]
---
Este post usa data files para mostrar conteúdo diferente de acordo com o idioma do usuário.
Data File (.yaml)
Todo o conteúdo real está no arquivo YAML:
title:
es: 'Antena Moxon para 6 metros'
en: 'Moxon Antenna for 6 meters'
pt: 'Antena Moxon para 6 metros'
content:
es: |
La Moxon es una direccional simple de dos elementos...
en: |
The Moxon is a simple two-element directional antenna...
pt: |
A Moxon é uma antena direcional simples de dois elementos...
Não estou convencido desta estrutura. Logo de cara, penso que não é necessário, e não sei se é conveniente, usar o arquivo em /data para o conteúdo traduzido, provavelmente voltarei a ter todo o conteúdo em todos os idiomas no arquivo .md.
Template de Renderização
O template detecta automaticamente o idioma e renderiza o conteúdo apropriado:
{{ $currentLang := .Language.Lang }}
{{ $contentKey := .File.BaseFileName }}
{{ $dataFile := index .Site.Data $contentKey }}
{{ if $dataFile }}
{{ $title := index $dataFile.title $currentLang }}
{{ $content := index $dataFile.content $currentLang }}
<h1>{{ $title }}</h1>
<div class="content">
{{ $content | markdownify }}
</div>
{{ end }}
Benefícios do Sistema
✅ Manutenção Simplificada
- Um único arquivo por post (o .yaml)
- Uma única URL servindo 3 idiomas
- Sem duplicação de estrutura de diretórios
✅ SEO Otimizado
- URLs limpas:
/es/post/,/en/post/,/pt-br/post/ - Hreflang automático entre versões
- Conteúdo único por idioma (não duplicado)
✅ Experiência do Usuário
- Troca instantânea de idioma (mesma URL)
- Consistência na navegação
- Fallback inteligente se faltar tradução
✅ Fluxo de Trabalho Eficiente
- Escrevo em espanhol
- Sistema traduz automaticamente para EN/PT-BR
- Uma única publicação para os 3 idiomas
Implementação Técnica
O coração do sistema está na lógica de templates que:
- Detecta o idioma atual (
{{ .Language.Lang }}) - Busca o data file correspondente
- Extrai conteúdo no idioma apropriado
- Renderiza com fallback se faltar tradução
{{/* Buscar conteúdo no idioma atual */}}
{{ $content := index $dataFile.content $currentLang }}
{{/* Fallback para espanhol se não existir tradução */}}
{{ if not $content }}
{{ $content = index $dataFile.content "es" }}
{{ end }}
{{ $content | markdownify }}
Resultados
O site agora serve automaticamente conteúdo em espanhol, inglês e português a partir de uma única base de código. Os leitores podem acessar os artigos em seu idioma preferido, mas eu só preciso manter um conjunto de arquivos.
git hooks
Como a principal vantagem disso é que publicar um post é muito simplificado (novo arquivo, escrever, git add, git commit, git push), fazer as traduções é definitivamente um ponto de fricção adicional e significativo. Por isso vou implementar um script que use a API de alguma IA para fazer as traduções, veremos como isso funciona.
URLs do site:
O sistema funciona de forma transparente para o usuário final, mas simplifica enormemente o trabalho de manutenção.