Localize all of the template text
This commit is contained in:
parent
790521df05
commit
504ec5b48c
21 changed files with 269 additions and 88 deletions
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
@ -16,6 +16,7 @@
|
|||
"editor.rulers": [120],
|
||||
},
|
||||
"files.associations": {
|
||||
"*.js.jinja": "javascript"
|
||||
"*.js.jinja": "javascript",
|
||||
"*.css.jinja": "css",
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,10 +40,8 @@ packages = [
|
|||
]
|
||||
|
||||
[template.args]
|
||||
title = "Hex Book"
|
||||
mod_name = "Hex Casting"
|
||||
author = "petrak@, Alwinfy"
|
||||
description = "The Hex Book, all in one place."
|
||||
icon_href = "logo.png"
|
||||
show_landing_text = true
|
||||
|
||||
|
|
|
@ -1,16 +1,75 @@
|
|||
{
|
||||
key: {
|
||||
use: "Right Click",
|
||||
sneak: "Left Shift",
|
||||
jump: "Space",
|
||||
hexdoc: {
|
||||
hexcasting: {
|
||||
title: "Hex Book",
|
||||
description: "The Hex Book, all in one place.",
|
||||
},
|
||||
|
||||
"item.minecraft": {
|
||||
amethyst_shard: "Amethyst Shard",
|
||||
budding_amethyst: "Budding Amethyst",
|
||||
welcome: {
|
||||
"1": "This is the online version of the %s documentation.",
|
||||
"2": "Embedded images and patterns are included, but not crafting recipes or items. \
|
||||
There's an in-game book for those.",
|
||||
"3": "Additionally, this is built from the latest code on GitHub. \
|
||||
It may describe $(bold)newer/$ features that you may not necessarily have, \
|
||||
even on the latest Modrinth/CurseForge version!",
|
||||
"4": "$(bold)Entries which are blurred are spoilers/$. \
|
||||
Click to reveal them, but be aware that they may spoil endgame progression. \
|
||||
Alternatively, click $(l:?nospoiler)here/$ to get a version with all spoilers showing.",
|
||||
"old_version": "$(italic)The past is a foreign country; they do things differently there./$",
|
||||
},
|
||||
|
||||
"block.hexcasting": {
|
||||
slate: "Blank Slate",
|
||||
toc: {
|
||||
// use the span tag to make everything after it wrap with the toc button
|
||||
// or put it at the very end to make the button wrap by itself
|
||||
// either way, do NOT remove it entirely
|
||||
title: "Table of {{ '<span class=\"nobr\">'|safe }}Contents",
|
||||
toggle_all: "Toggle all",
|
||||
},
|
||||
|
||||
pattern: {
|
||||
show: "Click to show spell",
|
||||
hide: "Click to hide spell",
|
||||
|
||||
multiplier: "${speedScale()}x",
|
||||
paused: "Paused",
|
||||
|
||||
not_supported: "Your browser does not support visualizing patterns. Pattern code: %s",
|
||||
},
|
||||
|
||||
pages: {
|
||||
patchouli: {
|
||||
crafting: {
|
||||
description: "Depicted in the book: The crafting recipe for the",
|
||||
separator: " and ",
|
||||
},
|
||||
},
|
||||
|
||||
hexcasting: {
|
||||
crafting_multi: {
|
||||
description: "Depicted in the book: Several crafting recipes, for the",
|
||||
separator: ", ",
|
||||
},
|
||||
|
||||
brainsweep: {
|
||||
description: "Depicted in the book: A mind-flaying recipe producing the",
|
||||
separator: "", // this value intentionally left blank
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
key: {
|
||||
use: "Right Click",
|
||||
sneak: "Left Shift",
|
||||
jump: "Space",
|
||||
},
|
||||
|
||||
"item.minecraft": {
|
||||
amethyst_shard: "Amethyst Shard",
|
||||
budding_amethyst: "Budding Amethyst",
|
||||
},
|
||||
|
||||
"block.hexcasting": {
|
||||
slate: "Blank Slate",
|
||||
},
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
{# shows the names of all the recipe results in a list of recipes #}
|
||||
{% macro recipe_block(recipes, result_attribute, description, separator) -%}
|
||||
<blockquote class="crafting-info">
|
||||
Depicted in the book: {{ description }} {{
|
||||
{{ description }} {{
|
||||
recipes
|
||||
|map(attribute="result." ~ result_attribute)
|
||||
|map("hexdoc_wrap", "code")
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="{{ props.url }}">{{ title }}</a>
|
||||
<a class="navbar-brand" href="{{ props.url }}">{{ _("hexdoc."~props.modid~".title") }}</a>
|
||||
</div>
|
||||
|
||||
<!-- content -->
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
<div id="table-of-contents"></div>
|
||||
<nav class="toc-container">
|
||||
<h2 class="page-header">
|
||||
Table of <span class="nobr">Contents<a
|
||||
{{ _("hexdoc.toc.title") }}<a
|
||||
href="javascript:void(0)"
|
||||
class="permalink toggle-link small"
|
||||
data-target="toc-category"
|
||||
title="Toggle all"
|
||||
title="{{ _('hexdoc.toc.toggle_all') }}"
|
||||
><i class="bi bi-list-nested"></i></a>{{ macros.permalink("table-of-contents", "toc-permalink") }}</span>
|
||||
</h2>
|
||||
|
||||
|
|
|
@ -1,26 +1,16 @@
|
|||
<div class="container" style="margin-top: 3em;">
|
||||
<blockquote>
|
||||
<h1>This is the online version of the {{ mod_name }} documentation.</h1>
|
||||
<p>
|
||||
Embedded images and patterns are included, but not crafting recipes or items. There's an in-game book for those.
|
||||
</p>
|
||||
|
||||
<h1>{{ _f("hexdoc.welcome.1")|format(mod_name) }}</h1>
|
||||
<p>{{ _f("hexdoc.welcome.2") }}</p>
|
||||
{% if is_bleeding_edge %}
|
||||
<p>
|
||||
Additionally, this is built from the latest code on GitHub.
|
||||
It may describe <b>newer</b> features that you may not necessarily have, even on the latest CurseForge version!
|
||||
</p>
|
||||
<p>{{ _f("hexdoc.welcome.3") }}</p>
|
||||
{% endif %}
|
||||
|
||||
<p>
|
||||
<b>Entries which are blurred are spoilers</b>. Click to reveal them, but be aware that they may spoil endgame
|
||||
progression. Alternatively, click <a href="?nospoiler">here</a> to get a version with all spoilers showing.
|
||||
</p>
|
||||
<p>{{ _f("hexdoc.welcome.4") }}</p>
|
||||
|
||||
{# conditionally revealed using js #}
|
||||
<span id="old-version-notice" class="hidden">
|
||||
<br />
|
||||
<i>The past is a foreign country; they do things differently there.</i>
|
||||
{{ _f("hexdoc.welcome.old_version") }}
|
||||
</span>
|
||||
</blockquote>
|
||||
</div>
|
|
@ -26,11 +26,11 @@ details[open] summary.collapse-spell {
|
|||
}
|
||||
|
||||
details .collapse-spell::before {
|
||||
content: "Click to show spell";
|
||||
content: "{{ _('hexdoc.pattern.show') }}";
|
||||
}
|
||||
|
||||
details[open] .collapse-spell::before {
|
||||
content: "Click to hide spell";
|
||||
content: "{{ _('hexdoc.pattern.hide') }}";
|
||||
}
|
||||
|
||||
blockquote.crafting-info {
|
|
@ -1,19 +1,19 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{{ title }}</title>
|
||||
<title>{{ _("hexdoc."~props.modid~".title") }}</title>
|
||||
<link rel="icon" href="{{ icon_href }}">
|
||||
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="description" content="{{ description }}" />
|
||||
<meta name="description" content="{{ _('hexdoc.'~props.modid~'.description') }}" />
|
||||
<meta name="author" content="{{ author }}" />
|
||||
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:title" content="{{ title }}" />
|
||||
<meta property="og:title" content="{{ _('hexdoc.'~props.modid~'.title') }}" />
|
||||
<meta property="og:image" content="{{ page_url }}/{{ icon_href }}" />
|
||||
<meta property="og:url" content="{{ page_url }}" />
|
||||
<meta property="og:description" content="{{ description }}" />
|
||||
<meta property="og:description" content="{{ _('hexdoc.'~props.modid~'.description') }}" />
|
||||
<meta property="og:site_name" content="{{ mod_name }}" />
|
||||
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.4.1/dist/css/bootstrap.min.css"
|
||||
|
@ -27,7 +27,7 @@
|
|||
|
||||
<style>
|
||||
{% filter indent(6) %}
|
||||
{%+ include_raw "main.css" %}
|
||||
{%+ include "main.css.jinja" %}
|
||||
{% endfilter %}
|
||||
</style>
|
||||
|
||||
|
|
|
@ -212,7 +212,9 @@ function initializeElem(canvas) {
|
|||
})`;
|
||||
context.font = `${pauseScale}px sans-serif`;
|
||||
context.fillText(
|
||||
speedScale() ? speedScale() + "x" : "Paused",
|
||||
// these variables are filled by Jinja
|
||||
// slightly scuffed, but it works for now
|
||||
speedScale() ? `{{ _('hexdoc.pattern.multiplier') }}` : "{{ _('hexdoc.pattern.paused') }}",
|
||||
0.2 * scale,
|
||||
canvas.height - 0.2 * scale
|
||||
);
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
{% extends "pages/patchouli/page.html.jinja" %}
|
||||
{% include "common/macros.html.jinja" %}
|
||||
{% include "common/macros.html.jinja" with context %}
|
||||
|
||||
{% block body %}
|
||||
{{ macros.recipe_block([page.recipe], "name", "A mind-flaying recipe producing the", "") }}
|
||||
{{ macros.recipe_block(
|
||||
[page.recipe],
|
||||
"name",
|
||||
_("hexdoc.pages.hexcasting.brainsweep.description"),
|
||||
_("hexdoc.pages.hexcasting.brainsweep.separator"),
|
||||
) }}
|
||||
{{ page.text|hexdoc_block }}
|
||||
{% endblock body %}
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
{% extends "pages/patchouli/text.html.jinja" %}
|
||||
{% include "common/macros.html.jinja" %}
|
||||
{% include "common/macros.html.jinja" with context %}
|
||||
|
||||
{% block inner_body %}
|
||||
{{ macros.recipe_block(page.recipes, "item", "Several crafting recipes, for the", ", ") }}
|
||||
{{ macros.recipe_block(
|
||||
page.recipes,
|
||||
"item",
|
||||
_("hexdoc.pages.hexcasting.crafting_multi.description"),
|
||||
_("hexdoc.pages.hexcasting.crafting_multi.separator"),
|
||||
) }}
|
||||
{{ super() }}
|
||||
{% endblock inner_body %}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
data-string="{{ pattern.signature }}"
|
||||
data-start="{{ pattern.startdir.name.lower() }}"
|
||||
data-per-world="{{ pattern.is_per_world }}"
|
||||
>Your browser does not support visualizing patterns. Pattern code: {{ pattern.signature }}</canvas>
|
||||
>{{ _("hexdoc.pattern.not_supported")|format(pattern.signature) }}</canvas>
|
||||
{% endfor %}
|
||||
</details>
|
||||
{{ super() }}
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
{% include "common/macros.html.jinja" %}
|
||||
|
||||
{% block inner_body %}
|
||||
{{ macros.recipe_block(page.recipes, "item", "The crafting recipe for the", " and ") }}
|
||||
{{ macros.recipe_block(
|
||||
page.recipes,
|
||||
"item",
|
||||
_("hexdoc.pages.patchouli.crafting.description"),
|
||||
_("hexdoc.pages.patchouli.crafting.separator"),
|
||||
) }}
|
||||
{{ super() }}
|
||||
{% endblock inner_body %}
|
||||
|
|
|
@ -124,6 +124,9 @@ class Book(HexdocModel):
|
|||
return self
|
||||
context = cast_or_raise(info.context, BookContext)
|
||||
|
||||
# make the macros accessible when rendering the template
|
||||
self.macros |= context.macros
|
||||
|
||||
self._link_bases: dict[tuple[ResourceLocation, str | None], str] = {}
|
||||
|
||||
# load categories
|
||||
|
|
|
@ -13,8 +13,7 @@ from pydantic.dataclasses import dataclass
|
|||
from pydantic.functional_validators import ModelWrapValidatorHandler
|
||||
|
||||
from hexdoc.minecraft import LocalizedStr
|
||||
from hexdoc.minecraft.i18n import I18nContext
|
||||
from hexdoc.patchouli.text.html import HTMLElement, HTMLStream
|
||||
from hexdoc.minecraft.i18n import I18n, I18nContext
|
||||
from hexdoc.utils import DEFAULT_CONFIG, HexdocModel
|
||||
from hexdoc.utils.deserialize import cast_or_raise
|
||||
from hexdoc.utils.resource import ResourceLocation
|
||||
|
@ -156,7 +155,12 @@ class Style(ABC, HexdocModel, frozen=True):
|
|||
type: CommandStyleType | FunctionStyleType | SpecialStyleType
|
||||
|
||||
@staticmethod
|
||||
def parse(style_str: str, context: FormattingContext) -> Style | _CloseTag | str:
|
||||
def parse(
|
||||
style_str: str,
|
||||
book_id: ResourceLocation,
|
||||
i18n: I18n,
|
||||
is_0_black: bool,
|
||||
) -> Style | _CloseTag | str:
|
||||
# direct text replacements
|
||||
if style_str in _REPLACEMENTS:
|
||||
return _REPLACEMENTS[style_str]
|
||||
|
@ -170,7 +174,7 @@ class Style(ABC, HexdocModel, frozen=True):
|
|||
return CommandStyle(type=style_type)
|
||||
|
||||
# reset color, but only if 0 is considered reset instead of black
|
||||
if not context.props.is_0_black and style_str == "0":
|
||||
if style_str == "0" and not is_0_black:
|
||||
return _CloseTag(type=SpecialStyleType.color)
|
||||
|
||||
# preset colors
|
||||
|
@ -187,11 +191,11 @@ class Style(ABC, HexdocModel, frozen=True):
|
|||
|
||||
# keys
|
||||
if name == "k":
|
||||
return str(context.i18n.localize_key(value))
|
||||
return str(i18n.localize_key(value))
|
||||
|
||||
# links
|
||||
if name == SpecialStyleType.link.value:
|
||||
return LinkStyle(value=_format_href(value, context.book_id))
|
||||
return LinkStyle(value=_format_href(value, book_id))
|
||||
|
||||
# all the other functions
|
||||
if style_type := FunctionStyleType.get(name):
|
||||
|
@ -224,7 +228,8 @@ def is_external_link(value: str) -> bool:
|
|||
|
||||
|
||||
def _format_href(value: str, book_id: ResourceLocation) -> str | BookLink:
|
||||
if is_external_link(value):
|
||||
# TODO: kinda hacky, BookLink should *probably* support query params
|
||||
if value.startswith("?") or is_external_link(value):
|
||||
return value
|
||||
return BookLink.from_str(value, book_id)
|
||||
|
||||
|
@ -326,13 +331,21 @@ class FormatTree:
|
|||
children: list[FormatTree | str] # this can't be Self, it breaks Pydantic
|
||||
|
||||
@classmethod
|
||||
def format(cls, string: str, context: FormattingContext) -> Self:
|
||||
def format(
|
||||
cls,
|
||||
string: str,
|
||||
*,
|
||||
book_id: ResourceLocation,
|
||||
i18n: I18n,
|
||||
macros: dict[str, str],
|
||||
is_0_black: bool,
|
||||
) -> Self:
|
||||
# resolve macros
|
||||
# this could use ahocorasick, but it works fine for now
|
||||
old_string = None
|
||||
while old_string != string:
|
||||
old_string = string
|
||||
for macro, replace in context.macros.items():
|
||||
for macro, replace in macros.items():
|
||||
string = string.replace(macro, replace)
|
||||
|
||||
# lex out parsed styles
|
||||
|
@ -347,7 +360,7 @@ class FormatTree:
|
|||
text_since_prev_style.append(leading_text)
|
||||
last_end = match.end()
|
||||
|
||||
match Style.parse(match[1], context):
|
||||
match Style.parse(match[1], book_id, i18n, is_0_black):
|
||||
case str(replacement):
|
||||
# str means "use this instead of the original value"
|
||||
text_since_prev_style.append(replacement)
|
||||
|
@ -404,11 +417,18 @@ class FormatTree:
|
|||
):
|
||||
if not info.context or isinstance(value, FormatTree):
|
||||
return handler(value)
|
||||
context = cast_or_raise(info.context, FormattingContext)
|
||||
|
||||
context = cast_or_raise(info.context, FormattingContext)
|
||||
if isinstance(value, str):
|
||||
value = context.i18n.localize(value)
|
||||
return cls.format(value.value, context)
|
||||
|
||||
return cls.format(
|
||||
value.value,
|
||||
book_id=context.book_id,
|
||||
i18n=context.i18n,
|
||||
macros=context.macros,
|
||||
is_0_black=context.props.is_0_black,
|
||||
)
|
||||
|
||||
|
||||
FormatTree._wrap_root
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
# pyright: reportUnknownMemberType=false, reportUnknownArgumentType=false
|
||||
# pyright: reportUnknownLambdaType=false
|
||||
|
||||
import io
|
||||
import json
|
||||
import logging
|
||||
|
@ -24,7 +27,12 @@ from hexdoc.patchouli import Book
|
|||
from hexdoc.plugin import PluginManager
|
||||
from hexdoc.utils import HexdocModel, ModResourceLoader, Properties
|
||||
from hexdoc.utils.deserialize import cast_or_raise
|
||||
from hexdoc.utils.jinja_extensions import IncludeRawExtension, hexdoc_block, hexdoc_wrap
|
||||
from hexdoc.utils.jinja_extensions import (
|
||||
IncludeRawExtension,
|
||||
hexdoc_block,
|
||||
hexdoc_localize,
|
||||
hexdoc_wrap,
|
||||
)
|
||||
from hexdoc.utils.path import write_to_path
|
||||
|
||||
MARKER_NAME = ".sitemap-marker.json"
|
||||
|
@ -151,7 +159,7 @@ def main(args: Args | None = None) -> None:
|
|||
|
||||
# load everything
|
||||
with ModResourceLoader.clean_and_load_all(props, pm) as loader:
|
||||
books = dict[str, Book]()
|
||||
books = dict[str, tuple[Book, I18n]]()
|
||||
|
||||
if args.lang:
|
||||
first_lang = args.lang
|
||||
|
@ -177,12 +185,15 @@ def main(args: Args | None = None) -> None:
|
|||
|
||||
# load one book with exporting enabled
|
||||
first_i18n = per_lang_i18n.pop(first_lang)
|
||||
books[first_lang] = load_hex_book(book_data, pm, loader, first_i18n)
|
||||
books[first_lang] = (
|
||||
load_hex_book(book_data, pm, loader, first_i18n),
|
||||
first_i18n,
|
||||
)
|
||||
|
||||
# then load the rest with exporting disabled for efficiency
|
||||
loader.export_dir = None
|
||||
for lang, i18n in per_lang_i18n.items():
|
||||
books[lang] = load_hex_book(book_data, pm, loader, i18n)
|
||||
books[lang] = (load_hex_book(book_data, pm, loader, i18n), i18n)
|
||||
|
||||
if args.export_only:
|
||||
return
|
||||
|
@ -203,10 +214,10 @@ def main(args: Args | None = None) -> None:
|
|||
IncludeRawExtension,
|
||||
],
|
||||
)
|
||||
|
||||
env.filters |= { # type: ignore
|
||||
env.filters |= { # pyright: ignore[reportGeneralTypeIssues]
|
||||
"hexdoc_block": hexdoc_block,
|
||||
"hexdoc_wrap": hexdoc_wrap,
|
||||
"hexdoc_localize": hexdoc_localize,
|
||||
}
|
||||
|
||||
template = env.get_template(props.template.main)
|
||||
|
@ -218,27 +229,53 @@ def main(args: Args | None = None) -> None:
|
|||
shutil.rmtree(output_dir, ignore_errors=True)
|
||||
|
||||
if args.update_latest:
|
||||
render_books(props, books, template, output_dir, "latest")
|
||||
render_books(
|
||||
props=props,
|
||||
books=books,
|
||||
template=template,
|
||||
output_dir=output_dir,
|
||||
allow_missing=args.allow_missing,
|
||||
version="latest",
|
||||
is_root=False,
|
||||
)
|
||||
|
||||
if args.is_release:
|
||||
render_books(props, books, template, output_dir, version)
|
||||
render_books(
|
||||
props=props,
|
||||
books=books,
|
||||
template=template,
|
||||
output_dir=output_dir,
|
||||
allow_missing=args.allow_missing,
|
||||
version=version,
|
||||
is_root=False,
|
||||
)
|
||||
|
||||
# the default book should be the latest released version
|
||||
if args.update_latest and args.is_release:
|
||||
render_books(props, books, template, output_dir, version, is_root=True)
|
||||
render_books(
|
||||
props=props,
|
||||
books=books,
|
||||
template=template,
|
||||
output_dir=output_dir,
|
||||
allow_missing=args.allow_missing,
|
||||
version=version,
|
||||
is_root=True,
|
||||
)
|
||||
|
||||
logger.info("Done.")
|
||||
|
||||
|
||||
def render_books(
|
||||
*,
|
||||
props: Properties,
|
||||
books: dict[str, Book],
|
||||
books: dict[str, tuple[Book, I18n]],
|
||||
template: Template,
|
||||
output_dir: Path,
|
||||
allow_missing: bool,
|
||||
version: str,
|
||||
is_root: bool = False,
|
||||
is_root: bool,
|
||||
):
|
||||
for lang, book in books.items():
|
||||
for lang, (book, i18n) in books.items():
|
||||
# /index.html
|
||||
# /lang/index.html
|
||||
# /v/version/index.html
|
||||
|
@ -254,17 +291,35 @@ def render_books(
|
|||
page_url = "/".join([props.url, *path.parts])
|
||||
|
||||
logging.getLogger(__name__).info(f"Rendering {output_dir}")
|
||||
docs = strip_empty_lines(
|
||||
template.render(
|
||||
**props.template.args,
|
||||
book=book,
|
||||
|
||||
raw_docs = template.render(
|
||||
**props.template.args,
|
||||
book=book,
|
||||
props=props,
|
||||
page_url=page_url,
|
||||
version=version,
|
||||
lang=lang,
|
||||
is_bleeding_edge=version == "latest",
|
||||
# i18n helper
|
||||
_=lambda key: hexdoc_localize(
|
||||
key,
|
||||
do_format=False,
|
||||
props=props,
|
||||
page_url=page_url,
|
||||
version=version,
|
||||
lang=lang,
|
||||
is_bleeding_edge=version == "latest",
|
||||
)
|
||||
book=book,
|
||||
i18n=i18n,
|
||||
allow_missing=allow_missing,
|
||||
),
|
||||
# i18n helper, but with patchi formatting
|
||||
_f=lambda key: hexdoc_localize(
|
||||
key,
|
||||
do_format=True,
|
||||
props=props,
|
||||
book=book,
|
||||
i18n=i18n,
|
||||
allow_missing=allow_missing,
|
||||
),
|
||||
)
|
||||
docs = strip_empty_lines(raw_docs)
|
||||
|
||||
write_to_path(output_dir / "index.html", docs)
|
||||
if props.template.static_dir:
|
||||
|
|
|
@ -6,11 +6,14 @@ from jinja2.parser import Parser
|
|||
from jinja2.runtime import Context
|
||||
from markupsafe import Markup
|
||||
|
||||
from hexdoc.minecraft import LocalizedStr
|
||||
from hexdoc.patchouli import FormatTree
|
||||
from hexdoc.minecraft import I18n, LocalizedStr
|
||||
from hexdoc.patchouli import Book, FormatTree
|
||||
from hexdoc.patchouli.book import Book
|
||||
from hexdoc.patchouli.text import HTMLStream
|
||||
from hexdoc.utils.deserialize import cast_or_raise
|
||||
from hexdoc.patchouli.text.formatting import FormatTree
|
||||
|
||||
from . import Properties
|
||||
from .deserialize import cast_or_raise
|
||||
|
||||
|
||||
# https://stackoverflow.com/a/64392515
|
||||
|
@ -30,15 +33,16 @@ class IncludeRawExtension(Extension):
|
|||
|
||||
|
||||
@pass_context
|
||||
def hexdoc_block(context: Context, value: Any) -> str:
|
||||
def hexdoc_block(context: Context | dict[{"book": Book}], value: Any) -> str:
|
||||
try:
|
||||
return _hexdoc_block(context, value)
|
||||
book = cast_or_raise(context["book"], Book)
|
||||
return _hexdoc_block(book, value)
|
||||
except Exception as e:
|
||||
e.add_note(f"Value:\n {value}")
|
||||
raise
|
||||
|
||||
|
||||
def _hexdoc_block(context: Context, value: Any) -> str:
|
||||
def _hexdoc_block(book: Book, value: Any) -> str:
|
||||
match value:
|
||||
case LocalizedStr() | str():
|
||||
# use Markup to tell Jinja not to escape this string for us
|
||||
|
@ -46,11 +50,10 @@ def _hexdoc_block(context: Context, value: Any) -> str:
|
|||
return Markup("<br />".join(Markup.escape(line) for line in lines))
|
||||
|
||||
case FormatTree():
|
||||
book = cast_or_raise(context["book"], Book)
|
||||
with HTMLStream() as out:
|
||||
with value.style.element(out, book.link_bases):
|
||||
for child in value.children:
|
||||
out.write(_hexdoc_block(context, child))
|
||||
out.write(_hexdoc_block(book, child))
|
||||
return Markup(out.getvalue())
|
||||
|
||||
case None:
|
||||
|
@ -66,3 +69,30 @@ def hexdoc_wrap(value: str, *args: str):
|
|||
else:
|
||||
attributes = ""
|
||||
return Markup(f"<{tag}{attributes}>{Markup.escape(value)}</{tag}>")
|
||||
|
||||
|
||||
# aliased as _() and _f() at render time
|
||||
def hexdoc_localize(
|
||||
key: str,
|
||||
*,
|
||||
do_format: bool,
|
||||
props: Properties,
|
||||
book: Book,
|
||||
i18n: I18n,
|
||||
allow_missing: bool,
|
||||
):
|
||||
# get the localized value from i18n
|
||||
localized = i18n.localize(key, allow_missing=allow_missing)
|
||||
|
||||
if not do_format:
|
||||
return Markup(localized.value)
|
||||
|
||||
# construct a FormatTree from the localized value (to allow using patchi styles)
|
||||
formatted = FormatTree.format(
|
||||
localized.value,
|
||||
book_id=book.id,
|
||||
i18n=i18n,
|
||||
macros=book.macros,
|
||||
is_0_black=props.is_0_black,
|
||||
)
|
||||
return Markup(hexdoc_block({"book": book}, formatted))
|
||||
|
|
|
@ -46,10 +46,8 @@ packages = [
|
|||
]
|
||||
|
||||
[template.args]
|
||||
title = "{{ cookiecutter.mod_name }} Book"
|
||||
mod_name = "{{ cookiecutter.mod_name }}"
|
||||
author = "{{ cookiecutter.author }}"
|
||||
description = "The {{ cookiecutter.mod_name }} Book, all in one place."
|
||||
icon_href = "icon.png"
|
||||
show_landing_text = false
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
hexdoc: {
|
||||
"{{ cookiecutter.modid }}": {
|
||||
title: "{{ cookiecutter.mod_name }} Book",
|
||||
description: "The {{ cookiecutter.mod_name }} Book, all in one place.",
|
||||
},
|
||||
}
|
||||
}
|
|
@ -97,6 +97,8 @@ include = ["doc/src"]
|
|||
extraPaths = ["doc/src"]
|
||||
exclude = ["doc/{{cookiecutter.directory}}"]
|
||||
|
||||
enableExperimentalFeatures = true
|
||||
|
||||
# mostly we use strict mode
|
||||
# but pyright doesn't allow decreasing error severity in strict mode
|
||||
# so we need to manually specify all of the strict mode overrides so we can do that :/
|
||||
|
|
Loading…
Reference in a new issue