]> git.openstreetmap.org Git - nominatim.git/commitdiff
output names as setting
authorastridx <info@astrid-guenther.de>
Fri, 14 Mar 2025 21:03:36 +0000 (22:03 +0100)
committerastridx <info@astrid-guenther.de>
Mon, 31 Mar 2025 14:55:05 +0000 (16:55 +0200)
docs/customize/Settings.md
settings/env.defaults
src/nominatim_api/localization.py
test/python/api/test_localization.py

index 94726ca7638ee33899e3d878ded7b5837e8b24a4..edf2241b41bdc5dbaeece29b1e36938c8cc0d03b 100644 (file)
@@ -602,6 +602,43 @@ results gathered so far.
 Note that under high load you may observe that users receive different results
 than usual without seeing an error. This may cause some confusion.
 
 Note that under high load you may observe that users receive different results
 than usual without seeing an error. This may cause some confusion.
 
+#### NOMINATIM_OUTPUT_NAMES
+
+| Summary            |                                                     |
+| --------------     | --------------------------------------------------- |
+| **Description:**   | Specifies order of name tags |
+| **Format:**        | string: comma-separated list of tag names |
+| **Default:**       | name:XX,name,brand,official_name:XX,short_name:XX,official_name,short_name,ref |
+
+Specifies the order in which different name tags are used.
+The values in this list determine the preferred order of name variants,
+including language-specific names (in OSM: the name tag with and without any language suffix).
+
+Comma-separated list, where :XX stands for language suffix
+(e.g. name:en) and no :XX stands for general tags (e.g. name).
+
+See also [NOMINATIM_DEFAULT_LANGUAGE](#nominatim_default_language).
+
+!!! note
+    If NOMINATIM_OUTPUT_NAMES = `name:XX,name,short_name:XX,short_name` the search follows
+
+        ```
+        'name', 'short_name'
+        ```
+
+    if we have no preferred language order for showing search results.
+
+    For languages ['en', 'es'] the search follows
+
+        ```
+        'name:en', 'name:es',
+        'name',
+        'short_name:en', 'short_name:es',
+        'short_name'
+        ```
+
+    For those familiar with the internal implementation, the `_place_*` expansion is added, but to simplify, it is not included in this example.
+
 ### Logging Settings
 
 #### NOMINATIM_LOG_DB
 ### Logging Settings
 
 #### NOMINATIM_LOG_DB
index b8c666677ff04616252e0ecf38ba3505ce3a668b..3ebb288f515b9fdaba6b3d50c53f4d75db747f19 100644 (file)
@@ -192,6 +192,13 @@ NOMINATIM_REQUEST_TIMEOUT=60
 # to geocode" instead.
 NOMINATIM_SEARCH_WITHIN_COUNTRIES=False
 
 # to geocode" instead.
 NOMINATIM_SEARCH_WITHIN_COUNTRIES=False
 
+# Specifies the order in which different name tags are used.
+# The values in this list determine the preferred order of name variants,
+# including language-specific names.
+# Comma-separated list, where :XX stands for language-specific tags
+# (e.g. name:en) and no :XX stands for general tags (e.g. name).
+NOMINATIM_OUTPUT_NAMES=name:XX,name,brand,official_name:XX,short_name:XX,official_name,short_name,ref
+
 ### Log settings
 #
 # The following options allow to enable logging of API requests.
 ### Log settings
 #
 # The following options allow to enable logging of API requests.
index bbf9225bb22fdac08fd6217dd84b14959aa21604..3414286e6c63d24163f1f630199caa4ac48cb67e 100644 (file)
@@ -8,6 +8,7 @@
 Helper functions for localizing names of results.
 """
 from typing import Mapping, List, Optional
 Helper functions for localizing names of results.
 """
 from typing import Mapping, List, Optional
+from .config import Configuration
 
 import re
 
 
 import re
 
@@ -20,14 +21,18 @@ class Locales:
     """
 
     def __init__(self, langs: Optional[List[str]] = None):
     """
 
     def __init__(self, langs: Optional[List[str]] = None):
+        self.config = Configuration(None)
         self.languages = langs or []
         self.name_tags: List[str] = []
 
         self.languages = langs or []
         self.name_tags: List[str] = []
 
-        # Build the list of supported tags. It is currently hard-coded.
-        self._add_lang_tags('name')
-        self._add_tags('name', 'brand')
-        self._add_lang_tags('official_name', 'short_name')
-        self._add_tags('official_name', 'short_name', 'ref')
+        parts = self.config.OUTPUT_NAMES.split(',')
+
+        for part in parts:
+            part = part.strip()
+            if part.endswith(":XX"):
+                self._add_lang_tags(part[:-3])
+            else:
+                self._add_tags(part)
 
     def __bool__(self) -> bool:
         return len(self.languages) > 0
 
     def __bool__(self) -> bool:
         return len(self.languages) > 0
index 0a30cdc110b1eac1d1ec825952c6982380162f64..c3e02596b42bf3a7d174d12d0d922b874e85edc0 100644 (file)
@@ -27,6 +27,62 @@ def test_display_name_none_localized():
     assert loc.display_name({'ref': '34', 'name:de': 'DE'}) == '34'
 
 
     assert loc.display_name({'ref': '34', 'name:de': 'DE'}) == '34'
 
 
+def test_output_names_none_localized():
+    loc = Locales()
+
+    expected_tags = [
+        'name', '_place_name', 'brand', '_place_brand', 'official_name', '_place_official_name',
+        'short_name', '_place_short_name', 'ref', '_place_ref'
+    ]
+
+    assert loc.name_tags == expected_tags, f'Expected {expected_tags}, but got {loc.name_tags}'
+
+
+def test_output_names_none_localized_and_custom_output_names(monkeypatch):
+    monkeypatch.setenv(
+        'NOMINATIM_OUTPUT_NAMES',
+        'name:XX,entrance:XX,name,brand,test_tag,'
+        'official_name:XX,short_name:XX,alt_name:XX'
+    )
+    loc = Locales()
+
+    expected_tags = [
+        'name', '_place_name', 'brand', '_place_brand', 'test_tag', '_place_test_tag'
+    ]
+
+    assert loc.name_tags == expected_tags, f'Expected {expected_tags}, but got {loc.name_tags}'
+
+
+def test_output_names_none_localized_and_custom_output_names_more_than_two_changes(monkeypatch):
+    monkeypatch.setenv(
+        'NOMINATIM_OUTPUT_NAMES',
+        'name:XX,brand,test_tag:XX,official_name,short_name:XX,'
+        'alt_name,another_tag_with:XX,another_tag_withoutXX'
+    )
+    loc = Locales()
+
+    expected_tags = [
+        'brand', '_place_brand', 'official_name', '_place_official_name', 'alt_name',
+        '_place_alt_name', 'another_tag_withoutXX', '_place_another_tag_withoutXX'
+    ]
+
+    assert loc.name_tags == expected_tags, f'Expected {expected_tags}, but got {loc.name_tags}'
+
+
+def test_output_names_none_localized_and_custom_output_names_including_space(monkeypatch):
+    monkeypatch.setenv(
+        'NOMINATIM_OUTPUT_NAMES',
+        'name:XX,name ,short_name:XX, short_name'
+    )
+    loc = Locales()
+
+    expected_tags = [
+        'name', '_place_name', 'short_name', '_place_short_name'
+    ]
+
+    assert loc.name_tags == expected_tags, f'Expected {expected_tags}, but got {loc.name_tags}'
+
+
 def test_display_name_localized():
     loc = Locales(['en', 'de'])
 
 def test_display_name_localized():
     loc = Locales(['en', 'de'])
 
@@ -35,6 +91,146 @@ def test_display_name_localized():
     assert loc.display_name({'ref': '34', 'name:de': 'DE'}) == 'DE'
 
 
     assert loc.display_name({'ref': '34', 'name:de': 'DE'}) == 'DE'
 
 
+def test_output_names_localized():
+    loc = Locales(['en', 'es'])
+
+    expected_tags = [
+        'name:en', '_place_name:en', 'name:es', '_place_name:es', 'name', '_place_name', 'brand',
+        '_place_brand', 'official_name:en', '_place_official_name:en', 'official_name:es',
+        '_place_official_name:es', 'short_name:en', '_place_short_name:en', 'short_name:es',
+        '_place_short_name:es', 'official_name', '_place_official_name', 'short_name',
+        '_place_short_name', 'ref', '_place_ref'
+    ]
+
+    assert loc.name_tags == expected_tags, f'Expected {expected_tags}, but got {loc.name_tags}'
+
+
+def test_output_names_localized_and_custom_output_names_including_space(monkeypatch):
+    monkeypatch.setenv(
+        'NOMINATIM_OUTPUT_NAMES',
+        'name:XX,name ,short_name:XX, short_name'
+    )
+    loc = Locales(['en', 'es'])
+
+    expected_tags = [
+        'name:en', '_place_name:en', 'name:es', '_place_name:es',
+        'name', '_place_name',
+        'short_name:en', '_place_short_name:en', 'short_name:es', '_place_short_name:es',
+        'short_name', '_place_short_name'
+    ]
+
+    assert loc.name_tags == expected_tags, f'Expected {expected_tags}, but got {loc.name_tags}'
+
+
+def test_output_names_localized_and_custom_output_names(monkeypatch):
+    monkeypatch.setenv(
+        'NOMINATIM_OUTPUT_NAMES',
+        'name:XX,entrance:XX,name,brand,test_tag,official_name:XX,short_name:XX,alt_name:XX'
+    )
+    loc = Locales(['en', 'es'])
+
+    expected_tags = [
+        'name:en', '_place_name:en', 'name:es', '_place_name:es', 'entrance:en',
+        '_place_entrance:en', 'entrance:es', '_place_entrance:es', 'name', '_place_name',
+        'brand', '_place_brand', 'test_tag', '_place_test_tag', 'official_name:en',
+        '_place_official_name:en', 'official_name:es', '_place_official_name:es',
+        'short_name:en', '_place_short_name:en', 'short_name:es', '_place_short_name:es',
+        'alt_name:en', '_place_alt_name:en', 'alt_name:es', '_place_alt_name:es'
+    ]
+
+    assert loc.name_tags == expected_tags, f'Expected {expected_tags}, but got {loc.name_tags}'
+
+
+def test_output_names_localized_and_custom_output_names_start_with_tag_that_has_no_XX(monkeypatch):
+    monkeypatch.setenv(
+        'NOMINATIM_OUTPUT_NAMES',
+        'name,brand,test_tag,official_name:XX,short_name:XX,alt_name:XX'
+    )
+    loc = Locales(['en', 'es'])
+
+    expected_tags = [
+        'name', '_place_name', 'brand', '_place_brand', 'test_tag', '_place_test_tag',
+        'official_name:en', '_place_official_name:en', 'official_name:es',
+        '_place_official_name:es', 'short_name:en', '_place_short_name:en', 'short_name:es',
+        '_place_short_name:es', 'alt_name:en', '_place_alt_name:en', 'alt_name:es',
+        '_place_alt_name:es'
+    ]
+
+    assert loc.name_tags == expected_tags, f'Expected {expected_tags}, but got {loc.name_tags}'
+
+
+def test_output_names_localized_and_custom_output_names_no_named_tags(monkeypatch):
+    monkeypatch.setenv(
+        'NOMINATIM_OUTPUT_NAMES',
+        'name,brand,test_tag'
+    )
+    loc = Locales(['en', 'es'])
+
+    expected_tags = [
+        'name', '_place_name', 'brand', '_place_brand', 'test_tag', '_place_test_tag'
+    ]
+
+    assert loc.name_tags == expected_tags, f'Expected {expected_tags}, but got {loc.name_tags}'
+
+
+def test_output_names_localized_and_custom_output_names_only_named_tags(monkeypatch):
+    monkeypatch.setenv(
+        'NOMINATIM_OUTPUT_NAMES',
+        'name:XX,entrance:XX,official_name:XX,short_name:XX,alt_name:XX'
+    )
+    loc = Locales(['en', 'es'])
+
+    expected_tags = [
+        'name:en', '_place_name:en', 'name:es', '_place_name:es', 'entrance:en',
+        '_place_entrance:en', 'entrance:es', '_place_entrance:es', 'official_name:en',
+        '_place_official_name:en', 'official_name:es', '_place_official_name:es',
+        'short_name:en', '_place_short_name:en', 'short_name:es', '_place_short_name:es',
+        'alt_name:en', '_place_alt_name:en', 'alt_name:es', '_place_alt_name:es'
+    ]
+
+    assert loc.name_tags == expected_tags, f'Expected {expected_tags}, but got {loc.name_tags}'
+
+
+def test_output_names_localized_and_custom_output_names_more_than_two_changes(monkeypatch):
+    monkeypatch.setenv(
+        'NOMINATIM_OUTPUT_NAMES',
+        'name:XX,brand,test_tag:XX,official_name,short_name:XX,'
+        'alt_name,another_tag_with:XX,another_tag_withoutXX'
+    )
+    loc = Locales(['en', 'es'])
+
+    expected_tags = [
+        'name:en', '_place_name:en', 'name:es', '_place_name:es', 'brand', '_place_brand',
+        'test_tag:en', '_place_test_tag:en', 'test_tag:es', '_place_test_tag:es', 'official_name',
+        '_place_official_name', 'short_name:en', '_place_short_name:en', 'short_name:es',
+        '_place_short_name:es', 'alt_name', '_place_alt_name', 'another_tag_with:en',
+        '_place_another_tag_with:en', 'another_tag_with:es', '_place_another_tag_with:es',
+        'another_tag_withoutXX', '_place_another_tag_withoutXX'
+    ]
+
+    assert loc.name_tags == expected_tags, f'Expected {expected_tags}, but got {loc.name_tags}'
+
+
+def test_output_names_localized_and_custom_output_names_XX_in_the_middle(monkeypatch):
+    monkeypatch.setenv(
+        'NOMINATIM_OUTPUT_NAMES',
+        'name:XX,br:XXand,test_tag:XX,official_name,sh:XXort_name:XX,'
+        'alt_name,another_tag_with:XX,another_tag_withoutXX'
+    )
+    loc = Locales(['en', 'es'])
+
+    expected_tags = [
+        'name:en', '_place_name:en', 'name:es', '_place_name:es', 'br:XXand', '_place_br:XXand',
+        'test_tag:en', '_place_test_tag:en', 'test_tag:es', '_place_test_tag:es', 'official_name',
+        '_place_official_name', 'sh:XXort_name:en', '_place_sh:XXort_name:en', 'sh:XXort_name:es',
+        '_place_sh:XXort_name:es', 'alt_name', '_place_alt_name', 'another_tag_with:en',
+        '_place_another_tag_with:en', 'another_tag_with:es', '_place_another_tag_with:es',
+        'another_tag_withoutXX', '_place_another_tag_withoutXX'
+    ]
+
+    assert loc.name_tags == expected_tags, f'Expected {expected_tags}, but got {loc.name_tags}'
+
+
 def test_display_name_preference():
     loc = Locales(['en', 'de'])
 
 def test_display_name_preference():
     loc = Locales(['en', 'de'])