ID generation with umlauts lead to bad links in table of contents
Version
@nuxt/content: v1.11.1 nuxt: v2.14.12
Reproduction Link
https://codesandbox.io/s/jovial-cray-sphhr?file=/content/index.md:55-119
Steps to reproduce
On the index page, you can not follow the h2 anchor with the umlauts "Ü".
What is Expected?
The anchor should be reachable.
What is actually happening?
The TOC-example snippet is correct, it encodes the anchor link to https://sphhr.sse.codesandbox.io/#%C3%BCber-uns---does-not-work
The output produced by <nuxt-content /> creates an ID to follow that is not escaped: id="über-uns---does-not-work".
There are two approaches:
a) Avoid URL escaping in the TOC snippet with <a :href="#${link.id}">{{ link.text }}</a> instead.
b) Strip all non-standard characters from TOC and ID generation.
Sadly even Github has its problem with the same topic: https://github.com/adrianrudnik/md-umlauts-test
Clicking the anchor icon in front produces #über-uns---does-not-work, right clicking the anchor icon > copy link will produce #%C3%BCber-uns---does-not-work, so no guidance there.
Agreed, the automatic anchor names need transliteration. For example:
https://www.npmjs.com/package/transliteration
The package provides a slugify() function to build readable anchor names.
The same occurred here with Latin characters with accents.
Eg: #visão-geral -> #vis%C3%A3o-geral.
Definitely, accented characters should be treated.
Is there a hook or alternative to handle this title information that becomes an ID and a link before it is rendered?
same issue here (sorry for dup)
I had resolved this problem by removing the plugin responsible for creating links on headings (this feature is not important to me).
On the Nuxt Content module configuration inside nuxt.config.js, you can disable the remark-autolink-headings.
/**
* Nuxt Content module configuration
*/
content: {
markdown: {
remarkPlugins: () => [
'remark-squeeze-paragraphs',
'remark-slug',
// 'remark-autolink-headings',
'remark-external-links',
'remark-footnotes',
],
},
},
I just ran into this issue as well and created a small hacky fix to throw into your _slug.vue till this gets properly fixed.
import {slugify} from 'transliteration'
function fixIds(elements = []) {
const slugifyOptions = {replace: {ü: 'ue', ä: 'ae', ö: 'oe', ß: 'ss'}}
elements.forEach((el) => {
if (el.props && el.props.id) {
el.props.id = slugify(el.props.id, slugifyOptions)
}
if (el.id) {
el.id = slugify(el.id, slugifyOptions)
}
if (el.children) {
fixIds(el.children)
}
})
}
export default {
async asyncData({$content, params}) {
const article = await $content('articles', params.slug).fetch()
fixIds(article.body.children)
fixIds(article.toc)
return {article}
},
...
Hi,
I also ran into this issue and I completed @wirk's solution to also slugify anchors:
import slugify from 'slugify';
interface Element {
id?: string;
props?: {
[key: string]: string;
};
children?: Element[];
}
/** @see https://github.com/nuxt/content/issues/702 */
export function slugifyAnchors(elements: Element[] = []): void {
elements.forEach((el) => {
if (el.props?.id) {
el.props.id = slugify(el.props.id);
}
if (el.props?.href) {
const URI = decodeURI(el.props.href);
const httpScheme = URI.startsWith('http://');
const httpsScheme = URI.startsWith('https://');
const isExternalURI = httpScheme || httpsScheme;
const href = isExternalURI ? URI : '#' + slugify(URI);
el.props.href = href;
}
if (el.id) {
el.id = slugify(el.id);
}
if (el.children) {
slugifyAnchors(el.children);
}
});
}
(Updated to allow external links)