Skip to content

Commit

Permalink
Translations feature added for documents
Browse files Browse the repository at this point in the history
  • Loading branch information
radoslaw-sz committed May 16, 2024
1 parent d5eb4ae commit 44b9338
Show file tree
Hide file tree
Showing 22 changed files with 258 additions and 79 deletions.
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,36 @@ Sometimes you may want to set your next invoice number (for instance when you ha

<b>Protip:</b> After setting change, you can always go to `Templates` to see a preview with your next invoice number.

## Translation

With version 0.8.0 we introduced translations for documents. You can now configure language for generated documents.

**_NOTE:_** We do not support (yet) translations of frontend.

You can configure language of documents using `medusa-config.js` file. Here is the example of Polish language:
```js
const plugins = [
// ... other plugins
{
resolve: `@rsc-labs/medusa-documents`,
options: {
enableUI: true,
documentLanguage: 'pl'
}
}
]
```

### How to add my own translations?

You have two options:

- create `translation.json` under `/assets/i18n/locales/{your-language}` and create a pull requests - we will be more than happy to merge it

- create `translation.json` only for local purposes. Then you need to go to `node_modules/@rsc-labs/medusa-documents/assets/i18n/locales/{your-language}` and paste there your translation file.

Please remember that `documentLanguage` in `medusa-config.js` needs to be equal to `{your-language}`.

## Q&A

### What means "We do not store documents"?
Expand Down
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@rsc-labs/medusa-documents",
"version": "0.7.2",
"version": "0.8.0",
"description": "Generate documents from Medusa",
"main": "dist/index.js",
"author": "RSC Labs (https://rsoftcon.com)",
Expand All @@ -19,10 +19,11 @@
],
"scripts": {
"clean": "cross-env ./node_modules/.bin/rimraf dist",
"copy-assets": "cp -rv src/assets dist/assets",
"build": "cross-env npm run clean && npm run build:server && npm run build:admin",
"build:server": "cross-env npm run clean && tsc -p tsconfig.json",
"build:admin": "cross-env medusa-admin build",
"prepare": "cross-env NODE_ENV=production npm run build:server && medusa-admin bundle"
"prepare": "cross-env NODE_ENV=production npm run build:server && npm run copy-assets && medusa-admin bundle"
},
"dependencies": {
"@medusajs/admin": "7.1.14",
Expand All @@ -39,7 +40,8 @@
"@emotion/styled": "11.11.5",
"lodash": "4.17.21",
"pdfkit": "0.15.0",
"moment": "^2.30.1"
"moment": "^2.30.1",
"i18next": "23.11.4"
},
"devDependencies": {
"@babel/cli": "^7.14.3",
Expand Down
Binary file added src/assets/fonts/IBMPlexSans-Bold.ttf
Binary file not shown.
Binary file added src/assets/fonts/IBMPlexSans-Regular.ttf
Binary file not shown.
26 changes: 26 additions & 0 deletions src/assets/i18n/locales/en/translation.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"invoice": "Invoice",
"invoice-number": "Invoice number",
"invoice-date": "Invoice date",
"invoice-customer-details": "Details",
"invoice-bill-to": "Bill to",
"invoice-ship-to": "Ship to",
"invoice-table-header-item": "Item",
"invoice-table-header-description": "Description",
"invoice-table-header-unit-cost": "Unit Cost",
"invoice-table-header-quantity": "Quantity",
"invoice-table-header-line-total": "Line Total",
"invoice-table-shipping": "Shipping",
"invoice-table-tax": "Tax",
"invoice-table-total": "Total",
"packing-slip": "Packing Slip",
"packing-slip-bill-to": "Bill to",
"packing-slip-ship-to": "Ship to",
"packing-slip-table-header-order-number": "Order #",
"packing-slip-table-header-order-date": "Order date",
"packing-slip-table-header-shipping-method": "Shipping method",
"packing-slip-table-header-item": "Item",
"packing-slip-table-header-description": "Description",
"packing-slip-table-header-quantity": "Quantity",
"packing-slip-table-header-total": "Total"
}
26 changes: 26 additions & 0 deletions src/assets/i18n/locales/pl/translation.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"invoice": "Faktura",
"invoice-number": "Numer faktury",
"invoice-date": "Data faktury",
"invoice-customer-details": "Dane klienta",
"invoice-bill-to": "Nabywca",
"invoice-ship-to": "Wysyłka do",
"invoice-table-header-item": "Produkt",
"invoice-table-header-description": "Opis",
"invoice-table-header-unit-cost": "Cena jedn.",
"invoice-table-header-quantity": "Ilość",
"invoice-table-header-line-total": "Wartość",
"invoice-table-shipping": "Wysyłka",
"invoice-table-tax": "Podatek",
"invoice-table-total": "Suma",
"packing-slip": "List przewozowy",
"packing-slip-bill-to": "Nabywca",
"packing-slip-ship-to": "Wysyłka do",
"packing-slip-table-header-order-number": "Zamówienie nr",
"packing-slip-table-header-order-date": "Data zamówienia",
"packing-slip-table-header-shipping-method": "Metoda wysyłki",
"packing-slip-table-header-item": "Produkt",
"packing-slip-table-header-description": "Opis",
"packing-slip-table-header-quantity": "Ilość",
"packing-slip-table-header-total": "Suma"
}
58 changes: 58 additions & 0 deletions src/loaders/i18next.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import {
ConfigModule,
MedusaContainer,
} from "@medusajs/medusa"

import i18next from 'i18next';
import path from "path";

export default async (
container: MedusaContainer,
config: ConfigModule
): Promise<void> => {
console.info("Starting i18next loader...")

try {
const defaultTranslationsPath = path.resolve(__dirname, `../assets/i18n/locales/en/translation.json`);
const defaultTranslations = await import(defaultTranslationsPath);

await i18next
.init({
fallbackLng: 'en',
defaultNS: 'translation',
ns: 'translation',
resources: {
en: {
translation: defaultTranslations
}
}
}).catch((error) => {
console.error(error);
});

} catch (error) {
console.error('Error initializing i18next:', error);
}


try {
const configLanguage = config['documentLanguage'];
if (configLanguage === undefined) {
console.info('Language is not configured, using "en" by default.')
} else {
console.info(`Language is configured as ${configLanguage}`)
const translationPath = path.resolve(__dirname, `../assets/i18n/locales/${configLanguage}/translation.json`);
const translations = await import(translationPath);
i18next.addResourceBundle(
configLanguage,
'translation',
translations
)
i18next.changeLanguage(configLanguage);
}
} catch {
console.error('Error adding language configured in config. Fallback to "en"');
}

console.info("Ending i18next loader...")
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { generateInvoiceTable } from "./parts/table";
import { generateInvoiceInformation } from "./parts/invoice-info";
import { generateHeaderForLogo } from "./parts/header-for-logo";
import { generateHeaderLogo } from "./parts/header-logo";
import path from "path";

export function validateInput(settings?: DocumentSettings) : ([boolean, string]) {
if (settings && settings.store_address && settings.store_address.company &&
Expand All @@ -32,18 +33,22 @@ export function validateInput(settings?: DocumentSettings) : ([boolean, string])

export default async (settings: DocumentSettings, invoice: Invoice, order: Order): Promise<Buffer> => {
var doc = new PDFDocument();
doc.registerFont('Regular', path.resolve(__dirname, '../../../../assets/fonts/IBMPlexSans-Regular.ttf'))
doc.registerFont('Bold', path.resolve(__dirname, '../../../../assets/fonts/IBMPlexSans-Bold.ttf'))
doc.font('Regular');

const buffers = []
doc.on("data", buffers.push.bind(buffers))

const endHeader = generateHeaderForLogo(doc, 50, settings);
await generateHeaderLogo(doc, 50, settings.store_logo_source);
const endInvoice = generateInvoiceInformation(doc, endHeader, invoice);
const endDetails = generateCustomerInformation(doc, endInvoice, order);
generateInvoiceTable(doc, endDetails, order, order.items);

// end and display the document in the iframe to the right
doc.end();

const bufferPromise = new Promise<Buffer>(resolve => {
const buffers = []
doc.on("data", buffers.push.bind(buffers))
doc.on("end", () => {
const pdfData = Buffer.concat(buffers)
resolve(pdfData)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { generateHeader } from "./parts/header";
import { generateCustomerInformation } from "./parts/customer-info";
import { generateInvoiceTable } from "./parts/table";
import { generateInvoiceInformation } from "./parts/invoice-info";
import path from "path";

export function validateInput(settings?: DocumentSettings) : ([boolean, string]) {
if (settings && settings.store_address && settings.store_address.company &&
Expand All @@ -30,6 +31,13 @@ export function validateInput(settings?: DocumentSettings) : ([boolean, string])

export default async (settings: DocumentSettings, invoice: Invoice, order: Order): Promise<Buffer> => {
var doc = new PDFDocument();
doc.registerFont('Regular', path.resolve(__dirname, '../../../../assets/fonts/IBMPlexSans-Regular.ttf'))
doc.registerFont('Bold', path.resolve(__dirname, '../../../../assets/fonts/IBMPlexSans-Bold.ttf'))
doc.font('Regular');

const buffers = []
doc.on("data", buffers.push.bind(buffers))

const endHeader = generateHeader(doc, 50, settings);
const endInvoiceInfo = generateInvoiceInformation(doc, endHeader, invoice);
const endY = generateCustomerInformation(doc, endInvoiceInfo, order);
Expand All @@ -38,13 +46,11 @@ export default async (settings: DocumentSettings, invoice: Invoice, order: Order
doc.end();

const bufferPromise = new Promise<Buffer>(resolve => {
const buffers = []
doc.on("data", buffers.push.bind(buffers))
doc.on("end", () => {
const pdfData = Buffer.concat(buffers)
resolve(pdfData)
})
})

return await bufferPromise;
};
16 changes: 9 additions & 7 deletions src/services/templates/invoices/basic/parts/customer-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,24 @@

import { Order } from "@medusajs/medusa";
import { generateHr } from "./hr";
import { t } from "i18next";

export function generateCustomerInformation(doc, y, order: Order) {
doc
.fillColor("#444444")
.fontSize(20)
.text("Details", 50, y + 30);
.text(`${t("invoice-customer-details", "Details")}`, 50, y + 30);

generateHr(doc, y + 55);

const customerInformationTop = y + 70;

doc
.fontSize(10)
.font("Helvetica-Bold")
.text('Bill to:', 50, customerInformationTop, {align: 'left'})
.font("Helvetica")
.font("Bold")

.text(`${t("invoice-bill-to", "Bill to")}:`, 50, customerInformationTop, {align: 'left'})
.font("Regular")
.text(`${order.billing_address.first_name} ${order.billing_address.last_name}`, 50, customerInformationTop + 15, {align: 'left'})
.text(`${order.billing_address.city} ${order.billing_address.postal_code}`, 50, customerInformationTop + 30, {align: 'left'})
const billAddress = order.billing_address.address_1;
Expand All @@ -37,9 +39,9 @@ export function generateCustomerInformation(doc, y, order: Order) {

doc
.fontSize(10)
.font("Helvetica-Bold")
.text('Ship to:', 50, customerInformationTop, {align: 'right'})
.font("Helvetica")
.font("Bold")
.text(`${t("invoice-ship-to", "Ship to")}:`, 50, customerInformationTop, {align: 'right'})
.font("Regular")
.text(`${order.shipping_address.first_name} ${order.shipping_address.last_name}`, 50, customerInformationTop + 15, {align: 'right'})
.text(`${order.shipping_address.city} ${order.shipping_address.postal_code}`, 50, customerInformationTop + 30, {align: 'right'})
.moveDown();
Expand Down
11 changes: 6 additions & 5 deletions src/services/templates/invoices/basic/parts/invoice-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,25 @@

import { Invoice } from "../../../../../models/invoice";
import { generateHr } from "./hr";
import { t } from "i18next";

export function generateInvoiceInformation(doc, y: number, invoice: Invoice) : number {
doc
.fillColor("#444444")
.fontSize(20)
.text("Invoice", 50, y + 40);
.text(t("invoice", "Invoice"), 50, y + 40);

generateHr(doc, y + 65);

const invoiceInformationTop = y + 80;

doc
.fontSize(10)
.text("Invoice Number:", 50, invoiceInformationTop)
.font("Helvetica-Bold")
.text(`${t("invoice-number", "Invoice number")}:`, 50, invoiceInformationTop)
.font("Bold")
.text(invoice.display_number, 150, invoiceInformationTop)
.font("Helvetica")
.text("Invoice Date:", 50, invoiceInformationTop + 15)
.font("Regular")
.text(`${t("invoice-date", "Invoice date")}:`, 50, invoiceInformationTop + 15)
.text(invoice.created_at.toLocaleDateString(), 150, invoiceInformationTop + 15)
.moveDown();

Expand Down
25 changes: 13 additions & 12 deletions src/services/templates/invoices/basic/parts/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import { LineItem, Order } from "@medusajs/medusa";
import { generateHr } from "./hr";
import { getDecimalDigits } from "../../../../utils/currency";
import { t } from "i18next";

function amountToDisplay(amount: number, currencyCode: string) : string {
const decimalDigits = getDecimalDigits(currencyCode);
Expand Down Expand Up @@ -41,18 +42,18 @@ export function generateInvoiceTable(doc, y, order: Order, items: LineItem[]) {
let i;
const invoiceTableTop = y + 35;

doc.font("Helvetica-Bold");
doc.font("Bold");
generateTableRow(
doc,
invoiceTableTop,
"Item",
"Description",
"Unit Cost",
"Quantity",
"Line Total"
t("invoice-table-header-item", "Item"),
t("invoice-table-header-description", "Description"),
t("invoice-table-header-unit-cost", "Unit Cost"),
t("invoice-table-header-quantity", "Quantity"),
t("invoice-table-header-line-total", "Line Total")
);
generateHr(doc, invoiceTableTop + 20);
doc.font("Helvetica");
doc.font("Regular");

for (i = 0; i < items.length; i++) {
const item = items[i];
Expand All @@ -76,7 +77,7 @@ export function generateInvoiceTable(doc, y, order: Order, items: LineItem[]) {
subtotalPosition,
"",
"",
"Shipping",
t("invoice-table-shipping", "Shipping"),
"",
amountToDisplay(order.shipping_total, order.currency_code)
);
Expand All @@ -87,21 +88,21 @@ export function generateInvoiceTable(doc, y, order: Order, items: LineItem[]) {
taxPosition,
"",
"",
"Tax",
t("invoice-table-tax", "Tax"),
"",
amountToDisplay(order.tax_total, order.currency_code)
);

const duePosition = taxPosition + 45;
doc.font("Helvetica-Bold");
doc.font("Bold");
generateTableRow(
doc,
duePosition,
"",
"",
"Total",
t("invoice-table-total", "Total"),
"",
amountToDisplay(order.total, order.currency_code)
);
doc.font("Helvetica");
doc.font("Regular");
}
Loading

0 comments on commit 44b9338

Please sign in to comment.