diff --git a/latte/cs/@left-menu.texy b/latte/cs/@left-menu.texy
index 31a85bcaf7..4556b26913 100644
--- a/latte/cs/@left-menu.texy
+++ b/latte/cs/@left-menu.texy
@@ -5,6 +5,7 @@
- [Dědičnost šablon |Template Inheritance]
- [Typový systém |type-system]
- [Sandbox]
+ - [HTML attributy |html-attributes]
- Pro designéry 🎨
- [Syntaxe |syntax]
diff --git a/latte/cs/cookbook/@home.texy b/latte/cs/cookbook/@home.texy
index b4d54b0b7e..b6af15e673 100644
--- a/latte/cs/cookbook/@home.texy
+++ b/latte/cs/cookbook/@home.texy
@@ -8,6 +8,7 @@ Příklady kódů a receptů pro provádění běžných úkolů pomocí Latte.
- [Předávání proměnných napříč šablonami |passing-variables]
- [Všechno, co jste kdy chtěli vědět o seskupování |grouping]
- [Jak psát SQL queries v Latte? |how-to-write-sql-queries-in-latte]
+- [Migrace z Latte 3.0 |migration-from-latte-30]
- [Migrace z Latte 2 |migration-from-latte2]
- [Migrace z PHP |migration-from-php]
- [Migrace z Twigu |migration-from-twig]
diff --git a/latte/cs/cookbook/migration-from-latte-30.texy b/latte/cs/cookbook/migration-from-latte-30.texy
new file mode 100644
index 0000000000..ad09a1d68a
--- /dev/null
+++ b/latte/cs/cookbook/migration-from-latte-30.texy
@@ -0,0 +1,109 @@
+Migrace z Latte 3.0
+*******************
+
+.[perex]
+Latte 3.1 přináší několik vylepšení a změn, díky kterým je psaní šablon bezpečnější a pohodlnější. Většina změn je zpětně kompatibilní, ale některé vyžadují pozornost při přechodu. Tento průvodce shrnuje BC breaky a jak je řešit.
+
+Latte 3.1 vyžaduje **PHP 8.2** nebo novější.
+
+
+Chytré atributy a migrace
+=========================
+
+Nejvýznamnější změnou v Latte 3.1 je nové chování [chytrých atributů |/html-attributes]. To ovlivňuje, jak se vykreslují hodnoty `null` a logické hodnoty v `data-` atributech.
+
+1. **Hodnoty `null`:** Dříve se `title={$null}` vykresloval jako `title=""`. Nyní se atribut zcela vynechá.
+2. **`data-` atributy:** Dříve se `data-foo={=true}` / `data-foo={=false}` vykreslovaly jako `data-foo="1"` / `data-foo=""`. Nyní se vykreslují jako `data-foo="true"` / `data-foo="false"`.
+
+Abychom vám pomohli identifikovat místa, kde se výstup ve vaší aplikaci změnil, Latte poskytuje migrační nástroj.
+
+
+Migrační varování
+-----------------
+
+Můžete zapnout [migrační varování |/develop#Migrační varování], která vás během vykreslování upozorní, pokud se výstup liší od Latte 3.0.
+
+```php
+$latte = new Latte\Engine;
+$latte->setMigrationWarnings();
+```
+
+Pokud jsou povolena, sledujte logy aplikace nebo Tracy bar pro `E_USER_WARNING`. Každé varování bude ukazovat na konkrétní řádek a sloupec v šabloně.
+
+**Jak varování vyřešit:**
+
+Pokud je nové chování správné (např. chcete, aby prázdný atribut zmizel), potvrďte jej použitím filtru `|accept` pro potlačení varování:
+
+```latte
+
+```
+
+Pokud chcete atribut zachovat jako prázdný (např. `title=""`) místo jeho vynechání, použijte null coalescing operátor:
+
+```latte
+
+```
+
+Nebo, pokud striktně vyžadujete staré chování (např. `"1"` pro `true`), explicitně přetypujte hodnotu na string:
+
+```latte
+
+```
+
+**Poté, co vyřešíte všechna varování:**
+
+Jakmile vyřešíte všechna varování, vypněte migrační varování a **odstraňte všechny** filtry `|accept` ze svých šablon, protože již nejsou potřeba.
+
+
+Strict Types
+============
+
+Latte 3.1 zapíná `declare(strict_types=1)` ve výchozím nastavení pro všechny kompilované šablony. To zlepšuje typovou bezpečnost, ale může způsobit typové chyby v PHP výrazech uvnitř šablon, pokud jste spoléhali na volné typování.
+
+Pokud typy nemůžete opravit okamžitě, můžete toto chování vypnout:
+
+```php
+$latte->setStrictTypes(false);
+```
+
+
+Globální konstanty
+==================
+
+Parser šablon byl vylepšen, aby lépe rozlišoval mezi jednoduchými řetězci a konstantami. V důsledku toho musí být globální konstanty nyní prefixovány zpětným lomítkem `\`.
+
+```latte
+{* Starý způsob (vyhodí varování, v budoucnu bude interpretováno jako string 'PHP_VERSION') *}
+{if PHP_VERSION > ...}
+
+{* Nový způsob (správně interpretováno jako konstanta) *}
+{if \PHP_VERSION > ...}
+```
+
+Tato změna předchází nejednoznačnostem a umožňuje volnější používání neuvodzovkovaných řetězců.
+
+
+Odstraněné funkce
+=================
+
+**Rezervované proměnné:** Proměnné začínající na `$__` (dvou podtržítko) a proměnná `$this` jsou nyní vyhrazeny pro vnitřní použití Latte. Nemůžete je používat v šablonách.
+
+**Undefined-safe operátor:** Operátor `??->`, což byla specifická funkce Latte vytvořená před PHP 8, byl odstraněn. Jde o historický relikt. Používejte prosím standardní PHP nullsafe operátor `?->`.
+
+**Filter Loader**
+Metoda `Engine::addFilterLoader()` byla označena jako zastaralá a odstraněna. Šlo o nekonzistentní koncept, který se jinde v Latte nevyskytoval.
+
+**Date Format**
+Statická vlastnost `Latte\Runtime\Filters::$dateFormat` byla odstraněna, aby se předešlo globálnímu stavu.
+
+
+Nové funkce
+===========
+
+Během migrace si můžete začít užívat nové funkce:
+
+- **Chytré HTML atributy:** Předávání polí do `class` a `style`, automatické vynechání `null` atributů.
+- **Nullsafe filtry:** Použijte `{$var?|filter}` pro přeskočení filtrování null hodnot.
+- **`n:elseif`:** Nyní můžete používat `n:elseif` společně s `n:if` a `n:else`.
+- **Zjednodušená syntaxe:** Pište `
` bez uvozovek.
+- **Toggle filtr:** Použijte `|toggle` pro ruční ovládání boolean atributů.
diff --git a/latte/cs/custom-filters.texy b/latte/cs/custom-filters.texy
index 562d8fd802..7f7cab0c0c 100644
--- a/latte/cs/custom-filters.texy
+++ b/latte/cs/custom-filters.texy
@@ -117,30 +117,6 @@ $latte->addExtension(new App\Latte\MyLatteExtension);
Tento přístup udrží logiku vašeho filtru zapouzdřenou a registraci jednoduchou.
-Použití načítače filtrů
------------------------
-
-Latte umožňuje registrovat načítač filtrů pomocí `addFilterLoader()`. Jde o jediné volatelné callable, které Latte požádá o jakýkoliv neznámý název filtru během kompilace. Načítač vrací PHP callable filtru nebo `null`.
-
-```php
-$latte = new Latte\Engine;
-
-// Načítač může dynamicky vytvářet/získávat callable filtry
-$latte->addFilterLoader(function (string $name): ?callable {
- if ($name === 'myLazyFilter') {
- // Představte si zde náročnou inicializaci...
- $service = get_some_expensive_service();
- return fn($value) => $service->process($value);
- }
- return null;
-});
-```
-
-Tato metoda byla primárně určena pro líné načítání filtrů s velmi **náročnou inicializací**. Avšak moderní praktiky vkládání závislostí (dependency injection) obvykle zvládají líné služby efektivněji.
-
-Načítače filtrů přidávají složitost a obecně se nedoporučují ve prospěch přímé registrace pomocí `addFilter()` nebo v rámci rozšíření pomocí `getFilters()`. Používejte načítače pouze pokud máte závažný, specifický důvod související s výkonnostními problémy při inicializaci filtrů, které nelze řešit jinak.
-
-
Filtry používající třídu s atributy
-----------------------------------
diff --git a/latte/cs/develop.texy b/latte/cs/develop.texy
index cd9df1f54b..8af8d4ff6b 100644
--- a/latte/cs/develop.texy
+++ b/latte/cs/develop.texy
@@ -15,7 +15,8 @@ Podporované verze PHP (platí pro poslední setinkové verze Latte):
| verze | kompatibilní s PHP
|-----------------|-------------------
-| Latte 3.0 | PHP 8.0 – 8.2
+| Latte 3.1 | PHP 8.2 – 8.5
+| Latte 3.0 | PHP 8.0 – 8.5
Jak vykreslit šablonu
@@ -193,6 +194,27 @@ $latte = new Latte\Engine;
$latte->setStrictTypes();
```
+.[note]
+Od verze Latte 3.1 jsou strict types povoleny ve výchozím nastavení. Můžete je deaktivovat pomocí `$latte->setStrictTypes(false)`.
+
+
+Migrační varování .{data-version:3.1}
+=====================================
+
+Latte 3.1 mění chování některých [HTML atributů|html-attributes]. Například hodnoty `null` nyní odstraní atribut namísto vypsání prázdného řetězce. Abyste snadno našli místa, kde tato změna ovlivňuje vaše šablony, můžete zapnout varování o migraci:
+
+```php
+$latte->setMigrationWarnings();
+```
+
+Pokud je toto zapnuto, Latte kontroluje vykreslované atributy a vyvolá uživatelské varování (`E_USER_WARNING`), pokud se výstup liší od toho, co by Latte 3.0 vygenerovalo. Když narazíte na varování, použijte jedno z těchto řešení:
+
+1. Pokud je nový výstup pro váš případ použití správný (např. preferujete, aby atribut zmizel při `null`), potlačte varování přidáním filtru `|accept`
+2. Pokud chcete, aby byl atribut vykreslen jako prázdný (např. `title=""`) namísto odstranění, když je proměnná `null`, poskytněte prázdný řetězec jako zálohu: `title={$val ?? ''}`
+3. Pokud striktně vyžadujete staré chování (např. vypsání `"1"` pro `true` namísto `"true"`), explicitně přetypujte hodnotu na řetězec: `data-foo={(string) $val}`
+
+Jakmile jsou všechna varování vyřešena, vypněte varování o migraci odstraněním `setMigrationWarnings()` a **odstraňte všechny** filtry `|accept` ze svých šablon, protože již nejsou potřeba.
+
Překládání v šablonách .{toc: TranslatorExtension}
==================================================
diff --git a/latte/cs/extending-latte.texy b/latte/cs/extending-latte.texy
index e155953867..a378ff8cf0 100644
--- a/latte/cs/extending-latte.texy
+++ b/latte/cs/extending-latte.texy
@@ -44,13 +44,6 @@ $latte->addFilter('truncate', $myTruncate);
// Použití v šabloně: {$text|truncate} nebo {$text|truncate:100}
```
-Můžete také zaregistrovat **Filter Loader**, funkci, která dynamicky poskytuje volatelné objekty filtrů podle požadovaného názvu:
-
-```php
-$latte->addFilterLoader(fn(string $name) => /* vrátí volatelný objekt nebo null */);
-```
-
-
Pro registraci funkce použitelné ve výrazech šablony použijte `addFunction()`.
```php
diff --git a/latte/cs/filters.texy b/latte/cs/filters.texy
index f7159ffd25..bcce55ae3e 100644
--- a/latte/cs/filters.texy
+++ b/latte/cs/filters.texy
@@ -55,6 +55,11 @@ V šablonách můžeme používat funkce, které pomáhají upravit nebo přefor
| `floor` | [zaokrouhlí číslo dolů na danou přesnost |#floor]
| `round` | [zaokrouhlí číslo na danou přesnost |#round]
+.[table-latte-filters]
+|## HTML atributy
+| `accept` | [potvrzuje nové chování chytrých atributů |#accept]
+| `toggle` | [přepíná přítomnost HTML atributu |#toggle]
+
.[table-latte-filters]
|## Escapování
| `escapeUrl` | [escapuje parametr v URL |#escapeUrl]
@@ -117,10 +122,31 @@ V šabloně se potom volá takto:
```
+Nullsafe filtry .{data-version:3.1}
+-----------------------------------
+
+Jakýkoliv filtr lze učinit nullsafe použitím `?|` místo `|`. Pokud je hodnota `null`, filtr se nevykoná a vrátí se `null`. Filtry následující v řetězci jsou také přeskočeny.
+
+To je užitečné v kombinaci s HTML atributy, které jsou vynechány, pokud je hodnota `null`.
+
+```latte
+
+{* Pokud je $title null:
*}
+{* Pokud je $title 'hello':
*}
+```
+
+
Filtry
======
+accept .[filter]{data-version:3.1}
+----------------------------------
+Filtr se používá při [migraci z Latte 3.0|cookbook/migration-from-latte-30] k potvrzení, že jste zkontrolovali změnu chování atributu a akceptujete ji. Nemění hodnotu.
+
+Jde o dočasný nástroj. Jakmile je migrace dokončena a varování při migraci jsou vypnuta, měli byste tento filtr ze svých šablon odstranit.
+
+
batch(int $length, mixed $item): array .[filter]
------------------------------------------------
Filtr, který zjednodušuje výpis lineárních dat do podoby tabulky. Vrací pole polí se zadaným počtem položek. Pokud zadáte druhý parametr, použije se k doplnění chybějících položek na posledním řádku.
@@ -827,6 +853,21 @@ Extrahuje část řetězce. Tento filtr byl nahrazen filtrem [#slice].
```
+toggle .[filter]{data-version:3.1}
+----------------------------------
+Filtr `toggle` ovládá přítomnost atributu na základě boolean hodnoty. Pokud je hodnota truthy, atribut je přítomen; pokud je falsy, atribut je zcela vynechán:
+
+```latte
+
+{* Pokud je $isGrid truthy:
*}
+{* Pokud je $isGrid falsy:
*}
+```
+
+Tento filtr je užitečný pro vlastní atributy nebo atributy JavaScriptových knihoven, které vyžadují kontrolu přítomnosti/nepřítomnosti podobně jako HTML boolean atributy.
+
+Filtr lze použít pouze uvnitř HTML atributů.
+
+
translate(...$args) .[filter]
-----------------------------
Překládá výrazy do jiných jazyků. Aby byl filtr k dispozici, je potřeba [nastavit překladač |develop#TranslatorExtension]. Můžete také použít [tagy pro překlad |tags#Překlady].
diff --git a/latte/cs/html-attributes.texy b/latte/cs/html-attributes.texy
new file mode 100644
index 0000000000..e9632b2586
--- /dev/null
+++ b/latte/cs/html-attributes.texy
@@ -0,0 +1,151 @@
+Chytré HTML atributy
+********************
+
+.[perex]
+Latte 3.1 přichází se sadou vylepšení, která se zaměřuje na jednu z nejčastějších činností v šablonách – vypisování HTML atributů. Přináší více pohodlí, flexibility a bezpečnosti.
+
+
+Boolean atributy
+================
+
+HTML používá speciální atributy jako `checked`, `disabled`, `selected` nebo `hidden`, u kterých nezáleží na konkrétní hodnotě – pouze na jejich přítomnosti. Fungují jako jednoduché příznaky.
+
+Latte je zpracovává automaticky. Atributu můžete předat jakýkoliv výraz. Pokud je pravdivý (truthy), atribut se vykreslí. Pokud je nepravdivý (falsey - např. `false`, `null`, `0` nebo prázdný řetězec), atribut se zcela vynechá.
+
+To znamená, že se můžete rozloučit se složitými podmínkami nebo `n:attr` a jednoduše použít:
+
+```latte
+
+```
+
+Pokud `$isDisabled` je `false` a `$isReadOnly` je `true`, vykreslí se:
+
+```latte
+
+```
+
+Pokud potřebujete přepínací chování pro standardní atributy, které nemají toto automatické zpracování (tedy např. atributy `data-` nebo `aria-`), použijte filtr [toggle |filters#toggle].
+
+
+Hodnoty null
+============
+
+Toto je jedna z nejpříjemnějších změn. Dříve, pokud byla proměnná `null`, vypsala se jako prázdný řetězec `""`. To často vedlo k prázdným atributům v HTML jako `class=""` nebo `title=""`.
+
+V Latte 3.1 platí nové univerzální pravidlo: **Hodnota `null` znamená, že atribut neexistuje.**
+
+```latte
+
+```
+
+Pokud `$title` je `null`, výstupem je ``. Pokud obsahuje řetězec, např. "Ahoj", výstupem je ``. Díky tomu nemusíte obalovat atributy do podmínek.
+
+Pokud používáte filtry, mějte na paměti, že obvykle převádějí `null` na řetězec (např. prázdný řetězec). Abyste tomu zabránili, použijte [nullsafe filtr |filters#Nullsafe filtry] `?|`:
+
+```latte
+
+```
+
+
+Třídy (Classes)
+===============
+
+Atributu `class` můžete předat pole. To je ideální pro podmíněné třídy: pokud je pole asociativní, klíče se použijí jako názvy tříd a hodnoty jako podmínky. Třída se vykreslí pouze v případě, že je podmínka splněna.
+
+```latte
+
+```
+
+Pokud je `$isActive` true, vykreslí se:
+
+```latte
+
+```
+
+Toto chování není omezeno pouze na `class`. Funguje pro jakýkoliv HTML atribut, který očekává seznam hodnot oddělených mezerou, jako jsou `itemprop`, `rel`, `sandbox` atd.
+
+```latte
+ $isExternal]}>odkaz
+```
+
+
+Styly (Styles)
+==============
+
+Atribut `style` také podporuje pole. Je to obzvláště užitečné pro podmíněné styly. Pokud položka pole obsahuje klíč (CSS vlastnost) a hodnotu, vlastnost se vykreslí pouze v případě, že hodnota není `null`.
+
+```latte
+
+```
+
+Pokud je `$isVisible` false, vykreslí se:
+
+```latte
+
+```
+
+
+Data atributy
+=============
+
+Často potřebujeme do HTML předat konfiguraci pro JavaScript. Dříve se to dělalo přes `json_encode`. Nyní můžete atributu `data-` jednoduše předat pole nebo objekt stdClass a Latte jej serializuje do JSONu:
+
+```latte
+
+```
+
+Vypíše:
+
+```latte
+
+```
+
+Také `true` a `false` se vykreslují jako řetězce `"true"` a `"false"` (tj. validní JSON).
+
+
+Aria atributy
+=============
+
+Specifikace WAI-ARIA vyžaduje textové hodnoty `"true"` a `"false"` pro logické hodnoty. Latte to pro atributy `aria-` řeší automaticky:
+
+```latte
+
+```
+
+Vypíše:
+
+```latte
+
+```
+
+
+Typová kontrola
+===============
+
+Už jste někdy viděli `` ve svém vygenerovaném HTML? Je to klasická chyba, která často projde bez povšimnutí. Latte zavádí přísnou typovou kontrolu pro HTML atributy, aby byly vaše šablony vůči takovým přehlédnutím odolnější.
+
+Latte ví, které atributy jsou které a jaké hodnoty očekávají:
+
+- **Standardní atributy** (jako `href`, `id`, `value`, `placeholder`...) očekávají hodnotu, kterou lze vykreslit jako text. To zahrnuje řetězce, čísla nebo stringable objekty. Také je akceptováno `null` (atribut vynechá). Pokud však omylem předáte pole, boolean nebo obecný objekt, Latte vyvolá varování a neplatnou hodnotu inteligentně ignoruje.
+- **Boolean atributy** (jako `checked`, `disabled`...) akceptují jakýkoliv typ, protože jejich přítomnost je určena logikou pravdivý/nepravdivý.
+- **Chytré atributy** (jako `class`, `style`, `data-`...) specificky zpracovávají pole jako validní vstupy.
+
+Tato kontrola zajišťuje, že vaše aplikace nebude produkovat neočekávané HTML.
+
+
+Migrace z Latte 3.0
+===================
+
+Protože se změnilo chování `null` (dříve vypisovalo `""`, nyní atribut vynechá) a atributů `data-` (boolean hodnoty vypisovaly `"1"`/`""`, nyní `"true"`/`"false"`), možná budete muset aktualizovat své šablony.
+
+Pro hladký přechod poskytuje Latte migrační režim, který upozorňuje na rozdíly. Přečtěte si podrobného průvodce [Migrace z Latte 3.0 na 3.1 |cookbook/migration-from-latte-30].
+
+[* html-attributes.webp *]
diff --git a/latte/cs/syntax.texy b/latte/cs/syntax.texy
index 0913d20fc7..1e21ab6dcf 100644
--- a/latte/cs/syntax.texy
+++ b/latte/cs/syntax.texy
@@ -111,6 +111,34 @@ Což vypíše v závislosti na proměnné `$url`:
Avšak n:atributy nejsou jen zkratkou pro párové značky. Existují i ryzí n:atributy, jako třeba [n:href |application:creating-links#V šabloně presenteru] nebo velešikovný pomocník kodéra [n:class |tags#n:class].
+Kromě syntaxe s uvozovkami `
` můžete použít alternativní syntaxi se složenými závorkami `
`. Hlavní výhodou je, že uvnitř `{...}` můžete volně používat jednoduché i dvojité uvozovky:
+
+```latte
+
...
+```
+
+
+Chytré HTML atributy .{data-version:3.1}
+========================================
+
+Latte dělá práci se standardními HTML atributy neuvěřitelně snadnou. Za vás řeší boolean atributy jako `checked`, odstraňuje atributy obsahující `null` a umožňuje vám skládat hodnoty `class` a `style` pomocí polí. Dokonce automaticky serializuje data pro `data-` atributy do JSON.
+
+```latte
+{* null odstraní atribut *}
+
+
+{* boolean ovládá přítomnost boolean atributů *}
+
+
+{* pole fungují v class *}
+
$isActive]}>
+
+{* pole jsou JSON-enkódována v data- atributech *}
+
+```
+
+Více informací v samostatné kapitole [Chytré HTML atributy|html-attributes].
+
Filtry
======
@@ -148,10 +176,17 @@ Na blok:
```
Nebo přímo na hodnotu (v kombinaci s tagem [`{=expr}` |tags#Vypisování]):
+
```latte
{=' Hello world '|trim}
```
+Pokud může být hodnota `null` a chcete v takovém případě zabránit použití filtru, použijte [nullsafe filter |filters#Nullsafe Filters] `?|`:
+
+```latte
+
{$heading?|upper}
+```
+
Dynamické HTML značky .{data-version:3.0.9}
===========================================
@@ -204,7 +239,7 @@ Jednoduché řetězce jsou ty, které jsou tvořeny čistě z písmen, číslic,
Konstanty
---------
-Jelikož lze u jednoduchých řetězců vynechávat uvozovky, doporučujeme pro odlišení zapisovat globální konstanty s lomítkem na začátku:
+K rozlišení globálních konstant od jednoduchých řetězců použijte oddělovač globálního jmenného prostoru:
```latte
{if \PROJECT_ID === 1} ... {/if}
@@ -265,8 +300,6 @@ Historické okénko
Latte přišlo v průběhu své historie s celou řadou syntaktických cukříků, které se po pár letech objevily v samotném PHP. Například v Latte bylo možné psát pole jako `[1, 2, 3]` místo `array(1, 2, 3)` nebo používat nullsafe operátor `$obj?->foo` dávno předtím, než to bylo možné v samotném PHP. Latte také zavedlo operátor pro rozbalení pole `(expand) $arr`, který je ekvivalentem dnešního operátoru `...$arr` z PHP.
-Undefined-safe operator `??->`, což je obdoba nullsafe operatoru `?->`, který ale nevyvolá chybu, pokud proměnná neexistuje, vznikl z historických důvodů a dnes doporučujeme používat standardní PHP operátor `?->`.
-
Omezení PHP v Latte
===================
diff --git a/latte/cs/tags.texy b/latte/cs/tags.texy
index d302666d5a..598b2b73dd 100644
--- a/latte/cs/tags.texy
+++ b/latte/cs/tags.texy
@@ -16,7 +16,7 @@ Přehled a popis všech tagů (neboli značek či maker) šablonovacího systém
| `{ifset}` … `{elseifset}` … `{/ifset}` | [podmínka ifset |#ifset elseifset]
| `{ifchanged}` … `{/ifchanged}` | [test jestli došlo ke změně |#ifchanged]
| `{switch}` `{case}` `{default}` `{/switch}` | [podmínka switch |#switch case default]
-| `n:else` | [alternativní obsah pro podmínky |#n:else]
+| `n:else`, `n:elseif` | [alternativní obsah pro podmínky |#n:else]
.[table-latte-tags language-latte]
|## Cykly
@@ -252,14 +252,16 @@ Víte, že k n:atributům můžete připojit prefix `tag-`? Pak se bude podmínk
Boží.
-`n:else` .{data-version:3.0.11}
--------------------------------
+`n:else` `n:elseif` .{data-version:3.0.11}
+------------------------------------------
-Pokud podmínku `{if} ... {/if}` zapíšete v podobě [n:attributu |syntax#n:atributy], máte možnost uvést i alternativní větev pomocí `n:else`:
+Pokud podmínku `{if} ... {/if}` zapíšete v podobě [n:attributu |syntax#n:atributy], máte možnost uvést i alternativní větev pomocí `n:else` a `n:elseif` (od Latte 3.1):
```latte
Skladem {$count} kusů
+Neplatný počet
+
není dostupné
```
@@ -947,6 +949,9 @@ Pomocníci HTML kodéra
n:class
-------
+.[note]
+Od verze Latte 3.1 získal standardní HTML atribut `class` [stejnou funkcionalitu |html-attributes#třídy-classes]. Není tedy již nutné používat n:class.
+
Díky `n:class` velice snadno vygenerujete HTML atribut `class` přesně podle představ.
Příklad: potřebuji, aby aktivní prvek měl třídu `active`:
@@ -997,6 +1002,12 @@ V závislosti na vrácených hodnotách vypíše např.:
```
+Funkce inteligentních atributů v Latte 3.1, jako je vynechání hodnot `null` nebo předávání polí do `class` nebo `style`, fungují také v rámci `n:attr`:
+
+```latte
+
+```
+
n:tag
-----
diff --git a/latte/en/@left-menu.texy b/latte/en/@left-menu.texy
index eebab75253..88fc173a96 100644
--- a/latte/en/@left-menu.texy
+++ b/latte/en/@left-menu.texy
@@ -5,6 +5,7 @@
- [Template Inheritance]
- [Type System]
- [Sandbox]
+ - [HTML attributes]
- For Designers 🎨
- [Syntax]
diff --git a/latte/en/cookbook/@home.texy b/latte/en/cookbook/@home.texy
index 7c2738769a..425654b8e8 100644
--- a/latte/en/cookbook/@home.texy
+++ b/latte/en/cookbook/@home.texy
@@ -8,7 +8,9 @@ Example codes and recipes for accomplishing common tasks with Latte.
- [Passing variables across templates |passing-variables]
- [Everything you always wanted to know about grouping |grouping]
- [How to write SQL queries in Latte? |how-to-write-sql-queries-in-latte]
+- [Migration from Latte 3.0 |migration-from-latte-30]
- [Migration from Latte 2 |migration-from-latte2]
- [Migration from PHP |migration-from-php]
- [Migration from Twig |migration-from-twig]
- [Using Latte with Slim 4 |slim-framework]
+- [Latte Plugin for CakePHP](https://github.com/josbeir/cakephp-latte-view)
diff --git a/latte/en/cookbook/migration-from-latte-30.texy b/latte/en/cookbook/migration-from-latte-30.texy
new file mode 100644
index 0000000000..9f7847b9ed
--- /dev/null
+++ b/latte/en/cookbook/migration-from-latte-30.texy
@@ -0,0 +1,110 @@
+Migration from Latte 3.0
+************************
+
+.[perex]
+Latte 3.1 brings several improvements and changes that make templates safer and more convenient to write. Most changes are backward compatible, but some require attention during migration. This guide summarizes the breaking changes and how to handle them.
+
+Latte 3.1 requires **PHP 8.2** or newer.
+
+
+Smart Attributes and Migration
+==============================
+
+The most significant change in Latte 3.1 is the new behavior of [Smart Attributes |/html-attributes]. This affects how `null` values and boolean values in `data-` attributes are rendered.
+
+1. **`null` values:** Previously, `title={$null}` rendered as `title=""`. Now, the attribute is completely dropped.
+2. **`data-` attributes:** Previously, `data-foo={=true}` / `data-foo={=false}` rendered as `data-foo="1"` / `data-foo=""`. Now, it renders as `data-foo="true"` / `data-foo="false"`.
+
+To help you identify places where the output has changed in your application, Latte provides a migration tool.
+
+
+Migration Warnings
+------------------
+
+You can enable [migration warnings |/develop#Migration Warnings], which will warn you during rendering if the output differs from Latte 3.0.
+
+```php
+$latte = new Latte\Engine;
+$latte->setMigrationWarnings();
+```
+
+When enabled, check your application logs or Tracy bar for `E_USER_WARNING`s. Each warning will point to the specific line, and column in template.
+
+**How to resolve warnings:**
+
+If the new behavior is correct (e.g. you want the empty attribute to disappear), confirm it using the `|accept` filter to suppress the warning:
+
+```latte
+
+```
+
+If you want to keep the attribute as empty (e.g. `title=""`) instead of dropping it, use the null coalescing operator:
+
+```latte
+
+```
+
+Or, if you strictly require the old behavior (e.g. `"1"` for `true`), explicitly cast the value to string:
+
+```latte
+
+```
+
+**After you resolve all warnings:**
+
+Once all warnings are resolved, disable migration warnings and **remove all** `|accept` filters from your templates, as they are no longer needed.
+
+
+Strict Types
+============
+
+Latte 3.1 enables `declare(strict_types=1)` by default for all compiled templates. This improves type safety but might cause type errors in PHP expressions inside your templates if you were relying on loose typing.
+
+If you cannot fix the types immediately, you can disable this behavior:
+
+```php
+$latte->setStrictTypes(false);
+```
+
+
+Global Constants
+================
+
+The template parser has been improved to better distinguish between simple strings and constants. As a result, global constants must now be prefixed with a backslash `\`.
+
+```latte
+{* Old way (throws a warning; in the future will be interpreted as the string 'PHP_VERSION') *}
+{if PHP_VERSION > ...}
+
+{* New way (correctly interpreted as constant) *}
+{if \PHP_VERSION > ...}
+```
+
+This change prevents ambiguity and allows you to use unquoted strings more freely.
+
+
+Removed Features
+================
+
+**Reserved Variables:** Variables starting with `$__` (double underscore) and the variable `$this` are now strictly reserved for Latte's internal use. You cannot use them in your templates.
+
+**Undefined-safe Operator:** The `??->` operator, which was a Latte-specific feature created before PHP 8, has been removed. It is a historical relic. Please use the standard PHP nullsafe operator `?->`.
+
+**Filter Loader**
+The `Engine::addFilterLoader()` method has been deprecated and removed. It was an inconsistent concept not found elsewhere in Latte.
+
+**Date Format**
+The static property `Latte\Runtime\Filters::$dateFormat` was removed to avoid global state.
+
+
+New Features
+============
+
+While migrating, you can start enjoying the new features:
+
+- **Smart HTML
+Attributes:** Pass arrays to `class` and `style`, auto-drop `null` attributes.
+- **Nullsafe filters:** Use `{$var?|filter}` to skip filtering null values.
+- **`n:elseif`:** You can now use `n:elseif` alongside `n:if` and `n:else`.
+- **Simplified syntax:** Write `
` without quotes.
+- **Toggle filter:** Use `|toggle` for manual control over boolean attributes.
diff --git a/latte/en/custom-filters.texy b/latte/en/custom-filters.texy
index ee38269043..9fc3ea58b6 100644
--- a/latte/en/custom-filters.texy
+++ b/latte/en/custom-filters.texy
@@ -117,30 +117,6 @@ $latte->addExtension(new App\Latte\MyLatteExtension);
This approach keeps your filter logic encapsulated and makes registration straightforward.
-Using a Filter Loader
----------------------
-
-Latte allows registering a filter loader via `addFilterLoader()`. This is a single callable that Latte asks for any unknown filter name during compilation. The loader returns the filter's PHP callable or `null`.
-
-```php
-$latte = new Latte\Engine;
-
-// Loader might dynamically create/fetch filter callables
-$latte->addFilterLoader(function (string $name): ?callable {
- if ($name === 'myLazyFilter') {
- // Imagine expensive initialization here...
- $service = get_some_expensive_service();
- return fn($value) => $service->process($value);
- }
- return null;
-});
-```
-
-This method was primarily intended for lazy loading filters with very **expensive initialization**. However, modern dependency injection practices usually handle lazy services more effectively.
-
-Filter loaders add complexity and are generally discouraged in favor of direct registration via `addFilter()` or within an Extension using `getFilters()`. Use loaders only if you have a strong, specific reason related to performance bottlenecks in filter initialization that cannot be addressed otherwise.
-
-
Filters Using a Class with Attributes .{toc: Filters Using the Class}
---------------------------------------------------------------------
diff --git a/latte/en/develop.texy b/latte/en/develop.texy
index 1f8d88b7ff..7d77e9aa95 100644
--- a/latte/en/develop.texy
+++ b/latte/en/develop.texy
@@ -15,7 +15,8 @@ Supported PHP versions (applies to the latest patch Latte versions):
| version | compatible with PHP
|-----------------|-------------------
-| Latte 3.0 | PHP 8.0 – 8.2
+| Latte 3.1 | PHP 8.2 – 8.5
+| Latte 3.0 | PHP 8.0 – 8.5
How to Render a Template
@@ -193,6 +194,27 @@ $latte = new Latte\Engine;
$latte->setStrictTypes();
```
+.[note]
+Since Latte 3.1, strict types are enabled by default. You can disable them with `$latte->setStrictTypes(false)`.
+
+
+Migration Warnings .{data-version:3.1}
+======================================
+
+Latte 3.1 changes the behavior of some [HTML attributes|html-attributes]. For example, `null` values now drop the attribute instead of printing an empty string. To easily find places where this change affects your templates, you can enable migration warnings:
+
+```php
+$latte->setMigrationWarnings();
+```
+
+When enabled, Latte checks rendered attributes and triggers a user warning (`E_USER_WARNING`) if the output differs from what Latte 3.0 would have produced. When you encounter a warning, apply one of the solutions:
+
+1. If the new output is correct for your use case (e.g., you prefer the attribute to disappear when `null`), suppress the warning by adding the `|accept` filter
+2. If you want the attribute to be rendered as empty (e.g. `title=""`) instead of being dropped when the variable is `null`, provide an empty string as a fallback: `title={$val ?? ''}`
+3. If you strictly require the old behavior (e.g., printing `"1"` for `true` instead of `"true"`), explicitly cast the value to a string: `data-foo={(string) $val}`
+
+Once all warnings are resolved, disable migration warnings by removing `setMigrationWarnings()` and **remove all** `|accept` filters from your templates, as they are no longer needed.
+
Translation in Templates .{toc: TranslatorExtension}
====================================================
diff --git a/latte/en/extending-latte.texy b/latte/en/extending-latte.texy
index 9f63a3a280..22499f514a 100644
--- a/latte/en/extending-latte.texy
+++ b/latte/en/extending-latte.texy
@@ -44,13 +44,6 @@ $latte->addFilter('truncate', $myTruncate);
// Template usage: {$text|truncate} or {$text|truncate:100}
```
-You can also register a **Filter Loader**, a function that dynamically provides filter callables based on the requested name:
-
-```php
-$latte->addFilterLoader(fn(string $name) => /* return callable or null */);
-```
-
-
Use `addFunction()` to register a function usable within template expressions.
```php
diff --git a/latte/en/filters.texy b/latte/en/filters.texy
index ef92176edb..c87f8ffceb 100644
--- a/latte/en/filters.texy
+++ b/latte/en/filters.texy
@@ -55,6 +55,11 @@ In templates, we can use functions that help modify or reformat data into its fi
| `floor` | [rounds a number down to a given precision |#floor]
| `round` | [rounds a number to a given precision |#round]
+.[table-latte-filters]
+|## HTML Attributes
+| `accept` | [accepts the new behavior of smart attributes |#accept]
+| `toggle` | [toggles the presence of an HTML attribute |#toggle]
+
.[table-latte-filters]
|## Escaping
| `escapeUrl` | [escapes a parameter in a URL |#escapeUrl]
@@ -117,10 +122,31 @@ It is then called in the template like this:
```
+Nullsafe Filters .{data-version:3.1}
+------------------------------------
+
+Any filter can be made nullsafe by using `?|` instead of `|`. If the value is `null`, the filter is not executed and `null` is returned. Subsequent filters in the chain are also skipped.
+
+This is useful in combination with HTML attributes, which are omitted if the value is `null`.
+
+```latte
+
+{* If $title is null:
*}
+{* If $title is 'hello':
*}
+```
+
+
Filters
=======
+accept .[filter]{data-version:3.1}
+----------------------------------
+The filter is used during [migration from Latte 3.0|cookbook/migration-from-latte-30] to acknowledge that you've reviewed the attribute behavior change and accept it. It does not modify the value.
+
+This is a temporary tool. Once the migration is complete and migration warnings are disabled, you should remove this filter from your templates.
+
+
batch(int $length, mixed $item): array .[filter]
------------------------------------------------
A filter that simplifies listing linear data in a table format. It returns an array of arrays with the specified number of items. If you provide a second parameter, it will be used to fill in missing items in the last row.
@@ -827,6 +853,21 @@ Extracts a portion of a string. This filter has been replaced by the [#slice] fi
```
+toggle .[filter]{data-version:3.1}
+----------------------------------
+The `toggle` filter controls the presence of an attribute based on a boolean value. If the value is truthy, the attribute is present; if falsy, the attribute is omitted entirely:
+
+```latte
+
+{* If $isGrid is truthy:
*}
+{* If $isGrid is falsy:
*}
+```
+
+This filter is useful for custom attributes or JavaScript library attributes that require presence/absence control similar to HTML boolean attributes.
+
+The filter can only be used within HTML attributes.
+
+
translate(...$args) .[filter]
-----------------------------
Translates expressions into other languages. To make the filter available, you need to [set up the translator |develop#TranslatorExtension]. You can also use the [tags for translation |tags#Translation].
diff --git a/latte/en/html-attributes.texy b/latte/en/html-attributes.texy
new file mode 100644
index 0000000000..1107579e99
--- /dev/null
+++ b/latte/en/html-attributes.texy
@@ -0,0 +1,151 @@
+Smart HTML Attributes
+*********************
+
+.[perex]
+Latte 3.1 comes with a set of improvements that focuses on one of the most common activities in templates – printing HTML attributes. It brings more convenience, flexibility and security.
+
+
+Boolean Attributes
+==================
+
+HTML uses special attributes like `checked`, `disabled`, `selected`, or `hidden`, where the specific value is irrelevant—only their presence matters. They act as simple flags.
+
+Latte handles them automatically. You can pass any expression to the attribute. If it is truthy, the attribute is rendered. If it is falsey (e.g. `false`, `null`, `0`, or an empty string), the attribute is completely omitted.
+
+This means you can say goodbye to cumbersome macro conditions or `n:attr` and simply use:
+
+```latte
+
+```
+
+If `$isDisabled` is `false` and `$isReadOnly` is `true`, it renders:
+
+```latte
+
+```
+
+If you need this toggling behavior for standard attributes that don't have this automatic handling (like `data-` or `aria-` attributes), use the [toggle |filters#toggle] filter.
+
+
+Null Values
+===========
+
+This is one of the most pleasant changes. Previously, if a variable was `null`, it printed as an empty string `""`. This often led to empty attributes in HTML like `class=""` or `title=""`.
+
+In Latte 3.1, a new universal rule applies: **A value of `null` means the attribute does not exist.**
+
+```latte
+
+```
+
+If `$title` is `null`, the output is ``. If it contains a string, e.g. "Hello", the output is ``. Thanks to this, you don't have to wrap attributes in conditions.
+
+If you use filters, keep in mind that they usually convert `null` to a string (e.g. empty string). To prevent this, use the [nullsafe filter |filters#Nullsafe Filters] `?|`:
+
+```latte
+
+```
+
+
+Classes
+=======
+
+You can pass an array to the `class` attribute. This is perfect for conditional classes: if the array is associative, the keys are used as class names and the values as conditions. The class is rendered only if the condition is true.
+
+```latte
+
+```
+
+If `$isActive` is true, it renders:
+
+```latte
+
+```
+
+This behavior is not limited to `class`. It works for any HTML attribute that expects a space-separated list of values, such as `itemprop`, `rel`, `sandbox`, etc.
+
+```latte
+ $isExternal]}>link
+```
+
+
+Styles
+======
+
+The `style` attribute also supports arrays. It is especially useful for conditional styles. If an array item contains a key (CSS property) and a value, the property is rendered only if the value is not `null`.
+
+```latte
+
+```
+
+If `$isVisible` is false, it renders:
+
+```latte
+
+```
+
+
+Data Attributes
+===============
+
+Often we need to pass configuration for JavaScript into HTML. Previously this was done via `json_encode`. Now you can simply pass an array or stdClass object to a `data-` attribute and Latte will serialize it to JSON:
+
+```latte
+
+```
+
+Outputs:
+
+```latte
+
+```
+
+Also, `true` and `false` are rendered as strings `"true"` and `"false"` (i.e. valid JSON).
+
+
+Aria Attributes
+===============
+
+The WAI-ARIA specification requires text values `"true"` and `"false"` for boolean values. Latte handles this automatically for `aria-` attributes:
+
+```latte
+
+```
+
+Outputs:
+
+```latte
+
+```
+
+
+Type Checking
+=============
+
+Have you ever seen `` in your generated HTML? It's a classic bug that often goes unnoticed. Latte introduces strict type checking for HTML attributes to make your templates more resilient against such oversight.
+
+Latte knows which attributes are which and what values they expect:
+
+- **Standard attributes** (like `href`, `id`, `value`, `placeholder`...) expect a value that can be rendered as text. This includes strings, numbers, or stringable objects. `null` is also accepted (it drops the attribute). However, if you accidentally pass an array, boolean or a generic object, Latte triggers a warning and intelligently ignores the invalid value.
+- **Boolean attributes** (like `checked`, `disabled`...) accept any type, as their presence is determined by truthy/falsey logic.
+- **Smart attributes** (like `class`, `style`, `data-`...) specifically handle arrays as valid inputs.
+
+This check ensures that your application doesn't produce unexpected HTML.
+
+
+Migration from Latte 3.0
+========================
+
+Since the behavior of `null` (it used to print `""`, now it drops the attribute) and `data-` attributes (booleans used to print `"1"`/`""`, now `"true"`/`"false"`) has changed, you might need to update your templates.
+
+For a smooth transition, Latte provides a migration mode that highlights differences. Read the detailed guide [Migration from Latte 3.0 to 3.1|cookbook/migration-from-latte-30].
+
+[* html-attributes.webp *]
diff --git a/latte/en/syntax.texy b/latte/en/syntax.texy
index a65b056772..ef926e75be 100644
--- a/latte/en/syntax.texy
+++ b/latte/en/syntax.texy
@@ -111,6 +111,34 @@ Which outputs, depending on the variable `$url`:
However, n:attributes are not only a shortcut for pair tags, there are some pure n:attributes as well, for example the coder's best friend [n:class|tags#n:class] or the very handy [n:href |application:creating-links#In the Presenter Template].
+In addition to the syntax using quotes `
`, you can use alternative syntax with curly braces `
`. The main advantage is that you can freely use both single and double quotes inside `{...}`:
+
+```latte
+
...
+```
+
+
+Smart HTML Attributes .{data-version:3.1}
+=========================================
+
+Latte makes working with standard HTML attributes incredibly easy. It handles boolean attributes like `checked` for you, removes attributes containing `null`, and allows you to compose `class` and `style` values using arrays. It even automatically serializes data for `data-` attributes into JSON.
+
+```latte
+{* null removes the attribute *}
+
+
+{* boolean controls presence of boolean attributes *}
+
+
+{* arrays work in class *}
+
$isActive]}>
+
+{* arrays are JSON-encoded in data- attributes *}
+
+```
+
+Read more in the separate chapter [Smart HTML Attributes|html-attributes].
+
Filters
=======
@@ -148,10 +176,17 @@ On a block:
```
Or directly on a value (in combination with the [`{=expr}` |tags#Printing] tag):
+
```latte
{=' Hello world '|trim}
```
+If the value can be `null` and you want to avoid applying the filter in that case, use the [nullsafe filter |filters#Nullsafe Filters] `?|`:
+
+```latte
+
{$heading?|upper}
+```
+
Dynamic HTML Tags .{data-version:3.0.9}
=======================================
@@ -204,7 +239,7 @@ Simple strings are those composed purely of letters, digits, underscores, hyphen
Constants
---------
-Since quotes can be omitted for simple strings, we recommend writing global constants with a leading slash to distinguish them:
+Use the global namespace separator to distinguish global constants from simple strings:
```latte
{if \PROJECT_ID === 1} ... {/if}
@@ -265,8 +300,6 @@ A Window into History
Over its history, Latte introduced several syntactic sugar features that appeared in PHP itself a few years later. For example, in Latte, it was possible to write arrays as `[1, 2, 3]` instead of `array(1, 2, 3)` or use the nullsafe operator `$obj?->foo` long before it was possible in PHP itself. Latte also introduced the array expansion operator `(expand) $arr`, which is equivalent to today's `...$arr` operator from PHP.
-The undefined-safe operator `??->`, which is similar to the nullsafe operator `?->` but does not raise an error if the variable does not exist, was created for historical reasons, and today we recommend using the standard PHP operator `?->`.
-
PHP Limitations in Latte
========================
diff --git a/latte/en/tags.texy b/latte/en/tags.texy
index e9cb9309ae..cfbc94ae6f 100644
--- a/latte/en/tags.texy
+++ b/latte/en/tags.texy
@@ -16,7 +16,7 @@ An overview and description of all the tags available by default in the Latte te
| `{ifset}` … `{elseifset}` … `{/ifset}` | [ifset condition |#ifset elseifset]
| `{ifchanged}` … `{/ifchanged}` | [tests if a value has changed |#ifchanged]
| `{switch}` `{case}` `{default}` `{/switch}` | [switch condition |#switch case default]
-| `n:else` | [alternative content for conditions |#n:else]
+| `n:else`, `n:elseif` | [alternative content for conditions |#n:else]
.[table-latte-tags language-latte]
|## Loops
@@ -252,18 +252,20 @@ Did you know you can add the `tag-` prefix to n:attributes? Then the condition w
Awesome.
-`n:else` .{data-version:3.0.11}
--------------------------------
+`n:else` `n:elseif` .{toc: n:else}{data-version:3.0.11}
+-------------------------------------------------------
-If you write the `{if} ... {/if}` condition in the form of an [n:attribute |syntax#n:attributes], you have the option to specify an alternative branch using `n:else`:
+If you write the `{if} ... {/if}` condition in the form of an [n:attribute |syntax#n:attributes], you have the option to specify alternative branches using `n:else` and `n:elseif` (since Latte 3.1):
```latte
In stock {$count} items
+Invalid count
+
not available
```
-The `n:else` attribute can also be used in conjunction with [`n:ifset` |#ifset elseifset], [`n:foreach` |#foreach], [`n:try` |#try], [#`n:ifcontent`], and [`n:ifchanged` |#ifchanged].
+The `n:else` attribute can also be used in conjunction with [`n:ifset` |#ifset elseifset], [`n:foreach` |#foreach], [`n:try` |#try], [`n:ifcontent`|#nifcontent], and [`n:ifchanged` |#ifchanged].
`{/if $cond}`
@@ -947,6 +949,9 @@ HTML Coder Helpers
`n:class`
---------
+.[note]
+Since Latte 3.1, the standard HTML class attribute has gained the [same functionality |html-attributes#classes]. So you don't need to use n:class anymore
+
Thanks to `n:class`, it's very easy to generate the HTML `class` attribute exactly as needed.
Example: I need the active element to have the class `active`:
@@ -997,6 +1002,12 @@ Depending on the returned values, it prints, for example:
```
+Smart attribute features in Latte 3.1, such as dropping `null` values or passing arrays into `class` or `style`, also work within `n:attr`:
+
+```latte
+
+```
+
`n:tag`
-------
diff --git a/latte/files/html-attributes.webp b/latte/files/html-attributes.webp
new file mode 100644
index 0000000000..56e729541b
Binary files /dev/null and b/latte/files/html-attributes.webp differ