• Skip to secondary menu
  • Skip to main content
  • Skip to primary sidebar
  • Home
  • Projects
  • Products
  • Themes
  • Tools
  • Request for Quote

Vengala Vinay

Having 12+ Years of Experience in Software Development

  • Home
  • WordPress
  • PHP
    • Codeigniter
  • Django
  • Magento
  • Selenium
  • Server
Home » Step-by-Step Guide to building a custom automatic translation switcher block for Gutenberg using REST API custom routes

Step-by-Step Guide to building a custom automatic translation switcher block for Gutenberg using REST API custom routes

Setting Up the WordPress Environment and Plugin Structure

Before diving into the custom block development, ensure you have a local WordPress development environment set up. This typically involves tools like LocalWP, Docker, or a LAMP/LEMP stack. We’ll be creating a simple WordPress plugin to house our custom Gutenberg block and its associated REST API endpoint.

Create a new directory for your plugin within the wp-content/plugins/ directory of your WordPress installation. For this example, let’s name it custom-translation-switcher.

Inside this directory, create the main plugin file, custom-translation-switcher.php. This file will contain the plugin header and the necessary hooks to register our block and REST API route.

<?php
/**
 * Plugin Name: Custom Translation Switcher
 * Description: A custom Gutenberg block for language switching using the REST API.
 * Version: 1.0.0
 * Author: Your Name
 * License: GPL-2.0+
 * License URI: http://www.gnu.org/licenses/gpl-2.0.txt
 * Text Domain: custom-translation-switcher
 */

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Enqueue block assets.
 */
function cts_enqueue_block_assets() {
	// Enqueue the block editor script.
	wp_enqueue_script(
		'custom-translation-switcher-editor-script',
		plugins_url( 'build/index.js', __FILE__ ),
		array( 'wp-blocks', 'wp-wp-data', 'wp-edit-post' ),
		filemtime( plugin_dir_path( __FILE__ ) . 'build/index.js' )
	);

	// Enqueue the block editor styles.
	wp_enqueue_style(
		'custom-translation-switcher-editor-style',
		plugins_url( 'build/index.css', __FILE__ ),
		array( 'wp-edit-blocks' ),
		filemtime( plugin_dir_path( __FILE__ ) . 'build/index.css' )
	);

	// Enqueue the front-end styles.
	wp_enqueue_style(
		'custom-translation-switcher-frontend-style',
		plugins_url( 'build/style-index.css', __FILE__ ),
		array(),
		filemtime( plugin_dir_path( __FILE__ ) . 'build/style-index.css' )
	);
}
add_action( 'enqueue_block_editor_assets', 'cts_enqueue_block_assets' );

/**
 * Register custom REST API route.
 */
function cts_register_rest_route() {
	register_rest_route( 'custom-translation-switcher/v1', '/languages', array(
		'methods'  => 'GET',
		'callback' => 'cts_get_available_languages',
	) );
}
add_action( 'rest_api_init', 'cts_register_rest_route' );

/**
 * Callback function to get available languages.
 * In a real-world scenario, this would fetch languages from your translation plugin or configuration.
 * For this example, we'll return a static list.
 */
function cts_get_available_languages() {
	$languages = array(
		'en' => array( 'name' => 'English', 'url' => '/' ),
		'es' => array( 'name' => 'Español', 'url' => '/es/' ),
		'fr' => array( 'name' => 'Français', 'url' => '/fr/' ),
	);

	return new WP_REST_Response( $languages, 200 );
}

/**
 * Register the custom block.
 */
function cts_register_block() {
	register_block_type( __DIR__ . '/build' );
}
add_action( 'init', 'cts_register_block' );

Next, we need to set up the build process for our JavaScript and CSS. Gutenberg blocks are typically built using modern JavaScript (ESNext) and React. We’ll use @wordpress/scripts for this, which provides a convenient way to compile our assets.

Create a package.json file in the root of your plugin directory:

{
  "name": "custom-translation-switcher",
  "version": "1.0.0",
  "description": "A custom Gutenberg block for language switching using the REST API.",
  "main": "build/index.js",
  "scripts": {
    "build": "wp-scripts build",
    "start": "wp-scripts start"
  },
  "keywords": [
    "wordpress",
    "gutenberg",
    "block"
  ],
  "author": "Your Name",
  "license": "GPL-2.0-or-later",
  "devDependencies": {
    "@wordpress/scripts": "^26.10.0"
  }
}

Install the dependencies by running the following command in your terminal, from the plugin’s root directory:

npm install

Now, create a src directory within your plugin folder. This is where your block’s JavaScript and CSS files will reside.

Developing the Gutenberg Block (JavaScript/React)

Inside the src directory, create two files: index.js (for the block’s JavaScript logic) and style.scss (for front-end styles). We’ll also create an editor.scss file for styles specific to the block editor.

src/index.js

import { registerBlockType } from '@wordpress/blocks';
import { __ } from '@wordpress/i18n';
import { useSelect } from '@wordpress/data';
import { useState, useEffect } from '@wordpress/element';
import { SelectControl } from '@wordpress/components';
import './editor.scss';
import './style.scss';

registerBlockType( 'custom-translation-switcher/block', {
	title: __( 'Translation Switcher', 'custom-translation-switcher' ),
	icon: 'translation',
	category: 'widgets',
	edit: ( { attributes, setAttributes } ) => {
		const { selectedLanguage } = attributes;

		// Fetch languages from the REST API
		const { languages, isLoading } = useSelect( ( select ) => {
			const apiFetch = select( 'core' ).apiFetch;
			return {
				languages: apiFetch( { path: '/custom-translation-switcher/v1/languages' } ),
				isLoading: true, // A simple way to indicate loading, could be more sophisticated
			};
		}, [] );

		const availableLanguages = ( languages || [] ).map( ( lang, key ) => ( {
			label: lang.name,
			value: key, // Using key as value for now, ideally would be language code
		} ) );

		const handleLanguageChange = ( newLanguageValue ) => {
			setAttributes( { selectedLanguage: newLanguageValue } );
			// In a real scenario, you'd trigger a page redirect or update state
			// For this example, we'll just log it.
			console.log( 'Selected Language:', newLanguageValue );
		};

		return (
			<div className="custom-translation-switcher-block">
				<h3>{ __( 'Language Switcher', 'custom-translation-switcher' ) }</h3>
				{ isLoading && <p>{ __( 'Loading languages...', 'custom-translation-switcher' ) }</p> }
				{ ! isLoading && availableLanguages.length > 0 && (
					<SelectControl
						label={ __( 'Select Language', 'custom-translation-switcher' ) }
						value={ selectedLanguage }
						options={ availableLanguages }
						onChange={ handleLanguageChange }
					/>
				) }
				{ ! isLoading && availableLanguages.length === 0 && (
					<p>{ __( 'No languages found.', 'custom-translation-switcher' ) }</p>
				) }
			</div>
		);
	},
	save: ( { attributes } ) => {
		// The save function determines how the block is rendered on the front-end.
		// For a dynamic block, this might be empty or render a placeholder.
		// For simplicity, we'll render a placeholder.
		return (
			<div className="custom-translation-switcher-block">
				<p>{ __( 'Language Switcher (Front-end)', 'custom-translation-switcher' ) }</p>
				{/* In a real scenario, you'd render the actual switcher here */}
			</div>
		);
	},
} );

Explanation:

  • registerBlockType: Registers our new Gutenberg block with WordPress.
  • title, icon, category: Basic block metadata.
  • edit: This function defines the block’s appearance and behavior within the Gutenberg editor.
    • useSelect: A hook from @wordpress/data to access WordPress data. We use it here to call our custom REST API endpoint using apiFetch.
    • useState and useEffect: Standard React hooks for managing component state and side effects.
    • SelectControl: A component from @wordpress/components to render a dropdown select element.
    • attributes and setAttributes: These are passed to the edit function and allow us to manage the block’s data.
  • save: This function defines how the block’s content is saved to the database and rendered on the front-end. For dynamic blocks that fetch data, this might render a placeholder or be handled by PHP.

src/editor.scss

.custom-translation-switcher-block {
	border: 1px dashed #ccc;
	padding: 15px;
	background-color: #f9f9f9;
	text-align: center;

	h3 {
		margin-top: 0;
		margin-bottom: 10px;
		font-size: 1.1em;
	}

	.components-base-control__field {
		margin-bottom: 0;
	}
}

src/style.scss

.custom-translation-switcher-block {
	padding: 10px;
	background-color: #e0e0e0;
	text-align: center;

	p {
		margin-bottom: 0;
	}
}

Building the Block Assets

With the JavaScript and SCSS files in place, navigate to your plugin’s root directory in the terminal and run the build command:

npm run build

This command will compile your src/index.js and src/style.scss into build/index.js and build/style-index.css respectively. It will also create build/index.css for the editor styles.

If you want to watch for changes and automatically rebuild during development, use:

npm run start

Integrating with WordPress and Testing

Activate your “Custom Translation Switcher” plugin from the WordPress admin area. Then, navigate to the post or page editor and add your new “Translation Switcher” block. You should see the block appear, and if the REST API call is successful, the select dropdown should populate with the languages defined in your PHP file.

Troubleshooting REST API Calls:

  • Check Permalinks: Ensure your WordPress permalink structure is not set to “Plain”. Go to Settings > Permalinks and select any option other than “Plain”.
  • REST API Enabled: Verify that the REST API is enabled in your WordPress installation. It’s enabled by default, but some security plugins might disable it.
  • Inspect Network Requests: Use your browser’s developer tools (usually F12) to inspect the Network tab. Look for a request to /wp-json/custom-translation-switcher/v1/languages. Check the response for errors or the expected JSON data.
  • Plugin Conflicts: Temporarily deactivate other plugins to rule out conflicts.
  • Server Logs: Check your web server’s error logs (Apache or Nginx) for any PHP errors related to the REST API callback.

Front-end Rendering:

As noted in the save function, this example uses a placeholder for the front-end. For a fully functional translation switcher, you would typically:

  • Implement logic in the save function to render the actual switcher UI (e.g., a list of links).
  • Alternatively, make the block “dynamic” by removing the save function and handling the front-end rendering entirely in PHP, similar to how shortcodes are rendered. This would involve creating a server-side rendering function hooked into render_block.
  • Integrate with your specific translation plugin (e.g., WPML, Polylang) to dynamically fetch available languages and generate correct URLs.

This guide provides a foundational structure for building a custom Gutenberg block that interacts with a custom REST API route. From here, you can expand upon this by adding more complex attributes, dynamic rendering, and deeper integration with translation plugins.

Primary Sidebar

A little about the Author

Having 12+ Years of Experience in Software Development, Vinay is a principal software architect, senior systems engineer, and elite technical consultant. He specializes in bespoke PHP/WordPress development, high-performance Magento 2 & Shopify architectures, custom plugin/theme development from scratch, and legacy code modernization (including VB6, VB.NET, PyQt, and Crystal Reports). Known for solving complex database bottlenecks, speed optimization (Core Web Vitals), and advanced security code auditing, Vinay engineers production-ready systems designed to scale under heavy concurrent load conditions.



Chat on WhatsApp

Recent Posts

  • How to build custom Timber Twig templating engines extensions utilizing modern Cron API (wp_schedule_event) schemas
  • How to securely integrate Shopify headless API endpoints into WordPress custom plugins using Rewrite API custom endpoints
  • Implementing automated compliance reporting for custom custom subscription logs ledgers using dompdf library
  • How to build custom Sage Roots modern environments extensions utilizing modern Filesystem API schemas
  • Step-by-Step Guide to building a custom Elasticsearch search bar block for Gutenberg using Svelte standalone templates

Categories

  • apache (1)
  • Business & Monetization (390)
  • Centos (4)
  • Comparisons & Decision Making (55)
  • Debian (2)
  • Debugging & Troubleshooting (609)
  • Desktop Applications (14)
  • DevOps (7)
  • DevOps & Cloud Scaling (962)
  • Django (1)
  • Laravel (4)
  • Migration & Architecture (192)
  • Mobile Applications (24)
  • MySQL (1)
  • Performance & Optimization (824)
  • PHP (5)
  • PHP Development (30)
  • Plugins & Themes (244)
  • Programming Languages (9)
  • Python (20)
  • Ruby on Rails (1)
  • Security & Compliance (588)
  • SEO & Growth (492)
  • Server (23)
  • Ubuntu (9)
  • VB6 & VB.NET (8)
  • Web Applications & Frontend (19)
  • Web Assembly (Wasm) (2)
  • WordPress (22)
  • WordPress Plugin Development (140)
  • WordPress Theme Development (357)

Recent Posts

  • How to build custom Timber Twig templating engines extensions utilizing modern Cron API (wp_schedule_event) schemas
  • How to securely integrate Shopify headless API endpoints into WordPress custom plugins using Rewrite API custom endpoints
  • Implementing automated compliance reporting for custom custom subscription logs ledgers using dompdf library

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (824)
  • Debugging & Troubleshooting (609)
  • Security & Compliance (588)
  • SEO & Growth (492)
  • Business & Monetization (390)

Our Products

  • ERP & LMS Systems (4)
  • Directories & Marketplaces (4)
  • Healthcare Portals (3)
  • Point of Sale (POS) (2)
  • E-Commerce Engines (2)

Our Services

  • E-Commerce Development (10)
  • WordPress Development (8)
  • Python & Desktop GUI (7)
  • General Consulting (7)
  • Legacy Modernization (5)
  • Mobile App Development (4)

Copyright © 2026 · Vinay Vengala