[feature] Shortcodes and anchor-link.html can access lang context (#1609)
* Pass lang to shortcodes context * Add tests for lang in shortcodes * Lang is passed to anchor-link.html template * Document passing lang to shortcodes/anchor-link.html Add a test to make sure lang can be overriden by passing an explicit argument to the shortcode, for usage in the markdown filter where the language context is not available. * Update docs for more information on shortcodes+i18n+markdown() filter Co-authored-by: southerntofu <southerntofu@thunix.net>
This commit is contained in:
parent
eceb1bd79d
commit
84b75f9725
@ -14,6 +14,7 @@ pub struct RenderContext<'a> {
|
||||
pub current_page_permalink: &'a str,
|
||||
pub permalinks: Cow<'a, HashMap<String, String>>,
|
||||
pub insert_anchor: InsertAnchor,
|
||||
pub lang: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> RenderContext<'a> {
|
||||
@ -34,10 +35,13 @@ impl<'a> RenderContext<'a> {
|
||||
permalinks: Cow::Borrowed(permalinks),
|
||||
insert_anchor,
|
||||
config,
|
||||
lang,
|
||||
}
|
||||
}
|
||||
|
||||
// In use in the markdown filter
|
||||
// NOTE: This RenderContext is not i18n-aware, see MarkdownFilter::filter for details
|
||||
// If this function is ever used outside of MarkdownFilter, take this into consideration
|
||||
pub fn from_config(config: &'a Config) -> RenderContext<'a> {
|
||||
Self {
|
||||
tera: Cow::Owned(Tera::default()),
|
||||
@ -46,6 +50,7 @@ impl<'a> RenderContext<'a> {
|
||||
permalinks: Cow::Owned(HashMap::new()),
|
||||
insert_anchor: InsertAnchor::None,
|
||||
config,
|
||||
lang: &config.default_language,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -342,6 +342,7 @@ pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result<Render
|
||||
let mut c = tera::Context::new();
|
||||
c.insert("id", &id);
|
||||
c.insert("level", &heading_ref.level);
|
||||
c.insert("lang", &context.lang);
|
||||
|
||||
let anchor_link = utils::templates::render_template(
|
||||
ANCHOR_LINK_TEMPLATE,
|
||||
|
@ -107,6 +107,11 @@ fn render_shortcode(
|
||||
body: Option<&str>,
|
||||
) -> Result<String> {
|
||||
let mut tera_context = Context::new();
|
||||
|
||||
// nth and lang are inserted before passed arguments, so that they can be overriden explicitly
|
||||
tera_context.insert("nth", &invocation_count);
|
||||
tera_context.insert("lang", &context.lang);
|
||||
|
||||
for (key, value) in args.iter() {
|
||||
tera_context.insert(key, value);
|
||||
}
|
||||
@ -114,7 +119,7 @@ fn render_shortcode(
|
||||
// Trimming right to avoid most shortcodes with bodies ending up with a HTML new line
|
||||
tera_context.insert("body", b.trim_end());
|
||||
}
|
||||
tera_context.insert("nth", &invocation_count);
|
||||
|
||||
tera_context.extend(context.tera_context.clone());
|
||||
|
||||
let mut template_name = format!("shortcodes/{}.md", name);
|
||||
|
@ -181,6 +181,47 @@ fn can_render_shortcode_with_markdown_char_in_args_value() {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_render_html_shortcode_with_lang() {
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let config = Config::default_for_test();
|
||||
let mut tera = Tera::default();
|
||||
tera.extend(&ZOLA_TERA).unwrap();
|
||||
tera.add_raw_template("shortcodes/i18nshortcode.html", "{{ lang }}").unwrap();
|
||||
let context = RenderContext::new(
|
||||
&tera,
|
||||
&config,
|
||||
&config.default_language,
|
||||
"",
|
||||
&permalinks_ctx,
|
||||
InsertAnchor::None,
|
||||
);
|
||||
|
||||
let res = render_content("a{{ i18nshortcode() }}a", &context).unwrap();
|
||||
assert_eq!(res.body, "<p>aena</p>\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_render_md_shortcode_with_lang() {
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let config = Config::default_for_test();
|
||||
let mut tera = Tera::default();
|
||||
tera.extend(&ZOLA_TERA).unwrap();
|
||||
tera.add_raw_template("shortcodes/i18nshortcode.md", "![Book cover in {{ lang }}](cover.{{ lang }}.png)").unwrap();
|
||||
let context = RenderContext::new(
|
||||
&tera,
|
||||
&config,
|
||||
&config.default_language,
|
||||
"",
|
||||
&permalinks_ctx,
|
||||
InsertAnchor::None,
|
||||
);
|
||||
|
||||
let res = render_content("{{ i18nshortcode() }}", &context).unwrap();
|
||||
assert_eq!(res.body, "<p><img src=\"cover.en.png\" alt=\"Book cover in en\" /></p>\n");
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn can_render_body_shortcode_with_markdown_char_in_name() {
|
||||
let permalinks_ctx = HashMap::new();
|
||||
@ -689,6 +730,28 @@ fn can_insert_anchor_with_other_special_chars() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_insert_anchor_with_lang() {
|
||||
let mut tera = Tera::default();
|
||||
tera.extend(&ZOLA_TERA).unwrap();
|
||||
tera.add_raw_template("anchor-link.html", "({{ lang }})").unwrap();
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let config = Config::default_for_test();
|
||||
let context = RenderContext::new(
|
||||
&tera,
|
||||
&config,
|
||||
&config.default_language,
|
||||
"",
|
||||
&permalinks_ctx,
|
||||
InsertAnchor::Right,
|
||||
);
|
||||
let res = render_content("# Hello", &context).unwrap();
|
||||
assert_eq!(
|
||||
res.body,
|
||||
"<h1 id=\"hello\">Hello(en)</h1>\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_make_toc() {
|
||||
let permalinks_ctx = HashMap::new();
|
||||
|
@ -33,6 +33,10 @@ impl MarkdownFilter {
|
||||
|
||||
impl TeraFilter for MarkdownFilter {
|
||||
fn filter(&self, value: &Value, args: &HashMap<String, Value>) -> TeraResult<Value> {
|
||||
// NOTE: RenderContext below is not aware of the current language
|
||||
// However, it should not be a problem because the surrounding tera
|
||||
// template has language context, and will most likely call a piece of
|
||||
// markdown respecting language preferences.
|
||||
let mut context = RenderContext::from_config(&self.config);
|
||||
context.permalinks = Cow::Borrowed(&self.permalinks);
|
||||
context.tera = Cow::Borrowed(&self.tera);
|
||||
@ -123,6 +127,22 @@ mod tests {
|
||||
assert_eq!(result.unwrap(), to_value(&"<h1 id=\"hey\">Hey</h1>\n").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn markdown_filter_override_lang() {
|
||||
// We're checking that we can use a workaround to explicitly provide `lang` in markdown filter from tera,
|
||||
// because otherwise markdown filter shortcodes are not aware of the current language
|
||||
// NOTE: This should also work for `nth` although i don't see a reason to do that
|
||||
let args = HashMap::new();
|
||||
let config = Config::default();
|
||||
let permalinks = HashMap::new();
|
||||
let mut tera = super::load_tera(&PathBuf::new(), &config).map_err(tera::Error::msg).unwrap();
|
||||
tera.add_raw_template("shortcodes/explicitlang.html", "a{{ lang }}a").unwrap();
|
||||
let filter = MarkdownFilter { config, permalinks, tera };
|
||||
let result = filter.filter(&to_value(&"{{ explicitlang(lang='jp') }}").unwrap(), &args);
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(result.unwrap(), to_value(&"ajpa").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn markdown_filter_inline() {
|
||||
let mut args = HashMap::new();
|
||||
|
@ -39,11 +39,12 @@ This option is set at the section level: the `insert_anchor_links` variable on t
|
||||
|
||||
The default template is very basic and will need CSS tweaks in your project to look decent.
|
||||
If you want to change the anchor template, it can be easily overwritten by
|
||||
creating an `anchor-link.html` file in the `templates` directory.
|
||||
creating an `anchor-link.html` file in the `templates` directory. [Here](https://github.com/getzola/zola/blob/master/components/templates/src/builtins/anchor-link.html) you can find the default template.
|
||||
|
||||
The anchor link template has the following variables:
|
||||
|
||||
- `id`: the heading's id after applying the rules defined by `slugify.anchors`
|
||||
- `lang`: the current language, unless called from the `markdown` template filter, in which case it will always be `en`
|
||||
- `level`: the heading level (between 1 and 6)
|
||||
|
||||
## Internal links
|
||||
|
@ -134,10 +134,19 @@ If you want to have some content that looks like a shortcode but not have Zola t
|
||||
you will need to escape it by using `{%/*` and `*/%}` instead of `{%` and `%}`. You won't need to escape
|
||||
anything else until the closing tag.
|
||||
|
||||
## Shortcode context
|
||||
|
||||
Every shortcode can access some variables, beyond what you explicitly passed as parameter. These variables are explained in the following subsections:
|
||||
|
||||
- invocation count (`nth`)
|
||||
- current language (`lang`), unless called from the `markdown` template filter (in which case it will always be the same value as `default_language` in configuration, or `en` when it is unset)
|
||||
|
||||
When one of these variables conflict with a variable passed as argument, the argument value will be used.
|
||||
|
||||
### Invocation Count
|
||||
|
||||
Every shortcode context is passed in a variable named `nth` that tracks how many times a particular shortcode has
|
||||
been invoked in a Markdown file. Given a shortcode `true_statement.html` template:
|
||||
been invoked in the current Markdown file. Given a shortcode `true_statement.html` template:
|
||||
|
||||
```jinja2
|
||||
<p id="number{{ nth }}">{{ value }} is equal to {{ nth }}.</p>
|
||||
@ -152,6 +161,18 @@ It could be used in our Markdown as follows:
|
||||
|
||||
This is useful when implementing custom markup for features such as sidenotes or end notes.
|
||||
|
||||
### Current language
|
||||
|
||||
**NOTE:** When calling a shortcode from within the `markdown` template filter, the `lang` variable will always be `en`. If you feel like you need that, please consider using template macros instead. If you really need that, you can rewrite your Markdown content to pass `lang` as argument to the shortcode.
|
||||
|
||||
Every shortcode can access the current language in the `lang` variable in the context. This is useful for presenting/filtering information in a shortcode depending in a per-language manner. For example, to display a per-language book cover for the current page in a shortcode called `bookcover.md`:
|
||||
|
||||
```jinja2
|
||||
![Book cover in {{ lang }}](cover.{{ lang }}.png)
|
||||
```
|
||||
|
||||
You can then use it in your Markdown like so: `{{/* bookcover() */}}`
|
||||
|
||||
## Built-in shortcodes
|
||||
|
||||
Zola comes with a few built-in shortcodes. If you want to override a default shortcode template,
|
||||
|
@ -64,7 +64,10 @@ Zola adds a few filters in addition to [those](https://tera.netlify.com/docs/#fi
|
||||
in Tera.
|
||||
|
||||
### markdown
|
||||
Converts the given variable to HTML using Markdown. Please note that shortcodes evaluated by this filter cannot access the current rendering context. `config` will be available, but accessing `section` or `page` (among others) from a shortcode called within the `markdown` filter will prevent your site from building. See [this discussion](https://github.com/getzola/zola/pull/1358).
|
||||
Converts the given variable to HTML using Markdown. There are a few differences compared to page/section Markdown rendering:
|
||||
|
||||
- shortcodes evaluated by this filter cannot access the current rendering context: `config` will be available, but accessing `section` or `page` (among others) from a shortcode called within the `markdown` filter will prevent your site from building (see [this discussion](https://github.com/getzola/zola/pull/1358))
|
||||
- `lang` in shortcodes will always be equal to the site's `default_lang` (or `en` otherwise) ; it should not be a problem, but if it is in most cases, but if you need to use language-aware shortcodes in this filter, please refer to the [Shortcode context](@/documentation/content/shortcodes.md#shortcode-context) section of the docs.
|
||||
|
||||
By default, the filter will wrap all text in a paragraph. To disable this behaviour, you can
|
||||
pass `true` to the inline argument:
|
||||
|
Loading…
Reference in New Issue
Block a user