You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

RemarkJS制作的HTML/Markdown幻灯片单文件多语言方案咨询

单文件双语Remark幻灯片实现方案

以下方案均无需维护双份文件,兼容原生RemarkJS逻辑,比简单的|||行分割容错率更高、维护成本更低:

方案1:基于Remark原生类注释实现(优先推荐)

RemarkJS本身支持通过<!-- .class: 标识 -->的注释语法给内容块打标记,完全不需要自定义特殊分割符,也不会和正文内容冲突。
首先在#source文本域里按如下格式写Markdown内容,不同语言块前加对应语言标记即可:

<textarea id="source">
class: middle, center

<!-- .lang: en -->
# First slide
My presentation about XYZ

<!-- .lang: fr -->
# Première diapositive
Ma présentation à propos de XYZ

---

<!-- .lang: en -->
## Second slide
Hello!

<!-- .lang: fr -->
## Seconde diapositive
Bonjour!
</textarea>

在初始化Remark之前加一段简单的过滤逻辑,只保留目标语言的内容块即可:

// 可从URL参数、localStorage读取用户选择的语言,这里默认英文
const TARGET_LANG = 'en';
const sourceNode = document.getElementById('source');
const rawMarkdown = sourceNode.value;

const processedMarkdown = rawMarkdown.split('\n---\n').map(slideContent => {
  // 保留幻灯片本身的布局、样式配置(比如开头的class配置)
  const slideConfig = slideContent.split('<!-- .lang:')[0];
  // 匹配当前幻灯片下所有带语言标记的内容块
  const langBlockReg = /<!-- \.lang: (en|fr) -->\n([\s\S]*?)(?=(<!-- \.lang:|$))/g;
  const targetContent = [...slideContent.matchAll(langBlockReg)]
    .filter(matchRes => matchRes[1] === TARGET_LANG)
    .map(matchRes => matchRes[2].trim())
    .join('\n\n');
  return slideConfig + targetContent;
}).join('\n---\n');

sourceNode.value = processedMarkdown;
// 内容处理完成后再初始化幻灯片
remark.create();

这个方案的优势:

  • 完全兼容Remark原生语法,不会和Markdown排版、幻灯片分页/布局标记冲突
  • 后续新增语言只需要新增对应lang标记块,无需修改解析逻辑
  • 搭配语言切换按钮可以实现无刷新切语言,切换时重新解析内容后调用幻灯片实例的reload方法即可,无需跳转页面

方案2:成对注释包裹方案(适合内容长度不对等的场景)

如果不同语言的内容长度差异大,不想逐块对齐拆分,可以用成对的HTML注释包裹对应语言的内容,容错率更高:
Markdown内容写法:

<!-- en:start -->
# First slide
My presentation about XYZ
This is a longer paragraph that only exists in English version for explanation
<!-- en:end -->
<!-- fr:start -->
# Première diapositive
Ma présentation à propos de XYZ
<!-- fr:end -->

---

<!-- en:start -->
## Second slide
Hello!
<!-- en:end -->
<!-- fr:start -->
## Seconde diapositive
Bonjour!
Voici un contenu plus long en français qui n'a pas d'équivalent anglais
<!-- fr:end -->

解析逻辑更简单,两次正则替换就能完成:

const TARGET_LANG = 'en';
const sourceNode = document.getElementById('source');
let content = sourceNode.value;
// 先删除所有非目标语言的内容块
['en', 'fr'].filter(lang => lang !== TARGET_LANG).forEach(lang => {
  const blockReg = new RegExp(`<!-- ${lang}:start -->[\\s\\S]*?<!-- ${lang}:end -->`, 'g');
  content = content.replace(blockReg, '');
});
// 清除目标语言的注释标记
content = content.replace(/<!-- (en|fr):start -->\s*/g, '').replace(/<!-- (en|fr):end -->\s*/g, '');
sourceNode.value = content;
remark.create();

这个方案不需要对齐不同语言的内容结构,哪怕某一版内容多几行、少几个段落,都不会影响解析结果。

方案3:自定义宏标记方案(适合逐行对照翻译的场景)

如果你习惯写内容的时候左右对照翻译,也可以替换掉容易和正文冲突的|||分隔符,用几乎不会在正文出现的宏标记做包裹,避免误解析:

# {{en}}First slide{{/en}}{{fr}}Première diapositive{{/fr}}

{{en}}My presentation about XYZ{{/en}}{{fr}}Ma présentation à propos de XYZ{{/fr}}

---

## {{en}}Second slide{{/en}}{{fr}}Seconde diapositive{{/fr}}

{{en}}Hello!{{/en}}{{fr}}Bonjour!{{/fr}}

解析时直接正则删除非目标语言的包裹内容即可,比逐行按|||分割的逻辑更稳定,不会因为某一行漏写分隔符导致整页内容错乱。


避坑提示

  • 不要用短符号、常用标点当语言分隔符,很容易和正文里的代码、公式、特殊标点冲突,导致解析错误
  • 可以把用户选择的语言存在localStorage里,下次打开自动沿用上次的语言设置
  • 做语言切换按钮时不需要刷新页面,重新替换文本域内容后调用幻灯片的reload方法就能完成切换

内容的提问来源于stack exchange,提问作者Basj

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 03:54:22