main
  1// 部分取自 modern-nju-thesis
  2
  3// 双语参考文献
  4#let bilingual-bibliography(
  5  bibliography: none,
  6  title: [参考文献#metadata((en: "References"))],
  7  full: false,
  8  style: "gb-7714-2005-numeric",
  9  mapping: (:),
 10  extra-comma-before-et-al-trans: false,
 11  // 用于控制多位译者时表现为 `et al. tran`(false) 还是 `et al., tran`(true)
 12  allow-comma-in-name: false,
 13  // 如果使用的 CSL 中,英文姓名中会出现逗号,请设置为 true
 14) = {
 15  assert(
 16    bibliography != none,
 17    message: "请传入带有 source 的 bibliography 函数。",
 18  )
 19
 20  // Please fill in the remaining mapping table here
 21  mapping = (
 22    (
 23      //"等": "et al",
 24      "卷": "Vol.",
 25      "册": "Bk.",
 26      // "译": ", tran",
 27      // "等译": "et al. tran",
 28      // 注: 请见下方译者数量判断部分。
 29    )
 30      + mapping
 31  )
 32
 33  let to-string(content) = {
 34    if content.has("text") {
 35      content.text
 36    } else if content.has("children") {
 37      content.children.map(to-string).join("")
 38    } else if content.has("child") {
 39      to-string(content.child)
 40    } else if content.has("body") {
 41      to-string(content.body)
 42    } else if content == [ ] {
 43      " "
 44    }
 45  }
 46
 47  show grid.cell.where(x: 1): it => {
 48    // 后续的操作是对 string 进行的。
 49    let ittext = to-string(it)
 50    // 判断是否为中文文献:去除特定词组后,仍有至少两个连续汉字。
 51    let pureittext = ittext.replace(
 52      regex("[等卷册和版本章期页篇译间者(不详)]"),
 53      "",
 54    )
 55    let ittext = if pureittext.find(regex("\p{sc=Hani}{2,}")) != none {
 56      ittext
 57    } else {
 58      // 若不是中文文献,进行替换
 59      // 第xxx卷、第xxx册的情况:变为 Vol. XXX 或 Bk. XXX。
 60      let reptext = ittext
 61      reptext = reptext.replace(
 62        regex("(第\s?)?\d+\s?[卷册]"),
 63        itt => {
 64          if itt.text.contains("卷") {
 65            "Vol. "
 66          } else {
 67            "Bk. "
 68          }
 69          itt.text.find(regex("\d+"))
 70        },
 71      )
 72
 73      // 第xxx版/第xxx本的情况:变为 1st ed 格式。
 74      reptext = reptext.replace(
 75        regex("(第\s?)?\d+\s?[版本]"),
 76        itt => {
 77          let num = itt.text.find(regex("\d+"))
 78          num
 79          if num.clusters().len() == 2 and num.clusters().first() == "1" {
 80            "th"
 81          } else {
 82            (
 83              "1": "st",
 84              "2": "nd",
 85              "3": "rd",
 86            ).at(num.clusters().last(), default: "th")
 87          }
 88          " ed"
 89        },
 90      )
 91
 92      // 译者数量判断:单数时需要用 trans,复数时需要用 tran 。
 93      /*
 94      注:
 95          1. 目前判断译者数量的方法非常草率:有逗号就是多个作者。但是在部分 GB/T 7714-2015 方言中,姓名中可以含有逗号。如果使用的 CSL 是姓名中含有逗号的版本,请将 bilingual-bibliography 的 allow-comma-in-name 参数设为 true。
 96          2. 在 GB/T 7714-2015 原文中有 `等译`(P15 10.1.3 小节 示例 1-[1] 等),但未给出相应的英文缩写翻译。CSL 社区库内的 GB/T 7714-2015 会使用 `等, 译` 和 `et al., tran` 的写法。为使中英文与标准原文写法一致,本小工具会译作 `et al. tran`。若需要添加逗号,请将 bilingual-bibliography 的 extra-comma-before-et-al-trans 参数设为 true。
 97          3. GB/T 7714-2015 P8 7.2 小节规定:“译”前需加逗号。因此单个作者的情形,“译” 会被替换为 ", trans"。与“等”并用时的情况请见上一条注。
 98          如果工作不正常,可以考虑换为简单关键词替换,即注释这段情况,取消 13 行 mapping 内 `译` 条目的注释。
 99      */
100      reptext = reptext.replace(
101        regex("\].+?译"),
102        itt => {
103          // 我想让上面这一行匹配变成非贪婪的,但加问号后没啥效果?
104          let comma-in-itt = itt.text.replace(regex(",?\s?译"), "").matches(",")
105          if (
106            type(comma-in-itt) == array
107              and comma-in-itt.len()
108                >= (
109                  if allow-comma-in-name { 2 } else { 1 }
110                )
111          ) {
112            if extra-comma-before-et-al-trans {
113              itt.text.replace(regex(",?\s?译"), ", tran")
114            } else {
115              itt.text.replace(regex(",?\s?译"), " tran")
116            }
117          } else {
118            itt.text.replace(regex(",?\s?译"), ", trans")
119          }
120        },
121      )
122
123      // `等` 特殊处理:`等`后方接内容也需要译作 `et al.`,如 `等译` 需要翻译为 `et al. trans`
124      reptext = reptext.replace(
125        regex("等."),
126        itt => {
127          "et al."
128          // 如果原文就是 `等.`,则仅需简单替换,不需要额外处理
129          // 如果原文 `等` 后没有跟随英文标点,则需要补充一个空格
130          if not (
131            itt.text.last()
132              in (
133                ".",
134                ",",
135                ";",
136                ":",
137                "[",
138                "]",
139                "/",
140                "\\",
141                "<",
142                ">",
143                "?",
144                "(",
145                ")",
146                " ",
147                "\"",
148                "'",
149              )
150          ) {
151            " "
152          }
153          // 原文有英文句号时不需要重复句号,否则需要将匹配到的最后一个字符吐回来
154          if not itt.text.last() == "." {
155            itt.text.last()
156          }
157        },
158      )
159
160      // 其他情况:直接替换
161      reptext = reptext.replace(
162        regex("\p{sc=Hani}+"),
163        itt => {
164          mapping.at(itt.text, default: itt.text)
165          // 注意:若替换功能工作良好,应该不会出现 `default` 情形
166        },
167      )
168      reptext
169    }
170
171    // 判断文献类型的方括号前是否为中文
172    // 如果不是中文且不是空格,加一个空格
173    // 并且 style 应为 gb-7714
174    if style.starts-with("gb-7714") {
175      ittext = ittext.replace(
176        regex("(.)\[([A-Z])"),
177        itt => {
178          if itt.captures.at(0) != none {
179            let char = itt.captures.at(0)
180            if char.match(regex("\p{sc=Hani}")) == none and char != " " {
181              char + " ["
182            } else {
183              char + "["
184            }
185          }
186          itt.captures.at(1)
187        },
188      )
189    }
190    ittext
191  }
192
193  set text(lang: "zh")
194  bibliography(
195    title: title,
196    full: full,
197    style: style,
198  )
199}