// Define English translations without enforcing TTranslationImplementation yet export const EN_translations = { address: "Address", "bank.accountHolder": "beneficiary", "bank.bic": "bic", "bank.iban": "iban", "bank.institution": "institution", "bankConnection@@title": "Bank Connection", "contact@@title": "Contact Info", "customer.number": "Your Customer ID", description: "Description", "empty.logo": "no logo provided", "empty.number.customer": "not registered", empty: "not provided", fax: "Fax", introStatement: "We hereby invoice the following products and services", "invoice.number": "Invoice number", invoice: "Invoice", "item.position": "Pos.", mail: "Mail", "overlay@@draft": "Draft", "page.continueNext": "Continues on page", "page.final": "This is the final page of this document.", page: "Page", pageOf: "of", "payment.qr": "Pay via QR code", "payment.qr.description": "Scan the QR code with you banking app", "payment.terms": "Payment Terms", "payment.terms.direct": "Without deduction until", "periodOfPerformance.day": "Delivery Date", "periodOfPerformance.range": "Delivery Period", phone: "Phone", "price.total.net": "Total Net Price", "price.unit.net": "Unit Net Price", price: "Price", quantity: "Quantity", referencedContract: "Referenced contract", "referencedContract.text": "This invoice is adhering to agreements made by contract between the parties on", "registration.label": "Registration Info", subject: "Subject", sum: "Sum", totalGross: "Total gross", "unit.type": "Unit Type", "vat.position": "on item positions", "vat.reverseCharge.note": "VAT arises on a reverse charge basis and is payable by the customer.", "vat.short": "VAT", "vat.yourId": "Your vat id on file", vat: "Valued Added Tax", }; // Infer keys of EN_translations /** * For example: * - price */ type RawTranslationKeys = keyof typeof EN_translations; /** * For example: * - price.item * - price.sum * - price.unit * - vat.yourId */ type NestedTranslationKeys = | RawTranslationKeys | `${RawTranslationKeys}.${string}` | `${RawTranslationKeys}.${string}.${string}` | `${RawTranslationKeys}.${string}.${string}.${string}` | `${RawTranslationKeys}.${string}.${string}.${string}.${string}`; /** * For example: * - contact@@mail * - vat = 'VAT' * - vat.yourId = 'your vat id' * - footer@@vat.yourId = 'your vat id' * - header@@vat.yourId = 'THIS IS YOUR VAT' */ type LocationBasedTranslationKeys = `${string}@@${NestedTranslationKeys}`; /** * Mix of everything */ export type TranslationKey = | NestedTranslationKeys | LocationBasedTranslationKeys; // Define the type for all translations based on EN_translations keys export type Dictionary = { [key in TranslationKey]: string; }; // Define German translations export const DE_translations: Dictionary = { address: "Adresse", "bank.accountHolder": "Kontoinhaber", "bank.bic": "BIC", "bank.iban": "IBAN", "bank.institution": "Bankinstitut", "bankConnection@@title": "Bankverbindung", "contact@@title": "Kontaktinformationen", "customer.number": "Ihre Kundennummer", description: "Beschreibung", "empty.logo": "Kein Logo gesetzt", "empty.number.customer": "nicht registriert", empty: "nicht angegeben", fax: "Fax", introStatement: "Wir stellen Ihnen hiermit folgende Produkte und Dienstleistungen in Rechnung", "invoice.number": "Rechnungsnr.", invoice: "Rechnung", "item.position": "Pos.", mail: "E-Mail", "overlay@@draft": "Entwurf", "page.continueNext": "Fortsetzung auf Seite", "page.final": "Dies ist die letzte Seite dieses Dokuments.", page: "Seite", pageOf: "von", "payment.qr": "Überweisen per QR-Code", "payment.qr.description": "Den QR-Code einfach mit der Banking-App einscannen", "payment.terms": "Zahlungsbedingungen", "payment.terms.direct": "Ohne Abzug bis zum", "periodOfPerformance.day": "Lieferdatum", "periodOfPerformance.range": "Lieferzeitraum", phone: "Telefon", "price.total.net": "Gesamtpreis", "price.unit.net": "Stückpreis", price: "Preis", quantity: "Menge", referencedContract: "Referenzierter Vertrag", "referencedContract.text": "Diese Rechnung bezieht sich auf die getroffenen Vertragsvereinbarungen vom", "registration.label": "Registrierungsinfo", subject: "Betreff", sum: "Summe", totalGross: "Gesamtbetrag brutto", "unit.type": "Einheit", "vat.position": "auf Positionen", "vat.reverseCharge.note": "Die Umsatzsteuer entsteht im Reverse-Charge-Verfahren und ist vom Kunden zu zahlen.", "vat.short": "MwSt.", "vat.yourId": "Ihre hinterlegte USt-Id", vat: "Umsatzsteuer", }; // Define Spanish translations // export const ES_translations: TTranslationImplementation = { // address: "Dirección", // bankConnection: "Conexión bancaria", // contactInfo: "Información de contacto", // description: "Descripción", // invoice: "Factura", // itemPos: "Pos.", // quantity: "Cantidad", // registrationInfo: "Información de registro", // reverseVatNote: // "El IVA se aplica por inversión del sujeto pasivo y debe ser pagado por el cliente.", // totalNetPrice: "Precio total neto", // unitNetPrice: "Precio unitario neto", // unitType: "Tipo de unidad", // yourCustomerId: "Su número de cliente:", // yourVatId: "Su ID de IVA:", // continuesOnPage: "Continúa en la página", // finalPageStatement: "Esta es la última página de este documento.", // page: "Página", // vatShort: "IVA", // }; // Define French translations // export const FR_translations: TTranslationImplementation = { // address: "Adresse", // bankConnection: "Coordonnées bancaires", // contactInfo: "Informations de contact", // description: "Description", // invoice: "Facture", // itemPos: "Position", // quantity: "Quantité", // registrationInfo: "Informations d'enregistrement", // reverseVatNote: // "La TVA s'applique selon le mécanisme d'autoliquidation et est à payer par le client.", // totalNetPrice: "Prix net total", // unitNetPrice: "Prix unitaire net", // unitType: "Type d'unité", // yourCustomerId: "Votre numéro de client :", // yourVatId: "Votre numéro de TVA :", // continuesOnPage: "Continue sur la page", // finalPageStatement: "Ceci est la dernière page de ce document.", // page: "Page", // vatShort: "TVA", // }; // Define Italian translations // export const IT_translations: TTranslationImplementation = { // address: "Indirizzo", // bankConnection: "Coordinate bancarie", // contactInfo: "Informazioni di contatto", // description: "Descrizione", // invoice: "Fattura", // itemPos: "Pos.", // quantity: "Quantità", // registrationInfo: "Informazioni di registrazione", // reverseVatNote: // "L'IVA è applicata con inversione contabile ed è a carico del cliente.", // totalNetPrice: "Prezzo netto totale", // unitNetPrice: "Prezzo netto unitario", // unitType: "Tipo di unità", // yourCustomerId: "Il tuo numero cliente:", // yourVatId: "Il tuo numero di partita IVA:", // continuesOnPage: "Continua alla pagina", // finalPageStatement: "Questa è l'ultima pagina di questo documento.", // page: "Pagina", // vatShort: "IVA", // }; // Language Code Map export const languageCodeMap: Record = { EN: EN_translations, DE: DE_translations, // ES: ES_translations, // FR: FR_translations, // IT: IT_translations, }; // Language Code Type export type LanguageCode = keyof typeof languageCodeMap; function* getTranslationKeyHierarchy( key: TranslationKey ): Generator { yield key; const areaSplit = key.split("@@") as [TranslationKey, TranslationKey]; let rest = areaSplit[1]; if (rest) { yield rest; } else { rest = areaSplit[0]; } if (!rest.includes(".")) return; const parts = rest.split("."); for (let i = parts.length - 1; i > 0; i--) { yield parts.slice(0, i).join(".") as TranslationKey; } } // Translate Function export const translate = ( languageCode: LanguageCode, key: TranslationKey ): string => { const dictionary = languageCodeMap[languageCode] || EN_translations; const lookupHierarchy = getTranslationKeyHierarchy(key); let found: string; for (let keyOption of lookupHierarchy) { found = dictionary[keyOption] || EN_translations[keyOption]; if (found) { break; } } return found; };