French typography


I was wondering if anyone already worked on a plugin to apply french typography on all contents ?
I mean, replace “TEXT” with « TEXT », adding a unbreakable space before double marks (;, !, ?..) and so on…

I was thinking working on a twig filter but is there a way to apply it onto the contents displayed inside the admin panel, without modifying admin templates ?

Thanks in advance for your ideas, propositions…

For those who are interested I’ve found the PHP-Typography library (GitHub - mundschenk-at/php-typography: A PHP library for improving your web typography.) and I’ve started a little plugin for Grav.
Here is the code : bricebou/grav-plugin-better-typography - grav-plugin-better-typography - vous délivre votre code

I’m using the onPageContentProcessed event to deal with the page.content ; and I’m using the onTwigInitialized event to add the |bettertypo Twig filter I can use on custom header.field. I didn’t find a way to apply the Twig filter on the page.content without having side effects…

The library deals with multiple languages and is quite configurable. I intend to add some options in the Grav plugin.

1 Like

May I suggest using a dynamic language instead of hardcoded? I think it should use a language from Grav. Talking about this part:

		$settings->set_diacritic_language( 'fr' );
		$settings->set_hyphenation_language( 'fr' );

Also would be great to have other settings available via blueprint

Thanks @Karmalakas !

I plan to do what you suggest :slight_smile: The todo list

  • propose an option to dynamically use the page content or the default site language;
  • adding options to (des)activate hyphenation, select quotes…

But first, I have to familiarize myself with Grav and its plugin configuration process ^^

1 Like

Done ! ^^ Check these two commits:

I’m looking forward to reading about your tests, your reviews…

Thanks in advance :slight_smile:

Great job :slight_smile:
Just don’t hold your breath waiting for my tests - I have my hands full for now :slight_smile:

Just have some comments looking at the code:

  • Is plugin really compatible with Grav 1.6.0? It might be, but just to make sure
  • What are these three warning icons in blueprints? Some unicode symbols?
  • If you plan at some point publishing this plugin to GPM, I’d really love to see code cleaned up :slight_smile: Currently it’s very inconsistent and a bit difficult to read. Also adding PHPDocs at least would be appreciated :wink: That’s if you plan to publish it

BTW, I noticed currently there’s no way to disable FR specific punctuation if your page is in FR language, is there? :thinking:

Thanks @Karmalakas.

  1. The plugin hasn’t been tested with the 1.6 version of Grav… I was put by the bin/plugin devtools new-plugin command and I haven’t modified this… But, I’m going to modify this dependency.
  2. in the blueprints.yaml, I’ve just copied what was produced by the PHP-Typography library… I should use a html value instead ?
  3. That would be the point to publish this plugin at some point, if it can be useful to others. I’m not a developer so that can explain why the code is inconsistent… I’ve just noticed I had an indentation issue… Can you point me to what I must deal with ?
  4. There is no way to disable FR specific typography when the page is in french… It’s because I assume you should enable these improvements when dealing with french language, as theye are basics. But if needed, I can add a configuration toggle to enable/disable french language improvements.

Thanks !

Copying is fine, but I think you copied too much. Even editor shows these weird symbols

IMO users should have a choice to enable or disable it. You can leave it on by default

Where to start… :sweat_smile:

It’s not just indentation, but small things :slight_smile: Eg. redundant comments:

        // DASH STYLE
        if ($config['smart_dashes'] === true) {

IMHO there’s absolutely no need for all of these, when right on the next line you can clearly see what’s happening.

Then the same if statements - what happens if for whatever reason user deletes some config options (eg. smart_dashes)? PHP will yield some errors :slight_smile: I would change them to

if (!empty($config['smart_dashes'])) {

Then code style - I’d suggest to go through PSR-12 and you’ll get at least an idea :wink: Of course it’s your plugin and you decide how it should look, but I think, if you’d like someone else to contribute, it’s much better to have a nice for everyone code to work with. I’m sure I’m guilty myself of not always writing a beautiful code, but I try :sweat_smile:

Thanks a lot @Karmalakas !

About the blueprints.yaml : I’m using VSCode on a Debian based Linux distribution and I don’t see this kind of symbol… I’ll use the HTML entities instead :slight_smile:

I’ll propose an option to enable/disable french advanced typographic improvements then.

I’ll try to cleanup my code and I’ll run phpcs & phpcbf with the --standard=PSR12 option to improve it.

I’ll post again when I’ve worked on these issues.

Thanks again.

1 Like

@bricebou, To add to the valid points mentioned by @Karmalakas

  • Config: better-typography.yaml

    • Not everyone uses Admin during development or on production server. Provide all variables defined in blueprints.yaml also in better-typography.yaml. Including the documentation and possible options. See eg. /system/config/system.yaml
    • Use defaults that will fit for most users.
    • I wonder, should the plugin run on each and every page, or maybe only for certain languages if multiple languages have been set in system.yaml.
  • Blueprint: blueprints.yaml

    • Also vscode gives warnings about the special characters: “The character U+200b is invisible. Adjust settings”
    • I would prefer meaningful variable names that are more “self-documenting”. E.g. useSmartDashes instead of smart_dashes
  • Use use to import classes:

    use PHP_Typography\PHP_Typography;
    use PHP_Typography\Settings;
    use Twig_SimpleFilter;
    new Twig_SimpleFilter(...);
    $settings = new Settings(true);
    $typography = new PHP_Typography();
  • Some If-statements could be removed/refactored:

    if ($config['smart_dashes'] === true)
      $settings->set_smart_dashes( true );
      $settings->set_smart_dashes_style( $config['dash_style'] );
      $settings->set_smart_dashes( false );


    $settings->set_smart_dashes($this->config->get('plugins.better-typography.useSmartDashes', false));
    $settings->set_smart_dashes_style($this->config->get('plugins.better-typography.dashStyle', 'traditionalUS'));
    • Use $this->config->get(‘’, default);
      • Use same default as in blueprints.yaml and better-typography.yaml.
      • This also prevents possible issues when variable has not been set in better-typography.yaml.
    • Setting smart_dashes_style doesn’t hurt. It’s ignored if useSmartDashes is false.

    Or maybe:

    $useSmartDashes = $this->config->get('plugins.better-typography.useSmartDashes', false);
    if ($useSmartDashes) {
      $settings->set_smart_dashes_style($this->config->get('plugins.better-typography.dashStyle', 'traditionalUS'));
  • Don’t use tabs… See also PSR-12


  • Using $language = $this->grav['language']->getLanguage();
    Using the active language might cause an unexpected behaviour. In a multilingual website, when a page has not been translated for the requested langauge, another language might be used as fallback. Eg. When user requests the Spanish page, a French page might be returned. If user is the first to access the page, the French page is updated and cached. If later the French page is requested, the cache with the Spanish update will be served.
    I think the language of the page file itself using Page::langueage() should be used.

Thanks a lot @pamtbaau !

I’ve started to work on all these issues but my week-end will be quite busy so I’ll post again only on Monday or Tuesday…

There is one point I’m not sure to understand…

By the way I’ve just saw that I need to test the diacritics language (only available for de-DE and en-US).

Thanks again, your comments (@Karmalakas and @pamtbaau) are highly appreciated :slight_smile:

@bricebou, See update on previous post.

With testing only certain languages I mean:
Grav can be setup for multiple languages. I can imagine that not every language needs to replace dashes, quotes and the likes. If so, wouldn’t it then be handly to have a config variable that can be set to an array of languages for which the markdown should be updated. Excluding the available languages which should not be handled by the plugin.


So I’ve tried to address all the issues and to apply all the recommendations both of you, @Karmalakas and @pamtbaau, gave me :slight_smile:

Here is the commit history if you want to check it out : bricebou/grav-plugin-better-typography - grav-plugin-better-typography - vous délivre votre code

I hope I don’t forget anything…

About the language… I’m not sur that using $this->grav['page']->language() is the solution : in my case, on a monolingual site, this return “NULL”… So I should run $this->grav['language']->getLanguage(); after ?

The ideal solution would be to have a language based configuration of the plugin ; each language has its own typographic rules, so we can imagine to declare for each option an array of settings :

Languages : [fr,en,ro]
SmartQuotes: [true,false,true]
QuotesStyles: [doubleGuillemetsFrench,doubleCurled,doubleGuillemets]

What do you think ? Is there a better way ?

But I don’t see how to provide an easy configuration form with this approach…

Thanks again and in advance for any suggestion, improvement…

I think in this case I would go with list field where in each item you can select a language (from available ones) and then all the typography options for it. but also leave separate list of all same options not assigned to any language as a default


So I should run $this->grav['language']->getLanguage(); after ?

I’ve never come across this typography issue, so I wouldn’t know exactly… I would think the plugin should respond to the target audience of the page. So the language set for the page is leading. If no language is set for the page, a default setting should be used.

What if the user’s language is leading? For example, when supported languages are being set as [fr, en, it] but there is only a single 02.typography/ available, then browsing to https://domain/fr/typography, https://domain/en/typography and https://domain/it/typography will all be present with the same French page. If the first user is Italian, the page will be processed with Italian settings, cached and reused for all other languages. I don’t think this is the behaviour you’re looking for.

What do you think ? Is there a better way ?

You could try playing with the following:

  • In php add:
    public static function languageList(): array
        /** @var Grav */
        $grav = Grav::instance();
        /** @var Data */
        $config = $grav['config'];
        $languages = [
            'default' => 'Default'
        forEach($config->get('system.languages.supported', []) as $language) {
            $languages[$language] = strtoupper($language);
        return $languages;
    public static function maxLanguages(): int
        /** @var Grav */
        $grav = Grav::instance();
        /** @var Data */
        $config = $grav['config'];
        return count($config->get('system.languages.supported', [])) + 1;
  • And in your blueprint:
      type: list
      style: vertical
      label: Language settings
      min: 1
      data-max@: 'Grav\Plugin\YourPlugin::maxLanguages'
          type: select
          label: Language
          data-options@: 'Grav\Plugin\YourPlugin::languageList'
          type: select
          label: Style
            a: aaa
            b: bbb

If no languages have been set, you can only set values for ‘Default’. If languages have been set, you can set Default values, and settings for each available language. For languages without settings, Default can be used.

Thanks a lot @Karmalakas & @pamtbaau ! :smiley:

I’ve worked on this and the result is on this branch : bricebou/grav-plugin-better-typography - grav-plugin-better-typography - vous délivre votre code

I’ve tested it a few and it seems to be working.

Let me know what you think !

If I want to release the plugin, how do I need to deal with the PHP Typography library ? Do I need to add the vendor/ folder to the repository or the admin plugin panel runs automatically composer update while installing a plugin ?

Thanks again :slight_smile:

ATM I don’t have much time to look closely at it, but I’m gonna nitpick a bit :smiley:

  • Suggestion to change Twig filter to camelCase - betterTypo
  • Update

Hello bricebou,

just chiming in to say that there are currently two typography plugins that don’t exactly do what you would like, but maybe you’d like to take a peek at the code:

(the latter hasn’t been added to GPM yet)

MT uses automatic replacements like what you are doing, TH is a manual thing for Admin editor fields, configurable for any characters you’d like to be able to add.

Hope this helps you in some way. It’s nice to see other people who think typography is important :slight_smile:

@bricebou, Please note that line 122 shows a deprecation warning on the following function:

Tp prevent future issues with your plugin, you might want to remove/replace this function before the maintainer removes the function in a future release.