📜 ⬆️ ⬇️

gettext: antelope roast recipe in Javascript


When developing CMF, I was faced with the need to correctly implement i18n (multilingual), and began to consider various options ...

At first, based on past experience, I wanted to make “language constants” and hardships with hacks for numerals. But then, fortunately, I chose GNU gettext, a powerful and popular (in Unix-based) tool. Soon I realized that there is no point in unnecessarily loading the server with translations of phrases that are not indexed by search engines, and that in some cases it is better to translate on the client. However, there was a need for a unified system that allows you to make transfers in a single format.
Googling gettext javascript, I saw several implementations.

First caught - code.google.com/p/gettext-js
The plus is that it does not require additional conversion of the source po-file, minus - there is no ngettext.

Then I found plugins.jquery.com/project/gettext
I decided to use it. However, the plugin requires the preparation of a special JSON file from the MO file.
')
For conversion, a function in Python is provided:
import simplejson as enc import gettext def gettext_json(domain, path, lang = [], indent = False): try: tr = gettext.translation(domain, path, lang) # for unknown reasons, instead of having plural entries like # key: [sg, pl1...] # tr._catalog has (key, n): pln, keys = tr._catalog.keys() keys.sort() ret = {} for k in keys: v = tr._catalog[k] if type(k) is tuple: if k[0] not in ret: ret[k[0]] = [] ret[k[0]].append(v) else: ret[k] = v return enc.dumps(ret, ensure_ascii = False, indent = indent) except IOError: return None 


I had to spend ... twenty minutes on google and studying the docks to fix the code and make it work. As a result, gave birth to a normal Unix program.

gettext2json
 #!/usr/bin/python import sys import simplejson as enc import gettext def gettext_json(domain, path, lang = [], indent = False): try: tr = gettext.translation(domain, path, lang) # for unknown reasons, instead of having plural entries like # key: [sg, pl1...] # tr._catalog has (key, n): pln, keys = tr._catalog.keys() keys.sort() ret = {} for k in keys: v = tr._catalog[k] if type(k) is tuple: if k[0] not in ret: ret[k[0]] = [] ret[k[0]].append(v) else: ret[k] = v return enc.dumps(ret, ensure_ascii = True, indent = indent) except IOError as (errno, strerror): print "I/O error({0}): {1}".format(errno, strerror) print gettext_json(sys.argv[1],sys.argv[2],[sys.argv[3]], True) 


Also, I decided to automate the process of creating binary MO files from text PO files:

BuildLocales:
 #!/usr/bin/php -q <?php chdir(__DIR__); $lcPath = './locale'; $jsPath = './static/locale'; foreach (glob($lcPath.'/*/LC_MESSAGES/*.po') as $poFile) { $locale = pathinfo(dirname(dirname($poFile)), PATHINFO_FILENAME); $domain = pathinfo($poFile, PATHINFO_FILENAME); $moFile = dirname($poFile).'/'.$domain.'.mo'; $jsFile = $jsPath.'/'.$locale.'/'.$domain.'.json'; shell_exec('mkdir -p '.escapeshellarg($jsPath.'/'.$locale)); shell_exec('msgfmt -o '.escapeshellarg($moFile).' '.escapeshellarg($poFile)); $cmd = 'gettext2json '.escapeshellarg($domain).' '.escapeshellarg($lcPath ).' '.escapeshellarg($locale).' > '.escapeshellarg($jsFile); shell_exec($cmd); } 

Thus, to prepare all the files, all you need to do is create / change text .po files in the locale folder and run the BuildLocales script.

To connect gettext to Javascript, you must specify the lang attribute on the html tag, add a link element to the head with the path to the json file, and load jquery.gettext.js.

The beginning of the HTML page will look like this:

 <!DOCTYPE html> <html lang="ru"> ... <link href="/locale/ru/mydomain.json" lang="ru" rel="gettext"/> <script type="text/javascript" src="/js/jquery.gettext.js" /> ... 


Then you can call the _ ("Hello world!") Function and enjoy. Oops! Does not work!

We'll have to fix something in jquery.gettext.js, impose a patch:

 63,66c63,70 < try { < var messages = eval('(' + data + ')'); < } catch(e) { < return; --- > if (typeof(data) == 'object') { > var messages = data; > } else { > try { > var messages = eval('(' + data + ')'); > } catch(e) { > return; > } 

Those who are too lazy to impose a patch take jquery.gettext.js .

I hope you enjoyed the roast, thank you for your attention.

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


All Articles