Implementing interlinear text in macOS

I’m sure that I’m not alone in wanting to view different versions or translations of text side by side. It’s easily done in parallel, with facing pages showing two versions, or multiple columns coping with even more, and demonstrated effectively in printed volumes in the Loeb Classical Library, Penguin Parallel Texts, and others. Getting the text to flow correctly in different apps can sometimes be tricky, but the major problem with parallel text is that it isn’t easy to use. This is best demonstrated by paralleltext.io for example.

If you’re interested in making such parallel texts at a finer level, it’s not difficult using Tinderbox. But even with such closely adjacent short passages of text, it’s not a great deal easier to access.

tbox17

The very best printed translations therefore bring both (or more) texts together in interleaved lines, but those have required close collaboration between an author or editor and the typesetter. They’re still in common use for short passages of text, but I can’t recall when I last saw an entire printed book other than the Bible set in such interlinear text. There’s even a specialist website which offers some examples.

My original idea was to layer together different versions of a text much as you layer graphics in a good graphics app. Thinking this into macOS terms, this would mean superimposing and linking multiple TextViews. Although this is feasible, to support scrolling, the standard approach is to put a TextView (NSTextView) inside a scroller. And there I hit a wall, as the right type of scroller can only contain a single TextView, not several in layers.

A little discussion led me to consider whether this could be implemented in terms of a TableView instead. That would contain lines drawn in rotation from the 2-4 different sources. The snag is that the data source for a table would have to contain interleaved content from each of the single-version sources, so there wouldn’t be any advantage to the TableView at all.

I concluded that, unless I wanted to re-provide large parts of Cocoa, the simple approach was to assemble a composite document containing interleaved text.

Looking at other implementations of similar systems, such as those in EXMARaLDA, texts require extensive preparation and markup using XML. For a great many users, that would make this an impossible task. If you can’t take a couple of text files from Project Gutenberg and, after minimal preparation, generate interlinear text, then the idea is doomed to a small and very specialist market, and ordinary users would be locked out.

I therefore switched to the idea of merging plain text from different translations/versions of a document in the form of Attributed Text, the macOS TextKit version of Rich Text, which is what DelightEd uses at its heart. What was going to be a separate app then became a feature to add to that app.

This required a simple and appropriate way to select the files to be merged. As these are few in number, limited to a maximum of 4, expecting the user to select them multiply in Open File dialogs, or put them all in a dedicated folder, seemed clumsy. Instead, this new feature uses tabbed windows to assemble the files to be merged.

delighted201

Normally, you wouldn’t expect an open document to gain access to the contents of other open documents. Put them together as tabbed views within a single window, though, and any of those documents can simply get a list of the documents which are open in other tabs in the same window:

  • NSWindow.tabbedWindows, which is essentially undocumented, returns a list of all the NSWindows which are tabs in the same window;
  • for each of those, NSWindow.windowController.document returns the Document which owns each of the tabbed views in that list.

My code then extracts from each of those documents the current Attributed Text for it, and passes that to a new document for merging into its Attributed Text. Creating a new document programmatically for this is a matter of:
NSDocumentController.shared.newDocument(self)
let theDoc = NSApplication.shared.orderedDocuments[0] as! Document

then calling the function of the document to process the Attributed Text from the list of documents to be merged.

This all seems relatively robust, and is available in macOS from Sierra onwards, so that is how DelightEd 2 handles this.

I’m very grateful to Jeff Johnson for pointing out how ugly and complex other solutions would have been.