Skip to content

feat: Add .po file pseudolocalization to example web app#24

Open
RomanPszonka wants to merge 1 commit intomainfrom
feature/po-file-pseudolocalization
Open

feat: Add .po file pseudolocalization to example web app#24
RomanPszonka wants to merge 1 commit intomainfrom
feature/po-file-pseudolocalization

Conversation

@RomanPszonka
Copy link
Owner

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.

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.
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +70 to +71


Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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'))

Copilot uses AI. Check for mistakes.
Comment on lines +128 to +134
if not file.filename.endswith(".po"):
return make_response(
jsonify(
{"error": "400 Error: Invalid file type. Please upload a .po file."}
),
400,
)
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
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


Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change

Copilot uses AI. Check for mistakes.
xforms.pad_length,
xforms.square_brackets
]
pofileutil.transforms = current_transforms
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Suggested change
pofileutil.transforms = current_transforms
pofileutil.l10nutil.transforms = current_transforms

Copilot uses AI. Check for mistakes.
Comment on lines +169 to +172
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)
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants