Problem/Motivation
In some situations the translations directory (where downloaded translation files get stored; e.g. sites/default/files/translations) is missing. This may happen for example in a development process, where a site is re-created on a dev machine from git and a live DB but without populating the files directory (as you use StageFileProxy).
Simplified steps to reproduce in a normally working site (translations directory = sites/default/files/translations)
$ rm -r sites/default/files/translations
$ drush locale-update
or on browser at admin/config/regional/translate/import and trying to import the .po file.
Example error messages:
[notice] File temporary://filejmiMlj could not be moved/copied because the destination directory translations:// is not configured correctly.
[error] Unable to download translation file https://ftp.drupal.org/files/translations/8.x/admin_toolbar/admin_toolbar-8.x-1.24.de.po.
[warning] fopen(translations://admin_toolbar-8.x-1.24.de.po): failed to open stream: "Drupal\locale\StreamWrapper\TranslationsStream::stream_open" call failed PoStreamReader.php:154
[warning] fgets() expects parameter 1 to be resource, boolean given PoStreamReader.php:248
[notice] Unable to import translations file: translations://admin_toolbar-8.x-1.24.de.po
Screenshot of error message on translation import form:

Processes that will profit from a fix:
- Update translation via user interface
- Upload a translation via the Translation import page
- Adding a language
Proposed resolution
- Auto-create the directory if at the start of the batch check/update process.
- Auto-create the directory when importing a translation via the Translation import page.
Remaining tasks
Find other other processes where the translation directory is yet not auto-created.
User interface changes
none
API changes
none
Data model changes
none
| Comment | File | Size | Author |
|---|---|---|---|
| #58 | 3013435-58.patch | 5.98 KB | jelle_s |
| #45 | 3013435-upload-error.png | 338.73 KB | sokru |
Issue fork drupal-3013435
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:
Comments
Comment #4
sophie.skI've seen this lately. We had the module enabled all the way up to prod, then new developers (me!) joined the company. The module is enabled in config, and we were provided with a database dump (to get content and users), so the module was enabled in the database dump.
The locale module therefore never got installed on my local machine and the folder never got created. I've had to create a new update hook to ensure it got created on all environments, including local and remote, as we were moving hosts as well.
Perhaps there could be a requirements check for this module that makes sure the translations directory exists and is writeable?
Comment #5
sutharsan commented@Sophie.SK, Can you please clarify "The locale module therefore never got installed on my local machine" How can you explain this? If you combine code and a database, any module enabled in the database should be enabled. Did you run config import after combining code and DB on your local environment?
The locale module does check for and create the directory at various points. For example when enabling the module and when writing po files.
I'm not sure if the module can be and should be made responsible for re-creating the environment. If your system relies on a translation directory within the public files directory, you can enforce this by placing a gitkeep file inside the translations directory and exclude that from gitignore. That way an empty directory is made available via the VCS.
Comment #6
sophie.sk@Sutharsan
Re-reading the comment, I think I mean "I didn't enable the module locally, therefore the install hooks never ran".
The module was enabled, but I didn't enable it. The directory creation happens on install.
This was further exacerbated when we switched hosts recently. We had the database and the config, so the module was enabled, but it wasn't being installed and the translations directory didn't get created when we deployed. I had to write an additional update hook to create the directory on each environment.
That could work. Should it be documented that a directory will be created on install but if it's required otherwise then a gitkeep would be needed?
Comment #7
geek-merlinI see a way how to reliably reproduce this, see IS.
Comment #8
sutharsan commentedComment #9
sutharsan commentedRestructured the issue summary with same content in an attempt to make the case easier to understand.
I can think of the following cases that should be considered:
- Update translation via user interface
- Upload a translation via the Translation Import page
- Adding a language
- Does configuration import trigger interface translation update?
- Update translation via drush
- Update translation via drupal console
Drush and Drupal Console are not in scope of this issue, but are important to consider when implementing a solution.
Some technical notes: Check translation status and Update translations are two independent processes. During 'check' the translation directory may be used for reading (
only if the translation source is local or local+remote). During 'update' the translation directory is used for reading and writing (writing only in case of remote translation source).Comment #10
geek-merlinHmm, we could extend \Drupal\Core\StreamWrapper\LocalStream with an "autocreate" option and set that for TranslationStream in locale.services.yml. This could benefit all others too and we're done. Thoughts?
Comment #11
sutharsan commentedInteresting idea, but it feels like cutting corners. When I look at https://www.php.net/manual/en/class.streamwrapper.php I think the streamwrapper must knows about the 'how' and not about the 'when'. It has an mkdir method, but looking at core implementations, it does not use it itself.
I have made a first patch that adds a batch task that creates the directory right before the download/import tasks. It also handles creating the directory when importing a translation via the interface.
Comment #12
sophie.skI'm not sure about this approach, as it seems to rely on uploading a translation manually through the UI. That means we wouldn't get the benefits when doing things elsewhere (eg enabling a new module, which fetches new translations).
I think this is useful for one particular use case... maybe it's worth adding a requirements check (shown on the Status Report) for the directory existing?
When performing another action that tries to create translations (such as enabling a module), it'd be useful to check whether the directory exists before trying to create them, to avoid the error message.
Comment #13
sutharsan commented@Sophie.SK, Mabe I was not clear about what the patch does. Right before the batch task that imports translations the translation directory is created (when needed). Additionally the translation directory is created before manual translation import.
Comment #14
sutharsan commented(updated the #11 text accordingly)
Comment #15
sutharsan commentedThis patch add a test for the new function that creates the directory. It updates an existing test for the added cron task.
Comment #17
sutharsan commentedComment #18
geek-merlin@Sutharsan: I rolled the other approach and to keep this focused posted it on #3105515: Local stream wrappers break when directory is missing. Thoughts?
Comment #21
dieterholvoet commentedI re-rolled the patch against 9.1.5.
Comment #23
benjifisherPlease do not ask the testbot to try again until #3207086: [HEAD BROKEN] Consistent failure in MonthDatePluginTest is fixed.
Comment #24
alexpott#3207086: [HEAD BROKEN] Consistent failure in MonthDatePluginTest is fixed. I have not reviewed the code.
Comment #25
alexpottI wish locale code was a bit more like the rest of core. Adding more functions to the global namespace is a shame.
Code in \Drupal\locale\Form\LocaleSettingsForm::buildForm
implies that is acceptable for this setting to be empty.
Also I'm not sure that the extra complexity around the permissions is really necessary. We don't do this in system_check_directory() so if you go to the file system form and press save the permissions will change.
There's no need for the name to be random - a consistent name would be better here - something like 'test_translations'...
FileSecurity::writeHtaccess($directory);in it - and then things are more consistent.Comment #28
sjerdoRe-roll patch #21
Still needs work, see #25
Comment #30
ravi.shankar commentedFixed failed tests of patch #28.
Comment #31
nico972 commentedIf you go to the admin/config/media/file-system page, the "translations" directory will be created and will contain a .htaccess but I don't know what code does this.
Comment #34
luksakJust ran into this issue once again. It would be great to fix this, since it can be pretty annoying such as breaking CI pipelines when installing modules.
Could we move this issue forward? I guess the comments in #25 are still valid?
Comment #35
adam-delaney commentedRe rolling patch 30 for latest Drupal 10.2.x
Comment #36
adam-delaney commentedI made a mistake re-rolling 35, so I've re-rolled a new patch to resolve the issue which should apply cleanly against latest 10.2.x
Comment #40
sokru commentedThe MR !8320 should address the feedback from #25.3 and #25.5, but I wonder what action (if any) should be taken for other comments on #25, so marking this "Needs review".
I feel the priority should be raised, because this issue is typical for causing frustration, since the the UI doesn't give a proper indication of what is wrong with settings.
Comment #41
ivnishMR works fine. Tested drush locale-update and translations update via UI
Comment #42
gábor hojtsyI have mixed feelings about this issue.
I think the missing translation directory in your dev situation for example is a lack of syncing files in your development workflow. Other critical files may also be missing. Eg. the JS translation files may get regenerated locally and that may end up leading to a mismatch in your live site when the updates get pushed (depending on what you push).
At the same time I understand the developer experience improvement that instead of an error, you get an empty directory at least. I am not sure though if that does not lead to unwanted side effects.
How are other files missing handled by Drupal? Eg. if your site logo is missing or other key files that you don't have on the dev environment?
One MR specific comment: How is the
locale/src/Plugin/QueueWorker/LocaleTranslation.phpchange related?Comment #43
smustgrave commentedShould this be in PNMI ?
Comment #44
smustgrave commentedTo address some of the questions in #42
Comment #45
sokru commentedI don't think its recommended to include files from sites/default/files to version control. If the theme build assets are not included in version control and the build process fails, at least there's visual cue that the files are missing. When translation directory missing, the error message on UI/watchdog doesn't give guidance how to resolve the issue. I added the screenshot of the error message on UI to issue summary.
For code specific discussion I think the comments on merge request would be preferred, but
locale/src/Plugin/QueueWorker/LocaleTranslation.phpchange is needed becauseLocaleTranslation::processItem()provides zero $args. The tests where failing without this change.Comment #46
gábor hojtsyIndeed that error message is not helpful. Could the error be more specific and tell the user the directory does not exist? What can the user then do to get the directory created? (Save the file settings config form?) At least if we let the user know this, they have a chance to sync their translation files if they wish so without silently getting them into a different situation.
Comment #47
smustgrave commentedThanks so much for taking a look @gábor hojtsy moving to NW to address the feedback.
Comment #48
sokru commentedre: #46
Asking users to go to separate form to resolve this issue does not help the command-line use cases (CI pipelines, syncing database from different environment, etc.).
IMHO automatically creating the missing translation directory gives the best DX/UX. We could add some message to inform about autocreation.
Comment #49
gordonio commentedI have run into this a few times with CI pipelines and it is a bit of an annoyance. I agree with sokru that we should handle it automatically if it doesn't exist. I can't see a downside to having it there, even if it is empty.
Comment #50
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #52
tobiasbMR updated + missing typehints.
Comment #53
smustgrave commentedThink would be a good compromise
Comment #55
nicrodgersWhoops - tried to update the MR to work with 11.2 but it seems to have gone wrong somewhere.. Sorry!
Comment #56
penyaskitoDevelopment happens on 11.x, not 11.2.x.
Comment #57
penyaskitoFixed MR by rebasing back with 11.x. Didn't really review, just git magic.
Comment #58
jelle_sPatch file of the MR for those that want to use it in composer.patches.json. Applies on 11.2.1.