From Jekyll to Hugo: A Bilingual Site Migration on AI age, Step by Step

systems development

Moving a long-running site from one static-site generator to another is not just a matter of changing a build command. Content, URLs, templates, language variants, deployment, and reader expectations all need to make the journey safely.

This article walks through the Jekyll-to-Hugo migration of this site, explains the September 2026 commits that recorded the work, and turns the experience into a process a person can repeat—with or without an AI assistant.

The migration in this repository

The starting point was a Jekyll site with years of Portuguese posts and an existing URL history. The goal was to replace Jekyll with Hugo, preserve the archive, add English translations, make English the default language, and keep Portuguese available under /pt/.

On September 27, the initial migration commit, 36a8a64, replaced the Jekyll project with a bilingual Hugo site. Its commit message records the migration of the Portuguese archive, English translations, localized navigation, and an initial GitHub Pages workflow. The change replaced the Jekyll configuration and theme structure with Hugo configuration, layouts, content, and static assets.

The deployment plan then changed. 7500f13 kept Hugo’s generated build lock out of version control, and 9a6d8c5 adjusted the initial Pages workflow permissions. Once Cloudflare Pages became the deployment target, cb749dc set the canonical domain to kindofidea.com and removed the unused GitHub Pages workflow.

The site was made more complete in follow-up commits: ca2c99a refreshed its Kind of Idea branding and fonts; 0f5833e added author profiles; and 7a780f4 and 4072f88 gave posts and static pages language-specific URLs. The shared translationKey connects each English and Portuguese page while each language keeps its own slug.

Documentation and polish were recorded too: 6b1c0c3 and badbaf4 documented the bilingual publishing workflow and Hugo commands; a28e14c improved article reading width; b19fbd5 introduced the handwritten navy K favicon; and, on September 28, 59caac3 added instructions for setting up Cloudflare Pages from scratch. These commits show that migration is a sequence: move the content first, then make routes, deployment, and the reader experience reliable.

A repeatable migration, step by step

1. Inventory before changing files

Read the Jekyll configuration, layouts, includes, plugins, assets, drafts, and representative posts. Record the current domain, permalink rules, feeds, taxonomies, and any integrations that matter. Take a backup and start from a clean Git branch so the original site remains recoverable.

Do not assume every Markdown file is interchangeable. Jekyll Liquid tags and template variables may need Hugo shortcodes or Go templates. Identify those cases before doing a bulk conversion.

2. Decide the content and URL model

Choose the default language and the URL shape for each language before converting posts. In this site, English uses the root domain and Portuguese uses /pt/. A pair of translated files can use localized filenames and slugs, but should share one translation key:

slug: "from-jekyll-to-hugo-bilingual-site-migration"
translationKey: "from-jekyll-to-hugo-bilingual-site-migration"

For the Portuguese file, keep the same translationKey and set a Portuguese slug, for example de-jekyll-para-hugo-migracao-bilingue-passo-a-passo. This lets the language switcher connect the pages without forcing both URLs to use the same language.

3. Configure Hugo and migrate in small batches

Install Hugo, create or update hugo.toml, and configure the base URL, languages, taxonomies, and permalinks. Migrate a representative post first. Check that its date, title, slug, categories, tags, images, and links survive before converting the rest of the archive.

Keep the original files or a backup until the generated site has been checked. Convert Jekyll-specific template behavior deliberately rather than copying Liquid markup into Hugo layouts. For large archives, automate repetitive front matter changes, then inspect the results and manually review edge cases.

4. Add translations and localized static pages

Use Hugo’s content command to create a matching pair of posts:

hugo new content/posts/2026-09-28-from-jekyll-to-hugo-bilingual-site-migration.en.md
hugo new content/posts/2026-09-28-de-jekyll-para-hugo-migracao-bilingue-passo-a-passo.pt.md

Write each article in its own language. Keep translationKey identical, but localize the title, body, and slug. Apply the same pairing approach to static pages such as About, Archive, and Categories. Confirm the language switcher leads to the corresponding translation rather than a generic home page.

5. Build and inspect the actual output

Run the local preview and production build:

hugo server
hugo --minify

Check both languages, representative old and new posts, category and tag pages, navigation, images, feeds, and 404 behavior. Inspect the generated files in public/ and verify that important legacy URLs either remain valid or have an intentional redirect. A successful build is necessary, but it does not by itself prove that every route or translation is correct.

6. Connect deployment only after validation

For Cloudflare Pages, connect the GitHub repository, choose the production branch, and configure the Hugo build command and public output directory. This project uses master, repository root /, and hugo --minify. Add a custom domain through Cloudflare’s Pages settings, follow its DNS and certificate instructions, and verify HTTPS and both language routes after deployment.

Push a small, reviewed change first if you are uncertain about the deployment settings. Check the Pages deployment logs and the live site before removing the old hosting setup. Keep credentials and API tokens out of the repository.

Where AI fits and where it does not

The migration was made using an AI assistant: Copilot SDK in VS Code. The role of the AI agent was to inspect the existing project, help transform its content and templates, make the requested bilingual and deployment changes, and run build and route checks. The commits provide a public record of the resulting work; they do not mean an AI can replace review or ownership of the site.

A useful AI-assisted workflow is conversational but still test-driven:

  1. Ask the assistant to inventory the source project and report the content types, URL rules, templates, and risks before editing.
  2. Agree on the target language and URL model, and migrate a small sample before asking for a bulk conversion.
  3. Ask for focused changes in reviewable batches. Preserve post dates and metadata, and verify translations rather than accepting generated text blindly.
  4. Run Hugo builds and inspect the generated URLs, language links, and representative pages. Fix specific failures and rerun the checks.
  5. Review the diff, commit only the intended files, push when ready, and check the actual Cloudflare deployment.

The same checklist works without AI: inspect, plan, migrate, build, review, and deploy. AI can reduce repetitive work and help explain unfamiliar templates, but the human still chooses what the site should do, checks factual content and URLs, protects credentials, and decides when a change is ready to publish.