Skip to content

Conversation

@kbrown9
Copy link
Member

@kbrown9 kbrown9 commented Feb 12, 2020

This is a POC to explore a possible solution for using textdomains in the packages. All of the implementation details are up for debate, but I think the general idea works.

The Consuming Plugin

The consuming plugin must set two items in its composer.json file:

  • the post-autoload-dump script in the scripts section.

  • the textdomain in the extra section.

For example, the scripts and extra sections of Jetpack’s composer.json file would look like:

"scripts": {
		"php:compatibility": "vendor/bin/phpcs -p -s --runtime-set testVersion '5.6-' --standard=PHPCompatibilityWP --ignore=docker,tools,tests,node_modules,vendor --extensions=php",
		"php:lint": "vendor/bin/phpcs -p -s",
		"php:changed": "vendor/sirbrillig/phpcs-changed/bin/phpcs-changed --git",
		"php:autofix": "vendor/bin/phpcbf",
		"php:lint:errors": "vendor/bin/phpcs -p -s --runtime-set ignore_warnings_on_exit 1"
		"php:lint:errors": "vendor/bin/phpcs -p -s --runtime-set ignore_warnings_on_exit 1",
		"post-autoload-dump": "Automattic\\Jetpack\\Config\\Textdomain_Customizer::post_autoload_dump"
	},
"extra": {
	"textdomain": "jetpack"
},

The Jetpack Packages

The Jetpack packages must provide a list of their translatable files in the extra section of their composer.json files. This PR includes a temporary example file in the Config package. The Config package composer.json includes this extra section listing the example file:

"extra": {
		"translatable": [
			"/automattic/jetpack-config/src/example-file.php"
		]
	}

How it works

  1. When composer dump-autoload is run (it's run after both install and update), the Textdomain_Customizer::post_autoload_dump() method is called at the end of the dump-autoload process.
  2. The version of the Config package is checked. If it’s a development version, the post_autoload_dump() method just returns. This prevents the files from being changed and causing headaches during development.
  3. The plugin's composer.json file is checked for a textdomain entry in the extra section. If it exists, that textdomain will be used in the package files. If it does not exist, 'default' will be used.
  4. The textdomain_customizer traverses through the installed composer packages, looking for Jetpack packages and the translatable entry in each Jetpack package's composer.json file. If a Jetpack package has a translatable entry, the listed files are processed.
  5. The files are processed by replacing every occurrence of the placeholder JETPACK_CUSTOMIZE_TEXTDOMAIN with either the plugin's textdomain or 'default'.

Changes proposed in this Pull Request:

  • Add a new class and method, Textdomain_Customizer, that customizes the textdomain of strings in the packages. I put this in the Config class because it might be useful for multiple packages.
  • Update Jetpack's composer.json file with the post_autoload_dump method and Jetpack's textdomain.
  • Added a unit test. I need to write more tests.

Is this a new feature or does it add/remove features to an existing part of Jetpack?

  • This is a new feature.

Testing instructions

  1. Checkout this branch.
  2. Run composer install and check the example file included in this PR, packages/config/src/example-file.php. The textdomain in this file should still be the placeholder JETPACK_CUSTOMIZE_TEXTDOMAIN because you're using a development version of the Config package.
  3. To test that the method works, comment out this line to skip the early return for development versions.
  4. Run composer install and check file packages/config/src/example-file.php. The textdomain should be 'jetpack'.

Proposed changelog entry for your changes:

  • tbd

To do

A couple of things need to be done:

  • Refactoring to make CodeClimate happy.
  • More unit tests.
  • Display error messages when expected files don't exist.

@matticbot
Copy link
Contributor

Caution: This PR has changes that must be merged to WordPress.com
Hello kbrown9! These changes need to be synced to WordPress.com - If you 're an a11n, please commandeer, review, and approve D38741-code before merging this PR. Thank you!

@github-actions github-actions bot added the [Status] Needs Package Release This PR made changes to a package. Let's update that package now. label Feb 12, 2020
@jetpackbot
Copy link
Collaborator

jetpackbot commented Feb 12, 2020

Thank you for the great PR description!

When this PR is ready for review, please apply the [Status] Needs Review label. If you are an a11n, please have someone from your team review the code if possible. The Jetpack team will also review this PR and merge it to be included in the next Jetpack release.

Scheduled Jetpack release: March 3, 2020.
Scheduled code freeze: February 25, 2020

Generated by 🚫 dangerJS against 9a935a6

@kbrown9 kbrown9 force-pushed the try/use_composer_extra_replace_textdomain branch from fc76d84 to adae740 Compare February 12, 2020 18:51
@kbrown9 kbrown9 requested a review from zinigor February 12, 2020 19:08
@kraftbj
Copy link
Contributor

kraftbj commented Feb 12, 2020

This is nice. From a quick read through, I ask default becomes default-textdomain or something that is more unique before this concept gets to a merge-ready state. I wouldn't want future us being really confused when get_option( 'something', 'default'); starts returning 'jetpack' in production.

@kbrown9
Copy link
Member Author

kbrown9 commented Feb 12, 2020

This is nice. From a quick read through, I ask default becomes default-textdomain or something that is more unique before this concept gets to a merge-ready state. I wouldn't want future us being really confused when get_option( 'something', 'default'); starts returning 'jetpack' in production.

Yes, definitely. I used 'default' so that there would be a valid textdomain if the consuming plugin doesn't provide one in composer.json. A better approach would be to use something unique, as you suggested, and then change it to 'default' when a textdomain isn't provided.

Add a new class, Textdomain_Customizer, that customizes the textdomain of
strings in the packages. It uses the textdomain given in the extra section
of the plugin's composer.json file.

Update Jetpack's composer.json file with the post_install method and
Jetpack's textdomain.

Provide a preliminary unit test for the new class.
@kbrown9 kbrown9 force-pushed the try/use_composer_extra_replace_textdomain branch from adae740 to 8bec3d0 Compare February 14, 2020 07:09
@matticbot
Copy link
Contributor

kbrown9, Your synced wpcom patch D38741-code has been updated.

@kbrown9
Copy link
Member Author

kbrown9 commented Feb 14, 2020

I made a lot of changes to this, so I force-pushed a new commit.

  • I used a more unique placeholder, JETPACK_CUSTOMIZE_TEXTDOMAIN.
  • The package files with strings now need to be identified in each package's composer.json file.
  • The Textdomain_Customizer has been totally refactored to support the new way that the package files are identified.

A couple of things need to be done:

  • The tests are failing because I used an anonymous class in a unit test. (Oops - I thought those were added in PHP 5.6.)
  • It looks like I have to do a bit more refactoring to make CodeClimate happy.
  • I added one unit test, and I plan to write more.

Copy link
Contributor

@zinigor zinigor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love where this is going! This will solve the i18n problem inside packages thanks to how flexible it promises to be! I left a couple of comments, but nothing major.

}

foreach ( $files as $file ) {
$file_path = $this->vendor_dir . $file;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried googling for best practices of combining path segments into a single path in PHP, and threw up a little :)
Let's at least do trailingslashit for vendor_dir and realpath for the entire thing?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class is used when composer install, composer update, or composer dump-autoload are run, so trailingslashit isn’t available. Maybe we can just use rtrim() to remove any trailing slashes, and then add one?

I’ll also use realpath() on the file paths.

return;
}

$file_contents = file_get_contents( $file_path );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think loading the whole file into memory is going to be scalable? I don't know how to do it line by line yet, I'm sure it's going to be more fuss, do you think it's worth looking into?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That’s a good point, and I’ll look into it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, it's going to be a lot of fuss with all the file renaming, etc. Let's just keep what we have here and add error handling for file not being writable.

foreach ( $files as $file ) {
$file_path = $this->vendor_dir . $file;

if ( ! file_exists( $file_path ) ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have three different places here that check for either file_exists or is_file, maybe that all can be moved to customize_textdomain_in_file? This will also help with the "cognitive complexity" in CodeClimate analysis.

…paths

Use rtrim() to remove trailing slashes and then add a slash to $vendor_dir. This
ensures that $vendor_dir has at least on trailing slash in case $file doesn't
have a starting slash. Use realpath() to prepare the file paths.

Refactor customize_textdomain_in_files() to separate the recursive directory
work into its own method. This should reduce the cognitive complexity a bit.
@stale
Copy link

stale bot commented May 19, 2020

This PR has been marked as stale. This happened because:

  • It has been inactive in the past 3 months.
  • It hasn’t been labeled `[Pri] Blocker`, `[Pri] High`.

No further action is needed. But it's worth checking if this PR has clear testing instructions, is it up to date with master, and it is still valid. Feel free to close this issue if you think it's not valid anymore — if you do, please add a brief explanation.

@stale stale bot added the [Status] Stale label May 19, 2020
@kbrown9
Copy link
Member Author

kbrown9 commented Dec 7, 2020

I'm closing this in favor of the approach introduced in PR #18003. The approach described in this PR is too prone to errors. For example, it would be easy for developers to forget to use the textdomain placeholder when adding strings to the packages. The approach introduced in PR #18003 is much easier to work with.

@kbrown9 kbrown9 closed this Dec 7, 2020
@kraftbj kraftbj deleted the try/use_composer_extra_replace_textdomain branch April 17, 2025 13:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Focus] Jetpack DNA [Pri] Normal [Status] Needs Package Release This PR made changes to a package. Let's update that package now.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants