No problem at all. Meanwhile I put it up for discussion at Belchertown on
github. And I worked further on it, and I learnt a lot more how WeeWX
works. So it is simpler now: preceeding a label tag by $locale, only. For
example: $locale.obs.label.outTemp.
I wish you to find all the bug that are still there easily.
Tom Keffer schrieb am Samstag, 27. März 2021 um 14:13:42 UTC+1:
> Karen,
>
> I have not forgotten you. I'm in the midst of the V4.5 deployment, but
> should have some time later to take a look at this.
>
> On Wed, Mar 24, 2021 at 12:42 PM Karen K <[email protected]> wrote:
>
>> Localization seems difficult to me in the moment. It works like that that
>> the user defines a lot of entries in weewx.conf or skin.conf.
>>
>> The disadvantages are to me:
>>
>> - It makes the configuration files very long.
>> - Changing localization involes changing lots of entries.
>> - Skins cannot come with multiple localizations included.
>>
>> So I thought about it for a long time, and I gave it a try. I want to
>> present a possible solution.
>>
>> To simplify the process a localization file is used that is referenced in
>> weewx.conf. To change localization simply the file name is changed. Then
>> the values come from another file. Each localization file has the same
>> structure as skin.conf. If the localization file has no entry for a label
>> the value from weewx.conf or skin.conf is returned instead.
>>
>> Usage is simple: for example $locale('obs.label.outTemp') looks for an
>> entry 'outTemp' in section [Labels][[Generic]] in the localization file. It
>> it is there, its value is returned. Otherwise the value from skin_dict is
>> returned.
>>
>> Advantages are:
>>
>> - Label value texts can be "outsourced" to separate files.
>> - Localization can be changed by changing one single entry.
>> - Skin authors can ship their packages with multiple localizations
>> included.
>> - No breaking changes.
>>
>> An alternative to that solution would be to include the localization
>> lookup into the standard $obs.label... or $Extras... lookup.
>>
>> --
>> You received this message because you are subscribed to the Google Groups
>> "weewx-development" group.
>> To unsubscribe from this group and stop receiving emails from it, send an
>> email to [email protected].
>> To view this discussion on the web visit
>> https://groups.google.com/d/msgid/weewx-development/259e43e6-efe9-467e-9949-862dda0f13c8n%40googlegroups.com
>>
>> <https://groups.google.com/d/msgid/weewx-development/259e43e6-efe9-467e-9949-862dda0f13c8n%40googlegroups.com?utm_medium=email&utm_source=footer>
>> .
>>
>
--
You received this message because you are subscribed to the Google Groups
"weewx-development" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/weewx-development/051115b5-4e1b-48e3-82c6-e17cc0ec1682n%40googlegroups.com.
# Copyright (C) 2021 Johanna Roedenbeck
"""
SearchList extension to simplify localization
advantages:
* change one entry only to change localization
* skin author can provide multiple languages within installation package
usage:
Instead of using label tags directly, they are used as parameter to
the $locale() tag. $locale() provides a language dependent label
string if some is available und otherwise the entry from weewx.conf
or skin.conf
Example:
$locale('obs.label.outTemp')
The extension looks in the appropriate localization file for an entry.
If it finds one it returns it. If it finds no entry the value from
skin_dict (weewx.conf or skin.conf) is returned.
The extension needs an additional entry called 'lang_file' directly
in the skin section of weewx.conf. It points to the localization
file to use. Relative paths are relative to the skin directory.
The localization file has the same structure as the skin.conf file.
Entries that depend on language need to be defined there, only.
Other entries remain defined in weewx.conf or skin.conf
The localization file is not restricted to "Extras" and "Labels"
section, but can process all sections of the file.
"""
from weewx.cheetahgenerator import SearchList
from weewx.units import ValueTuple,ValueHelper
import configobj
import weeutil.weeutil
import weeutil.config
import os
import os.path
try:
# Test for new-style weewx v4 logging by trying to import weeutil.logger
import weeutil.logger
import logging
log = logging.getLogger(__name__)
def logdbg(msg):
log.debug(msg)
def loginf(msg):
log.info(msg)
def logerr(msg):
log.error(msg)
except ImportError:
# Old-style weewx logging
import syslog
def logmsg(level, msg):
syslog.syslog(level, 'skin_locale: %s' % msg)
def logdbg(msg):
logmsg(syslog.LOG_DEBUG, msg)
def loginf(msg):
logmsg(syslog.LOG_INFO, msg)
def logerr(msg):
logmsg(syslog.LOG_ERR, msg)
class SkinLocaleSearchList(SearchList):
def __init__(self,generator):
"""Create an instance of the class"""
super(SkinLocaleSearchList,self).__init__(generator)
# get language file name
try:
lang_file = self.generator.skin_dict['lang_file']
except (KeyError,IndexError):
lang_file = ""
# if path is relative get skin directory
try:
ww=self.generator.config_dict['WEEWX_ROOT']
xx=self.generator.config_dict['StdReport']['SKIN_ROOT']
yy=self.generator.skin_dict['skin']
zz=os.path.join(ww,xx,yy)
except (KeyError,IndexError):
zz = ''
report=self.generator.skin_dict['REPORT_NAME']
# get dict from language file or empty dict if not defined
if lang_file is not None and lang_file:
lang_file=os.path.join(zz,lang_file)
try:
d=configobj.ConfigObj(lang_file)
loginf("Using localization file %s for report '%s'" %
(lang_file,report))
except IOError as e:
logerr("Failed to read localization file %s for report '%s': %s" %
(lang_file,report,e))
except SyntaxError as e:
logerr("Failed to read localization file %s for report '%s': %s" %
(lang_file,report,e))
else:
d={}
# localization dict
self.lang_dict=d
# localization of [Extras] section
self.extras_dict=weeutil.config.deep_copy(self.generator.skin_dict['Extras'] if 'Extras' in self.generator.skin_dict else {})
if 'Extras' in d:
weeutil.config.merge_config(self.extras_dict,d['Extras'])
if 'Extras' in self.generator.config_dict['StdReport'][report]:
weeutil.config.merge_config(self.extras_dict,self.generator.config_dict['StdReport'][report]['Extras'])
# localization of [Labels] section
self.labels_dict=weeutil.config.deep_copy(self.generator.skin_dict['Labels'] if 'Labels' in self.generator.skin_dict else {})
if 'Labels' in d:
weeutil.config.merge_config(self.labels_dict,d['Labels'])
if 'Labels' in self.generator.config_dict['StdReport'][report]:
weeutil.config.merge_config(self.labels_dict,self.generator.config_dict['StdReport'][report]['Labels'])
def nested(self,search_dict,keys):
""" get value from structured dict
search_dict: contents of the localization file as dict
keys: array of keys
"""
try:
x=search_dict[keys[0]]
except (KeyError,IndexError):
return None
# if no more keys or result is no dict anymore
if len(keys)==1 or not isinstance(x,dict):
return x
# next level
return self.nested(x,keys[1:])
def get_extension_list(self,timespan,db_lookup):
def locale_label(tmpl='',tag=''):
""" $locale()
tmpl: file to be created by Cheetah
tag: tag like $obs.label... or $Extras...
"""
d=self.lang_dict
if tmpl:
# get the appropriate section for page
try:
d = d['Templates'][tmpl]
if not isinstance(d,dict):
d={tmpl:d}
except (KeyError,IndexError,ValueError,TypeError):
d = {}
if tag:
# tag is not empty
key=tag.split('.')
# obs.label.observation_type is in ['Label']['Generic']
if len(key)>2 and key[0]=='obs' and key[1]=='label':
key[0]='Labels'
key[1]='Generic'
# get value from localization dict
val = self.nested(d,key)
# if no value is defined there, fall back to skin_dict
if val is None:
try:
if key[0].lower()=='extras':
val = self.generator.skin_dict['Extras'][key[1]]
elif key[0].lower()=='labels' and key[1].lower()=='generic':
val = self.labels_dict['Generic'][key[2]]
elif key[0].lower()=='almanac':
val = self.generator.skin_dict['Almanac'][key[1]]
else:
val = ""
except (KeyError,IndexError,ValueError,TypeError):
val = ""
return val
if tmpl:
cheetah_dict={}
if 'CheetahGenerator' in self.generator.skin_dict:
for i in self.generator.skin_dict['CheetahGenerator']:
if tmpl in i:
cheetah_dict=i[tmpl]
break
#
return FilesBinder(tmpl,d,self.labels_dict['Generic'] if 'Generic' in self.labels_dict else {},cheetah_dict)
else:
return LocaleBinder(self.lang_dict,self.labels_dict,self.extras_dict)
return [{'locale':locale_label}]
class FilesBinder(object):
""" special labels for a specific page to be created by Cheetah """
def __init__(self,page,lang_files_dict,skin_labels_dict,cheetah_dict):
self.page=page
self.lang_files_dict=lang_files_dict
self.skin_labels_dict=skin_labels_dict
self.cheetah_dict=cheetah_dict
def __getattr__(self,attr):
if attr in self.lang_files_dict:
# entry found in localization file
return self.lang_files_dict[attr]
elif attr in self.cheetah_dict:
# entry found [CheetahGenerator] subsection
return self.cheetah_dict[attr]
elif attr=='nav':
# if $locale(file='xxx').nav not in localization file, try
# nav_xxx in [Labels][[Generic]] section of skin.conf
if 'nav_'+self.page in self.skin_labels_dict:
return self.skin_labels_dict['nav_'+self.page]
elif attr=='page_header':
# if $locale(file='xxx').page_header not in localization file,
# try xxx_page_header in [Labels][[Generic]] section of skin.conf
x=self.page+'_page_header'
if x in self.skin_labels_dict:
return self.skin_labels_dict[x]
elif attr in self.skin_labels_dict:
# finally look in [Labels][[Generic]] section if skin.conf
#if attr=='html_description':
# loginf("html_description: %s" % self.skin_labels_dict[attr])
return str(self.skin_labels_dict[attr])
return '$%s.%s' % (self.page,attr)
class LocaleBinder(object):
def __init__(self,lang_dict,labels_dict,extras_dict):
self.lang_dict=lang_dict
self.labels_dict=labels_dict
self.Extras=extras_dict
self.obs=LocaleObsBinder(labels_dict)
class LocaleObsBinder(object):
def __init__(self,labels_dict):
self.label=labels_dict['Generic'] if 'Generic' in labels_dict else {}