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.
# 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 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 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):
    
        # 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 = ''
        
        # 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)
            d=configobj.ConfigObj(lang_file)
        else:
            d={}
            
        # localization dict
        self.lang_dict=d
        
        def locale_label(expression):
            key=expression.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(self.lang_dict,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.generator.skin_dict['Labels']['Generic'][key[2]]
                    elif key[0].lower()=='almanac':
                        val = self.generator.skin_dict['Almanac'][key[1]]
                    else:
                        val = ""
                except (KeyError,IndexError,ValueError):
                    val = ""
            return val
                    
        return [{'locale':locale_label}]
        

Reply via email to