Skip to content

Commit 737ec69

Browse files
committed
i18n for custom menu items; fix persian translation loading; etke.cc billing translations
1 parent 59e5cbd commit 737ec69

File tree

17 files changed

+477
-35
lines changed

17 files changed

+477
-35
lines changed

docs/custom-menu.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@ The examples below contain the configuration settings to add a link to the [Syna
99

1010
Each `menu` item can contain the following fields:
1111

12+
* `url` (required): The URL to navigate to when the menu item is clicked.
1213
* `label` (required): The text to display in the menu.
14+
* `i18n` (optional): Dictionary of translations for the label. The keys should be [BCP 47 language tags](https://en.wikipedia.org/wiki/IETF_language_tag) (e.g., `en`, `fr`, `de`) supported by Synapse Admin (see [src/i18n/](../src/i18n)).
1315
* `icon` (optional): The icon to display next to the label, one of the [src/utils/icons.ts](../src/utils/icons.ts) icons, otherwise a
1416
default icon will be used.
15-
* `url` (required): The URL to navigate to when the menu item is clicked.
1617

1718
[Configuration options](config.md)
1819

@@ -23,6 +24,11 @@ default icon will be used.
2324
"menu": [
2425
{
2526
"label": "Contact support",
27+
"i18n": {
28+
"de": "Support kontaktieren",
29+
"fr": "Contacter le support",
30+
"zh": "联系支持"
31+
},
2632
"icon": "SupportAgent",
2733
"url": "https://github.com/etkecc/synapse-admin/issues"
2834
}
@@ -38,6 +44,11 @@ default icon will be used.
3844
"menu": [
3945
{
4046
"label": "Contact support",
47+
"i18n": {
48+
"de": "Support kontaktieren",
49+
"fr": "Contacter le support",
50+
"zh": "联系支持"
51+
},
4152
"icon": "SupportAgent",
4253
"url": "https://github.com/etkecc/synapse-admin/issues"
4354
}

public/config.json

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,11 @@
1-
{}
1+
{
2+
"menu": [
3+
{
4+
"label": "Test",
5+
"url": "/test",
6+
"i18n": {
7+
"ru": "Тест"
8+
}
9+
}
10+
]
11+
}

src/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import ScheduledCommandShow from "./components/etke.cc/schedules/components/sche
1717
import UserImport from "./components/user-import/UserImport";
1818
import germanMessages from "./i18n/de";
1919
import englishMessages from "./i18n/en";
20+
import persianMessages from "./i18n/fa";
2021
import frenchMessages from "./i18n/fr";
2122
import italianMessages from "./i18n/it";
2223
import japaneseMessages from "./i18n/ja";
@@ -38,6 +39,7 @@ import dataProvider from "./synapse/dataProvider";
3839
const messages = {
3940
de: germanMessages,
4041
en: englishMessages,
42+
fa: persianMessages,
4143
fr: frenchMessages,
4244
it: italianMessages,
4345
ja: japaneseMessages,

src/components/AdminLayout.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
UserMenu,
1515
useStore,
1616
useLocaleState,
17+
useLocale,
1718
} from "react-admin";
1819

1920
import Footer from "./Footer";
@@ -83,6 +84,7 @@ const AdminAppBar = () => {
8384
};
8485

8586
const AdminMenu = props => {
87+
const locale = useLocale();
8688
const [menu, setMenu] = useState([] as MenuItem[]);
8789
const icfg = GetInstanceConfig();
8890
const [etkeRoutesEnabled, setEtkeRoutesEnabled] = useState(false);
@@ -131,20 +133,25 @@ const AdminMenu = props => {
131133
)}
132134
<Menu.ResourceItems />
133135
{etkeRoutesEnabled && !icfg.disabled.payments && (
134-
<Menu.Item key="billing" to="/billing" leftIcon={<PaymentIcon />} primaryText="Billing" />
136+
<Menu.Item key="billing" to="/billing" leftIcon={<PaymentIcon />} primaryText="etkecc.billing.name" />
135137
)}
136138
{menu &&
137139
menu.map((item, index) => {
138-
const { url, icon, label } = item;
140+
const { url, icon, label, i18n } = item;
139141
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
140142
const IconComponent = Icons[icon] as React.ComponentType<any> | undefined;
141143

144+
let primaryText = label;
145+
if (i18n && i18n[locale]) {
146+
primaryText = i18n[locale];
147+
}
148+
142149
return (
143150
<Suspense key={index}>
144151
<Menu.Item
145152
to={url}
146153
target="_blank"
147-
primaryText={label}
154+
primaryText={primaryText}
148155
leftIcon={IconComponent ? <IconComponent /> : <DefaultIcon />}
149156
onClick={props.onMenuClick}
150157
/>

src/components/etke.cc/BillingPage.tsx

Lines changed: 36 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {
1919
import { Stack } from "@mui/material";
2020
import IconButton from "@mui/material/IconButton";
2121
import { useState, useEffect } from "react";
22-
import { useDataProvider, useLocale, useNotify } from "react-admin";
22+
import { useDataProvider, useLocale, useNotify, useTranslate } from "react-admin";
2323

2424
import { EtkeAttribution } from "./EtkeAttribution";
2525
import { useAppContext } from "../../Context";
@@ -46,6 +46,7 @@ const BillingPage = () => {
4646
const dataProvider = useDataProvider() as SynapseDataProvider;
4747
const notify = useNotify();
4848
const locale = useLocale();
49+
const translate = useTranslate();
4950
const [paymentsData, setPaymentsData] = useState<Payment[]>([]);
5051
const [loading, setLoading] = useState(true);
5152
const [maintenance, setMaintenance] = useState(false);
@@ -78,7 +79,7 @@ const BillingPage = () => {
7879
try {
7980
setDownloadingInvoice(transactionId);
8081
await dataProvider.getInvoice(etkeccAdmin, transactionId);
81-
notify("Invoice download started", { type: "info" });
82+
notify("etkecc.billing.helper.download_started", { type: "info" });
8283
} catch (error) {
8384
// Use the specific error message from the dataProvider
8485
const errorMessage = error instanceof Error ? error.message : "Error downloading invoice";
@@ -92,20 +93,19 @@ const BillingPage = () => {
9293
const header = (
9394
<Box>
9495
<Typography variant="h4">
95-
<PaymentIcon sx={{ verticalAlign: "middle", mr: 1 }} /> Billing
96+
<PaymentIcon sx={{ verticalAlign: "middle", mr: 1 }} /> {translate("etkecc.billing.name")}
9697
</Typography>
97-
<Typography variant="body1">View payments and generate invoices from here.</Typography>
9898
<EtkeAttribution>
9999
<Typography variant="body1">
100-
View payments and generate invoices from here. More details about billing can be found{" "}
101-
<Link href="https://etke.cc/help/extras/scheduler/#payments" target="_blank">
102-
here
100+
{translate("etkecc.billing.description1")}{" "}
101+
<Link href="https://etke.cc/help/payments/" target="_blank">
102+
etke.cc/help/payments
103103
</Link>
104104
.
105105
<br />
106-
If you'd like to change your billing email, or add company details, please{" "}
106+
{translate("etkecc.billing.description2")}{" "}
107107
<Link href="https://etke.cc/contacts/" target="_blank">
108-
contact etke.cc support
108+
etke.cc/contacts
109109
</Link>
110110
.
111111
</Typography>
@@ -118,7 +118,7 @@ const BillingPage = () => {
118118
<Stack spacing={3} mt={3}>
119119
{header}
120120
<Box sx={{ mt: 3 }}>
121-
<Typography>Loading billing information...</Typography>
121+
<Typography>{translate("etkecc.billing.helper.loading")}</Typography>
122122
</Box>
123123
</Stack>
124124
);
@@ -130,18 +130,18 @@ const BillingPage = () => {
130130
{header}
131131
<Box sx={{ mt: 3 }}>
132132
<Typography>
133-
There was a problem loading your billing information.
133+
{translate("etkecc.billing.helper.loading_failed1")}
134134
<br />
135-
This might be a temporary issue - please try again in a few minutes.
135+
{translate("etkecc.billing.helper.loading_failed2")}
136136
<br />
137137
</Typography>
138138
<EtkeAttribution>
139139
<Typography>
140-
If it persists, contact{" "}
140+
{translate("etkecc.billing.helper.loading_failed3")}{" "}
141141
<Link href="https://etke.cc/contacts/" target="_blank">
142-
etke.cc support team
142+
etke.cc/contacts
143143
</Link>{" "}
144-
with the following error message:
144+
{translate("etkecc.billing.helper.loading_failed4")}
145145
</Typography>
146146
</EtkeAttribution>
147147
<Typography variant="body2" color="error" sx={{ mt: 1 }}>
@@ -158,11 +158,11 @@ const BillingPage = () => {
158158
{header}
159159
<Box sx={{ mt: 3 }}>
160160
<Alert severity="info">
161-
The system is currently in maintenance mode.
161+
{translate("etkecc.maintenance.title")}
162162
<br />
163-
Please try again later.
163+
{translate("etkecc.maintenance.try_again")}
164164
<br />
165-
You don't need to contact support about this, we are already working on it!
165+
{translate("etkecc.maintenance.note")}
166166
</Alert>
167167
</Box>
168168
</Stack>
@@ -174,16 +174,16 @@ const BillingPage = () => {
174174
{header}
175175
<Box sx={{ mt: 2 }}>
176176
<Typography variant="h5" sx={{ mb: 2 }}>
177-
Payment History
177+
{translate("etkecc.billing.title")}
178178
</Typography>
179179
{paymentsData.length === 0 ? (
180180
<Typography variant="body1">
181-
No payments found.
181+
{translate("etkecc.billing.no_payments")}
182182
<EtkeAttribution>
183183
<Typography>
184-
If you believe that's an error, please{" "}
184+
{translate("etkecc.billing.no_payments_helper")}{" "}
185185
<Link href="https://etke.cc/contacts/" target="_blank">
186-
contact etke.cc support
186+
etke.cc/contacts
187187
</Link>
188188
.
189189
</Typography>
@@ -194,12 +194,12 @@ const BillingPage = () => {
194194
<Table>
195195
<TableHead>
196196
<TableRow>
197-
<TableCell>Transaction ID</TableCell>
198-
<TableCell>Email</TableCell>
199-
<TableCell>Type</TableCell>
200-
<TableCell>Amount</TableCell>
201-
<TableCell>Paid At</TableCell>
202-
<TableCell>Download Invoice</TableCell>
197+
<TableCell>{translate("etkecc.billing.fields.transaction_id")}</TableCell>
198+
<TableCell>{translate("etkecc.billing.fields.email")}</TableCell>
199+
<TableCell>{translate("etkecc.billing.fields.type")}</TableCell>
200+
<TableCell>{translate("etkecc.billing.fields.amount")}</TableCell>
201+
<TableCell>{translate("etkecc.billing.fields.paid_at")}</TableCell>
202+
<TableCell>{translate("etkecc.billing.helper.download_invoice")}</TableCell>
203203
</TableRow>
204204
</TableHead>
205205
<TableBody>
@@ -209,7 +209,9 @@ const BillingPage = () => {
209209
<TruncatedUUID uuid={payment.transaction_id} />
210210
</TableCell>
211211
<TableCell>{payment.email}</TableCell>
212-
<TableCell>{payment.is_subscription ? "Subscription" : "One-time"}</TableCell>
212+
<TableCell>
213+
{translate(`etkecc.billing.enums.type.${payment.is_subscription ? "subscription" : "one_time"}`)}
214+
</TableCell>
213215
<TableCell>${payment.amount.toFixed(2)}</TableCell>
214216
<TableCell>{new Date(payment.paid_at).toLocaleDateString(locale)}</TableCell>
215217
<TableCell>
@@ -220,7 +222,11 @@ const BillingPage = () => {
220222
onClick={() => handleInvoiceDownload(payment.transaction_id)}
221223
disabled={downloadingInvoice === payment.transaction_id}
222224
>
223-
{downloadingInvoice === payment.transaction_id ? "Downloading..." : "Invoice"}
225+
{translate(
226+
downloadingInvoice === payment.transaction_id
227+
? "etkecc.billing.helper.downloading"
228+
: "etkecc.billing.fields.invoice"
229+
)}
224230
</Button>
225231
</TableCell>
226232
</TableRow>

src/i18n/de.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,48 @@ const de: SynapseTranslationMessages = {
533533
helper: { length: "Länge des Tokens, wenn kein Token vorgegeben wird." },
534534
},
535535
},
536+
etkecc: {
537+
billing: {
538+
name: "Abrechnung",
539+
title: "Zahlungshistorie",
540+
no_payments: "Keine Zahlungen gefunden.",
541+
no_payments_helper: "Wenn Sie glauben, dass das ein Fehler ist, kontaktieren Sie bitte den etke.cc-Support unter",
542+
description1:
543+
"Hier können Sie Zahlungen einsehen und Rechnungen erstellen. Mehr zur Verwaltung von Abonnements erfahren Sie unter",
544+
description2:
545+
"Wenn Sie Ihre Abrechnungs-E-Mail ändern oder Firmendaten hinzufügen möchten, kontaktieren Sie bitte den etke.cc-Support unter",
546+
fields: {
547+
transaction_id: "Transaktions-ID",
548+
email: "E-Mail",
549+
type: "Typ",
550+
amount: "Betrag",
551+
paid_at: "Bezahlt am",
552+
invoice: "Rechnung",
553+
},
554+
enums: {
555+
type: {
556+
subscription: "Abonnement",
557+
one_time: "Einmalig",
558+
},
559+
},
560+
helper: {
561+
download_invoice: "Rechnung herunterladen",
562+
downloading: "Wird heruntergeladen...",
563+
download_started: "Der Rechnungsdownload wurde gestartet.",
564+
loading: "Abrechnungsinformationen werden geladen...",
565+
loading_failed1: "Beim Laden der Abrechnungsinformationen ist ein Problem aufgetreten.",
566+
loading_failed2: "Bitte versuchen Sie es später erneut.",
567+
loading_failed3: "Wenn das Problem weiterhin besteht, kontaktieren Sie bitte den etke.cc-Support unter",
568+
loading_failed4: "mit der folgenden Fehlermeldung:",
569+
},
570+
},
571+
maintenance: {
572+
title: "Das System befindet sich derzeit im Wartungsmodus.",
573+
try_again: "Bitte versuchen Sie es später erneut.",
574+
note: "Sie müssen den Support hierzu nicht kontaktieren — wir arbeiten bereits daran!",
575+
},
576+
},
577+
536578
scheduled_commands: {
537579
action: {
538580
create_success: "Geplanter Befehl erfolgreich erstellt.",

src/i18n/en.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,47 @@ const en: SynapseTranslationMessages = {
501501
helper: { length: "Length of the token if no token is given." },
502502
},
503503
},
504+
etkecc: {
505+
billing: {
506+
name: "Billing",
507+
title: "Payment History",
508+
no_payments: "No payments found.",
509+
no_payments_helper: "If you believe that's an error, please contact etke.cc support on",
510+
description1:
511+
"View payments and generate invoices from here. You can learn more about subscription management on",
512+
description2:
513+
"If you'd like to change your billing email, or add company details, please contact etke.cc support on",
514+
fields: {
515+
transaction_id: "Transaction ID",
516+
email: "Email",
517+
type: "Type",
518+
amount: "Amount",
519+
paid_at: "Paid At",
520+
invoice: "Invoice",
521+
},
522+
enums: {
523+
type: {
524+
subscription: "Subscription",
525+
one_time: "One-time",
526+
},
527+
},
528+
helper: {
529+
download_invoice: "Download Invoice",
530+
downloading: "Downloading...",
531+
download_started: "Invoice download started.",
532+
loading: "Loading billing information...",
533+
loading_failed1: "There was a problem loading billing information.",
534+
loading_failed2: "Please try again later.",
535+
loading_failed3: "If the problem persists, please contact etke.cc support on",
536+
loading_failed4: "with the following error message:",
537+
},
538+
},
539+
maintenance: {
540+
title: "The system is currently in maintenance mode.",
541+
try_again: "Please try again later.",
542+
note: "You don't need to contact support about this, we are already working on it!",
543+
},
544+
},
504545
scheduled_commands: {
505546
action: {
506547
create_success: "Scheduled command created successfully",

0 commit comments

Comments
 (0)