diff --git a/babel/messages/catalog.py b/babel/messages/catalog.py index 5e6c28255..a7e34d751 100644 --- a/babel/messages/catalog.py +++ b/babel/messages/catalog.py @@ -1025,11 +1025,11 @@ def _merge( else: if not no_fuzzy_matching: # do some fuzzy matching with difflib - matches = get_close_matches( - self._to_fuzzy_match_key(key), - fuzzy_candidates.keys(), - 1, - ) + matchkey = self._to_fuzzy_match_key(key) + if matchkey in fuzzy_candidates: + matches = [matchkey] + else: + matches = get_close_matches(matchkey, fuzzy_candidates.keys(), 1) if matches: modified_key = matches[0] newkey, newctxt = fuzzy_candidates[modified_key] diff --git a/tests/messages/test_catalog.py b/tests/messages/test_catalog.py index 7c730d325..9f8eb43ca 100644 --- a/tests/messages/test_catalog.py +++ b/tests/messages/test_catalog.py @@ -14,6 +14,9 @@ import datetime import pickle from io import StringIO +from unittest.mock import patch + +import pytest from babel.dates import UTC, format_datetime from babel.messages import catalog, pofile @@ -542,6 +545,91 @@ def test_catalog_update(): assert cat.header_comment == template.header_comment # Header comment also gets updated +@pytest.mark.parametrize( + ('old_id', 'new_id'), + [ + ('Save changes', 'SAVE CHANGES'), + (' Save changes ', 'save changes'), + (('One file', 'Many files'), ('ONE FILE', 'MANY FILES')), + (' ', '\t'), + ], +) +def test_update_exact_normalized_match(old_id, new_id): + cat = catalog.Catalog(locale='de') + translation = ('Eine Datei', 'Viele Dateien') if isinstance(old_id, tuple) else 'Speichern' + cat.add(old_id, translation, user_comments=['Keep this note']) + template = catalog.Catalog() + template.add(new_id, context='review', locations=[('review.py', 12)]) + + cat.update(template) + + message = cat.get(new_id, context='review') + assert message.string == translation + assert message.previous_id == (list(old_id) if isinstance(old_id, tuple) else [old_id]) + assert message.user_comments == ['Keep this note'] + assert message.locations == [('review.py', 12)] + assert 'fuzzy' in message.flags + assert not cat.obsolete + + +def test_update_avoids_fuzzy_search_for_exact_normalized_match(): + cat = catalog.Catalog(locale='de') + cat.add('Save changes', 'Speichern') + template = catalog.Catalog() + template.add('SAVE CHANGES') + + with patch('babel.messages.catalog.get_close_matches') as fuzzy_search: + cat.update(template) + + fuzzy_search.assert_not_called() + assert cat['SAVE CHANGES'].string == 'Speichern' + + +def test_update_still_searches_for_a_nonexact_match(): + cat = catalog.Catalog(locale='de') + cat.add('Save changes', 'Speichern') + template = catalog.Catalog() + template.add('Save change') + + with patch( + 'babel.messages.catalog.get_close_matches', wraps=catalog.get_close_matches, + ) as search: + cat.update(template) + + search.assert_called_once() + assert cat['Save change'].string == 'Speichern' + assert 'fuzzy' in cat['Save change'].flags + + +def test_update_exact_normalized_match_keeps_existing_candidate_choice(): + cat = catalog.Catalog(locale='de') + cat.add('Save changes', 'Erste Übersetzung', context='toolbar') + cat.add('save changes', 'Zweite Übersetzung', context='dialog') + template = catalog.Catalog() + template.add('SAVE CHANGES', context='review') + + cat.update(template) + + message = cat.get('SAVE CHANGES', context='review') + assert message.string == 'Zweite Übersetzung' + assert message.previous_id == ['save changes'] + assert list(cat.obsolete) == [('Save changes', 'toolbar')] + + +def test_update_exact_normalized_match_with_fuzzy_matching_disabled(): + cat = catalog.Catalog(locale='de') + cat.add('Save changes', 'Speichern') + template = catalog.Catalog() + template.add('SAVE CHANGES') + + with patch('babel.messages.catalog.get_close_matches') as fuzzy_search: + cat.update(template, no_fuzzy_matching=True) + + fuzzy_search.assert_not_called() + assert cat['SAVE CHANGES'].string is None + assert 'Save changes' in cat.obsolete + + def test_datetime_parsing(): val1 = catalog._parse_datetime_header('2006-06-28 23:24+0200') assert val1.timetuple()[:5] == (2006, 6, 28, 23, 24)