A Zotero translator for Perspectives on History

February 2026

Perspectives on History is a wonderful magazine published by the American Historical Association. Zotero by default sees entries as websites, not articles. This translator, coded by Claude, will instead import them into Zotero as magazine articles, capturing the title, author or authors, publication, and date.

To install a translator, place the file in your Zotero data>translators directory and restart Zotero.

{
	"translatorID": "b5f2e4a1-9c3d-4f7b-8e6a-1d2c3b4a5f6e",
	"label": "AHA Perspectives on History",
	"creator": "Custom",
	"target": "historians\\.org/perspectives-article/",
	"minVersion": "5.0",
	"maxVersion": "",
	"priority": 100,
	"inRepository": false,
	"translatorType": 4,
	"browserSupport": "gcsibv",
	"lastUpdated": "2026-02-22 12:00:00"
}

function detectWeb(doc, url) {
	if (url.indexOf('perspectives-article') !== -1) {
		return 'magazineArticle';
	}
	return false;
}

function doWeb(doc, url) {
	var item = new Zotero.Item('magazineArticle');

	// TITLE
	var h1 = doc.querySelector('h1');
	item.title = h1
		? h1.textContent.trim()
		: doc.title.replace(/\s*[-\u2013\u2014]+\s*AHA\s*$/i, '').trim();

	// AUTHOR
	// Page text order: [nav1...Donate][nav2...Donate] AUTHOR TITLE ...
	// We scan every occurrence of 'Donate' and check whether the title
	// appears within 500 chars after it. The first hit where something
	// non-empty sits between Donate and the title is the byline.
	var normBody = doc.body ? doc.body.textContent.replace(/\s+/g, ' ').trim() : '';
	var searchKey = item.title.replace(/\s+/g, ' ').trim().substring(0, 30);
	var authorText = '';

	if (normBody && searchKey) {
		var pos = 0;
		while (pos < normBody.length) {
			var di = normBody.indexOf('Donate', pos);
			if (di === -1) break;
			var slice = normBody.substring(di + 6, di + 506);
			var ti = slice.indexOf(searchKey);
			if (ti > 0) {
				var candidate = slice.substring(0, ti).trim();
				if (candidate.length > 0 && candidate.length < 200 &&
				    candidate.split(/\s+/).length >= 2 &&
				    !/[:/]/.test(candidate)) {
					authorText = candidate;
					break;
				}
			}
			pos = di + 6;
		}
	}

	if (authorText) {
		var normalised = authorText
			.replace(/\s+with\s+/gi, ';')
			.replace(/,?\s+and\s+/gi, ';')
			.replace(/,/g, ';');
		var names = normalised.split(';')
			.map(function(s) { return s.trim(); })
			.filter(function(s) { return s.length > 0; });
		for (var n = 0; n < names.length; n++) {
			var parts = names[n].split(/\s+/);
			if (parts.length < 2) continue;
			var lastName = parts.pop();
			var firstName = parts.join(' ');
			item.creators.push({ firstName: firstName, lastName: lastName, creatorType: 'author' });
		}
	}

	// DATE
	var dateText = '';
	var monthMap = {
		january:'01', february:'02', march:'03', april:'04',
		may:'05', june:'06', july:'07', august:'08',
		september:'09', october:'10', november:'11', december:'12'
	};
	var um = url.match(/-([a-z]+)-(\d{4})\/?/i);
	if (um && monthMap[um[1].toLowerCase()]) {
		dateText = um[2] + '-' + monthMap[um[1].toLowerCase()];
	}
	if (!dateText) {
		var strongs = doc.querySelectorAll('strong');
		for (var s = 0; s < strongs.length; s++) {
			if (strongs[s].textContent.trim() === 'Publication Date') {
				var parent = strongs[s].parentElement;
				if (parent) {
					var sib = parent.nextElementSibling;
					dateText = sib
						? sib.textContent.trim()
						: parent.textContent.replace(/Publication\s+Date/i, '').trim();
				}
				break;
			}
		}
	}
	if (!dateText) {
		var dm = normBody.match(/Publication Date ([A-Z][a-z]+(?: \d{1,2},)? \d{4})/);
		if (dm) dateText = dm[1].trim();
	}
	if (dateText) item.date = dateText;

	// FIXED FIELDS
	item.publicationTitle = 'Perspectives on History';
	item.url = url;
	item.accessDate = 'CURRENT_TIMESTAMP';

	item.complete();
}