Problem/Motivation

Deprecate and replace:

  • locale.bulk.inc
    • locale_translate_get_interface_translation_files
    • locale_translate_file_create
    • locale_translate_file_attach_properties
    • locale_translate_delete_translation_files
  • locale.batch.inc
    • locale_translation_http_check
    • locale_translation_download_source

Steps to reproduce

N/A

Proposed resolution

Create LocaleFileManager
- downloadTranslationSource
- getInterfaceTranslationFiles
- deleteTranslationFiles
- checkRemoteFileStatus
Create LocaleFile value object
- add createFromPath static method to set some default properties from file names
-- This now has the regex to try to detect the project, version and language from the filename
- tracks locale file status, type, last updated, project, and other information
Create RemoteFileInfo value object
Create RemoteFileStatus enum
- tracks the download for success, error or 404

Remaining tasks

N/A

User interface changes

N/A

Introduced terminology

N/A

API changes

New LocaleFileManager
New LocaleFile
New RemoteFileInfo
New RemoteFileStatus

Data model changes

All LocaleFile operations must happen on the LocaleFile value object instead of a stdObject

Release notes snippet

N/A

Issue fork drupal-3577671

Command icon 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

nicxvan created an issue. See original summary.

nicxvan’s picture

Issue summary: View changes
Status: Active » Needs review
nicxvan’s picture

I have some type questions I'm not sure if we should update.

berdir’s picture

Status: Needs review » Needs work

Added a proposal on how to deal with those file objects. I'm certain it goes further than you had planned to go here, but I think it's still quite a manageable scope if we use public properties.

Didn't see any other functions that would fit here, but made a proposal for another small isolated service that we can extract for those file history functions.

nicxvan’s picture

I kind of figured we needed more cleanup which is why I didn't add more.

Even though there isn't much usage in contrib I think there is custom usage due to this: #3575678: Locale checks file mtime but this is tricky if the translation files are copied to an image by Docker use a hash of the file instead

I guess technically this should be postponed on that but I want to work out what the api looks like here first before postponing it, and the other issue will likely get in soon anyway.

Thanks for the review I'll work on it.

nicxvan’s picture

nicxvan’s picture

Obviously got some failures to work through, but I think I have the structure in place.

Also looking through the other files.
I think we should handle
locale_translation_http_check
locale_translation_download_source
here as well, they are the only two non batch related functions in locale.batch.inc and they both relate to files as well.
Compare has a bunch, but there is a separate issue for that. Once this, and the compare are in there are only batch related functions in .inc which can probably be done in one issue.

locale_translation_http_check I think should go on localeFileManager
locale_translation_download_source should also go on localeFileManager and return a LocaleFile, there is a type property on the file set in locale_translation_download_source, but it's not used so I think we drop it.

Happy to keep type if we think it's necessary.

nicxvan’s picture

Title: Modernize locale.bulk.inc part 1 » Modernize locale file handling
Issue summary: View changes
nicxvan’s picture

Issue summary: View changes
nicxvan’s picture

We did need type and a couple more that get added dynamically.

I think there is likely one test failure I could use help with, but I think this is ready for a general architecture review again.

nicxvan’s picture

Thinking about this i have a theory that we need to check if project or version were set by the constructor, I suspect it's an order of operations issue.

nicxvan’s picture

Fixed most comments and did some cleanup.

core/modules/locale/tests/src/Functional/LocaleUpdateTest.php has a bunch of failures now I can't look at at the moment.

There is a failure on HEAD too and a random as well, but I think this is close.

I suspect the new failures are due to the ImportForm change.

nicxvan’s picture

locale_translation_get_file_history is returning the timestamp.
It gets set in locale_translation_update_file_history

This is called in three places:
locale bulk: locale_translate_batch_import_save
locale module: locale_translation_status_save
LocaleSource: sourceBuild

It seems to be set from timestampMedium which is offset by 200.

Seems to be something to do with the default:

    $default = [
      'langcode' => $langcode,
      'uri' => '',
      'timestamp' => $this->timestampMedium,
      'last_checked' => $this->timestampMedium,
    ];
nicxvan’s picture

The failing test is: core/modules/locale/tests/src/Functional/LocaleUpdateTest.php
It seems that somehow the history lookup is returning stale information.

I pushed up a debug only branch that adds some checks to make it easier to see what is happening, all line numbers are on that branch

I set a local variable for the two timestamps involved:
$mediumTimestamp = $this->timestampMedium;
$nowTimestamp = $this->timestampNow;

The medium is the default timestamp set on files, the now is what we compare against for updates.
Line 196 is asserting that the updated timestamp is greater than now and fails because the timestamp set is still set to the medium timestamp.

I initially got suspicious of $history = locale_translation_get_file_history(); so I looked at it and extracted the direct db call:

\Drupal::database()->select('locale_file')
      ->fields('locale_file', ['project', 'langcode', 'filename', 'version', 'uri', 'timestamp', 'hash', 'last_checked'])
      ->execute()
      ->fetchAll();

I then checked that 6 separate times in the test to see if the timestamp for the files change.
It does not.

One this branch:

IMPORTANT NOTE you need to check the actual project in the result, since each run the order of the projects is different in results or history.
Make sure you're comparing the same project, they are named similarly.
Example test run checking values for "contrib_module_one"

$mediumTimestamp = 1774534743
$nowTimestamp = 1774534943
$result3[contrib_module_one]->timestamp = "1774534743"
$result4[contrib_module_one]->timestamp = "1774534743"
$history['contrib_module_one']['de']->timestamp = "1774534743"

On main

I then checked out main and copied the modified test in: it passes as expected.
Note you have to be careful to check for the same project since the order of the results can change.
Example test run checking values for "contrib_module_one"

$mediumTimestamp = 1774534495
$nowTimestamp = 1774534695
$result3[contrib_module_one]->timestamp = "1774534495"
$result4[contrib_module_one]->timestamp = "1774534712"
$history['contrib_module_one']['de']->timestamp = "1774534712"

The culprit seems to be somewhere in:

    $this->drupalGet('admin/reports/translations');
    $this->submitForm([], 'Update translations');

Which only submits: core/modules/locale/src/Form/TranslationStatusForm.php

I can't dig further now, but I suspect something with type is getting changed so the logic in prepareUpdateData is not following the same path.

nicxvan’s picture

StatusFileSize
new35.84 KB
new9.96 KB

Ok this has been super hard to debug, but I think I'm finally on track: because the test does this $this->drupalGet('admin/reports/translations'); xdebug doesn't stop in the form for the submission, so I added a file put contents and output the medium timestamp and added the type and printed the projects, I see the type is changing on something so I see it's not being set properly somewhere, I'll attach the debug so I can chase it down.

nicxvan’s picture

StatusFileSize
new19.96 KB

Here is a better version on main with other tests commented out, the number of calls are higher since the test doesn't fail.

Edit: looking closer I think it's another red herring :/

alexpott made their first commit to this issue’s fork.

alexpott’s picture

I'm not sure about the approach of moving the regex into the LocaleFile constructor. Before locale_translate_file_attach_properties() was being called from two places. Now it is running much much more often. That makes me a bit uncomfortable. One we're likely to be doing unnecessary work, and two we have bugs like the one fixed in https://git.drupalcode.org/project/drupal/-/merge_requests/15013/diffs?c...

nicxvan’s picture

Assigned: Unassigned » nicxvan

I will move those properties to the constructor and the regex to a method that only gets called in the path it was before. Thank you again!

Assigning to myself to rework.

nicxvan changed the visibility of the branch 3577671-debug-only to hidden.

nicxvan’s picture

Assigned: nicxvan » Unassigned
Status: Needs work » Needs review

I think this is ready for review!

I've addressed the concern with the regex being called in scenarios it didn't used to be called by moving it to createFromPath and directly instantiating LocaleFile elsewhere.

Edit: I just pushed up a parameter order change so the constructor and createFromPath share the order since all calls to createFromPath now have both parameters.

nicxvan’s picture

I've addressed almost all of your feedback, I really think we should leave checkRemoteFileStatus to a follow up.

We can easily add internal to it and update later.

nicxvan’s picture

I took another look and just updated checkRemoteFileStatus, it now uses a value object with an enum for the status.

Everything is green so it's ready for review again.

berdir’s picture

Status: Needs review » Reviewed & tested by the community

This was quite the journey and it's touching quite few places and introducing quite a few things, new value objects. It's not _perfect_ (nothing ever is, and this is locale module. We have a long way to go) but I think it has significant improvements (we get rid of hard-to-document array structures and stdClass objects, sometimes for functions that do not have a single known call to them in contrib and some only from drush_language which isn't even D11 compatible yet)

nicxvan’s picture

Issue summary: View changes
nicxvan’s picture

Issue summary: View changes
nicxvan’s picture

Issue summary: View changes

Thank you for the reviews, it was quite a change, but as you mentioned I think it's a great improvement, and we have a lot of follow ups for the rest of locale.

I updated the IS and CR.

nicxvan’s picture

Issue summary: View changes

  • catch committed 44856397 on main
    fix: #3577671 Modernize locale file handling
    
    By: nicxvan
    By: berdir
    By...

nicxvan’s picture

Status: Reviewed & tested by the community » Patch (to be ported)

nicxvan’s picture

Status: Patch (to be ported) » Needs review
berdir’s picture

Status: Needs review » Needs work

Lets redo the 11.x MR now that #3549730: Enable LongLineDeclaration is in, should be almost identical to main now except the services.yml file.

nicxvan’s picture

Status: Needs work » Needs review

This is ready again!

berdir’s picture

Status: Needs review » Needs work

Manually compared the two commits as files (is there really no better way to do this?), there's an empty line difference compared to main.

berdir’s picture

Status: Needs work » Reviewed & tested by the community

Thanks. Can confirm that this is now identical except the expected locale.services.yml changes.

catch’s picture

Version: main » 11.x-dev
Status: Reviewed & tested by the community » Fixed

I think my original commit comment here got eaten by the Drupal.org DDOS attack, just to confirm I did in fact commit this on purpose to main and it was still open for 11.x backport due to commit conflicts.

Now committed the 11.x backport MR to 11.x, thanks!

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

  • catch committed db002cb1 on 11.x
    fix: #3577671 Modernize locale file handling
    
    By: nicxvan
    By: berdir
    By...
webflo’s picture

I profiled the installation process on a website with 38 languages and 290 enabled modules and opened #3586654: Avoid scanning the file system for local po files as a follow-up.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

nicxvan’s picture

ptmkenny’s picture

Testing 11.4.0-rc1, I found this commit broke drush locale:import because the deprecated shim forces the import to be 'und' instead of the language you specify: #3600694: locale_translate_file_attach_properties() BC layer incorrectly always sets override langcode to undefined