Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add localization support #827

Merged
merged 44 commits into from
Feb 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
1319092
Add i18n
bkthomps Sep 17, 2022
3c97359
Fix lint rules
bkthomps Sep 17, 2022
06891b6
Fix lint issues
bkthomps Sep 17, 2022
0125f68
Fix lint issues
bkthomps Sep 17, 2022
0bc1dfa
translations using gettext
dragoon Nov 29, 2022
78cedfa
Merge branch 'master' into gettext-translations
dragoon Nov 29, 2022
318e02a
Merge remote-tracking branch 'origin/beta' into gettext-translations
dragoon Nov 29, 2022
9eca48f
Add i18n PoC.
arkid15r Dec 8, 2022
a5004e3
Add MY, SG.
arkid15r Dec 8, 2022
ae9e138
Add the rest of changes.
arkid15r Dec 8, 2022
6c003ec
Merge branch 'beta' into i18n
arkid15r Dec 8, 2022
8f3c456
Add ISO 639-1 suggestion.
arkid15r Dec 8, 2022
2d9fc85
Accept name opt. suggestion.
arkid15r Dec 8, 2022
ae24c87
Address review comments.
arkid15r Dec 8, 2022
dbf3865
Merge branch 'beta' into i18n
arkid15r Dec 9, 2022
f40a858
Replace `ValueError` with `warnings.warn` for unavailable translations.
arkid15r Dec 9, 2022
20ad4e0
Attempt to fix Windows tests.
arkid15r Dec 9, 2022
ce0a504
Introduce `HolidayBase::copy()`.
arkid15r Dec 9, 2022
b3e6efc
Add multiple i18n updates.
arkid15r Dec 19, 2022
bccced2
Merge branch 'beta' into i18n
arkid15r Dec 19, 2022
29dd118
Attempt to fix i18n tests.
arkid15r Dec 19, 2022
e4348a3
Attempt to fix GH CI/CD.
arkid15r Dec 19, 2022
9f4322d
Attempt to fix GH CI/CD (Windows).
arkid15r Dec 19, 2022
142644e
Attempt to fix GH CI/CD (Windows) #2.
arkid15r Dec 20, 2022
9c9fe37
Standardize .py file headers.
arkid15r Dec 23, 2022
4965efb
Move locale directory to holidays.
arkid15r Jan 10, 2023
be67ab5
Merge branch 'beta' into i18n
arkid15r Jan 10, 2023
2214ee5
Merge branch 'beta' into i18n
arkid15r Jan 20, 2023
d427307
Merge branch 'beta' of github.com:arkid15r/python-holidays into beta
arkid15r Feb 3, 2023
e9b748e
Merge branch 'beta' into i18n
arkid15r Feb 3, 2023
8d3d893
Merge branch 'beta' into i18n
arkid15r Feb 4, 2023
799942b
Merge branch 'beta' of github.com:arkid15r/python-holidays into beta
arkid15r Feb 4, 2023
67aa848
Merge branch 'beta' into i18n
arkid15r Feb 4, 2023
8a3aa4e
Merge branch 'beta' into i18n
arkid15r Feb 10, 2023
8913073
Merge branch 'beta' into i18n
arkid15r Feb 10, 2023
596bc91
Simplify test classes setup.
arkid15r Feb 11, 2023
15b8309
Exclude tests from the MANIFEST.in.
arkid15r Feb 11, 2023
6d43e3c
Rework i18n.
arkid15r Feb 13, 2023
64c2b26
Merge branch 'beta' of github.com:arkid15r/python-holidays into beta
arkid15r Feb 13, 2023
956843f
Merge branch 'beta' into i18n
arkid15r Feb 13, 2023
16bce8d
Update pre-commit settings.
arkid15r Feb 13, 2023
eddf797
Fix naming and minor issues in .pot files generator.
arkid15r Feb 13, 2023
1881234
Update translations.
arkid15r Feb 13, 2023
07f33a5
Add final fixes.
arkid15r Feb 13, 2023
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
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
build/
build/*
dist/
venv/
MANIFEST
.coverage
*.egg-info
*.mo
*.pot
*.pyc
*.DS_Store
coverage.xml
.tox
.venv
docs/_build
docs/build
docs/source/_build
holidays/locale/pot

# IDE Stuff
.idea
Expand Down
4 changes: 3 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ repos:
rev: 23.1.0
hooks:
- id: black
exclude: ^(docs|scripts/l10n/msgfmt.py)
language_version: python3

- repo: https://github.com/pycqa/flake8
Expand All @@ -24,6 +25,7 @@ repos:
additional_dependencies:
- flake8-print
args: [--max-line-length=79]
exclude: ^(docs|scripts/l10n/msgfmt.py)

- repo: https://github.com/pre-commit/mirrors-isort
rev: v5.10.1
Expand All @@ -32,7 +34,7 @@ repos:
# holidays/countries/__init__.py is excluded due to a bug whereby
# black and isort don't agree and enter into an infinite fix loop.
# TODO remove this exclusion when isort and black play nice again.
exclude: ^docs/|^holidays/countries/__init__.py
exclude: ^(docs|holidays/countries/__init__.py|scripts/l10n/msgfmt.py)

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.0.0
Expand Down
43 changes: 40 additions & 3 deletions CONTRIBUTING.rst
Original file line number Diff line number Diff line change
Expand Up @@ -59,23 +59,60 @@ You can specific tests using ``pytest`` command:

.. code-block:: shell

$ pytest tests/countries/test_albania.py
$ pytest tests/countries/test_argentina.py

Or even more granular:

.. code-block:: shell

$ pytest tests/countries/test_albania.py::TestAlbania::test_country_aliases
$ pytest tests/countries/test_argentina.py::TestArgentina::test_country_aliases

Due to how pytest-xdist is implemented, the -s/--capture=no option
`doesn't work <https://pytest-xdist.readthedocs.io/en/latest/known-limitations.html#output-stdout-and-stderr-from-workers>`_.
Use pytest directly if you need ``-s`` option:

.. code-block:: shell

$ pytest -s tests/countries/test_albania.py
$ pytest -s tests/countries/test_argentina.py


Localization
--------------------------
.. _ISO 639-1 codes: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes

In order to add or update existing holiday names translation you'll need to
generate pygettext .pot file first:

.. code-block:: shell

$ make l10n

If the template file is empty make sure that the country/market entity has the
:py:attr:`default_language` attribute set and all holiday names are wrapped
with ``tr``/``self.tr`` helpers. Use `ISO 639-1 codes`_ when adding new
languages. Copy the generated template to all locale folders you're going to
translate this country holiday names into (e.g., for Argentina:
holidays/locale/en/LC_MESSAGES/AR.po - note the file extension difference here).
Also copy the template to a default country language folder (e.g., for Argentina
holidays/locale/es/LC_MESSAGES) and leave it as is. After copying the .po files
open them with your favorite .po file editor and translate accordingly. Don't
forget to fill in the translation file headers. Finally, update the list of
supported translations for the country in the README.rst.

If the translation already exists you'll just need to update it with the new
template entries (your .po file editor may help you to do that with no hassle).

Please also add tests (see already translated countries tests for examples).
The .mo files are generated automatically for the tests and the python-holidays
package so you shouldn't worry about it. Just don't forget to
initialize the ``setUpClass`` properly:

.. code-block:: python

@classmethod
def setUpClass(cls):
super().setUpClass(Argentina)

Build sphinx documentation
--------------------------

Expand Down
8 changes: 6 additions & 2 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
exclude tests/*
include *.py
include *.rst
include LICENSE
include CHANGES
include py.typed
include holidays/locale/*/LC_MESSAGES/*.mo
include holidays/locale/*/LC_MESSAGES/*.po
include holidays/py.typed
include LICENSE
include scripts/l10n/msgfmt.py
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ help:
@echo " coverage identify code not covered with tests"
@echo " help show summary of available commands"
@echo " install install and setup development environment"
@echo " l10n update .pot and .po files"
@echo " pre-commit run pre-commit against all files"
@echo " test run tests (in parallel)"
@echo " tox run tox (in parallel)"
Expand All @@ -21,6 +22,10 @@ install:
pip install --requirement requirements/docs.txt
pre-commit install --hook-type pre-commit
pre-commit install --hook-type pre-push
mkdir -p holidays/locale/pot

l10n:
scripts/l10n/generate_po_files.py

pre-commit:
pre-commit run --all-files
Expand Down
36 changes: 36 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,42 @@ following financial markets are available:
- NYSE market holidays (used by all other US-exchanges, including NASDAQ, etc.)


Internationalization
===========================

.. _ISO 639-1 code: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes

Some of the available countries support more than one
language for holiday names output. The default language is defined by
:py:attr:`default_language` of :py:class:`HolidayBase` and is used as a fallback
when neither user specified language nor user locale language available. The
default language code is a `ISO 639-1 code`_.

.. list-table::
:widths: 23 4 83
:header-rows: 1
:class: tight-table

* - Country
- Default Language
- Supported languages
* - Argentina
- es
- en, es
* - Canada
- en
- en, fr
* - Denmark
- da
- da, en
* - Poland
- pl
- en, pl
* - Ukraine
- uk
- en, uk


Beta Version
------------

Expand Down
63 changes: 25 additions & 38 deletions holidays/countries/argentina.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class Argentina(HolidayBase):
"""

country = "AR"
default_language = "es"

def _populate(self, year):
super()._populate(year)
Expand All @@ -37,18 +38,17 @@ def _populate(self, year):
if not self.observed and self._is_weekend(year, JAN, 1):
pass
else:
self[date(year, JAN, 1)] = "Año Nuevo [New Year's Day]"
self[date(year, JAN, 1)] = self.tr("Año Nuevo")

easter_date = easter(year)
# Carnival days
name = "Día de Carnaval [Carnival's Day]"
name = self.tr("Día de Carnaval")
self[easter_date + td(days=-48)] = name
self[easter_date + td(days=-47)] = name

# Memory's National Day for the Truth and Justice
name = (
"Día Nacional de la Memoria por la Verdad y la Justicia "
"[Memory's National Day for the Truth and Justice]"
name = self.tr(
"Día Nacional de la Memoria por la Verdad y la Justicia"
)

if not self.observed and self._is_weekend(year, MAR, 24):
Expand All @@ -57,9 +57,9 @@ def _populate(self, year):
self[date(year, MAR, 24)] = name

# Holy Week
name_thu = "Semana Santa (Jueves Santo) [Holy day (Holy Thursday)]"
name_fri = "Semana Santa (Viernes Santo) [Holy day (Holy Friday)]"
name_easter = "Día de Pascuas [Easter Day]"
name_thu = self.tr("Semana Santa (Jueves Santo)")
name_fri = self.tr("Semana Santa (Viernes Santo)")
name_easter = self.tr("Día de Pascuas")

self[easter_date + td(days=-3)] = name_thu
self[easter_date + td(days=-2)] = name_fri
Expand All @@ -73,61 +73,52 @@ def _populate(self, year):
if not self.observed and self._is_weekend(year, APR, 2):
pass
else:
self[date(year, APR, 2)] = (
"Día del Veterano y de los Caidos "
"en la Guerra de Malvinas [Veterans"
" Day and the Fallen in the"
" Malvinas War]"
self[date(year, APR, 2)] = self.tr(
"Día del Veterano y de los Caidos en la Guerra de Malvinas"
)

# Labor Day
name = "Día del Trabajo [Labour Day]"
name = self.tr("Día del Trabajo")
if not self.observed and self._is_weekend(year, MAY, 1):
pass
else:
self[date(year, MAY, 1)] = name

# May Revolution Day
name = "Día de la Revolucion de Mayo [May Revolution Day]"
name = self.tr("Día de la Revolución de Mayo")
if not self.observed and self._is_weekend(year, MAY, 25):
pass
else:
self[date(year, MAY, 25)] = name

# Day Pass to the Immortality of General Martín Miguel de Güemes.
name = (
"Día Pase a la Inmortalidad "
"del General Martín Miguel de Güemes [Day Pass "
"to the Immortality of General Martín Miguel de Güemes]"
name = self.tr(
"Día Pase a la Inmortalidad del General Martín Miguel de Güemes"
)
if not self.observed and self._is_weekend(year, JUN, 17):
pass
else:
self[date(year, JUN, 17)] = name

# Day Pass to the Immortality of General D. Manuel Belgrano.
name = (
"Día Pase a la Inmortalidad "
"del General D. Manuel Belgrano [Day Pass "
"to the Immortality of General D. Manuel Belgrano]"
name = self.tr(
"Día Pase a la Inmortalidad del General D. Manuel Belgrano"
)
if not self.observed and self._is_weekend(year, JUN, 20):
pass
else:
self[date(year, JUN, 20)] = name

# Independence Day
name = "Día de la Independencia [Independence Day]"
name = self.tr("Día de la Independencia")
if not self.observed and self._is_weekend(year, JUL, 9):
pass
else:
self[date(year, JUL, 9)] = name

# Day Pass to the Immortality of General D. José de San Martin
name = (
"Día Pase a la Inmortalidad "
"del General D. José de San Martin [Day Pass "
"to the Immortality of General D. José de San Martin]"
name = self.tr(
"Día Pase a la Inmortalidad del General D. José de San Martin"
)
if not self.observed and self._is_weekend(year, AUG, 17):
pass
Expand All @@ -138,15 +129,13 @@ def _populate(self, year):
if not self.observed and self._is_weekend(year, OCT, 12):
pass
elif year < 2010:
self[date(year, OCT, 12)] = "Día de la Raza [Columbus day]"
self[date(year, OCT, 12)] = self.tr("Día de la Raza")
else:
self[date(year, OCT, 12)] = (
"Día del Respeto a la Diversidad"
" Cultural [Respect for"
" Cultural Diversity Day]"
self[date(year, OCT, 12)] = self.tr(
"Día del Respeto a la Diversidad Cultural"
)
# National Sovereignty Day
name = "Día Nacional de la Soberanía [National Sovereignty Day]"
name = self.tr("Día Nacional de la Soberanía")
if not self.observed and self._is_weekend(year, NOV, 20):
pass
elif year >= 2010:
Expand All @@ -156,12 +145,10 @@ def _populate(self, year):
if not self.observed and self._is_weekend(year, DEC, 8):
pass
else:
self[
date(year, DEC, 8)
] = "La Inmaculada Concepción [Immaculate Conception]"
self[date(year, DEC, 8)] = self.tr("La Inmaculada Concepción")

# Christmas
self[date(year, DEC, 25)] = "Navidad [Christmas]"
self[date(year, DEC, 25)] = self.tr("Navidad")


class AR(Argentina):
Expand Down
Loading