1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
|
#!/usr/bin/env python3
# holidays
# --------
# A fast, efficient Python library for generating country, province and state
# specific sets of holidays on the fly. It aims to make determining whether a
# specific date is a holiday as fast and flexible as possible.
#
# Authors: Vacanza Team and individual contributors (see CONTRIBUTORS file)
# dr-prodigy <dr.prodigy.github@gmail.com> (c) 2017-2023
# ryanss <ryanssdev@icloud.com> (c) 2014-2017
# Website: https://github.com/vacanza/holidays
# License: MIT (see LICENSE file)
import importlib
import inspect
import sys
from concurrent.futures import ProcessPoolExecutor
from pathlib import Path
from time import perf_counter
from lingva.extract import extract as create_pot_file
from lingva.extract import _location_sort_key
from polib import pofile
sys.path.insert(0, str(Path.cwd())) # Make holidays visible.
from holidays import __version__ as package_version
from holidays.holiday_base import HolidayBase
WRAP_WIDTH = 99
class POGenerator:
"""Generates .po files for supported country/market entities."""
@staticmethod
def _process_entity_worker(
entity_code_info: tuple[str, tuple[str, Path]],
) -> list[tuple[str, str]]:
"""Process a single entity: create .pot, default .po, and return update tasks."""
entity_code, (default_language, class_file_path) = entity_code_info
locale_path = Path("holidays/locale")
pot_path = locale_path / "pot"
pot_path.mkdir(exist_ok=True)
pot_file_path = pot_path / f"{entity_code}.pot"
# Create .pot file.
create_pot_file(
sources=[class_file_path],
keywords=["tr"],
output=pot_file_path,
package_name="Holidays",
package_version=package_version,
width=WRAP_WIDTH,
allow_empty=True,
)
# Update .pot file metadata.
pot_file = pofile(pot_file_path, wrapwidth=WRAP_WIDTH)
pot_file.metadata.update(
{
"Language": default_language,
"Language-Team": "Holidays Localization Team",
"X-Source-Language": default_language,
}
)
pot_file.save(newline="\n")
# Create entity default .po file from the .pot file.
po_directory = locale_path / default_language / "LC_MESSAGES"
po_directory.mkdir(parents=True, exist_ok=True)
default_po_path = po_directory / f"{entity_code}.po"
if not default_po_path.exists():
pot_file.save(str(default_po_path), newline="\n")
# Collect .po update tasks.
return [
(str(po_file_path), str(pot_file_path))
for po_file_path in locale_path.rglob(f"{entity_code}.po")
]
@staticmethod
def _update_po_file(args: tuple[str, str]) -> None:
"""Merge .po file with .pot"""
po_path, pot_path = args
po_file = pofile(po_path, wrapwidth=WRAP_WIDTH)
po_file_initial = po_file.copy()
po_file.merge(pofile(pot_path))
po_file.sort(key=_location_sort_key)
for entry in po_file:
entry.occurrences.clear()
# Only update the project version if po file translation entries has changed.
if po_file != po_file_initial:
po_file.metadata["Project-Id-Version"] = f"Holidays {package_version}"
# Save the file each time in order to capture all other changes properly.
po_file.save(po_path, newline="\n")
def process_entities(self):
"""Processes entities in specified directory."""
entity_code_info_mapping = {}
for entity_type in ("countries", "financial"):
for path in Path(f"holidays/{entity_type}").glob("*.py"):
if path.stem == "__init__":
continue
module = f"holidays.{entity_type}.{path.stem}"
for _, cls in inspect.getmembers(importlib.import_module(module), inspect.isclass):
if (
issubclass(cls, HolidayBase)
and cls.__module__ == module
and getattr(cls, "default_language") is not None
):
name = getattr(cls, "country", getattr(cls, "market", None))
entity_code_info_mapping[name.upper()] = (cls.default_language, path)
break
all_po_update_tasks: list[tuple[str, str]] = []
with ProcessPoolExecutor() as executor:
for po_tasks in executor.map(
self._process_entity_worker, entity_code_info_mapping.items()
):
all_po_update_tasks.extend(po_tasks)
list(executor.map(POGenerator._update_po_file, all_po_update_tasks))
@staticmethod
def run():
"""Runs the .po files generation process."""
POGenerator().process_entities()
if __name__ == "__main__":
po_time_start = perf_counter()
POGenerator.run()
po_time_end = perf_counter()
print(f"[TIMER] Total generate po files runtime: {po_time_end - po_time_start:.2f} seconds")
|