Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace inline JSON Babel plugin with a custom version which supports i18n #15169

Closed
gziolo opened this issue Apr 25, 2019 · 4 comments
Closed
Assignees
Labels
[Feature] Block API API that allows to express the block paradigm. [Feature] Block Directory Related to the Block Directory, a repository of block plugins Internationalization (i18n) Issues or PRs related to internationalization efforts [Status] In Progress Tracking issues with work in progress [Type] Build Tooling Issues or PRs related to build tooling [Type] Task Issues or PRs that have been broken down into an individual action to take

Comments

@gziolo
Copy link
Member

gziolo commented Apr 25, 2019

This issue is part of the requirements for Block Registration API RFC (#13693).

One of the aspects that needs to be covered is internationalization. This is what RFC proposes as of today for JavaScript:

Localized properties are automatically wrapped in _x function calls on the back end and the front end of WordPress. These translations are added as an inline script to the wp-block-library script handle in WordPress core or to the plugin's script handle when it defines metadata definition.

WordPress string discovery automatically translates these strings using the textdomain value specified in the plugin header.

Example:

{
	"title": "My block",
	"description": "My block is fantastic",
	"keywords": [ "fanstastic" ],
	"textdomain": "my-plugin"
}

In JavaScript, with the help of a Babel plugin, this becomes:

const metadata = {
	title: _x( 'My block', 'block title', 'my-plugin' ),
	description: _x( 'My block is fantastic', 'block description', 'my-plugin' ),
	keywords: [ _x( 'fanstastic', 'block keywords', 'my-plugin' ) ],
}

To get us there, we will need a custom Babel plugin which will inline imported JSON file and wrap translatable properties with the _x function as shown in the example above. Also, this plugin will have to ensure that the function mentioned above is available (imported) in the scope. To make it simpler for start, let's assume that textdomain is provided as another property in block.json file.

As of today, we use babel-plugin-inline-json-import which was integrated as an intermediate solution in #14551:

plugins: [ 'babel-plugin-inline-json-import' ],

It isn't clear whether we can build new functionality on top of it or rather we have to duplicate its logic and apply our own modifications in the cloned plugin. It needs to be investigated first.

@gziolo gziolo added [Type] Task Issues or PRs that have been broken down into an individual action to take [Feature] Block API API that allows to express the block paradigm. [Type] Build Tooling Issues or PRs related to build tooling [Feature] Block Directory Related to the Block Directory, a repository of block plugins labels Apr 25, 2019
@gziolo gziolo added the Internationalization (i18n) Issues or PRs related to internationalization efforts label Apr 25, 2019
@michaelzangl
Copy link

michaelzangl commented Apr 27, 2019

My thoughts about this:
I don't think transpiling the .json files in this way is the ideal solution to the problem.

I see two alternative solutions:

Use PHP files instead of .json
Do not use .json files. You could use a .php file with a restricted syntax. Name: my-block.block.php, content:

<?php
// This file may only contain comments and 
return [
  // translators: Keep it short
  "title" => _x( 'My block', 'block title', 'my-plugin' ),
  "description" => __( 'Block description', 'my-plugin' ),
  // ...
];

You can then register a webpack loader for .block.php-Files. Use php-parser to parse the AST, validate it for the restricted syntax and return the corresponding JavaScript AST. Example on how to use

Advantages:

  • Good, existing tooling support for translations.
  • Support translations with and without context
  • Allow translations in attributes / other values. Example
  • Supports translators comments

Disadvantage:

  • Allowed PHP syntax needs to be restricted to allow for the file to be parsed. No variables, no constants.

Transform file after import
To import a block.js file, use:

import block from './block.json'
// Replaces i18n
let translated = translateBlock(block);

@gziolo
Copy link
Member Author

gziolo commented Apr 29, 2019

Use PHP files instead of .json

@michaelzangl, that's an interesting approach to use PHP and convert it to JS. Is there a Babel plugin which would work as well? This solution needs to work with the code which Gutenberg ships to npm and it doesn't use webpack for publishing workflow.

@tellyworth how do you feel about using JSON vs PHP file in the context of Block Discovery project? It seems like JSON file is easier to process but I might be wrong.

@michaelzangl
Copy link

@gziolo
That is no problem, it can be done for babel as well. But the plugin needs to be written, there is no plugin that does exactly what you do.
The algorithm is pretty simple:

  • Parse the input file using php-parser (result: AST for PHP)
  • Validate that the file only contains one return statement and find it
  • Let the value of the return statement be returnValue
  • Then call convertRecursive(returnValue)

Procedure convertRecursive(phpAstNode):

  • If phpAstNode is a number: Return a JS Number AST node
  • If phpAstNode is a string: Return a JS String AST node
  • If phpAstNode is a associative array: Return a JS Object AST node wit a sub node for each array entry. Use convertRecursive to convert the values.
  • If phpAstNode is a normal array: Return a JS Array AST node. Use convertRecursive to convert the values.
  • If phpAstNode is a function: Validate that the function name is __ and that it has 2 arguments and first/second argument is of type String constant or that . Then return the AST nodes for the expression: wp.i18n.<function name>(convertRecursive(arg1), ...)

All in all, it should be ~200 lines, most of which are for validation.

@gziolo gziolo self-assigned this Oct 20, 2019
@gziolo gziolo added the [Status] In Progress Tracking issues with work in progress label Oct 20, 2019
@gziolo
Copy link
Member Author

gziolo commented Jan 3, 2020

We decided that this is going to be handled by WP-CLI. See wp-cli/i18n-command#163.

@gziolo gziolo closed this as completed Jan 3, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] Block API API that allows to express the block paradigm. [Feature] Block Directory Related to the Block Directory, a repository of block plugins Internationalization (i18n) Issues or PRs related to internationalization efforts [Status] In Progress Tracking issues with work in progress [Type] Build Tooling Issues or PRs related to build tooling [Type] Task Issues or PRs that have been broken down into an individual action to take
Projects
None yet
Development

No branches or pull requests

2 participants