bobbingwide / sb-post-edit-block

Post edit block to allow direct editing of the post
GNU General Public License v3.0
0 stars 0 forks source link

Internationalize and localize the Post Edit block ( `oik-sb/sb-post-edit-block` ) #5

Open bobbingwide opened 3 years ago

bobbingwide commented 3 years ago

The Post Edit block is registered from block.json but the internationalization and localization doesn't work. This is a simpler version of the problem reported in https://github.com/bobbingwide/oik/issues/177#issuecomment-898885822

Requirement

Proposed solution

  1. Build the localized version using

    npm run makepot
    npm run l10n
    npm run makejson
  2. Determine whether or not we need to call wp_set_script_translations. Find out what value for the $handle parameter should be.

    /*
    * Localise the script by loading the required strings for the build/index.js file
    * from the locale specific .json file in the languages folder
    */
    $ok = wp_set_script_translations( 'sb-chart-block-block-editor', 'sb-chart-block' , $dir .'/languages' );
bobbingwide commented 3 years ago

The post edit block is currently produced as

<script id='oik-sb-sb-post-edit-block-editor-script-js-translations'>
            (function(domain, translations) {
                var localeData = translations.locale_data[domain] || translations.locale_data.messages;
                localeData[""].domain = domain;
                wp.i18n.setLocaleData(localeData, domain);
            }
            )("sb-post-edit-block", {
                "locale_data": {
                    "messages": {
                        "": {}
                    }
                }
            });
        </script>
        <script src='https://s.b/cwiccer/wp-content/plugins/sb-post-edit-block/./build/index.js?ver=adcb066b64af553fb3daa0134a33ec2a' id='oik-sb-sb-post-edit-block-editor-script-js'></script>    

There should be entries in the locale_data.messages object

bobbingwide commented 3 years ago

In the load_script_textdomain() function in l10n.php the value of the $relative variable is set to ./build/index.js.

The $md5_filename for this

sb-post-edit-block-en_GB-1b3034803df8a21550db0d04471ea571.json which is different from the file we've got. sb-post-edit-block-bb_BB-dfbff627e6c248bcb3b61d7d06da9ca9.json

I then checked what the md5 value should be for build/index.js and found that I'd previously debugged this on 28th July 2020 with a routine called md5.php

<?php 

//echo md5( 'sb-breadcrumbs-block-bb_BB' );
md5is( 'build/index.js' );
md5is( './build/index.js' );
md5is( 'src/index.js' );
md5is( 'src/sb-breadcrumbs-block/edit.js' );

function md5is( $file ) {
    echo "md5 for $file is ";
    echo md5( $file );
    echo PHP_EOL;
}   
C:\apache\htdocs\wordpress\wp-content\plugins\play>php md5.php
md5 for build/index.js is dfbff627e6c248bcb3b61d7d06da9ca9
md5 for ./build/index.js is 1b3034803df8a21550db0d04471ea571
md5 for src/index.js is 1fdf421c05c1140f6d71444ea2b27638
md5 for src/sb-breadcrumbs-block/edit.js is 30338b81b9d834a0a3ccc281c0c5c485

Then I re-read the issue https://github.com/bobbingwide/sb-breadcrumbs-block/issues/1 It's a very similar problem. I no longer understand what my Previous thoughts were.

Now I reckon that I can implement a filter function for

$relative = apply_filters( 'load_script_textdomain_relative_path', $relative, $src );

With the filter function changing relative to build/index.js if it's ./build/index.js or src/block-name/../../build/index.js

bobbingwide commented 3 years ago

For the sb-post-edit-block plugin it wasn't necessary to implement the filter hook. I just changed filenames in block.json, removing the unnecessary .\ prefix.

But the code doesn't cater for the translatable fields in block.json. I've updated wp-cli to v2.5.0 but that doesn't appear to process the file.

I'm going to see what happens with the latest version of npx @wordpress/create-block. The new plugin will be called local-eyes.

C:\apache\htdocs\wordpress\wp-content\plugins>npx @wordpress/create-block
npx: installed 213 in 24.304s

Let's customize your block:
? The block slug used for identification (also the plugin and output folder name): local-eyes
? The internal namespace for the block name (something unique for your products): local-eyes
? The display title for your block: Localise
? The short description for your block (optional): Example block to test i18n/l10n
? The dashicon to make it easier to identify your block (optional): translation
? The category name to help users browse and discover your block: widgets
? The name of the plugin author (optional). Multiple authors may be listed using commas: bobbingwide
? The short name of the plugin’s license (optional): GPL-2.0-or-later
? A link to the full text of the license (optional): https://www.gnu.org/licenses/gpl-2.0.html
? The current version number of the plugin: 0.0.0

Creating a new WordPress block in "local-eyes" folder.

Creating a "block.json" file.

Creating a "package.json" file.

Installing npm dependencies. It might take a couple of minutes...

The plugin created by @wordpress/create-block is not internationalised.

bobbingwide commented 3 years ago

It seems that the i18n package on wp-cli v2.5.0 is not the latest. Trying to install it using wp package install git@github.com:wp-cli/i18n-command.git

Installing package wp-cli/i18n-command (dev-master)
Updating C:\Users\herb\.wp-cli\packages\composer.json to require the package...
Registering git@github.com:wp-cli/i18n-command.git as a VCS repository...
Using Composer to install the package...
---
Loading composer repositories with package information
Updating dependencies
Generating rules
Resolving dependencies through SAT
Looking at all rules.
Something's changed, looking at all rules again (pass #1)

Dependency resolution completed in 0.001 seconds
Analyzed 84 packages to resolve dependencies
Analyzed 99 rules to resolve dependencies
Lock file operations: 0 installs, 4 updates, 0 removals
Updates: gettext/languages:2.8.1, mck89/peast:v1.13.5, gettext/gettext:v4.8.5, wp-cli/i18n-command:dev-master 134784d
- Upgrading gettext/gettext (v4.6.2 => v4.8.5)
- Upgrading gettext/languages (2.5.0 => 2.8.1)
- Upgrading mck89/peast (v1.9.1 => v1.13.5)
- Upgrading wp-cli/i18n-command (dev-master 163ffd2 => dev-master 134784d)
Writing lock file
Installing dependencies from lock file
Package operations: 0 installs, 4 updates, 0 removals
Updates: gettext/languages:2.8.1, mck89/peast:v1.13.5, gettext/gettext:v4.8.5, wp-cli/i18n-command:dev-master 134784d
Generating autoload files
2 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
bobbingwide commented 3 years ago

Having installed the latest version of the wp-cli i18n command I was able to get the strings from block.json into the .pot file. In order to get the strings translated I needed to add code calling load_plugin_textdomain() before registering the block.

For plugins where the block.json files are in the block's subfolder within src the definition of the makepot command will need to be changed. From

"makepot": "wp i18n make-pot . languages/sb-post-edit-block.pot --exclude=node_modules,vendor,src",

To

"makepot": "wp i18n make-pot . languages/sb-post-edit-block.pot --exclude=node_modules,vendor,src/*.js",

This will enable the routine to find the block.json files but ignore the source Javascript files. The translatable strings are extracted from the build/index.js file. Note: It's this file name that's used to create the md5 suffix. ( dfbff627e6c248bcb3b61d7d06da9ca9 ). There should only be one file per locale.

bobbingwide commented 3 years ago

Summary

  1. To support internationalization of block.json files you need to install the latest version of the wp-cli i18n command which provides internationalization tools for WordPress projects. I used the latest version at the time, v2.2.9

  2. You need to call load_text_domain() before registering the blocks.

  3. You need to call wp_set_script_translations() to load the JavaScript translations

  4. If your block.json file's editorScript setting is a relative path such as below

    "editorScript": "file:../../build/index.js",

    then you need to implement a filter hook for load_script_textdomain_relative_path to change the value to that used by the makejson command.

Each time you change a translatable string in the JavaScript source you need to rebuild the language files

npm run dev  or npm run build
npm run makepot
npm run l10n
npm run makejson

If you just change a string in a PHP file or block.json then you don't need to build the JavaScript.

The npm commands are defined in package.json as:

"makepot": "wp i18n make-pot . languages/sb-post-edit-block.pot --exclude=node_modules,vendor,src/*.js",
"makejson": "wp i18n make-json languages --no-purge",
"l10n": "l10n sb-post-edit-block"

See https://github.com/bobbingwide/oik-i18n/issues/6 for an explanation of these commands. I should really create a diagram.

bobbingwide commented 3 years ago

Regarding "default" values for "attributes" in block.json

I don't yet know how to ensure that these get translated.

bobbingwide commented 3 years ago

When building the .pot file for oik I found that the block.json files were ignored if they contained the textdomain attribute, even if this was set to "oik" and it matched the --domain=oik parameter needed on the makepot command to support the PHP shared library files.

The workaround was to

  1. Set the makepot command to
    "makepot": "wp i18n make-pot . languages/oik.pot --ignore-domain --domain=oik --exclude=node_modules,vendor,src/*.js,tests",
  2. Remove the "textdomain": "oik" attribute from each block.json file.
bobbingwide commented 3 years ago

This didn't resolve the problem of the strings from block.json not being translated! The textdomain attribute is still needed in the block.json file.

Reinstating this attribute in the block.json file after it's been parsed by makepot could be used as a workaround.

Or can I remove --ignore-domain?

bobbingwide commented 3 years ago

Or can I remove --ignore-domain?

No. --ignore-domain is required for the PHP shared library files which pass a domain of null.

So why doesn't --domain=oik match the textdomain attribute?

bobbingwide commented 3 years ago

It looks like there's a runtime workaround using the block_type_metadata filter called in register_block_type_from_metadata

$metadata = apply_filters( 'block_type_metadata', $metadata );

If the $metadata['textdomain'] is not set, then set it to the prefix part of the block's name eg for oik/address set the textdomain to oik.

bobbingwide commented 3 years ago

Regarding "default" values for "attributes" in block.json I don't yet know how to ensure that these get translated.

The link text attribute called label is currently defined in the block.json file with a default value of (Edit). This doesn't get translated. So the default value if we enable example using block.json is the untranslated string (Edit). Let's just confirm this!

The official documentation for Metadata indicates that attributes and example are not automatically translated ( Localized: No ). So we have to find another way to get the default values to be translated.

bobbingwide commented 3 years ago

So we have to find another way to get the default values to be translated.

The solution is to not use default values for attributes and set the default value when the attribute is not set or completely empty.

bobbingwide commented 3 years ago

So we have to find another way to get the default values to be translated.

Try translating in a filter run for register_block_type_from_metadata().

Questions: