Skip to content

Commit a87bff6

Browse files
committed
Read project translations from QGIS Server via GetTranslations
Ensures embedded project layer translations are also properly handled
1 parent 9350261 commit a87bff6

File tree

4 files changed

+51
-86
lines changed

4 files changed

+51
-86
lines changed

src/config_generator/capabilities_reader.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from urllib.parse import urljoin, urlparse
33
from xml.etree import ElementTree
44

5+
import json
56
import os
67
import posixpath
78
import re
@@ -622,3 +623,36 @@ def collect_wfs_layers_attributes(self, full_url):
622623
)
623624
self.logger.debug(traceback.format_exc())
624625
return {}
626+
627+
def read_project_translations(self, service_name, viewer_languages):
628+
""" Read translations from QGIS Server.
629+
:param str service_name: service name
630+
:param list viewer_languages: viewer languages
631+
632+
"""
633+
all_translations = {}
634+
for language in viewer_languages:
635+
try:
636+
full_url = urljoin(
637+
self.default_qgis_server_url,
638+
posixpath.join(self.qgis_server_url_tenant_suffix, service_name)
639+
)
640+
params = {
641+
'SERVICE': 'GetTranslations',
642+
'LANG': language
643+
}
644+
document = self.fetch_cached(full_url, params, "GetTranslations_" + language, "GetTranslations " + language, True)
645+
if not document:
646+
return {}
647+
all_translations[language] = json.loads(document)
648+
if all_translations[language]:
649+
self.logger.info("Read " + language + " translations for " + service_name)
650+
651+
except Exception as e:
652+
self.logger.error(
653+
"Could not parse GetTranslations from %s:\n%s" %
654+
(full_url, e)
655+
)
656+
self.logger.debug(traceback.format_exc())
657+
658+
return all_translations

src/config_generator/map_viewer_config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,7 @@ def theme_item(self, cfg_item, themes_config, assets_dir, autogenExternalLayers,
446446
item['keywords'] = cap.get('keywords', '')
447447
item['onlineResource'] = cap.get('onlineResource', '')
448448
item['contact'] = cap.get('contact', {})
449-
item['translations'] = project_metadata['translations']
449+
item['translations'] = self.themes_reader.project_translations(service_name)
450450

451451

452452
projectCrs = project_metadata['project_crs']

src/config_generator/qgs_reader.py

Lines changed: 1 addition & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -38,32 +38,24 @@ def deep_merge(d1, d2):
3838
class QGSReader:
3939
""" Read QGIS projects and extract data for QWC config """
4040

41-
def __init__(self, config, logger, assets_dir, translations_dir, use_cached_project_metadata, global_print_layouts):
41+
def __init__(self, config, logger, assets_dir, use_cached_project_metadata, global_print_layouts):
4242
"""Constructor
4343
4444
:param obj config: Config generator config
4545
:param Logger logger: Application logger
4646
:param string assets_dir: Assets directory
47-
:param str translations_dir: Viewer translations directory
4847
:param bool use_cached_project_metadata: Whether to use cached project metadata
4948
:param list global_print_layouts: Global print layouts
5049
"""
5150
self.config = config
5251
self.logger = logger
5352
self.assets_dir = assets_dir
54-
self.translations_dir = translations_dir
5553
self.use_cached_project_metadata = use_cached_project_metadata
5654
self.global_print_layouts = global_print_layouts
5755

5856
self.qgs_resources_path = config.get('qgis_projects_base_dir', '/tmp/')
5957
self.qgs_ext = config.get('qgis_project_extension', '.qgs')
6058
self.nested_nrels = config.get('generate_nested_nrel_forms', False)
61-
try:
62-
with open(os.path.join(self.translations_dir, 'tsconfig.json')) as fh:
63-
self.viewer_languages = json.load(fh)['languages']
64-
except:
65-
self.logger.warning("Failed to detect viewer languages from tsconfig.json")
66-
self.viewer_languages = ["en-US"]
6759

6860
self.db_engine = DatabaseEngine()
6961

@@ -147,20 +139,15 @@ def read(self, map_prefix, theme_item, edit_datasets):
147139

148140
# Build layername -> shortname lookup
149141
shortname_map = {}
150-
id_name_map = {}
151142
for maplayer in root.findall('.//maplayer'):
152143
layernameEl = maplayer.find('layername')
153144
if layernameEl is not None:
154145
shortnameEl = maplayer.find('shortname')
155-
idEl = maplayer.find('id')
156146
shortname = shortnameEl.text if shortnameEl is not None else layernameEl.text
157147
shortname_map[layernameEl.text] = shortname
158-
if idEl is not None:
159-
id_name_map[idEl.text] = shortname
160148

161149
return {
162150
"project_crs": self.__project_crs(root),
163-
"translations": self.__theme_translations(qgs_dir, projectname, id_name_map),
164151
"print_templates": self.__print_templates(root, shortname_map),
165152
"visibility_presets": self.__visibility_presets(root),
166153
"layer_metadata": self.__layer_metadata(root, shortname_map, map_prefix, edit_datasets, theme_item, qgs_dir),
@@ -172,76 +159,6 @@ def __project_crs(self, root):
172159
authid = root.find('./projectCrs/spatialrefsys/authid')
173160
return authid.text if authid is not None else None
174161

175-
def __theme_translations(self, qgs_dir, projectname, id_name_map):
176-
""" Read theme portion of translations from <projectname>_<lang>.json. """
177-
all_translations = {}
178-
179-
for language in self.viewer_languages:
180-
translations = {}
181-
182-
ts_file = os.path.join(qgs_dir, f"{projectname}_{language}.ts")
183-
if not os.path.exists(ts_file):
184-
ts_file = os.path.join(qgs_dir, f"{projectname}_{language[0:2]}.ts")
185-
if os.path.exists(ts_file):
186-
self.logger.info('Reading project translations %s' % ts_file)
187-
try:
188-
ts_document = ElementTree.parse(ts_file)
189-
190-
# Build translation string lookup
191-
for context in ts_document.findall("./context"):
192-
context_name = context.find('./name')
193-
if context_name is None:
194-
continue
195-
196-
context_name_parts = context_name.text.split(":")
197-
key = None
198-
if len(context_name_parts) >= 3 and context_name_parts[0] == "project" and context_name_parts[1] == "layers":
199-
# replace layer id with layer name
200-
layername = id_name_map[context_name_parts[2]]
201-
202-
if len(context_name_parts) == 3:
203-
ts_path = f"layertree"
204-
key = layername
205-
elif len(context_name_parts) == 4 and context_name_parts[3] == "fieldaliases":
206-
ts_path = f"layers.{layername}.fields"
207-
elif len(context_name_parts) == 4 and context_name_parts[3] == "formcontainers":
208-
ts_path = f"layers.{layername}.form"
209-
elif len(context_name_parts) == 2 and context_name_parts[0] == "project" and context_name_parts[1] == "layergroups":
210-
ts_path = f"layertree"
211-
else:
212-
# Unknown ts context
213-
continue
214-
215-
context_ts = translations
216-
for entry in ts_path.split("."):
217-
context_ts[entry] = context_ts.get(entry, {})
218-
context_ts = context_ts[entry]
219-
220-
for message in context.findall("./message"):
221-
source = message.find('./source')
222-
translation = message.find('./translation')
223-
if source is not None and translation is not None and translation.get('type', '') != "unfinished":
224-
context_ts[key or source.text] = translation.text
225-
226-
except Exception as e:
227-
self.logger.info('Failed to auxiliary project translations %s: %s' % (ts_file, str(e)))
228-
229-
json_file = os.path.join(qgs_dir, f"{projectname}_{language}.json")
230-
if not os.path.exists(json_file):
231-
json_file = os.path.join(qgs_dir, f"{projectname}_{language[0:2]}.json")
232-
if os.path.exists(json_file):
233-
self.logger.info('Reading auxiliary project translations %s' % json_file)
234-
try:
235-
with open(json_file) as fh:
236-
translations = deep_merge(translations, json.load(fh))
237-
except Exception as e:
238-
self.logger.info('Failed to read auxiliary project translations %s: %s' % (json_file, str(e)))
239-
240-
if translations:
241-
all_translations[language] = translations
242-
243-
return all_translations
244-
245162
def __print_templates(self, root, shortname_map):
246163
""" Collect print templates from QGS and merge with global print layouts. """
247164

src/config_generator/theme_reader.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import json
12
import os
23
from collections import OrderedDict
34
from pathlib import Path
@@ -36,8 +37,15 @@ def __init__(self, config, logger, config_models, themes_config, assets_dir, tra
3637

3738
global_print_layouts = self.__search_global_print_layouts()
3839

40+
try:
41+
with open(os.path.join(translations_dir, 'tsconfig.json')) as fh:
42+
self.viewer_languages = json.load(fh)['languages']
43+
except:
44+
self.logger.warning("Failed to detect viewer languages from tsconfig.json")
45+
self.viewer_languages = ["en-US"]
46+
3947
self.capabilities_reader = CapabilitiesReader(config, logger, use_cached_project_metadata, cache_dir)
40-
self.qgs_reader = QGSReader(config, logger, assets_dir, translations_dir, use_cached_project_metadata, global_print_layouts)
48+
self.qgs_reader = QGSReader(config, logger, assets_dir, use_cached_project_metadata, global_print_layouts)
4149

4250
self.default_qgis_server_url = config.get(
4351
'default_qgis_server_url', 'http://localhost:8001/ows/'
@@ -145,13 +153,15 @@ def __read_metadata_for_theme(self, item):
145153

146154
wms_capabilities = self.capabilities_reader.read_wms_service_capabilities(service_name, item, self.themes_config)
147155
wfs_capabilities = self.capabilities_reader.read_wfs_service_capabilities(service_name, item)
156+
project_translations = self.capabilities_reader.read_project_translations(service_name, self.viewer_languages)
148157
project_metadata = self.qgs_reader.read(service_name, item, self.__get_edit_datasets(service_name))
149158

150159
self.theme_metadata[service_name] = {
151160
'service_name': service_name,
152161
'url': url,
153162
'wms_capabilities': wms_capabilities,
154163
'wfs_capabilities': wfs_capabilities,
164+
'project_translations': project_translations,
155165
'project_metadata': project_metadata
156166
}
157167

@@ -207,6 +217,10 @@ def wfs_capabilities(self, service_name):
207217
""" Return the WFS servcice capabilities for the specified OWS service. """
208218
return self.theme_metadata.get(service_name, {}).get('wfs_capabilities', {})
209219

220+
def project_translations(self, service_name):
221+
""" Return the QGS project translations for the specified OWS service. """
222+
return self.theme_metadata.get(service_name, {}).get('project_translations', {})
223+
210224
def project_metadata(self, service_name):
211225
""" Return the QGS project metadata for the specified OWS service. """
212226
return self.theme_metadata.get(service_name, {}).get('project_metadata', {})

0 commit comments

Comments
 (0)