Code Monkey home page Code Monkey logo

Comments (10)

ultrabug avatar ultrabug commented on June 7, 2024

Hello @tombreit I think you can take inspiration from : https://github.com/ultrabug/mkdocs-static-i18n/blob/main/mkdocs_static_i18n/custom_i18n_sitemap/sitemap.xml

from mkdocs-static-i18n.

tombreit avatar tombreit commented on June 7, 2024

Hi @ultrabug, thanks for your prompt reply and the sitemap-hint! But in my case, there must be something wrong with my project setup - even i18n_alternates is not defined:

# requirements.txt

mkdocs                 1.5.3
mkdocs-static-i18n     1.2.2
# mkdocs.yml

site_name: Testsite
strict: true
plugins:
  - i18n:
      docs_structure: suffix
      languages:
        - locale: en
          default: true
          name: English
          build: true
        - locale: de
          name: Deutsch
          build: true
theme:
  name: null
  custom_dir: 'theme/'
# project layout
.
├── docs
│   ├── page1.de.md
│   └── page1.en.md
├── mkdocs.yml
├── theme
│   ├── base.html
│   ├── main.html
│   └── mkdocs_theme.yml
└── upload.sh
<!-- theme/base.html -->

i18n_page_locale:  {{ i18n_page_locale }} 
i18n_languages:    {{ i18n_languages }}
i18n_alternates:   {{ i18n_alternates.items() }}

mkdocs serve

  File "project/theme_csl/main.html", line 1, in top-level template code
    {% extends "base.html" %}
  File "project/theme_csl/base.html", line 86, in top-level template code
    {{ i18n_alternates.items() }}
    ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "project/.venv/lib/python3.11/site-packages/jinja2/environment.py", line 485, in getattr
    return getattr(obj, attribute)
           ^^^^^^^^^^^^^^^^^^^^^^^
jinja2.exceptions.UndefinedError: 'i18n_alternates' is undefined

Do you have any other tips or ideas?

from mkdocs-static-i18n.

kamilkrzyskow avatar kamilkrzyskow commented on June 7, 2024

The i18n_alternates are set only in the template context:

context["i18n_alternates"] = self.i18n_files_per_language

For sitemap.xml and perhaps 404.html as well, as it's included in static_templates.

The page context has only those values:

if isinstance(page, Page) and hasattr(page.file, "locale"):
# export some useful i18n related variables on page context, see #75
context["i18n_config"] = self.config
context["i18n_file_locale"] = page.file.locale
context["i18n_page_locale"] = self.current_language
if self.config.reconfigure_material is True:
context = self.reconfigure_page_context(context, page, config, nav)

from mkdocs-static-i18n.

kamilkrzyskow avatar kamilkrzyskow commented on June 7, 2024

Based on this excerpt from the material theme, the templates can access the config:
https://github.com/squidfunk/mkdocs-material/blob/cc78979185dfca30ad6657192174733f702d86f5/src/templates/base.html#L63-L65

So try:

{% if "i18n" in config.plugins %}
{% set i18n_alternates = config.plugins["i18n"].i18n_files_per_language  %}
{% else %}
{% set i18n_alternates = {} %}
{% endif %}

{{ i18n_alternates.items() }}

I haven't tested it ✌️

from mkdocs-static-i18n.

tombreit avatar tombreit commented on June 7, 2024

Sorry, but I don't get it. You put me on the right track - but

<!-- mytheme/base.html -->

{% for locale, i18n_files in config.plugins["i18n"].i18n_files_per_language.items() %}
- locale:     {{ locale }}
  i18n_files: {{ i18n_files }}
{% endfor %}

only works for my secondary language pages - it does not list alternate de pages for en pages - but lists en and de pages for de pages:

# page: docs/index.en.md

- locale:     en
  i18n_files: [File(src_uri='index.en.md', dest_uri='index.html', name='index', url='./')]
# page: docs/index.de.md

- locale:     en
  i18n_files: [File(src_uri='index.en.md', dest_uri='index.html', name='index', url='./')]

- locale:     de
  i18n_files: [File(src_uri='index.de.md', dest_uri='de/index.html', name='index', url='de/')]

Hard to explain, so I have put together a demo page:

from mkdocs-static-i18n.

kamilkrzyskow avatar kamilkrzyskow commented on June 7, 2024

Didn't run the demo, your comment explained the situation quite well:

# update the (cumulative) global alternates map which is
# used by the sitemap.xml template
self.i18n_files_per_language[self.current_language] = i18n_files.documentation_pages()

It turned out that the i18n_files_per_language is a mapping that gets updated after each language build, and the sitemap uses the data from the last build to save the information, once all languages are built.


Going back to your OP:

I would like to build a language selector in a custom (non-mkdocs-material) theme, but I do not know how to access the alternate language versions of a given page:

As far as I know, the i18n plugin requires or assumes that the file tree is the same in each language, so every file path is the same too only the .en.md file suffix or /en/... folder prefix changes.

Given this information you don't need to access the files from other languages, you only need the language identifier.

self.current_language = None

@property
def build_languages(self):
return [
lang.locale for lang in filter(lambda lang: lang.build is True, self.config.languages)
]

You should be able to access the data with:
config.plugins["i18n"].current_language get current language
you have access to the current locale already:

context["i18n_file_locale"] = page.file.locale 
context["i18n_page_locale"] = self.current_language 

config.plugins["i18n"].build_languages loop over that to get alternates
page.url | replace(current_language, alternate, 1) replace the current with the alternate once inside the url 🤔

accessing page might crash on 404.html, so add a safety check if that happens if page

from mkdocs-static-i18n.

tombreit avatar tombreit commented on June 7, 2024

That would work if all pages exists in all configured languages. Looping over config.plugins["i18n"].all_languages seems to get same resuts.

But neither config.plugins["i18n"].build_languages nor config.plugins["i18n"].all_languages seem to return only the actually existing alternates. Instead these return all possible alternates according to the languages config key.

Given the following docs layout:

docs/
├── enonly.en.md
├── index.de.md
└── index.en.md

this would work for index.en.mdindex.de.md - but not for enonly.en.md.

Perhaps I implement this language switcher in Javascript;-)
And thank you very much for taking the time to look at my issue.

from mkdocs-static-i18n.

kamilkrzyskow avatar kamilkrzyskow commented on June 7, 2024

Well, both build_languages and all_languages are global containers for the whole build, and aren't "resolved" for each page separately.
I always use the https://ultrabug.github.io/mkdocs-static-i18n/setup/controlling-your-builds/#fallbacking-to-default setting, so I never even considered a non-translated page alternate as an issue 😅

Perhaps I implement this language switcher in Javascript;-)

So that after a page load, the JavaScript fetches the urls to check if the page is available and returns a status 200? I advise against this, as this would just create unnecessary requests.


This is the code that reconfigures the context and configures the alternates:

def reconfigure_page_context(self, context, page, config: MkDocsConfig, nav: Navigation):
"""
Support dynamic reconfiguration of the material language selector so that
users can switch between the different localized versions of their current page.
"""
if self.extra_alternate:
config.extra.alternate = deepcopy(self.extra_alternate)
if PurePath(page.url) == PurePath("."):
return context
if PurePath(page.url) == PurePath(page.file.locale_alternate_of):
return context
#
for extra_alternate in config.extra.alternate:
alternate_lang = extra_alternate["lang"]
# current page has an alternate for this language, use it
if alternate_lang in page.file.alternates:
alternate_file = page.file.alternates[alternate_lang]
extra_alternate["link"] = alternate_file.url
return context

There is the page.file.alternates list, but I don't think it will limit the alternates to only existent ones, still worth a try.


I would like to build a language selector in a custom (non-mkdocs-material) theme,

Also the Material theme uses the extra.alternate config, which later is modified by the plugin. So why not copy the logic from the material theme? The licence is MIT, so you can reuse it, just provide attribution.

from mkdocs-static-i18n.

tombreit avatar tombreit commented on June 7, 2024

I already tried to hijack the language switcher from mkdocs-material. But, again, I'm unable to use the provided "integration" via reconfigure_material_theme. To me it looks like this functionality is strongly coupled with/for the material theme and it is not clear to me how I could use this functionality with my theme:

# material theme specific reconfiguration (can be disabled)
if config.theme.name == "material" and self.config.reconfigure_material is True:

This integration actually sounds perfect and I would love to use it - but the static-i18n plugin only seems to provide/expose this for "mkdocs-material"?

from mkdocs-static-i18n.

kamilkrzyskow avatar kamilkrzyskow commented on June 7, 2024

True, my bad, I forgot about the config.theme.name validation. The i18n plugin makes the assumption that extra.alternate is only used by the mkdocs-material theme, which for now it was, and has merged the alternate handling with the reconfiguration logic.

The code responsible for the alternate deepcopy could be moved out into another function reconfigure_extra_alternate:

# configure extra.alternate language switcher
if len(self.build_languages) > 1 or "null" in self.all_languages:
# 'on_page_context' overrides the config.extra.alternate
# so we need to reset it to its initial computed value if present
if self.extra_alternate:
config.extra["alternate"] = deepcopy(self.extra_alternate)
# user has setup its own extra.alternate
# warn if it's poorly configured
if "alternate" in config.extra:
for alternate in config.extra["alternate"]:
alternate_link = alternate.get("link", "")
if not alternate_link.startswith("http") and (
not alternate_link.startswith("/") or not alternate_link.endswith("/")
):
log.info(
"The 'extra.alternate' configuration contains a "
"'link' option that should be an absolute path '/' and "
f"end with a trailing '/' in {alternate}"
)
for required_key in ["name", "link", "lang"]:
if required_key not in alternate:
log.info(
"The 'extra.alternate' configuration is missing a required "
f"'{required_key}' option in {alternate}"
)
# configure the extra.alternate for the user
# https://squidfunk.github.io/mkdocs-material/setup/changing-the-language/#site-language-selector
else:
if config.site_url:
base_url = urlsplit(config.site_url).path.rstrip("/")
else:
base_url = ""
config.extra["alternate"] = []
# Add index.html file name when used with
# use_directory_urls = True
link_suffix = "" if config.get("use_directory_urls") else "index.html"
# setup language switcher
for language in self.all_languages:
lang_config = self.get_language_config(language)
# skip language if not built unless we are the special "null" locale
if lang_config.build is False and lang_config.locale != "null":
continue
config.extra["alternate"].append(
{
"name": lang_config.name,
"link": lang_config.fixed_link
or f"{base_url}{lang_config.link}{link_suffix}",
"lang": language,
}
)
self.extra_alternate = deepcopy(config.extra["alternate"])

and later it can be called inside the reconfigure_material_theme like before, and in an elif "alternate" in config.extra statement after that whole material validation:
if config.theme.name == "material" and self.config.reconfigure_material is True:

I think it's a valid proposition to support that feature in other themes, one issue might be that other themes use extra.alternate in a different way from the material theme. You could do some market research with the more popular themes, and built-in themes, and make sure there won't be any conflict. if there is a conflict additional validation would be required for that extra.alternate to make sure it has valid values.

IMO that logic is not that coupled with material, that it can't be taken out 🤔However, I won't be developing anything for at least a week+ so can't make a PR myself. Once the market research is done ping ultrabug, maybe he'll have the time if you don't want to dabble with Python directly.

from mkdocs-static-i18n.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.