diff --git a/docs/howto/specify-locale.md b/docs/howto/specify-locale.md
index cfeb46d..1123898 100644
--- a/docs/howto/specify-locale.md
+++ b/docs/howto/specify-locale.md
@@ -1,6 +1,6 @@
# Specify a locale
-`locale` is a two letter [ISO639](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) language code that `git-revision-date-localized` uses to display dates in your preferred language.
+`locale` is aa two letter [ISO639](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) language code (f.e. `en`) or [5-letter language code with added territory/region/country](https://www.mkdocs.org/user-guide/localizing-your-theme/#supported-locales) (`en_US`) that `git-revision-date-localized` uses to display dates in your preferred language.
For example:
@@ -68,6 +68,7 @@ plugins:
If no `locale` is specified anywhere, the fallback is English with the US date format (`en`).
!!! info "Supported locales"
+
- When used in combination with `type: date` or `type: datetime`, translation is done using [babel](https://github.com/python-babel/babel) which supports [these locales](http://www.unicode.org/cldr/charts/latest/supplemental/territory_language_information.html)
- When used in combination with `type: timeago` then [timeago.js](https://github.com/hustcc/timeago.js) is added to your website, which supports [these locales](https://github.com/hustcc/timeago.js/tree/master/src/lang). If you specify a locale not supported by timeago.js, the fallback is English (`en`)
diff --git a/docs/options.md b/docs/options.md
index 1f00e52..c0835d6 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -53,17 +53,19 @@ Default is `UTC`. Specify a time zone database name ([reference](https://en.wiki
## `locale`
-Default is `None`. Specify a two letter [ISO639](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) language code to display dates in your preferred language. Note this plugin supports many different ways to [specify the locale](howto/specify-locale.md), but if not specified anywhere the fallback is English (`en`).
+Default is `None`. Specify a two letter [ISO639](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) language code (f.e. `en`) or [5-letter language code with added territory/region/country](https://www.mkdocs.org/user-guide/localizing-your-theme/#supported-locales) (`en_US`) to display dates in your preferred language. Note this plugin supports many different ways to [specify the locale](howto/specify-locale.md), but if not specified _anywhere_ the fallback will be English (`en`). `locale` is used to translate timestamps to dates when `type: date` or `type: datetime` (using [babel](https://github.com/python-babel/babel)) as well as to translate datetimes to a relative timeago string when `type: timeago` (using [timeago.js](https://github.com/hustcc/timeago.js)).
Example outputs:
```yaml
-April 27, 2021 # `locale: en` with `type: date` (default)
-April 27, 2021 13:11:28 # `locale: en` with `type: datetime`
-2 weeks ago # `locale: en` with `type: timeago`
-27 de marzo de 2021 # `locale: es` with `type: date`
-27 de marzo de 2021 13:57:28 # `locale: es` with `type: datetime`
-hace 2 semanas # `locale: es` with `type: timeago`
+# `locale: en`
+April 27, 2021 # with `type: date` (default)
+April 27, 2021 13:11:28 # with `type: datetime`
+2 weeks ago # with `type: timeago`
+# `locale: es`
+27 de marzo de 2021 # with `type: date`
+27 de marzo de 2021 13:57:28 # with `type: datetime`
+hace 2 semanas # with `type: timeago`
```
=== ":octicons-file-code-16: mkdocs.yml"
@@ -71,9 +73,13 @@ hace 2 semanas # `locale: es` with `type: timeago`
```yaml
plugins:
- git-revision-date-localized:
- locale: en
+ locale: en_US
```
+!!! note when using `type: timeago`
+
+ When using `type: timeago` then [timeago.js](https://github.com/hustcc/timeago.js) is added to your website, which supports [these locales](https://github.com/hustcc/timeago.js/tree/master/src/lang). If you specify a locale not supported by timeago.js, the fallback is English (`en`). It might happen that your specific locale is supported by babel (used by date formats) but not by timeago. In that case open an issue with this plugin.
+
## `fallback_to_build_date`
diff --git a/src/mkdocs_git_revision_date_localized_plugin/dates.py b/src/mkdocs_git_revision_date_localized_plugin/dates.py
index 4eafe7e..3fd4dd6 100644
--- a/src/mkdocs_git_revision_date_localized_plugin/dates.py
+++ b/src/mkdocs_git_revision_date_localized_plugin/dates.py
@@ -1,4 +1,4 @@
-from babel.dates import format_date, get_timezone
+from babel.dates import format_date, get_timezone, format_datetime
from datetime import datetime, timezone
from typing import Any, Dict
@@ -36,5 +36,56 @@ def get_date_formats(
"iso_date": loc_revision_date.strftime("%Y-%m-%d"),
"iso_datetime": loc_revision_date.strftime("%Y-%m-%d %H:%M:%S"),
"timeago": '' % (loc_revision_date.isoformat(), locale),
- "custom": loc_revision_date.strftime(custom_format),
+ "custom": format_datetime(loc_revision_date, format=strftime_to_babel_format(custom_format), locale=locale),
}
+
+
+def strftime_to_babel_format(fmt: str) -> str:
+ """
+ Convert strftime format string to Babel format pattern.
+
+ Args:
+ fmt (str): strftime format string
+
+ Returns:
+ str: Babel format pattern
+ """
+ # Dictionary mapping strftime directives to Babel format patterns
+ mapping = {
+ '%a': 'EEE', # Weekday abbreviated
+ '%A': 'EEEE', # Weekday full
+ '%b': 'MMM', # Month abbreviated
+ '%B': 'MMMM', # Month full
+ '%c': '', # Locale's date and time (not directly mappable)
+ '%d': 'dd', # Day of month zero-padded
+ '%-d': 'd', # Day of month
+ '%e': 'd', # Day of month space-padded
+ '%f': 'SSSSSS', # Microsecond
+ '%H': 'HH', # Hour 24h zero-padded
+ '%-H': 'H', # Hour 24h
+ '%I': 'hh', # Hour 12h zero-padded
+ '%-I': 'h', # Hour 12h
+ '%j': 'DDD', # Day of year
+ '%m': 'MM', # Month zero-padded
+ '%-m': 'M', # Month
+ '%M': 'mm', # Minute zero-padded
+ '%-M': 'm', # Minute
+ '%p': 'a', # AM/PM
+ '%S': 'ss', # Second zero-padded
+ '%-S': 's', # Second
+ '%w': 'e', # Weekday as number
+ '%W': 'w', # Week of year
+ '%x': '', # Locale's date (not directly mappable)
+ '%X': '', # Locale's time (not directly mappable)
+ '%y': 'yy', # Year without century
+ '%Y': 'yyyy', # Year with century
+ '%z': 'Z', # UTC offset
+ '%Z': 'z', # Timezone name
+ '%%': '%' # Literal %
+ }
+
+ result = fmt
+ for strftime_code, babel_code in mapping.items():
+ result = result.replace(strftime_code, babel_code)
+
+ return result
\ No newline at end of file
diff --git a/src/mkdocs_git_revision_date_localized_plugin/plugin.py b/src/mkdocs_git_revision_date_localized_plugin/plugin.py
index a33f96b..49747da 100644
--- a/src/mkdocs_git_revision_date_localized_plugin/plugin.py
+++ b/src/mkdocs_git_revision_date_localized_plugin/plugin.py
@@ -81,7 +81,7 @@ def on_config(self, config: config_options.Config, **kwargs) -> Dict[str, Any]:
# Get locale from plugin configuration
plugin_locale = self.config.get("locale", None)
- # theme locale
+ # Get locale from theme configuration
if "theme" in config and "language" in config.get("theme"):
custom_theme = config.get("theme")
theme_locale = (
@@ -96,7 +96,6 @@ def on_config(self, config: config_options.Config, **kwargs) -> Dict[str, Any]:
custom_theme.locale if Version(mkdocs_version) >= Version("1.6.0") else custom_theme._vars.get("locale")
)
logging.debug("Locale '%s' extracted from the custom theme: '%s'" % (theme_locale, custom_theme.name))
-
else:
theme_locale = None
logging.debug("No locale found in theme configuration (or no custom theme set)")
@@ -116,9 +115,6 @@ def on_config(self, config: config_options.Config, **kwargs) -> Dict[str, Any]:
# Validate locale
locale_set = str(locale_set)
- assert len(locale_set) == 2, (
- "locale must be a 2 letter code, see https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes"
- )
# set locale also in plugin configuration
self.config["locale"] = locale_set
@@ -173,7 +169,6 @@ def on_page_markdown(self, markdown: str, page: Page, config: config_options.Con
# First prio is use mkdocs-static-i18n locale if set
try:
locale = page.file.locale
-
except AttributeError:
locale = None
@@ -183,18 +178,12 @@ def on_page_markdown(self, markdown: str, page: Page, config: config_options.Con
locale = page.meta["locale"]
# Finally, if no page locale set, we take the locale determined on_config()
+ # (fourth prio is plugin configuration)
+ # (firth prio is theme configuration)
+ # (sixth prio is fallback to English)
if not locale:
locale = self.config.get("locale")
- # MkDocs supports 2-letter and 5-letter locales
- # https://www.mkdocs.org/user-guide/localizing-your-theme/#supported-locales
- # We need the 2 letter variant
- if len(locale) == 5:
- locale = locale[:2]
- assert len(locale) == 2, (
- "locale must be a 2 letter code, see https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes"
- )
-
# Retrieve git commit timestamp
# Except for generated pages (f.e. by mkdocs-gen-files plugin)
if getattr(page.file, "generated_by", None):
diff --git a/tests/test_dates.py b/tests/test_dates.py
index bbe0e94..c98750c 100644
--- a/tests/test_dates.py
+++ b/tests/test_dates.py
@@ -1,7 +1,7 @@
import pytest
from datetime import datetime, timezone
from babel.dates import get_timezone
-
+from babel.core import UnknownLocaleError
from mkdocs_git_revision_date_localized_plugin.dates import get_date_formats
@@ -18,6 +18,37 @@ def test_get_dates():
}
assert get_date_formats(0) == expected_output
+ # Test with 4-letter locale
+ new_expected_output = expected_output.copy()
+ new_expected_output["timeago"] = ''
+ assert get_date_formats(0, locale="en_US") == new_expected_output
+
+ # Test with different locale
+ expected_output = {
+ "date": "1 janvier 1970",
+ "datetime": "1 janvier 1970 00:00:00",
+ "iso_date": "1970-01-01",
+ "iso_datetime": "1970-01-01 00:00:00",
+ "timeago": '',
+ "custom": "01. janvier 1970"
+ }
+ assert get_date_formats(0, locale="fr") == expected_output
+
+ # Test with pt_BR locale
+ expected_output = {
+ "date": "1 de janeiro de 1970",
+ "datetime": "1 de janeiro de 1970 00:00:00",
+ "iso_date": "1970-01-01",
+ "iso_datetime": "1970-01-01 00:00:00",
+ "timeago": '',
+ "custom": "01. janeiro 1970"
+ }
+ assert get_date_formats(0, locale="pt_BR") == expected_output
+
+ # Test with non-existing locale
+ with pytest.raises(UnknownLocaleError):
+ get_date_formats(0, locale="abcd")
+
# Test with custom arguments
expected_output = {
"date": "January 1, 1970",