Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions odoo_module_migrate/migration_scripts/migrate_130_allways.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import re
from odoo_module_migrate.base_migration_script import BaseMigrationScript


def multi_value_translation_replacement_function(match, single_quote=True):
format_string = match.group(1)
dictionary_entries = match.group(2)

formatted_entries = []
for entry in dictionary_entries.split(","):
if ":" in entry:
[key, value] = entry.split(":")
formatted_entries.append(
"{}={}".format(key.strip().strip("'").strip('"'), value.strip())
)

formatted_entries = ", ".join(formatted_entries)

if single_quote:
return f"_('{format_string}', {formatted_entries})"
return f'_("{format_string}", {formatted_entries})'


def format_parenthesis(match):
format_string = match.group(1)
dictionary_entries = match.group(2)

if dictionary_entries.endswith(","):
dictionary_entries = dictionary_entries[:-1]

return f"_({format_string}, {dictionary_entries})"


def format_replacement_function(match, single_quote=True):
format_string = re.sub(r"\{\d*\}", "%s", match.group(1))
format_string = re.sub(r"{(\w+)}", r"%(\1)s", format_string)
arguments = " ".join(match.group(2).split())

if arguments.endswith(","):
arguments = arguments[:-1]

if single_quote:
return f"_('{format_string}', {arguments})"
return f'_("{format_string}", {arguments})'


def replace_translation_function(
logger, module_path, module_name, manifest_path, migration_steps, tools
):
files_to_process = tools.get_files(module_path, (".py",))

replaces = {
r'_\(\s*"([^"]+)"\s*\)\s*%\s*\{([^}]+)\}': lambda match: multi_value_translation_replacement_function(
match, single_quote=False
),
r"_\(\s*'([^']+)'\s*\)\s*%\s*\{([^}]+)\}": lambda match: multi_value_translation_replacement_function(
match, single_quote=True
),
r'_\(\s*(["\'].*?%[ds].*?["\'])\s*\)\s*%\s*\(\s*(.+)\s*\)': format_parenthesis,
r'_\(\s*(["\'].*?%[ds].*?["\'])\s*\)\s*?%\s*?([^\s]+)': r"_(\1, \2)",
r'_\(\s*"([^"]*)"\s*\)\.format\(\s*(\s*[^)]+)\)': lambda match: format_replacement_function(
match, single_quote=False
),
r"_\(\s*'([^']*)'\s*\)\.format\(\s*(\s*[^)]+)\)": lambda match: format_replacement_function(
match, single_quote=True
),
}

for file in files_to_process:
try:
tools._replace_in_file(
file,
replaces,
log_message=f"""Improve _() function: {file}""",
)
except Exception as e:
logger.error(f"Error processing file {file}: {str(e)}")


class MigrationScript(BaseMigrationScript):

_GLOBAL_FUNCTIONS = [replace_translation_function]
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.py:
_\(\s*["\'].*?%s.*?["\']: "[Warning] In some languages the order of the placeholders may have to be modified, which is impossible if they are unnamed. We can use named placedholders to avoid this"
1 change: 1 addition & 0 deletions tests/data_result/module_130_140/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
1 change: 1 addition & 0 deletions tests/data_result/module_130_140/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import res_partner
58 changes: 58 additions & 0 deletions tests/data_result/module_130_140/models/res_partner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import _, models
from odoo.exceptions import ValidationError


class ResPartner(models.Model):
_inherit = "res.partner"

def test_improve_translation(self):
return _("It's %s", 2024)

def test_improve_transalation_with_line_break(self):
raise ValidationError(
_("The '%s' is empty or 0. It should have a non-null value.", "Price")
)

def test_improve_translation_with_parenthesis(self):
return _("It's %s", 2024)

def test_improve_translation_with_single_quote(self):
return _('It is %s', 2024)

def test_improve_translation_with_parenthesis_and_line_break(self):
raise ValidationError(
_("%s are only valid until %s", "User", 2024)
)

def test_improve_translation_with_brackets(self):
return _("User %(name)s has %(items)s items", name="Dev", items=5)

def test_improve_translation_with_single_quote(self):
return _('User %(name)s has %(items)s items', name='Dev', items=5)

def test_improve_translation_with_brackets_and_line_break(self):
return _("User %(name)s has %(items)s items", name="Dev", items=5)

def test_improve_translation_with_format(self):
return _("It's %s", 2024)

def test_improve_translation_with_format_and_single_quote(self):
return _('It is %s', 2024)

def test_improve_translation_with_format_and_line_break(self):
return _("It's %s", 2024)

def test_improve_translation_with_format_has_end_comma(self):
return _("It's %s", 2024)

def test_improve_translation_with_format_multi_params(self):
return _("User %(name)s has %(items)s items", name="Dev", items=5)

def test_improve_translation_with_format_multi_params_and_line_break(self):
return _("User %(name)s has %(items)s items", name="Dev", items=5)

def test_improve_translation_with_format_multi_params_has_end_comma(self):
return _('User %(name)s has "acb" %(items)s items', name="Dev", items=5)

1 change: 1 addition & 0 deletions tests/data_template/module_130/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
1 change: 1 addition & 0 deletions tests/data_template/module_130/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import res_partner
75 changes: 75 additions & 0 deletions tests/data_template/module_130/models/res_partner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import _, models
from odoo.exceptions import ValidationError


class ResPartner(models.Model):
_inherit = "res.partner"

def test_improve_translation(self):
return _("It's %s") % 2024

def test_improve_transalation_with_line_break(self):
raise ValidationError(
_("The '%s' is empty or 0. It should have a non-null value.")
% "Price"
)

def test_improve_translation_with_parenthesis(self):
return _("It's %s") % (2024)

def test_improve_translation_with_single_quote(self):
return _('It is %s') % (2024)

def test_improve_translation_with_parenthesis_and_line_break(self):
raise ValidationError(
_("%s are only valid until %s") % (
"User", 2024,
)
)

def test_improve_translation_with_brackets(self):
return _("User %(name)s has %(items)s items") % {"name":"Dev", "items": 5}

def test_improve_translation_with_single_quote(self):
return _('User %(name)s has %(items)s items') % {'name':'Dev', 'items': 5}

def test_improve_translation_with_brackets_and_line_break(self):
return _(
"User %(name)s has %(items)s items"
) % {
"name": "Dev",
"items": 5,
}

def test_improve_translation_with_format(self):
return _("It's {}").format(2024)

def test_improve_translation_with_format_and_single_quote(self):
return _('It is {}').format(2024)

def test_improve_translation_with_format_and_line_break(self):
return _(
"It's {}"
).format(
2024
)

def test_improve_translation_with_format_has_end_comma(self):
return _("It's {}").format(2024,)

def test_improve_translation_with_format_multi_params(self):
return _("User {name} has {items} items").format(name="Dev", items=5)

def test_improve_translation_with_format_multi_params_and_line_break(self):
return _(
"User {name} has {items} items"
).format(
name="Dev",
items=5
)

def test_improve_translation_with_format_multi_params_has_end_comma(self):
return _('User {name} has "acb" {items} items').format(name="Dev", items=5,)

Loading