content icon indicating copy to clipboard operation
content copied to clipboard

ID generation with umlauts lead to bad links in table of contents

Open adrianrudnik opened this issue 5 years ago • 7 comments

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.

adrianrudnik avatar Dec 31 '20 14:12 adrianrudnik

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.

Peter-Krebs avatar Jan 07 '21 10:01 Peter-Krebs

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?

leandromatos-hotmart avatar Feb 05 '21 15:02 leandromatos-hotmart

same issue here (sorry for dup)

snajjar avatar Feb 07 '21 00:02 snajjar

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',
    ],
  },
},

leandromatos-hotmart avatar Feb 08 '21 18:02 leandromatos-hotmart

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}
  },
...

wirk avatar Feb 11 '21 19:02 wirk

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)

deraw avatar Nov 04 '21 14:11 deraw