Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 91 additions & 34 deletions python_gpt_po/services/translation_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ class FileStats:
total_in_file: int = 0
untranslated: int = 0
translated_in_file: int = 0
fuzzy: int = 0


class TranslationService:
Expand Down Expand Up @@ -427,7 +428,10 @@ def _show_results_summary(self, stats: TranslationStats):
"""Show final results summary after processing."""
logging.info("")
logging.info("=" * 70)
logging.info("TRANSLATION RESULTS")
if self.config.flags.fix_fuzzy:
logging.info("FUZZY FIX RESULTS")
else:
logging.info("TRANSLATION RESULTS")
logging.info("=" * 70)

logging.info("PROCESSING SUMMARY:")
Expand All @@ -436,8 +440,12 @@ def _show_results_summary(self, stats: TranslationStats):
logging.info(" Total files handled: %d", stats.files.processed + stats.files.skipped)

logging.info("")
logging.info("TRANSLATION RESULTS:")
logging.info(" Entries successfully translated: %d", stats.entries.translated)
if self.config.flags.fix_fuzzy:
logging.info("FUZZY FIX RESULTS:")
logging.info(" Fuzzy entries fixed: %d", stats.entries.translated)
else:
logging.info("TRANSLATION RESULTS:")
logging.info(" Entries successfully translated: %d", stats.entries.translated)
if stats.entries.failed > 0:
logging.info(" Entries failed/skipped: %d", stats.entries.failed)

Expand Down Expand Up @@ -515,22 +523,34 @@ def _show_mode_info(self, total_entries: int):
def _show_translation_summary(self, scan_results, languages):
"""Show summary of files to translate."""
logging.info("=" * 70)
logging.info("TRANSLATION OVERVIEW")
if self.config.flags.fix_fuzzy:
logging.info("FUZZY FIX OVERVIEW")
else:
logging.info("TRANSLATION OVERVIEW")
logging.info("=" * 70)
logging.info("SCAN RESULTS:")
logging.info(" Total PO files found: %d", scan_results['files_scanned'])
files_matched = len(scan_results['files_to_process']) + len(scan_results['skipped_files'])
logging.info(" Files matching languages: %d", files_matched)
logging.info(" Files with language mismatch: %d", scan_results['language_mismatch_files'])
logging.info("")
logging.info("TRANSLATION STATUS:")
logging.info(" Files needing translation: %d", len(scan_results['files_to_process']))
logging.info(" Files already fully translated: %d", len(scan_results['skipped_files']))
logging.info("")
logging.info("ENTRY STATISTICS:")
logging.info(" Total entries in all files: %d", scan_results['total_entries_in_all_files'])
logging.info(" Already translated entries: %d", scan_results['total_translated_in_all_files'])
logging.info(" Entries to translate: %d", scan_results['total_entries'])
if self.config.flags.fix_fuzzy:
logging.info("FUZZY STATUS:")
logging.info(" Files with fuzzy entries: %d", len(scan_results['files_to_process']))
logging.info(" Files without fuzzy entries: %d", len(scan_results['skipped_files']))
logging.info("")
logging.info("ENTRY STATISTICS:")
logging.info(" Total entries in all files: %d", scan_results['total_entries_in_all_files'])
logging.info(" Fuzzy entries to fix: %d", scan_results['total_entries'])
else:
logging.info("TRANSLATION STATUS:")
logging.info(" Files needing translation: %d", len(scan_results['files_to_process']))
logging.info(" Files already fully translated: %d", len(scan_results['skipped_files']))
logging.info("")
logging.info("ENTRY STATISTICS:")
logging.info(" Total entries in all files: %d", scan_results['total_entries_in_all_files'])
logging.info(" Already translated entries: %d", scan_results['total_translated_in_all_files'])
logging.info(" Entries to translate: %d", scan_results['total_entries'])

if scan_results['total_entries_in_all_files'] > 0:
completion_percent = (
Expand All @@ -548,6 +568,15 @@ def _analyze_po_file(self, po_file):
stats.total_in_file = len([e for e in po_file if e.msgid])
stats.untranslated = len([e for e in po_file if is_entry_untranslated(e)])
stats.translated_in_file = stats.total_in_file - stats.untranslated
# Also count fuzzy entries when fix_fuzzy is enabled
if self.config.flags.fix_fuzzy:
# Count all fuzzy entries
stats.fuzzy = len([e for e in po_file if 'fuzzy' in e.flags])
# Also count header if it's fuzzy
if hasattr(po_file, 'metadata_is_fuzzy') and po_file.metadata_is_fuzzy:
stats.fuzzy += 1
else:
stats.fuzzy = 0
return stats

def _scan_po_files(self, input_folder: str, languages: List[str], gitignore_parser):
Expand All @@ -569,11 +598,24 @@ def _scan_po_files(self, input_folder: str, languages: List[str], gitignore_pars
results.total_entries_in_all_files += stats.total_in_file
results.total_translated_in_all_files += stats.translated_in_file

if stats.untranslated > 0:
results.files_to_process.append((po_file_path, po_file_result, stats.untranslated))
results.total_entries += stats.untranslated
logging.debug("File %s: %d/%d entries need translation",
po_file_path, stats.untranslated, stats.total_in_file)
# Include files with fuzzy entries when fix_fuzzy is enabled
if self.config.flags.fix_fuzzy:
# In fix-fuzzy mode, only process files with fuzzy entries
needs_processing = stats.fuzzy > 0
entries_to_process = stats.fuzzy
else:
# In normal mode, process files with untranslated entries
needs_processing = stats.untranslated > 0
entries_to_process = stats.untranslated

if needs_processing:
results.files_to_process.append((po_file_path, po_file_result, entries_to_process))
results.total_entries += entries_to_process
if self.config.flags.fix_fuzzy:
logging.debug("File %s: %d fuzzy entries to fix", po_file_path, stats.fuzzy)
else:
logging.debug("File %s: %d/%d entries need translation",
po_file_path, stats.untranslated, stats.total_in_file)
else:
results.skipped_files.append(po_file_path)
logging.debug("Skipping fully translated file: %s", po_file_path)
Expand All @@ -586,7 +628,15 @@ def _track_file_progress(self, po_file_path, initial_count):
"""Track translation progress for a single file."""
try:
po_file = polib.pofile(po_file_path)
remaining = len([e for e in po_file if is_entry_untranslated(e)])
if self.config.flags.fix_fuzzy:
# In fix-fuzzy mode, count remaining fuzzy entries
remaining = len([e for e in po_file if 'fuzzy' in e.flags])
# Also check if header is still fuzzy
if hasattr(po_file, 'metadata_is_fuzzy') and po_file.metadata_is_fuzzy:
remaining += 1
else:
# In normal mode, count untranslated entries
remaining = len([e for e in po_file if is_entry_untranslated(e)])
return initial_count - remaining, remaining
except Exception:
return initial_count, 0
Expand Down Expand Up @@ -919,25 +969,32 @@ def fix_fuzzy_entries(
detail_language: Optional[str] = None,
):
"""Find and fix fuzzy entries in a PO file using AI translation."""
# Check if the metadata/header is fuzzy
header_was_fuzzy = False
if hasattr(po_file, 'metadata_is_fuzzy') and po_file.metadata_is_fuzzy:
header_was_fuzzy = True
po_file.metadata_is_fuzzy = False
logging.info("Removed fuzzy flag from file header in %s", po_file_path)

# Find all fuzzy entries
fuzzy_entries = [entry for entry in po_file if 'fuzzy' in entry.flags]

if not fuzzy_entries:
if not fuzzy_entries and not header_was_fuzzy:
logging.info("No fuzzy entries found in %s", po_file_path)
return
if fuzzy_entries:
logging.info("Found %d fuzzy entries to fix in %s", len(fuzzy_entries), po_file_path)

logging.info("Found %d fuzzy entries to fix in %s", len(fuzzy_entries), po_file_path)

texts_to_translate = [entry.msgid for entry in fuzzy_entries]
translations = self.get_translations(texts_to_translate, target_language, po_file_path, detail_language)
texts_to_translate = [entry.msgid for entry in fuzzy_entries]
translations = self.get_translations(texts_to_translate, target_language, po_file_path, detail_language)

self._update_fuzzy_po_entries(po_file, translations, entries_to_update=fuzzy_entries)
self._update_fuzzy_po_entries(po_file, translations, entries_to_update=fuzzy_entries)

po_file.save(po_file_path)

self.po_file_handler.log_translation_status(
po_file_path,
texts_to_translate,
[entry.msgstr for entry in fuzzy_entries]
)

logging.info("Fuzzy fix completed for %s", po_file_path)
self.po_file_handler.log_translation_status(
po_file_path,
texts_to_translate,
[entry.msgstr for entry in fuzzy_entries]
)
# Save the file if any changes were made (header fuzzy removal or entry translations)
if header_was_fuzzy or fuzzy_entries:
po_file.save(po_file_path)
logging.info("Fuzzy fix completed for %s", po_file_path)