Files
mdg-filius/packages/editor/test/i18n.test.ts
T
julian.kiedaischandClaude Opus 5.5 2b85e7432c feat: F4.6g3 a computer's firewall window, measured against Java's with traffic from two networks
The firewall-pc scenario and FirewallWindowRun use Java's window as a person
does and send a ping, a TCP connection and a DNS question from the server's
network and from behind the router after each step. The core adds and
removes port rules (personalFirewall.ts) and writes both into the firewall's
log, which now carries each line's time; the room takes the window's
actions and sends its view and the log as lines; the editor shows Java's
window with its two tabs. Not adopted from Java: any integer as a port,
a port after a space refused without a word, a log one can type into.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 21:18:05 +00:00

404 lines
21 KiB
TypeScript

// The catalogs: the same keys in both languages, placeholders filled, and —
// where Java FILIUS names a thing — Java's word in its bundle (M11).
import { existsSync, readFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { describe, expect, it } from 'vitest';
import { CATALOGS, translate } from '../src/i18n/i18n.js';
const bundles = join(dirname(fileURLToPath(import.meta.url)), '../../../tools/java-oracle/.upstream/src/main/resources/filius/messages');
/** A Java .properties file: ISO-8859-1, \uXXXX escapes, `key = value`, a backslash at a line's end going on in the next without its indent. */
function properties(file: string): Map<string, string> {
const text = new TextDecoder('latin1').decode(readFileSync(join(bundles, file))).replace(/\\\r?\n[ \t]*/g, '');
const entries = new Map<string, string>();
for (const line of text.split(/\r?\n/)) {
const match = /^([\w.]+)\s*=\s*(.*)$/.exec(line);
if (match !== null) entries.set(match[1]!, match[2]!.replace(/\\u([0-9a-fA-F]{4})/g, (_, hex: string) => String.fromCharCode(Number.parseInt(hex, 16))).trim());
}
return entries;
}
/** The editor's keys whose words are Java's, and the key of each in Java's bundle. */
const FROM_JAVA: Readonly<Record<string, string>> = {
'device.pc': 'hw_rechner_msg1',
'explorer.newFolder': 'fileexplorer_msg3',
'explorer.delete': 'fileexplorer_msg4',
'explorer.cut': 'fileexplorer_msg5',
'explorer.copy': 'fileexplorer_msg6',
'explorer.paste': 'fileexplorer_msg7',
'explorer.rename': 'fileexplorer_msg8',
'explorer.newName': 'fileexplorer_msg9',
'explorer.file': 'fileexplorer_msg10',
'explorer.folder': 'fileexplorer_msg11',
'explorer.import': 'fileexplorer_msg2',
'explorer.importTitle': 'fileexplorer_msg12',
'explorer.localFile': 'fileexplorer_msg13',
'explorer.chooseFile': 'fileexplorer_msg14',
'explorer.importButton': 'fileexplorer_msg15',
'explorer.importMissing': 'fileexplorer_msg16',
'explorer.sure': 'fileexplorer_msg18',
'explorer.invalidTarget': 'fileexplorer_msg19',
'explorer.close': 'sw_fileimporter_msg3',
'explorer.imported': 'sw_fileexplorer_msg2',
'explorer.notImported': 'sw_fileexplorer_msg1',
'explorer.tooLarge': 'sw_fileexplorer_msg3',
'viewer.open': 'imageviewer_msg2',
'echoServer.port': 'serverbaustein_msg1',
'echoServer.stop': 'serverbaustein_msg2',
'echoServer.start': 'serverbaustein_msg3',
'simpleClient.address': 'clientbaustein_msg1',
'simpleClient.port': 'clientbaustein_msg2',
'simpleClient.connect': 'clientbaustein_msg3',
'simpleClient.message': 'clientbaustein_msg4',
'simpleClient.send': 'clientbaustein_msg5',
'simpleClient.disconnect': 'clientbaustein_msg6',
'simpleClient.connected': 'sw_clientbaustein_msg2',
'simpleClient.disconnected': 'sw_clientbaustein_msg3',
'simpleClient.notConnected': 'sw_clientbaustein_msg4',
'simpleClient.receiveFailed': 'sw_tcpsocket_msg9',
'simpleClient.failure.unresolved': 'sw_socket_msg1',
'simpleClient.failure.timedOut': 'sw_tcpsocket_msg2',
'simpleClient.failure.failed': 'sw_tcpsocket_msg3',
'dnsServer.start': 'dnsserver_msg1',
'dnsServer.stop': 'dnsserver_msg14',
'dnsServer.tabA': 'dnsserver_msg2',
'dnsServer.tabMX': 'dnsserver_msg3',
'dnsServer.tabNS': 'dnsserver_msg15',
'dnsServer.aName': 'dnsserver_msg4',
'dnsServer.aAddress': 'dnsserver_msg5',
'dnsServer.add': 'dnsserver_msg6',
'dnsServer.remove': 'dnsserver_msg7',
'dnsServer.aNameColumn': 'dnsserver_msg8',
'dnsServer.aAddressColumn': 'dnsserver_msg9',
'dnsServer.mxDomain': 'dnsserver_msg10',
'dnsServer.mxServer': 'dnsserver_msg11',
'dnsServer.mxDomainColumn': 'dnsserver_msg12',
'dnsServer.mxServerColumn': 'dnsserver_msg13',
'dnsServer.nsDomain': 'dnsserver_msg16',
'dnsServer.nsServer': 'dnsserver_msg17',
'dnsServer.nsDomainColumn': 'dnsserver_msg18',
'dnsServer.nsServerColumn': 'dnsserver_msg19',
'dnsServer.recursive': 'dnsserver_msg20',
'textEditor.new': 'texteditor_msg3',
'textEditor.open': 'texteditor_msg4',
'textEditor.save': 'texteditor_msg5',
'textEditor.saveAs': 'texteditor_msg6',
'fileDialog.up': 'dmtnfilechooser_msg1',
'fileDialog.name': 'dmtnfilechooser_msg2',
'fileDialog.cancel': 'dmtnfilechooser_msg4',
'fileDialog.saveTitle': 'dmtnfilechooser_msg5',
'fileDialog.save': 'dmtnfilechooser_msg6',
'fileDialog.openTitle': 'dmtnfilechooser_msg7',
'fileDialog.open': 'dmtnfilechooser_msg8',
'fileDialog.folder': 'dmtnfilechooser_msg11',
'device.notebook': 'hw_notebook_msg1',
'device.networkSwitch': 'hw_switch_msg1',
'device.router': 'hw_vermittlungsrechner_msg1',
'device.gateway': 'hw_gateway_msg1',
'device.cable': 'hw_kabel_msg1',
'menu.removeCable': 'guievents_msg5',
'menu.removeAllCables': 'guievents_msg6',
'menu.delete': 'guievents_msg7',
'cable.noFreePort': 'guievents_msg1',
'cable.alreadyConnected': 'guievents_msg12',
'cable.wanOccupied': 'guievents_msg22',
'cable.lanOccupied': 'guievents_msg23',
'cable.wan': 'guievents_msg24',
'cable.lan': 'guievents_msg25',
'cable.wirelessNotPossible': 'guievents_msg26',
'router.title': 'guicontainer_msg2',
'sheet.name': 'jhostkonfiguration_msg1',
'sheet.ipAddress': 'jhostkonfiguration_msg3',
'sheet.subnetMask': 'jhostkonfiguration_msg4',
'sheet.defaultGateway': 'jhostkonfiguration_msg5',
'sheet.dnsServer': 'jhostkonfiguration_msg6',
'sheet.dhcpClient': 'jhostkonfiguration_msg7',
'sheet.macAddress': 'jhostkonfiguration_msg9',
'sheet.ipAsName': 'jhostkonfiguration_msg10',
'sheet.ipForwarding': 'jhostkonfiguration_msg11',
'sheet.macAsName': 'jhostkonfiguration_msg12',
'sheet.wireless': 'jhostkonfiguration_msg13',
'sheet.cable': 'jhostkonfiguration_msg14',
'sheet.chooseNetwork': 'jhostkonfiguration_msg16',
'sheet.cloud': 'jswitchkonfiguration_msg3',
'sheet.switchSsid': 'jswitchkonfiguration_msg4',
'sheet.retention': 'jswitchkonfiguration_msg5',
'sheet.port': 'jmodemkonfiguration_msg4',
'sheet.general': 'jvermittlungsrechnerkonfiguration_msg17',
'sheet.notConnected': 'jvermittlungsrechnerkonfiguration_msg16',
'sheet.rip': 'jvermittlungsrechnerkonfiguration_msg26',
'sheet.forwardingTable': 'jvermittlungsrechnerkonfiguration_msg15',
'sheet.showAllEntries': 'jvermittlungsrechnerkonfiguration_msg11',
'sheet.newEntry': 'jvermittlungsrechnerkonfiguration_msg12',
'route.destinationNetwork': 'jweiterleitungstabelle_msg3',
'route.subnetMask': 'jweiterleitungstabelle_msg4',
'route.gateway': 'jweiterleitungstabelle_msg5',
'route.interfaceIPAddress': 'jweiterleitungstabelle_msg6',
'sheet.dhcpServer': 'jhostkonfiguration_msg8',
'sheet.portForwarding': 'jgatewayconfiguration_msg19',
'dhcp.lowerBound': 'jdhcpkonfiguration_msg1',
'dhcp.upperBound': 'jdhcpkonfiguration_msg2',
'dhcp.subnetMask': 'jdhcpkonfiguration_msg3',
'dhcp.gateway': 'jdhcpkonfiguration_msg4',
'dhcp.dnsServer': 'jdhcpkonfiguration_msg5',
'dhcp.active': 'jdhcpkonfiguration_msg6',
'dhcp.ownSettings': 'jdhcpkonfiguration_msg8',
'dhcp.basic': 'jdhcpkonfiguration_msg10',
'dhcp.static': 'jdhcpkonfiguration_msg11',
'dhcp.macAddress': 'jdhcpkonfiguration_msg12',
'dhcp.ipAddress': 'jdhcpkonfiguration_msg13',
'dhcp.add': 'jdhcpkonfiguration_msg14',
'dhcp.remove': 'jdhcpkonfiguration_msg15',
'forwarding.protocolValue': 'jportforwarding_msg4',
'forwarding.publicPortValue': 'jportforwarding_msg5',
'forwarding.lanIPAddress': 'jportforwarding_msg6',
'forwarding.lanPortValue': 'jportforwarding_msg7',
'sheet.firewall': 'jvermittlungsrechnerkonfiguration_msg4',
'firewall.rules': 'jfirewalldialog_msg21',
'firewall.up': 'jfirewalldialog_msg22',
'firewall.down': 'jfirewalldialog_msg23',
'firewall.add': 'jfirewalldialog_msg24',
'firewall.delete': 'jfirewalldialog_msg25',
'firewall.number': 'jfirewalldialog_msg26',
'firewall.sourceIPAddress': 'jfirewalldialog_msg27',
'firewall.sourceSubnetMask': 'jfirewalldialog_msg28',
'firewall.destinationIPAddress': 'jfirewalldialog_msg29',
'firewall.destinationSubnetMask': 'jfirewalldialog_msg30',
'firewall.protocol': 'jfirewalldialog_msg31',
'firewall.accept': 'jfirewalldialog_msg33',
'firewall.drop': 'jfirewalldialog_msg34',
'firewall.port': 'jfirewalldialog_msg35',
'firewall.active': 'jfirewalldialog_msg38',
'firewall.activeHint': 'jfirewalldialog_msg39',
'firewall.dropIcmp': 'jfirewalldialog_msg40',
'firewall.dropIcmpHint': 'jfirewalldialog_msg41',
'firewall.filterSyn': 'jfirewalldialog_msg42',
'personalFirewall.hint': 'firewall_msg2',
'personalFirewall.port': 'firewall_msg3',
'personalFirewall.allSenders': 'firewall_msg4',
'personalFirewall.sameNetwork': 'firewall_msg5',
'personalFirewall.add': 'firewall_msg6',
'personalFirewall.delete': 'firewall_msg7',
'personalFirewall.portColumn': 'firewall_msg8',
'personalFirewall.sender': 'firewall_msg9',
'personalFirewall.setup': 'firewall_msg10',
'personalFirewall.log': 'firewall_msg11',
'personalFirewall.localNetwork': 'firewall_msg12',
'personalFirewall.allowIcmp': 'firewall_msg13',
'personalFirewall.filterUdp': 'firewall_msg14',
'personalFirewall.portRuleRemoved': 'sw_firewall_msg7',
'sheet.connections': 'jvermittlungsrechnerkonfiguration_msg23',
'connections.local': 'jvermittlungsrechnerkonfiguration_msg22',
'connections.add': 'jvermittlungsrechnerkonfiguration_msg24',
'connections.most': 'jvermittlungsrechnerkonfiguration_msg25',
'mode.speed': 'guimainmemu_msg16',
'installation.installed': 'installationsdialog_msg3',
'installation.available': 'installationsdialog_msg4',
'installation.apply': 'installationsdialog_msg2',
'mode.currentSpeed': 'guimainmemu_msg15',
'menu.showDesktop': 'guievents_msg3',
'desktop.home': 'desktoppanel_application',
'desktop.installation': 'desktoppanel_msg1',
'network.ipAddress': 'network_msg1',
'network.subnetMask': 'network_msg4',
'network.dnsServer': 'network_msg2',
'network.gateway': 'network_msg3',
'network.macAddress': 'network_msg9',
'browser.go': 'webbrowser_msg2',
'server.start': 'webserver_msg1',
'server.stop': 'webserver_msg2',
'webServer.virtualHosts': 'webserver_msg3',
'webServer.hostName': 'webserver_msg4',
'webServer.folder': 'webserver_msg5',
'webServer.useVirtualHosts': 'webserver_msg6',
'server.acceptingStopped': 'sw_serveranwendung_msg1',
'server.acceptingStarted': 'sw_serveranwendung_msg2',
'server.serverSocketClosed': 'sw_serveranwendung_msg3',
'server.serverSocketFailed': 'sw_serveranwendung_msg6',
'server.sendFailed.notConnected': 'sw_tcpsocket_msg6',
'server.sendFailed.connectionLost': 'sw_tcpsocket_msg7',
'server.sendFailed.timedOut': 'sw_tcpsocket_msg8', 'emailClient.sender': 'emailanwendung_msg1',
'emailClient.subject': 'emailanwendung_msg2',
'emailClient.recipient': 'emailanwendung_msg3',
'emailClient.inbox': 'emailanwendung_msg5',
'emailClient.sent': 'emailanwendung_msg6',
'emailClient.fetch': 'emailanwendung_msg7',
'emailClient.compose': 'emailanwendung_msg8',
'emailClient.answer': 'emailanwendung_msg9',
'emailClient.deleteHint': 'emailanwendung_msg10',
'emailClient.accountHint': 'emailanwendung_msg11',
'emailClient.composeTitle': 'emailanwendung_msg12',
'emailClient.send': 'emailanwendung_msg13',
'emailClient.toLabel': 'emailanwendung_msg14',
'emailClient.ccLabel': 'emailanwendung_msg15',
'emailClient.bccLabel': 'emailanwendung_msg16',
'emailClient.subjectLabel': 'emailanwendung_msg17',
'emailClient.re': 'emailanwendung_msg18',
'emailClient.wrote': 'emailanwendung_msg19',
'emailClient.invalidAddress': 'emailanwendung_msg20',
'emailClient.noRecipient': 'emailanwendung_msg21',
'emailClient.accountTitle': 'emailanwendung_msg25',
'emailClient.email': 'emailanwendung_msg26',
'emailClient.pop3Host': 'emailanwendung_msg27',
'emailClient.pop3Port': 'emailanwendung_msg28',
'emailClient.smtpHost': 'emailanwendung_msg29',
'emailClient.smtpPort': 'emailanwendung_msg30',
'emailClient.username': 'emailanwendung_msg31',
'emailClient.password': 'emailanwendung_msg32',
'emailClient.save': 'emailanwendung_msg33',
'emailClient.cancel': 'emailanwendung_msg37',
'emailClient.delete': 'emailanwendung_msg43',
'emailClient.account': 'emailanwendung_msg44',
'emailClient.name': 'emailanwendung_msg45',
'emailClient.accountInvalid': 'emailanwendung_msg46',
'emailClient.noAccount': 'emailanwendung_msg47',
'emailServer.start': 'emailserver_msg1',
'emailServer.started': 'emailserver_msg2',
'emailServer.stopped': 'emailserver_msg3',
'emailServer.domain': 'emailserver_msg4',
'emailServer.deleteAccount': 'emailserver_msg8',
'emailServer.yes': 'emailserver_msg9',
'emailServer.cancel': 'emailserver_msg10',
'emailServer.confirmTitle': 'emailserver_msg13',
'emailServer.address': 'emailserver_msg14',
'emailServer.mails': 'emailserver_msg15',
'emailServer.username': 'emailserver_msg16',
'emailServer.name': 'emailserver_msg17',
'emailServer.password': 'emailserver_msg18',
'emailServer.create': 'emailserver_msg19',
'emailServer.lastNamePlaceholder': 'emailserver_msg20',
'emailServer.firstNamePlaceholder': 'emailserver_msg21',
'emailServer.whiteSpace': 'emailserver_msg24',
'emailServer.missing': 'emailserver_msg25',
'emailServer.newAccount': 'emailserver_msg26',
'emailServer.accounts': 'emailserver_msg27',
'emailServer.log': 'emailserver_msg28',
'emailServer.stop': 'emailserver_msg29',
'mail.failure.unresolved': 'sw_socket_msg1',
'mail.failure.timedOut': 'sw_tcpsocket_msg2',
'mail.failure.failed': 'sw_tcpsocket_msg3',
'mail.failure.receiveFailed': 'sw_tcpsocket_msg6',
};
/** Java's words, mended: the prompt says "Vermittlungsrechnerns" (guicontainer_msg1). */
const FROM_JAVA_MENDED: Readonly<Record<string, readonly [string, (java: string) => string]>> = {
'router.prompt': ['guicontainer_msg1', (java) => java.replace(' \\n ', ' ').replace('Vermittlungsrechnerns', 'Vermittlungsrechners')],
// A label above its list, without the colon of Java's label beside it.
'sheet.network': ['jhostkonfiguration_msg15', (java) => java.replace(/:$/, '')],
// Java's message dialog breaks its lines with \n.
'dnsServer.invalidA': ['dnsserver_msg21', (java) => java.replaceAll('\\n', '\n')],
'dnsServer.invalidNS': ['dnsserver_msg22', (java) => java.replaceAll('\\n', '\n')],
'dnsServer.invalidMX': ['dnsserver_msg23', (java) => java.replaceAll('\\n', '\n')],
// Java writes "Eintrag Löschen".
'sheet.deleteEntry': ['jvermittlungsrechnerkonfiguration_msg13', (java) => java.replace('Löschen', 'löschen')],
// Java's word, then what it joins or which card.
'sheet.connectedTo': ['jvermittlungsrechnerkonfiguration_msg6', (java) => `${java} {device}`],
'sheet.nic': ['jvermittlungsrechnerkonfiguration_msg10', (java) => `${java} {number}`],
// A column's title without Java's question mark ("Aktion?").
'firewall.action': ['jfirewalldialog_msg32', (java) => java.replace(/\?$/, '')],
// A label above its list, without the colon.
'firewall.defaultPolicy': ['jfirewalldialog_msg36', (java) => java.replace(/:$/, '')],
// Java's "\n" as line breaks; the last sentence of the German starts with a small letter there.
'firewall.intro': ['jfirewalldialog_msg37', (java) => java.replaceAll('\\n', '\n').replace('\nüber', '\nÜber')],
'firewall.filterSynHint': ['jfirewalldialog_msg43', (java) => java.replaceAll('\\n', '\n')],
// Java's welcome without its bold tags (the panel writes it as text, Java's "!" after it).
'emailClient.welcome': ['emailanwendung_msg4', (java) => java.replace(/<\/?b>/g, '')],
// Without Java's key (Strg+D, Strg+R): in a browser they bookmark and reload the page.
'mode.design': ['guimainmemu_msg3', (java) => java.replace(/ \((Strg|Ctrl)\+D\)$/, '')],
'mode.simulation': ['guimainmemu_msg4', (java) => java.replace(/ \((Strg|Ctrl)\+R\)$/, '')],
// Java's lines without their leading space and the empty one; a step starts with a capital letter.
'connections.hint': [
'jvermittlungsrechnerkonfiguration_msg19',
(java) =>
java
.split('\\n')
.map((line) => line.trim().replace(/^(\d\. )(\p{Ll})/u, (_, step: string, letter: string) => step + letter.toUpperCase()))
.filter((line) => line !== '')
.join('\n'),
],
};
/** Java's words around a value, as its programs join them („Verbindung zu“ + address + „hergestellt“). */
const FROM_JAVA_JOINED: Readonly<Record<string, readonly [string, string, string]>> = {
'server.connectionAccepted': ['sw_serveranwendung_msg4', '{address}', 'sw_serveranwendung_msg5'],
'simpleClient.connectionClosed': ['sw_clientbaustein_msg5', '{address}', 'sw_clientbaustein_msg6'],
'server.connectionClosed': ['sw_servermitarbeiter_msg1', '{address}', 'sw_servermitarbeiter_msg2'],
'emailServer.created': ['emailserver_msg22', '{name}', 'emailserver_msg23'],
'mail.stored': ['sw_smtpmitarbeiter_msg12', '{name}', 'sw_smtpmitarbeiter_msg13'],
'mail.connected': ['sw_smtpclient_msg1', '{address}', 'sw_smtpclient_msg2'],
'mail.disconnected': ['sw_smtpclient_msg1', '{address}', 'sw_smtpclient_msg3'],
};
/** Java's words before a value, joined as its programs join them (SMTPMitarbeiter, SMTPClient, the server window's question). */
const FROM_JAVA_BEFORE: Readonly<Record<string, readonly [string, string, string?]>> = {
'personalFirewall.ruleMatched': ['sw_firewall_msg8', ' #{number} ({rule}) -> {action}'],
'personalFirewall.icmpDropped': ['firewallthread_msg1', ' {sender} -> {receiver} (code: {code}, type: {type})'],
'personalFirewall.portRuleAdded': ['sw_firewall_msg5', ' {port}/{protocol}'],
'mail.sendFailed': ['sw_smtpmitarbeiter_msg14', ' {address}'],
'mail.mailSent': ['sw_smtpclient_msg4', ' {recipients}'],
// "'" + name + "'", the question's two halves written against it.
'emailServer.confirmDelete': ['emailserver_msg11', '{name}', 'emailserver_msg12'],
};
/** Java's page for a server it cannot reach (sw_webbrowser_msg1): its title and its heading. */
const FROM_JAVA_PAGE: Readonly<Record<string, readonly [string, RegExp]>> = {
'browser.unreachableTitle': ['sw_webbrowser_msg1', /<title>(.*)<\/title>/],
'browser.unreachable': ['sw_webbrowser_msg1', /<h1>(.*)<\/h1>/],
};
const templates = join(dirname(fileURLToPath(import.meta.url)), '../../../tools/java-oracle/.upstream/src/main/resources/tmpl');
describe('the catalogs', () => {
it('have the same keys in German and English, each with a text', () => {
expect(Object.keys(CATALOGS.en).sort()).toEqual(Object.keys(CATALOGS.de).sort());
for (const catalog of Object.values(CATALOGS)) for (const text of Object.values(catalog)) expect(text.trim()).not.toBe('');
});
it('fill placeholders, and show a key they lack', () => {
expect(translate('de', 'zoom.level', { percent: 75 })).toBe('75 %');
expect(translate('en', 'zoom.level')).toBe('{percent} %');
expect(translate('de', 'no.such.key')).toBe('no.such.key');
});
it('name every kind of device', () => {
for (const kind of ['pc', 'notebook', 'networkSwitch', 'router', 'gateway', 'remoteLink', 'unsupported']) expect(CATALOGS.de[`device.${kind}`]).toBeDefined();
// Java's Modem.TYPE, the same in every language.
expect([CATALOGS.de['device.remoteLink'], CATALOGS.en['device.remoteLink']]).toEqual(['Modem', 'Modem']);
});
it.skipIf(!existsSync(bundles))("use Java's words where Java has them (M11)", () => {
const [de, en] = [properties('MessagesBundle_de_DE.properties'), properties('MessagesBundle_en_GB.properties')];
for (const [key, javaKey] of Object.entries(FROM_JAVA)) {
expect(CATALOGS.de[key], key).toBe(de.get(javaKey));
expect(CATALOGS.en[key], key).toBe(en.get(javaKey));
}
for (const [key, [javaKey, mend]] of Object.entries(FROM_JAVA_MENDED)) {
expect(CATALOGS.de[key], key).toBe(mend(de.get(javaKey)!));
expect(CATALOGS.en[key], key).toBe(mend(en.get(javaKey)!));
}
for (const [key, [before, value, after]] of Object.entries(FROM_JAVA_JOINED)) {
expect(CATALOGS.de[key], key).toBe(`${de.get(before)} ${value} ${de.get(after)}`);
expect(CATALOGS.en[key], key).toBe(`${en.get(before)} ${value} ${en.get(after)}`);
}
for (const [key, [before, value, after]] of Object.entries(FROM_JAVA_BEFORE)) {
expect(CATALOGS.de[key], key).toBe(`${de.get(before)}${value}${after === undefined ? '' : de.get(after)}`);
expect(CATALOGS.en[key], key).toBe(`${en.get(before)}${value}${after === undefined ? '' : en.get(after)}`);
}
for (const [key, [javaKey, part]] of Object.entries(FROM_JAVA_PAGE)) {
expect(CATALOGS.de[key], key).toBe(part.exec(de.get(javaKey)!)![1]);
expect(CATALOGS.en[key], key).toBe(part.exec(en.get(javaKey)!)![1]);
}
});
it.skipIf(!existsSync(templates))("write the browser's error page as Java's template does (tmpl/http_fehler)", () => {
for (const [language, file] of [['de', 'http_fehler_de_DE.txt'], ['en', 'http_fehler_en_GB.txt']] as const) {
const template = readFileSync(join(templates, file), 'utf8').replaceAll(':meldung:', '{message}').replaceAll(':code:', '{code}');
expect(CATALOGS[language]['browser.errorTitle']).toBe(/<title>(.*)<\/title>/.exec(template)![1]);
expect(CATALOGS[language]['browser.errorText']).toBe(/<p>(.*)<\/p>/.exec(template)![1]!.trim());
expect(/<h1>(.*)<\/h1>/.exec(template)![1]).toBe('{message}');
}
});
});