[IMP] core: store translated fields as JSONB columns

[IMP] core: store translated fields as JSONB columns

Translated fields no longer use the model ir.translation.  Instead they store
all their values as JSON, and store them into JSONB columns in the model's
table.  The field's column value is either NULL or a JSON dict mapping language
codes to text (the field's value in the corresponding language), and must
contain an entry for key 'en_US' (as it is used as a fallback for all other
languages).  Empty text is allowed in translation values, but not NULL.

Here are examples for a field with translate=True:

    `NULL`
    `{"en_US": "Foo"}`
    `{"en_US": "Foo", "fr_FR": "Bar", "nl_NL": "Baz"}`
    `{"en_US": "Foo", "fr_FR": "", "nl_NL": "Baz"}`

Like before, writing False to the field makes it NULL, i.e., False in all
languages.  However, writing "" to the field makes its value empty in the
current language, but does not discard the values in the other languages.

Here are examples for a field with translate=xml_translate:

   ` NULL`
    `{"en_US": "<div>Foo<p>Bar</p></div>", "fr_FR": "<div>Fou<p>Barre</p></div>"}`

Change for callable(translate) fields: one can now write any value in any
language on such a field.  The new value will be adapted in all languages, based
on the mapping of terms between languages in the old values.  Basically the
structure of the value must remain the same in all languages, like before.

Reading a translated field is now both simpler and faster than the former
implementation.  We fetch the value of the field in the current language by
coalescing its value with the 'en_US' value of the field:

    `SELECT id, COALESCE(name->>'fr_FR', name->>'en_US') AS name ...`

The raw cache of the field contains either None or a dict which is conceptually
a subset of the JSON value in database (except for missing languages).  For the
sake of simplicity, most cache operations deal with the dict and return the text
value in the current language.

Trigram indexes have been adapted to the new storing strategy, and should enable
to search in any language.  Before this change, only the source value of the
field ('en_US') could be indexed.

Computed stored translated fields are not supported by the framework, because of
the complexity of the computation itself: the field would need to be computed in
all active languages.  We chose to not provide any hook to compute a field in
all languages at once, and the framework always invokes a compute method once to
recompute it.

Code translations are no longer stored into the database.  They become static,
and are extracted from the PO files when needed.  The worker simply uses a cache
with extracted code translations for performance.  This is reasonable, since
fr_FR code translations for all modules takes around 2MB of memory, and the
cache can be shared among all registries in the worker.  Changing code
translations requires to update the corresponding PO file and reloading the
worker(s).

Performance summary:
(+) reading 'model' translated fields is faster
(+) reading 'model_terms' translated fields is much faster (no need to inject
     translations into the source value)
(+) searching translated fields with operator 'ilike' is much faster when the
     field is indexed with 'trigram'
(+) updating translated fields requires less ORM flushing
(-) importing translations from PO files is 2x slower

Some extra fixes:
- make field 'name' of ir.actions.actions translated; because of the PG
   inheritance, this is necessary to make the column definition consistent in
   all models that inherit from ir.actions.actions.
- add some backend API for the web/website client for editing translations
- move methods get_field_string() to model ir.model.fields
- move _load_module_terms to model ir.module.module
- adapt tests in test_impex, test_new_api
- because env.lang is injected into SQL queries, its returned value is
   now guaranteed to correspond to a valid active language or None
- remove wizard to insert missing translations (no longer makes sense)

closes odoo/odoo#97692

Task-id: 2081307
Signed-off-by: Raphael Collet <rco@odoo.com>
This commit is contained in:
Odoo's Mergebot
2022-09-16 14:25:13 +02:00
committed by GitHub
173 changed files with 2948 additions and 5389 deletions
+9 -2
View File
@@ -727,8 +727,15 @@ class Import(models.TransientModel):
if '/' not in header:
# Then, try exact match
IrTranslation = self.env['ir.translation']
translated_header = IrTranslation._get_source('ir.model.fields,field_description', 'model', self.env.lang, header).lower()
if header:
field_rec = (
self.env['ir.model.fields'].sudo().with_context(lang='en_US')
.search([('field_description', '=', header)], limit=1)
.with_env(self.env)
)
translated_header = (field_rec.sudo().field_description or header).lower()
else:
translated_header = ""
for field in fields_tree:
# exact match found based on the field technical name
if header.casefold() == field['name'].casefold():
+3 -1
View File
@@ -193,7 +193,9 @@ class HrEmployeePrivate(models.Model):
public.read(fields)
for fname in fields:
values = self.env.cache.get_values(public, public._fields[fname])
self.env.cache.update(self, self._fields[fname], values)
if self._fields[fname].translate:
values = [(value.copy() if value else None) for value in values]
self.env.cache.update_raw(self, self._fields[fname], values)
@api.model
def _cron_check_work_permit_validity(self):
+1 -1
View File
@@ -297,7 +297,7 @@ class IrHttp(models.AbstractModel):
modules = IrHttpModel.get_translation_frontend_modules()
user_context = request.session.context if request.session.uid else {}
lang = user_context.get('lang')
translation_hash = request.env['ir.translation'].get_web_translations_hash(modules, lang)
translation_hash = request.env['ir.http'].get_web_translations_hash(modules, lang)
session_info.update({
'translationURL': '/website/translations',
+18 -14
View File
@@ -153,14 +153,19 @@ class AccountFrFec(models.TransientModel):
unaffected_earnings_results = self._do_query_unaffected_earnings()
unaffected_earnings_line = False
sql_query = '''
if self.pool['account.account'].name.translate:
lang = self.env.user.lang or get_lang(self.env).code
aa_name = f"COALESCE(aa.name->>'{lang}', aa.name->>'en_US')"
else:
aa_name = "aa.name"
sql_query = f'''
SELECT
'OUV' AS JournalCode,
'Balance initiale' AS JournalLib,
'OUVERTURE/' || %s AS EcritureNum,
%s AS EcritureDate,
MIN(aa.code) AS CompteNum,
replace(replace(MIN(aa.name), '|', '/'), '\t', '') AS CompteLib,
replace(replace(MIN({aa_name}), '|', '/'), '\t', '') AS CompteLib,
'' AS CompAuxNum,
'' AS CompAuxLib,
'-' AS PieceRef,
@@ -236,14 +241,14 @@ class AccountFrFec(models.TransientModel):
rows_to_write.append(unaffected_earnings_results)
# INITIAL BALANCE - receivable/payable
sql_query = '''
sql_query = f'''
SELECT
'OUV' AS JournalCode,
'Balance initiale' AS JournalLib,
'OUVERTURE/' || %s AS EcritureNum,
%s AS EcritureDate,
MIN(aa.code) AS CompteNum,
replace(MIN(aa.name), '|', '/') AS CompteLib,
replace(MIN({aa_name}), '|', '/') AS CompteLib,
CASE WHEN MIN(aa.account_type) IN ('asset_receivable', 'liability_payable')
THEN
CASE WHEN rp.ref IS null OR rp.ref = ''
@@ -298,14 +303,19 @@ class AccountFrFec(models.TransientModel):
rows_to_write.append(listrow)
# LINES
sql_query = '''
if self.pool['account.journal'].name.translate:
lang = self.env.user.lang or get_lang(self.env).code
aj_name = f"COALESCE(aj.name->>'{lang}', aj.name->>'en_US')"
else:
aj_name = "aj.name"
sql_query = f'''
SELECT
REGEXP_REPLACE(replace(aj.code, '|', '/'), '[\\t\\r\\n]', ' ', 'g') AS JournalCode,
REGEXP_REPLACE(replace(COALESCE(aj__name.value, aj.name), '|', '/'), '[\\t\\r\\n]', ' ', 'g') AS JournalLib,
REGEXP_REPLACE(replace({aj_name}, '|', '/'), '[\\t\\r\\n]', ' ', 'g') AS JournalLib,
REGEXP_REPLACE(replace(am.name, '|', '/'), '[\\t\\r\\n]', ' ', 'g') AS EcritureNum,
TO_CHAR(am.date, 'YYYYMMDD') AS EcritureDate,
aa.code AS CompteNum,
REGEXP_REPLACE(replace(aa.name, '|', '/'), '[\\t\\r\\n]', ' ', 'g') AS CompteLib,
REGEXP_REPLACE(replace({aa_name}, '|', '/'), '[\\t\\r\\n]', ' ', 'g') AS CompteLib,
CASE WHEN aa.account_type IN ('asset_receivable', 'liability_payable')
THEN
CASE WHEN rp.ref IS null OR rp.ref = ''
@@ -343,11 +353,6 @@ class AccountFrFec(models.TransientModel):
LEFT JOIN account_move am ON am.id=aml.move_id
LEFT JOIN res_partner rp ON rp.id=aml.partner_id
JOIN account_journal aj ON aj.id = am.journal_id
LEFT JOIN ir_translation aj__name ON aj__name.res_id = aj.id
AND aj__name.type = 'model'
AND aj__name.name = 'account.journal,name'
AND aj__name.lang = %s
AND aj__name.value != ''
JOIN account_account aa ON aa.id = aml.account_id
LEFT JOIN res_currency rc ON rc.id = aml.currency_id
LEFT JOIN account_full_reconcile rec ON rec.id = aml.full_reconcile_id
@@ -369,9 +374,8 @@ class AccountFrFec(models.TransientModel):
am.name,
aml.id
'''
lang = self.env.user.lang or get_lang(self.env).code
self._cr.execute(
sql_query, (lang, self.date_from, self.date_to, company.id))
sql_query, (self.date_from, self.date_to, company.id))
for row in self._cr.fetchall():
rows_to_write.append(list(row))
-6
View File
@@ -82,9 +82,3 @@ class AccountAnalyticAccount(models.Model):
_inherit = 'account.analytic.account'
name = fields.Char(translate=True)
class ResCountryState(models.Model):
_inherit = 'res.country.state'
name = fields.Char(translate=True)
+2 -19
View File
@@ -38,26 +38,9 @@ class AccountChartTemplate(models.Model):
:return: True
"""
xlat_obj = self.env['ir.translation']
#find the source from Account Template
for lang in langs:
#find the value from Translation
value = xlat_obj._get_ids(in_ids._name + ',' + in_field, 'model', lang, in_ids.ids)
counter = 0
for element in in_ids.with_context(lang=None):
if value[element.id]:
#copy Translation from Source to Destination object
xlat_obj._set_ids(
out_ids._name + ',' + in_field,
'model',
lang,
out_ids[counter].ids,
value[element.id],
element[in_field]
)
else:
_logger.info('Language: %s. Translation from template: there is no translation available for %s!' % (lang, element[in_field]))
counter += 1
for in_id, out_id in zip(in_ids.with_context(lang=lang), out_ids.with_context(lang=lang)):
out_id[in_field] = in_id[in_field]
return True
def process_coa_translations(self):
-1
View File
@@ -52,7 +52,6 @@ from . import ir_http
from . import ir_mail_server
from . import ir_model
from . import ir_model_fields
from . import ir_translation
from . import ir_ui_view
from . import ir_qweb
from . import ir_websocket
+1 -1
View File
@@ -18,7 +18,7 @@ class IrHttp(models.AbstractModel):
user_context = {'lang': guest.lang}
mods = odoo.conf.server_wide_modules or []
lang = user_context.get("lang")
translation_hash = request.env['ir.translation'].sudo().get_web_translations_hash(mods, lang)
translation_hash = request.env['ir.http'].sudo().get_web_translations_hash(mods, lang)
result['cache_hashes']['translations'] = translation_hash
result.update({
'name': guest.name,
-59
View File
@@ -1,59 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models, _
from odoo.exceptions import AccessError
class IrTranslation(models.Model):
_inherit = 'ir.translation'
def create(self, vals_list):
translations = super().create(vals_list)
translations._check_is_dynamic()
return translations
def write(self, vals):
res = super().write(vals)
self._check_is_dynamic()
return res
def _check_is_dynamic(self):
# if we don't modify translation of at least a model that inherits from mail.render.mixin, we ignore it
# translation.name can be a path, and so not in the pool, so type(None) will exclude these translations.
translations_for_mail_render_mixin = self.filtered(
lambda translation: issubclass(type(self.env.get(translation.name.split(',')[0])), self.pool['mail.render.mixin'])
)
if not translations_for_mail_render_mixin:
return
# if we are admin, or that we can update mail.template we ignore
if self.env.is_admin() or self.env.user.has_group('mail.group_mail_template_editor'):
return
# Check that we don't add qweb code in translation when you don't have the rights
# prefill cache
ids_by_model_by_lang = {}
tuple_lang_model_id = translations_for_mail_render_mixin.mapped(
lambda translation: (translation.lang, translation.name.split(',')[0], translation.res_id)
)
for lang, model, _id in tuple_lang_model_id:
ids_by_model_by_lang.setdefault(lang, {}).setdefault(model, set()).add(_id)
for lang in ids_by_model_by_lang:
for res_model, res_ids in ids_by_model_by_lang[lang].items():
self.env[res_model].with_context(lang=lang).browse(res_ids)
for trans in translations_for_mail_render_mixin:
res_model, res_id = trans.name.split(',')[0], trans.res_id
rec = self.env[res_model].with_context(lang=trans.lang).browse(res_id)
if rec._is_dynamic():
group = self.env.ref('mail.group_mail_template_editor')
more_info = len(self) > 1 and ' [%s]' % rec or ''
raise AccessError(
_('Only users belonging to the "%(group)s" group can modify translation related to dynamic templates.%(xtra)s',
group=group.name,
xtra=more_info
)
)
+10
View File
@@ -104,6 +104,16 @@ class MailRenderMixin(models.AbstractModel):
self._check_access_right_dynamic_template()
return True
def _update_field_translations(self, fname, translations, digest=None):
res = super()._update_field_translations(fname, translations, digest)
# TBD the below check is only for model_term translations.
# Because for model translations, super().update_field_translations will call write to check
if self._unrestricted_rendering:
# If the rendering is unrestricted (e.g. mail.template),
# check the user is part of the mail editor group to modify a template if the template is dynamic
self._check_access_right_dynamic_template()
return res
# ------------------------------------------------------------
# TOOLS
# ------------------------------------------------------------
+5 -26
View File
@@ -11,7 +11,7 @@ from odoo.modules import get_module_resource
from odoo.modules.module import get_resource_from_path, get_resource_path
from odoo.tools.convert import xml_import
from odoo.tools.misc import file_open
from odoo.tools.translate import TranslationFileReader
from odoo.tools.translate import TranslationFileReader, _trans_load_data
class TemplateResetMixin(models.AbstractModel):
@@ -62,10 +62,8 @@ class TemplateResetMixin(models.AbstractModel):
def _override_translation_term(self, module_name, xml_ids):
processed_base_langs = []
trans_to_remove = []
for code, _ in self.env['res.lang'].get_installed():
lang_code = tools.get_iso_codes(code)
translation_processed = False
# In case of sub languages (e.g fr_BE), load the base language first, (e.g fr.po) and
# then load the main translation file (e.g fr_BE.po)
@@ -76,28 +74,17 @@ class TemplateResetMixin(models.AbstractModel):
if base_trans_file:
if base_lang_code not in processed_base_langs:
processed_base_langs.append(base_lang_code)
translation_processed = self._process_translation_data(base_trans_file, code, xml_ids)
self._process_translation_data(base_trans_file, code, xml_ids)
# Step 2: reset translation file with main language file (can possibly override the
# terms coming from the base language)
trans_file = get_module_resource(module_name, 'i18n', lang_code + '.po')
if trans_file:
translation_processed = self._process_translation_data(trans_file, code, xml_ids)
self._process_translation_data(trans_file, code, xml_ids)
# If no translation data available to update, unlink custom translated terms linked to template
if not translation_processed:
trans_to_remove.append(code)
if trans_to_remove:
self.env['ir.translation'].search([
('name', 'like', f'{self._name},%'),
('res_id', '=', self.id),
('lang', 'in', trans_to_remove),
('module', 'in', [module_name, False]),
]).unlink()
def _process_translation_data(self, trans_file, lang, xml_ids):
"""Populates the ir_translation table.
"""Load translations.
:param trans_file: path to a translation file to open, e.g. {addon_path}/mail/i18n/es.po
:param lang: language code of the translations contained in `trans_file`
@@ -106,17 +93,9 @@ class TemplateResetMixin(models.AbstractModel):
:return Boolean: True if translation data is available and processed, False otherwise
"""
with file_open(trans_file, mode='rb', filter_ext=(".po",)) as fileobj:
translation_processed = False
reader = TranslationFileReader(fileobj)
# Process a single PO entry
for row in reader:
if row.get('imd_name') in xml_ids:
translation_processed = True
# copy Translation from Source to Destination object
self.env['ir.translation']._set_ids(
row['name'], 'model', lang, self._ids, row.get('value', ''), row['src'],
)
return translation_processed
_trans_load_data(self.env.cr, reader, lang, overwrite=True, force_overwrite=True, xml_ids=xml_ids)
def reset_template(self):
"""Resets the Template with values given in source file. We ignore the case of
+7 -86
View File
@@ -1117,84 +1117,11 @@ class MailCommon(common.TransactionCase, MailCase):
# Make sure Spanish translations have not been altered
if test_record:
description_translations = cls.env['ir.translation'].search([
('module', '=', 'test_mail'),
('src', '=', test_record._description),
('lang', '=', lang_code)
])
if description_translations:
description_translations.update({'value': 'Spanish description'})
else:
description_translations.create({
'lang': lang_code,
'module': 'test_mail',
'name': 'ir.model,name',
'res_id': cls.env['ir.model']._get_id(test_record._name),
'src': test_record._description,
'state': 'translated',
'type': 'model',
'value': 'Spanish description',
})
translations_tocreate = []
# Have a TestStuff always available
test_stuff_translations = cls.env['ir.translation'].search([
('module', '=', 'test_mail'),
('src', '=', 'TestStuff'),
('lang', '=', lang_code)
])
if test_stuff_translations:
test_stuff_translations.update({'value': 'TestSpanishStuff'})
else:
translations_tocreate.append({
'lang': lang_code,
'name': 'idontknow',
'module': 'test_mail',
'res_id': False,
'src': 'TestStuff',
'state': 'translated',
'type': 'code',
'value': 'TestSpanishStuff',
})
view_translations = cls.env['ir.translation'].search([
('module', '=', 'mail'),
('src', '=', 'View %s'),
('lang', '=', lang_code)
])
if view_translations:
view_translations.update({'value': 'SpanishView'})
else:
translations_tocreate.append({
'lang': lang_code,
'name': 'idontknow',
'module': 'mail',
'res_id': False,
'src': 'View %s',
'state': 'translated',
'type': 'code',
'value': 'SpanishView %s',
})
cls.env['ir.model']._get(test_record._name).with_context(lang=lang_code).name = 'Spanish description'
# Prepare some translated value for template if given
if test_template:
translations_tocreate += [{
'lang': lang_code,
'module': 'mail',
'name': 'mail.template,subject',
'res_id': test_template.id,
'state': 'translated',
'type': 'model',
'value': 'SpanishSubject for {{ object.name }}',
}, {
'lang': lang_code,
'module': 'mail',
'name': 'mail.template,body_html',
'res_id': test_template.id,
'state': 'translated',
'type': 'model',
'value': '<p>SpanishBody for <t t-out="object.name" /></p>',
}]
test_template.with_context(lang=lang_code).subject = 'SpanishSubject for {{ object.name }}'
test_template.with_context(lang=lang_code).body_html = '<p>SpanishBody for <t t-out="object.name" /></p>'
# create a custom layout for email notification
if not layout_arch_db:
@@ -1233,17 +1160,11 @@ class MailCommon(common.TransactionCase, MailCase):
'name': 'test_layout',
'res_id': view.id
})
translations_tocreate.append({
'lang': lang_code,
'module': 'mail',
'name': 'ir.ui.view,arch_db',
'res_id': view.id,
'src': 'English Layout for',
'state': 'translated',
'type': 'model_terms',
'value': 'Spanish Layout para',
view.update_field_translations('arch_db', {
lang_code: {
'English Layout for': 'Spanish Layout para'
}
})
cls.env['ir.translation'].create(translations_tocreate)
def _generate_attachments_data(self, count, res_model=None, res_id=None, attach_values=None):
# attachment visibility depends on what they are attached to
+3 -16
View File
@@ -112,22 +112,9 @@ class TestMailRender(common.MailCommon):
})
# some translations
cls.env['ir.translation'].create({
'type': 'model',
'name': 'mail.template,subject',
'lang': 'fr_FR',
'res_id': cls.test_template.id,
'src': cls.test_template.subject,
'value': cls.base_qweb_bits_fr[0],
})
cls.env['ir.translation'].create({
'type': 'model',
'name': 'mail.template,body_html',
'lang': 'fr_FR',
'res_id': cls.test_template.id,
'src': cls.test_template.body_html,
'value': cls.base_qweb_bits_fr[1],
})
cls.test_template.with_context(lang='fr_FR').subject = cls.base_qweb_bits_fr[0]
cls.test_template.with_context(lang='fr_FR').body_html = cls.base_qweb_bits_fr[1]
cls.env['ir.model.data'].create({
'name': 'test_template_xmlid',
'module': 'mail',
+9 -42
View File
@@ -107,62 +107,29 @@ class TestMailTemplate(MailCommon):
'body_html': '<p>foo</p>',
})
Translation = self.env['ir.translation']
### check qweb dynamic
Translation.insert_missing(employee_template._fields['body_html'], employee_template)
employee_translations_of_body = Translation.with_user(self.user_employee).search(
[('res_id', '=', employee_template.id), ('name', '=', 'mail.template,body_html'), ('lang', '=', 'fr_FR')],
limit=1
)
# keep a copy to create new translation later
body_translation_vals = employee_translations_of_body.read([])[0]
# write on translation for template without dynamic code is allowed
employee_translations_of_body.value = 'non-qweb'
employee_template.with_context(lang='fr_FR').body_html = 'non-qweb'
# cannot write dynamic code on mail_template translation for employee without the group mail_template_editor.
with self.assertRaises(AccessError):
employee_translations_of_body.value = '<t t-esc="foo"/>'
employee_template.with_context(lang='fr_FR').body_html = '<t t-esc="foo"/>'
employee_translations_of_body.unlink() # delete old translation, to test the creation now
body_translation_vals['value'] = '<p t-esc="foo"/>'
# admin can create
new = Translation.create(body_translation_vals)
new.unlink()
# Employee without mail_template_editor group cannot create dynamic translation for mail.render.mixin
with self.assertRaises(AccessError):
Translation.with_user(self.user_employee).create(body_translation_vals)
employee_template.with_context(lang='fr_FR').sudo().body_html = '<t t-esc="foo"/>'
# reset the body_html to static
employee_template.body_html = False
employee_template.body_html = '<p>foo</p>'
### check qweb inline dynamic
Translation.insert_missing(employee_template._fields['subject'], employee_template)
employee_translations_of_subject = Translation.with_user(self.user_employee).search(
[('res_id', '=', employee_template.id), ('name', '=', 'mail.template,subject'), ('lang', '=', 'fr_FR')],
limit=1
)
# keep a copy to create new translation later
subject_translation_vals = employee_translations_of_subject.read([])[0]
# write on translation for template without dynamic code is allowed
employee_translations_of_subject.value = 'non-qweb'
employee_template.with_context(lang='fr_FR').subject = 'non-qweb'
# cannot write dynamic code on mail_template translation for employee without the group mail_template_editor.
with self.assertRaises(AccessError):
employee_translations_of_subject.value = '{{ object.foo }}'
employee_template.with_context(lang='fr_FR').subject = '{{ object.foo }}'
employee_translations_of_subject.unlink() # delete old translation, to test the creation now
subject_translation_vals['value'] = '{{ object.foo }}'
# admin can create
new = Translation.create(subject_translation_vals)
new.unlink()
# Employee without mail_template_editor group cannot create dynamic translation for mail.render.mixin
with self.assertRaises(AccessError):
Translation.with_user(self.user_employee).create(subject_translation_vals)
employee_template.with_context(lang='fr_FR').sudo().subject = '{{ object.foo }}'
def test_server_archived_usage_protection(self):
""" Test the protection against using archived server (servers used cannot be archived) """
@@ -434,7 +434,7 @@ class MailPluginController(http.Controller):
def _prepare_translations(self):
lang = request.env['res.users'].browse(request.uid).lang
translations_per_module = request.env["ir.translation"].get_translations_for_webclient(
translations_per_module = request.env["ir.http"].get_translations_for_webclient(
self._translation_modules_whitelist(), lang)[0]
translations_dict = {}
for module in self._translation_modules_whitelist():
@@ -91,7 +91,7 @@ class CustomerPortal(portal.CustomerPortal):
user_context = dict(request.env.context) if request.session.uid else {}
mods = conf.server_wide_modules or []
lang = user_context.get("lang")
translation_hash = request.env['ir.translation'].get_web_translations_hash(mods, lang)
translation_hash = request.env['ir.http'].get_web_translations_hash(mods, lang)
cache_hashes = {
"translations": translation_hash,
}
@@ -131,6 +131,8 @@ class PrivacyLookupWizard(models.TransientModel):
rec_name = model._rec_name or 'name'
is_normalized = field_name == 'email_normalized' or (model_name == 'mailing.trace' and field_name == 'email')
if rec_name in model and model._fields[model._rec_name].type == 'char':
if model._fields[model._rec_name].translate:
rec_name = f"jsonb_path_query_array({rec_name}, '$.*')::text"
additional_query += """
{field_name} {search_type} %s OR {rec_name} ilike %s
""".format(
+1 -1
View File
@@ -139,7 +139,7 @@ class ProjectCustomerPortal(CustomerPortal):
# Update Cache
user_context['lang'] = lang
lang = user_context.get("lang")
translation_hash = request.env['ir.translation'].get_web_translations_hash(mods, lang)
translation_hash = request.env['ir.http'].get_web_translations_hash(mods, lang)
cache_hashes = {
"translations": translation_hash,
}
@@ -576,8 +576,8 @@ class TestReorderingRule(TransactionCase):
])],
})
self.env['res.lang']._activate_lang('fr_FR')
self.env['ir.translation']._set_ids('product.template,name', 'model', 'fr_FR', product.product_tmpl_id.ids, 'produit en français')
self.env['ir.translation']._set_ids('product.product,name', 'model', 'fr_FR', product.ids, 'produit en français')
product.product_tmpl_id.with_context(lang='fr_FR').name = 'produit en français'
product.with_context(lang='fr_FR').name = 'produit en français'
default_vendor = self.env["res.partner"].create({
"name": "Supplier A",
})
@@ -94,33 +94,7 @@ class SaleOrderTemplate(models.Model):
for lang in languages:
for line in self.sale_order_template_line_ids:
if line.name == line.product_id.get_product_multiline_description_sale():
self.create_or_update_translations(
model_name='sale.order.template.line,name', lang_code=lang.code,
res_id=line.id, src=line.name,
value=line.product_id.with_context(lang=lang.code).get_product_multiline_description_sale())
line.with_context(lang=lang.code).name = line.product_id.with_context(lang=lang.code).get_product_multiline_description_sale()
for option in self.sale_order_template_option_ids:
if option.name == option.product_id.get_product_multiline_description_sale():
self.create_or_update_translations(
model_name='sale.order.template.option,name', lang_code=lang.code,
res_id=option.id, src=option.name,
value=option.product_id.with_context(lang=lang.code).get_product_multiline_description_sale())
def create_or_update_translations(self, model_name, lang_code, res_id, src, value):
data = {
'type': 'model',
'name': model_name,
'lang': lang_code,
'res_id': res_id,
'src': src,
'value': value,
'state': 'inprogress',
}
existing_trans = self.env['ir.translation'].search([
('name', '=', model_name),
('res_id', '=', res_id),
('lang', '=', lang_code)
])
if not existing_trans:
self.env['ir.translation'].create(data)
else:
existing_trans.write(data)
option.with_context(lang=lang.code).name = option.product_id.with_context(lang=lang.code).get_product_multiline_description_sale()
@@ -19,6 +19,7 @@ from odoo.exceptions import AccessError
from odoo.tests import tagged
from odoo.tools import mute_logger, formataddr
from odoo.tests.common import users
from odoo.tools.translate import code_translations
class TestMessagePostCommon(TestMailCommon, TestRecipients):
@@ -1240,6 +1241,12 @@ class TestMessagePostLang(TestMailCommon, TestRecipients):
@users('employee')
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_layout_email_lang_context(self):
# hack into code translations:
code_translations.python_translations[('test_mail', 'es_ES')] = {'TestStuff': 'TestSpanishStuff'}
self.addCleanup(code_translations.python_translations.pop, ('test_mail', 'es_ES'))
code_translations.python_translations[('mail', 'es_ES')] = {'View %s': 'SpanishView %s'}
self.addCleanup(code_translations.python_translations.pop, ('mail', 'es_ES'))
test_records = self.test_records.with_user(self.env.user).with_context(lang='es_ES')
test_records[1].message_subscribe(self.partner_2.ids)
@@ -415,14 +415,7 @@ class TestSMSComposerMass(TestSMSCommon):
def test_composer_mass_active_ids_w_template_and_lang(self):
self.env['res.lang']._activate_lang('fr_FR')
self.env['ir.translation'].create({
'type': 'model',
'name': 'sms.template,body',
'lang': 'fr_FR',
'res_id': self.sms_template.id,
'src': self.sms_template.body,
'value': 'Cher·e· {{ object.display_name }} ceci est un SMS.',
})
self.sms_template.with_context(lang='fr_FR').body = 'Cher·e· {{ object.display_name }} ceci est un SMS.'
# set template to try to use customer lang
self.sms_template.write({
'lang': '{{ object.customer_id.lang }}',
@@ -481,14 +474,7 @@ class TestSMSComposerMass(TestSMSCommon):
and the usage with the sms composer """
# Create the lang info
self.env['res.lang']._activate_lang('fr_FR')
self.env['ir.translation'].create({
'type': 'model',
'name': 'sms.template,body',
'lang': 'fr_FR',
'res_id': self.sms_template.id,
'src': self.sms_template.body,
'value': "Hello {{ object.display_name }} ceci est en français.",
})
self.sms_template.with_context(lang='fr_FR').body = 'Hello {{ object.display_name }} ceci est en français.'
# set template to try to use customer lang
self.sms_template.write({
'lang': '{{ object.customer_id.lang }}',
@@ -29,13 +29,8 @@ class TestSmsTemplate(TestSMSCommon, TestSMSRecipients):
def test_sms_template_lang(self):
self.env['res.lang']._activate_lang('fr_FR')
self.user_admin.write({'lang': 'en_US'})
self.env['ir.translation'].create({
'type': 'model',
'name': 'sms.template,body',
'lang': 'fr_FR',
'res_id': self.sms_template.id,
'src': self.sms_template.body,
'value': self.body_fr,
self.sms_template.update_field_translations('body', {
'fr_FR': self.body_fr
})
# set template to try to use customer lang
self.sms_template.write({
+3 -14
View File
@@ -38,21 +38,10 @@ class TestIsMultiLang(odoo.tests.HttpCase):
website.domain = self.base_url() # for _is_canonical_url
website.default_lang_id = en
website.language_ids = en + it + be
params = {
'src': country1.name,
'value': country1.name + ' Italia',
'type': 'model',
'name': 'res.country,name',
'res_id': country1.id,
'lang': it.code,
'state': 'translated',
}
self.env['ir.translation'].create(params)
params.update({
'value': country1.name + ' Belgium',
'lang': be.code,
country1.update_field_translations('name', {
it.code: country1.name + ' Italia',
be.code: country1.name + ' Belgium'
})
self.env['ir.translation'].create(params)
r = self.url_open(f'/test_lang_url/{country1.id}')
self.assertEqual(r.status_code, 200)
-4
View File
@@ -1,4 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import models
-29
View File
@@ -1,29 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'Transifex integration',
'version': '1.0',
'summary': 'Add a link to edit a translation in Transifex',
'category': 'Hidden/Tools',
'description':
"""
Transifex integration
=====================
This module will add a link to the Transifex project in the translation view.
The purpose of this module is to speed up translations of the main modules.
To work, Odoo uses Transifex configuration files `.tx/config` to detec the
project source. Custom modules will not be translated (as not published on
the main Transifex project).
The language the user tries to translate must be activated on the Transifex
project.
""",
'data': [
'data/transifex_data.xml',
'data/ir_translation_view.xml',
],
'depends': ['base'],
'license': 'LGPL-3',
}
@@ -1,45 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="ir_translation_view_tree_transifex" model="ir.ui.view">
<field name="name">ir.translation.transifex</field>
<field name="model">ir.translation</field>
<field name="inherit_id" ref="base.view_translation_tree"/>
<field name="arch" type="xml">
<field name="state" position="after">
<field name="transifex_url"
widget="url"
text="Contribute"
string="Transifex" />
</field>
</field>
</record>
<record id="ir_translation_dialog_view_tree_transifex" model="ir.ui.view">
<field name="name">ir.translation.transifex</field>
<field name="model">ir.translation</field>
<field name="inherit_id" ref="base.view_translation_dialog_tree"/>
<field name="arch" type="xml">
<field name="state" position="after">
<field name="transifex_url"
widget="url"
text="Contribute"
string="Transifex" />
</field>
</field>
</record>
<record id="ir_translation_dialog_view_src_value_tree_transifex" model="ir.ui.view">
<field name="name">ir.translation.src.value.transifex</field>
<field name="model">ir.translation</field>
<field name="inherit_id" ref="base.view_translation_lang_src_value_tree"/>
<field name="arch" type="xml">
<field name="value" position="after">
<field name="transifex_url"
widget="url"
text="Contribute"
string="Transifex" />
</field>
</field>
</record>
</odoo>
-9
View File
@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<record id="transifex_project_url" model="ir.config_parameter">
<field name="key">transifex.project_url</field>
<field name="value">https://www.transifex.com/odoo</field>
</record>
</odoo>
-37
View File
@@ -1,37 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# Shaima Safar <shaima.safar@open-inside.com>, 2018
# Mustafa Rawi <mustafa@cubexco.com>, 2019
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2018-08-24 09:33+0000\n"
"Last-Translator: Mustafa Rawi <mustafa@cubexco.com>, 2019\n"
"Language-Team: Arabic (https://www.transifex.com/odoo/teams/41243/ar/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: ar\n"
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr "Transifex"
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr "رابط Transifex"
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr "الترجمة"
-32
View File
@@ -1,32 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2018-08-24 09:33+0000\n"
"Language-Team: Azerbaijani (https://www.transifex.com/odoo/teams/41243/az/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: az\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr ""
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr ""
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr ""
-35
View File
@@ -1,35 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# Martin Trigaux, 2018
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2018-09-21 13:17+0000\n"
"Last-Translator: Martin Trigaux, 2018\n"
"Language-Team: Bosnian (https://www.transifex.com/odoo/teams/41243/bs/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: bs\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr ""
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr ""
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr "Prevod"
-37
View File
@@ -1,37 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# Martin Trigaux, 2018
# Manel Fernandez <manelfera@outlook.com>, 2018
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2018-08-24 09:33+0000\n"
"Last-Translator: Manel Fernandez <manelfera@outlook.com>, 2018\n"
"Language-Team: Catalan (https://www.transifex.com/odoo/teams/41243/ca/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: ca\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr "Transifex"
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr "Transifex URL"
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr "Traducció"
-36
View File
@@ -1,36 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# trendspotter <j.podhorecky@volny.cz>, 2019
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2019-08-26 09:14+0000\n"
"Last-Translator: trendspotter <j.podhorecky@volny.cz>, 2019\n"
"Language-Team: Czech (https://www.transifex.com/odoo/teams/41243/cs/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: cs\n"
"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr "Transifex"
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr "Transifex URL"
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr "Překlad"
-36
View File
@@ -1,36 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# Martin Trigaux, 2018
# lhmflexerp <lhm@flexerp.dk>, 2018
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2018-09-21 13:17+0000\n"
"Last-Translator: lhmflexerp <lhm@flexerp.dk>, 2018\n"
"Language-Team: Danish (https://www.transifex.com/odoo/teams/41243/da/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: da\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr "Transifex"
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr "Transifex URL"
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr "Oversættelse"
-36
View File
@@ -1,36 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# AppleMentalist <felix.schnatbaum@gmail.com>, 2018
# darenkster <inactive+darenkster@transifex.com>, 2018
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2018-09-21 13:17+0000\n"
"Last-Translator: darenkster <inactive+darenkster@transifex.com>, 2018\n"
"Language-Team: German (https://www.transifex.com/odoo/teams/41243/de/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: de\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr "Transifex"
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr "Transifex URL"
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr "Übersetzung"
-35
View File
@@ -1,35 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# Martin Trigaux, 2018
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2018-09-21 13:17+0000\n"
"Last-Translator: Martin Trigaux, 2018\n"
"Language-Team: Greek (https://www.transifex.com/odoo/teams/41243/el/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: el\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr ""
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr ""
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr "Μετάφραση"
-36
View File
@@ -1,36 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# e2f <projects@e2f.com>, 2018
# Jesús Alan Ramos Rodríguez <alan.ramos@jarsa.com.mx>, 2018
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2018-09-21 13:17+0000\n"
"Last-Translator: Jesús Alan Ramos Rodríguez <alan.ramos@jarsa.com.mx>, 2018\n"
"Language-Team: Spanish (https://www.transifex.com/odoo/teams/41243/es/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: es\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr "Transifex"
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr "URL de Transifex"
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr "Traducción"
-37
View File
@@ -1,37 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# Rivo Zängov <eraser@eraser.ee>, 2018
# Martin Trigaux, 2018
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2018-08-24 09:33+0000\n"
"Last-Translator: Martin Trigaux, 2018\n"
"Language-Team: Estonian (https://www.transifex.com/odoo/teams/41243/et/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: et\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr "Transifex"
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr "Transifexi URL"
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr "Tõlge"
-35
View File
@@ -1,35 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# Martin Trigaux, 2018
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2018-09-21 13:17+0000\n"
"Last-Translator: Martin Trigaux, 2018\n"
"Language-Team: Persian (https://www.transifex.com/odoo/teams/41243/fa/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: fa\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr ""
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr ""
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr "ترجمه"
-35
View File
@@ -1,35 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# Tuomo Aura <tuomo.aura@web-veistamo.fi>, 2018
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2018-09-21 13:17+0000\n"
"Last-Translator: Tuomo Aura <tuomo.aura@web-veistamo.fi>, 2018\n"
"Language-Team: Finnish (https://www.transifex.com/odoo/teams/41243/fi/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: fi\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr ""
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr ""
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr "Käännös"
-36
View File
@@ -1,36 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# e2f <projects@e2f.com>, 2018
# Lucas Deliege <lud@odoo.com>, 2018
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2018-09-21 13:17+0000\n"
"Last-Translator: Lucas Deliege <lud@odoo.com>, 2018\n"
"Language-Team: French (https://www.transifex.com/odoo/teams/41243/fr/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: fr\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr "Transifex"
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr "URL Transifex"
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr "Traduction"
-32
View File
@@ -1,32 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2018-09-21 13:17+0000\n"
"Language-Team: Gujarati (https://www.transifex.com/odoo/teams/41243/gu/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: gu\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr ""
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr ""
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr ""
-35
View File
@@ -1,35 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# Martin Trigaux, 2018
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2018-09-21 13:17+0000\n"
"Last-Translator: Martin Trigaux, 2018\n"
"Language-Team: Hebrew (https://www.transifex.com/odoo/teams/41243/he/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: he\n"
"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % 1 == 0) ? 1: (n % 10 == 0 && n % 1 == 0 && n > 10) ? 2 : 3;\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr ""
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr ""
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr "תרגום"
-36
View File
@@ -1,36 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# Karolina Tonković <karolina.tonkovic@storm.hr>, 2019
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2019-08-26 09:14+0000\n"
"Last-Translator: Karolina Tonković <karolina.tonkovic@storm.hr>, 2019\n"
"Language-Team: Croatian (https://www.transifex.com/odoo/teams/41243/hr/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: hr\n"
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr ""
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr ""
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr "Prijevod"
-36
View File
@@ -1,36 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# krnkris, 2018
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2018-08-24 09:33+0000\n"
"Last-Translator: krnkris, 2018\n"
"Language-Team: Hungarian (https://www.transifex.com/odoo/teams/41243/hu/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: hu\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr ""
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr ""
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr "Fordítás"
-35
View File
@@ -1,35 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# Rachmat Aditiya <raditiya@me.com>, 2017
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 10.saas~18\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-10-02 11:26+0000\n"
"PO-Revision-Date: 2017-10-02 11:26+0000\n"
"Last-Translator: Rachmat Aditiya <raditiya@me.com>, 2017\n"
"Language-Team: Indonesian (https://www.transifex.com/odoo/teams/41243/id/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: id\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr ""
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation_transifex_url
msgid "Transifex URL"
msgstr ""
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "ir.translation"
msgstr "ir.translation"
-36
View File
@@ -1,36 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# Bjorn Ingvarsson <boi@exigo.is>, 2018
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2018-08-24 09:33+0000\n"
"Last-Translator: Bjorn Ingvarsson <boi@exigo.is>, 2018\n"
"Language-Team: Icelandic (https://www.transifex.com/odoo/teams/41243/is/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: is\n"
"Plural-Forms: nplurals=2; plural=(n % 10 != 1 || n % 100 == 11);\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr ""
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr ""
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr "Translation"
-37
View File
@@ -1,37 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# Sergio Zanchetta <primes2h@gmail.com>, 2018
# Paolo Valier, 2018
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2018-08-24 09:33+0000\n"
"Last-Translator: Paolo Valier, 2018\n"
"Language-Team: Italian (https://www.transifex.com/odoo/teams/41243/it/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: it\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr "Transifex"
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr "URL di Transifex"
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr "Traduzione"
-35
View File
@@ -1,35 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# Martin Trigaux, 2018
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2018-09-21 13:17+0000\n"
"Last-Translator: Martin Trigaux, 2018\n"
"Language-Team: Japanese (https://www.transifex.com/odoo/teams/41243/ja/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: ja\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr ""
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr ""
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr "翻訳"
-32
View File
@@ -1,32 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2018-09-21 13:17+0000\n"
"Language-Team: Khmer (https://www.transifex.com/odoo/teams/41243/km/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: km\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr ""
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr ""
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr ""
-36
View File
@@ -1,36 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# Link Up링크업 <linkup.way@gmail.com>, 2018
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2018-08-24 09:33+0000\n"
"Last-Translator: Link Up링크업 <linkup.way@gmail.com>, 2018\n"
"Language-Team: Korean (https://www.transifex.com/odoo/teams/41243/ko/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: ko\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr "Transifex"
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr "Transifex URL"
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr ""
-32
View File
@@ -1,32 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2019-08-26 09:14+0000\n"
"Language-Team: Luxembourgish (https://www.transifex.com/odoo/teams/41243/lb/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: lb\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr ""
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr ""
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr ""
-37
View File
@@ -1,37 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# Martin Trigaux, 2019
# Silvija Butko <silvija.butko@gmail.com>, 2019
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2019-08-26 09:14+0000\n"
"Last-Translator: Silvija Butko <silvija.butko@gmail.com>, 2019\n"
"Language-Team: Lithuanian (https://www.transifex.com/odoo/teams/41243/lt/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: lt\n"
"Plural-Forms: nplurals=4; plural=(n % 10 == 1 && (n % 100 > 19 || n % 100 < 11) ? 0 : (n % 10 >= 2 && n % 10 <=9) && (n % 100 > 19 || n % 100 < 11) ? 1 : n % 1 != 0 ? 2: 3);\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr "Transifex"
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr "Transifex URL"
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr "Vertimas"
-32
View File
@@ -1,32 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 10.saas~18\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-10-02 11:26+0000\n"
"PO-Revision-Date: 2017-10-02 11:26+0000\n"
"Language-Team: Latvian (https://www.transifex.com/odoo/teams/41243/lv/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: lv\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2);\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr ""
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation_transifex_url
msgid "Transifex URL"
msgstr ""
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "ir.translation"
msgstr ""
-37
View File
@@ -1,37 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# Martin Trigaux, 2019
# nurbakhit nurka <nurbakhit@bumanit.mn>, 2019
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2019-08-26 09:14+0000\n"
"Last-Translator: nurbakhit nurka <nurbakhit@bumanit.mn>, 2019\n"
"Language-Team: Mongolian (https://www.transifex.com/odoo/teams/41243/mn/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: mn\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr "Трансфикс"
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr ""
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr "Орчуулга"
-36
View File
@@ -1,36 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# Jan Pedro Tumusok <jpt@eyenetworks.no>, 2018
# Jorunn D. Newth, 2018
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2018-09-21 13:17+0000\n"
"Last-Translator: Jorunn D. Newth, 2018\n"
"Language-Team: Norwegian Bokmål (https://www.transifex.com/odoo/teams/41243/nb/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: nb\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr "Transifex"
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr "Transifex URL"
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr "Oversettelse"
-36
View File
@@ -1,36 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# Yenthe Van Ginneken <yenthespam@gmail.com>, 2018
# Pol Van Dingenen <pol.vandingenen@vanroey.be>, 2018
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2018-09-21 13:17+0000\n"
"Last-Translator: Pol Van Dingenen <pol.vandingenen@vanroey.be>, 2018\n"
"Language-Team: Dutch (https://www.transifex.com/odoo/teams/41243/nl/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: nl\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr "Transifex"
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr "Transifex URL"
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr "Vertaling"
-36
View File
@@ -1,36 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# Piotr Szlązak <szlazakpiotr@gmail.com>, 2018
# Tadeusz Karpiński <tadeuszkarpinski@gmail.com>, 2018
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2018-09-21 13:17+0000\n"
"Last-Translator: Tadeusz Karpiński <tadeuszkarpinski@gmail.com>, 2018\n"
"Language-Team: Polish (https://www.transifex.com/odoo/teams/41243/pl/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: pl\n"
"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr "Transifex"
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr "Strona Transifex"
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr "Tłumaczenie"
-32
View File
@@ -1,32 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2019-08-26 09:14+0000\n"
"Language-Team: Portuguese (https://www.transifex.com/odoo/teams/41243/pt/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: pt\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr ""
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr ""
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr ""
-35
View File
@@ -1,35 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# Mateus Lopes <mateus1@gmail.com>, 2018
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2018-09-21 13:17+0000\n"
"Last-Translator: Mateus Lopes <mateus1@gmail.com>, 2018\n"
"Language-Team: Portuguese (Brazil) (https://www.transifex.com/odoo/teams/41243/pt_BR/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: pt_BR\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr ""
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr ""
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr "Tradução"
-32
View File
@@ -1,32 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2019-08-26 09:14+0000\n"
"Language-Team: Romanian (https://www.transifex.com/odoo/teams/41243/ro/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: ro\n"
"Plural-Forms: nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1));\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr ""
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr ""
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr ""
-37
View File
@@ -1,37 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# Denis Trepalin <zekarious@gmail.com>, 2019
# Vasiliy Korobatov <korobatov@gmail.com>, 2019
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2019-08-26 09:14+0000\n"
"Last-Translator: Vasiliy Korobatov <korobatov@gmail.com>, 2019\n"
"Language-Team: Russian (https://www.transifex.com/odoo/teams/41243/ru/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: ru\n"
"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr "Transifex"
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr "URL-адрес Transifex"
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr "Перевод"
-35
View File
@@ -1,35 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# Jaroslav Bosansky <jaro.bosansky@ekoenergo.sk>, 2018
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2018-09-21 13:17+0000\n"
"Last-Translator: Jaroslav Bosansky <jaro.bosansky@ekoenergo.sk>, 2018\n"
"Language-Team: Slovak (https://www.transifex.com/odoo/teams/41243/sk/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: sk\n"
"Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n == 1 ? 0 : n % 1 == 0 && n >= 2 && n <= 4 ? 1 : n % 1 != 0 ? 2: 3);\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr ""
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr ""
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr "Preklad"
-32
View File
@@ -1,32 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 10.saas~18\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-10-02 11:26+0000\n"
"PO-Revision-Date: 2017-10-02 11:26+0000\n"
"Language-Team: Slovenian (https://www.transifex.com/odoo/teams/41243/sl/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: sl\n"
"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr ""
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation_transifex_url
msgid "Transifex URL"
msgstr ""
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "ir.translation"
msgstr ""
-35
View File
@@ -1,35 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# Martin Trigaux, 2018
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2018-09-21 13:17+0000\n"
"Last-Translator: Martin Trigaux, 2018\n"
"Language-Team: Serbian (https://www.transifex.com/odoo/teams/41243/sr/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: sr\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr ""
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr ""
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr "Prevod"
-35
View File
@@ -1,35 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# Martin Trigaux <mat@odoo.com>, 2017
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 10.saas~18\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-10-02 11:26+0000\n"
"PO-Revision-Date: 2017-10-02 11:26+0000\n"
"Last-Translator: Martin Trigaux <mat@odoo.com>, 2017\n"
"Language-Team: Serbian (Latin) (https://www.transifex.com/odoo/teams/41243/sr%40latin/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: sr@latin\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr ""
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation_transifex_url
msgid "Transifex URL"
msgstr ""
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "ir.translation"
msgstr "ir.translation"
-35
View File
@@ -1,35 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# Martin Trigaux, 2018
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.2\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-03-22 14:10+0000\n"
"PO-Revision-Date: 2018-03-22 14:10+0000\n"
"Last-Translator: Martin Trigaux, 2018\n"
"Language-Team: Swedish (https://www.transifex.com/odoo/teams/41243/sv/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: sv\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr ""
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr ""
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "ir.translation"
msgstr "ir.translation"
-36
View File
@@ -1,36 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# Khwunchai Jaengsawang <khwunchai.j@ku.th>, 2018
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2018-08-24 09:33+0000\n"
"Last-Translator: Khwunchai Jaengsawang <khwunchai.j@ku.th>, 2018\n"
"Language-Team: Thai (https://www.transifex.com/odoo/teams/41243/th/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: th\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr ""
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr ""
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr "การแปล"
-36
View File
@@ -1,36 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# Murat Kaplan <muratk@projetgrup.com>, 2018
# Ediz Duman <neps1192@gmail.com>, 2018
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2018-09-21 13:17+0000\n"
"Last-Translator: Ediz Duman <neps1192@gmail.com>, 2018\n"
"Language-Team: Turkish (https://www.transifex.com/odoo/teams/41243/tr/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: tr\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr "Transifex"
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr "Transifex URL"
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr "Çeviri"
-45
View File
@@ -1,45 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~14.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-14 10:29+0000\n"
"PO-Revision-Date: 2021-09-14 10:29+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_src_value_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Contribute"
msgstr ""
#. module: transifex
#: model:ir.model.fields,help:transifex.field_ir_translation__transifex_url
msgid "Propose a modification in the official version of Odoo"
msgstr ""
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_src_value_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr ""
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr ""
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr ""
-36
View File
@@ -1,36 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# Alina Lisnenko <alinasemeniuk1@gmail.com>, 2018
# Bohdan Lisnenko, 2018
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2018-09-21 13:17+0000\n"
"Last-Translator: Bohdan Lisnenko, 2018\n"
"Language-Team: Ukrainian (https://www.transifex.com/odoo/teams/41243/uk/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: uk\n"
"Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != 11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || (n % 100 >=11 && n % 100 <=14 )) ? 2: 3);\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr "Transifex"
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr "Transifex URL"
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr "Переклад"
-35
View File
@@ -1,35 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# thanh nguyen <thanhnguyen.icsc@gmail.com>, 2018
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2018-09-21 13:17+0000\n"
"Last-Translator: thanh nguyen <thanhnguyen.icsc@gmail.com>, 2018\n"
"Language-Team: Vietnamese (https://www.transifex.com/odoo/teams/41243/vi/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: vi\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr ""
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr ""
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr "Bản dịch"
-37
View File
@@ -1,37 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# e2f <projects@e2f.com>, 2018
# e2f_cn c7 <e2f_cn_c7@outlook.com>, 2018
# Jeffery CHEN Fan <jeffery9@gmail.com>, 2018
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2018-09-21 13:17+0000\n"
"Last-Translator: Jeffery CHEN Fan <jeffery9@gmail.com>, 2018\n"
"Language-Team: Chinese (China) (https://www.transifex.com/odoo/teams/41243/zh_CN/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: zh_CN\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr "Transifex"
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr "Transifex 网址"
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr "翻译"
-35
View File
@@ -1,35 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * transifex
#
# Translators:
# 敬雲 林 <chingyun@yuanchih-consult.com>, 2018
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2018-09-21 13:17+0000\n"
"Last-Translator: 敬雲 林 <chingyun@yuanchih-consult.com>, 2018\n"
"Language-Team: Chinese (Taiwan) (https://www.transifex.com/odoo/teams/41243/zh_TW/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: zh_TW\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#. module: transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_dialog_view_tree_transifex
#: model_terms:ir.ui.view,arch_db:transifex.ir_translation_view_tree_transifex
msgid "Transifex"
msgstr "Transifex"
#. module: transifex
#: model:ir.model.fields,field_description:transifex.field_ir_translation__transifex_url
msgid "Transifex URL"
msgstr "Transifex 網址"
#. module: transifex
#: model:ir.model,name:transifex.model_ir_translation
msgid "Translation"
msgstr "翻譯"
-4
View File
@@ -1,4 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import ir_translation
-86
View File
@@ -1,86 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from configparser import ConfigParser
from os.path import join as opj
import os
import werkzeug.urls
import odoo
from odoo import models, fields
class IrTranslation(models.Model):
_inherit = 'ir.translation'
transifex_url = fields.Char("Transifex URL", compute='_get_transifex_url', help="Propose a modification in the official version of Odoo")
def _get_transifex_url(self):
""" Construct transifex URL based on the module on configuration """
# e.g. 'https://www.transifex.com/odoo/'
base_url = self.env['ir.config_parameter'].sudo().get_param('transifex.project_url')
tx_config_file = ConfigParser()
tx_sections = []
for addon_path in odoo.addons.__path__:
tx_path = opj(addon_path, '.tx', 'config')
if os.path.isfile(tx_path):
tx_config_file.read(tx_path)
# first section is [main], after [odoo-11.sale]
tx_sections.extend(tx_config_file.sections()[1:])
# parent directory ad .tx/config is root directory in odoo/odoo
tx_path = opj(addon_path, os.pardir, '.tx', 'config')
if os.path.isfile(tx_path):
tx_config_file.read(tx_path)
tx_sections.extend(tx_config_file.sections()[1:])
if not base_url or not tx_sections:
self.update({'transifex_url': False})
else:
base_url = base_url.rstrip('/')
# will probably be the same for all terms, avoid multiple searches
translation_languages = list(set(self.mapped('lang')))
languages = self.env['res.lang'].with_context(active_test=False).search(
[('code', 'in', translation_languages)])
language_codes = dict((l.code, l.iso_code) for l in languages)
# .tx/config files contains the project reference
# using ini files like '[odoo-master.website_sale]'
translation_modules = set(self.mapped('module'))
project_modules = {}
for module in translation_modules:
for section in tx_sections:
tx_project, tx_mod = section.split('.')
if tx_mod == module:
project_modules[module] = tx_project
for translation in self:
if not translation.module or not translation.src or translation.lang == 'en_US':
# custom or source term
translation.transifex_url = False
continue
lang_code = language_codes.get(translation.lang)
if not lang_code:
translation.transifex_url = False
continue
project = project_modules.get(translation.module)
if not project:
translation.transifex_url = False
continue
# e.g. https://www.transifex.com/odoo/odoo-10/translate/#fr/sale/42?q=text:'Sale+Order'
src = werkzeug.urls.url_quote_plus(translation.src[:50].replace("\n", "").replace("'", "\\'"))
src = f"'{src}'" if "+" in src else src
translation.transifex_url = "%(url)s/%(project)s/translate/#%(lang)s/%(module)s/42?q=%(src)s" % {
'url': base_url,
'project': project,
'lang': lang_code,
'module': translation.module,
'src': f"text%3A{src}",
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="70" height="70"><defs><path id="a" d="M4 0h61c4 0 5 1 5 5v60c0 4-1 5-5 5H4c-3 0-4-1-4-5V5c0-4 1-5 4-5z"/><linearGradient id="c" x1="100%" x2="0%" y1="0%" y2="98.616%"><stop offset="0%" stop-color="#797C79"/><stop offset="100%" stop-color="#545554"/></linearGradient></defs><g fill="none" fill-rule="evenodd"><mask id="b" fill="#fff"><use xlink:href="#a"/></mask><g mask="url(#b)"><path fill="url(#c)" d="M0 0H70V70H0z"/><path fill="#FFF" fill-opacity=".383" d="M4 1h61c2.667 0 4.333.667 5 2V0H0v3c.667-1.333 2-2 4-2z"/><path fill="#393939" d="M4 69c-2 0-4-1-4-4V36.095L18.936 13l5.025 8H30.5L41 28l7.5-7-5.564 10.127L55.577 49 36 69H4z" opacity=".324"/><path fill="#000" fill-opacity=".383" d="M4 69h61c2.667 0 4.333-1 5-3v4H0v-4c.667 2 2 3 4 3z"/><path fill="#000" fill-rule="nonzero" d="M19.077 15h5.5v8h4v5h-4v18h4v5H23.54c-3.026 0-4.513-1.667-4.462-5V28h-4.452v-5h4.452v-8zm11.5 36l9-14-9.5-14h7l5.5 9 6-9h7l-9.5 14 9.5 14h-7l-5.5-8.445-5.5 8.445h-7zm-15.54 2.642H14V53h2.833v.642h-1.037v2.832h-.76v-2.832zM17 53h1.07l.809 2.389h.01L19.652 53h1.07v3.474h-.711v-2.462h-.01l-.847 2.462h-.586l-.848-2.438h-.01v2.438H17V53z" opacity=".3"/><path fill="#FFF" fill-rule="nonzero" d="M19.077 13h5.5v8h4v5h-4v18h4v5H23.54c-3.026 0-4.513-1.667-4.462-5V26h-4.452v-5h4.452v-8zm11.5 36l9-14-9.5-14h7l5.5 9 6-9h7l-9.5 14 9.5 14h-7l-5.5-8.445-5.5 8.445h-7zm-15.54 2.642H14V51h2.833v.642h-1.037v2.832h-.76v-2.832zM17 51h1.07l.809 2.389h.01L19.652 51h1.07v3.474h-.711v-2.462h-.01l-.847 2.462h-.586l-.848-2.438h-.01v2.438H17V51z"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

+1 -1
View File
@@ -96,7 +96,7 @@ class WebClient(http.Controller):
elif mods is None:
mods = list(request.env.registry._init_modules) + (odoo.conf.server_wide_modules or [])
translations_per_module, lang_params = request.env["ir.translation"].get_translations_for_webclient(mods, lang)
translations_per_module, lang_params = request.env["ir.http"].get_translations_for_webclient(mods, lang)
body = json.dumps({
'lang': lang,
+1 -1
View File
@@ -104,7 +104,7 @@ class Http(models.AbstractModel):
"max_file_upload_size": max_file_upload_size,
"home_action_id": user.action_id.id,
"cache_hashes": {
"translations": request.env['ir.translation'].sudo().get_web_translations_hash(
"translations": request.env['ir.http'].sudo().get_web_translations_hash(
mods, request.session.context['lang']
) if session_uid else None,
},
@@ -944,25 +944,15 @@ var BasicController = AbstractController.extend(FieldManagerMixin, {
}
var record = this.model.get(ev.data.id, { raw: true });
var res_id = record.res_id || record.res_ids[0];
var result = await this._rpc({
route: '/web/dataset/call_button',
params: {
model: 'ir.translation',
method: 'translate_fields',
args: [record.model, res_id, ev.data.fieldName],
kwargs: { context: record.getContext() },
}
});
this.translationDialog = new TranslationDialog(this, {
domain: result.domain,
searchName: result.context.search_default_name,
fieldName: ev.data.fieldName,
resId: res_id,
userLanguageValue: ev.target.value || '',
dataPointModel: record.model,
dataPointID: record.id,
isComingFromTranslationAlert: ev.data.isComingFromTranslationAlert,
isText: result.context.translation_type === 'text',
showSrc: result.context.translation_show_src,
context: record.getContext(),
});
return this.translationDialog.open();
},
@@ -14,29 +14,24 @@ odoo.define('web.TranslationDialog', function (require) {
* @constructor
* @param {Widget} parent
* @param {Object} [options]
* @param {string} [options.domain] the domain needed to get the translation terms
* @param {string} [options.fieldName] the name of the field currently translated (from the model of the form view)
* @param {string} [options.searchName] the name of the actual field that is the reference for translation (in the form of model,field)
* @param {integer} [options.resId] the ID of record currently translated
* @param {string} [options.userLanguageValue] the value of the translation in the language of the user, as seen in the from view (might be empty)
* @param {string} [options.dataPointID] the data point id of the record for which we do the translations
* @param {boolean} [options.isComingFromTranslationAlert] the initiator of the dialog, might be a link on a field or the translation alert on top of the form
* @param {boolean} [options.isText] is the field a text field (multiline) or char (single line)
* @param {boolean} [options.showSrc] is the source of the translation should be rendered (for partial translations, i.e. XML content)
*
*/
init: function (parent, options) {
options = options || {};
this.fieldName = options.fieldName;
this.domain = options.domain;
this.searchName = options.searchName;
this.resId = options.resId;
this.userLanguageValue = options.userLanguageValue;
this.domain.push(['name', "=", `${this.searchName}`]);
this.dataPointModel = options.dataPointModel;
this.dataPointID = options.dataPointID;
this.isComingFromTranslationAlert = options.isComingFromTranslationAlert;
this.currentInterfaceLanguage = session.user_context.lang;
this.isText = options.isText;
this.showSrc = options.showSrc;
this.context = options.context;
this._super(parent, _.extend({
size: 'large',
@@ -56,31 +51,35 @@ odoo.define('web.TranslationDialog', function (require) {
this._loadLanguages().then((l) => {
this.languages = l;
return this._loadTranslations().then((t) => {
this.translations = t;
[this.translations, this.context] = t;
let id = 1;
this.translations.forEach((t) => t['id'] = id++);
this.isText = this.context.translation_type === 'text';
this.showSource = this.context.translation_show_source;
});
}),
]).then(() => {
this.data = this.translations.map((term) => {
let relatedLanguage = this.languages.find((language) => language[0] === term.lang);
if (!term.value && !this.showSrc) {
term.value = term.src;
if (!term.value && !this.showSource) {
term.value = term.source;
}
return {
id: term.id,
lang: term.lang,
langName: relatedLanguage[1],
source: term.src,
source: term.source,
// we set the translation value coming from the database, except for the language
// the user is currently utilizing. Then we set the translation value coming
// from the value of the field in the form
value: (term.lang === this.currentInterfaceLanguage &&
!this.showSrc &&
!this.showSource &&
!this.isComingFromTranslationAlert) ?
this.userLanguageValue : term.value || ''
};
});
this.data.sort((left, right) =>
(left.langName < right.langName || (left.langName === right.langName && left.source < right.source)) ? -1 : 1);
left.langName.localeCompare(right.langName));
});
},
@@ -92,12 +91,11 @@ odoo.define('web.TranslationDialog', function (require) {
* @private
*/
_loadTranslations: function () {
const domain = [...this.domain, ['lang', 'in', this.languages.map(l => l[0])]];
return this._rpc({
model: 'ir.translation',
method: 'search_read',
fields: ['lang', 'src', 'value'],
domain: domain,
model: this.dataPointModel,
method: 'get_field_translations',
args: [[this.resId], this.fieldName],
context: this.context,
});
},
/**
@@ -137,16 +135,16 @@ odoo.define('web.TranslationDialog', function (require) {
this.el.querySelectorAll('input[type=text],textarea').forEach((t) => {
var initialValue = this.data.find((d) => d.id == t.dataset.id);
if (initialValue.value !== t.value) {
updatedTerm[t.dataset.id] = t.value;
updatedTerm[t.dataset.id] = {lang: initialValue.lang, source: initialValue.source, value: t.value};
if (initialValue.lang === this.currentInterfaceLanguage && !this.showSrc) {
if (initialValue.lang === this.currentInterfaceLanguage && !this.showSource) {
// when the user has changed the term for the language he is
// using in the interface, this change should be reflected
// in the form view
// partial translations being handled server side are
// also ignored
var changes = {};
changes[this.fieldName] = updatedTerm[initialValue.id];
changes[this.fieldName] = t.value;
updateFormViewField = {
dataPointID: this.dataPointID,
changes: changes,
@@ -158,16 +156,23 @@ odoo.define('web.TranslationDialog', function (require) {
// updatedTerm only contains the id and values of the terms that
// have been updated by the user
var saveUpdatedTermsProms = Object.keys(updatedTerm).map((id) => {
var writeTranslation = {
model: 'ir.translation',
method: 'write',
context: this.context,
args: [[parseInt(id, 10)], { value: updatedTerm[id] }]
};
return this._rpc(writeTranslation);
});
return Promise.all(saveUpdatedTermsProms).then(() => {
const translations = {};
if (this.showSource) { // model terms translation
Object.entries(updatedTerm).forEach(([id, term]) => {
if (!translations[term.lang]) {
translations[term.lang] = {};
}
translations[term.lang][term.source] = term.value;
});
}
else { // model translation
Object.entries(updatedTerm).forEach(([id, term]) => translations[term.lang] = term.value);
}
return this._rpc({
model: this.dataPointModel,
method: 'update_field_translations',
args: [[this.resId], this.fieldName, translations],
}).then(() => {
// we might have to update the value of the field on the form
// view that opened the translation dialog
if (updateFormViewField) {
@@ -713,6 +713,20 @@ $o-form-label-margin-right: 0px;
padding-right: 25px;
}
}
.oe_no_translation_content {
&:not(.btn) {
display: none;
}
&.btn {
width: 100% !important;
margin-left: 0px;
visibility: hidden;
}
&.btn:after {
content: 'EN';
visibility: visible;
}
}
// Text field with oe_inline class
.o_field_text.oe_inline {
@@ -2,13 +2,13 @@
<templates id="template" xml:space="preserve">
<div t-name="web.TranslationDialogWidget" class="o_translation_dialog">
<div class="row" t-foreach="widget.data" t-as="term">
<div t-attf-class="col-12 #{widget.showSrc ? 'col-lg-2' : 'col-lg-3'} #{widget.currentInterfaceLanguage == term.lang ? 'o_language_current' : ''}">
<div t-attf-class="col-12 #{widget.showSource ? 'col-lg-2' : 'col-lg-3'} #{widget.currentInterfaceLanguage == term.lang ? 'o_language_current' : ''}">
<t t-esc="term.langName"></t>
</div>
<div t-if="widget.showSrc" class="col-12 col-lg-3 source">
<div t-if="widget.showSource" class="col-12 col-lg-3 source">
<t t-esc="term.source"></t>
</div>
<div t-attf-class="col-12 #{widget.showSrc ? 'col-lg-7' : 'col-lg-9'} translation">
<div t-attf-class="col-12 #{widget.showSource ? 'col-lg-7' : 'col-lg-9'} translation">
<input t-if="!widget.isText" type="text"
class="o_field_char o_input"
t-att-value="term.value"
@@ -17,7 +17,7 @@
<textarea t-if="widget.isText"
t-esc="term.value"
t-att-data-id="term.id"
t-att-rows="widget.showSrc ? 2 : 5"
t-att-rows="widget.showSource ? 2 : 5"
class="o_field_text o_field_translate o_field_widget o_input">
</textarea>
</div>
@@ -17,7 +17,6 @@ const { Component, useEnv } = owl;
*/
export function useTranslationDialog() {
const dialog = useService("dialog");
const rpc = useService("rpc");
const env = useEnv();
async function openTranslationDialog({ record, fieldName, updateField }) {
@@ -43,22 +42,14 @@ export function useTranslationDialog() {
return;
}
}
const { resModel, resId, context } = record;
const result = await rpc("/web/dataset/call_button", {
model: "ir.translation",
method: "translate_fields",
args: [resModel, resId, fieldName],
kwargs: { context },
});
const { resModel, resId } = record;
dialog.add(TranslationDialog, {
domain: result.domain,
searchName: result.context.search_default_name,
fieldName: fieldName,
resId: resId,
resModel: resModel,
userLanguageValue: record.data[fieldName] || "",
isComingFromTranslationAlert: false,
isText: result.context.translation_type === "text",
showSource: result.context.translation_show_src,
updateField,
});
}
@@ -20,18 +20,22 @@ export class TranslationDialog extends Component {
onWillStart(async () => {
const languages = await loadLanguages(this.orm);
const translations = await this.loadTranslations(languages);
const [translations, context] = await this.loadTranslations(languages);
let id = 1;
translations.forEach((t) => (t.id = id++));
this.props.isText = context.translation_type === "text";
this.props.showSource = context.translation_show_source;
this.terms = translations.map((term) => {
const relatedLanguage = languages.find((l) => l[0] === term.lang);
if (!term.value && !this.props.showSource) {
term.value = term.src;
term.value = term.source;
}
return {
id: term.id,
lang: term.lang,
langName: relatedLanguage[1],
source: term.src,
source: term.source,
// we set the translation value coming from the database, except for the language
// the user is currently utilizing. Then we set the translation value coming
// from the value of the field in the form
@@ -43,11 +47,7 @@ export class TranslationDialog extends Component {
: term.value || "",
};
});
this.terms.sort((a, b) =>
a.langName < b.langName || (a.langName === b.langName && a.source < b.source)
? -1
: 1
);
this.terms.sort((a, b) => a.langName.localeCompare(b.langName));
});
}
@@ -63,23 +63,37 @@ export class TranslationDialog extends Component {
* Load the translation terms for the installed language, for the current model and res_id
*/
async loadTranslations(languages) {
const domain = [...this.domain, ["lang", "in", languages.map((l) => l[0])]];
return this.orm.searchRead("ir.translation", domain, ["lang", "src", "value"]);
return this.orm.call(this.props.resModel, "get_field_translations", [
[this.props.resId],
this.props.fieldName,
]);
}
/**
* Save all the terms that have been updated
*/
async onSave() {
await Promise.all(
this.terms.map(async (term) => {
if (term.id in this.updatedTerms && term.value !== this.updatedTerms[term.id]) {
await this.orm.write("ir.translation", [term.id], {
value: this.updatedTerms[term.id],
});
const translations = {};
this.terms.map((term) => {
const updatedTermValue = this.updatedTerms[term.id];
if (term.id in this.updatedTerms && term.value !== updatedTermValue) {
if (this.props.showSource) {
if (!translations[term.lang]) {
translations[term.lang] = {};
}
translations[term.lang][term.source] = updatedTermValue;
} else {
translations[term.lang] = updatedTermValue;
}
})
);
}
});
await this.orm.call(this.props.resModel, "update_field_translations", [
[this.props.resId],
this.props.fieldName,
translations,
]);
// we might have to update the value of the field on the form
// view that opened the translation dialog
@@ -436,6 +436,20 @@ $o-form-label-margin-right: 0px;
padding-right: 25px;
}
}
.oe_no_translation_content {
:not(.btn) {
display: none;
}
.btn {
width: 100% !important;
margin-left: 0px;
visibility: hidden;
}
.btn:after {
content: 'EN';
visibility: visible;
}
}
iframe.wysiwyg_iframe + .o_field_translate {
right: 30px !important;
top: 7px !important;
@@ -101,25 +101,6 @@ function manageFilters({ action, env }) {
};
}
function technicalTranslation({ action, env }) {
if (!action.res_model) {
return null;
}
return {
type: "item",
description: env._t("Technical Translation"),
callback: async () => {
const result = await env.services.orm.call(
"ir.translation",
"get_technical_translations",
[action.res_model]
);
env.services.action.doAction(result);
},
sequence: 140,
};
}
function viewAccessRights({ accessRights, action, env }) {
if (!action.res_model || !accessRights.canSeeModelAccess) {
return null;
@@ -190,7 +171,6 @@ debugRegistry
.add("editAction", editAction)
.add("viewFields", viewFields)
.add("manageFilters", manageFilters)
.add("technicalTranslation", technicalTranslation)
.add("accessSeparator", accessSeparator)
.add("viewAccessRights", viewAccessRights)
.add("viewRecordRules", viewRecordRules);
@@ -160,19 +160,6 @@ QUnit.module('Legacy basic_fields', {
position: "after",
}]
},
"ir.translation": {
fields: {
lang: {type: "char"},
value: {type: "char"},
res_id: {type: "integer"}
},
records: [{
id: 99,
res_id: 37,
value: '',
lang: 'en_US'
}]
},
};
target = getFixture();
@@ -1653,25 +1640,18 @@ QUnit.module('Legacy basic_fields', {
user_context: {lang: 'en_US'},
},
mockRPC: function (route, args) {
if (route === "/web/dataset/call_button" && args.method === 'translate_fields') {
assert.deepEqual(args.args, ["partner",1,"foo"], 'should call "call_button" route');
return Promise.resolve({
domain: [],
context: {search_default_name: 'partnes,foo'},
});
if (route === "/web/dataset/call_kw/partner/get_field_translations") {
assert.deepEqual(args.args, [[1],"foo"], "should translate the foo field of the record");
return Promise.resolve([
[{lang: "en_US", source: "yop", value: "yop"}, {lang: "fr_BE", source: "yop", value: "valeur français"}],
{translation_type: "char", translation_show_source: false},
]);
}
if (route === "/web/dataset/call_kw/res.lang/get_installed") {
return Promise.resolve([["en_US", "English"], ["fr_BE", "French (Belgium)"]]);
}
if (args.method === "search_read" && args.model == "ir.translation") {
return Promise.resolve([
{lang: 'en_US', src: 'yop', value: 'yop', id: 42},
{lang: 'fr_BE', src: 'yop', value: 'valeur français', id: 43}
]);
}
if (args.method === "write" && args.model == "ir.translation") {
assert.deepEqual(args.args[1], {value: "english value"},
"the new translation value should be written");
if (route === "/web/dataset/call_kw/partner/update_field_translations") {
assert.deepEqual(args.args, [[1], "foo", {"en_US": "english value"}], "the new translation value should be written");
return Promise.resolve();
}
return this._super.apply(this, arguments);
@@ -1741,31 +1721,21 @@ QUnit.module('Legacy basic_fields', {
user_context: {lang: 'en_US'},
},
mockRPC: function (route, args) {
if (route === "/web/dataset/call_button" && args.method === 'translate_fields') {
assert.deepEqual(args.args, ["partner",1,"foo"], 'should call "call_button" route');
return Promise.resolve({
domain: [],
context: {
search_default_name: 'partner,foo',
translation_type: 'char',
translation_show_src: true,
},
});
if (route === "/web/dataset/call_kw/partner/get_field_translations") {
assert.deepEqual(args.args, [[1],"foo"], "should translate the foo field of the record");
return Promise.resolve([
[{lang: "en_US", source: "first paragraph", value: "first paragraph"},
{lang: "en_US", source: "second paragraph", value: "second paragraph"},
{lang: "fr_BE", source: "first paragraph", value: "premier paragraphe"},
{lang: "fr_BE", source: "second paragraph", value: "deuxième paragraphe"}],
{translation_type: "char", translation_show_source: true},
]);
}
if (route === "/web/dataset/call_kw/res.lang/get_installed") {
return Promise.resolve([["en_US", "English"], ["fr_BE", "French (Belgium)"]]);
}
if (args.method === "search_read" && args.model == "ir.translation") {
return Promise.resolve([
{lang: 'en_US', src: 'first paragraph', value: 'first paragraph', id: 42},
{lang: 'en_US', src: 'second paragraph', value: 'second paragraph', id: 43},
{lang: 'fr_BE', src: 'first paragraph', value: 'premier paragraphe', id: 44},
{lang: 'fr_BE', src: 'second paragraph', value: 'deuxième paragraphe', id: 45},
]);
}
if (args.method === "write" && args.model == "ir.translation") {
assert.deepEqual(args.args[1], {value: "first paragraph modified"},
"Wrong update on translation");
if (route === "/web/dataset/call_kw/partner/update_field_translations") {
assert.deepEqual(args.args, [[1], "foo", {"en_US": {"first paragraph": "first paragraph modified"}}], "the new translation value should be written");
return Promise.resolve();
}
return this._super.apply(this, arguments);
@@ -2694,15 +2664,15 @@ QUnit.module('Legacy basic_fields', {
'</form>',
res_id: 1,
mockRPC: function (route, args) {
if (route === "/web/dataset/call_button" && args.method === 'translate_fields') {
assert.deepEqual(args.args, ["partner",1,"txt"], 'should call "call_button" route');
return Promise.resolve({
domain: [],
context: {search_default_name: 'partnes,foo'},
});
if (route === "/web/dataset/call_kw/partner/get_field_translations") {
assert.deepEqual(args.args, [[1],"txt"], "should translate the txt field of the record");
return Promise.resolve([
[{lang: "en_US", source: "yop", value: "yop"}, {lang: "fr_BE", source: "yop", value: "valeur français"}],
{translation_type: "text", translation_show_source: false},
]);
}
if (route === "/web/dataset/call_kw/res.lang/get_installed") {
return Promise.resolve([["en_US"], ["fr_BE"]]);
return Promise.resolve([["en_US", "English"], ["fr_BE", "French (Belgium)"]]);
}
return this._super.apply(this, arguments);
},
@@ -140,19 +140,6 @@ QUnit.module('LegacyViews', {
{id: 14, display_name: "silver", color: 5},
]
},
"ir.translation": {
fields: {
lang_code: {type: "char"},
value: {type: "char"},
res_id: {type: "integer"}
},
records: [{
id: 99,
res_id: 12,
value: '',
lang_code: 'en_US'
}]
},
user: {
fields: {
name: {string: "Name", type: "char"},
@@ -7382,16 +7369,7 @@ QUnit.module('LegacyViews', {
});
QUnit.test('translation alerts preserved on reverse breadcrumb', async function (assert) {
assert.expect(2);
serverData.models['ir.translation'] = {
fields: {
name: { string: "name", type: "char" },
source: {string: "Source", type: "char"},
value: {string: "Value", type: "char"},
},
records: [],
};
assert.expect(1);
serverData.models.partner.fields.foo.translate = true;
@@ -7402,12 +7380,6 @@ QUnit.module('LegacyViews', {
'</sheet>' +
'</form>',
'partner,false,search': '<search></search>',
'ir.translation,false,list': '<tree>' +
'<field name="name"/>' +
'<field name="source"/>' +
'<field name="value"/>' +
'</tree>',
'ir.translation,false,search': '<search></search>',
};
serverData.actions = {
@@ -7418,14 +7390,6 @@ QUnit.module('LegacyViews', {
type: 'ir.actions.act_window',
views: [[false, 'form']],
},
2: {
id: 2,
name: 'Translate',
res_model: 'ir.translation',
type: 'ir.actions.act_window',
views: [[false, 'list']],
target: 'current',
}
};
const webClient = await createWebClient({ serverData });
@@ -7440,13 +7404,6 @@ QUnit.module('LegacyViews', {
assert.containsOnce(target, '.o_legacy_form_view .alert > div',
"should have a translation alert");
await doAction(webClient, 2);
await testUtils.dom.click($('.o_control_panel .breadcrumb a:first'));
await legacyExtraNextTick();
assert.containsOnce(target, '.o_legacy_form_view .alert > div',
"should have a translation alert");
});
QUnit.test('translate event correctly handled with multiple controllers', async function (assert) {
@@ -7485,16 +7442,16 @@ QUnit.module('LegacyViews', {
if (route === '/web/dataset/call_kw/product/get_formview_id') {
return Promise.resolve(false);
}
if (route === "/web/dataset/call_button" && args.method === 'translate_fields') {
assert.deepEqual(args.args, ["product",37,"name"], 'should call "call_button" route');
if (route === "/web/dataset/call_kw/product/get_field_translations") {
assert.deepEqual(args.args, [[37],"name"], "should translate the name field of the record");
nbTranslateCalls++;
return Promise.resolve({
domain: [],
context: {search_default_name: 'partnes,foo'},
});
return Promise.resolve([
[{lang: "en_US", source: "yop", value: "yop"}, {lang: "fr_BE", source: "yop", value: "valeur français"}],
{translation_type: "char", translation_show_source: false},
]);
}
if (route === "/web/dataset/call_kw/res.lang/get_installed") {
return Promise.resolve([["en_US"], ["fr_BE"]]);
return Promise.resolve([["en_US", "English"], ["fr_BE", "French (Belgium)"]]);
}
return this._super.apply(this, arguments);
},
@@ -123,31 +123,6 @@ QUnit.module('LegacyViews', {
{id: "2-20170808020000", name: "virtual"},
]
},
"ir.translation": {
fields: {
lang_code: {type: "char"},
src: {type: "char"},
value: {type: "char"},
res_id: {type: "integer"},
name: {type: "char"},
lang: {type: "char"},
},
records: [{
id: 99,
res_id: 1,
value: '',
lang_code: 'en_US',
lang: 'en_US',
name: 'foo,foo'
},{
id: 100,
res_id: 1,
value: '',
lang_code: 'fr_BE',
lang: 'fr_BE',
name: 'foo,foo'
}]
},
};
serverData = { models: this.data };
@@ -3620,11 +3595,11 @@ QUnit.module('LegacyViews', {
model: 'foo',
data: this.data,
mockRPC: function (route, args) {
if (route === "/web/dataset/call_button" && args.method === 'translate_fields') {
return Promise.resolve({
domain: [],
context: {search_default_name: 'foo,foo'},
});
if (route === "/web/dataset/call_kw/foo/get_field_translations") {
return Promise.resolve([
[{lang: "en_US", source: "yop", value: "yop"}, {lang: "fr_BE", source: "yop", value: "valeur français"}],
{translation_type: "char", translation_show_source: false},
]);
}
if (route === "/web/dataset/call_kw/res.lang/get_installed") {
return Promise.resolve([["en_US","English"], ["fr_BE", "Frenglish"]]);
@@ -123,21 +123,6 @@ QUnit.module("Fields", (hooks) => {
},
],
},
"ir.translation": {
fields: {
lang: { type: "char" },
value: { type: "char" },
res_id: { type: "integer" },
},
records: [
{
id: 99,
res_id: 37,
value: "",
lang: "en_US",
},
],
},
},
};
@@ -301,7 +286,7 @@ QUnit.module("Fields", (hooks) => {
});
QUnit.test("char field translatable", async function (assert) {
assert.expect(13);
assert.expect(11);
serverData.models.partner.fields.foo.translate = true;
serviceRegistry.add("localization", makeFakeLocalizationService({ multiLang: true }), {
@@ -325,33 +310,25 @@ QUnit.module("Fields", (hooks) => {
</sheet>
</form>`,
mockRPC(route, { args, method, model }) {
if (route === "/web/dataset/call_button" && method === "translate_fields") {
assert.deepEqual(
args,
["partner", 1, "foo"],
'should call "call_button" route'
);
return Promise.resolve({
domain: [],
context: { search_default_name: "partnes,foo" },
});
}
if (route === "/web/dataset/call_kw/res.lang/get_installed") {
return Promise.resolve([
["en_US", "English"],
["fr_BE", "French (Belgium)"],
]);
}
if (method === "search_read" && model === "ir.translation") {
if (route === "/web/dataset/call_kw/partner/get_field_translations") {
return Promise.resolve([
{ lang: "en_US", src: "yop", value: "yop", id: 42 },
{ lang: "fr_BE", src: "yop", value: "valeur français", id: 43 },
[
{ lang: "en_US", source: "yop", value: "yop" },
{ lang: "fr_BE", source: "yop", value: "valeur français" },
],
{ translation_type: "char", translation_show_source: false },
]);
}
if (method === "write" && model === "ir.translation") {
if (route === "/web/dataset/call_kw/partner/update_field_translations") {
assert.deepEqual(
args[1],
{ value: "english value" },
args[2],
{ en_US: "english value" },
"the new translation value should be written"
);
return Promise.resolve(null);
@@ -417,7 +394,7 @@ QUnit.module("Fields", (hooks) => {
});
QUnit.test("html field translatable", async function (assert) {
assert.expect(6);
assert.expect(5);
serverData.models.partner.fields.foo.translate = true;
serviceRegistry.add("localization", makeFakeLocalizationService({ multiLang: true }), {
@@ -441,55 +418,48 @@ QUnit.module("Fields", (hooks) => {
</sheet>
</form>`,
mockRPC(route, { args, method, model }) {
if (route === "/web/dataset/call_button" && method === "translate_fields") {
assert.deepEqual(
args,
["partner", 1, "foo"],
`should call "call_button" route`
);
return Promise.resolve({
domain: [],
context: {
search_default_name: "partner,foo",
translation_type: "char",
translation_show_src: true,
},
});
}
if (route === "/web/dataset/call_kw/res.lang/get_installed") {
return Promise.resolve([
["en_US", "English"],
["fr_BE", "French (Belgium)"],
]);
}
if (method === "search_read" && model === "ir.translation") {
if (route === "/web/dataset/call_kw/partner/get_field_translations") {
return Promise.resolve([
{ lang: "en_US", src: "first paragraph", value: "first paragraph", id: 42 },
[
{
lang: "en_US",
source: "first paragraph",
value: "first paragraph",
},
{
lang: "en_US",
source: "second paragraph",
value: "second paragraph",
},
{
lang: "fr_BE",
source: "first paragraph",
value: "premier paragraphe",
},
{
lang: "fr_BE",
source: "second paragraph",
value: "deuxième paragraphe",
},
],
{
lang: "en_US",
src: "second paragraph",
value: "second paragraph",
id: 43,
},
{
lang: "fr_BE",
src: "first paragraph",
value: "premier paragraphe",
id: 44,
},
{
lang: "fr_BE",
src: "second paragraph",
value: "deuxième paragraphe",
id: 45,
translation_type: "char",
translation_show_source: true,
},
]);
}
if (method === "write" && model === "ir.translation") {
if (route === "/web/dataset/call_kw/partner/update_field_translations") {
assert.deepEqual(
args[1],
{ value: "first paragraph modified" },
"Wrong update on translation"
args[2],
{ en_US: { "first paragraph": "first paragraph modified" } },
"the new translation value should be written"
);
return Promise.resolve(null);
}
@@ -49,21 +49,6 @@ QUnit.module("Fields", (hooks) => {
},
],
},
"ir.translation": {
fields: {
lang: { type: "char" },
value: { type: "char" },
res_id: { type: "integer" },
},
records: [
{
id: 99,
res_id: 37,
value: "",
lang: "en_US",
},
],
},
},
};
@@ -370,7 +355,7 @@ QUnit.module("Fields", (hooks) => {
});
QUnit.test("text field translatable", async function (assert) {
assert.expect(4);
assert.expect(3);
serverData.models.partner.fields.txt.translate = true;
serviceRegistry.add("localization", makeFakeLocalizationService({ multiLang: true }), {
@@ -391,19 +376,20 @@ QUnit.module("Fields", (hooks) => {
</sheet>
</form>`,
mockRPC(route, { args, method }) {
if (route === "/web/dataset/call_button" && method === "translate_fields") {
assert.deepEqual(
args,
["partner", 1, "txt"],
`should call "call_button" route`
);
return Promise.resolve({
domain: [],
context: { search_default_name: "partnes,foo" },
});
}
if (route === "/web/dataset/call_kw/res.lang/get_installed") {
return Promise.resolve([["en_US"], ["fr_BE"]]);
return Promise.resolve([
["en_US", "English"],
["fr_BE", "French (Belgium)"],
]);
}
if (route === "/web/dataset/call_kw/partner/get_field_translations") {
return Promise.resolve([
[
{ lang: "en_US", source: "yop", value: "yop" },
{ lang: "fr_BE", source: "yop", value: "valeur français" },
],
{ translation_type: "text", translation_show_source: false },
]);
}
},
});
@@ -180,21 +180,6 @@ QUnit.module("Views", (hooks) => {
{ id: 14, display_name: "silver", color: 5 },
],
},
"ir.translation": {
fields: {
lang_code: { type: "char" },
value: { type: "char" },
res_id: { type: "integer" },
},
records: [
{
id: 99,
res_id: 12,
value: "",
lang_code: "en_US",
},
],
},
user: {
fields: {
name: { string: "Name", type: "char" },
@@ -8466,20 +8451,16 @@ QUnit.module("Views", (hooks) => {
["CUST2", "second custom"],
];
}
if (args.method === "translate_fields") {
if (route === "/web/dataset/call_kw/partner/get_field_translations") {
assert.step(`translate args ${JSON.stringify(args.args)}`);
assert.step(`translate context ${JSON.stringify(args.kwargs.context)}`);
return {
domain: [["res_id", "=", args.args[1]]],
context: { search_default_name: "partner_type,foo" },
};
}
if (args.method === "search_read" && args.model === "ir.translation") {
assert.step(
`search_read translations args: ${JSON.stringify(
args.args
)} ; kwargs: ${JSON.stringify(args.kwargs)}`
);
return Promise.resolve([
[
{ lang: "CUST", source: "yop", value: "yop" },
{ lang: "CUST2", source: "yop", value: "valeur français" },
],
{ translation_type: "char", translation_show_source: false },
]);
}
},
});
@@ -8487,9 +8468,8 @@ QUnit.module("Views", (hooks) => {
await click(target, ".o_field_translate.btn-link");
assert.verifySteps([
`translate args ["partner",1,"foo"]`,
`translate args [[1],"foo"]`,
`translate context {"lang":"en","uid":7,"tz":"taht"}`,
`search_read translations args: [] ; kwargs: {"domain":[["res_id","=",1],["name","=","partner_type,foo"],["lang","in",["CUST","CUST2"]]],"fields":["lang","src","value"],"context":{"lang":"en","uid":7,"tz":"taht"}}`,
]);
assert.containsOnce(target, ".modal");
@@ -8522,6 +8502,15 @@ QUnit.module("Views", (hooks) => {
["CUST2", "second custom"],
];
}
if (route === "/web/dataset/call_kw/partner/get_field_translations") {
return Promise.resolve([
[
{ lang: "CUST", source: "yop", value: "yop" },
{ lang: "CUST2", source: "yop", value: "valeur français" },
],
{ translation_type: "char", translation_show_source: false },
]);
}
},
});
@@ -8536,7 +8525,7 @@ QUnit.module("Views", (hooks) => {
await click(target, ".o_field_translate.btn-link");
await click(target.querySelectorAll(".modal-footer button")[0]); // save
assert.verifySteps(["create", "read", "translate_fields", "get_installed", "search_read"]);
assert.verifySteps(["create", "read", "get_installed", "get_field_translations"]);
assert.containsOnce(target, ".modal");
assert.strictEqual(target.querySelector(".modal-title").textContent, "Translate: foo");
});
@@ -8583,12 +8572,21 @@ QUnit.module("Views", (hooks) => {
["CUST2", "second custom"],
];
}
if (route === "/web/dataset/call_kw/partner/get_field_translations") {
return Promise.resolve([
[
{ lang: "CUST", source: "yop", value: "yop" },
{ lang: "CUST2", source: "yop", value: "valeur français" },
],
{ translation_type: "char", translation_show_source: false },
]);
}
},
});
assert.verifySteps(["get_views", "read"]);
await clickEdit(target);
await click(target, ".o_field_legacy_char .o_field_translate.btn-link");
assert.verifySteps(["translate_fields", "get_installed", "search_read"]);
assert.verifySteps(["get_installed", "get_field_translations"]);
assert.containsOnce(target, ".modal");
assert.strictEqual(target.querySelector(".modal-title").textContent, "Translate: foo");
});
@@ -8644,22 +8642,11 @@ QUnit.module("Views", (hooks) => {
});
QUnit.test("translation alerts preserved on reverse breadcrumb", async function (assert) {
serverData.models["ir.translation"] = {
fields: {
name: { string: "name", type: "char" },
source: { string: "Source", type: "char" },
value: { string: "Value", type: "char" },
},
records: [],
};
serverData.models.partner.fields.foo.translate = true;
serverData.views = {
"partner,false,form": `<form><sheet><field name="foo"/></sheet></form>`,
"partner,false,search": "<search></search>",
"ir.translation,false,list": `<tree><field name="name"/><field name="source"/><field name="value"/></tree>`,
"ir.translation,false,search": "<search></search>",
};
serverData.actions = {
@@ -8670,15 +8657,6 @@ QUnit.module("Views", (hooks) => {
type: "ir.actions.act_window",
views: [[false, "form"]],
},
2: {
id: 2,
name: "Translate",
res_model: "ir.translation",
type: "ir.actions.act_window",
views: [[false, "list"]],
target: "current",
flags: { search_view: true, action_buttons: true },
},
};
const webClient = await createWebClient({ serverData });
@@ -8692,17 +8670,12 @@ QUnit.module("Views", (hooks) => {
await clickSave(target);
assert.containsOnce(target, ".alert .o_field_translate", "should have a translation alert");
await doAction(webClient, 2);
await click(target.querySelector(".o_control_panel .breadcrumb a:nth-child(1)"));
assert.containsOnce(target, ".alert .o_field_translate", "should have a translation alert");
});
QUnit.test(
"translate event correctly handled with multiple controllers",
async function (assert) {
assert.expect(3);
assert.expect(2);
serverData.models.product.fields.name.translate = true;
serverData.models.partner.records[0].product_id = 37;
@@ -8742,23 +8715,21 @@ QUnit.module("Views", (hooks) => {
if (route === "/web/dataset/call_kw/product/get_formview_id") {
return false;
}
if (
route === "/web/dataset/call_button" &&
args.method === "translate_fields"
) {
assert.deepEqual(
args.args,
["product", 37, "name"],
'should call "call_button" route'
);
nbTranslateCalls++;
return {
domain: [],
context: { search_default_name: "partnes,foo" },
};
}
if (route === "/web/dataset/call_kw/res.lang/get_installed") {
return [["en_US"], ["fr_BE"]];
return [
["en_US", "English"],
["fr_BE", "French (Belgium)"],
];
}
if (route === "/web/dataset/call_kw/product/get_field_translations") {
nbTranslateCalls++;
return Promise.resolve([
[
{ lang: "en_US", source: "yop", value: "yop" },
{ lang: "fr_BE", source: "yop", value: "valeur français" },
],
{ translation_type: "char", translation_show_source: false },
]);
}
},
});
@@ -197,34 +197,6 @@ QUnit.module("Views", (hooks) => {
},
records: [{ id: "2-20170808020000", name: "virtual" }],
},
"ir.translation": {
fields: {
lang_code: { type: "char" },
src: { type: "char" },
value: { type: "char" },
res_id: { type: "integer" },
name: { type: "char" },
lang: { type: "char" },
},
records: [
{
id: 99,
res_id: 1,
value: "",
lang_code: "en_US",
lang: "en_US",
name: "foo,foo",
},
{
id: 100,
res_id: 1,
value: "",
lang_code: "fr_BE",
lang: "fr_BE",
name: "foo,foo",
},
],
},
},
};
setupViewRegistries();
@@ -4096,18 +4068,21 @@ QUnit.module("Views", (hooks) => {
resModel: "foo",
serverData,
mockRPC(route, args) {
if (route === "/web/dataset/call_button" && args.method === "translate_fields") {
return Promise.resolve({
domain: [],
context: { search_default_name: "foo,foo" },
});
}
if (route === "/web/dataset/call_kw/res.lang/get_installed") {
return Promise.resolve([
["en_US", "English"],
["fr_BE", "Frenglish"],
]);
}
if (route === "/web/dataset/call_kw/foo/get_field_translations") {
return Promise.resolve([
[
{ lang: "en_US", source: "yop", value: "yop" },
{ lang: "fr_BE", source: "yop", value: "valeur français" },
],
{ translation_type: "char", translation_show_source: false },
]);
}
},
arch: '<tree editable="top">' + '<field name="foo" required="1"/>' + "</tree>",
});
-1
View File
@@ -5,7 +5,6 @@ from . import ir_attachment
from . import ir_qweb_fields
from . import ir_ui_view
from . import ir_http
from . import ir_translation
from . import ir_websocket
from . import models
+7 -4
View File
@@ -155,10 +155,13 @@ class Field(models.AbstractModel):
attrs['placeholder'] = placeholder
if options['translate'] and field.type in ('char', 'text'):
name = "%s,%s" % (record._name, field_name)
domain = [('name', '=', name), ('res_id', '=', record.id), ('type', '=', 'model'), ('lang', '=', options.get('lang'))]
translation = record.env['ir.translation'].search(domain, limit=1)
attrs['data-oe-translation-state'] = translation and translation.state or 'to_translate'
lang = record.env.lang or 'en_US'
if lang == 'en_US':
attrs['data-oe-translation-state'] = 'translated'
else:
value_en = record.with_context(lang='en_US')[field_name]
value_lang = record.with_context(lang=lang)[field_name]
attrs['data-oe-translation-state'] = 'translated' if value_en != value_lang else 'to_translate'
return attrs
@@ -1,46 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from lxml import etree
from odoo import models, api
from odoo.tools.translate import encode, xml_translate, html_translate
def edit_translation_mapping(data):
data = dict(data, model=data['name'].partition(',')[0], value=data['value'] or data['src'])
return '<span data-oe-model="%(model)s" data-oe-translation-id="%(id)s" data-oe-translation-state="%(state)s">%(value)s</span>' % data
class IrTranslation(models.Model):
_inherit = 'ir.translation'
@api.model
def _get_terms_mapping(self, field, records):
if self._context.get('edit_translations'):
self.insert_missing(field, records)
return edit_translation_mapping
return super(IrTranslation, self)._get_terms_mapping(field, records)
def save_html(self, value):
""" Convert the HTML fragment ``value`` to XML if necessary, and write
it as the value of translation ``self``.
"""
assert len(self) == 1 and self.type == 'model_terms'
mname, fname = self.name.split(',')
field = self.env[mname]._fields[fname]
if field.translate == xml_translate:
# wrap value inside a div and parse it as HTML
div = "<div>%s</div>" % encode(value)
root = etree.fromstring(div, etree.HTMLParser(encoding='utf-8'))
# root is html > body > div
# serialize div as XML and discard surrounding tags
value = etree.tostring(root[0][0], encoding='utf-8')[5:-6]
elif field.translate == html_translate:
# wrap value inside a div and parse it as HTML
div = "<div>%s</div>" % encode(value)
root = etree.fromstring(div, etree.HTMLParser(encoding='utf-8'))
# root is html > body > div
# serialize div as HTML and discard surrounding tags
value = etree.tostring(root[0][0], encoding='utf-8', method='html')[5:-6]
return self.write({'value': value})
+9
View File
@@ -1,5 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from hashlib import sha256
from odoo import api, models
@@ -12,3 +14,10 @@ class Base(models.AbstractModel):
keys.append('sanitize')
keys.append('sanitize_tags')
return keys
class BaseModel(models.AbstractModel):
_inherit = 'base'
def update_field_translations_sha(self, fname, translations):
return self._update_field_translations(fname, translations, lambda old_term: sha256(old_term.encode()).hexdigest())
@@ -3818,8 +3818,8 @@ var SnippetsMenu = Widget.extend({
}
if (!range ||
!$currentSelectionTarget.parents('#wrapwrap, .iframe-editor-wrapper .o_editable').length ||
$(selection.anchorNode).parent('[data-oe-model]:not([data-oe-type="html"]):not([data-oe-field="arch"]):not([data-oe-translation-id])').length ||
$(selection.focusNode).parent('[data-oe-model]:not([data-oe-type="html"]):not([data-oe-field="arch"]):not([data-oe-translation-id])').length ||
$(selection.anchorNode).parent('[data-oe-model]:not([data-oe-type="html"]):not([data-oe-field="arch"]):not([data-oe-translation-initial-sha])').length ||
$(selection.focusNode).parent('[data-oe-model]:not([data-oe-type="html"]):not([data-oe-field="arch"]):not([data-oe-translation-initial-sha])').length ||
(e && $(e.target).closest('.fa, img').length ||
this.options.wysiwyg.lastMediaClicked && $(this.options.wysiwyg.lastMediaClicked).is('.fa, img')) ||
(this.options.wysiwyg.lastElement && !this.options.wysiwyg.lastElement.isContentEditable)

Some files were not shown because too many files have changed in this diff Show More