Problem/Motivation
#3577671: Modernize locale file handling introduced a new Locale Manager, which Drush 13.7.3 (the most recent as of this writing) does not yet support. There is a deprecated wrapper, but it incorrectly handles the following command:
drush locale:import ja mytranslations.po
This completes (incorrectly!) with "No configuration objects have been updated." when the file is full of translations. Nothing is imported. This works On Drupal 11.3 and below.
Here is the root cause analysis from Claude Opus 4.8 which I believe to be correct:
Root cause — the chain
1. Drush still calls the old procedural API and passes a plain stdClass.
vendor/drush/drush/src/Commands/core/LocaleCommands.php:322
$poFile = (object) ['filename' => basename($file), 'uri' => $file]; // stdClass, NOT a FileInterface
$poFile = locale_translate_file_attach_properties($poFile, $translationOptions); // $translationOptions['langcode']
=== 'ja'
$batch = locale_translate_batch_build([$poFile->uri => $poFile], $translationOptions);
2. The 11.4 BC shim forces the langcode to und for non-FileInterface input.
web/core/modules/locale/locale.bulk.inc:213
function locale_translate_file_attach_properties($file, array $options = []) {
@trigger_error(/* deprecated in 11.4.0 … */);
if ($file instanceof FileInterface) {
$uri = $file->getFileUri();
$filename = $file->getFilename();
}
else {
$uri = $file->uri;
$filename = $file->filename;
$options['langcode'] = LanguageInterface::LANGCODE_NOT_SPECIFIED; // ← discards caller's 'ja'
}
return LocaleFile::createFromPath($filename, $uri, $options['langcode']); // override = 'und'
}
Pre-11.4, this function preserved $options['langcode']. The new shim overwrites it.
3. createFromPath() treats the non-null 'und' as an authoritative override.
web/core/modules/locale/src/File/LocaleFile.php:104
if (isset($matches[5])) { // regex extracts 'ja' from 'ja.po' into $matches[5]
$project = $matches[2] . $matches[3];
$version = $matches[4];
$langcode = $langcodeOverride ?? $matches[5]; // 'und' ?? 'ja' → 'und' (?? does not fall through)
}
The correctly-detected ja is thrown away; the resulting LocaleFile->langcode is und.
4. The import operation skips any file whose langcode is und.
web/core/modules/locale/src/LocaleImportBatch.php:122
public function batchImport(LocaleFile $file, array $options, ...&$context): void {
...
if (isset($file->langcode) && $file->langcode != LanguageInterface::LANGCODE_NOT_SPECIFIED) {
// parse + write to DB happens here — NEVER reached for our file
}
}
Why it surfaces now
11.4 converted the locale batch callbacks to the LocaleImportBatch / LocaleConfigBatch services and replaced the
procedural file helpers with the LocaleFile value object (change records node/3589759 and node/3577675). Drush 13.7.3
still calls the procedural BC layer, so it hits the buggy shim. The bug is the shim's unconditional
$options['langcode'] = LANGCODE_NOT_SPECIFIED for non-FileInterface input.
Proposed resolution
The shim should reflect the provided langcode, not drop it.
Remaining tasks
API changes
Restores behavior to how it worked before.
Issue fork drupal-3600694
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
- fix_locale_shim
changes, plain diff MR !16056
Comments
Comment #2
ptmkenny commentedComment #4
berdirThe fix makes sense and I think it can be summarized into a single sentence which I've used as the new issue title. I'll never understand why LLM's need to use so many words, I guess to use as many tokens as possible.
That slipped in because the old implementation had a similar else case, but it was a completely different if condition back then.
Comment #9
catchI think it's partly that, and also that lengthy responses result in something along the lines of a Gish Gallop https://en.wikipedia.org/wiki/Gish_gallop which makes it harder for people to evaluate and more likely to accept the result.
This looks fine, it's on the borderline of where we should start adding test coverage, but given it's 100% in the deprecated code path I think we should just go ahead here.
Comment #11
dpalicepeio@drupbuilder.org commentedHello All,
If have applied the fix.
But, using drush 13.7.3.0 on Drupal core 11.4.0-rc2
when i launch drush locale:check
I obtain
> [error] TypeError: call_user_func_array(): Argument #1 ($callback) must be a valid callback, function "Drupal\locale\LocaleFetch:batchVersionCheck" not found or invalid function name in _drush_batch_worker() (line 257 of /var/www/D11/vendor/drush/drush/includes/batch.inc) #0 /var/www/D11/vendor/drush/drush/includes/batch.inc(204): _drush_batch_worker()
> #1 /var/www/D11/vendor/drush/drush/includes/batch.inc(75): _drush_batch_command()
> #2 /var/www/D11/vendor/drush/drush/src/Commands/core/BatchCommands.php(25): drush_batch_command()
> #3 [internal function]: Drush\Commands\core\BatchCommands->process()
> #4 /var/www/D11/vendor/consolidation/annotated-command/src/CommandProcessor.php(276): call_user_func_array()
> #5 /var/www/D11/vendor/consolidation/annotated-command/src/CommandProcessor.php(212): Consolidation\AnnotatedCommand\CommandProcessor->runCommandCallback()
> #6 /var/www/D11/vendor/consolidation/annotated-command/src/CommandProcessor.php(175): Consolidation\AnnotatedCommand\CommandProcessor->validateRunAndAlter()
> #7 /var/www/D11/vendor/consolidation/annotated-command/src/AnnotatedCommand.php(389): Consolidation\AnnotatedCommand\CommandProcessor->process()
> #8 /var/www/D11/vendor/symfony/console/Command/Command.php(341): Consolidation\AnnotatedCommand\AnnotatedCommand->execute()
> #9 /var/www/D11/vendor/symfony/console/Application.php(1117): Symfony\Component\Console\Command\Command->run()
> #10 /var/www/D11/vendor/drush/drush/src/Application.php(201): Symfony\Component\Console\Application->doRunCommand()
> #11 /var/www/D11/vendor/symfony/console/Application.php(356): Drush\Application->doRunCommand()
> #12 /var/www/D11/vendor/symfony/console/Application.php(195): Symfony\Component\Console\Application->doRun()
> #13 /var/www/D11/vendor/drush/drush/src/Runtime/Runtime.php(113): Symfony\Component\Console\Application->run()
> #14 /var/www/D11/vendor/drush/drush/src/Runtime/Runtime.php(40): Drush\Runtime\Runtime->doRun()
> #15 /var/www/D11/vendor/drush/drush/drush.php(140): Drush\Runtime\Runtime->run()
> #16 /var/www/D11/vendor/bin/drush.php(119): include('...')
> #17 {main}.
> TypeError: call_user_func_array(): Argument #1 ($callback) must be a valid callback, function "Drupal\locale\LocaleFetch:batchVersionCheck" not found or invalid function name in /var/www/D11/vendor/drush/drush/includes/batch.inc on line 257 #0 /var/www/D11/vendor/drush/drush/includes/batch.inc(204): _drush_batch_worker()
> #1 /var/www/D11/vendor/drush/drush/includes/batch.inc(75): _drush_batch_command()
> #2 /var/www/D11/vendor/drush/drush/src/Commands/core/BatchCommands.php(25): drush_batch_command()
> #3 [internal function]: Drush\Commands\core\BatchCommands->process()
> #4 /var/www/D11/vendor/consolidation/annotated-command/src/CommandProcessor.php(276): call_user_func_array()
> #5 /var/www/D11/vendor/consolidation/annotated-command/src/CommandProcessor.php(212): Consolidation\AnnotatedCommand\CommandProcessor->runCommandCallback()
> #6 /var/www/D11/vendor/consolidation/annotated-command/src/CommandProcessor.php(175): Consolidation\AnnotatedCommand\CommandProcessor->validateRunAndAlter()
> #7 /var/www/D11/vendor/consolidation/annotated-command/src/AnnotatedCommand.php(389): Consolidation\AnnotatedCommand\CommandProcessor->process()
> #8 /var/www/D11/vendor/symfony/console/Command/Command.php(341): Consolidation\AnnotatedCommand\AnnotatedCommand->execute()
> #9 /var/www/D11/vendor/symfony/console/Application.php(1117): Symfony\Component\Console\Command\Command->run()
> #10 /var/www/D11/vendor/drush/drush/src/Application.php(201): Symfony\Component\Console\Application->doRunCommand()
> #11 /var/www/D11/vendor/symfony/console/Application.php(356): Drush\Application->doRunCommand()
> #12 /var/www/D11/vendor/symfony/console/Application.php(195): Symfony\Component\Console\Application->doRun()
> #13 /var/www/D11/vendor/drush/drush/src/Runtime/Runtime.php(113): Symfony\Component\Console\Application->run()
> #14 /var/www/D11/vendor/drush/drush/src/Runtime/Runtime.php(40): Drush\Runtime\Runtime->doRun()
> #15 /var/www/D11/vendor/drush/drush/drush.php(140): Drush\Runtime\Runtime->run()
> #16 /var/www/D11/vendor/bin/drush.php(119): include('...')
> #17 {main}
> [warning] Drush command terminated abnormally.
In ProcessBase.php line 171:
Unable to decode output into JSON: Syntax error
TypeError: call_user_func_array(): Argument #1 ($callback) must be a valid callback, function "Drupal\locale\LocaleFetch:batchVersionCheck" not found or invalid function name in _drush_batch_
worker() (line 257 of /var/www/D11/vendor/drush/drush/includes/batch.inc).
Comment #12
dpalicepeio@drupbuilder.org commentedAn now when i try to check translation using Ui Checking translations , i obtain a red message "
An AJAX HTTP error occurred.
HTTP Result Code: 500
Debugging information follows.
Path: /batch?id=2280&op=do_nojs&op=do
StatusText: 500 Service unavailable (with message)
ResponseText: The website encountered an unexpected error. Try again later.TypeError: Drupal\locale\LocaleSource::loadSource(): Return value must be of type Drupal\locale\LocaleTranslationSource, stdClass returned in Drupal\locale\LocaleSource->loadSource() (line 85 of core/modules/locale/src/LocaleSource.php). Drupal\locale\LocaleFetch->batchVersionCheck() (Line: 300)
_batch_process() (Line: 139)
_batch_do() (Line: 96)
_batch_page() (Line: 52)
Drupal\system\Controller\BatchController->batchPage()
call_user_func_array() (Line: 123)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->{closure:Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber::wrapControllerExecutionInRenderContext():121}() (Line: 638)
Drupal\Core\Render\Renderer::{closure:Drupal\Core\Render\Renderer::executeInRenderContext():638}()
Fiber->start() (Line: 639)
Drupal\Core\Render\Renderer->executeInRenderContext() (Line: 121)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext() (Line: 97)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->{closure:Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber::onController():96}() (Line: 183)
Symfony\Component\HttpKernel\HttpKernel->handleRaw() (Line: 76)
Symfony\Component\HttpKernel\HttpKernel->handle() (Line: 53)
Drupal\Core\StackMiddleware\Session->handle() (Line: 30)
Drupal\Core\StackMiddleware\KernelPreHandle->handle() (Line: 28)
Drupal\Core\StackMiddleware\ContentLength->handle() (Line: 32)
Drupal\big_pipe\StackMiddleware\ContentLength->handle() (Line: 118)
Drupal\page_cache\StackMiddleware\PageCache->pass() (Line: 92)
Drupal\page_cache\StackMiddleware\PageCache->handle() (Line: 34)
Drupal\counter\StackMiddleware\CounterMiddleware->handle() (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle() (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle() (Line: 61)
Drupal\Core\StackMiddleware\AjaxPageState->handle() (Line: 54)
Drupal\Core\StackMiddleware\StackedHttpKernel->handle() (Line: 753)
Drupal\Core\DrupalKernel->handle() (Line: 19)"
and
Oops, something went wrong. Check your browser's developer console for more details.
Comment #13
berdirThe drush error is https://github.com/drush-ops/drush/issues/6566, merged but not yet released I believe. The second error is #3601433: Fix local_status deprecation by clearing source in a post update, will hopefully still land before 11.4.0. Instead of the patch, you can also run that one line in the post update manually with drush ev or so.
Comment #14
dpalicepeio@drupbuilder.org commentedThank you berdir : I'have just reported the two fixes, batch and UI . All is working