Move the translation marker onto Translation - #44
Merged
Conversation
`GravityPdf\Upload\__()` was a function in `src/Upload/i18n.php`, loaded by Composer's `files` autoload. That entry is Composer's own: an autoloader that indexes classes and nothing else — a classmap over php-scoper output, which is how a WordPress plugin usually consumes this library — never runs it. Every error path in the library then fatals with `Call to undefined function`, on a site where uploads had only ever succeeded. It is now `Translation::__()`, a static method, so it loads with the class. `composer.json` declares PSR-4 and nothing else, and `I18nTest` refuses both a bare function and a `files` entry coming back. Extraction is unchanged and needs no configuration, upstream or downstream: `xgettext` matches a keyword against the trailing identifier and ignores the class prefix, so `-k__:1` reads `Translation::__()` as it read `__()`. `i18n/upload.pot` regenerates byte-identical. The namespace fallback goes with it. An unqualified function call resolves against the global namespace when the current one has no match, so a validator forgetting the import reached WordPress's `__()` and translated at the throw. A class name has no such fallback — the same mistake now fatals at the call — and `I18nTest::testEveryCallerCanReachTheMarker()` still reads `src/` for the import, because that call sits on an error path either way. `tools/translator-readme/marker-import.php` proved the leading backslash in the Laravel and WordPress recipes mattered, which it did only while a consumer could import our `__()`. Both the fixture and the docs' caveat go. Fixes #42 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #42.
GravityPdf\Upload\__()was a function insrc/Upload/i18n.php, loaded by Composer'sfilesautoload. That entry is Composer's own — an autoloader that indexes classes and nothing else, such as a classmap built over php-scoper output, never runs it. Every error path then fatals withCall to undefined function GravityPdf\Upload\__(), on a site where uploads had only ever succeeded.It is now
Translation::__(), a static method, so it travels with the class.composer.jsondeclares PSR-4 and nothing else.Verification
The issue's own repro, loading every class and no
filesentry:Extraction is unchanged and needs no configuration, upstream or downstream.
xgettextmatches a keyword against the trailing identifier and ignores the class prefix, so-k__:1readsTranslation::__(),\GravityPdf\Upload\Translation::__()and an aliasedT::__()alike.composer i18n:potregeneratesi18n/upload.potbyte-identical across the move — that is the proof, and thei18nworkflow is what keeps it true.One correction to the issue
composer dump-autoload --classmap-authoritativedoes not dropautoload.files— tested against a fixture;autoload_files.phpis still emitted andautoload_real.phpstill requires it. The trigger is specifically an autoloader that isn't Composer's, which is what a scoped WordPress plugin tree normally has. The changelog says it that way so nobody on plain--classmap-authoritativegoes looking for a bug they don't have.The namespace fallback goes with it
An unqualified function call resolves against the global namespace when the current one has no match, so a validator forgetting
use function GravityPdf\Upload\__;reached WordPress's__()and translated at the throw. A class name has no such fallback: the same mistake now fatals at the call.That is still only reached on an error path, so
I18nTest::testEveryCallerCanReachTheMarker()is retargeted rather than deleted — it reads every file undersrc/for the import instead of waiting for a rejected upload.testNoGlobalMarkerCanMaskAMissingImport()is gone; there is no fallback left for a global stub to mask. A newtestTheMarkerTravelsWithTheClass()refuses both a bare function and afilesentry coming back.tools/translator-readme/marker-import.phpexisted to prove the "note the leading backslash" advice in the Laravel and WordPress recipes, which was load-bearing only while a consumer could import our__(). The fixture and the caveat both go;global-underscore.phpstays, since the documented adapters still call a global__().Naming
Kept
__rather thanmark(). PHP documents the__prefix as reserved by convention;__alone has never been a defined magic method, and CI proves it on 7.3 through 8.5.mark()would cost an extractor flag in our script and in every consumer extracting their own marked strings.No deprecation shim: 4.0.0 is unreleased (latest tag is
3.1.0), so the function was never published.Checks
phpunit(698 tests, 1,392 assertions, 1 skipped) ·phpstanlevel 9 ·lint·check-syntax·i18n:pot(no diff) ·base64-docs·psr7-readme·translator-readme— all pass locally.🤖 Generated with Claude Code