Skip to content

[Fixes #13646] : Use multilang entries in ISO19115#14421

Open
nrjadkry wants to merge 3 commits into
masterfrom
ISSUE_13646
Open

[Fixes #13646] : Use multilang entries in ISO19115#14421
nrjadkry wants to merge 3 commits into
masterfrom
ISSUE_13646

Conversation

@nrjadkry

@nrjadkry nrjadkry commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Fixes #13646

Checklist

Reviewing is a process done by project maintainers, mostly on a volunteer basis. We try to keep the overhead as small as possible and appreciate if you help us to do so by completing the following items. Feel free to ask in a comment if you have troubles with any of them.

For all pull requests:

  • Confirm you have read the contribution guidelines
  • You have sent a Contribution Licence Agreement (CLA) as necessary (not required for small changes, e.g., fixing typos in the documentation)
  • Make sure the first PR targets the master branch, eventual backports will be managed later. This can be ignored if the PR is fixing an issue that only happens in a specific branch, but not in newer ones.

The following are required only for core and extension modules (they are welcomed, but not required, for contrib modules):

  • There is a ticket in https://github.com/GeoNode/geonode/issues describing the issue/improvement/feature (a notable exemption is, changes not visible to end-users)
  • The issue connected to the PR must have Labels and Milestone assigned
  • PR for bug fixes and small new features are presented as a single commit
  • PR title must be in the form "[Fixes #<issue_number>] Title of the PR"
  • New unit tests have been added covering the changes, unless there is an explanation on why the tests are not necessary/implemented

Submitting the PR does not require you to check all items, but by the time it gets merged, they should be either satisfied or inapplicable.

@cla-bot cla-bot Bot added the cla-signed CLA Bot: community license agreement signed label Jul 10, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces multilingual support for metadata fields in GeoNode by adding new template tags, utility functions for ISO 639-1/639-2 language code mapping, and updating the XML metadata templates. The reviewer identified several potential runtime errors related to input validation, data structure handling, and dictionary iteration in the new template tags and utility functions, providing specific code improvements to address these issues.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread geonode/catalogue/templatetags/multilang.py
Comment thread geonode/catalogue/templatetags/multilang.py
Comment thread geonode/catalogue/templatetags/multilang.py
Comment thread geonode/metadata/multilang/utils.py Outdated
Comment thread geonode/metadata/multilang/utils.py
@nrjadkry nrjadkry requested a review from etj July 10, 2026 07:34
@etj etj assigned nrjadkry and unassigned etj Jul 13, 2026
@nrjadkry nrjadkry requested a review from etj July 14, 2026 05:57
@etj etj requested a review from Copilot July 14, 2026 08:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR implements ISO19115 multilingual metadata rendering by adding Django template tags to surface multilang values and language descriptors, updating the default ISO19115 XML template to emit gmd:PT_FreeText/gmd:locale structures, and introducing ISO 639-1 ⇄ ISO 639-2 mapping helpers.

Changes:

  • Add multilang templatetags to expose multilang field checks, translations, and locale descriptors for ISO19115 templates.
  • Introduce ISO 639-1/639-2 mapping utilities and a default LANGUAGE_MAPPINGS setting.
  • Update the default ISO19115 template to output gmd:LanguageCode, gmd:locale, and gmd:PT_FreeText for title and abstract, plus add unit tests for the new tags.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
geonode/settings.py Adds LANGUAGE_MAPPINGS to support ISO639-1 ⇄ ISO639-2 conversions for ISO19115 output.
geonode/metadata/multilang/utils.py Adds mapping helper functions get_3_from_2() / get_2_from_3() used by the ISO template tags.
geonode/catalogue/templatetags/multilang.py Introduces template tags/filters to detect multilingual fields, extract translations, and build ISO locale descriptors.
geonode/catalogue/templatetags/init.py Adds the templatetags package initializer required for Django to discover the library.
geonode/catalogue/templates/catalogue/full_metadata.xml Updates ISO19115 output to use gmd:LanguageCode, emit gmd:locale blocks, and render PT_FreeText for title/abstract.
geonode/catalogue/tests.py Adds unit tests for the new multilang template tags.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +122 to +145
@register.simple_tag(name="language_info")
def language_info(lang_3):
"""
Return information about a single ISO639-2 language.
"""
if not lang_3:
lang_2 = get_default_language()
lang_3 = get_3_from_2(lang_2) or "eng"
elif len(lang_3) == 2:
lang_2 = lang_3
lang_3 = get_3_from_2(lang_2) or "eng"
else:
lang_2 = get_2_from_3(lang_3)

if lang_2 is None:
logger.warning(
"No ISO639-1 language found for '%s'; using the ISO639-2 code as the label.",
lang_3,
)

return _language_descriptor(
lang_2,
lang_3,
)
Comment thread geonode/settings.py
Comment on lines +2155 to +2179
("de", ["ger", "deu"]),
("en", "eng"),
("es", "spa"),
("fr", ["fre", "fra"]),
("it", "ita"),
("km", "khm"),
("nl", ["dut", "nld"]),
("ne", "nep"),
("fa", ["per", "fas"]),
("pl", "pol"),
("pt", "por"),
("ru", "rus"),
("si", "sin"),
("sw", "swa"),
("sv", "swe"),
("tl", "tgl"),
("ta", "tam"),
("uk", "ukr"),
("vi", "vie"),
("el", ["gre", "ell"]),
("th", "tha"),
("zh", ["chi", "zho"]),
("ja", "jpn"),
("ko", "kor"),
("sk", ["slo", "slk"]),
Comment on lines +331 to +339
@override_settings(LANGUAGE_MAPPINGS=(("en", "eng"), ("it", "ita")))
def test_languages_info_missing_iso_mapping_is_skipped_and_warns(self):
metadata = {
"title": "Hello",
"title_multilang_it": "Ciao",
"title_multilang_de": "Hallo",
}
result = tags.languages_info(metadata)
self.assertEqual(["locale-it"], [d["id"] for d in result])
Comment on lines +1 to +18
#########################################################################
#
# Copyright (C) 2026 OSGeo
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
#########################################################################

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

pls leave the __init__ empty

Comment on lines +89 to +90
Return all translations for a multilingual field except the default
language.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Add the default as well.
The ISO XML will match exactly the model, for instance, for title:

  • metadata["title"] -> element title
  • for each metadata["title_multilang_xx"] -> element PT_FreeText/TextGroup

Comment on lines +80 to +84
@register.filter(name="is_multilang")
def is_multilang(field_name):
"""Return whether the field is configured as multilingual."""
return _is_multilang_field(field_name)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

why do we have both

_is_multilang_field(field_name):

and

is_multilang_field(field_name):

which calls the 1-line _is_multilang_field function?

Comment on lines +92 to +118
if not isinstance(metadata, dict) or not _is_multilang_field(field_name):
return []

default_language = get_default_language()
translations = []
included_languages = set()

for language_code in get_2letters_languages():
if language_code == default_language or language_code in included_languages:
continue

included_languages.add(language_code)

translated_text = _translation_value(
metadata,
field_name,
language_code,
)

if translated_text:
translations.append(
{
"locale": language_code,
"text": translated_text,
}
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This whole stuff becomes much simpler if you use 'get_multilang_field_names(base_name)`

Comment thread geonode/settings.py
Comment on lines +2148 to +2179
LANGUAGE_MAPPINGS = (
("af", "afr"),
("sq", "alb"),
("am", "amh"),
("ar", "ara"),
("id", "ind"),
("bn", "ben"),
("de", ["ger", "deu"]),
("en", "eng"),
("es", "spa"),
("fr", ["fre", "fra"]),
("it", "ita"),
("km", "khm"),
("nl", ["dut", "nld"]),
("ne", "nep"),
("fa", ["per", "fas"]),
("pl", "pol"),
("pt", "por"),
("ru", "rus"),
("si", "sin"),
("sw", "swa"),
("sv", "swe"),
("tl", "tgl"),
("ta", "tam"),
("uk", "ukr"),
("vi", "vie"),
("el", ["gre", "ell"]),
("th", "tha"),
("zh", ["chi", "zho"]),
("ja", "jpn"),
("ko", "kor"),
("sk", ["slo", "slk"]),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Too many entries here, your code loops on all of them also if they are not needed.
Better to define a really small set of mappings, and let the projects add more if they need.

Comment on lines +162 to +174
for language_code in get_2letters_languages():
if language_code == default_language or language_code in included_languages:
continue

included_languages.add(language_code)

if not any(
_has_translation(metadata, field_name, language_code)
for field_name in multilingual_fields
if _is_multilang_field(field_name)
):
continue

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please consider using 'get_all_multilang_fields()`

if isinstance(mappings, dict):
mappings = mappings.items()

for lang_2, lang_3_codes in mappings:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This loop will be executed over and over.
Pls consider simplifiyng it by having less entries in the mappings or caching a reversed code3->code2 mapping.
Also, we don't need a mapping for languages not set in the LANGUAGES setting.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-signed CLA Bot: community license agreement signed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Multilang: use multilang entries in ISO19115

3 participants