[FIX] tools: extract translatable terms from spreadsheet dashboard

This commit extract translatable content from spreadsheet data.
- chart titles and description
- pivot and list names
- argument of _t() functions
- labels of links

X-original-commit: d210a55f48b316fdf38fcbe5ae5a70cf0e2c3814
Part-of: odoo/odoo#101120
This commit is contained in:
Lucas Lefèvre
2022-09-26 14:43:21 +02:00
parent 8862fafa87
commit 723d8f8b50
3 changed files with 265 additions and 1 deletions
@@ -0,0 +1,175 @@
{
"version": 12,
"sheets": [
{
"id": "Sheet1",
"name": "Sheet1",
"colNumber": 26,
"rowNumber": 100,
"rows": {},
"cols": {},
"merges": [],
"cells": {
"A1": {
"content": "not exported"
},
"A2": {
"content": "=_t(\"exported 1\")"
},
"A3": {
"content": "=_t(\"exported 2\") + _t(\"exported 3\")"
},
"A4": {
"content": "[link label](https://odoo.com)"
},
"A5": {
"content": "_t(\"not a formula\")"
},
"A6": {
"content": "=\"_t(\\\"inside an escaped string literal\\\")\""
},
"A7": {
"content": "=\"_t(\"inside a string literal\")\""
},
"A8": {
"content": "=_t(\"aa (\\\"inside\\\") bb\")"
},
"A9": {
"content": "=CONCATENATE(\"a\", \"b\")"
},
"A10": {
"content": "=_t( \"with spaces\" )"
},
"A11": {
"content": ""
},
"A12": {
"content": "=_t(\"123\")"
},
"A13": {
"content": "=_t(456)"
},
"A14": {
"content": "=_t(A1)"
},
"A15": {
"content": "=_t(now())"
},
"A16": {
"content": "=_t(\"hello \\\"world\\\"\")"
}
},
"conditionalFormats": [],
"figures": [
{
"id": "1",
"x": 0,
"y": 0,
"width": 100,
"height": 100,
"tag": "chart",
"data": {
"type": "bar",
"dataSetsHaveTitle": false,
"background": "#FFFFFF",
"dataSets": [
"A4"
],
"legendPosition": "none",
"verticalAxisPosition": "left",
"title": "Bar chart title",
"stackedBar": false
}
},
{
"id": "2",
"x": 0,
"y": 0,
"width": 100,
"height": 100,
"tag": "chart",
"data": {
"baselineColorDown": "#DC6965",
"baselineColorUp": "#00A04A",
"baselineMode": "difference",
"title": "",
"type": "scorecard",
"background": "#FFFFFF",
"keyValue": "A5"
}
},
{
"id": "2",
"x": 0,
"y": 0,
"width": 100,
"height": 100,
"tag": "chart",
"data": {
"baselineColorDown": "#DC6965",
"baselineColorUp": "#00A04A",
"baselineMode": "difference",
"title": "Scorecard chart",
"type": "scorecard",
"background": "#FFFFFF",
"baselineDescr": "Scorecard description",
"keyValue": "A6"
}
},
{
"id": "3",
"x": 541,
"y": 61,
"width": 536,
"height": 335,
"tag": "chart",
"data": {
"title": "Opportunities",
"id": "3",
"background": "#FFFFFF",
"legendPosition": "top",
"metaData": {
"groupBy": [],
"measure": "__count",
"resModel": "crm.lead"
},
"searchParams": {
"comparison": null,
"domain": [],
"groupBy": [],
"orderBy": []
},
"type": "odoo_bar",
"verticalAxisPosition": "left",
"stacked": true
}
}
],
"areGridLinesVisible": true,
"isVisible": true
}
],
"odooVersion": 4,
"lists": {
"1": {
"columns": [],
"domain": [],
"model": "crm.lead",
"orderBy": [],
"id": "1",
"name": "Pipeline"
}
},
"pivots": {
"1": {
"colGroupBys": [],
"domain": [],
"id": "1",
"measures": [],
"model": "crm.lead",
"rowGroupBys": [],
"name": "Pipeline Analysis",
"sortedColumn": null
}
}
}
@@ -329,3 +329,26 @@ class TestTranslationFlow(common.TransactionCase):
self.assertIn('text node', trans_static)
self.assertIn('slot', trans_static)
self.assertIn('slot 2', trans_static)
def test_export_spreadsheet(self):
terms = []
po_reader = TranslationModuleReader(self.env.cr, ['test_translation_import'])
for line in po_reader:
_module, _ttype, name, _res_id, source, _value, _comments = line
if name == "addons/test_translation_import/data/files/test_spreadsheet_dashboard.json":
terms.append(source)
self.assertEqual(set(terms), {
'exported 1',
'exported 2',
'exported 3',
'Bar chart title',
'Scorecard description',
'Scorecard chart',
'Opportunities',
'Pipeline',
'Pipeline Analysis',
'link label',
'aa (\\"inside\\") bb',
'with spaces',
'hello \\"world\\"',
})
+67 -1
View File
@@ -5,9 +5,12 @@ import fnmatch
import functools
import inspect
import io
import itertools
import json
import locale
import logging
import os
from tokenize import generate_tokens, STRING, NEWLINE, INDENT, DEDENT
import polib
import re
import tarfile
@@ -889,6 +892,65 @@ def babel_extract_qweb(fileobj, keywords, comment_tags, options):
_extract_translatable_qweb_terms(tree.getroot(), handle_text)
return result
def extract_formula_terms(formula):
"""Extract strings in a spreadsheet formula which are arguments to '_t' functions
>>> extract_formula_terms('=_t("Hello") + _t("Raoul")')
["Hello", "Raoul"]
"""
tokens = generate_tokens(io.StringIO(formula).readline)
tokens = (token for token in tokens if token.type not in {NEWLINE, INDENT, DEDENT})
for t1 in tokens:
if not t1.string == '_t':
continue
t2 = next(tokens, None)
if t2 and t2.string == '(':
t3 = next(tokens, None)
t4 = next(tokens, None)
if t4 and t4.string == ')' and t3 and t3.type == STRING:
yield t3.string[1:][:-1] # strip leading and trailing quotes
def extract_spreadsheet_terms(fileobj, keywords, comment_tags, options):
"""Babel message extractor for spreadsheet data files.
:param fileobj: the file-like object the messages should be extracted from
:param keywords: a list of keywords (i.e. function names) that should
be recognized as translation functions
:param comment_tags: a list of translator tags to search for and
include in the results
:param options: a dictionary of additional options (optional)
:return: an iterator over ``(lineno, funcname, message, comments)``
tuples
"""
terms = []
data = json.load(fileobj)
for sheet in data.get('sheets', []):
for cell in sheet['cells'].values():
content = cell.get('content', '')
if content.startswith('='):
terms += extract_formula_terms(content)
else:
markdown_link = re.fullmatch(r'\[(.+)\]\(.+\)', content)
if markdown_link:
terms.append(markdown_link[1])
for figure in sheet['figures']:
terms.append(figure['data']['title'])
if 'baselineDescr' in figure['data']:
terms.append(figure['data']['baselineDescr'])
pivots = data.get('pivots', {}).values()
lists = data.get('lists', {}).values()
for data_source in itertools.chain(lists, pivots):
terms.append(data_source['name'])
for global_filter in data.get('globalFilters', []):
terms.append(global_filter['label'])
return (
(0, None, term, [])
for term in terms
if any(x.isalpha() for x in term)
)
ImdInfo = namedtuple('ExternalId', ['name', 'model', 'res_id', 'module'])
@@ -1090,6 +1152,7 @@ class TranslationModuleReader:
- the python strings marked with _() or _lt()
- the javascript strings marked with _t() or _lt() inside static/src/js/
- the strings inside Qweb files inside static/src/xml/
- the spreadsheet data files
"""
# Also scan these non-addon paths
@@ -1116,7 +1179,10 @@ class TranslationModuleReader:
for fname in fnmatch.filter(files, '*.xml'):
self._babel_extract_terms(fname, path, root, 'odoo.tools.translate:babel_extract_qweb',
extra_comments=[WEB_TRANSLATION_COMMENT])
if fnmatch.fnmatch(root, '*/data/*'):
for fname in fnmatch.filter(files, '*_dashboard.json'):
self._babel_extract_terms(fname, path, root, 'odoo.tools.translate:extract_spreadsheet_terms',
extra_comments=[WEB_TRANSLATION_COMMENT])
if not recursive:
# due to topdown, first iteration is in first level
break