Skip to content

Commit

Permalink
Add flags to language selector
Browse files Browse the repository at this point in the history
  • Loading branch information
Andre601 committed Nov 2, 2023
1 parent 3916dd5 commit 0f38bb4
Show file tree
Hide file tree
Showing 4 changed files with 529 additions and 0 deletions.
11 changes: 11 additions & 0 deletions docs/assets/css/styling/padding.css
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,15 @@
color: #ff9100;
border: 1px solid #ff9100;
background-color: #ff91001a;
}

.md-select button img.twemoji {
height: 1.2rem;
vertical-align: middle;
}

.md-select__link img.twemoji {
height: 1rem;
vertical-align: text-bottom;
margin-right: 1%;
}
2 changes: 2 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ plugins:

hooks:
- theme/.hooks/badges.py
- theme/.hooks/theme_overrides_manager.py
- theme/.hooks/language_flags.py

markdown_extensions:
- attr_list
Expand Down
237 changes: 237 additions & 0 deletions theme/.hooks/language_flags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
"""MkDocs hook, which adds flag emojis to the language selector.
Works only with the Material theme, and requires the "theme_overrides_manager" hook.
The hook dynamically on the fly modifies the "header.html" template of the Material theme before the build.
It uses the theme_overrides_manager hook to assure original file preservation.
Works both with and without the "i18n" MkDocs plugin.
MIT Licence 2023 Kamil Krzyśków (HRY)
Parts adapted from the translations.py hook
https://github.com/squidfunk/mkdocs-material/blob/master/src/.overrides/hooks/translations.py
and from the materialx.emoji and pymdownx.emoji modules.
"""
import logging
from pathlib import Path
from typing import Dict, Optional, Union

from jinja2 import Environment
from mkdocs.config.defaults import MkDocsConfig
from mkdocs.plugins import PrefixedLogger
from pymdownx import twemoji_db, util
from pymdownx.emoji import TWEMOJI_SVG_CDN

# region Core Logic Events


def on_env(*_, config: MkDocsConfig, **__) -> Optional[Environment]:
"""Main function. Triggers just before the build begins."""

LOG.debug('Running "on_env"')

if not _is_runnable(config=config):
return None

import material

partials: Path = Path(material.__file__).parent / "templates" / "partials"
header: Path = partials / "alternate.html"

config.extra[HOOK_MANAGER].paths_with_processors.append((header, _add_flags))

LOG.info(f"Registered processors")

return None


def _is_runnable(*, config: MkDocsConfig) -> bool:
"""Make sure the hook should run."""

if HOOK_MANAGER not in config["extra"]:
LOG.info(f'"{HOOK_MANAGER}" not detected')
return False

if config.theme["name"] != "material":
LOG.info('Only the "material" theme is supported')
return False

if "alternate" not in config["extra"]:
LOG.info('"extra.alternate" not detected')
return False

if len(config["extra"]["alternate"]) < 2:
LOG.info(f"Not enough languages")
return False

return True


def _add_flags(*, partial: Path, config: MkDocsConfig, **__) -> None:
"""Process the "header.html" partial and add flags to the language selector template."""

# Configure the tokens
tokens: Dict[str, str] = {
"START": '<div class="md-select">',
"CONFIG": '{% set icon = config.theme.icon.alternate or "material/translate" %}',
"SELECTOR": '{% include ".icons/" ~ icon ~ ".svg" %}',
"LINK": "{{ alt.name }}",
"END": "</div>",
}

# A negative number means the same level as the START token.
end_indent_level: int = -1

override_manager = config.extra[HOOK_MANAGER]

loaded_section: str = override_manager.load_section(
partial=partial,
tokens=tokens,
end_level=end_indent_level,
)

# Do not continue when section is not loaded
if not loaded_section:
return

# Load all flags relevant to the current build
flag_mapping: Dict[str, str] = {
alt["lang"]: _flag_svg(alt["lang"]) for alt in config["extra"]["alternate"]
}

# Set the "flag_mapping" selector
# Choose different selector for the "i18n" plugin
selector: str = "i18n_page_locale" if "i18n" in config.plugins else "config.theme.language"

modified_section: str = (
loaded_section.replace(
tokens["CONFIG"], "{% set flag_mapping = " + str(flag_mapping) + " %}"
)
.replace(tokens["SELECTOR"], "{{ flag_mapping[" + selector + "] }}")
.replace(
tokens["LINK"],
"{{ flag_mapping[alt.lang] }} "
+ '{{ alt.name | replace(alt.lang, "") | replace("-", "") }}',
)
)

# Modify the partial
override_manager.save_section(
partial=partial, original_section=loaded_section, modified_section=modified_section
)

LOG.debug(f'Processed "{partial.name}".')


def _flag_svg(alternate_lang: str) -> str:
"""Returns a str with a <img> tag containing the flag. Adapted from materialx.emoji and pymdownx.emoji"""

emoji_code: str = f":flag_{COUNTRIES[alternate_lang]}:"

shortname: str = INDEX["aliases"].get(emoji_code, emoji_code)
emoji: Dict[str, Optional[str]] = INDEX["emoji"].get(shortname, None)

if not emoji:
return emoji_code

unicode: Optional[str] = emoji.get("unicode")
unicode_alt: Optional[str] = emoji.get("unicode_alt", unicode)

title: str = shortname
alt: str = shortname

if unicode_alt is not None:
alt = "".join((util.get_char(int(c, 16)) for c in unicode_alt.split("-")))

return f'<img alt="{alt}" class="twemoji" src="{TWEMOJI_SVG_CDN}{unicode}.svg" title="{title}">'


# endregion


# region Constants

COUNTRIES: Dict[str, str] = {
"af": "za",
"ar": "ae",
"bg": "bg",
"bn": "bd",
"ca": "es",
"cs": "cz",
"da": "dk",
"de": "de",
"de-CH": "ch",
"el": "gr",
"en": "gb",
"eo": "eu",
"es": "es",
"et": "ee",
"fa": "ir",
"fi": "fi",
"fr": "fr",
"gl": "es",
"he": "il",
"hi": "in",
"hr": "hr",
"hu": "hu",
"hy": "am",
"id": "id",
"is": "is",
"it": "it",
"ja": "jp",
"ka": "ge",
"ko": "kr",
"lt": "lt",
"lv": "lv",
"mk": "mk",
"mn": "mn",
"ms": "my",
"my": "mm",
"nb": "no",
"nl": "nl",
"nn": "no",
"pl": "pl",
"pt-BR": "br",
"pt": "pt",
"ro": "ro",
"ru": "ru",
"sh": "rs",
"si": "lk",
"sk": "sk",
"sl": "si",
"sr": "rs",
"sv": "se",
"th": "th",
"tl": "ph",
"tr": "tr",
"uk": "ua",
"ur": "pk",
"uz": "uz",
"vi": "vn",
"zh": "cn",
"zh-Hant": "cn",
"zh-TW": "tw",
}
"""Mapping of ISO 639-1 (languages) to ISO 3166 (countries)."""
# Adapted from
# https://github.com/squidfunk/mkdocs-material/blob/master/src/.overrides/hooks/translations.py

HOOK_NAME: str = "language_flags"
"""Name of this hook. Used in logging."""

HOOK_MANAGER: str = "theme_overrides_manager"
"""Name of the hook manager. Used to access it in `config.extra`."""

INDEX: Dict[str, Union[str, Dict]] = {
"name": "twemoji",
"emoji": twemoji_db.emoji,
"aliases": twemoji_db.aliases,
}
"""Copy of the Twemoji index."""
# Adapted from
# materialx.emoji

LOG: PrefixedLogger = PrefixedLogger(
HOOK_NAME, logging.getLogger(f"mkdocs.hooks.theme_overrides.{HOOK_NAME}")
)
"""Logger instance for this hook."""

# endregion
Loading

0 comments on commit 0f38bb4

Please sign in to comment.