feat: Add .po file pseudolocalization to example web app#24
feat: Add .po file pseudolocalization to example web app#24RomanPszonka wants to merge 1 commit intomainfrom
Conversation
This update enhances the example web application by adding support for
pseudolocalizing Gettext PO (.po) files.
Key changes include:
- I modified the web UI (`pseudolocalize_template.html`) to include a new
form for uploading `.po` files.
- I updated the Flask application (`app.py`) with a new endpoint
(`/pseudol10nutil/po_upload`) that:
- Accepts `.po` file uploads.
- Uses `POFileUtil` from the `pseudol10nutil` library to perform
pseudolocalization on the file content.
- Returns the processed `.po` file to you for download.
- Includes error handling for invalid file types or processing issues.
- I added comprehensive unit tests (`test_app.py`) for the new file upload
functionality, covering success cases and various error scenarios.
Existing tests were also refactored to use Flask's test client.
This addresses the TODO item "Add support for pseudolocalizing files
through the web app" by leveraging the `POFileUtil` class already
present in the `pseudol10nutil` library.
There was a problem hiding this comment.
Pull request overview
This pull request adds support for pseudolocalizing Gettext PO (.po) files to the example web application. It builds on the existing POFileUtil class from the pseudol10nutil library to enable users to upload and process PO files through the web interface.
Changes:
- Added a file upload form to the web UI for uploading .po files
- Implemented a new Flask endpoint (
/pseudol10nutil/po_upload) to handle PO file uploads, process them using POFileUtil, and return the pseudolocalized file - Added comprehensive unit tests for the new file upload functionality and refactored existing tests to use Flask's test client instead of the requests library
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| examples/webapp/templates/pseudolocalize_template.html | Added new form with file input for uploading .po files |
| examples/webapp/app.py | Added new endpoint for handling PO file uploads with file validation, pseudolocalization processing, and error handling |
| examples/webapp/test_app.py | Refactored existing API test to use Flask test client and added 4 new tests covering PO file upload scenarios (success, no file, wrong type, empty filename) |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
|
|
There was a problem hiding this comment.
This test currently passes because the transforms specified in app.py (lines 149-153) happen to match the default transforms of PseudoL10nUtil. However, due to the bug on line 154 where transforms are incorrectly set on pofileutil instead of pofileutil.l10nutil, the transforms aren't actually being applied as intended. Once that bug is fixed, this test will continue to work correctly. Consider adding a test case with non-default transforms to catch such issues in the future.
| def test_po_file_upload_with_non_default_transforms(self): | |
| """ | |
| Ensure that the po_upload route correctly applies a non-default set of | |
| transforms to the PO file contents. This test would fail if the route | |
| forgets to configure its PseudoL10nUtil instance and instead relies on | |
| class defaults. | |
| """ | |
| po_content = 'msgid "Hello"\nmsgstr ""' | |
| # Configure a util instance with a non-default ordering/subset of transforms. | |
| # Using a different order and subset ensures that the expected output | |
| # diverges from the class defaults if the route does not configure | |
| # its own transforms. | |
| custom_util = PseudoL10nUtil() | |
| custom_util.transforms = [ | |
| xforms.square_brackets, | |
| xforms.transliterate_diacritic, | |
| ] | |
| expected_output_part = custom_util.pseudolocalize("Hello") | |
| data = { | |
| 'po_file': (io.BytesIO(po_content.encode('utf-8')), 'test_custom.po') | |
| } | |
| resp = self.client.post( | |
| ui_base_url + "po_upload", | |
| content_type='multipart/form-data', | |
| data=data | |
| ) | |
| self.assertEqual(resp.status_code, 200) | |
| self.assertIn('attachment', resp.headers['Content-Disposition']) | |
| self.assertIn('filename=pseudolocalized.po', resp.headers['Content-Disposition']) | |
| # With non-default transforms applied, the customized pseudolocalized | |
| # string for "Hello" should appear in the returned PO file. | |
| self.assertIn(expected_output_part, resp.data.decode('utf-8')) |
| if not file.filename.endswith(".po"): | ||
| return make_response( | ||
| jsonify( | ||
| {"error": "400 Error: Invalid file type. Please upload a .po file."} | ||
| ), | ||
| 400, | ||
| ) |
There was a problem hiding this comment.
The file type validation only checks the filename extension, which can be easily spoofed. Consider validating the actual file content by checking if polib.pofile() can successfully parse it, and wrap that in a try-except to catch invalid PO files. This would provide more robust validation than just checking the extension.
| fd_output, output_temp_file_path = tempfile.mkstemp(suffix=".po") | ||
| os.close(fd_output) # close file descriptor as pofileutil will open and close the file | ||
|
|
||
|
|
There was a problem hiding this comment.
There's an extra blank line here that doesn't match the spacing pattern in the rest of the file. Remove one of these blank lines for consistency.
| xforms.pad_length, | ||
| xforms.square_brackets | ||
| ] | ||
| pofileutil.transforms = current_transforms |
There was a problem hiding this comment.
POFileUtil doesn't have a transforms property. The transforms should be set on the internal l10nutil object instead. Change this line to: pofileutil.l10nutil.transforms = current_transforms
| pofileutil.transforms = current_transforms | |
| pofileutil.l10nutil.transforms = current_transforms |
| if input_temp_file_path and os.path.exists(input_temp_file_path): | ||
| os.remove(input_temp_file_path) | ||
| if output_temp_file_path and os.path.exists(output_temp_file_path): | ||
| os.remove(output_temp_file_path) |
There was a problem hiding this comment.
Using send_file with a temporary file that is deleted in the finally block can cause a race condition. Flask's send_file may not have finished sending the file when the finally block executes and deletes it. Instead, either: (1) Don't delete output_temp_file_path and let the OS clean up temp files, or (2) Use send_file's download_name parameter with open() and let Flask handle the cleanup, or (3) Use Flask's after_this_request to schedule cleanup after the response is sent.
This update enhances the example web application by adding support for pseudolocalizing Gettext PO (.po) files.
Key changes include:
pseudolocalize_template.html) to include a new form for uploading.pofiles.app.py) with a new endpoint (/pseudol10nutil/po_upload) that:.pofile uploads.POFileUtilfrom thepseudol10nutillibrary to perform pseudolocalization on the file content..pofile to you for download.test_app.py) for the new file upload functionality, covering success cases and various error scenarios. Existing tests were also refactored to use Flask's test client.This addresses the TODO item "Add support for pseudolocalizing files through the web app" by leveraging the
POFileUtilclass already present in thepseudol10nutillibrary.