slidevjs / slidev

Presentation Slides for Developers
https://sli.dev
MIT License
32.72k stars 1.32k forks source link

Discussing support for multilingual slides (i18n) #1125

Open twitwi opened 1 year ago

twitwi commented 1 year ago

Is your feature request related to a problem? Please describe. Sometimes, we might want to create slide decks that are translated in different language without making a complete copy of the deck (and having coherence issue when updating the slides).

Describe the solution you'd like It would be nice to have a way to put different languages close to each other in the slides. Also a way to select the local is needed (probably a bookmarkable way to override the default locale too), and when exporting, maybe a way to export the different versions (or a single version that allow to select the language dynamically).

Describe alternatives you've considered There is a generic i18n plugin for vue, https://vue-i18n.intlify.dev/ but it is probably not convenient (translation are externalized).

First elements of discussion are available in https://github.com/slidevjs/slidev/issues/600 where @d-koppenhagen suggests "there are three different levels of i18n to consider:

I can imagine a few approaches, but this issue is to gather ideas and feedback towards a good syntax/api for translated slides.

twitwi commented 1 year ago

Some syntax ideas below (split in different comments)

twitwi commented 1 year ago

v1: a verbose generic component (some thinking on the names would be required)

# <Tr><en>Hello</en><fr>Salut</fr></Tr>
- <Tr><en>Step</en><fr>Étape</fr></Tr>
- ...

<Tr><en>

A block that is **interpreted** as markdown.

</en><fr>

Un bloc **intérprété** en markdown.

</fr></Tr>
twitwi commented 1 year ago

v2: same as v1 with a default locale

# <Tr>Hello<fr>Salut</fr></Tr>
- <Tr>Step<fr>Étape</fr></Tr>
- ...

<Tr>

A block that is **interpreted** as markdown.

<fr>

Un bloc **intérprété** en markdown.

</fr></Tr>
twitwi commented 1 year ago

v3: same as v2 but ignoring the Tr component, where a translation replaces its parent element content if it used (and is not visible else)

# Hello<fr>Salut</fr>
- Step<fr>Étape</fr>
- ...

A block that is **interpreted** as markdown.
<fr>

Un bloc **intérprété** en markdown.

</fr>
twitwi commented 1 year ago

v4: With a custom separator (with all the problems / questions about escaping etc...)

# Hello |fr| Salut
- Step |fr| Étape
- ...

A block that is **interpreted** as markdown.
|fr| Un bloc **intérprété** en markdown.
twitwi commented 1 year ago

v5: same as v4 but with an implicit language order (on the order of the translations, that needs to be specified, i.e. in the frontmatter)...

# Hello || Salut
- Step || Étape
- ...

A block that is **interpreted** as markdown.
|| Un bloc **intérprété** en markdown.
antfu commented 1 year ago

I honestly feel it's would be much better to just duplicate the file. This topic is essentially discussing how to do i18n to markdown files, which I think there isn't a good solution out there. I guess the root cause is that it doesn't scale, saying if you need to add a third or fourth language, the syntax would be hard to keep up.

I think it might be better to introduce better IDE tools for diffing across two markdown files for i18n usage instead.

d-koppenhagen commented 1 year ago

After having a look at the suggestions and re-thinking about if it really makes sense to translate by paragraphs or words I agree with @antfu . All variants seems to introduce much more complexity and are making the content less readable. Also I think often slides in other languages can't or should be transferred 1:1 at any time.

But I think a slide-wide translation solution based on a frontmatter option could be good.

This option could be evaluated in the result to switch the lang or even in the IDE plugins for preview.

twitwi commented 1 year ago

ok, great, if I understand well, the proposition is (apart from duplicating the slide deck) to have v6: somewhat like v1 but with a single block per slide, using frontmatter (which implementation-wise could look like the hidden frontmatter switch)

---
lang: en
---

# Hello
- Step 1
- ...

A block that is **interpreted** as markdown.

---
lang: fr
---

# Salut
- Étape 1
- ...

Un bloc **intérprété** en markdown.
antfu commented 1 year ago

I think it might be better for us to directly support registering multiple entries to a slidev instance, that the user could switch on the client side. For example, slides.md and slides.fr.md and even general-propose slidev foo.md bar.md.

d-koppenhagen commented 1 year ago

That sounds good. It even allows to have multiple deck versions in parallel (for example for different events). Like a slidev monorepo.

lauriegriffiths commented 1 year ago

I like suggestions v3 and v5.

My reasons for wanting inline translations:

I thought about preparing two different versions of slides in different files. Unfortunately, the user experience isn't great. Either you duplicate your code blocks or you import them from another shared file, so now you have to manage the code snippet file as well.

It would be nice if presenter notes could be supported too.

One more thing, I think support for two languages would be a big win, even if we don't support three or more languages. I think writing something in local language and then translating to English is common, but translating slides into many languages is much less common.

george-gca commented 1 year ago

I would use the solution of creating the common content in shared files, but I guess it does not solve the problem since the title (h1, #) should have a translation for each language. For instance, suppose you want to display some code, that could be shared between two slides. But in one you would want the header to be Code and in another one Código.

The solution I believe is more feasible in avoiding duplicating whole slides is creating something like a translation file for each language, and then in the slides using a reference to it. Something like:

File i18n/en-us.yml

hello: Hello world!

File i18n/pt-br.yml

hello: Olá mundo!

File slides.md

---
---

# {{translation.hello}}

This kind of solution is used in a multilingual plugin for jekyll. What do you think?

twitwi commented 11 months ago

ok, it seems there are 3 different feature sets:

  1. registering multiple entries to an instance, this is probably a lot of work, but very useful for many things (monorepos etc)
  2. a solution like v3 or v5, for quick and easy bilingual authoring (+maybe v6)
  3. generic localization using @george-gca comment (for the low-text, many-languages cases)
antfu commented 11 months ago

I kinda like @george-gca's solution personally, as it works without an extra compile step, and supports components outside of markdown (basically a vue-i18n integration). It might be possible to be done as a plugin/addons on userland.

twitwi commented 10 months ago

I hacked a kind of v3, as an <alt> component that replaces its parent content if the url contains the alt parameter as in http://localhost:3030/?alt#/4?clicks=1

It can be copied from there https://github.com/twitwi/slidev-addon-ultracharger/blob/main/components/Alt.vue

And used in any place like


# Hello<alt>Salut</alt>
- Step<alt>Étape</alt>
- ...

<div>

A block that is **interpreted** as markdown.
<alt>

Un bloc **intérprété** en markdown.

</alt>
</div>
antfu commented 9 months ago

I don't really want to introduce too much markdown syntax to do so. On top of the i18n yaml files, I guess we would have a some inline-local translations (that only apply to that slide) as well. For example:

---
i18n:
  en:
    hello: Salut
    step: Step
  fr:
    hello: Salut
    step: Étape
---

# {{ $t('hello') }}
- {{ $t.step }}
- ...

<div v-if="$locale==='en'>

A block that is **interpreted** as markdown.

</div>
<div v-else>

Un bloc **intérprété** en markdown.

</div>

We might also include some runtime component to do this as a shorthand:

<locale-en>

A block that is **interpreted** as markdown.

</locale-en>
<locale-fr>

Un bloc **intérprété** en markdown.

</locale-fr>
<locale-fallback>

Fallback

</locale-fallback>

This way it can also be used inside Vue components.

What do you think?

george-gca commented 9 months ago

This looks great! I think I prefer the <locale-XX> solution though. It is more clear than some condition hidden in a v-if statement.

I think adding some localized import type like they have in the plugin I talked about would be useful. Something like this:

---
src: ./pages/multiple-entries.md
i18n: true
---

This way, you could have a structure that looks like this, while the slide selects at runtime which file to import based on the locale:

i18n/
|___en/
|   |___pages/
|       |___multiple-entries.md
|___fr/
    |___pages/
        |___multiple-entries.md

Also, I believe a good guideline would be thinking about use cases. For example, if I want to create a slide with just one figure and a title, how could I do this? Would the localization also work on the frontmatter of the slides? For example:

---
layout: image-right
image: https://source.unsplash.com/collection/94734566/1920x1080
---

# Code

Would I be able to do something like:

# localization file
i18n:
  en:
    code: Code
    img_path: img/img_en.png
  pt-br:
    code: Código
    img_path: img/img_pt-br.png

and

---
layout: image-right
image: {{ $t.img_path }}
---

# {{ $t.code }}

or the translation would only work for the body of the slides, and if you want a localization variation of something in the frontmatter you would have to import different whole slides?

piranna commented 9 months ago

I would prefer to have a structure similar to what GitBook does, that's a folder with the language ISO code per location, and a LANGS.md file with links to each one of them. This way it would be easier to combine both documentation tools in a single repository.

eric-burel commented 2 months ago

Hey folks, Just chiming in to describe what I currently do to have a "poor man" i18n for my slides:

export default function markdownItSetup(md) {
  md.use(markdownBracketedSpans)
  md.use(markdownItAttrs, {
    leftDelimiter: "{",
    rightDelimiter: "}",
    allowedAttributes: [], // empty array = all attributes are allowed
  });
...
// Will create <span class="notranslate">
# :work: Dynamic route and ["next export"]{.notranslate}`

image

Now I can click "traduire en français"

This is enough for me as I have 99% people using the slides in French and just need a fallback for people who prefer English.