解决Zotero条目的作者混乱问题

记录下最近在使用 zotero 的时候,遇到的一个不大不小的问题,以及解决方案。

问题

我最近发现 zotero 在解析文章的时候,作者的解析会出现以下bug

  1. 把作者的名和姓解析反了
  2. 一长串的英文作者,例如 A B, C D, E F,有时候会被解析到一个作者里面,作者的 last nameA,但是作者的 first name 会被解析为 B, C D, E F
  3. 一长串中文作者,例如 张三,李四,王五,赵六,会被解析到一个作者中 张三 作为姓氏,李四,王五,赵六 作为名字
  4. 其他乱七八糟的名字的问题

需要说明的是,zotero 的作者顺序是 last name 也就是姓氏在前,first name 也就是名字在后,且 zotero 的每个作者支持 single fieldtwo fields,前者只保留一个字段,后者同时保留姓和名。举例来说,如果一个作者叫 Han Meimei,前面是姓氏,后面是名字。那么当为 single field 的时候,会合并为 Meimei Han,并被放在一个字段里面;当为 two fields 时,last nameHan,而 first nameMeimei

解决方案

针对第一个问题,刚开始的时候,我的解决方案是:手动修改条目的名和姓。毕竟有问题的是个别条目,大多数还是没有问题的。后来发现,出现问题的条目变多之后,特别是有的文章作者很多的时候,那就是灾难了。加上 zotero 的修改实在是很难用,真的改不动。

针对第二个和第三个问题,刚开始我也是手动修改的。后来发现 Jasminum 插件有个功能 Merge Names,能把姓和名合并在一起。

https://lynne-markdown.oss-cn-hangzhou.aliyuncs.com/img/image-2024-01-10.png

但是,特喵的这个插件合并的时候并不会添加逗号,也不好用。起码解决不了我的问题。但是这个插件的设置语言功能还是不错的,可以批量设置,且插件还有一些其他好用的功能。

既然 zotero 支持插件,那么 zotero 应该也会提供即时运行窗口,能运行脚本。发现,确实有:

https://lynne-markdown.oss-cn-hangzhou.aliyuncs.com/img/image-2024-01-10-1.png

可惜只支持运行 javascript,不支持 python,那就写 javascript 吧。

这里提供我的脚本:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
function MergeAndSplitNames() {
    var items = Zotero.getActiveZoteroPane().getSelectedItems();
    //alert('----');
    for (var i = 0; i < items.length; i++) {

        var curItem = items[i];
        if (!curItem.isRegularItem()) {
            continue;
        }
        var curCreator = curItem.getCreators();
        var cl = curCreator.length;
        var authors = curCreator.filter((x) => x.creatorType === undefined || x.creatorType === "author");
        if (authors.length != curCreator.length) {
            var userConfirmed = confirm("检测到存在editot的creator,是否继续?");
            if (!userConfirmed) {
                return;
            }
        }

        // 去除firstName和lastName
        var fullName = "";
        for (var cc of curCreator) {
            
            fullName += cc.lastName.trim() + ",";
            if (cc.firstName.trim() != '') {
                // var gogo = cc.firstName.trim().indexOf(" ") != -1;
                // if(gogo){
                //     fullName += cc.firstName + ",";
                // }else {
                    
                // }
                alert("[Error] 请检查作者firstName:" + cc.firstName);
                return;
            }

        }
        if (fullName === "") {
            alert("[Error] 作者名字为空啊");
            return;
        }
        var newNames = fullName.split(",").map(x => x.trim()).filter(x => x != "");
        if (newNames.length === 0) {
            alert("[Error] 作者名字为空啊2");
            return;
        }
        var newAuthors = [];
        for (var n of newNames) {
            if (n.indexOf(' ') != -1) {
                // 有空格
                var nn = n.split(" ");
                newAuthors.push({
                    "firstName": nn[1],
                    "lastName": nn[0],
                    "creatorType": "author"
                });
            } else {
                // 没有空格
                newAuthors.push({
                    "firstName": "",
                    "lastName": n,
                    "creatorType": "author"
                });
            }
        }

        curItem.setCreators(newAuthors);
        curItem.saveTx();
        alert(curItem.getField('title') + "操作完成!")

    }
}


function inputName() {
     // Get first selected item
     var selectedItems = ZoteroPane.getSelectedItems();
     var curItem = selectedItems[0];
     var condi = curItem && !curItem.isNote() && curItem.isRegularItem();

    if (condi) {
        if (!confirm("确定要手动覆盖作者吗?")) return;
        var i = prompt("请输入作者,使用分号或逗号分割:", "");
        if (i.trim() === "") {
            return;
        }
        var names = i.split(/[,,;;]+/).map(x => x.trim()).filter(x => x != " ");
        
        var newAuthors = [];
        for (var n of names) {
            if (n.indexOf(' ') != -1) {
                // 有空格
                var nn = n.split(" ");
                newAuthors.push({
                    "firstName": nn[1],
                    "lastName": nn[0],
                    "creatorType": "author"
                });
            } else {
                // 没有空格
                newAuthors.push({
                    "firstName": "",
                    "lastName": n,
                    "creatorType": "author"
                });
            }
        }

        curItem.setCreators(newAuthors);
        curItem.saveTx();
        alert(curItem.getField('title') + "操作完成!");
    }

}

function exchangefirstNamelastName() {
    var items = Zotero.getActiveZoteroPane().getSelectedItems();
    //alert('----');
    for (var i = 0; i < items.length; i++) {

        var curItem = items[i];
        if (!curItem.isRegularItem()) {
            continue;
        }
        var curCreator = curItem.getCreators();
        for (var c of curCreator)
        {
          var temp = c.firstName;
          c.firstName =  c.lastName;
          c.lastName = temp;
        }
        
        curItem.setCreators(curCreator);
        curItem.saveTx();
        alert(curItem.getField('title') + "操作完成!");   
  }

}

// xxx 执行xxx函数
  • console.log 找不出输出,建议使用 alert/prompt/confirm 等函数
  • 需要执行的时候,选中条目,然后点击 Run 就可以
  • js 开发成本很低,人生苦短,我也用js

所以,除了费大力气写个插件,还是可以在本地运行 javascript 代码来完成对选中条目信息的修改的。有的功能只需要简单写几行代码就好。

提供的代码并不完全符合所有场景,一方面是记录下 zotero local javascript api 的使用方式,一方面是记录解决的这几个问题。

写代码有不懂的,直接问 ChatGPT 即可。

参考

Buy me a coffee~
roderick 支付宝支付宝
roderick 微信微信
0%