Kurz gesagt
Ein Heft über drei alte Astrolabien, gedruckt auf Englisch, Spanisch und Arabisch. Alle drei teilen einen Entwurf; nur die arabische Ausgabe ändert Schriften und Größen.
Was Sie setzen
Ein Heft von vier A5-Seiten über drei Astrolabien im Metropolitan Museum of Art: eines 1291 im Jemen gebaut, eines 1654 in Iran, eines 1462 in Europa. Der Verlag bringt es auf Englisch, Spanisch und Arabisch heraus, und die drei Ausgaben sind ein Entwurf. Umschlagfoto, messingfarbene Zwischentitel, der Kasten mit seiner Linie, die umflossenen Abbildungen und der dunkle Tabellenkopf kommen aus denselben Einstellungen. Die arabische Ausgabe ändert, was zu ihrer Schrift gehört: Amiri und IBM Plex Sans Arabic statt Source Serif und Source Sans, einen größeren Grad und Beschriftungen ohne Versalien und Sperrung. Sie liest sich von rechts und wird rechts gebunden, ohne eine einzige eigene Koordinate.
Dieses Rezept beantwortet
- Wie teile ich eine Gestaltung zwischen den Ausgaben eines Buchs in mehreren Sprachen und ändere nur Schriften, Silbentrennung, Anführungszeichen und Beschriftungen jeder Sprache?
- Wie veröffentliche ich dasselbe Buch aus einem Projekt in zwei Sprachen?
Die kurze Antwort
const ARABIC_LABEL = { fontFamily: 'IBM Plex Sans Arabic', letterSpacing: pt(0),
textTransform: 'none' }; // Arabic letters join: never tracked, and they have no capitals
// English and Spanish need nothing more than their locale: it picks the hyphenation patterns
// and the words of the figure and table labels. Arabic sets the faces of its script.
const overrides = {
ar: {
bodyText: { fontFamily: 'Amiri', fontSize: pt(12), lineHeight: pt(19),
firstLineIndent: em(1.5) },
headings: { fontFamily: 'Amiri', fontWeight: 700,
levels: [{ level: 2, fontSize: pt(15), lineHeight: pt(24) }] },
// Lists keyed by id merge entry by entry: only these fields of these elements change.
headingStyles: [{ id: 'cover', advancedDesign: { slot: { elements: [
{ id: 'kicker', ...ARABIC_LABEL, fontSize: pt(10) },
{ id: 'title', fontFamily: 'Amiri', fontWeight: 700, fontSize: pt(50), lineHeight: 1.25 },
{ id: 'facts', ...ARABIC_LABEL, fontSize: pt(9.5) },
] } } }],
header: { elements: ['verso-folio', 'verso-title', 'recto-title', 'recto-folio']
.map((id) => ({ id, ...ARABIC_LABEL, fontSize: pt(9) })) },
calloutStyles: [{ id: 'note', titleStyle: { ...ARABIC_LABEL, fontSize: pt(10) },
body: { fontSize: pt(11.5), lineHeight: pt(19) } }],
tableStyle: { headerFontFamily: 'IBM Plex Sans Arabic', headerFontSize: pt(9),
bodyFontFamily: 'IBM Plex Sans Arabic', bodyFontSize: pt(9.4) },
captionStyle: { fontFamily: 'IBM Plex Sans Arabic', fontSize: pt(9) },
paragraphStyles: [{ id: 'colophon', fontFamily: 'IBM Plex Sans Arabic', fontSize: pt(8.4),
lineHeight: pt(13) }],
},
};
const edition = mergeConfigLayers(config, overrides[LANG]); // config itself for en and es
Zutaten
- Funktionen
- Sprachspezifische AbweichungenText von rechts nach linksArabische SchriftenRechts gebundene BücherZiffern der erzeugten ZahlenSilbentrennung und DokumentspracheAbbildung und Tabelle in Ihrer SpracheUmschläge, Titelseiten und ImpressumÜberschriftenformateÜberschriftenattributeBilder in SeitengestaltungenKolumnentitel und SeitenzahlenTextumfluss um Bilder und KästenKästenTabellenformatFormat der BildunterschriftSemantische FarbpalettePDF-Export
- Nutzt außerdem
- Abbildungen genau hierKapitelband über die ganze BreiteGestaltete AuftakteKopfzeilen nach SeitenrolleAbsatzformateIm PDF eingebettete SchriftenAbbildungen und Tabellen als RessourcenZeilenumbrüche in Titeln
- Schrift
- Source Serif 4, Source Sans 3, Amiri, IBM Plex Sans Arabic (SIL OFL 1.1)
- Dateien
mashhad-800.jpgrasulid-cover-1400.jpgregiomontanus-800.jpg- Astrolabe of ʿUmar ibn Yusuf, Yemen, 1291 (the cover; background extended at the top and darkened) (The Metropolitan Museum of Art, CC0 1.0)
- Muhammad Zaman, planispheric astrolabe, Iran, 1654–55 (cropped) (The Metropolitan Museum of Art, CC0 1.0)
- Astrolabe attributed to Regiomontanus, 1462 (cropped) (The Metropolitan Museum of Art, CC0 1.0)
Zubereitung
#1 · Allgemeine Einstellungen und drei kleine Abweichungen
Der Code steht in der kurzen Antwort oben. config enthält die allgemeinen Einstellungen; mergeConfigLayers legt die Abweichung einer Ausgabe Feld für Feld darüber. Englisch und Spanisch haben keine Abweichung: ihr locale wählt die Trennmuster und die Wörter Figure und Table, Figura und Tabla. Die arabische Abweichung nennt nur einzelne Felder: bodyText.fontFamily ändert die Schrift und behält Farbe, Ausrichtung und Silbentrennung, und das Element kicker des Umschlags ändert Schrift und Sperrung und behält Anker, Farbe und Größe. Eine Änderung an Palette oder Rändern in config erreicht alle drei Ausgaben.
#2 · Seiten nach der Schreibrichtung benannt
// An even page's outer edge is its start side in either direction (the left of an English
// verso, the right of an Arabic one), an odd page's its end side.
const head = (id, content, parity, edge, x, extra = {}) => ({ kind: 'text', id, content,
parity, pages: 'body', fontFamily: LABEL, fontSize: pt(7.5), fontWeight: 600,
letterSpacing: pt(1.1), textTransform: 'uppercase', color: col('muted'),
align: edge.endsWith('start') ? 'start' : 'end',
placement: { anchor: { to: 'page', edge }, offset: { x: mm(x), y: mm(12) } }, ...extra });
const folio = { fontWeight: 700, color: col('brass') };
const header = { elements: [
head('verso-folio', '{pageNumber}', 'even', 'top-start', 16, folio),
head('verso-title', '{title}', 'even', 'top-start', 24),
head('recto-title', '{subtitle}', 'odd', 'top-end', -24),
head('recto-folio', '{pageNumber}', 'odd', 'top-end', -16, folio),
] };
Der Außenrand einer geraden Seite ist in beiden Richtungen der Zeilenanfang, der einer ungeraden das Zeilenende; top-start und top-end setzen die Seitenzahlen deshalb auf einer englischen wie auf einer arabischen linken Seite außen. Die Linie des Kastens ist side: 'start', und der Umschlagtext hängt an top-start: keines braucht eine Abweichung.
#3 · Abbildungen am Zeilenende, vom Text umflossen
const photo = (id, file, w, h, extra = {}) => ({ id, typeId: 'figure', kind: 'bitmap',
createdAt: 0, updatedAt: 0, ...extra,
bitmap: { fileId: file, format: 'jpeg', width: w, height: h } });
// 'end' is the right of an English column and the left of an Arabic one.
const wrapped = { position: 'here', wrap: 'end', width: 0.38 };
const note = (n) => `The Met, ${n}, CC0`;
const resources = [
photo('rasulid', 'rasulid-cover-1400.jpg', 1400, 1985),
photo('mashhad', 'mashhad-800.jpg', 800, 1187, { placement: wrapped, note: note('63.166a–j'),
caption: t({ en: 'Planispheric astrolabe, Iran, 1654–55. Brass and steel.',
es: 'Astrolabio planisférico, Irán, 1654-1655. Latón y acero.',
ar: 'أسطرلاب مسطّح، إيران، ١٠٦٥\u00a0هـ / ١٦٥٤–١٦٥٥\u00a0م. نحاس أصفر وفولاذ.' }),
altText: t({ en: 'A brass astrolabe whose rete is a lattice of leaves and scrolls',
es: 'Un astrolabio de latón cuya red es un enrejado de hojas y volutas',
ar: 'أسطرلاب من النحاس الأصفر عنكبوته شبكة من الأوراق والأغصان الملتفّة' }) }),
photo('regiomontanus', 'regiomontanus-800.jpg', 800, 1083,
{ placement: wrapped,
note: note('2025.347a–j'),
caption: t({ en: 'Astrolabe attributed to Regiomontanus, 1462. Copper alloy.',
es: 'Astrolabio atribuido a Regiomontano, 1462. Aleación de cobre.',
ar: 'أسطرلاب يُنسب إلى ريجيومونتانوس، ١٤٦٢\u00a0م. سبيكة نحاسية.' }),
altText: t({ en: 'A round brass astrolabe with Latin star names on its rete',
es: 'Un astrolabio redondo de latón con nombres latinos de estrellas en la red',
ar: 'أسطرلاب مستدير من النحاس عليه أسماء نجوم لاتينية' }) }),
];
wrap: 'end' setzt jedes Foto rechts in eine englische und links in eine arabische Spalte, und der Text läuft daneben. Bildunterschriften und Alternativtexte stehen in der Sprache der Ausgabe; die arabischen nennen zuerst das Hidschra-Datum.
#4 · Die Tabelle in den Wörtern und Ziffern jeder Ausgabe
const row = (...cells) => cells.map((content) => ({ content }));
const head2 = (...cells) => cells.map((content) => ({ content, isHeader: true }));
resources.push({ id: 'three', typeId: 'table', kind: 'table', createdAt: 0, updatedAt: 0,
placement: { position: 'here' },
caption: t({ en: 'The three instruments.', es: 'Los tres instrumentos.',
ar: 'الأدوات الثلاث.' }),
table: { model: { headerRowCount: 1, columnWidths: [1.15, 1.1, 1.2, 1.1], rows: t({
en: [head2('Made', 'Where', 'Maker', 'Size'),
row('1291', 'Yemen', 'ʿUmar ibn Yūsuf', '15.6 cm wide'),
row('1654–55', 'Iran, Mashhad?', 'Muhammad Zaman', '21.6 cm high'),
row('1462', 'Germany or Italy', 'Regiomontanus', '13.5 cm high')],
es: [head2('Fecha', 'Lugar', 'Autor', 'Tamaño'),
row('1291', 'Yemen', 'ʿUmar ibn Yūsuf', '15,6 cm de ancho'),
row('1654-1655', 'Irán, ¿Mashhad?', 'Muhammad Zaman', '21,6 cm de alto'),
row('1462', 'Alemania o Italia', 'Regiomontano', '13,5 cm de alto')],
ar: [head2('التاريخ', 'المكان', 'الصانع', 'المقاس'),
row('٦٩٠\u00a0هـ / ١٢٩١\u00a0م', 'اليمن', 'عمر بن يوسف', 'عرضه ١٥٫٦\u00a0سم'),
row('١٠٦٥\u00a0هـ / ١٦٥٤\u00a0م', 'إيران، مشهد؟', 'محمد زمان', 'ارتفاعه ٢١٫٦\u00a0سم'),
row('١٤٦٢\u00a0م', 'ألمانيا أو إيطاليا', 'ريجيومونتانوس', 'ارتفاعه ١٣٫٥\u00a0سم')] }) } } });
Die Tabellendaten sind Inhalt und wechseln mit der Ausgabe: arabisch-indische Ziffern und das arabische Dezimaltrennzeichen in der arabischen Ausgabe, ein Dezimalkomma in der spanischen. Ihr Stil ist allgemein: die Abweichung setzt nur die arabische Schrift und ihren Grad.
Das ganze Rezept
// ═══ Postext Cookbook · Nº 178 · One design for three language editions ═══════════
// https://postext.dev/en/cookbook/shared-design-per-language
// Code: MIT · Text: original (CC BY 4.0) · Photographs: The Met, Open Access (CC0)
// Fonts: Source Serif 4, Source Sans 3, Amiri, IBM Plex Sans Arabic (OFL) · Needs postext ≥ 1.65.0
import {
buildDocument, withLoadedFonts, renderPageToCanvas, registerResourceImage, mergeConfigLayers,
} from 'https://esm.sh/postext';
import { renderToPdf, decompressWoff2 } from 'https://esm.sh/postext-pdf';
const LANG = 'en'; // @lang: the edition: 'en' | 'es' | 'ar'
const RECIPE = 'shared-design-per-language';
// ─── 1 · Design ─────────────────────────────────────────────────────────────
// #region palette: night ink, the brass of the instruments, a parchment tint
const palette = {
ink: '#1f2230', // text: a blue near-black
brass: '#80561c', // the one accent: kickers, rules, the box stripe (6.2:1 on paper)
tint: '#f4eee2', // the box ground
rule: '#cfc5b2', // hairlines
muted: '#6a655c', // running heads, credits
paper: '#ffffff',
};
const col = (id) => ({ hex: palette[id], model: 'hex', paletteId: id });
const colorPalette = [
...Object.entries(palette).map(([id, hex]) => ({ id, name: id, value: { hex, model: 'hex' } })),
{ id: 'main-color', name: 'brass (defaults)', value: { hex: palette.brass, model: 'hex' } },
];
// #endregion
const [TEXT, LABEL] = ['Source Serif 4', 'Source Sans 3'];
const PAGE = { w: 148, h: 210 }; // mm: an A5 booklet
const LEAD = 14; // pt
// #region cover: the photograph to the trim, the title on its dark head
const at = (edge, x, y, width) => ({ anchor: { to: 'page', edge },
offset: { x: mm(x), y: mm(y) }, ...(width ? { size: { width: mm(width) } } : {}) });
const caps = { fontFamily: LABEL, fontWeight: 700, textTransform: 'uppercase',
letterSpacing: pt(1.6) };
// Every side is named by the direction of the text: 'top-start' is the left of an English
// page and the right of an Arabic one, so the cover needs no override to turn round.
const cover = { id: 'cover',
advancedDesign: { enabled: true, slot: { elements: [
{ kind: 'image', id: 'photo', resourceId: 'rasulid', decorative: true, fit: 'cover',
placement: { anchor: { to: 'page', edge: 'top-left' },
size: { width: mm(PAGE.w), height: mm(PAGE.h) } } },
{ kind: 'text', id: 'kicker', content: '{attr.kicker}', fontSize: pt(8.5), align: 'start',
color: col('paper'), ...caps, placement: at('top-start', 17, 18) },
{ kind: 'rule', id: 'rule', direction: 'horizontal', thickness: pt(1.5), color: col('tint'),
placement: { anchor: { to: '#kicker', edge: 'below' }, offset: { x: mm(0), y: mm(3) },
Das zusammengesetzte script.js läuft unverändert: Fügen Sie es in das Modulskript einer beliebigen Seite ein, oder öffnen Sie das Rezept auf CodePen. Ordner des Rezepts auf GitHub ↗ (öffnet sich in einem neuen Tab)
Varianten
#Die Abweichungen in ein Paket schreiben
Die .postext-Hin-und-Rückreise schreibt ein Buch in zwei Sprachen in eine Datei. Seit postext 1.58 speichert createBundle die allgemeinen Einstellungen einmal und neben den Kapiteln jeder Sprache ihre Abweichung, so wie mergeConfigLayers sie hier liest. Das Sandbox-Buch dieses Rezepts öffnet sich so: Schalten Sie das Design-Panel zwischen Alle Sprachen und der arabischen Ausgabe um, um zu sehen, welche Felder die arabische Ausgabe setzt.
#Die arabische Ausgabe für den Maghreb drucken
ar-MA behält Richtung und Bindung und schreibt die erzeugten Zahlen, Seitenzahlen und Abbildungsnummern, in europäischen Ziffern. Tippen Sie auch die Ziffern der Tabelle neu.
- locale: t({ en: 'en-gb', es: 'es', ar: 'ar' }), // ar: right to left, bound on the right
+ locale: t({ en: 'en-gb', es: 'es', ar: 'ar-MA' }), // ar-MA: European digitsHäufige Fehler
Häufiger Fehler
Arabische Schnitte brauchen ihre arabic-Datei, über den Block arabic
Fontsource liefert Amiri, Noto Naskh Arabic oder Scheherazade New als eine Datei pro Teilmenge aus, und die arabischen Buchstaben stehen in der Datei arabic. loadFonts holt nur latin (und latin-ext), am Bildschirm stammt das Arabische also aus einer Systemschrift und wird falsch gemessen, und fontsourceProvider gibt dem PDF diese latin-Datei, die leere Kästchen druckt. Führen Sie den Kit-Block arabic auf, rufen Sie loadArabicFonts(FONTS, markdown) nach loadFonts auf, mit jeder Schriftstärke, die die Seiten auf Arabisch setzen, in FONTS aufgeführt, und geben Sie renderToPdf fontProvider: arabicPdfProvider. Arabische Schriften →
- Arabische Buchstaben sind verbunden: Eine in den allgemeinen Einstellungen gesperrte Beschriftung muss in der arabischen Abweichung ihr
letterSpacingverlieren, sonst meldet die EnginejoiningScriptLetterSpacing. Dazu gehörttextTransform: 'none': Arabisch hat keine Versalien. - Die Zeilen einer arabischen Ausgabe sind höher, ihre Seiten brechen an anderen Stellen. Jede Ausgabe wurde für sich eingepasst: Der arabische Kasten fließt an den Fuß seiner Seite (
placement="bottom"), und die Schlusssätze laufen neben dem letzten Foto.
Nachweise
- Rezept
- Ignacio Ferro
- Text
- Eigener Text, CC BY 4.0
- Bilder
- Astrolabe of ʿUmar ibn Yusuf, Yemen, 1291 (the cover; background extended at the top and darkened) · The Metropolitan Museum of Art · CC0 1.0
- Muhammad Zaman, planispheric astrolabe, Iran, 1654–55 (cropped) · The Metropolitan Museum of Art · CC0 1.0
- Astrolabe attributed to Regiomontanus, 1462 (cropped) · The Metropolitan Museum of Art · CC0 1.0
- Schriften
- Source Serif 4 (SIL OFL 1.1) · Source Sans 3 (SIL OFL 1.1) · Amiri (SIL OFL 1.1) · IBM Plex Sans Arabic (SIL OFL 1.1)
- Code
- MIT, wie Postext
Diesen Text bearbeiten ↗ (öffnet sich in einem neuen Tab)Ordner des Rezepts auf GitHub ↗ (öffnet sich in einem neuen Tab)


