📜 ⬆️ ⬇️

Django do-it-yourself part 2: Internationalization

Part 1.

Introduction


Initially, I wanted to write more about mentioning the routes and fighting errors in the article, but the topic of internationalization turned out to be quite extensive, so it was decided to dedicate the post exclusively to it. We will use the common gettext tool within the framework of the framework.

All translations can be divided into two parts:
a) Those directly in files with the .py extension - everything is simple with them.
b) And those in the templates - they will have to write a small bicycle :).
Well, then you need to automate the process of managing translations.
So welcome under cat.
')

Translation of phrases from Python files


In order for the translation system to function, we import the function that deals with the translation; as an argument, we transfer to it the name of the module in which we make the translation and that's it. Then we simply call this function as _ () and pass the translated phrase as argument.
import core.union; _ = core.union.get_trans('module') _('text') 


Translation of phrases from templates


Paste the following snippet into the render_templ function, which deals with rendering templates, to pass the translation function to the templates:
 p['gettext'] = get_trans(module) p['_'] = get_trans(module) 

As a result, we obtain:
 def render_templ(template, **kwarg): template = jinja.get_template(template) module = split_templ_name( template)[0] kwarg['gettext'] = get_trans(module) kwarg['_'] = get_trans(module) kwarg['context'] = context() return template.render(**kwarg) 


Also in the templates, you can pass the context of the environment, so as not to pass it in each function.
Next come the functions that do the translations.
 langs = {} def load_lng(path, module_name, lang): """     """ if not module_name in langs[lang]: langs[lang][module_name] = [] path = os.path.join( path, module_name, 'locale') if module_name else os.path.join (path, 'locale') if os.path.isdir(path): t = gettext.translation('_', path, [cur_lang()], codeset='UTF-8') langs[lang][module_name].append(t) def get_lng(module): """     . """ lang = cur_lang() if not lang in langs: langs[lang] = {} if not module in langs[lang]: langs[lang][module] = [] load_lng(os.path.join (settings.lib_path,'app'), module, lang) load_lng(os.path.join (os.getcwd(),'app'), module, lang) if not module: load_lng(os.path.join (os.getcwd()), '', lang) return langs[lang][module] def trans(module, s): """     ,      """ if type(s) == str: s = s.decode('UTF-8') translated = s lng = get_lng(module) if lng: for i in reversed(lng): translated = i.gettext(s).decode('UTF-8') #     translated        . if s != translated: break return translated def get_trans(module): #       return lambda s: trans(module, s) 


Work with gettext


Now we have to automate the translation after we wrote in the template {{_ ('text')}}. We need to get the _.mo, _.po files in the / app / module / en / LS_MESSAGES folders.
_.mo - compiled file from where gettext then reads translations.
_.po - source file for translators using the following pattern:
 #: /path/modul/templ/base.tpl:25 msgid "text" msgstr "" #      . 

Work with these files is carried out by standard commands from the console:
xgettext - collects lines for translation by files.
msginit - creates a translation file for a specific language _.po.
msgfmt - compiles to the _.mo binary file.
msgmerge - update translation files.
But first, I would like to automate everything and not to write several commands every time, and secondly, xgettext does not know how to work with templates, but only with * .py files, at least such an opportunity was not found.
Therefore, we will write a small utility that one team would perform for us all these actions.

 #     . list_lang = ['ru_RU', 'en_US']     . s = str(sys.argv) s = s[1:-1]; app = [] for word in s.split(", "): app.append(word) #      lib_path = '/path' #      app_path = app[1][1:-1] def iter_trans(dir, is_app=True): """             .""" if is_app: #    for res in os.listdir(dir): path_ = os.path.join(dir, res, 'locale') if os.path.isdir(path_): for res in os.listdir(path_): path = os.path.join(path_, res, 'LC_MESSAGES') if os.path.isdir(path): po_f = os.path.join(path, '_.po') mo_f = os.path.join(path, '_.mo') os.popen("msgfmt -o %s %s" % (mo_f, po_f )).read() else: #      . path_ = os.path.join(dir, 'locale') if os.path.isdir(path_): for res in os.listdir(path_): path = os.path.join(path_, res, 'LC_MESSAGES') if os.path.isdir(path): po_f = os.path.join(path, '_.po') mo_f = os.path.join(path, '_.mo') os.popen("msgfmt -o %s %s" % (mo_f, po_f )).read() def iter_mo(dir, is_app=True): """    """ if is_app: for res in os.listdir(dir): path = os.path.join(dir, res, 'templ') if os.path.isdir(path): iter_templ(path) else: path = os.path.join(dir, 'templ') if os.path.isdir(path): iter_templ(path) pot_header = """# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\\n" "Report-Msgid-Bugs-To: \\n" "POT-Creation-Date: %s\\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n" "Language-Team: LANGUAGE <LL@li.org>\\n" "MIME-Version: 1.0\\n" "Content-Type: text/plain; charset=UTF-8\\n" "Content-Transfer-Encoding: 8bit\\n" """ % (time.strftime("%Y-%m-%d %H:%M%z")) def iter_templ(dir): """           pot .        _.po      . """ out_f = os.path.join(dir, '..', 'locale', '_.pot') file_o = open(out_f, 'w') #    . file_o.write(pot_header) for name in os.listdir(dir): if name.endswith('.tpl'): load_translation(os.path.join(dir, name), file_o) file_o.close() for res in list_lang: lang = res[:2] po_path = os.path.join(dir, '..', 'locale', lang) if not os.path.isdir(po_path): os.makedirs(po_path, 0755) po_path = os.path.join(po_path, 'LC_MESSAGES') if not os.path.isdir(po_path): os.makedirs(po_path, 0755) po_f = os.path.join(po_path, '_.po') if not os.path.isfile(po_f): os.popen("msginit --no-translator -i %s -o %s -l %s" % ( out_f, po_f, res+'.UTF-8')).read() else: os.popen("msgmerge %s %s -o %s" % (po_f, out_f, po_f)).read() def load_translation(in_f, file): """         . """ with open(in_f, 'r') as f: l = f.read().split('\n') n = 0; r = {} for rs in l: n += 1 #     aa = re.findall(r'_\([^)]+\)', rs) for res in aa: #         res = res[3:-2] #       if not res in r: r[res] = [] #    r[res].append(n) for res, nums in r.iteritems(): file.write('#: '+in_f+':'+','.join([str(x) for x in nums])+'\n') file.write('msgid "'+res+'"\n') file.write('msgstr ""\n\n') #       'cpl',  ,  ,    . itr = iter_trans if len(app) > 2 and app[2][1:-1] == 'cpl' else iter_mo for res in [lib_path + '/', False, lib_path +'/app/', app_path +'/app/', app_path + '/', False]: itr(res) 


Summary


Now the framework has a modular structure, templating, and internalization. To create a workable framework, it remains for us to add debugging, working with routs and statics. After that, you can start creating the main components: admin panel, data presentation, and so on.
For now.

Materials used


About nature gettext
Introduction to gettext in python
gettext and jinja2
Python and gettext documentation

Continuation

Source: https://habr.com/ru/post/146798/


All Articles