WP Manifestindependent plugin directory
manifest / i18n / blocklingo

Blocklingo

Multilingual WordPress, block by block: translate content, templates and menus in the block editor, with translated URLs and hreflang. Free, GPL.

★ 0stars

Install

Download source zip No release zip yet

This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.

Install straight from GitHub with WP-CLI:

wp plugin install https://github.com/renautmestdagh/blocklingo/archive/refs/heads/main.zip
No built-in updater. New versions won't appear under Dashboard → Updates. Install new releases yourself, or let Git Updater track the repository.

Multilingual WordPress, block by block. Write content once in your default language and translate it block by block in the block editor, including templates, headers, footers and menus in the Site Editor. Translated URLs, hreflang, a language switcher block. Free, GPL, no external service.

User documentation: readme.txt (shown on WordPress.org). For theme authors: docs/themes.md, what works automatically and what a theme has to do itself.

Status: early development. Done: languages, URL prefixes, translated slugs, link localisation, hreflang, Language Switcher block, translating blocks in the editor, strings for themes and plugins, WPML hooks, sitemap, Contact Form 7, optional DeepL, switching the default language. Next: WordPress.org release.

How it works

Part Where
Languages and settings (Settings → Blocklingo) includes/class-languages.php, includes/class-settings-page.php
Language of a request (/fr/…), prefix hidden while WordPress parses the URL includes/class-request.php
Translated title, slug, excerpt per post (meta blocklingo, lookup rows _blocklingo_slug) includes/class-posts.php
Permalinks and links in content in the right language includes/class-links.php
Translated slug → post, redirects to the one real address includes/class-router.php
hreflang, URL of the current page in another language includes/class-seo.php
Language Switcher block (source in src/, built to build/) includes/class-switcher-block.php
Translations in blocks (attribute blocklingo: { "fr": { "content": [ original, translation ] } }), shown on the site includes/class-blocks.php
Translating in the editor: language switch, in-place editing, status per block src/editor/, includes/class-editor.php
Texts outside blocks (site title, theme and plugin strings), Settings → Blocklingo → Strings includes/class-strings.php, includes/class-strings-page.php
WPML's hooks for themes and plugins written for WPML includes/class-wpml-compat.php
One sitemap per other language includes/class-sitemap.php, includes/class-sitemap-provider.php
Contact Form 7 (form, messages, auto-reply; language of REST submissions) includes/class-contact-form-7.php
Optional DeepL (REST blocklingo/v1/translate, key only on the server) includes/class-machine-translation.php
Changing the default language (swap originals and translations, resumable), wp blocklingo switch-default includes/class-default-language.php, includes/class-cli.php
Classic menus (Appearance → Menus): label, title attribute and URL per language includes/class-menus.php
Functions for themes and plugins includes/functions.php

How a block is translated

A block keeps its own attributes in the default language, so it saves exactly as without the plugin. Translations are an extra attribute: per language and attribute, the original as it was when translated and the translation. In the editor (src/editor/block-edit.js) a block shows its translations while another language is selected, and typing writes into the translation; adding, moving or styling blocks is shared by all languages. On the site (includes/class-blocks.php) the stored original is swapped for the translation in the block's own markup, as the whole content of an element or as a text attribute (alt, title…), or in its comment attributes for blocks rendered on the server. When the original has changed since, nothing matches and the current original shows, while the editor marks the block as outdated. Translatable attributes are found from block types: attributes with role: "content" holding text, so blocks from other plugins work too. Text in lists (source: "query", like a table's rows) is translated per item, under its path (body.0.cells.1.content). Links (url, href and a few like them) are translated the same way, but kept as a cleaned URL and never counted as untranslated.

Development

Requirements: DDEV (Docker), or PHP 7.4+ with Composer and Node (version in .nvmrc).

ddev start                 # WordPress at https://blocklingo.ddev.site (admin / admin), plugin active, 3 languages
ddev composer install
ddev npm ci
ddev npm run build         # or: ddev npm start (rebuilds on change)
Command What
ddev composer test PHPUnit with the WordPress test suite (database db_tests)
ddev composer lint / ddev composer fix PHP: WordPress Coding Standards, PHP 7.4+ compatibility
ddev npm test JS unit tests (Vitest, through wp-scripts test-unit-js)
ddev npm run lint / ddev npm run format JS and CSS: WordPress ESLint, Stylelint and Prettier configs
ddev npm run plugin-zip Zip of the plugin as it ships

GitHub Actions run on every push and pull request: Lint (PHPCS, PHP syntax 7.4 to 8.5, ESLint, Stylelint, JS unit tests, build), Tests (PHPUnit on every PHP version from 7.4 to 8.5 against WordPress 6.6, 6.9 and the latest), Plugin Check (the checks WordPress.org runs) and, on main, Format (fixes style and commits it).

Releasing

  1. Set the new version in blocklingo.php (header and BLOCKLINGO_VERSION), readme.txt (Stable tag), package.json and src/blocks/language-switcher/block.json; npm run check:version checks they agree. Add the changelog entry to readme.txt.
  2. Merge to main, then publish a GitHub release with tag vX.Y.Z. The Release workflow checks the version against the tag, builds, attaches blocklingo.zip to the release and, once set up, deploys to WordPress.org.

WordPress.org, once: submit the zip from the first release at https://wordpress.org/plugins/developers/add/ (needs a WordPress.org account). After approval, set the repository secrets SVN_USERNAME and SVN_PASSWORD (the SVN password from your WordPress.org profile) and the variable WPORG_DEPLOY to true. From then on releases deploy themselves, and changes to readme.txt or .wordpress-org/ (banner, icon, screenshots) on main are pushed to the directory page.

License

GPL-2.0-or-later.