Skip to content

Commit

Permalink
feat: add new options, merge-js, combile-partial, and text-domain
Browse files Browse the repository at this point in the history
combine-partial: no partial files are generated, because they replaced the original django.po and djangojs.po
merge-js: no djangojs*.po is generated, because it has been merged into django*.po
text-domain: django*.po and djangojs*.po are renamed to text*.po and textjs*.po respectively

Refs: FC-0012 OEP-58
  • Loading branch information
shadinaif committed Sep 1, 2023
1 parent 53b8f06 commit d58e142
Show file tree
Hide file tree
Showing 2 changed files with 167 additions and 10 deletions.
69 changes: 65 additions & 4 deletions i18n/extract.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from path import Path

from i18n import Runner
from i18n.execute import execute
from i18n.execute import remove_file, execute
from i18n.segment import segment_pofiles


Expand Down Expand Up @@ -57,6 +57,30 @@ def add_args(self):
Adds arguments
"""
self.parser.description = __doc__
self.parser.add_argument(
'--merge-js',
action='store_true',
help='Merge djangojs.po with django.po using msgcat'
)
self.parser.add_argument(
'--combine-partial',
action='store_true',
help=(
'Renames (django-partial.po) and (djangojs-partial.po) to (django.po) and (djangojs.po) respectively. '
'This will also remove (django-saved.po) and (djangojs-saved.po) that are used to restore original '
'(django.po) and (djangojs.po) after extract is completed. '
'This is helpful when this `extract` command output is needed to be used immediately '
'with no segment/merge workflow in small repositories.'
)
)
self.parser.add_argument(
'--text-domain',
action='store_true',
help=(
'Use the name "text" instead of "django" for the generated files. This is useful for XBlocks '
'since they depend on the "text" domain.'
)
)

def rename_source_file(self, src, dst):
"""
Expand Down Expand Up @@ -149,9 +173,31 @@ def run(self, args):
for filename in files_to_clean:
clean_pofile(self.source_msgs_dir.joinpath(filename))

# Restore the saved .po files.
self.rename_source_file('django-saved.po', 'django.po')
self.rename_source_file('djangojs-saved.po', 'djangojs.po')
if args.merge_js:
self.merge_js(stderr)

if args.combine_partial:
# Overwrite django.po and djangojs.po from django-partial.po and djangojs-partial.po
self.rename_source_file('django-partial.po', 'django.po')
self.rename_source_file('djangojs-partial.po', 'djangojs.po')
remove_file(self.source_msgs_dir.joinpath('django-saved.po'))
remove_file(self.source_msgs_dir.joinpath('djangojs-saved.po'))
else:
# Restore the saved .po files.
self.rename_source_file('django-saved.po', 'django.po')
self.rename_source_file('djangojs-saved.po', 'djangojs.po')

if args.text_domain:
partial_postfix = '' if args.combine_partial else '-partial'
# Rename the generated files to use the text domain.
self.rename_source_file(
'django{partial_postfix}.po'.format(partial_postfix=partial_postfix),
'text{partial_postfix}.po'.format(partial_postfix=partial_postfix),
)
self.rename_source_file(
'djangojs{partial_postfix}.po'.format(partial_postfix=partial_postfix),
'textjs{partial_postfix}.po'.format(partial_postfix=partial_postfix),
)

def babel_extract(self, stderr, verbosity):
"""
Expand Down Expand Up @@ -189,6 +235,21 @@ def babel_extract(self, stderr, verbosity):

execute(babel_underscore_cmd, working_directory=configuration.root_dir, stderr=stderr)

def merge_js(self, stderr):
"""
Merge djangojs.po with django.po using msgcat
"""
# Some projects don't have any javascript, so there is no djangojs-partial.po
if not file_exists(self.source_msgs_dir.joinpath('djangojs-partial.po')):
return

execute(
'msgcat django-partial.po djangojs-partial.po -o django-partial.po',
working_directory=self.source_msgs_dir,
stderr=stderr,
)
remove_file(self.source_msgs_dir.joinpath('djangojs-partial.po'))


def clean_pofile(path_name):
"""
Expand Down
108 changes: 102 additions & 6 deletions tests/test_extract.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from datetime import datetime, timedelta
from functools import wraps

import ddt
import polib
from i18n import extract, config
from path import Path
Expand All @@ -12,26 +13,50 @@
def perform_extract():
"""
Decorator for test methods in TestExtract class.
It wraps the test method in a function that calls (extract.main) with various options using (ddt.data)
Sets the following attributes:
- ddt_flag_merge_js: True if the test method should be run with merge-js=True
- ddt_flag_combine_partial: True if the test method should be run with combine-partial=True
"""
def decorator(test_method):
"""
The decorator itself
"""
@wraps(test_method)
def wrapped(self):
@ddt.data( # all combinations of flags
(True, True, True),
(True, True, False),
(True, False, True),
(True, False, False),
(False, True, True),
(False, True, False),
(False, False, True),
(False, False, False),
)
@ddt.unpack
def wrapped(self, flag_merge_js, flag_combine_partial, flag_text_domain):
"""
The wrapper function
"""
self.ddt_flag_merge_js = flag_merge_js
self.ddt_flag_combine_partial = flag_combine_partial
self.ddt_flag_text_domain = flag_text_domain
extract.main(
verbosity=0,
config=self.configuration._filename,
root_dir=MOCK_DJANGO_APP_DIR,
merge_js=flag_merge_js,
combine_partial=flag_combine_partial,
text_domain=flag_text_domain,
)
test_method(self)
return wrapped
return decorator


@ddt.ddt
class TestExtract(I18nToolTestCase):
"""
Tests functionality of i18n/extract.py
Expand All @@ -52,27 +77,49 @@ def setUp(self):
)
self.configuration = config.Configuration(root_dir=MOCK_DJANGO_APP_DIR)

# These will be set by extract_options decorator. Also get_files will fail if they are None (to remind us
# to use the decorator in all tests methods)
self.ddt_flag_merge_js = None
self.ddt_flag_combine_partial = None
self.ddt_flag_text_domain = None

@property
def django_po(self):
"""
Returns the name of the generated django file
Returns file or partial file name according to the combine-partial and text-domain flags
"""
return 'django-partial.po'
return '{domain}{partial_postfix}.po'.format(
domain='text' if self.ddt_flag_text_domain else 'django',
partial_postfix='' if self.ddt_flag_combine_partial else '-partial',
)

@property
def djangojs_po(self):
"""
Returns the name of the generated djangojs file
Returns jsfile or partial jsfile name according to the combine-partial and text-domain flags
"""
return 'djangojs-partial.po'
return '{domain}js{partial_postfix}.po'.format(
domain='text' if self.ddt_flag_text_domain else 'django',
partial_postfix='' if self.ddt_flag_combine_partial else '-partial',
)

def get_files(self):
"""
This is a generator.
Returns the fully expanded filenames for all extracted files
Fails assertion if one of the files doesn't exist.
"""
generated_files = ('mako.po', self.django_po, self.djangojs_po,)
assert self.ddt_flag_merge_js is not None, "Use perform_extract decorator"
assert self.ddt_flag_combine_partial is not None, "Use perform_extract decorator"
assert self.ddt_flag_text_domain is not None, "Use perform_extract decorator"

# According to merge-js, combine-partial, and text-domain flags, we may have generated different files
# combine-partial: no partial files are generated, because they replaced the original django.po and djangojs.po
# merge-js: no djangojs*.po is generated, because it has been merged into django*.po
# text-domain: django*.po and djangojs*.po are renamed to text*.po and textjs*.po respectively
generated_files = ('mako.po', self.django_po,)
if not self.ddt_flag_merge_js:
generated_files += (self.djangojs_po,)

for filename in generated_files:
path = Path.joinpath(self.configuration.source_messages_dir, filename)
Expand Down Expand Up @@ -137,3 +184,52 @@ def test_metadata_no_create_date(self):
po = polib.pofile(path)
metadata = po.metadata
self.assertIsNone(metadata.get('POT-Creation-Date'))

@perform_extract()
def test_merge_js(self):
"""
Verify that djangojs*.po is generated only if merge-js is False
"""
assert self.ddt_flag_merge_js != Path.exists(
Path.joinpath(self.configuration.source_messages_dir, self.djangojs_po,)
)

@perform_extract()
def test_combine_partial_guard_to_verify_names_in_tests(self):
"""
Verify that (django_po) and (djangojs_po) properties return the correct file names
according to combine-partial flag
"""
assert self.ddt_flag_combine_partial != ('-partial' in self.django_po)
if not self.ddt_flag_merge_js:
assert self.ddt_flag_combine_partial != ('-partial' in self.djangojs_po)

@perform_extract()
def test_combine_partial(self):
"""
Verify that partial files are not generated if combine-partial is True
"""
# We can't use (django_po) and (djangojs_po) properties here because we need to always check
# for (-partial) post-fix files
domain = 'text' if self.ddt_flag_text_domain else 'django'

# The existence of the partial files must be the opposite of the combine-partial flag
assert self.ddt_flag_combine_partial != Path.exists(
Path.joinpath(self.configuration.source_messages_dir, '{domain}-partial.po'.format(domain=domain),)
)
if not self.ddt_flag_merge_js:
assert self.ddt_flag_combine_partial != Path.exists(
Path.joinpath(self.configuration.source_messages_dir, '{domain}js-partial.po'.format(domain=domain),)
)

@perform_extract()
def test_text_domain(self):
"""
Verify that the file name 'django' and 'djangojs' are replaced by 'text' and 'textjs' respectively
when the text_domain option is set
"""
assert self.ddt_flag_text_domain == self.django_po.startswith('text')
assert self.ddt_flag_text_domain == ('django' not in self.django_po)
if not self.ddt_flag_merge_js:
assert self.ddt_flag_text_domain == self.djangojs_po.startswith('textjs')
assert self.ddt_flag_text_domain == ('django' not in self.djangojs_po)

0 comments on commit d58e142

Please sign in to comment.