Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Jolitypo Bundle Laravel Package

jolicode/jolitypo-bundle

Symfony bundle integrating JoliTypo to automatically apply typographic fixes to rendered HTML. A Twig filter corrects dashes, quotes, ellipses, non-breaking spaces and more, keeping French and other content clean without manual editing.

View on GitHub
Deep Wiki
Context7

Getting Started

1.0 is the first stable release, and it changes the framing. The bundle was long dormant; 1.0 lands alongside a stable JoliTypo core, which means the package is maintained again and the old "archived, migrate away" advice no longer applies as a blanket rule. The release note is one line, though, so before you plan around it, check the actual constraints on Packagist (composer why-not jolicode/jolitypo-bundle ^1.0) against your PHP and Symfony versions, and skim the tags between your current version and 1.0 for BC breaks — the note doesn't list any, which is not the same as there being none.

If you're on a legacy Symfony 2/3 app pinned to a 0.x release: nothing about your situation changes until you upgrade, and 1.0 is now a destination rather than a dead end.

If you're starting fresh:

  1. composer require jolicode/jolitypo-bundle — verify it resolves against your Symfony version; if it doesn't, use the core library directly (below) rather than fighting the constraints.
  2. Register it in AppKernel / config/bundles.php: new JoliTypoBundle\JoliTypoBundle().
  3. Add the minimal config block and confirm the service exists: bin/console debug:container jolitypo.
  4. First use case — a Twig filter over CMS/editorial body copy:
{{ article.body|jolitypo }}

That single line is the 90% use case: turning straight quotes into typographic quotes, ... into …, -- into en/em dashes, and inserting French non-breaking spaces before : ; ! ? in rendered content.

Look first at three things: DependencyInjection/Configuration.php (the real config schema — README-era docs drift), the Twig extension (filter names), and the service definition for jolitypo (how fixers and locale are wired). Those three files answer almost every question you'll have.

A note if you're on Laravel: this is a Symfony bundle and won't register there. Require jolicode/jolitypo and construct JoliTypo\Fixer yourself in a service provider — every pattern below still applies, you just skip the bundle layer entirely.

Implementation Patterns

Twig filter for display. The bundle's Twig extension is the lowest-friction path. Apply it at the outermost presentation layer so the stored content stays clean and portable:

{# content is trusted HTML from your CMS #}
{{ article.body|jolitypo|raw }}

Order matters: fix the string, then mark it raw. Do not run |jolitypo on a string that has already been escaped and then un-escape it — you'll typographically "fix" entities like ".

Service injection for non-Twig paths. Grab the jolitypo service (a JoliTypo\Fixer) and call fix() explicitly. This is what you want for emails, PDF generation, API payloads, or console commands:

class NewsletterRenderer
{
    public function __construct(private \JoliTypo\Fixer $fixer) {}

    public function render(Article $article): string
    {
        return $this->fixer->fix($article->getBody());
    }
}

Fix at write time vs. render time. The real architectural decision:

  • Render time (|jolitypo) — best when fixer config changes and you want existing content to pick it up, or when content is short and traffic is low. Cost is paid on every request.
  • Write time (in a Doctrine listener or an import step) — best for high-traffic pages and for content that gets exported to search indexes, sitemaps, or third-party feeds. Cost is paid once, but a config change requires a backfill.

Most teams end up hybrid: fix on write for the CMS body, fix on render for short dynamic strings (titles, bylines).

Backfilling existing content. Write a console command that iterates and fixes in batches. JoliTypo is regex-based and CPU-cheap per string but you don't want it in a request loop over 50k rows:

foreach ($this->articleRepo->iterateBatch($batchSize) as $article) {
    $article->setBody($this->fixer->fix($article->getBody()));
}
$this->em->flush();
$this->em->clear();

Locale-aware configuration. The French-oriented fixers (FrenchNoBreakSpace) behave differently depending on locale, and quote styles differ between fr_FR, en_US, and de_DE. If you serve multiple locales, configure separate fixer presets and select the right one per request rather than relying on a single global setting. In the bundle this means either multiple service definitions or constructing a JoliTypo\Fixer manually with the locale and fixer list you need — the latter is often simpler, and it's what the Symfony bridge and any Laravel integration make first-class.

Scope narrowly. Apply the fixer to editorial prose only. Explicitly exclude: code samples, URLs, email addresses, API/JSON responses, filenames, and anything that will be diffed or parsed downstream.

Custom fixers. JoliTypo accepts fixer instances, so domain rules (e.g. "always use a thin space before %", "normalize No. to N°") can be added as your own class and registered alongside the built-ins. With 1.0 the DI layer is maintained again, so container-level registration is a reasonable option — but building your own Fixer in a dedicated service is still the least surprising, and it ports unchanged if you later drop the bundle.

Gotchas and Tips

"Stable" is not the same as "battle-tested on your stack." 1.0 is the first stable tag after a multi-year gap. That's a good sign, not a guarantee: pin the version (^1.0), read Configuration.php rather than the docs, and smoke-test one known French string and one English string before rolling it across every template. Treat it as a new dependency, not as an old one that merely got a tag.

Migrating from a 0.x pin is an upgrade, not a rename. The release note lists no breaking changes, which for a package this dormant usually means "nobody wrote them down." Diff DependencyInjection/Configuration.php and the service definitions against your installed version before you bump. The fixer classes and output should be identical, but the wiring is where the risk sits.

Do not install the bundle and the Symfony bridge together. Both want to own the jolitypo service id and the Twig filter. You'll get silent double-registration or a container override that depends on bundle load order. Pick one — bundle if you want zero boilerplate, bridge or core lib if you want explicit control.

Non-breaking spaces are invisible. FrenchNoBreakSpace inserts U+00A0, which looks identical to a space in an editor and in a PR diff. This causes three recurring bugs: tests that assert with a plain space and mysteriously fail, trim()/preg_replace('/\s+/') calls that strip or collapse the NBSP downstream, and DB collations that treat it as equal to a normal space during LIKE comparisons. In tests, assert with explicit escapes ("\u{00A0}" or "\xC2\xA0").

Fixer order is load-bearing. Dashes and ellipses should run before quote fixers, and quote fixers should run before any fixer that inserts spacing. If you hand-build the fixer list, keep the built-in preset ordering as a reference rather than inventing your own.

Not idempotent in the strict sense. Running the filter twice is usually harmless but not guaranteed — already-smart quotes can be re-interpreted, and spacing fixers can accumulate. If content flows through multiple render layers, guard against double application rather than trusting it.

Ellipsis and dash transformations break naive assertions. ... becomes … (one character, not three), which affects strlen(), meta-description truncation (mb_substr at 155 chars now cuts one char earlier), regexes matching \.\.\., full-text search stemming, and any diff-based editorial workflow.

HTML awareness is limited. These fixers are regex-based string transformers. Running them over raw HTML risks touching attribute values (title="...", data-json='...'), inline <script>, or JSON-LD blocks. Either fix at the text-node level, or restrict input to content you know is markup-light. When in doubt, test with a fixture containing an anchor with a title attribute and a data-* JSON payload.

Debug by bypassing the container. If output looks wrong, instantiate JoliTypo\Fixer directly in a console command or bin/console shell with an explicit fixer list and locale. That immediately tells you whether the problem is JoliTypo itself or the bundle's wiring.

Cache invalidation after config changes. If you cache rendered fragments (Twig cache, HTTP cache, Varnish), changing fixer config won't retroactively change cached output. Clear all layers, not just Symfony's.

Locale defaults silently misbehave. If the locale isn't set, quote fixers fall back to whatever the default is, which may not be your target language — producing ” where you wanted ». Always set it explicitly, and assert on one known string per locale in your test suite.

Extension point to prefer. Let the bundle provide the service and Twig filter, but do all custom behavior by constructing JoliTypo\Fixer with your own fixer array in your own service. That code survives a later move to the bridge, a Laravel port, or a bundle removal untouched — and after a long dormancy, that optionality is worth more than the config sugar.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource