Skip to content

Instantly share code, notes, and snippets.

@yelizariev
Last active August 17, 2020 11:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yelizariev/5cc80c634cce9271132fb141971fe23a to your computer and use it in GitHub Desktop.
Save yelizariev/5cc80c634cce9271132fb141971fe23a to your computer and use it in GitHub Desktop.
Automatic translation of odoo files by using existing translation (e.g. from Ukrainian to Russian, English to Russian, Russian to Russian)
# install dependencies
sudo pip install polib
sudo pip install google-cloud-translate==2.0.0
# https://docs.transifex.com/client/installing-the-client
sudo pip install transifex-client
# prepare and export your google cloud credentials
# See https://cloud.google.com/translate/docs/basic/setup-basic
# and https://cloud.google.com/docs/authentication/getting-started
export GOOGLE_APPLICATION_CREDENTIALS=path/to/credentials.json
# prepare and export transifex token
# https://www.transifex.com/user/settings/api/
export TX_TOKEN=<your_Transifex_API_token>
cd
wget https://gist.github.com/yelizariev/5cc80c634cce9271132fb141971fe23a/raw/translate.py
wget https://gist.github.com/yelizariev/5cc80c634cce9271132fb141971fe23a/raw/fix-translation.py
wget https://gist.github.com/yelizariev/5cc80c634cce9271132fb141971fe23a/raw/move-translation.py
import sys
import re
import polib
TAGS = ['menuselection', 'doc', 'ref', 'class', 'meth', 'func']
def fix_po_item(msgid, msgstr):
res = msgstr
for t in TAGS:
search = re.search(r"(:%s:`[^`]*`)" % t, msgid)
if not search:
# no tag in source
continue
original_value = search.group(1)
res = re.sub(":\s*%s\s*:\s*`[^`]*`" % t, ' %s' % original_value, res)
print ("tag :%s: is replaced with following:\n%s" % (t, original_value))
# fix replaced quotes
res = re.sub("&quot;", "`", res)
res = re.sub("&#39;", "'", res)
# fix variables
res = re.sub("% \(", "%(", res)
# TODO: this doesn't work well in latin aplhabet
res = re.sub("\) s", ")s ", res)
res = re.sub("% \.", "%.", res)
res = re.sub("% S", "%s", res)
res = re.sub("% s", "%s", res)
res = re.sub("% D", " %d", res)
res = re.sub("% d", " %d", res)
res = re.sub("% R", " %r", res)
res = re.sub("% r", " %r", res)
res = re.sub("%set", "% set", res)
res = re.sub("%S", "%s ", res)
res = re.sub("\$ {", "${", res)
#res = re.sub("# ", "#", res)
# fix broken links
res = re.sub("\> `_", ">`_", res)
if res != msgstr:
return res
def fix_po(fname):
dest_po = polib.pofile(fname)
changed = False
for dest_entry in dest_po:
res = fix_po_item(dest_entry.msgid, dest_entry.msgstr)
# python3 check
if isinstance(res, str):
changed = True
dest_entry.msgstr = res
if changed:
dest_po.save()
if __name__ == '__main__':
print (sys.argv)
fname = sys.argv[1]
fix_po(fname)
import sys
import polib
import os
def move_po(lang, source_path, dest_fname):
src_fname = os.path.join(source_path, dest_fname)
if not os.path.exists(src_fname):
print ("Source file doesn't exist: %s" % src_fname)
return
src_po = polib.pofile(src_fname)
dest_po = polib.pofile(dest_fname)
changed = False
for dest_entry in dest_po:
src_entry = src_po.find(dest_entry.msgid)
if not src_entry:
# print ("%s: source entry not found or empty: \n%s\n" % (src_fname, dest_entry.msgid))
continue
if dest_entry.msgstr == src_entry.msgstr:
# same translation
continue
if not any(s in dest_entry.msgstr for s in {'&', '%', '$'}):
# we update only entries with special characters See https://github.com/odoo/odoo/issues/43161#issuecomment-575015099
continue
#print (u'========================================\nUpdate \n%s\n--->\n%s ' % (str(dest_entry.msgstr), str(src_entry.msgstr)))
dest_entry.msgstr = src_entry.msgstr
changed = True
if changed:
dest_po.save()
if __name__ == '__main__':
print (sys.argv)
lang = sys.argv[1]
source_path = sys.argv[2]
dest_fname = sys.argv[3]
move_po(lang, source_path, dest_fname)
# Transifex usually syncs translation by itself: https://docs.transifex.com/translation-memory/tm-introduction
# You may need this script only to fix previously translated terms
# You need to use this script twice: for CE and EE
# Each time you need two odoo folders: source and target
LANG=ru
VERSION_SOURCE=odoo-12
VERSION_TARGET=odoo-13
# download existing translations
cd path/to/odoo-source
tx pull -r "$VERSION_SOURCE.*" -l $LANG -f
cd path/to/odoo-target
tx pull -r "$VERSION_TARGET.*" -l $LANG -f
# make a commit to easily compare changes
git commit -a -m "sync translations with transifex"
# move translations
#export PYTHONIOENCODING=utf-8
find . -iname "ru.po" | xargs -L1 python3 ~/move-translation.py $LANG ../path/to/odoo-source
# upload translation
tx push -r "$VERSION_TARGET.*" -l ru -t
import sys
import polib
from google.cloud import translate_v2 as translate
translate_client = translate.Client()
def translate_po(lang, fname):
po = polib.pofile(fname)
for entry in po:
if entry.msgstr:
# already translated
continue
try:
tr_text = translate_text(entry.msgid, 'en', lang)
except Exception as e:
# save file and exit
print ('ERROR on translation: ', e)
break
entry.msgstr = tr_text
po.save()
def translate_text(text, src, dest):
result = translate_client.translate(text, source_language=src, target_language=dest)
return result['translatedText']
if __name__ == '__main__':
print (sys.argv)
lang = sys.argv[1]
fname = sys.argv[2]
translate_po(lang, fname)
VERSION=odoo-13-doc
SRC_LANG=uk
DEST_LANG=ru
git clone https://github.com/odoo/documentation-user.git
cd documentation-user
tx pull -r "$VERSION.*" -l $SRC_LANG -f
tx pull -r "$VERSION.*" -l $DEST_LANG -f
# translate
find . -type d -iname i18n | xargs -L1 python3 ~/translate.py $SRC_LANG $DEST_LANG
# fix
find locale/$DEST_LANG/LC_MESSAGES -iname "*.po" | xargs -L1 python3 ~/fix-translation.py
# upload
tx push -r "$VERSION.*" -l $DEST_LANG -t
# You need to use this script twice: for CE and EE
cd path/to/odoo
SRC_LANG=uk
DEST_LANG=ru
VERSION=odoo-12
# download source
tx pull -r "$VERSION.*" -l $SRC_LANG -f
tx pull -r "$VERSION.*" -l $DEST_LANG -f
# translate
find . -type d -iname i18n | xargs -L1 python3 ~/translate.py uk ru
# fix
find . -iname "ru.po" | xargs -L1 python3 ~/fix-translation.py
# upload translation
tx push -r "$VERSION.*" -l ru -t
RESOURSES=(website_event_stand saas_trial release_notes openerp_website openerp_enterprise main help features)
SRC_LANG=uk
DEST_LANG=ru
# TODO: use command "tx" instead
# get it from transifex.com
COOKIE="Cookie: ......"
mkdir /tmp/odoo.com/
cd /tmp/odoo.com/
# download
for r in ${RESOURSES[@]}; do
mkdir -p $r/i18n/
wget https://www.transifex.com/odoo/odoo-com/$r/$SRC_LANG/download/for_use/ --header="$COOKIE" -O $r/i18n/$SRC_LANG.po
wget https://www.transifex.com/odoo/odoo-com/$r/$DEST_LANG/download/for_translation/ --header="$COOKIE" -O $r/i18n/$DEST_LANG.po
done
# translate
find . -type d -iname i18n | xargs -L1 python3 ~/translate.py uk ru
import sys
import os
import subprocess
import polib
from google.cloud import translate_v2 as translate
translate_client = translate.Client()
def translate_po(src_lang, dest_lang, src_fname, dest_fname, template_fname):
if not os.path.exists(src_fname):
print ("Source file doesn't exist: %s" % src_fname)
return
if not os.path.exists(dest_fname):
print ("Target file doesn't exist: %s" % dest_fname)
subprocess.check_output(["cp %s" % template_fname, dest_fname])
src_po = polib.pofile(src_fname)
dest_po = polib.pofile(dest_fname)
for dest_entry in dest_po:
if dest_entry.msgstr:
# already translated
continue
if any(s in dest_entry.msgid for s in ["%", "<", ">", "$", "&", "#"]):
# don't translate messages with special symbols
continue
src_entry = src_po.find(dest_entry.msgid)
if not src_entry:
print ("%s: source entry not found or empty: \n%s\n" % (src_fname, dest_entry.msgid))
continue
if src_entry.msgstr == src_entry.msgid:
# Not for translattion.
# Just copy original.
# If we leave it empty, transifex will always offer to translate it, which is annoying
dest_entry.msgstr = src_entry.msgid
continue
try:
tr_text = translate_text(src_entry.msgstr, src_lang, dest_lang)
except Exception as e:
# save file and exit
print ('ERROR on translation: ', e)
break
dest_entry.msgstr = tr_text
dest_po.save()
def translate_text(text, src, dest):
result = translate_client.translate(text, source_language=src, target_language=dest)
return result['translatedText']
if __name__ == '__main__':
print (sys.argv)
src_lang = sys.argv[1]
dest_lang = sys.argv[2]
if len(sys.argv) == 4:
folder = sys.argv[3]
src_fname = os.path.join(folder, '%s.po' % src_lang)
dest_fname = os.path.join(folder, '%s.po' % dest_lang)
template_fname = os.path.join(folder, '*.pot')
else:
src_fname = sys.argv[3]
dest_fname = sys.argv[4]
template_fname = sys.argv[5]
translate_po(src_lang, dest_lang, src_fname, dest_fname, template_fname)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment