---
title: 'v0.1.0-next.45'
description: 'Baseline stops loading your .env, translations move onto the page, three filters lose a prefix, and a pre-pass bug that switched dev into build mode.'
slug: '0-1-0-next-45'
type: 'page'
lang: 'en'
url: 'https://www.eleventy-baseline.dev/release-notes/0-1-0-next-45/'
---

Two strands of work in one release. Half of it is Baseline claiming something about your site that was not so: an origin anchored to the wrong place, a breadcrumb trail pointing at itself, a debug page asking to be indexed. The other half is multilingual: a page can now list its own translations without going through a collection, and the strings in your templates get somewhere to live.

Four breaking changes, which is more than usual. The first one asks you to change a file before anything else works, so it goes first.

---

## Breaking

{% stepsBlock 'compact' %}

- **Baseline no longer loads your `.env` file.**

  Importing the plugin used to run `dotenv` as a side effect, which put your environment variables in place before your own files were evaluated. That is gone.

  This only affects you if you keep a `.env` file. If your variables come from npm scripts or your host, nothing changes. If you do keep one, install `dotenv` and import it at the top of whichever file reads the values, normally `_data/settings.js`:

  ```js
  import 'dotenv/config';
  ```

  Keep that import in the same file as the read rather than in `eleventy.config.js`. A file that loads its own environment works whatever imported it, in whatever order; one that relies on someone else having done it first depends on the order of two import lines.

  - _Symptom: a startup warning that `settings.url` is missing, and pages built without canonical, Open Graph or JSON-LD._

- **The three translation filters lost their `i18n` prefix.**

  `i18nTranslationsFor`, `i18nTranslationIn` and `i18nDefaultTranslation` are now `translationsFor`, `translationIn` and `defaultTranslation`. Their arguments changed too, two entries down.

  The prefix was on the wrong system. Across Hugo, the Eleventy dictionary plugins and most of npm, `i18n` means string translation, and these three answer a different question: which pages are versions of each other. The name is now free for the thing it usually means, which arrives below as `t`.

- **`translationsMap` leaves are records, not pages.**

  Each entry was `{ title, url, lang, isDefaultLang, data }`, where `data` was the page's entire resolved cascade, copied per language per key and kept alive until the build finished. It is now `{ url, lang, label, title, description, isDefaultLang }`, with `label` being that language's `languageName`.

  - _If you read `entry.data.something`, either use `title` and `description`, or look the page up by `url`._

- **`collections.translations` is gone, and the three filters no longer take a collection.**

  The flat list existed only to be passed into the filters. They read the translation map directly now, so the argument went with it:

  {% raw %}

  ```nunjucks
  {{ page | translationsFor }}
  {{ page | translationIn("fr") }}
  {{ page | defaultTranslation }}
  ```

  {% endraw %}

  This also frees the word `translations` for the string tables below. If you were using the collection to list pages by language, `collections.all` grouped by `data.lang` is the replacement, and it stays yours to shape.

{% endstepsBlock %}

---

## Changed

{% stepsBlock 'compact' %}

- **Absolute URLs come from `settings.url` and nothing else.**

  Baseline used to fall back to the environment when resolving the origin. If the two disagree now, settings wins and the sitemap follows it. Deploy previews are where that bites, since host variables tend to stay pointed at production.

- **`settings.url` has to be an absolute http(s) URL.**

  A relative value like `/`, or one missing its scheme like `localhost:8080`, used to be truthy enough to pass every internal check and then reach the JSON-LD graph as relative `@id`s. It is ignored with a warning now, and an unusable value is treated the same as an absent one: no canonical, no Open Graph, no JSON-LD.

- **Breadcrumb labels come from the section's own index page.**

  A crumb for `/docs/` used to be built out of the folder name. It now reads as the page at that URL titles itself, so a folder called `docs` holding a page called "Documentation" gives you Documentation. Sections with no index page keep the folder name, sentence-cased: `core-reference` reads as "Core reference" rather than "Core Reference". Both the visible trail and the `BreadcrumbList` change together.

  Nothing to configure. Retitle a section index page and its crumb follows.

- **A site-wide share card is no longer a claim about each page.**

  `settings.seo.ogImage` used to feed `primaryImageOfPage` as well as `og:image`, so one card said it was the image representing every page on the site. It now fills the social tags only. Set `ogImage` on a page and that page gets `primaryImageOfPage` and a sized `ImageObject` node as before.

  - _Symptom: `primaryImageOfPage` and an `ImageObject` node disappearing from pages that never set their own image. An organisation logo or person photo is unaffected; those come from `schema`._

- **hreflang is built from the content graph.**

  No output change. Head used to read a map assembled by a collection; it now reads the same relationships grouped from the graph, which is where they already were.

{% endstepsBlock %}

---

## Added

{% stepsBlock 'compact' %}

- **`page.translations`, the current page's siblings in other languages.**

  A list, on the page, with no collection to pass around. Each entry carries the `url`, the `lang`, a `label` from that language's `languageName`, that translation's own `title` and `description`, and `isDefaultLang`.

  {% raw %}

  ```nunjucks
  {% for entry in page.translations %}
  	<a href="{{ entry.url }}" hreflang="{{ entry.lang }}">{{ entry.label }}</a>
  {% endfor %}
  ```

  {% endraw %}

  The current page is left out, matching Eleventy's own `locale_links` and Hugo's `.Translations`. It is built from the content graph, so a page marked `_internal`, excluded from the graph, or not rendering to `.html` is absent here. The filters still see it.

- **`t`, for translating UI strings.**

  The other half of the word. Strings live in `_data/translations/<lang>.js`, one file per language, which Eleventy auto-loads. Keys are dot-paths.

  {% raw %}

  ```nunjucks
  {{ "nav.home" | t }}
  {{ "greeting" | t({ name: "Cris" }) }}
  {{ "items" | t({ count: 7 }) }}
  ```

  {% endraw %}

  The language comes from the page, falling back to your default. `{name}` placeholders are interpolated, plural forms are selected by `count` through `Intl.PluralRules`, and a key missing everywhere renders as the key itself with a warning rather than as a blank.

  It is registered whether or not multilingual mode is on. A single-language site still benefits from one place for its labels.

- **`homeLabel`, per language.**

  The first breadcrumb was hardcoded to "Home". Set `homeLabel` on a language entry and that language's trail uses it. Unset, you get "Home".

  ```js
  languages: {
    fr: { locale: 'fr', languageName: 'Français', homeLabel: 'Accueil' }
  }
  ```

- **`root` is a reserved section segment.**

  A page whose `section` is `['root']` sits at the site root: the segment contributes no crumb, so a top-level page gets a `Home > Page` trail without `/root/` landing in its URL. It shipped undocumented in an earlier release. Worth knowing if you have a real `root` segment, because that crumb is skipped.

{% endstepsBlock %}

---

## Fixed

The home page no longer emits a breadcrumb trail. It was a two-item `BreadcrumbList` with the same URL in both entries. Language-aware, so `/fr/` and `/nl/` match `/`.

The navigator page renders one `<head>` instead of two nested inside each other. Its template wrapped `<baseline-head>` in a `<head>` of its own, and the placeholder is the head rather than something that goes inside one. Its styles now arrive through the head module like any other.

Warnings print once. They were emitted by the content-graph pre-pass and again by the real build, because the pre-pass log gate covered `info` and not `warn`. Errors are still ungated, since a pre-pass that fails may be the only place it says so.

The navigator page is `noindex, nofollow` and out of the sitemap. It was shipping `index, follow` and a canonical, and the only way to stop it was turning the navigator off.

No more raw `fatal: not a git repository` in the build log. `dateModified` falls back to a file's last commit, so Baseline asks git for one, and outside a repository git writes that line straight to the terminal before throwing. Unprefixed, in the middle of Baseline's own output, it read as the build dying. The build was always fine and wrote every file; it is quiet now, and the date chain still degrades to `datePublished` as before.

`_navigator.edges` falls back to an empty array rather than an empty object. Before the content graph exists it was `{}`, so the shape changed between the pre-pass and the real build and anything treating it as an array saw an object on the first one.

Drafts render in dev again, and images transform on request again. The content-graph pre-pass runs a second Eleventy instance, and a nested instance overwrites `ELEVENTY_RUN_MODE` for the whole process. It was defaulting to `build`, so a `--serve` run stopped looking like one: `draft: true` pages vanished and the image shortcode stopped passing `transformOnRequest`. Dev only, production was always correct. True since the pre-pass landed, so next.42 through next.44.

A page in a non-default language could inherit the default language's locale. With `languages` written as an array (`['en', 'nl']`), no entry carries a locale of its own, and the resolver reached past the page's own language straight to the site default, so a Dutch page reported `en`. It now derives from the page's language first. Object-form config with explicit locales was never affected.

The translation collections did the same walk twice per build, once each, deep-copying every translated page both times and discarding half the result each way. They share one walk now.

---

## Docs

The site got as much work as the plugin did this time.

**The tutorial is rewritten end to end.** Ten pages from an empty folder to a site in two languages, explaining each decision as it makes it rather than listing them afterwards. It ends with a draft post, a production build and a sitemap you can read.

**The chapters are restructured.** The tutorial now sits in front of the reference layer rather than replacing any of it, with the introduction staying its own chapter for anyone new to Eleventy.

**A how-to for translating UI strings**, covering `t` and where the string files live. Deployment URL checks moved into how-to as well, where it always belonged.

**Release notes are one page per version now**, listed at `/release-notes/`, rather than one page that grew.

**The install lines no longer name `eleventy-img`.** npm resolves the peer itself, and naming it was sending readers into an ERESOLVE they did not need to meet.
