summaryrefslogtreecommitdiff
path: root/app/akk-keilschrift-lib.js
diff options
context:
space:
mode:
authorMatthias Andreas Benkard <code@mail.matthias.benkard.de>2026-08-09 21:50:19 +0200
committerMatthias Andreas Benkard <code@mail.matthias.benkard.de>2026-08-22 19:18:04 +0200
commit4b635085e9f10ccdded0e90e850be9274724683e (patch)
tree67c8671e2138c67b26b464887bf166c01d299af2 /app/akk-keilschrift-lib.js
parent26c96e92cda9441ed3fe5e7b1b5849290bfef14c (diff)
Noch mehr Sprachen
Diffstat (limited to 'app/akk-keilschrift-lib.js')
-rw-r--r--app/akk-keilschrift-lib.js123
1 files changed, 123 insertions, 0 deletions
diff --git a/app/akk-keilschrift-lib.js b/app/akk-keilschrift-lib.js
new file mode 100644
index 0000000..a5871b3
--- /dev/null
+++ b/app/akk-keilschrift-lib.js
@@ -0,0 +1,123 @@
+/* Umschrift → Keilschrift für das altbabylonische Wörterbuch.
+ Quelle: normalisiertes Akkadisch in Lateinschrift. Die Silbentrennung
+ geschieht mechanisch (V / KV / VK, keine KVK-Zeichen), Logogramme
+ stehen in geschweiften Klammern als Unicode-Zeichennamen.
+ Jedes unbekannte Zeichen bricht den Lauf ab — stille Fehler wären
+ hier die schlimmste Sorte, weil niemand sie lesen könnte. */
+const SIGN = {
+ a:'A', e:'E', i:'I', u:'U',
+ ba:'BA', bi:'BI', bu:'BU', be:'BAD',
+ pa:'PA', pi:'BI', pu:'BU', pe:'BAD',
+ da:'DA', di:'DI', du:'DU', de:'DI',
+ ta:'TA', ti:'TI', tu:'TU', te:'TE',
+ 'ṭa':'DA', 'ṭi':'DI', 'ṭu':'DU', 'ṭe':'DI',
+ ga:'GA', gi:'GI', gu:'GU', ge:'GI',
+ ka:'KA', ki:'KI', ku:'KU', ke:'KI',
+ qa:'GA', qi:'KI', qu:'KU', qe:'KI',
+ za:'ZA', zi:'ZI', zu:'ZU', ze:'ZE2',
+ sa:'SA', si:'SI', su:'SU', se:'SI',
+ 'ṣa':'ZA', 'ṣi':'ZI', 'ṣu':'ZU', 'ṣe':'ZE2',
+ 'ša':'SHA', 'ši':'IGI', 'šu':'SHU', 'še':'SHE',
+ ha:'HA', hi:'HI', hu:'HU', he:'HI',
+ la:'LA', li:'LI', lu:'LU', le:'LI',
+ ma:'MA', mi:'MI', mu:'MU', me:'ME',
+ na:'NA', ni:'NI', nu:'NU', ne:'NE',
+ ra:'RA', ri:'RI', ru:'RU', re:'RI',
+ wa:'PI', wi:'PI', wu:'PI', we:'PI',
+ ab:'AB', ap:'AB', ib:'IB', ip:'IB', ub:'UB', up:'UB', eb:'IB', ep:'IB',
+ ad:'AD', at:'AD', 'aṭ':'AD', id:'A2', it:'A2', 'iṭ':'A2',
+ ud:'UD', ut:'UD', 'uṭ':'UD', ed:'A2', et:'A2', 'eṭ':'A2',
+ ag:'AK', ak:'AK', aq:'AK', ig:'IG', ik:'IG', iq:'IG', eg:'IG', ek:'IG', eq:'IG',
+ al:'AL', il:'IL', el:'EL',
+ am:'GUD TIMES KUR', im:'IM', um:'UM', em:'IM',
+ an:'AN', in:'IN', un:'UN', en:'EN',
+ ir:'IR', ur:'UR', er:'IR',
+ 'aš':'ASH', 'iš':'ISH', 'uš':'USH', 'eš':'ESH2',
+ as:'UD KUSHU2', az:'UD KUSHU2', 'aṣ':'UD KUSHU2'
+};
+/* KVK-Zeichen. Das Altbabylonische schreibt geschlossene Silben gern mit
+ einem einzigen Zeichen statt mit KV+VK; wo es eines gibt, wird es
+ genommen. Das ist nicht nur kürzer, sondern schliesst auch Lücken der
+ VK-Reihe: /šar/ hat ein Zeichen (ŠÁR), /ar/ allein nicht. */
+const CVC = {
+ tum:'TUM', lum:'LUM', kur:'KUR', nam:'NAM', tar:'TAR', lal:'LAL', sum:'SUM',
+ gar:'GAR', gal:'GAL', til:'TIL', din:'DIN', tab:'TAB', zag:'ZAG', sag:'SAG',
+ dam:'DAM', dub:'DUB', dug:'DUG', bar:'BAR', bal:'BAL', bur:'BUR', dag:'DAG',
+ dar:'DAR', gam:'GAM', gan:'GAN', gur:'GUR', hal:'HAL', kak:'KAK', kal:'KAL',
+ kin:'KIN', kul:'KUL', kun:'KUN', lam:'LAM', lul:'LUL', mar:'MAR', mes:'MES',
+ nim:'NIM', nun:'NUN', pad:'PAD', pan:'PAN', sar:'SAR', sig:'SIG', sud:'SUD',
+ sur:'SUR', tag:'TAG', tir:'TIR', tuk:'TUK', mah:'MAH', gig:'GIG', gul:'GUL',
+ gum:'GUM', mug:'MUG', rab:'RAB', pap:'PAP', zig:'ZIG', zum:'ZUM', bad:'BAD',
+ kam:'KAM2', tur:'TUR', kid:'KID', lil:'LIL', nin:'NIN', zib:'ZIB',
+ 'šar':'SHAR2', 'šeš':'SHESH', 'šid':'SHID', 'šim':'SHIM', 'šir':'SHIR',
+ 'maš':'MASH', 'muš':'MUSH', 'kiš':'KISH',
+ /* stimmlos/emphatisch teilen sich das Zeichen, wie ueblich */
+ dum:'TUM', qar:'GAR', kam2:'KAM2', 'ṭab':'TAB', 'ṭar':'TAR', qal:'GAL',
+ bat:'BAD', pat:'BAD', 'ṭup':'DUB', dur:'BUR', sik:'SIK2'
+};
+const V = 'aeiu', LONG = {'ā':'a','ē':'e','ī':'i','ū':'u','â':'a','ê':'e','î':'i','û':'u'};
+
+let NAMES = null;
+function names(){
+ if (NAMES) return NAMES;
+ NAMES = new Map();
+ /* Node kennt keine Unicode-Namenstabelle; sie wird deshalb einmalig
+ aus einer JSON-Datei gelesen, die ein Python-Vorlauf erzeugt hat. */
+ const tab = require('./akk-zeichennamen.json');
+ for (const [n, cp] of Object.entries(tab)) NAMES.set(n, String.fromCodePoint(cp));
+ return NAMES;
+}
+function glyph(signName){
+ const g = names().get('CUNEIFORM SIGN ' + signName);
+ if (!g) { throw new Error('unbekanntes Keilschriftzeichen: ' + signName); }
+ return g;
+}
+/* Silbentrennung: Konsonant zwischen Vokalen zum folgenden Vokal,
+ zwei Konsonanten werden getrennt. Danach KVK → KV + VK. */
+function syllables(w){
+ const s = [...w].map(c => LONG[c] || c).join('');
+ const isV = c => V.includes(c);
+ const out = []; let i = 0;
+ while (i < s.length){
+ let on = '';
+ if (!isV(s[i])) { on = s[i]; i++; }
+ if (i >= s.length) { if (on) throw new Error('Konsonant ohne Vokal: ' + w); break; }
+ const vow = s[i]; i++;
+ let coda = '';
+ if (i < s.length && !isV(s[i]) && (i+1 >= s.length || !isV(s[i+1]))) { coda = s[i]; i++; }
+ out.push(on + vow + coda);
+ }
+ return out;
+}
+function word(w){
+ let g = '';
+ for (const syl of syllables(w)){
+ if (CVC[syl]) { g += glyph(CVC[syl]); continue; }
+ const m = /^(.*?)([aeiu])(.*)$/.exec(syl);
+ const on = m[1], vow = m[2], coda = m[3];
+ /* Ohne Anlaut und mit Auslaut ist die Silbe selbst ein VK-Zeichen:
+ um-ma-nu-um, nicht u-um-ma-nu-um. Ein eigenes V-Zeichen davor
+ waere schlicht falsch. */
+ if (!on && coda){ const k = vow + coda; if (!SIGN[k]) throw new Error('keine Silbe ' + k + ' (in ' + w + ')'); g += glyph(SIGN[k]); continue; }
+ if (on){ const k = on + vow; if (!SIGN[k]) throw new Error('keine Silbe ' + k + ' (in ' + w + ')'); g += glyph(SIGN[k]); }
+ else { g += glyph(SIGN[vow]); }
+ if (coda){ const k = vow + coda; if (!SIGN[k]) throw new Error('keine Silbe ' + k + ' (in ' + w + ')'); g += glyph(SIGN[k]); }
+ }
+ return g;
+}
+/* HTML-Auszeichnung, Entities, Zahlen, Paragraphenketten und lateinische
+ Kuerzel bleiben unangetastet. Umgesetzt wird nur, was ganz aus kleinen
+ akkadischen Buchstaben besteht und einen Vokal enthaelt: damit fallen
+ BetrAVG, EStG, ETF, IRR und die Formelvariablen von selbst heraus.
+ [[...]] ist die Notbremse fuer alles Uebrige (file://, localStorage). */
+const AKK = /^[a-zāēīūâêîûšṣṭ]+$/;
+function convert(text){
+ return text.split(/(<[^>]*>|\{[^}]*\}|\[\[[^\]]*\]\]|&[a-zA-Z]+;|&#\d+;)/).map(part => {
+ if (part.startsWith('<') || part.startsWith('&')) return part;
+ if (part.startsWith('{')) return part.slice(1,-1).split('.').map(glyph).join('');
+ if (part.startsWith('[[')) return part.slice(2,-2);
+ return part.replace(/(?<![0-9])[A-Za-zāēīūâêîûšṣṭ]+/g, m =>
+ AKK.test(m) && /[aeiuāēīū]/.test(m) ? word(m) : m);
+ }).join('');
+}
+module.exports = { convert, word, glyph, syllables };