<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <title>Ehundu News</title>
  <link href="https://ehundu.com/en/feed.xml" rel="self"/>
  <link href="https://ehundu.com/en/"/>
  <updated>2026-10-17T00:00:00+02:00</updated>
  <id>https://ehundu.com/en/</id>
  <author><name>Ehundu</name></author>
  <entry>
    <title>Ehundu 0.15.0</title>
    <link href="https://ehundu.com/en/news/0-15-0/"/>
    <updated>2026-10-17T00:00:00+02:00</updated>
    <id>https://ehundu.com/en/news/0-15-0/</id>
    <summary>The contract, reviewed before freezing it</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;The contract, reviewed before freezing it.&lt;/strong&gt; 0.15 brings no new features: it is the version in which the whole contract was read against the code, so that what 1.0 is going to promise is true. Decisions 128 to 140 of &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.15.0/docs/formato.md&quot;&gt;&lt;code&gt;docs/formato.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt; come out of it. What the engine did and was only in the code is now written down (the exact rules of shortcodes, what a link to another domain is, the order of texts, the table of letters of the &lt;code&gt;slug&lt;/code&gt; filter, what the engine writes in &lt;code&gt;salida/&lt;/code&gt;), and what was taken blindly, because it was dangerous or ambiguous, now warns or stops the build. Built with 0.14.0 and with this one, the twenty-three test sites give the same files, one by one, with no new warnings.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What now stops the build&lt;/strong&gt; (with the file and the line, in the error). A &lt;code&gt;formato&lt;/code&gt; that is not an integer greater than zero (&lt;code&gt;&amp;quot;2&amp;quot;&lt;/code&gt; in quotes, &lt;code&gt;0&lt;/code&gt;): before it was warned about and taken as 1, and it could pass off a project of another format as good. A &lt;code&gt;url&lt;/code&gt; in &lt;code&gt;sitio.yml&lt;/code&gt; with a path or with parameters (&lt;code&gt;https://ejemplo.com/sub/&lt;/code&gt;): the engine accepted it and left the site half done, and the &lt;code&gt;url&lt;/code&gt; is that of the domain, with or without the trailing slash (decisions 130 and 136). A dot in the name of a page, of a &lt;code&gt;datos/&lt;/code&gt; file or of a &lt;code&gt;_datos.yml&lt;/code&gt; when what follows the last dot is not shaped like a language code (&lt;code&gt;guia.v2.md&lt;/code&gt;, &lt;code&gt;2024.10.01-titulo.md&lt;/code&gt;): before, the file was skipped with a warning and the next deployment deleted the page from the server (decision 135).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What now warns.&lt;/strong&gt; A date (&lt;code&gt;fecha&lt;/code&gt;, &lt;code&gt;publicar&lt;/code&gt;) written with a time or a zone warns and only the day is taken, from its midnight in the zone of the site: a date is a day (decision 129). The five fields that the engine always writes in &lt;code&gt;pagina&lt;/code&gt; (&lt;code&gt;ruta&lt;/code&gt;, &lt;code&gt;contenido&lt;/code&gt;, &lt;code&gt;markdown&lt;/code&gt;, &lt;code&gt;encabezados&lt;/code&gt; and &lt;code&gt;traducciones&lt;/code&gt;) warn if the front matter uses them, and the field is ignored, as &lt;code&gt;idioma&lt;/code&gt; already was; before, they covered them silently (decision 133). &lt;code&gt;componentes&lt;/code&gt;, reserved for some time, warns if a site uses it as a field of its own, although it still reaches the template (decision 138).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What changes in the output.&lt;/strong&gt; An image linked to another domain carries &lt;code&gt;target=&amp;quot;_blank&amp;quot;&lt;/code&gt; and &lt;code&gt;rel&lt;/code&gt;, like any link written in Markdown: the figure of a single linked image and the &lt;code&gt;imagen&lt;/code&gt; shortcode with &lt;code&gt;enlace&lt;/code&gt;. The &lt;code&gt;imagen&lt;/code&gt; template receives two new variables, &lt;code&gt;externo&lt;/code&gt; and &lt;code&gt;rel&lt;/code&gt;; an &lt;code&gt;imagen&lt;/code&gt; template of the site that does not use them stays as it was written (decision 131). The &lt;code&gt;publico/&lt;/code&gt; folder stops copying what the system and Git leave behind (&lt;code&gt;.DS_Store&lt;/code&gt;, &lt;code&gt;.gitkeep&lt;/code&gt;, &lt;code&gt;Thumbs.db&lt;/code&gt;, &lt;code&gt;desktop.ini&lt;/code&gt; and any &lt;code&gt;.git&lt;/code&gt;), and &lt;code&gt;contenido/&lt;/code&gt; ignores &lt;code&gt;Thumbs.db&lt;/code&gt; and &lt;code&gt;desktop.ini&lt;/code&gt;; &lt;code&gt;.htaccess&lt;/code&gt; and &lt;code&gt;.well-known/&lt;/code&gt; are still copied (decision 138). None of the test sites changes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The persisted incremental build, safer.&lt;/strong&gt; The fingerprint that decides whether the memory in &lt;code&gt;.ehundu/&lt;/code&gt; is good for this engine now looks at the content of the engine&apos;s files and the versions of its installed dependencies (and the minor version of PHP), not only at the date of the files: a &lt;code&gt;composer update&lt;/code&gt; that changes Twig or CommonMark no longer reuses pages made with the previous version. Each file fingerprint notes when it was taken, so that a file modified while building is not taken as seen. The wait for the lock on &lt;code&gt;.ehundu/&lt;/code&gt; has a limit of one minute, and an enormous time budget in &lt;code&gt;avanzar&lt;/code&gt; or in the deployment no longer ends after the first task. Sorting by text calculates the key of each page once, instead of in every comparison. When updating, the first build is a full one (the memory from 0.14 is not valid for this one): it happens once.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What 1.0 promises and what it does not.&lt;/strong&gt; Batch building (&lt;code&gt;CompiladorPorLotes&lt;/code&gt; and its state in &lt;code&gt;.ehundu/lote.json&lt;/code&gt;) becomes &lt;code&gt;@internal&lt;/code&gt;: what is promised for spreading a build over calls is &lt;code&gt;CompiladorIncrementalEnProceso::avanzar&lt;/code&gt;, which returns the same &lt;code&gt;Lote&lt;/code&gt;. If you used it, it still works; it is just no longer promised (decision 128). &lt;code&gt;docs/biblioteca.md&lt;/code&gt; specifies the named parameters of &lt;code&gt;Desplegador::desplegar&lt;/code&gt; and what is inside &lt;code&gt;Pagina::$campos&lt;/code&gt; and &lt;code&gt;Lectura::$datos&lt;/code&gt;. The contract promises the same HTML with the same version of the engine and its dependencies, and not between versions: what changes when updating will be noted here (decision 137).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For whoever embeds the library.&lt;/strong&gt; &lt;code&gt;Formato::leer&lt;/code&gt; no longer receives the warnings: it throws &lt;code&gt;ErrorDeProyecto&lt;/code&gt; if the value is not valid. &lt;code&gt;Informe::$rehechas&lt;/code&gt; is always a number (in a full build, that of pages), not &lt;code&gt;null&lt;/code&gt;. For a deployment of your own, &lt;code&gt;Configuracion::abrir&lt;/code&gt; opens the destination the configuration says, and &lt;code&gt;PlanDeDespliegue::deSalida&lt;/code&gt; and &lt;code&gt;PlanDeDespliegue::ficheros&lt;/code&gt; trace or read the files in &lt;code&gt;salida/&lt;/code&gt; without repeating the deployment code. And &lt;code&gt;Manifiesto::leer&lt;/code&gt; rejects paths that leave the site (&lt;code&gt;..&lt;/code&gt;, a leading slash, a drive letter): the manifest lives at the destination, and whoever could write it there must not be able to make Ehundu delete something else. With &lt;code&gt;--todo&lt;/code&gt; it is rebuilt.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Documentation.&lt;/strong&gt; The record of decisions moves to the end of &lt;code&gt;formato.md&lt;/code&gt;, as appendix A, so that whoever reads the contract finds the rule before its history (the sections are not renumbered). The schema for editors has its own document, &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.15.0/docs/esquema.md&quot;&gt;&lt;code&gt;docs/esquema.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;, which separates what is format from how an editor behaves. The documentation will stay in Spanish, like the format: it is not promised in English. And &lt;code&gt;ehundu --version&lt;/code&gt; (or &lt;code&gt;-V&lt;/code&gt;) says which version it is.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.15.0&quot;&gt;This version on GitHub&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;, with the &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ehundu 0.16.0</title>
    <link href="https://ehundu.com/en/news/0-16-0/"/>
    <updated>2026-10-17T00:00:00+02:00</updated>
    <id>https://ehundu.com/en/news/0-16-0/</id>
    <summary>The final reading of the contract</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;The final reading of the contract.&lt;/strong&gt; 0.16 brings no new features: it is the version in which three independent readers checked the contract, line by line, against the code (about 250 claims, of which about 200 were true as they stood). Decisions 141 to 148 of &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.16.0/docs/formato.md&quot;&gt;&lt;code&gt;docs/formato.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt; come out of it: some ninety touch-ups to the text, which writes down what the engine did without saying so (text files are UTF-8, a registered &lt;code&gt;[nombre]&lt;/code&gt; is always a call, what &lt;code&gt;pagina.ruta&lt;/code&gt; is on a generated page, how &lt;code&gt;donde&lt;/code&gt; and &lt;code&gt;orden&lt;/code&gt; compare, what counts as empty in the schema…), rule 7 on names, and a few fixes to the code so that it does what the contract already said. Built with 0.15.0 and with this one, the twenty-three test sites give the same files, one by one, with no new warnings.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What now stops the build.&lt;/strong&gt; A name with the extension of a file you want to generate (&lt;code&gt;robots.txt.twig&lt;/code&gt;, &lt;code&gt;sitemap.xml.twig&lt;/code&gt;, &lt;code&gt;llms.txt.md&lt;/code&gt;, &lt;code&gt;tabla.csv.yml&lt;/code&gt;): before, it was skipped with a warning and an exit code of 0, and the next deployment deleted the published &lt;code&gt;robots.txt&lt;/code&gt;. The error says how to do it: a &lt;code&gt;robots.twig&lt;/code&gt; page with &lt;code&gt;url: /robots.txt&lt;/code&gt; (decision 143). A language code that the site does not declare (&lt;code&gt;contacto.fr.md&lt;/code&gt;) is still a warning.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What now warns.&lt;/strong&gt; A shortcode registered with badly written attributes (&lt;code&gt;[imagen fichero=a.png]&lt;/code&gt;, without quotes), which stayed on the page as text without anybody noticing. A &lt;code&gt;feed.coleccion&lt;/code&gt; that no page uses, which gave an empty feed with the date of 1970. A page with its extension in capitals or &lt;code&gt;.markdown&lt;/code&gt; (&lt;code&gt;LEEME.MD&lt;/code&gt;), which is copied as it is (decision 147).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What changes in the output.&lt;/strong&gt; A day without a time and without quotes in a field of your own, in &lt;code&gt;datos/&lt;/code&gt; or in &lt;code&gt;sitio.yml&lt;/code&gt; is that day in the zone of the site: with &lt;code&gt;zonaHoraria: America/Mexico_City&lt;/code&gt;, &lt;code&gt;|fecha&lt;/code&gt; wrote 18 March for 19 March (decision 144). The &lt;code&gt;imagen&lt;/code&gt;, &lt;code&gt;video&lt;/code&gt; and &lt;code&gt;archivo&lt;/code&gt; shortcodes no longer read a &lt;code&gt;fichero&lt;/code&gt; from outside &lt;code&gt;publico/&lt;/code&gt;: they warn and insert nothing. &lt;code&gt;svg()&lt;/code&gt; removes the XML declaration and the DOCTYPE even if a comment separates them. A link to &lt;code&gt;https://EJEMPLO.com&lt;/code&gt; is already internal even if the domain is in capitals. &lt;code&gt;slugify&lt;/code&gt; also works in the pattern of &lt;code&gt;agrupar&lt;/code&gt; and &lt;code&gt;paginar&lt;/code&gt;, and &lt;code&gt;paginar&lt;/code&gt; warns about a &lt;code&gt;url&lt;/code&gt; without a trailing slash even if everything fits in one chunk. A &lt;code&gt;url&lt;/code&gt; of only spaces is a blank one. None of the test sites changes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The memory of the incremental build, by records.&lt;/strong&gt; &lt;code&gt;.ehundu/memoria.bin&lt;/code&gt; is saved and read record by record, without building a whole string of the memory, which on a large site was a hundred megabytes too many (decision 141). On the heaviest of the test sites (1,204 pages, 97 MB of saved memory) the peak of PHP memory goes down from 332 to 170 MB on the first build, from 352 to 190 when changing a page and from 416 to 252 when changing a template; on two sites of 27 and 29 MB, from 102-134 to 50-82 MB. And if the saved memory does not fit in the &lt;code&gt;memory_limit&lt;/code&gt; of PHP (three times the size of the file plus 16 MB), &lt;code&gt;compilar&lt;/code&gt; builds everything without touching it and warns how far to raise it, and &lt;code&gt;avanzar&lt;/code&gt;, which cannot spread the work without it, fails with that same text instead of the PHP error. &lt;code&gt;docs/compilacion.md&lt;/code&gt; carries the measurements and what fits with 128, 256 and 512 MB.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For whoever uses the library.&lt;/strong&gt; &lt;code&gt;paginacion.cantidad&lt;/code&gt; is now called &lt;code&gt;paginacion.entradas&lt;/code&gt;, with no alias, because no site uses &lt;code&gt;paginar&lt;/code&gt; yet and the name did not say what of (decision 142; &lt;code&gt;total&lt;/code&gt; is still the one for pages). &lt;code&gt;Desplegador::desplegar&lt;/code&gt; with &lt;code&gt;$todo&lt;/code&gt; ignores &lt;code&gt;$segundos&lt;/code&gt;: spread out, it never finished. &lt;code&gt;Version::ACTUAL&lt;/code&gt; is promised. &lt;code&gt;Lote::progreso()&lt;/code&gt; of a batch that is &lt;code&gt;ocupado()&lt;/code&gt; is 0. The destination folder of a &lt;code&gt;carpeta&lt;/code&gt; deployment is created when the first file is uploaded and not when it is opened, so &lt;code&gt;--simular&lt;/code&gt; leaves nothing behind (if your code counted on opening it to create it, create it yourself). A dump of &lt;code&gt;Configuracion&lt;/code&gt; no longer shows where the private key is either, and the &lt;code&gt;sitemap.xml&lt;/code&gt; and &lt;code&gt;feed.xml&lt;/code&gt; of each language are uploaded last. &lt;code&gt;docs/biblioteca.md&lt;/code&gt; gains what is promised and was not written: the signatures of &lt;code&gt;Destino&lt;/code&gt;, &lt;code&gt;ErrorDeProyecto&lt;/code&gt;, &lt;code&gt;trazar&lt;/code&gt;, &lt;code&gt;anotar&lt;/code&gt; and &lt;code&gt;quitar&lt;/code&gt;, the semantics of &lt;code&gt;Proyecto&lt;/code&gt; and &lt;code&gt;Pagina::$campos&lt;/code&gt;, the exit codes of the console and the format of the deployment manifest.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;When updating.&lt;/strong&gt; The first build of each site that has saved memory is a full one, because how it is saved changes (once). The HTML of the sites does not change, so they do not have to be published again. If you used &lt;code&gt;pagina.paginacion.cantidad&lt;/code&gt; in a template, change it to &lt;code&gt;entradas&lt;/code&gt;: Twig does not warn about a name that does not exist.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.16.0&quot;&gt;This version on GitHub&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;, with the &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ehundu 0.11.0</title>
    <link href="https://ehundu.com/en/news/0-11-0/"/>
    <updated>2026-10-16T00:00:00+02:00</updated>
    <id>https://ehundu.com/en/news/0-11-0/</id>
    <summary>Images with several widths come out with their srcset</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Images with several widths come out with their &lt;code&gt;srcset&lt;/code&gt;.&lt;/strong&gt; Ehundu still does not process images, but now it reads the variants that another tool (an editor when you upload the photo, a separate program) leaves in &lt;code&gt;publico/&lt;/code&gt;. With the &lt;code&gt;imagenes&lt;/code&gt; section of &lt;code&gt;sitio.yml&lt;/code&gt; (&lt;code&gt;anchos: [480, 960, 1600]&lt;/code&gt;, &lt;code&gt;formatos: [avif, webp]&lt;/code&gt; and a &lt;code&gt;sizes&lt;/code&gt; if needed), the &lt;code&gt;variantes(&apos;img/foto&apos;)&lt;/code&gt; function gives &lt;code&gt;src&lt;/code&gt;, &lt;code&gt;ancho&lt;/code&gt;, &lt;code&gt;alto&lt;/code&gt;, &lt;code&gt;srcset&lt;/code&gt; and &lt;code&gt;fuentes&lt;/code&gt; to write the &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; or the &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt;, and the &lt;code&gt;[imagen …]&lt;/code&gt; shortcode and the figures in Markdown do it by themselves. The &lt;code&gt;srcset&lt;/code&gt; carries the real width of each file, because whoever generates the variants does not enlarge a narrow photo; an image without variants comes out as before, without a warning; every file that is looked for, whether it exists or not, is a dependency of the page in the incremental build, and the feed turns the addresses in each &lt;code&gt;srcset&lt;/code&gt; into complete ones (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.11.0/docs/formato.md&quot;&gt;&lt;code&gt;docs/formato.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;, §2, §7.3 and §8.2, decision 120).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;And a long list is split into pages with &lt;code&gt;paginar&lt;/code&gt;.&lt;/strong&gt; The page itself is the first one, at its address, and the engine generates the rest: &lt;code&gt;paginar: {coleccion: blog, porPagina: 10, orden: fecha desc}&lt;/code&gt;. Each one sees the entries of its chunk in &lt;code&gt;pagina.paginas&lt;/code&gt; and where it is in &lt;code&gt;pagina.paginacion&lt;/code&gt; (&lt;code&gt;actual&lt;/code&gt;, &lt;code&gt;total&lt;/code&gt;, &lt;code&gt;anterior&lt;/code&gt;, &lt;code&gt;siguiente&lt;/code&gt;, &lt;code&gt;enlaces&lt;/code&gt;…). By default they come out at &lt;code&gt;/blog/pagina/2/&lt;/code&gt;, and &lt;code&gt;paginar.url&lt;/code&gt; with &lt;code&gt;{{ numero }}&lt;/code&gt; changes the form for whoever migrates and does not want to lose addresses. A new entry that overflows the last page opens the next one by itself. The pages it generates do not enter collections or the sitemap. Splitting each &lt;code&gt;agrupar&lt;/code&gt; group is reserved (§6.4, decision 121).&lt;/p&gt;
&lt;p&gt;0.10 sites build the same: nothing changes that does not use &lt;code&gt;imagenes&lt;/code&gt; or &lt;code&gt;paginar&lt;/code&gt;. Built from scratch with 0.10.0 and with this one, the twenty-three test sites (PDFs included) give the same files, one by one.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.11.0&quot;&gt;This version on GitHub&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;, with the &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ehundu 0.11.1</title>
    <link href="https://ehundu.com/en/news/0-11-1/"/>
    <updated>2026-10-16T00:00:00+02:00</updated>
    <id>https://ehundu.com/en/news/0-11-1/</id>
    <summary>Updates symfony/yaml to 8.1.9, which fixes CVE-2026-109413</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Updates &lt;code&gt;symfony/yaml&lt;/code&gt; to 8.1.9, which fixes CVE-2026-109413.&lt;/strong&gt; The unbounded recursion of the inline YAML reader lets a single document bring the process down. The &lt;code&gt;ehundu.phar&lt;/code&gt; of 0.11.0 carried 8.0.15 inside: Ehundu&apos;s &lt;code&gt;composer.json&lt;/code&gt; calculated its &lt;code&gt;composer.lock&lt;/code&gt; as if PHP were 8.4.0, and 8.1 asks for 8.4.1 or higher. Now the &lt;code&gt;platform&lt;/code&gt; is 8.4.1 and the &lt;code&gt;.phar&lt;/code&gt; carries 8.1.9.&lt;/p&gt;
&lt;p&gt;Nothing changes in the engine or the format: built from scratch with 0.11.0 and with this one, the twenty-three test sites (PDFs included) give the same files, one by one. The minimum is still PHP 8.4. Whoever installs Ehundu with Composer and PHP 8.4.1 or higher was already receiving 8.1.x; if your project has a &lt;code&gt;composer.lock&lt;/code&gt; with 8.0, &lt;code&gt;composer update symfony/yaml&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.11.1&quot;&gt;This version on GitHub&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;, with the &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ehundu 0.12.0</title>
    <link href="https://ehundu.com/en/news/0-12-0/"/>
    <updated>2026-10-16T00:00:00+02:00</updated>
    <id>https://ehundu.com/en/news/0-12-0/</id>
    <summary>A large site is built and uploaded in several calls</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;A large site is built and uploaded in several calls.&lt;/strong&gt; A web process has a time limit, and building a thousand-page site in one go does not fit. The new &lt;code&gt;CompiladorPorLotes&lt;/code&gt; interface (with &lt;code&gt;CompiladorPorLotesEnProceso&lt;/code&gt; and &lt;code&gt;Lote&lt;/code&gt; as the result) does part of the work per call, with a budget of seconds given by the caller: &lt;code&gt;iniciar($proyecto)&lt;/code&gt; starts, &lt;code&gt;avanzar($proyecto, 20.0)&lt;/code&gt; does tasks until the budget runs out, and always at least one, and when &lt;code&gt;completo()&lt;/code&gt; the &lt;code&gt;informe()&lt;/code&gt; is that of a whole build and &lt;code&gt;salida/&lt;/code&gt; is left the same as if it had been built in one go. Where it has got to is saved in &lt;code&gt;.ehundu/lote.json&lt;/code&gt;, inside the project, with its own &lt;code&gt;.gitignore&lt;/code&gt; so that it does not clutter a repository; if the project changes halfway, the batch starts again and says so. The upload is also spread out: &lt;code&gt;Desplegador::desplegar()&lt;/code&gt; accepts a budget (&lt;code&gt;$segundos&lt;/code&gt;) and an already finished build (&lt;code&gt;$compilado&lt;/code&gt;), and &lt;code&gt;InformeDeDespliegue&lt;/code&gt; says whether it finished (&lt;code&gt;$completo&lt;/code&gt;) and how many files are left (&lt;code&gt;$pendientes&lt;/code&gt;). Each page is written as it is made, so &lt;code&gt;salida/&lt;/code&gt; mixes new and old pages until the last batch. The &lt;code&gt;ehundu&lt;/code&gt; command does not change (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.12.0/docs/formato.md&quot;&gt;&lt;code&gt;docs/formato.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;, §10.4 and decisions 124 and 125; &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.12.0/docs/compilacion.md&quot;&gt;&lt;code&gt;docs/compilacion.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A project declares which version of the format it is written for: &lt;code&gt;formato: 1&lt;/code&gt;.&lt;/strong&gt; In &lt;code&gt;sitio.yml&lt;/code&gt; (§2), and &lt;code&gt;&amp;quot;formato&amp;quot;: 1&lt;/code&gt; in the &lt;code&gt;esquema.json&lt;/code&gt; of an editor (§11). Without the key, the format is 1 and there is no warning, so no 0.11 project has to change; a value that is not an integer greater than zero is warned about and taken as 1; and one greater than the one the engine understands stops the build with a clear error, instead of blindly building a format it does not know. It is only the major version, because within one the format only grows (decision 122; class &lt;code&gt;Ehundu\Formato&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;And at last it is written down what the library promises.&lt;/strong&gt; &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.12.0/docs/biblioteca.md&quot;&gt;&lt;code&gt;docs/biblioteca.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt; lists the console command and the PHP classes that will remain stable within a major version: building, reading a project, warnings and errors, &lt;code&gt;Slug&lt;/code&gt; and deployment. The other classes carry &lt;code&gt;@internal&lt;/code&gt; and may change in any minor version. What is going to stop being valid warns for at least one minor version before being removed (decision 123). 1.0 will freeze the format, the command and that list.&lt;/p&gt;
&lt;p&gt;0.11 sites build the same: nothing changes that does not use &lt;code&gt;formato&lt;/code&gt; or batches. Built from scratch with 0.11.1 and with this one, the twenty-three test sites (PDFs included) give the same files, one by one, and built in batches they give the same ones as in one go.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.12.0&quot;&gt;This version on GitHub&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;, with the &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ehundu 0.13.0</title>
    <link href="https://ehundu.com/en/news/0-13-0/"/>
    <updated>2026-10-16T00:00:00+02:00</updated>
    <id>https://ehundu.com/en/news/0-13-0/</id>
    <summary>The incremental build leaves the process</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;The incremental build leaves the process.&lt;/strong&gt; Until now only &lt;code&gt;ehundu servir&lt;/code&gt; built incrementally, because what a build remembers did not survive the process that made it; whoever embedded the engine in a web process, such as an editing panel, had to build the whole site on every save. The new &lt;code&gt;CompiladorIncrementalEnProceso&lt;/code&gt; class (&lt;code&gt;compilar&lt;/code&gt;, &lt;code&gt;avanzar&lt;/code&gt; and &lt;code&gt;olvidar&lt;/code&gt;, &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.13.0/docs/biblioteca.md&quot;&gt;&lt;code&gt;docs/biblioteca.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;) saves what it remembers in &lt;code&gt;.ehundu/memoria.bin&lt;/code&gt;, inside the project, and the next build, even from another process, takes advantage of it: on the largest of the test sites, 1,204 pages, changing one page rebuilds one and takes about 3 s, against the 35-45 s of a full build. The result is always that of a full build: the same files and the same warnings.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;And it can be spread over calls.&lt;/strong&gt; &lt;code&gt;avanzar($proyecto, $segundos)&lt;/code&gt; rebuilds pages until the budget runs out and saves what it has done; the next call carries on. No separate state is needed, as in batch building: a memory with half the pages is a memory with pages missing, and the plan of the incremental build rebuilds the missing ones and reuses the valid ones, also if the project changes between two calls. The &lt;code&gt;Informe&lt;/code&gt; of the call that finishes counts the whole build, not just its part. The 0.12 batches stay as they were, for whoever wants to spread a full build without saving anything about the site.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The memory is ignored without an error if it is not valid.&lt;/strong&gt; If it is missing, damaged or cut off, was written by another version of the memory or of the engine (it carries a fingerprint of the size and date of its code, so updating the engine makes the first build a full one), or belongs to another project, a full build is done. It is only saved when a call ends well, and it is not written again if no page has been rebuilt. A call that arrives while another is building the same project does not spoil it: &lt;code&gt;avanzar&lt;/code&gt; returns &lt;code&gt;ocupado()&lt;/code&gt; and &lt;code&gt;compilar&lt;/code&gt; waits. The memory weighs about the same as &lt;code&gt;salida/&lt;/code&gt; plus what was read (97 MB on the 1,204-page site) and the process that loads it needs about 350 MB on that site; &lt;code&gt;.ehundu/&lt;/code&gt; carries its own &lt;code&gt;.gitignore&lt;/code&gt;, and can be deleted whenever you like (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.13.0/docs/formato.md&quot;&gt;&lt;code&gt;docs/formato.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;, §10.5, decision 126; &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.13.0/docs/compilacion.md&quot;&gt;&lt;code&gt;docs/compilacion.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;0.12 sites build the same: nothing changes that does not use the new class, and the &lt;code&gt;ehundu&lt;/code&gt; command does not use it. Built from scratch with 0.12.0 and with this one, the twenty-three test sites (PDFs included) give the same files, one by one; and built with the persisted incremental build, spread over calls and then without changes, they give the same as in one go.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.13.0&quot;&gt;This version on GitHub&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;, with the &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ehundu 0.14.0</title>
    <link href="https://ehundu.com/en/news/0-14-0/"/>
    <updated>2026-10-16T00:00:00+02:00</updated>
    <id>https://ehundu.com/en/news/0-14-0/</id>
    <summary>Pages carry an Ehundu mark</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Pages carry an Ehundu mark.&lt;/strong&gt; The engine adds &lt;code&gt;&amp;lt;meta name=&amp;quot;generator&amp;quot; content=&amp;quot;Ehundu&amp;quot;&amp;gt;&lt;/code&gt; to the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of every HTML page it writes, so that sites made with it can be recognised, as other generators do. The tag goes just before the first &lt;code&gt;&amp;lt;/head&amp;gt;&lt;/code&gt;, on its own line and with the indentation of that line plus two spaces (attached to it, if the HTML is minified). It is removed with a key in &lt;code&gt;sitio.yml&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;generador: no
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Without a version, on purpose.&lt;/strong&gt; The content is always &lt;code&gt;Ehundu&lt;/code&gt;, without the version number: with it, every update of the engine would change all the pages of all the sites and force them to be uploaded again, and whoever looks at the code of a website has no reason to know which exact version made it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What does not change.&lt;/strong&gt; A page that already carries its own &lt;code&gt;generator&lt;/code&gt; tag in the template stays as it is: the template rules. Nor does anything change for what has no &lt;code&gt;&amp;lt;/head&amp;gt;&lt;/code&gt; (a fragment, a &lt;code&gt;.txt&lt;/code&gt;, an XML), the PDF of a page, the &lt;code&gt;sitemap.xml&lt;/code&gt; or the &lt;code&gt;feed.xml&lt;/code&gt;. &lt;code&gt;sí&lt;/code&gt; or not putting the key is the same; any value that is neither &lt;code&gt;sí&lt;/code&gt; nor &lt;code&gt;no&lt;/code&gt; warns and is taken as &lt;code&gt;sí&lt;/code&gt;. Changing the key rebuilds all the pages, like any other change to &lt;code&gt;sitio.yml&lt;/code&gt; (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.14.0/docs/formato.md&quot;&gt;&lt;code&gt;docs/formato.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;, §2, decision 127).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;When updating.&lt;/strong&gt; It is the first time the engine touches the HTML that comes out of templates, and that is why every page of every site gains a line when building with 0.14: sites have to be published again once. It will not happen again from one version to the next, because the tag does not change with it. Built with 0.13.0 and with this one, the twenty-three test sites give the same files, one by one, except for that line.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.14.0&quot;&gt;This version on GitHub&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;, with the &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ehundu 0.10.0</title>
    <link href="https://ehundu.com/en/news/0-10-0/"/>
    <updated>2026-10-11T00:00:00+02:00</updated>
    <id>https://ehundu.com/en/news/0-10-0/</id>
    <summary>Formatted text in a field is converted to HTML with the markdown filter</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Formatted text in a field is converted to HTML with the &lt;code&gt;markdown&lt;/code&gt; filter.&lt;/strong&gt; Until now only the body of a page could be converted from Markdown: a headline with an italic word, or a summary with some bold, had no way of reaching the template as HTML. With &lt;code&gt;titular: &amp;quot;A kitchen that *travels*&amp;quot;&lt;/code&gt;, &lt;code&gt;{{ pagina.titular|markdown }}&lt;/code&gt; gives &lt;code&gt;A kitchen that &amp;lt;em&amp;gt;travels&amp;lt;/em&amp;gt;&lt;/code&gt;. Without an argument it is inline: Markdown without blocks, without &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt;, with emphasis, bold, strikethrough, code, links, automatic links and hand-written HTML (&lt;code&gt;&amp;lt;mark&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;br&amp;gt;&lt;/code&gt;); a line break with a backslash or two spaces at the end of the line comes out as &lt;code&gt;&amp;lt;br&amp;gt;&lt;/code&gt; (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.10.0/docs/formato.md&quot;&gt;&lt;code&gt;docs/formato.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;, §7.3, decision 113).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;And block-level, with &lt;code&gt;markdown(&apos;bloque&apos;)&lt;/code&gt;.&lt;/strong&gt; For a text with several paragraphs inside a block that the template provides: it is converted like the body, with paragraphs, lists, quotes and headings. It still has no shortcodes, and it does not turn images into figures, nor convert tables or footnotes, nor give headings an &lt;code&gt;id&lt;/code&gt;: that is for the body (decision 114).&lt;/p&gt;
&lt;p&gt;In both modes, links to other domains are treated as in the body (&lt;code&gt;target&lt;/code&gt;, &lt;code&gt;rel&lt;/code&gt; and the &lt;code&gt;enlacesExternos&lt;/code&gt; notice), the result is not escaped again, an empty value gives nothing, a number is taken as text, and a list, a map or a yes or a no warn. A mode that does not exist warns and the inline one is used.&lt;/p&gt;
&lt;p&gt;It came out of rebuilding a site with landing pages that are edited from a panel: the schema (§11) already offered the &lt;code&gt;markdown&lt;/code&gt; type for any field, but the engine had no way to paint it.&lt;/p&gt;
&lt;p&gt;0.9 sites build the same: they are new filters and change nothing that does not use them. Built from scratch with 0.9.0 and with this one, the sixteen test sites that build with 0.9 give the same files, one by one.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.10.0&quot;&gt;This version on GitHub&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;, with the &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ehundu 0.5.0</title>
    <link href="https://ehundu.com/en/news/0-5-0/"/>
    <updated>2026-10-10T00:00:00+02:00</updated>
    <id>https://ehundu.com/en/news/0-5-0/</id>
    <summary>One page for each value</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;One page for each value.&lt;/strong&gt; A page with &lt;code&gt;agrupar&lt;/code&gt; is a template of pages: the engine repeats it for each value of a field of the pages of a collection, which is what you need to have one page per tag without writing it by hand, on a blog where the next post may bring a new tag. It groups by &lt;code&gt;slug&lt;/code&gt;, so &lt;code&gt;Cultura pop&lt;/code&gt; and &lt;code&gt;cultura-pop&lt;/code&gt; are one group; it gives the template &lt;code&gt;grupo&lt;/code&gt; (the most used variant) and &lt;code&gt;paginas&lt;/code&gt; (those of the group, with the shape of the ones &lt;code&gt;coleccion()&lt;/code&gt; gives); it generates no page for a group with no published pages; and with &lt;code&gt;etiquetas: [temas]&lt;/code&gt; the generated pages form a collection that serves as an index. It runs no code from the site, it is not pagination (which stays outside v1) and the incremental build knows about it: a new tag is a new page, and editing the body of a post does not rebuild the pages of its tags (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.5.0/docs/formato.md&quot;&gt;&lt;code&gt;docs/formato.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;, §6.3, decision 105).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Publish on its date.&lt;/strong&gt; A future &lt;code&gt;fecha&lt;/code&gt; still hides nothing, like that of an event in a calendar. For a blog that is written in advance, &lt;code&gt;publicarEnSuFecha: sí&lt;/code&gt; in the &lt;code&gt;_datos.yml&lt;/code&gt; of its folder makes a page not be published before its &lt;code&gt;fecha&lt;/code&gt;: it is the same as writing &lt;code&gt;publicar&lt;/code&gt; with that date, without repeating it in every file. A subfolder removes it with &lt;code&gt;no&lt;/code&gt;, an explicit &lt;code&gt;publicar&lt;/code&gt; overrides it and &lt;code&gt;borrador&lt;/code&gt; overrides both. A held-back page generates no file and does not appear in collections, sitemap, feed or translations, and it comes out when the site is built again after its day (§4, decision 106).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;pagina.markdown&lt;/code&gt;.&lt;/strong&gt; The Markdown of a page as it is written, without the front matter and unconverted, for an &lt;code&gt;llms-full.txt&lt;/code&gt; that gathers the text of several articles. The pages that &lt;code&gt;coleccion()&lt;/code&gt; gives have it too. Whoever reads it depends on the file of that page (§7.2, decision 104).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The format, for editors.&lt;/strong&gt; The schema (§11) now describes the manual order of a type, fixed pages (&lt;code&gt;unico&lt;/code&gt;), the side menu in sections (&lt;code&gt;grupo&lt;/code&gt;, &lt;code&gt;rotulo&lt;/code&gt;, &lt;code&gt;excluir&lt;/code&gt;), drafts and the scheduling of pages from the editor (decisions 99 to 103). The engine reads none of these attributes: they are matters for editors.&lt;/p&gt;
&lt;p&gt;0.4 sites build the same, file by file. The only thing that changes is that &lt;code&gt;agrupar&lt;/code&gt; and &lt;code&gt;publicarEnSuFecha&lt;/code&gt; are now reserved fields of the front matter, and that &lt;code&gt;grupo&lt;/code&gt; and &lt;code&gt;paginas&lt;/code&gt; only exist on the pages that &lt;code&gt;agrupar&lt;/code&gt; generates.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.5.0&quot;&gt;This version on GitHub&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;, with the &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ehundu 0.6.0</title>
    <link href="https://ehundu.com/en/news/0-6-0/"/>
    <updated>2026-10-10T00:00:00+02:00</updated>
    <id>https://ehundu.com/en/news/0-6-0/</id>
    <summary>The pages of a folder, without the folder in the URL</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;The pages of a folder, without the folder in the URL.&lt;/strong&gt; The &lt;code&gt;url&lt;/code&gt; pattern now has &lt;code&gt;{{ nombreDeFichero }}&lt;/code&gt;: the file name of the page, without folder, without extension and without the language code. With &lt;code&gt;url: &amp;quot;/{{ nombreDeFichero }}/&amp;quot;&lt;/code&gt; in the &lt;code&gt;_datos.yml&lt;/code&gt; of &lt;code&gt;contenido/posts/&lt;/code&gt;, the file &lt;code&gt;mi-entrada.md&lt;/code&gt; comes out as &lt;code&gt;/mi-entrada/&lt;/code&gt; and not as &lt;code&gt;/posts/mi-entrada/&lt;/code&gt;, and a new post has nothing to write. It accepts &lt;code&gt;|slug&lt;/code&gt;, in a page with &lt;code&gt;agrupar&lt;/code&gt; it is the name of the file that generates the pages, and it overrides a field with the same name. It is what was needed to migrate a blog whose addresses never carried the folder (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.6.0/docs/formato.md&quot;&gt;&lt;code&gt;docs/formato.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;, §5.1, decision 107).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The notice for links that open another tab.&lt;/strong&gt; A link to another site written in Markdown already came out with &lt;code&gt;target=&amp;quot;_blank&amp;quot;&lt;/code&gt; and &lt;code&gt;rel=&amp;quot;noopener&amp;quot;&lt;/code&gt;. The new &lt;code&gt;enlacesExternos&lt;/code&gt; section of &lt;code&gt;sitio.yml&lt;/code&gt; adds &lt;code&gt;noreferrer&lt;/code&gt; to those links (&lt;code&gt;noreferrer: sí&lt;/code&gt;) and a hidden text for screen readers, which warns that another tab opens (&lt;code&gt;aviso: (opens in a new window)&lt;/code&gt;). On a site with several languages, the notice takes one text per language, with its code as the key, and each page uses its own. The class of the text is &lt;code&gt;visually-hidden&lt;/code&gt; or the one &lt;code&gt;clase&lt;/code&gt; says: the engine brings no CSS, so the site defines its own. A link that only wraps an image, which is a figure, does not carry it, and hand-written HTML and the links in templates are not touched. Without the section, links come out as before (§2 and §8, decision 108).&lt;/p&gt;
&lt;p&gt;0.5 sites build the same, file by file. The only thing that changes is that &lt;code&gt;nombreDeFichero&lt;/code&gt; now works in &lt;code&gt;url&lt;/code&gt; patterns, where it did not exist before, and that &lt;code&gt;enlacesExternos&lt;/code&gt; is a key of &lt;code&gt;sitio.yml&lt;/code&gt; with a meaning.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.6.0&quot;&gt;This version on GitHub&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;, with the &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ehundu 0.7.0</title>
    <link href="https://ehundu.com/en/news/0-7-0/"/>
    <updated>2026-10-10T00:00:00+02:00</updated>
    <id>https://ehundu.com/en/news/0-7-0/</id>
    <summary>An id for Markdown headings, and a contents list built from them</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;An &lt;code&gt;id&lt;/code&gt; for Markdown headings, and a contents list built from them.&lt;/strong&gt; The new &lt;code&gt;encabezados&lt;/code&gt; section of &lt;code&gt;sitio.yml&lt;/code&gt; gives an &lt;code&gt;id&lt;/code&gt; to the Markdown headings of the levels you indicate (&lt;code&gt;niveles: [2]&lt;/code&gt;, and by default from 2 to 6): the &lt;code&gt;slug&lt;/code&gt; of their text, with &lt;code&gt;-2&lt;/code&gt;, &lt;code&gt;-3&lt;/code&gt;… if it repeats on the page, and &lt;code&gt;seccion&lt;/code&gt; if nothing is left. With &lt;code&gt;enlace: ¶&lt;/code&gt; each one ends in a permanent link to its section, with the &lt;code&gt;clase&lt;/code&gt; the site defines and an &lt;code&gt;etiqueta&lt;/code&gt; for screen readers (&lt;code&gt;Link to «{texto}»&lt;/code&gt;, with one text per language on a site with several). Templates read the list in &lt;code&gt;pagina.encabezados&lt;/code&gt;, with the &lt;code&gt;nivel&lt;/code&gt;, &lt;code&gt;texto&lt;/code&gt; and &lt;code&gt;id&lt;/code&gt; of each one, to build a contents list; the pages of a collection have it too. It acts when converting the Markdown and does not afterwards walk the HTML already generated: it does not touch the headings of a shortcode that wraps content, nor those of hand-written HTML, nor those that templates put in (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.7.0/docs/formato.md&quot;&gt;&lt;code&gt;docs/formato.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;, §2, §7.2 and §8, decision 109).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The &lt;code&gt;slug&lt;/code&gt; knows &lt;code&gt;ș&lt;/code&gt; and &lt;code&gt;ț&lt;/code&gt;.&lt;/strong&gt; The table had &lt;code&gt;ş&lt;/code&gt; and &lt;code&gt;ţ&lt;/code&gt;, with a cedilla, but not the Romanian letters with a comma below, so &lt;code&gt;Ceaușescu&lt;/code&gt; gave &lt;code&gt;ceau-escu&lt;/code&gt;. Now it gives &lt;code&gt;ceausescu&lt;/code&gt; (§5.2).&lt;/p&gt;
&lt;p&gt;0.6 sites build the same, file by file. The only things that change are that &lt;code&gt;encabezados&lt;/code&gt; is a key of &lt;code&gt;sitio.yml&lt;/code&gt; with a meaning, that &lt;code&gt;encabezados&lt;/code&gt; is now a page variable that covers a front matter field of the same name, and the addresses or ids of texts with &lt;code&gt;ș&lt;/code&gt; or &lt;code&gt;ț&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.7.0&quot;&gt;This version on GitHub&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;, with the &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ehundu 0.8.0</title>
    <link href="https://ehundu.com/en/news/0-8-0/"/>
    <updated>2026-10-10T00:00:00+02:00</updated>
    <id>https://ehundu.com/en/news/0-8-0/</id>
    <summary>Texts are sorted without counting ¿ or ¡</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Texts are sorted without counting &lt;code&gt;¿&lt;/code&gt; or &lt;code&gt;¡&lt;/code&gt;.&lt;/strong&gt; When sorting with &lt;code&gt;orden()&lt;/code&gt;, the opening question and exclamation marks (the inverted ones that Spanish uses) no longer count: “¿Cómo llegar?” is sorted as “Cómo llegar?”, among the “c”s, and not at the start of the list, which is where the order of the characters sent it. Two texts that differ only by those marks tie and stay in the order they had. The rest of the punctuation counts as before, and capitals, accents and &lt;code&gt;ñ&lt;/code&gt; stay the same (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.8.0/docs/formato.md&quot;&gt;&lt;code&gt;docs/formato.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;, §6.2, decision 110).&lt;/p&gt;
&lt;p&gt;0.7 sites build the same, file by file, except those that sort by a text that carries those marks: in them, only the position of those pages in the list changes.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.8.0&quot;&gt;This version on GitHub&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;, with the &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ehundu 0.9.0</title>
    <link href="https://ehundu.com/en/news/0-9-0/"/>
    <updated>2026-10-10T00:00:00+02:00</updated>
    <id>https://ehundu.com/en/news/0-9-0/</id>
    <summary>The CSS and JavaScript of a page can be linked as a file</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;The CSS and JavaScript of a page can be linked as a file.&lt;/strong&gt; &lt;code&gt;css_fichero()&lt;/code&gt; and &lt;code&gt;js_fichero()&lt;/code&gt; do with what the page has declared the same as &lt;code&gt;css()&lt;/code&gt; and &lt;code&gt;js()&lt;/code&gt;, but instead of embedding it they write the tag that links a file with that same content: &lt;code&gt;&amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;/css/3fa9c1d2ab.css&amp;quot;&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;script src=&amp;quot;/js/7c01e4b9d3.js&amp;quot; defer&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;. The engine writes the file in &lt;code&gt;salida/css/&lt;/code&gt; or &lt;code&gt;salida/js/&lt;/code&gt; with the fingerprint of its content in the name (the first 10 characters of the SHA-256), so pages that declare the same thing share it and the browser can keep it for a year with no risk of serving an old one after a change. It is for long CSS, which embedded is repeated on every page. &lt;code&gt;css()&lt;/code&gt; and &lt;code&gt;js()&lt;/code&gt; do not change, and the engine still minifies and processes nothing (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.9.0/docs/formato.md&quot;&gt;&lt;code&gt;docs/formato.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;, §9.1, decision 111).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;version(ruta)&lt;/code&gt;&lt;/strong&gt; gives the address of a file in &lt;code&gt;publico/&lt;/code&gt; with the fingerprint of its content, &lt;code&gt;/css/estilo.css?v=3fa9c1d2ab&lt;/code&gt;, to link with a long expiry the fonts, loose JavaScript and anything else that does not go through &lt;code&gt;css()&lt;/code&gt; (§7.3, decision 112).&lt;/p&gt;
&lt;p&gt;0.8 sites build the same, file by file. The only thing that changes is the text of the warning that appears when a page declares CSS and no template writes it, which now names both ways.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.9.0&quot;&gt;This version on GitHub&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;, with the &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ehundu 0.3.2</title>
    <link href="https://ehundu.com/en/news/0-3-2/"/>
    <updated>2026-09-29T00:00:00+02:00</updated>
    <id>https://ehundu.com/en/news/0-3-2/</id>
    <summary>Dates, in German too</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Dates, in German too.&lt;/strong&gt; &lt;code&gt;fecha()&lt;/code&gt; writes months and days in German on pages in that language, capitalised as they are written, and “18. März 2025” by default; &lt;code&gt;fecha(&apos;l j F Y&apos;)&lt;/code&gt; gives “Dienstag 18 März 2025”. Until now they came out in English, and a site with &lt;code&gt;idioma: de&lt;/code&gt; warned about it on every build (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.3.2/docs/formato.md&quot;&gt;&lt;code&gt;docs/formato.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;, §15.9, decision 89).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;An impossible date inside a multi-line list no longer stops the build.&lt;/strong&gt; Since 0.3.1, a date without quotes that does not exist, such as &lt;code&gt;2026-13-01&lt;/code&gt;, is read as the text it is. One case was missing: inside a list or a map in square brackets that spans several lines, the reading of the whole YAML still failed, with an error that pointed at the closing bracket (“Cerca de «]»”). Now it is read as text there too, and the same date written inside a text is left alone (§4, decision 88).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A feed with no entries no longer changes on every build.&lt;/strong&gt; Its date (&lt;code&gt;updated&lt;/code&gt;) was the moment of the build, so an empty feed came out different every time: it was rewritten in &lt;code&gt;salida/&lt;/code&gt; and uploaded again on every deployment, and the preview could give a different feed from the one of a full build. Now it always carries 1 January 1970 in the time zone of the site. With entries nothing changes: it carries the date of the most recent one (§10.2, decision 90).&lt;/p&gt;
&lt;p&gt;0.3 sites build the same, file by file, except in two things: on pages in German, the dates that &lt;code&gt;fecha()&lt;/code&gt; writes come out in German and not in English, and a feed with no entries carries the date of 1970.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.3.2&quot;&gt;This version on GitHub&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;, with the &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ehundu 0.4.0</title>
    <link href="https://ehundu.com/en/news/0-4-0/"/>
    <updated>2026-09-29T00:00:00+02:00</updated>
    <id>https://ehundu.com/en/news/0-4-0/</id>
    <summary>The PDF of a page</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;The PDF of a page.&lt;/strong&gt; A page with &lt;code&gt;pdf: sí&lt;/code&gt; also comes out as a PDF, as well as HTML. Its own template lays it out, &lt;code&gt;&amp;lt;plantilla&amp;gt;.pdf.twig&lt;/code&gt;, with the same data as the web one but with simple HTML and CSS (the PDF supports neither &lt;code&gt;flex&lt;/code&gt; nor grid). It comes out at the address of the page with &lt;code&gt;.pdf&lt;/code&gt; instead of the trailing slash, and templates link to it with &lt;code&gt;pagina.pdf&lt;/code&gt;. It is one more output of the build, declared by the page, and not a processor over the HTML of another (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.4.0/docs/formato.md&quot;&gt;&lt;code&gt;docs/formato.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;, §10.3, decisions 96 and 97).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The PDF is generated by dompdf, which is optional.&lt;/strong&gt; It is pure PHP, with no external programs or GD, and is installed with &lt;code&gt;composer require dompdf/dompdf&lt;/code&gt; (the &lt;code&gt;dom&lt;/code&gt; extension is needed). The engine only asks for it if a page declares &lt;code&gt;pdf: sí&lt;/code&gt;; if it is missing, the build stops with an error that says so. The &lt;code&gt;ehundu.phar&lt;/code&gt; does not bring it: to generate PDFs you have to use the engine through Composer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The same project always gives the same bytes.&lt;/strong&gt; The PDF carries the date of the page (or 1 January 1970 if it has none), an identifier taken from its content and the font cache in the system&apos;s temporary folder, so it writes nothing outside &lt;code&gt;salida/&lt;/code&gt; and only reads from &lt;code&gt;publico/&lt;/code&gt;. A PDF that does not change is not uploaded again in the deployment, and the incremental build takes it into account: what dompdf reads from &lt;code&gt;publico/&lt;/code&gt; counts as a dependency of the page.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PHP files stop the build.&lt;/strong&gt; A PHP file in &lt;code&gt;publico/&lt;/code&gt; or in &lt;code&gt;contenido/&lt;/code&gt; (&lt;code&gt;.php&lt;/code&gt;, &lt;code&gt;.php3&lt;/code&gt; to &lt;code&gt;.php8&lt;/code&gt;, &lt;code&gt;.pht&lt;/code&gt;, &lt;code&gt;.phtml&lt;/code&gt;, &lt;code&gt;.phar&lt;/code&gt;, and also &lt;code&gt;foto.php.jpg&lt;/code&gt;, which some servers run) is no longer copied: the error says which one it is. A static site has no reason to carry them, and in an infected one they are the first thing that gets in (§9, decision 98). &lt;strong&gt;It is the only thing that can prevent a 0.3 site from building.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The format, for editors.&lt;/strong&gt; The schema (§11) admits a list inside a list of simple fields, &lt;code&gt;traducible&lt;/code&gt; (“translatable”) fields with one text per language, the &lt;code&gt;seleccion-multiple&lt;/code&gt; (“multiple choice”) type with the options in &lt;code&gt;datos/&lt;/code&gt;, &lt;code&gt;recuerda&lt;/code&gt; (“remembers”: what an editor keeps from the rows of a list) and &lt;code&gt;datos/&lt;/code&gt; files editable from the schema (§11.1 to §11.5, decisions 91 to 95). The engine reads none of these attributes: they are matters for editors, and the engine only reads what they write into the project.&lt;/p&gt;
&lt;p&gt;0.3 sites build the same, file by file, unless they carry PHP files in &lt;code&gt;publico/&lt;/code&gt; or &lt;code&gt;contenido/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.4.0&quot;&gt;This version on GitHub&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;, with the &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ehundu 0.3.1</title>
    <link href="https://ehundu.com/en/news/0-3-1/"/>
    <updated>2026-09-28T00:00:00+02:00</updated>
    <id>https://ehundu.com/en/news/0-3-1/</id>
    <summary>An impossible date without quotes no longer stops the build</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;An impossible date without quotes no longer stops the build.&lt;/strong&gt; &lt;code&gt;fecha: 2026-13-01&lt;/code&gt; or &lt;code&gt;publicar: 2026-01-32&lt;/code&gt;, without quotes, made the reading of the whole YAML fail (“El YAML no es válido”). Now they warn with the file and the line and the field counts as if it were not there, just as &lt;code&gt;fecha: 2025-02-31&lt;/code&gt; already did, in the front matter and in &lt;code&gt;_datos.yml&lt;/code&gt;. In a field that is not reserved, that date reaches the template as the text it is, without a warning (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.3.1/docs/formato.md&quot;&gt;&lt;code&gt;docs/formato.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;, §4, decision 88).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Deployment over FTP no longer gets stuck on certain file sizes.&lt;/strong&gt; Some FTPS servers always cut the upload of some exact file sizes, whatever their content, and repeating it did not help. Now, as a last resort, a file that does not upload whole is uploaded in two parts, the second one appended at the end (APPE), and it is checked that it arrives with its size; a warning says so (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.3.1/docs/despliegue.md&quot;&gt;&lt;code&gt;docs/despliegue.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The schema for an editor, in the format.&lt;/strong&gt; &lt;code&gt;esquema.json&lt;/code&gt; (§11) now describes what a real editor needs: folders with wildcards and in a list, fields with help and with a role, the &lt;code&gt;enlace&lt;/code&gt; type, groups and one-level lists, the file name of new pages and the body with its label (decisions 82 to 87). The engine does not read it; it is for the programs that embed it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The brand, apart from the licence.&lt;/strong&gt; The MIT licence covers the code and the documentation, but not the logo or the illustrations in &lt;code&gt;marca/&lt;/code&gt; (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.3.1/marca/README.md&quot;&gt;&lt;code&gt;marca/README.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;); the illustrations are not in the Composer package either. Ehundu is a Paigar project.&lt;/p&gt;
&lt;p&gt;The 0.3 sites build the same, file by file, without changing anything.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.3.1&quot;&gt;This version on GitHub&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;, with the &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ehundu 0.2.0</title>
    <link href="https://ehundu.com/en/news/0-2-0/"/>
    <updated>2026-09-27T00:00:00+02:00</updated>
    <id>https://ehundu.com/en/news/0-2-0/</id>
    <summary>Orden and donde also filter by the address and the path of the page</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;&lt;code&gt;orden&lt;/code&gt; and &lt;code&gt;donde&lt;/code&gt; also see &lt;code&gt;url&lt;/code&gt; and &lt;code&gt;ruta&lt;/code&gt;&lt;/strong&gt;, with the same values as the templates: &lt;code&gt;donde(&apos;url&apos;, &apos;/contacto/&apos;)&lt;/code&gt; finds a page even if its URL comes from the path of the file and is not written in the front matter. &lt;code&gt;contenido&lt;/code&gt; does not count, so as not to convert the body of every page in the list (decision 71 of &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.2.0/docs/formato.md&quot;&gt;&lt;code&gt;docs/formato.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The deployment saves the &lt;code&gt;url&lt;/code&gt; of the site in the manifest&lt;/strong&gt; and stops before touching anything, also when simulating, if the destination belongs to another site: with several sites on the same server, a wrong &lt;code&gt;ruta&lt;/code&gt; would delete what the other one uploaded. &lt;code&gt;--todo&lt;/code&gt; confirms that it is the same site with another address. A manifest from 0.1, with no &lt;code&gt;url&lt;/code&gt;, is accepted and gets one on the next deployment (decision 70; &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.2.0/docs/despliegue.md&quot;&gt;&lt;code&gt;docs/despliegue.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.2.0&quot;&gt;This version on GitHub&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;, with the &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ehundu 0.3.0</title>
    <link href="https://ehundu.com/en/news/0-3-0/"/>
    <updated>2026-09-27T00:00:00+02:00</updated>
    <id>https://ehundu.com/en/news/0-3-0/</id>
    <summary>Sites in several languages</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Sites in several languages&lt;/strong&gt;, with one file per language in the same tree: &lt;code&gt;contacto.md&lt;/code&gt; and &lt;code&gt;contacto.eu.md&lt;/code&gt; are the same page in Spanish and in Basque, without declaring it anywhere. The languages are declared in &lt;code&gt;idiomas&lt;/code&gt; in &lt;code&gt;sitio.yml&lt;/code&gt;, and the first one is the default. A site in a single language writes none of this and builds the same as with 0.2 (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.3.0/docs/formato.md&quot;&gt;&lt;code&gt;docs/formato.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;, §15, decisions 72 to 81).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Each language with its prefix in the URL&lt;/strong&gt;, which Ehundu also applies to the URL written in &lt;code&gt;url&lt;/code&gt; and to the one from a pattern: &lt;code&gt;url: /instalazioak/&lt;/code&gt; in &lt;code&gt;instalaciones.eu.md&lt;/code&gt; gives &lt;code&gt;/eu/instalazioak/&lt;/code&gt;, and the pattern of a &lt;code&gt;_datos.yml&lt;/code&gt; works for all languages.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The translations, in the templates&lt;/strong&gt;: &lt;code&gt;pagina.idioma&lt;/code&gt;, &lt;code&gt;pagina.traducciones&lt;/code&gt; (the published versions of the page, itself included) and &lt;code&gt;sitio.idiomas&lt;/code&gt;, with which a template writes the &lt;code&gt;hreflang&lt;/code&gt; and a language selector that never links to a page that does not exist. Translating only part of the site is the normal case: there is no automatic fallback and no warnings for the translations that are missing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Collections and data for each language.&lt;/strong&gt; &lt;code&gt;coleccion()&lt;/code&gt; gives the pages in the language of the page that asks for it, or in another with &lt;code&gt;coleccion(&apos;blog&apos;, &apos;eu&apos;)&lt;/code&gt; or &lt;code&gt;&apos;todos&apos;&lt;/code&gt;. &lt;code&gt;datos/menus.eu.yml&lt;/code&gt; is merged with &lt;code&gt;datos/menus.yml&lt;/code&gt; on the pages in Basque, &lt;code&gt;_datos.eu.yml&lt;/code&gt; goes on top of the &lt;code&gt;_datos.yml&lt;/code&gt; of its folder, and what is not translated in a page can go in a &lt;code&gt;.yml&lt;/code&gt; with its name, common to all its versions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;fecha()&lt;/code&gt; in the language of the page&lt;/strong&gt;, with Spanish, Basque (“2025eko martxoaren 18a”) and English; also in single-language sites that are not in Spanish.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;One sitemap with all the languages and one feed per language&lt;/strong&gt; (&lt;code&gt;/feed.xml&lt;/code&gt;, &lt;code&gt;/eu/feed.xml&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;The incremental build of &lt;code&gt;ehundu servir&lt;/code&gt; knows which translations each page looks at and keeps giving the same as a full build (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.3.0/docs/compilacion.md&quot;&gt;&lt;code&gt;docs/compilacion.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Changes for 0.2 sites:&lt;/strong&gt; &lt;code&gt;sitio.idioma&lt;/code&gt; is &lt;code&gt;es&lt;/code&gt; if &lt;code&gt;sitio.yml&lt;/code&gt; does not say otherwise, and the feed always carries &lt;code&gt;xml:lang&lt;/code&gt;; a &lt;code&gt;.yml&lt;/code&gt; inside &lt;code&gt;contenido/&lt;/code&gt; with the name of a page is no longer copied to &lt;code&gt;salida/&lt;/code&gt;, because it holds its common fields; &lt;code&gt;idioma&lt;/code&gt; becomes a reserved field; and two files for the same page in the same language (&lt;code&gt;contacto.md&lt;/code&gt; and &lt;code&gt;contacto.twig&lt;/code&gt;) stop the build.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.3.0&quot;&gt;This version on GitHub&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;, with the &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ehundu 0.1.0</title>
    <link href="https://ehundu.com/en/news/0-1-0/"/>
    <updated>2026-09-26T00:00:00+02:00</updated>
    <id>https://ehundu.com/en/news/0-1-0/</id>
    <summary>The first usable version of Ehundu</summary>
    <content type="html">&lt;p&gt;The first usable version of Ehundu: it builds Markdown and Twig sites, previews them with incremental builds and publishes them to a folder, over FTP, over SFTP or to S3-compatible storage.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.1.0&quot;&gt;This version on GitHub&lt;span class=&quot;oculto&quot;&gt; (opens in a new tab)&lt;/span&gt;&lt;/a&gt;, with the &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
</feed>
