Architecting Scalable Full Site Editing (FSE) Block Themes and theme.json in Multi-Language Site Networks
Leveraging `theme.json` for Global Styles in Multi-Language FSE Themes
Architecting Full Site Editing (FSE) block themes for multi-language WordPress installations presents unique challenges, particularly concerning global styles and localized assets. The `theme.json` file is the cornerstone of FSE’s styling system, enabling centralized control over design tokens, typography, color palettes, and layout settings. When deploying a multi-language site network, ensuring these global styles are correctly applied and potentially localized requires a robust strategy that goes beyond simple string translation.
The primary mechanism for managing styles in FSE is the `theme.json` file. This JSON document defines a theme’s design system, acting as a single source of truth for all block styles. For multi-language support, we need to consider how `theme.json` interacts with WordPress’s internationalization (i18n) APIs and how to manage language-specific style variations.
Internationalizing `theme.json` Values
Directly translating values within `theme.json` is not natively supported by WordPress’s i18n functions. However, certain string-based properties, such as font family names or specific CSS class names used in custom styles, can be made translatable. This is achieved by extracting these strings and passing them through `__()` or `_x()` within PHP functions that enqueue or process the theme’s assets.
Consider a scenario where font family names need to be localized. While `theme.json` itself doesn’t have a direct translation mechanism for its values, we can use PHP to dynamically generate or modify the `theme.json` content based on the current language context. A more practical approach is to use `theme.json` for structural and numerical values and handle translatable strings in other theme files (e.g., block styles, custom CSS). However, for properties like `fontFamily.name` or `label` within `theme.json`’s settings, we can leverage PHP filters.
Let’s illustrate how to filter `theme.json` settings to potentially inject localized strings. This is more about demonstrating the hook’s existence and potential than a direct translation of JSON values, as `theme.json` is primarily for design tokens.
Example: Filtering `theme.json` Font Family Labels
While `theme.json` values are generally not directly translatable via `__()`, we can use filters to modify the *output* or *interpretation* of these values, particularly for UI elements that might display these names. A more common pattern is to use `theme.json` for the actual CSS variables and then use localized strings in the WordPress Customizer or Site Editor UI elements that *reference* these styles.
Here’s a conceptual PHP snippet that demonstrates hooking into the `theme.json` data before it’s processed. This is more illustrative of how one *might* approach it if direct translation of specific `theme.json` keys were required, though it’s often better to manage translatable strings outside `theme.json` itself.
/**
* Filter theme.json data to potentially inject localized strings.
* Note: Direct translation of theme.json values is not a standard feature.
* This example shows how to hook into the data for potential modification,
* but managing translatable strings in UI elements referencing these styles
* is a more common and robust approach.
*/
function my_theme_filter_theme_json_data( $theme_json_data ) {
// Example: If we had a specific font family defined with a translatable label
// This is a hypothetical scenario as theme.json doesn't directly support __()
// A more practical approach would be to use localized strings in the Site Editor UI
// that *refer* to these font families.
// Let's assume a structure like this in theme.json:
// "settings": {
// "typography": {
// "fontFamilies": [
// {
// "name": "My Sans Serif",
// "slug": "my-sans-serif",
// "label": "My Sans Serif Font" // This label could be localized
// }
// ]
// }
// }
// To localize such a label, you'd typically do it in the PHP that *renders*
// the UI element displaying this label, not directly within theme.json processing.
// However, if you absolutely needed to modify the JSON data itself based on context:
$current_lang = get_current_language(); // Assuming WPML or similar is active
if ( $current_lang === 'fr' ) {
// Hypothetical modification:
// This is complex and generally not recommended for theme.json values.
// It's better to manage UI strings separately.
// If theme.json had a structure that allowed for language-specific overrides,
// this is where you'd implement it.
}
// A more realistic use case: modifying CSS variables based on language
// For example, if a language requires a different default spacing.
// This would involve more complex logic to parse and modify the 'styles' section.
return $theme_json_data;
}
add_filter( 'theme_json_data_file', 'my_theme_filter_theme_json_data' );
The above snippet is largely illustrative. The primary challenge is that `theme.json` is parsed as static JSON. For dynamic, language-dependent styling, consider these strategies:
- Separate `theme.json` files per language: This is cumbersome and difficult to maintain.
- Dynamic generation of `theme.json` via PHP: Hook into `theme_json_data_file` or `theme_json_data_theme` filters to return modified JSON based on `get_locale()` or `get_current_language()`. This allows for conditional styling.
- CSS Custom Properties (Variables): Define language-agnostic variables in `theme.json` and then use PHP to output language-specific CSS rules that override these variables.
- Block-level overrides: For specific blocks, use PHP to enqueue language-specific stylesheets or inline styles.
Managing Assets in Multi-Language FSE Themes
When building FSE themes for multi-language sites, asset management (CSS, JavaScript, fonts) becomes critical. Each language might require different font files, specific CSS adjustments, or localized JavaScript strings.
Enqueueing Scripts and Styles Conditionally
The standard WordPress way to enqueue assets is via `wp_enqueue_script` and `wp_enqueue_style`. For multi-language support, these functions need to be called conditionally based on the current language context.
The `get_locale()` function returns the locale string for the current request (e.g., ‘en_US’, ‘fr_FR’). If using a plugin like WPML, `get_current_language()` might be more appropriate for finer-grained control.
/**
* Enqueue language-specific assets for FSE themes.
*/
function my_theme_enqueue_language_assets() {
$locale = get_locale(); // Or get_current_language() if using WPML
// Enqueue a language-specific stylesheet
if ( 'fr_FR' === $locale ) {
wp_enqueue_style(
'my-theme-fr-styles',
get_template_directory_uri() . '/assets/css/fr-styles.css',
array( 'my-theme-styles' ), // Dependency on main theme styles
filemtime( get_template_directory() . '/assets/css/fr-styles.css' )
);
} elseif ( 'es_ES' === $locale ) {
wp_enqueue_style(
'my-theme-es-styles',
get_template_directory_uri() . '/assets/css/es-styles.css',
array( 'my-theme-styles' ),
filemtime( get_template_directory() . '/assets/css/es-styles.css' )
);
}
// Enqueue language-specific JavaScript
if ( 'fr_FR' === $locale ) {
wp_enqueue_script(
'my-theme-fr-scripts',
get_template_directory_uri() . '/assets/js/fr-scripts.js',
array( 'wp-blocks', 'wp-element', 'wp-editor' ), // Dependencies
filemtime( get_template_directory() . '/assets/js/fr-scripts.js' ),
true // Load in footer
);
// Localize strings for JavaScript
wp_localize_script(
'my-theme-fr-scripts',
'myThemeFrData',
array(
'greeting' => __( 'Bonjour le monde!', 'my-theme-textdomain' ),
)
);
}
}
add_action( 'wp_enqueue_scripts', 'my_theme_enqueue_language_assets' );
add_action( 'admin_enqueue_scripts', 'my_theme_enqueue_language_assets' ); // For admin context if needed
This approach ensures that only the necessary language-specific assets are loaded for the current user, optimizing performance and providing accurate styling and functionality.
Advanced Diagnostics for FSE Multi-Language Issues
When debugging FSE themes in a multi-language environment, several areas commonly cause issues. Here’s a systematic approach to diagnosing them.
1. `theme.json` Parsing and Application
Symptom: Global styles (colors, typography, spacing) are not applied correctly, or they appear inconsistent across different language versions of the site.
Diagnostic Steps:
- Verify `theme.json` Syntax: Use an online JSON validator or a code editor’s built-in linter to ensure `theme.json` is valid. Even a single syntax error can prevent it from being parsed.
- Check for PHP Errors: Temporarily enable `WP_DEBUG` and `WP_DEBUG_LOG` in `wp-config.php`. Look for errors related to `theme.json` parsing or filters applied to it.
- Inspect Loaded `theme.json` Data: Use the `theme_json_data_theme` filter to dump the processed `theme.json` data. This helps see what WordPress is actually working with.
/**
* Debugging: Dump the processed theme.json data.
*/
function debug_theme_json_data( $theme_json ) {
// Log the data to the debug log file.
error_log( print_r( $theme_json->get_config(), true ) );
return $theme_json;
}
add_filter( 'theme_json_data_theme', 'debug_theme_json_data', 100, 1 );
Analysis: If the dumped data doesn’t match your `theme.json` file, or if it’s missing expected sections, investigate any custom filters or plugins that might be modifying it. If language-specific logic is intended, ensure `get_locale()` or `get_current_language()` is returning the expected value in the context where `theme.json` is processed.
2. Asset Enqueueing Conflicts
Symptom: Stylesheets or JavaScript files are not loading, loading in the wrong order, or are being duplicated. Language-specific styles are missing.
Diagnostic Steps:
- Browser Developer Tools: Use the Network tab to check if all expected CSS and JS files are being requested and loaded successfully (HTTP status 200). Check the Console tab for JavaScript errors.
- Inspect Enqueued Scripts/Styles: Use `wp_print_scripts()` and `wp_print_styles()` in a template or hook to see exactly which scripts and styles are registered and enqueued.
- Check Dependencies: Ensure that dependencies (e.g., `wp-blocks`, `wp-element`) are correctly specified and that your custom scripts/styles are enqueued *after* their dependencies.
- Verify `get_locale()`/`get_current_language()`: Add `error_log( ‘Current Locale: ‘ . get_locale() );` within your enqueueing function to confirm the correct locale is detected.
- `filemtime()` Issues: Ensure the paths used in `get_template_directory()` are correct. Incorrect paths can lead to incorrect cache-busting versions or files not being found.
/**
* Debugging: Output all enqueued scripts and styles.
*/
function debug_enqueued_assets() {
echo '<h3>Enqueued Scripts:</h3>';
global $wp_scripts;
echo '<pre>';
print_r( $wp_scripts->registered );
echo '</pre>';
echo '<h3>Enqueued Styles:</h3>';
global $wp_styles;
echo '<pre>';
print_r( $wp_styles->registered );
echo '</pre>';
}
// Add this hook to a relevant action, e.g., 'wp_head' or 'admin_head'
// add_action( 'wp_head', 'debug_enqueued_assets' );
Analysis: If assets are missing, verify the conditional logic (`if ( ‘fr_FR’ === $locale )`). If they are duplicated, check if the enqueueing function is being called multiple times or if different plugins/themes are enqueuing the same asset. Ensure `add_action` priorities are set appropriately if conflicts arise.
3. Block Rendering and Content Localization
Symptom: Block content (text, images, links) is not translated, or blocks render incorrectly on specific language sites.
Diagnostic Steps:
- Use `get_template_part()` or `get_template_directory()` correctly: Ensure that any custom block templates or partials are loaded using appropriate WordPress functions.
- Check Block Registration: If custom blocks are used, verify their registration and ensure any translatable strings within the block’s JavaScript (e.g., `edit` function) are passed via `wp_localize_script`.
- Inspect HTML Output: Use browser developer tools to inspect the HTML output of blocks. Look for missing attributes, incorrect classes, or unescaped content that might indicate translation issues.
- Multi-Language Plugin Integration: If using WPML, Polylang, or similar, ensure that theme templates and block templates are compatible. Check the plugin’s documentation for specific FSE integration guidelines.
- `get_post_meta()` and `get_option()`: Verify that these functions are retrieving the correct language-specific data when blocks are rendered.
Analysis: This often points to issues with how content is saved and retrieved for different languages. Ensure that your theme’s custom fields or block attributes are correctly associated with the correct language versions of posts and pages. For custom blocks, ensure the `save` function correctly outputs HTML that is language-agnostic or uses appropriate translation mechanisms.
Conclusion
Architecting scalable FSE block themes for multi-language WordPress sites requires a deep understanding of `theme.json`, WordPress’s asset management system, and internationalization best practices. By systematically diagnosing issues related to `theme.json` parsing, asset enqueueing, and block rendering, developers can build robust, performant, and globally-ready themes.