Categories
Uncategorized

Building Gutenberg Blocks (Part 3)

Internationalization

We’ll take a break from building actual blocks and talk about internationalization or i18n. Taking good care of i18n when you build your plugin will allow you or someone else to translate the text of your plugin so it’s easier to use in languages other than the one it was written on

It works very close to how the PHP i18n functions work (and I believe this is deliberate), just different enough to take the Javascript needs into account.

First, we import the necessary functions. Most of the time it’ll just be __ but there are others.

import { __ } from '@wordpress/i18n';

Now, whenever we have a string that we want to translate, we put inside the __ command and indicate the text domain it’s associated with.

This example tells translators that it’s the string to translate is Example 07 and the text domain is rivendellweb-blocks.

title: __( 'Example  07', 'rivendellweb-blocks' )

Once we have all the strings ready for translation we need to generate a POT file to translate from and a PO file with the translated strings.

Translating plugins is similar to how you translate themes. I’ve documented the process in Translating WordPress Themes so I won’t go into too much detail about it here.

Using Poedit the process is simple when using the Pro or Pro+ version.

  1. Open Poedit and select Translate WordPress theme or plugin
  2. Locate the plugin folder
  3. Generate a POT file
  4. Save the POT file to a languages folder inside your plugin. Create it if it doesn’t exist
  5. Use the POT file to generate your translation
  6. Save the .po translation file to a languages folder inside your plugin

The last step is to load all available translations for our plugin from the languages directory by using load_plugin_textdomain() like the example below.

<?php
function rivendellweb_blocks_example_07_load_textdomain() {
    load_plugin_textdomain(
        'rivendellweb-blocks',
    false,
    basename( __DIR__ ) . '/languages' );
}

add_action( 'init', 'rivendellweb_blocks_example_07_load_textdomain' );

So now we’ve made our plugins fully translatable and if we add new functionality or update the wording of the plugin text shown to the user we just need to update the template and the translations.

Categories
Uncategorized

Building Gutenberg Blocks (Part 2)

We’ve only worked with static content so far. But the idea behind Gutenberg is to let you work with your own content.

This section will also introduce the RichText block components as opposed to the plain text elements we’ve been working with.

Editable Blocks

The first part of this process is to create an editable block where we can enter arbitrary data that will be saved when we save or publish the post.

IN addition to i18n and blocks imports we import the RichText component from the block-editor package. This will do the heavy load in the code below.

import { __ } from '@wordpress/i18n';
import { registerBlockType } from '@wordpress/blocks';
import { RichText } from '@wordpress/block-editor';

The first difference is that we’ve now added an attributes attribute that will hold the structured data needs of a block. WordPress will also use this to validate the content of the block.

In this example, the content child of the attribute is an array of children that paragraphs (the children match the p selector).

registerBlockType( 'rivendellweb-blocks/example-03', {
    title: __( 'Example 03', 'rivendellweb-blocks' ),
    icon: 'universal-access-alt',
    category: 'layout',
    attributes: {
        content: {
            type: 'array',
            source: 'children',
            selector: 'p',
        },
    },

The edit and save methods get a little more complicated when we use custom data.

The save() method now takes props as an attribute.

We assign the content attribute, the setAttribute and className to props.

Next, we create an onChangeContent function that will trigger when we add or remove content from the element.

Finally, we return a RichText element that returns p elements with the className class and the content as the value of the element.

The onChange event firest with the onChangeContent every time the element changes.

    example: {},
    edit: ( props ) => {
        const {
            attributes: { content },
            setAttributes,
            className,
        } = props;

        const onChangeContent = ( newContent ) => {
            setAttributes( { content: newContent } );
        };

    return (
            <RichText
                tagName="p"
                className={ className }
                onChange={ onChangeContent }
                value={ content }
            />
        );
    },

The save() method also takes props as a parameter and uses props.attributes.content as the value of the value parameter.

    save: ( props ) => {
        return (
            <RichText.Content
                tagName="p"
                value={
                  props.attributes.content
        } />
        );
    },
} );

Inner blocks

The next idea is to see if we have any way to automate the content of the block. Right now our block is limited to whatever tag we use as the value of tagName.

We can create a single block with a list of default blocks using inner blocks.

We import InnerBlocks from @wordpress/block-editor and then use it as the child of our content div.

import { registerBlockType } from '@wordpress/blocks';
import { InnerBlocks } from '@wordpress/block-editor';

// Register block
registerBlockType( 'rivendellweb-blocks/example-04', {
    title: 'Example 04',
    category: 'rivendellweb-blocks',
    icon: 'translation',
    edit: ( { className } ) => {
        return (
            <div className={ className }>
                <InnerBlocks />
            </div>
        );
    },
    save: ( { className } ) => {
        return (
            <div className={ className }>
                <InnerBlocks.Content />
            </div>
        );
    },
} );

The idea is that using Inner Blocks we get a set of blocks available without any additional code.

Blocks with customized content content

Inner blocks give us a set of sensible default set of blocks to use but it may or may not be enough to suit our needs. Fortunately, we don’t have to stick with the defaults if we need specific blocks to accomplish our goals.

First we run all our imports as usual, __ for i18n, registerBlockType and InnerBlocks

import { __ } from '@wordpress/i18n';
import { registerBlockType } from '@wordpress/blocks';
import { InnerBlocks } from '@wordpress/block-editor';

The first difference is the inclusion of an ALLOWED_BLOCKS constant that we’ll use later to tell Gutenberg what blocks to allow in our inner blocks.

const ALLOWED_BLOCKS = [
  'core/image',
  'core/heading',
  'core/paragraph'
];

The final change is in the edit method of registerBlockType where we add an allowedBlock attribute and use the ALLOWED_BLOCKS constant as the value.

registerBlockType( 'rivendellweb-blocks/example-05', {
  title: __('Example 05', 'rivendellweb-blocks'),
    category: 'rivendellweb-blocks',
    icon: 'translation',

  edit: ( { className } ) => {
    return (
      <div className={ className }>
        <InnerBlocks
          allowedBlocks = { ALLOWED_BLOCKS }/>
      </div>
    );
  },

  save: ( { className } ) => {
    return (
      <div className={ className }>
        <InnerBlocks.Content />
      </div>
    );
  },
} );

The idea is that we restrict the content of the InnerBlock element to whatever elements we want to use. This way we can be sure that users don’t modify the content in ways we don’t expect.

Block Templates

We can go even further and define a complete template for the block, not only limiting the blocks but providing a ready-to-complete template

The import elements don’t change. We import __, registerBlockType and InnerBlocks as before.

import { __ } from '@wordpress/i18n';
import { registerBlockType } from '@wordpress/blocks';
import { InnerBlocks } from '@wordpress/block-editor';

Instead of an allowed blocks section, we create a BLOCK_TEMPLATE variable that lists the blocks we want to use, the order of the blocks and, where appropriate, placeholder text.

const BLOCK_TEMPLATE = [
  [ 'core/image', {} ],
  [ 'core/heading', { placeholder: 'Book Title' } ],
  [ 'core/heading', { placeholder: 'Book Author' } ],
  [ 'core/paragraph', { placeholder: 'Summary' } ],
];

The template attribute uses our BLOCK_TEMPLATE constant as the layout of the blocks we’re building

templateLocks allows locking the InnerBlocks area for the current template. It takes one of the following values:

  • all — prevents all operations. It is not possible to insert new blocks. Move existing blocks or delete them.
  • insert — prevents inserting or removing blocks, but allows moving existing ones.
  • false — prevents locking from being applied to an InnerBlocks area even if a parent block contains locking.

For example, our book block may want to do more than one paragraph summary or we may want to add more images. Or maybe not, the choice is up to the designer.

registerBlockType( 'rivendellweb-blocks/example-06', {
  title: __('Example 06', 'rivendellweb-blocks'),
    category: 'rivendellweb-blocks',
    icon: 'translation',
  edit: ( { className } ) => {
    return (
      <div className={ className }>
        <InnerBlocks
        template={ BLOCK_TEMPLATE }
        templateLock="false" />
      </div>
    );
  },

  save: ( { className } ) => {
    return (
      <div className={ className }>
        <InnerBlocks.Content />
      </div>
    );
  },
} );

We can build multicolumn blocks using core blocks and place them in a template. The following example creates a two-column layout with an image in the first column and one or more paragraphs in the second.

const TEMPLATE = [ 
  [ 'core/columns', {}, [
    [ 'core/column', {}, [
        [ 'core/image' ],
    ] ],
    [ 'core/column', {}, [
        [ 'core/paragraph', { placeholder: 'Enter side content...' } ],
    ] ],
  ] ] 
];

So now we have the flexibility of building composite blocks with pre-defined structure and content flow.

Categories
Uncategorized

Building Gutenberg Blocks (Part 1)

Whether we like it or use it, the Gutenberg editor is here to stay. While I don’t use it for my own content and have developed themes that rely on the classic editor plugins, I realize that any WordPress shop moving forward needs to at least be aware of how to build blocks.

It’s unlikely that the Classic Editor plugin or other means to disable Gutenberg will remain available past 2022 (when the Classic Editor plugin is supposed to stop being fully supported).

So this post will cover the creation of Gutenberg blocks and some basic setup in both PHP and Javascript

Creating the build system

The following block examples are meant to be built inside a plugin. They won’t work if installed inside a theme, which is also not recommended and will result in your theme being rejected for inclusion in the plugin directory.

PHP setup

On the root of the plugin folder, we’ll add the main PHP file to be the driver for all the blocks that we will create.

The first part is the plugin metadata. The information here will allow WordPress to use the plugin.

The name is required, everything else is optional.

<?php
/*
Plugin Name: rivendellweb-blocks
Description: Collection of blocks based
  on work done on the Rivendellweb
  theme
Version: 0.0.1
Author: Carlos Araya
Author URI: https://example.org
*/

The second part block is the actual code.

Exit the page unless there is a defined ABSPATH constant.

If we haven’t exited then require the index.php for each block that we want to work with. This allows us to keep the code for each block separately and the code inside the plugin

<?php
if ( ! defined( 'ABSPATH' ) ) {
  exit;
}

include 'rivendellweb-static/index.php';

Setting up the build environment

Because I’ve chosen to write my blocks in ESNext I have to set up a Node-based build system.

The first thing to do is to create a package.json file to store the plugins.

npm init --yes

This will create the package file and populate it automatically with all my configured preferences.

Next, we need to create a .gitignore file so that we won’t push unnecessary files and folders to git. To do this we run the following command

npx gitignore node

This will run npx, part of Node as of 5.2, and run the gitignore package for Node projects.

Instead of configuring WebPack to build and bundle the code directly, I will use @wordpress/scripts as the tooling for the project.

npm i -D @wordpress/scripts

Once we have the package installed, we need to replace the scripts block with one configured to use the scripts in the package. Modify the scripts portion of package.json to use the following scripts:

`"scripts": {
  "build": "wp-scripts build",
  "check-engines": "wp-scripts check-engines",
  "check-licenses": "wp-scripts check-licenses",
  "format:js": "wp-scripts format-js",
  "lint:css": "wp-scripts lint-style",
  "lint:js": "wp-scripts lint-js",
  "lint:md:docs": "wp-scripts lint-md-docs",
  "lint:md:js": "wp-scripts lint-md-js",
  "lint:pkg-json": "wp-scripts lint-pkg-json",
  "packages-update": "wp-scripts packages-update",
  "start": "wp-scripts start",
  "test:e2e": "wp-scripts test-e2e",
  "test:unit": "wp-scripts test-unit-js"
}`

Building Blocks

With the basic building tools in place, we’ll concentrate on building the blocks themselves inside a plugin.

We’ll build a base block and then enhance it with additional functionality.

Each block has two main files: index.php that manages the communication with WordPress core and index.js that runs the react code we need to make the block display in Gutenberg and render on the page.

All the block examples require you to build them using the process outlined in the previous section. They will not work otherwise.

You can check a finished version of the blocks in the Github repository at https://github.com/caraya/rivendellweb-blocks

Base

The first block will display static content as we might do for a call to action or other static content.

PHP

In the PHP side, the first part makes sure that we’re not trying to access the script directly by checking if the constant ABSPATH is defined. If not, we exit and do nothing.

<?php
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

If we pass the check then we start working with internationalization.

We use the load_plugin_textdomain to load a plugin’s translated .mo files.

We then add the function to the init hook.

function rivendellweb_blocks_example_01_load_textdomain() {
    load_plugin_textdomain( 'rivendellweb-blocks', false, basename( __DIR__ ) . '/languages' );
}
add_action( 'init', 'rivendellweb_blocks_example_01_load_textdomain' );

The second part is a little more complex.

We first test to see if Gutenberg is supported by checking if register_block_type is supported. If it’s not then we return as there is nothing for us to do.

It uses index.asset.php, a file generated during the build process, to reference pre-requisites and other assets to set them to a variable.

wp_register_script will register a script and its dependencies to be enqueued at a later time. It is different than using wp_enqueue_script.

register_block_type does the bulk of the work, it registers the block type and makes it available to the block editor.

The last part of this function is to check if wp_set_script_translations is available, meaning that we have translation tools available. If it is then we use it to load all the translated scripts for the plugin.

We also hook this function to the init hook to make sure that it’s available when WordPress first starts.

function rivendellweb_blocks_example_01_register_block() {

  if ( ! function_exists( 'register_block_type' ) ) {
    // Gutenberg is not active.
    return;
  }

    // automatically load dependencies and version
    $asset_file = include( plugin_dir_path( __FILE__ ) . 'build/index.asset.php');

    wp_register_script(
        'rivendellweb-blocks-example-01',
        plugins_url( 'build/index.js', __FILE__ ),
        $asset_file['dependencies'],
        $asset_file['version']
    );

    register_block_type( 'rivendellweb-blocks/example-01', array(
        'editor_script' => 'rivendellweb-blocks-example-01',
    ) );

  if ( function_exists('wp_set_script_translations' ) ) {
    wp_set_script_translations( 'rivendellweb-blocks-example-01', 'rivendellweb-blocks' );
  }

}
add_action( 'init', 'rivendellweb_blocks_example_01_register_block' );

Javascript / React

The Javascript portion of the block uses the i18n and blocks packages from WordPress to build the block.

If you’ve done work with internationalization you may recognize the __() command as the command you use to internationalize strings.

We first import the components that we want to use.

For this block, we’ll define the styles inside a constant, blockStyle, that will be used for both the editor and the rendered content.

The block title uses a localized string to indicate that translators can work with it.

The icon is one of those available in the WordPress dashicon library.

The part that surprised me the most is the need for two statements with the same content. The edit() creates the content specific to the editor while save() works with content specific to what will render on the client.

The block will use the same style in the editor and the front end.

import { __ } from '@wordpress/i18n';
import { registerBlockType } from '@wordpress/blocks';

const blockStyle = {
    backgroundColor: '#639',
    color: '#fff',
    padding: '20px',
};

registerBlockType( 'rivendellweb-blocks/example-01', {
    title: __( 'Example 01', 'rivendellweb-blocks' ),
    icon: 'universal-access-alt',
    category: 'layout',
    example: {},
    edit() {
        return (
            <div style={ blockStyle }>
                <h2>Notice</h2>
                <p>Hello World, step 1 (from the editor).</p>
            </div>
        );
    },
    save() {
        return (
            <div style={ blockStyle }>
                <h2>Notice</h2>
                <p>Hello World, step 1 (from the front end).</p>
            </div>
        );
    },
} );

We could create different constants holding different styles and then use them to produce different results for the editor block versus what appears in the front end.

External Styles

SO far all the styles have been hardcoded to the block. In this example, we’ll make the block use an external stylesheet for styling the block.

To use external style sheets we first have to remove the constant containing the styles for the element.

We then add one or more calls to wp_register_style to add the stylesheets.

We also have to modify the call to register_block_type to accommodate the styles

  • style is used for the default styles
  • editor_style is used for editor styles if they are different than the default. This loads after the default styles so they will override the basic styles if they are different.
<?php
// The editor styles override the default styles set below
wp_register_style(
  'rivendellweb-blocks-example-02-editor-stylesheets',
  plugins_url( 'editor.css', __FILE__ ),
  array( 'wp-edit-blocks' ),
  filemtime( plugin_dir_path( __FILE__ ) . 'editor.css' )
);

// Default styles
wp_register_style(
  'rivendellweb-blocks-example-02',
  plugins_url( 'style.css', __FILE__ ),
  array( ),
  filemtime( plugin_dir_path( __FILE__ ) . 'style.css' )
);

register_block_type( 'rivendellweb-blocks/example-02-stylesheets', array(
  'style' => 'rivendellweb-blocks-example-02',
  'editor_style' => 'rivendellweb-blocks-example-02-editor-stylesheets',
  'editor_script' => 'rivendellweb-blocks-example-02',
) );
Categories
Uncategorized

Translating WordPress Themes

I’ve been working on a new WordPress theme for a few weeks. Now I’m at the point where I can start worrying about the last finishing touches before I can start dogfooding it.

One of the things left in the to-do list is internationalizing the theme. Making sure that it’s translated to Spanish and that it is ready for people who want to translate it to other languages to do so.

In this post, we’ll concentrate on theme i18n (internationalization) for a theme. Gutenberg and blocks will be discussed in a separate post.

Translating Themes

For the translations to work, you have to make sure that textdomain is set on your styles.css main stylesheet. You also have to change the way that you write the strings using PHP.

Textdomain

When building the CSS stylesheet for your theme, make sure that you add a Text Domain field to the header of the stylesheet. WordPress will use it as the text domain to tie all translations to.

A minimal example of the theme header looks like this:

/*!
Theme Name: rivendellweb
Version: 1.0.0
Text Domain: rivendellweb
*/

Changing how you write strings

In order to translate our themes, we need to change the way we write our strings.

Using echo on its own, like this:

<?php
if ($req) echo ('(required)');

Will always produce the string required, regardless of the language we are using in WordPress. Instead use one of the localization functions built into WordPress.

Using _e() the string to localize now looks like this:

<?php
if ($req) ( _e( 'Required', textdomain ) );

The first parameter is the string to localize, the second is the text domain that we want to use the localization with since we may have different localization domains for themes, plugins, and WordPress itself.

As always, test the localization functions to see which one works best for your project and what results they produce.

Getting the translations ready

You’re not required to use Poedit or to pay for the Pro version. I’ve found out this is the easiest way to do it but your mileage may vary.

WordPress uses GNU Gettext to manage the translations.

I downloaded Poedit and chose to upgrade to the Pro version because it allows you to automatically create the translation files for a WordPress theme or plugin without having to do the work manually.

Opening screen for Poedit application
Opening screen for Poedit application

In this case we’ll select Translate WordPress theme or plugin and move on to the next step.

When we choose to work with a WordPress theme or plugin we’re prompted for the following information

  • If we want to work with a remote server or locally
    • If we want to work locally then enter the location of the theme or plugin we want to work with
  • If we want to create a new translation or a POT template to use as the basis for further transalations

For this post, we’ll create a POT template for our theme.

POT Screen in Poedit
POT Screen in Poedit

When we click on create POT, we see all the translated strings available on the theme. At the bottom, we have a create new translation button.

You will be prompted to save the POT file and when you do, you will be asked what language are you translating for.

Poedit language selection
Poedit Language Selection

We can then enter translations for each of the strings we chose to translate in our code and the translation that we want for it.

The Pro version allows you to pull translation from online sources. But, even without the online sources, you can translate the content yourself or you can handle the POT file to a commercial translator to work with.

Poedit language selection
Poedit Translation Screen

You can use the POT to translate to multiple languages. Place the translations on your theme’s languages directory.

Links and Resources

Categories
Uncategorized

Resource Hints in WordPress

Resource hints are a relationship used to indicates that a given origin or resource should be acted upon before they are loaded.

Doing this in WordPress is more complicated. While we could add the resources directly to the templates it would require changing the templates whenever we change fonts or any detail of the type of resource hint.

The code below is taken from the Twenty Seventeen theme and with some modifications will work on any theme running on recent versions of WordPress.

It requires enqueuing the fonts generated through twentyseventeen_fonts_url(), which is outside the scope of this post but is available on their Subversion repository

The enqueuing script is similar to how we normally enqueue scripts and fonts to use with WordPress but the second parameter calls a theme function rather than a static asset. The modified code looks like this.

<?php
function twentyseventeen_scripts() {
    // Add custom fonts, used in the main stylesheet.
  wp_enqueue_style( 'twentyseventeen-fonts', twentyseventeen_fonts_url(), array(), null );

add_action( 'wp_enqueue_scripts', 'twentyseventeen_scripts' );

?>

We then create resource hints in twentyseventeen_resource_hints(). It takes two parameters:

  • @param array $urls URLs to print for resource hints.
  • @param string $relation_type The relation type the URLs are printed.

And returns an array of URLs of resource hints to print.

<?php
 function twentyseventeen_resource_hints( $urls, $relation_type ) {
  if ( wp_style_is( 'twentyseventeen-fonts', 'queue' ) && 'preconnect' === $relation_type ) {
    $urls[] = array(
      'href' => 'https://fonts.gstatic.com',
      'crossorigin',
    );
  }

    return $urls;
}
add_filter( 'wp_resource_hints', 'twentyseventeen_resource_hints', 10, 2 );
?>

We choose what URL to create by checking if the style has been enqueued and if the type of relation (indicated in the $relation_type variable) is preconnect.

Finally, we add the wp_resource_hint filter with the function we just defined.