Initial commit: Core packages

This commit is contained in:
Ernad Husremovic 2025-08-29 15:20:45 +02:00
commit 12c29a983b
9512 changed files with 8379910 additions and 0 deletions

View file

@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import models
from . import transifex_code_translation
from . import transifex_translation

View file

@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models
class BaseModel(models.AbstractModel):
_inherit = 'base'
def get_field_translations(self, field_name, langs=None):
""" get model/model_term translations for records with transifex url
:param str field_name: field name
:param list langs: languages
:return: (translations, context) where
translations: list of dicts like [{"lang": lang, "source": source_term, "value": value_term,
"module": module, "transifexURL": transifex_url}]
context: {"translation_type": "text"/"char", "translation_show_source": True/False}
"""
translations, context = super().get_field_translations(field_name, langs=langs)
external_id = self.get_external_id().get(self.id)
if not external_id:
return translations, context
module = external_id.split('.')[0]
if module not in self.pool._init_modules:
return translations, context
for translation in translations:
translation['module'] = module
self.env['transifex.translation']._update_transifex_url(translations)
return translations, context

View file

@ -0,0 +1,71 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import psycopg2
from odoo import api, models, fields
from odoo.tools.translate import CodeTranslations
class TransifexCodeTranslation(models.Model):
_name = "transifex.code.translation"
_description = "Code Translation"
_log_access = False
source = fields.Text(string='Code')
value = fields.Text(string='Translation Value')
module = fields.Char(help="Module this term belongs to")
lang = fields.Selection(selection='_get_languages', string='Language', validate=False)
transifex_url = fields.Char("Transifex URL", compute='_compute_transifex_url',
help="Propose a modification in the official version of Odoo")
def _get_languages(self):
return self.env['res.lang'].get_installed()
def _compute_transifex_url(self):
self.transifex_url = False
self.env['transifex.translation']._update_transifex_url(self)
def _load_code_translations(self, module_names=None, langs=None):
try:
# the table lock promises translations for a (module, language) will only be created once
self.env.cr.execute(f'LOCK TABLE {self._table} IN EXCLUSIVE MODE NOWAIT')
if module_names is None:
module_names = self.env['ir.module.module'].search([('state', '=', 'installed')]).mapped('name')
if langs is None:
langs = [lang for lang, _ in self._get_languages() if lang != 'en_US']
self.env.cr.execute(f'SELECT DISTINCT module, lang FROM {self._table}')
loaded_code_translations = set(self.env.cr.fetchall())
create_value_list = [
{
'source': src,
'value': value,
'module': module_name,
'lang': lang,
}
for module_name in module_names
for lang in langs
if (module_name, lang) not in loaded_code_translations
for src, value in CodeTranslations._get_code_translations(module_name, lang, lambda x: True).items()
]
self.sudo().create(create_value_list)
except psycopg2.errors.LockNotAvailable:
return False
return True
def _open_code_translations(self):
self._load_code_translations()
return {
'name': 'Code Translations',
'type': 'ir.actions.act_window',
'res_model': 'transifex.code.translation',
'view_mode': 'list',
}
@api.model
def reload(self):
self.env.cr.execute(f'DELETE FROM {self._table}')
return self._load_code_translations()

View file

@ -0,0 +1,85 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import werkzeug.urls
from configparser import ConfigParser
from os import pardir
from os.path import isfile, join as opj
import odoo
from odoo import models, tools
class TransifexTranslation(models.AbstractModel):
_name = "transifex.translation"
_description = "Transifex Translation"
@tools.ormcache()
def _get_transifex_projects(self):
""" get the transifex project name for each module
.tx/config files contains the project reference
first section is [main], after '[odoo-16.sale]'
:rtype: dict
:return: {module_name: tx_project_name}
"""
tx_config_file = ConfigParser()
projects = {}
for addon_path in odoo.addons.__path__:
for tx_path in (
opj(addon_path, '.tx', 'config'),
opj(addon_path, pardir, '.tx', 'config'),
):
if isfile(tx_path):
tx_config_file.read(tx_path)
for sec in tx_config_file.sections()[1:]:
if len(sec.split(":")) != 6:
# old format ['main', 'odoo-16.base', ...]
tx_project, tx_mod = sec.split(".")
else:
# tx_config_file.sections(): ['main', 'o:odoo:p:odoo-16:r:base', ...]
_, _, _, tx_project, _, tx_mod = sec.split(':')
projects[tx_mod] = tx_project
return projects
def _update_transifex_url(self, translations):
""" Update translations' Transifex URL
:param translations: the translations to update, may be a recordset or a list of dicts.
The elements of `translations` must have the fields/keys 'source', 'module', 'lang',
and the field/key 'transifex_url' is updated on them.
"""
# e.g. 'https://www.transifex.com/odoo/'
base_url = self.env['ir.config_parameter'].sudo().get_param('transifex.project_url')
if not base_url:
return
base_url = base_url.rstrip('/')
res_langs = self.env['res.lang'].search([])
lang_to_iso = {l.code: l.iso_code for l in res_langs}
if not lang_to_iso:
return
projects = self._get_transifex_projects()
if not projects:
return
for translation in translations:
if not translation['source'] or translation['lang'] == 'en_US':
continue
lang_iso = lang_to_iso.get(translation['lang'])
if not lang_iso:
continue
project = projects.get(translation['module'])
if not project:
continue
# e.g. https://www.transifex.com/odoo/odoo-16/translate/#fr_FR/sale/42?q=text:'Sale+Order'
# 42 is an arbitrary number to satisfy the transifex URL format
source = werkzeug.urls.url_quote_plus(translation['source'][:50].replace("\n", "").replace("'", "\\'"))
source = f"'{source}'" if "+" in source else source
translation['transifex_url'] = f"{base_url}/{project}/translate/#{lang_iso}/{translation['module']}/42?q=text%3A{source}"