Locales and localization refactor with Locales as a localizer object.

Removed auto-localization from search/search_address APIs (now explicit), simplified AddressLines to subclass List[AddressLine], made display_name a computed property in Results instead of field and removed result-localization circular dependencies
This commit is contained in:
anqixxx
2025-07-24 16:54:13 -04:00
parent b7d77b9b43
commit 6b627df4fb
10 changed files with 113 additions and 67 deletions

View File

@@ -11,7 +11,10 @@ Data classes are part of the public API while the functions are for
internal use only. That's why they are implemented as free-standing functions
instead of member functions.
"""
from typing import Optional, Tuple, Dict, Sequence, TypeVar, Type, List, cast, Callable
from typing import (
Optional, Tuple, Dict, Sequence, TypeVar, Type, List,
cast, Callable
)
import enum
import dataclasses
import datetime as dt
@@ -23,7 +26,6 @@ from .sql.sqlalchemy_types import Geometry
from .types import Point, Bbox, LookupDetails
from .connection import SearchConnection
from .logging import log
from .localization import Locales
# This file defines complex result data classes.
@@ -130,27 +132,21 @@ class AddressLine:
[Localization](Result-Handling.md#localization) below.
"""
@property
def display_name(self) -> Optional[str]:
""" Dynamically compute the display name for the Address Line component
"""
if self.local_name:
return self.local_name
elif 'name' in self.names:
return self.names['name']
elif self.names:
return next(iter(self.names.values()), None)
return None
class AddressLines(List[AddressLine]):
""" Sequence of address lines order in descending order by their rank.
"""
def localize(self, locales: Locales) -> List[str]:
""" Set the local name of address parts according to the chosen
locale. Return the list of local names without duplicates.
Only address parts that are marked as isaddress are localized
and returned.
"""
label_parts: List[str] = []
for line in self:
if line.isaddress and line.names:
line.local_name = locales.display_name(line.names)
if not label_parts or label_parts[-1] != line.local_name:
label_parts.append(line.local_name)
return label_parts
""" A wrapper around a list of AddressLine objects."""
@dataclasses.dataclass
@@ -189,7 +185,6 @@ class BaseResult:
admin_level: int = 15
locale_name: Optional[str] = None
display_name: Optional[str] = None
names: Optional[Dict[str, str]] = None
address: Optional[Dict[str, str]] = None
@@ -225,6 +220,35 @@ class BaseResult:
"""
return self.centroid[0]
@property
def display_name(self) -> Optional[str]:
""" Dynamically compute the display name for the result place
and, if available, its address information..
"""
if self.address_rows: # if this is true we need additional processing
label_parts: List[str] = []
for line in self.address_rows: # assume locale_name is set by external formatter
if line.isaddress and line.names:
address_name = line.display_name
if address_name and (not label_parts or label_parts[-1] != address_name):
label_parts.append(address_name)
if label_parts:
return ', '.join(label_parts)
# Now adding additional information for reranking
if self.locale_name:
return self.locale_name
elif self.names and 'name' in self.names:
return self.names['name']
elif self.names:
return next(iter(self.names.values()))
elif self.housenumber:
return self.housenumber
return None
def calculated_importance(self) -> float:
""" Get a valid importance value. This is either the stored importance
of the value or an artificial value computed from the place's
@@ -232,16 +256,6 @@ class BaseResult:
"""
return self.importance or (0.40001 - (self.rank_search/75.0))
def localize(self, locales: Locales) -> None:
""" Fill the locale_name and the display_name field for the
place and, if available, its address information.
"""
self.locale_name = locales.display_name(self.names)
if self.address_rows:
self.display_name = ', '.join(self.address_rows.localize(locales))
else:
self.display_name = self.locale_name
BaseResultT = TypeVar('BaseResultT', bound=BaseResult)
@@ -456,8 +470,6 @@ async def add_result_details(conn: SearchConnection, results: List[BaseResultT],
log().comment('Query keywords')
for result in results:
await complete_keywords(conn, result)
for result in results:
result.localize(details.locales)
def _result_row_to_address_row(row: SaRow, isaddress: Optional[bool] = None) -> AddressLine: