Skip to content

Commit

Permalink
[IMP] _() improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
chaule97 committed Dec 11, 2024
1 parent 38e7a02 commit 509bf1b
Show file tree
Hide file tree
Showing 8 changed files with 222 additions and 0 deletions.
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,)

0 comments on commit 509bf1b

Please sign in to comment.