diff --git a/client/package-lock.json b/client/package-lock.json index 3c3cd08..9f4e672 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -16,6 +16,7 @@ "@refinedev/simple-rest": "^4.5.0", "@tanstack/react-query": "^4.29.19", "antd": "^5.6.4", + "flag-icons": "^6.7.0", "i18next": "^23.2.8", "i18next-browser-languagedetector": "^7.1.0", "i18next-xhr-backend": "^3.2.2", @@ -4837,6 +4838,11 @@ "node": ">=8" } }, + "node_modules/flag-icons": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/flag-icons/-/flag-icons-6.7.0.tgz", + "integrity": "sha512-+KXrrrXN2jiETFxisFl+3f83Bq7tj5nuIWnbv9fX59k05lvldEXRCOffybb5hAIjMWt4nmG0E8OfKt7Flm99Eg==" + }, "node_modules/flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", diff --git a/client/package.json b/client/package.json index 667030a..ff929c5 100644 --- a/client/package.json +++ b/client/package.json @@ -15,6 +15,7 @@ "@refinedev/simple-rest": "^4.5.0", "@tanstack/react-query": "^4.29.19", "antd": "^5.6.4", + "flag-icons": "^6.7.0", "i18next": "^23.2.8", "i18next-browser-languagedetector": "^7.1.0", "i18next-xhr-backend": "^3.2.2", diff --git a/client/public/images/flags/en.svg b/client/public/images/flags/en.svg deleted file mode 100644 index 79ff4af..0000000 --- a/client/public/images/flags/en.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/client/public/locales/de/common.json b/client/public/locales/de/common.json new file mode 100644 index 0000000..c2e3606 --- /dev/null +++ b/client/public/locales/de/common.json @@ -0,0 +1,48 @@ +{ + "actions": { + "list": "Aufführen", + "create": "Erstellen", + "edit": "Bearbeiten", + "show": "Zeigen" + }, + "buttons": { + "create": "Erstellen", + "save": "Speichern", + "logout": "Abmelden", + "delete": "Löschen", + "edit": "Bearbeiten", + "cancel": "Abbrechen", + "confirm": "Sicher?", + "filter": "Filter", + "clear": "Löschen", + "refresh": "Erneuern", + "show": "Zeigen", + "undo": "Undo", + "import": "Importieren", + "clone": "Klon", + "notAccessTitle": "Sie haben keine zugriffsberechtigung" + }, + "warnWhenUnsavedChanges": "Nicht gespeicherte Änderungen werden nicht übernommen.", + "notifications": { + "success": "Erfolg", + "error": "Fehler (status code: {{statusCode}})", + "undoable": "Sie haben {{seconds}} Sekunden Zeit für Undo.", + "createSuccess": "{{resource}} erfolgreich erstellt.", + "createError": "Fehler beim Erstellen {{resource}} (status code: {{statusCode}})", + "deleteSuccess": "{{resource}} erfolgreich gelöscht.", + "deleteError": "Fehler beim Löschen {{resource}} (status code: {{statusCode}})", + "editSuccess": "{{resource}} erfolgreich bearbeitet.", + "editError": "Fehler beim Bearbeiten {{resource}} (status code: {{statusCode}})", + "importProgress": "{{processed}}/{{total}} importiert" + }, + "loading": "Wird geladen", + "tags": { + "clone": "Klon" + }, + "dashboard": { + "title": "Dashboard" + }, + "table": { + "actions": "Aktionen" + } +} diff --git a/client/public/locales/en/common.json b/client/public/locales/en/common.json index f1326b2..96b49e6 100644 --- a/client/public/locales/en/common.json +++ b/client/public/locales/en/common.json @@ -1,70 +1,4 @@ { - "pages": { - "login": { - "title": "Sign in to your account", - "signin": "Sign in", - "signup": "Sign up", - "divider": "or", - "fields": { - "email": "Email", - "password": "Password" - }, - "errors": { - "validEmail": "Invalid email address" - }, - "buttons": { - "submit": "Login", - "forgotPassword": "Forgot password?", - "noAccount": "Don’t have an account?", - "rememberMe": "Remember me" - } - }, - "forgotPassword": { - "title": "Forgot your password?", - "fields": { - "email": "Email" - }, - "errors": { - "validEmail": "Invalid email address" - }, - "buttons": { - "submit": "Send reset instructions" - } - }, - "register": { - "title": "Sign up for your account", - "fields": { - "email": "Email", - "password": "Password" - }, - "errors": { - "validEmail": "Invalid email address" - }, - "buttons": { - "submit": "Register", - "haveAccount": "Have an account?" - } - }, - "updatePassword": { - "title": "Update password", - "fields": { - "password": "New Password", - "confirmPassword": "Confirm new password" - }, - "errors": { - "confirmPasswordNotMatch": "Passwords do not match" - }, - "buttons": { - "submit": "Update" - } - }, - "error": { - "info": "You may have forgotten to add the {{action}} component to {{resource}} resource.", - "404": "Sorry, the page you visited does not exist.", - "resource404": "Are you sure you have created the {{resource}} resource.", - "backHome": "Back Home" - } - }, "actions": { "list": "List", "create": "Create", @@ -87,7 +21,9 @@ "undo": "Undo", "import": "Import", "clone": "Clone", - "notAccessTitle": "You don't have permission to access" + "notAccessTitle": "You don't have permission to access", + "hideColumns": "Hide Columns", + "clearFilters": "Clear Filters" }, "warnWhenUnsavedChanges": "Are you sure you want to leave? You have unsaved changes.", "notifications": { @@ -103,25 +39,33 @@ "importProgress": "Importing: {{processed}}/{{total}}" }, "loading": "Loading", + "version": "Version", "tags": { "clone": "Clone" }, "dashboard": { "title": "Dashboard" }, - "spools": { - "spools": "Spools", + "spool": { + "spool": "Spools", "fields": { "id": "Id", - "filament_name": "Filament Name", + "filament_name": "Filament", + "filament": "Filament", "used_weight": "Used Weight", "remaining_weight": "Remaining Weight", "location": "Location", "lot_nr": "Lot Nr", "first_used": "First Used", "last_used": "Last Used", + "registered": "Registered", "comment": "Comment" }, + "fields_help": { + "used_weight": "How much filament has been used from the spool. A new spool should have 0g used.", + "location": "Where the spool is located if you have multiple locations where you store your spools.", + "lot_nr": "Manufacturer's lot number. Can be used to ensure a print has consistent color if multiple spools are used." + }, "titles": { "create": "Create Spool", "clone": "Clone Spool", @@ -130,11 +74,12 @@ "show": "Show Spool" } }, - "filaments": { - "filaments": "Filaments", + "filament": { + "filament": "Filaments", "fields": { "id": "Id", - "vendor_name": "Vendor Name", + "vendor_name": "Vendor", + "vendor": "Vendor", "name": "Name", "material": "Material", "price": "Price", @@ -149,6 +94,14 @@ "settings_bed_temp": "Bed Temp", "color_hex": "Color" }, + "fields_help": { + "name": "Filament name, to distinguish this filament type among others from the same vendor. Should contain the color for example.", + "material": "E.g. PLA, ABS, PETG, etc.", + "price": "Price of a full spool in the system configured currency.", + "weight": "The filament weight of a full spool (net weight). This should not include the weight of the spool itself, only the filament. It is what is usually written on the packaging.", + "spool_weight": "The weight of an empty spool. Currently not used for anything, but could be used by thirdparty apps to e.g. calculate the filament weight from a weight measurement.", + "article_number": "E.g. EAN, UPC, etc." + }, "titles": { "create": "Create Filament", "clone": "Clone Filament", @@ -157,8 +110,8 @@ "show": "Show Filament" } }, - "vendors": { - "vendors": "Vendors", + "vendor": { + "vendor": "Vendors", "fields": { "id": "Id", "name": "Name", @@ -175,5 +128,30 @@ }, "table": { "actions": "Actions" + }, + "documentTitle": { + "default": "Spoolman", + "suffix": " | Spoolman", + "filament": { + "list": "Filaments | Spoolman", + "show": "#{{id}} Show Filament | Spoolman", + "edit": "#{{id}} Edit Filament | Spoolman", + "create": "Create Filament | Spoolman", + "clone": "#{{id}} Clone Filament | Spoolman" + }, + "spool": { + "list": "Spools | Spoolman", + "show": "#{{id}} Show Spool | Spoolman", + "edit": "#{{id}} Edit Spool | Spoolman", + "create": "Create Spool | Spoolman", + "clone": "#{{id}} Clone Spool | Spoolman" + }, + "vendor": { + "list": "Vendors | Spoolman", + "show": "#{{id}} Show Vendor | Spoolman", + "edit": "#{{id}} Edit Vendor | Spoolman", + "create": "Create Vendor | Spoolman", + "clone": "#{{id}} Clone Vendor | Spoolman" + } } } diff --git a/client/public/locales/sv/common.json b/client/public/locales/sv/common.json new file mode 100644 index 0000000..dc4a79d --- /dev/null +++ b/client/public/locales/sv/common.json @@ -0,0 +1,157 @@ +{ + "actions": { + "list": "Lista", + "create": "Skapa", + "edit": "Ändra", + "show": "Visa", + "clone": "Klona" + }, + "buttons": { + "create": "Skapa", + "save": "Spara", + "logout": "Logga ut", + "delete": "Ta bort", + "edit": "Ändra", + "cancel": "Avbryt", + "confirm": "Är du säker?", + "filter": "Filtrera", + "clear": "Rensa", + "refresh": "Uppdatera", + "show": "Visa", + "undo": "Ångra", + "import": "Importera", + "clone": "Klona", + "notAccessTitle": "Du har inte tillgång till denna sida", + "hideColumns": "Dölj kolumner", + "clearFilters": "Rensa filter" + }, + "warnWhenUnsavedChanges": "Du har osparade ändringar. Är du säker på att du vill lämna sidan?", + "notifications": { + "success": "Lyckades!", + "error": "Fel (statuskod: {{statusCode}})", + "undoable": "Du har {{seconds}} sekunder på dig att ångra", + "createSuccess": "Lyckades skapa {{resource}}", + "createError": "Det gick inte att skapa {{resource}} (statuskod: {{statusCode}})", + "deleteSuccess": "Lyckades ta bort {{resource}}", + "deleteError": "Det gick inte att ta bort {{resource}} (statuskod: {{statusCode}})", + "editSuccess": "Lyckades ändra {{resource}}", + "editError": "Det gick inte att ändra {{resource}} (statuskod: {{statusCode}})", + "importProgress": "Importerar: {{processed}}/{{total}}" + }, + "loading": "Laddar...", + "version": "Version", + "tags": { + "clone": "Klona" + }, + "dashboard": { + "title": "Hem" + }, + "spool": { + "spool": "Spolar", + "fields": { + "id": "Id", + "filament_name": "Filament", + "filament": "Filament", + "used_weight": "Använd vikt", + "remaining_weight": "Återstående vikt", + "location": "Plats", + "lot_nr": "Batchnummer", + "first_used": "Användes först", + "last_used": "Användes senast", + "registered": "Registrerad", + "comment": "Kommentar" + }, + "fields_help": { + "used_weight": "Hur mycket filament som använts på spolen. En ny spole har värdet 0g.", + "location": "Vart spolen finns om du har flera platser att förvara spolar på.", + "lot_nr": "Tillverkarens batchnummer. Kan användas för att se till att en utskrift har jämn färg ifall flera spolar används." + }, + "titles": { + "create": "Skapa spole", + "clone": "Klona spole", + "edit": "Ändra spole", + "list": "Spolar", + "show": "Visa spole" + } + }, + "filament": { + "filament": "Filament", + "fields": { + "id": "Id", + "vendor_name": "Leverantör", + "vendor": "Leverantör", + "name": "Namn", + "material": "Material", + "price": "Pris", + "density": "Densitet", + "diameter": "Diameter", + "weight": "Vikt", + "spool_weight": "Spolvikt", + "article_number": "Artikelnummer", + "registered": "Registrerad", + "comment": "Kommentar", + "settings_extruder_temp": "Extrudertemp", + "settings_bed_temp": "Bäddtemp", + "color_hex": "Färg" + }, + "fields_help": { + "name": "Filamentets namn, för att skilja detta filament från andra från samma leverantör. Bör innehålla färgen till exempel.", + "material": "T.ex. PLA, ABS, PETG, etc.", + "price": "Pris för en full spole i systemets valuta.", + "weight": "Vikten på en full spole (nettovikt). Detta ska inte inkludera vikten på spolen själv, bara filamentet. Det är vad som vanligtvis står på förpackningen.", + "spool_weight": "Vikten på en tom spole. Detta används inte till något i dagsläget, men skulle kunna användas av tredjepartsappar för att t.ex. beräkna filamentvikten från en viktmätning.", + "article_number": "T.ex. EAN, UPC, etc." + }, + "titles": { + "create": "Skapa filament", + "clone": "Klona filament", + "edit": "Ändra filament", + "list": "Filament", + "show": "Visa filament" + } + }, + "vendor": { + "vendor": "Leverantörer", + "fields": { + "id": "Id", + "name": "Namn", + "registered": "Registrerad", + "comment": "Kommentar" + }, + "titles": { + "create": "Skapa leverantör", + "clone": "Klona leverantör", + "edit": "Ändra leverantör", + "list": "Leverantörer", + "show": "Visa leverantör" + } + }, + "table": { + "actions": "Åtgärder" + }, + "documentTitle": { + "default": "Spoolman", + "suffix": " | Spoolman", + "filament": { + "list": "Filament | Spoolman", + "show": "#{{id}} Visa filament | Spoolman", + "edit": "#{{id}} Redigera filament | Spoolman", + "create": "Skapa filament | Spoolman", + "clone": "#{{id}} Klona filament | Spoolman" + }, + "spool": { + "list": "Spolar | Spoolman", + "show": "#{{id}} Visa spole | Spoolman", + "edit": "#{{id}} Redigera spole | Spoolman", + "create": "Skapa spole | Spoolman", + "clone": "#{{id}} Klona spole | Spoolman" + }, + "vendor": { + "list": "Leverantörer | Spoolman", + "show": "#{{id}} Visa leverantör | Spoolman", + "edit": "#{{id}} Redigera leverantör | Spoolman", + "create": "Skapa leverantör | Spoolman", + "clone": "#{{id}} Klona leverantör | Spoolman" + } + } +} diff --git a/client/src/App.tsx b/client/src/App.tsx index f19b68d..5a68631 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -11,6 +11,7 @@ import { import "@refinedev/antd/dist/reset.css"; import routerBindings, { + DocumentTitleHandler, NavigateToResource, UnsavedChangesNotifier, } from "@refinedev/react-router-v6"; @@ -41,6 +42,9 @@ import { import { ConfigProvider } from "antd"; import { Footer } from "antd/es/layout/layout"; import { Version } from "./components/version"; +import React from "react"; +import { Locale } from "antd/es/locale"; +import { languages } from "./i18n"; function App() { const { t, i18n } = useTranslation(); @@ -51,6 +55,20 @@ function App() { getLocale: () => i18n.language, }; + // Fetch the antd locale using dynamic imports + const [antdLocale, setAntdLocale] = React.useState(); + React.useEffect(() => { + const fetchLocale = async () => { + const locale = await import( + `./../node_modules/antd/es/locale/${languages[ + i18n.language + ].fullCode.replace("-", "_")}.js` + ); + setAntdLocale(locale.default); + }; + fetchLocale().catch(console.error); + }, [i18n.language]); + if (!import.meta.env.VITE_APIURL) { return ( <> @@ -68,6 +86,7 @@ function App() { (
- Spoolman - Version + Spoolman - {t("version")}
)} > @@ -199,6 +218,7 @@ function App() { +
diff --git a/client/src/components/column.tsx b/client/src/components/column.tsx index 64243f8..6fa4bc7 100644 --- a/client/src/components/column.tsx +++ b/client/src/components/column.tsx @@ -14,12 +14,13 @@ import utc from "dayjs/plugin/utc"; import { DateField, TextField } from "@refinedev/antd"; import Icon from "@ant-design/icons"; import { ReactComponent as SpoolIcon } from "../icon_spool.svg"; +import { useTranslate } from "@refinedev/core"; dayjs.extend(utc); interface BaseColumnProps { id: keyof Obj & string; - title: string; + i18ncat: string; dataSource: Obj[]; tableState: TableState; } @@ -42,6 +43,7 @@ interface CustomColumnProps { function Column( props: BaseColumnProps & FilteredColumnProps & CustomColumnProps ) { + const t = useTranslate(); if ( props.tableState.showColumns && !props.tableState.showColumns.includes(props.id) @@ -51,7 +53,7 @@ function Column( const typedSorters = typeSorters(props.tableState.sorters); const columnProps: AntdColumnProps = { dataIndex: props.id, - title: props.title, + title: t(`${props.i18ncat}.fields.${props.id}`), sorter: true, sortOrder: getSortOrderForField(typedSorters, props.id), }; diff --git a/client/src/components/header/index.tsx b/client/src/components/header/index.tsx index f3a2b41..a74fe5c 100644 --- a/client/src/components/header/index.tsx +++ b/client/src/components/header/index.tsx @@ -1,8 +1,7 @@ import { DownOutlined } from "@ant-design/icons"; import type { RefineThemedLayoutV2HeaderProps } from "@refinedev/antd"; -import { useGetIdentity, useGetLocale, useSetLocale } from "@refinedev/core"; +import { useGetLocale, useSetLocale } from "@refinedev/core"; import { - Avatar, Button, Dropdown, Layout as AntdLayout, @@ -10,20 +9,15 @@ import { Space, Switch, theme, - Typography, } from "antd"; import React, { useContext } from "react"; import { useTranslation } from "react-i18next"; import { ColorModeContext } from "../../contexts/color-mode"; -const { Text } = Typography; -const { useToken } = theme; +import "/node_modules/flag-icons/css/flag-icons.min.css"; +import { languages } from "../../i18n"; -type IUser = { - id: number; - name: string; - avatar: string; -}; +const { useToken } = theme; export const Header: React.FC = ({ sticky, @@ -32,7 +26,6 @@ export const Header: React.FC = ({ const { i18n } = useTranslation(); const locale = useGetLocale(); const changeLanguage = useSetLocale(); - const { data: user } = useGetIdentity(); const { mode, setMode } = useContext(ColorModeContext); const currentLocale = locale(); @@ -43,11 +36,12 @@ export const Header: React.FC = ({ key: lang, onClick: () => changeLanguage(lang), icon: ( - - - + ), - label: lang === "en" ? "English" : "German", + label: languages[lang].name, })); const headerStyles: React.CSSProperties = { @@ -76,8 +70,12 @@ export const Header: React.FC = ({ > @@ -88,10 +86,6 @@ export const Header: React.FC = ({ onChange={() => setMode(mode === "light" ? "dark" : "light")} defaultChecked={mode === "dark"} /> - - {user?.name && {user.name}} - {user?.avatar && } - ); diff --git a/client/src/i18n.ts b/client/src/i18n.ts index e1c100d..9fdfcf4 100644 --- a/client/src/i18n.ts +++ b/client/src/i18n.ts @@ -3,17 +3,41 @@ import detector from "i18next-browser-languagedetector"; import Backend from "i18next-xhr-backend"; import { initReactI18next } from "react-i18next"; +interface Language { + name: string; + countryCode: string; + fullCode: string; +} + +export const languages: { [key: string]: Language } = { + ["en"]: { + name: "English", + countryCode: "gb", + fullCode: "en-GB", + }, + ["sv"]: { + name: "Svenska", + countryCode: "se", + fullCode: "sv-SE", + }, + ["de"]: { + name: "Deutsch", + countryCode: "de", + fullCode: "de-DE", + }, +} + i18n .use(Backend) .use(detector) .use(initReactI18next) .init({ - supportedLngs: ["en"], + supportedLngs: Object.keys(languages), backend: { loadPath: "/locales/{{lng}}/{{ns}}.json", }, defaultNS: "common", - fallbackLng: ["en"], + fallbackLng: Object.keys(languages), }); export default i18n; diff --git a/client/src/pages/filaments/create.tsx b/client/src/pages/filaments/create.tsx index 1e60aab..2dbe964 100644 --- a/client/src/pages/filaments/create.tsx +++ b/client/src/pages/filaments/create.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { IResourceComponentsProps } from "@refinedev/core"; +import { IResourceComponentsProps, useTranslate } from "@refinedev/core"; import { Create, useForm, useSelect } from "@refinedev/antd"; import { Form, Input, Select, InputNumber, ColorPicker } from "antd"; import TextArea from "antd/es/input/TextArea"; @@ -14,6 +14,8 @@ interface CreateOrCloneProps { export const FilamentCreate: React.FC< IResourceComponentsProps & CreateOrCloneProps > = (props) => { + const t = useTranslate(); + const { formProps, saveButtonProps, formLoading } = useForm(); if (props.mode === "clone" && formProps.initialValues) { @@ -36,8 +38,8 @@ export const FilamentCreate: React.FC< >
= () => { + const t = useTranslate(); + const { formProps, saveButtonProps } = useForm(); const { selectProps } = useSelect({ @@ -32,7 +34,7 @@ export const FilamentEdit: React.FC = () => { = () => { = () => { = () => { = () => { = () => { /> = () => { /> = () => { /> = () => { = () => { = () => { = () => { = () => { { } export const FilamentList: React.FC = () => { + const t = useTranslate(); + // Load initial state const initialState = useInitialTableState("filamentList"); @@ -133,14 +138,14 @@ export const FilamentList: React.FC = () => { setCurrent(1); }} > - Clear Filters + {t("buttons.clearFilters")} ({ key: column_id, - label: i18n.t(`filaments.fields.${column_id}`), + label: t(`filament.fields.${column_id}`), })), selectedKeys: showColumns, selectable: true, @@ -154,7 +159,7 @@ export const FilamentList: React.FC = () => { }} > {defaultButtons} @@ -177,38 +182,38 @@ export const FilamentList: React.FC = () => { > {SortedColumn({ id: "id", - title: "Id", + i18ncat: "filament", dataSource, tableState, })} {FilteredColumn({ id: "vendor_name", - title: "Vendor", + i18ncat: "filament", dataSource, tableState, })} {SpoolIconColumn({ id: "name", - title: "Name", + i18ncat: "filament", color: (record: IFilamentCollapsed) => record.color_hex, dataSource, tableState, })} {FilteredColumn({ id: "material", - title: "Material", + i18ncat: "filament", dataSource, tableState, })} {SortedColumn({ id: "price", - title: "Price", + i18ncat: "filament", dataSource, tableState, })} {NumberColumn({ id: "density", - title: "Density", + i18ncat: "filament", unit: "g/cm³", decimals: 2, dataSource, @@ -216,7 +221,7 @@ export const FilamentList: React.FC = () => { })} {NumberColumn({ id: "diameter", - title: "Diameter", + i18ncat: "filament", unit: "mm", decimals: 2, dataSource, @@ -224,7 +229,7 @@ export const FilamentList: React.FC = () => { })} {NumberColumn({ id: "weight", - title: "Weight", + i18ncat: "filament", unit: "g", decimals: 1, dataSource, @@ -232,7 +237,7 @@ export const FilamentList: React.FC = () => { })} {NumberColumn({ id: "spool_weight", - title: "Spool Weight", + i18ncat: "filament", unit: "g", decimals: 1, dataSource, @@ -240,42 +245,41 @@ export const FilamentList: React.FC = () => { })} {FilteredColumn({ id: "article_number", - title: "Article Number", + i18ncat: "filament", dataSource, tableState, })} {DateColumn({ id: "registered", - title: "Registered", + i18ncat: "filament", dataSource, tableState, })} {SortedColumn({ id: "comment", - title: "Comment", + i18ncat: "filament", dataSource, tableState, })} ( diff --git a/client/src/pages/filaments/show.tsx b/client/src/pages/filaments/show.tsx index 9183893..491f6b2 100644 --- a/client/src/pages/filaments/show.tsx +++ b/client/src/pages/filaments/show.tsx @@ -1,5 +1,9 @@ import React from "react"; -import { IResourceComponentsProps, useShow } from "@refinedev/core"; +import { + IResourceComponentsProps, + useShow, + useTranslate, +} from "@refinedev/core"; import { Show, NumberField, DateField, TextField } from "@refinedev/antd"; import { Typography } from "antd"; import { NumberFieldUnit } from "../../components/numberField"; @@ -12,6 +16,8 @@ dayjs.extend(utc); const { Title } = Typography; export const FilamentShow: React.FC = () => { + const t = useTranslate(); + const { queryResult } = useShow(); const { data, isLoading } = queryResult; @@ -19,25 +25,25 @@ export const FilamentShow: React.FC = () => { return ( - Id + {t("filament.fields.id")} - Registered + {t("filament.fields.registered")} - Name + {t("filament.fields.name")} - Vendor - {/* {vendorIsLoading ? <>Loading... : <>{vendorData?.data?.id}} */} - Color + {/* {t("filament.fields.id")} + {vendorIsLoading ? <>Loading... : <>{vendorData?.data?.id}} */} + {t("filament.fields.color_hex")} - Material + {t("filament.fields.material")} - Price + {t("filament.fields.price")} - Density + {t("filament.fields.density")} = () => { maximumFractionDigits: 2, }} /> - Diameter + {t("filament.fields.diameter")} = () => { maximumFractionDigits: 2, }} /> - Weight + {t("filament.fields.weight")} = () => { maximumFractionDigits: 1, }} /> - Spool Weight + {t("filament.fields.spool_weight")} = () => { maximumFractionDigits: 1, }} /> - Overridden Extruder Temperature + {t("filament.fields.settings_extruder_temp")} {!record?.settings_extruder_temp ? ( ) : ( @@ -78,15 +84,15 @@ export const FilamentShow: React.FC = () => { unit="°C" /> )} - Overridden Bed Temperature + {t("filament.fields.settings_bed_temp")} {!record?.settings_bed_temp ? ( ) : ( )} - Article Number + {t("filament.fields.article_number")} - Comment + {t("filament.fields.comment")} ); diff --git a/client/src/pages/spools/create.tsx b/client/src/pages/spools/create.tsx index 0592962..dacd1b5 100644 --- a/client/src/pages/spools/create.tsx +++ b/client/src/pages/spools/create.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { IResourceComponentsProps } from "@refinedev/core"; +import { IResourceComponentsProps, useTranslate } from "@refinedev/core"; import { Create, useForm, useSelect } from "@refinedev/antd"; import { Form, Input, DatePicker, Select, InputNumber } from "antd"; import dayjs from "dayjs"; @@ -14,6 +14,8 @@ interface CreateOrCloneProps { export const SpoolCreate: React.FC< IResourceComponentsProps & CreateOrCloneProps > = (props) => { + const t = useTranslate(); + const { formProps, saveButtonProps, formLoading } = useForm(); if (props.mode === "clone" && formProps.initialValues) { @@ -49,13 +51,17 @@ export const SpoolCreate: React.FC< return ( = () => { + const t = useTranslate(); + const { formProps, saveButtonProps } = useForm(); const { queryResult } = useSelect({ @@ -40,7 +42,7 @@ export const SpoolEdit: React.FC = () => { = () => { = () => { = () => { = () => { = () => { = () => { = () => { + const t = useTranslate(); + // Load initial state const initialState = useInitialTableState("spoolList"); @@ -131,14 +136,14 @@ export const SpoolList: React.FC = () => { setCurrent(1); }} > - Clear Filters + {t("buttons.clearFilters")} ({ key: column, - label: i18n.t(`spools.fields.${column}`), + label: t(`spool.fields.${column}`), })), selectedKeys: showColumns, selectable: true, @@ -152,7 +157,7 @@ export const SpoolList: React.FC = () => { }} > {defaultButtons} @@ -175,20 +180,20 @@ export const SpoolList: React.FC = () => { > {SortedColumn({ id: "id", - title: "Id", + i18ncat: "spool", dataSource, tableState, })} {SpoolIconColumn({ id: "filament_name", - title: "Filament", + i18ncat: "spool", color: (record: ISpoolCollapsed) => record.filament.color_hex, dataSource, tableState, })} {NumberColumn({ id: "used_weight", - title: "Used Weight", + i18ncat: "spool", unit: "g", decimals: 1, dataSource, @@ -196,7 +201,7 @@ export const SpoolList: React.FC = () => { })} {NumberColumn({ id: "remaining_weight", - title: "Estimated Remaining Weight", + i18ncat: "spool", unit: "g", decimals: 1, dataSource, @@ -204,53 +209,53 @@ export const SpoolList: React.FC = () => { })} {FilteredColumn({ id: "location", - title: "Location", + i18ncat: "spool", dataSource, tableState, })} {FilteredColumn({ id: "lot_nr", - title: "Lot Nr", + i18ncat: "spool", dataSource, tableState, })} {DateColumn({ id: "first_used", - title: "First Used", + i18ncat: "spool", dataSource, tableState, })} {DateColumn({ id: "last_used", - title: "Last Used", + i18ncat: "spool", dataSource, tableState, })} {SortedColumn({ id: "comment", - title: "Comment", + i18ncat: "spool", dataSource, tableState, })} ( diff --git a/client/src/pages/spools/show.tsx b/client/src/pages/spools/show.tsx index 5caa89c..efa1520 100644 --- a/client/src/pages/spools/show.tsx +++ b/client/src/pages/spools/show.tsx @@ -1,5 +1,9 @@ import React from "react"; -import { IResourceComponentsProps, useShow } from "@refinedev/core"; +import { + IResourceComponentsProps, + useShow, + useTranslate, +} from "@refinedev/core"; import { Show, NumberField, DateField, TextField } from "@refinedev/antd"; import { Typography } from "antd"; import { NumberFieldUnit } from "../../components/numberField"; @@ -12,6 +16,8 @@ dayjs.extend(utc); const { Title } = Typography; export const SpoolShow: React.FC = () => { + const t = useTranslate(); + const { queryResult } = useShow(); const { data, isLoading } = queryResult; @@ -19,35 +25,35 @@ export const SpoolShow: React.FC = () => { return ( - Id + {t("spool.fields.id")} - Registered + {t("spool.fields.registered")} - First Used + {t("spool.fields.first_used")} ); diff --git a/client/src/pages/vendors/create.tsx b/client/src/pages/vendors/create.tsx index 8fc6b88..4e2b65f 100644 --- a/client/src/pages/vendors/create.tsx +++ b/client/src/pages/vendors/create.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { IResourceComponentsProps } from "@refinedev/core"; +import { IResourceComponentsProps, useTranslate } from "@refinedev/core"; import { Create, useForm } from "@refinedev/antd"; import { Form, Input } from "antd"; import TextArea from "antd/es/input/TextArea"; @@ -12,17 +12,23 @@ interface CreateOrCloneProps { export const VendorCreate: React.FC< IResourceComponentsProps & CreateOrCloneProps > = (props) => { + const t = useTranslate(); + const { formProps, saveButtonProps, formLoading } = useForm(); return ( = () => { + const t = useTranslate(); + const { formProps, saveButtonProps } = useForm(); return ( = () => { = () => { = () => { = () => { + const t = useTranslate(); + // Load initial state const initialState = useInitialTableState("vendorList"); @@ -106,14 +111,14 @@ export const VendorList: React.FC = () => { setCurrent(1); }} > - Clear Filters + {t("buttons.clearFilters")} ({ key: column, - label: i18n.t(`vendors.fields.${column}`), + label: t(`vendor.fields.${column}`), })), selectedKeys: showColumns, selectable: true, @@ -127,7 +132,7 @@ export const VendorList: React.FC = () => { }} > {defaultButtons} @@ -150,48 +155,47 @@ export const VendorList: React.FC = () => { > {SortedColumn({ id: "id", - title: "Id", + i18ncat: "vendor", dataSource, tableState, })} {SortedColumn({ id: "name", - title: "Name", + i18ncat: "vendor", dataSource, tableState, })} {DateColumn({ id: "registered", - title: "Registered", + i18ncat: "vendor", dataSource, tableState, })} {SortedColumn({ id: "comment", - title: "Comment", + i18ncat: "vendor", dataSource, tableState, })} ( diff --git a/client/src/pages/vendors/show.tsx b/client/src/pages/vendors/show.tsx index 0d4a49e..c6778f4 100644 --- a/client/src/pages/vendors/show.tsx +++ b/client/src/pages/vendors/show.tsx @@ -1,5 +1,9 @@ import React from "react"; -import { IResourceComponentsProps, useShow } from "@refinedev/core"; +import { + IResourceComponentsProps, + useShow, + useTranslate, +} from "@refinedev/core"; import { Show, NumberField, DateField, TextField } from "@refinedev/antd"; import { Typography } from "antd"; import dayjs from "dayjs"; @@ -11,6 +15,8 @@ dayjs.extend(utc); const { Title } = Typography; export const VendorShow: React.FC = () => { + const t = useTranslate(); + const { queryResult } = useShow(); const { data, isLoading } = queryResult; @@ -18,17 +24,17 @@ export const VendorShow: React.FC = () => { return ( - Id + {t("vendor.fields.id")} - Registered + {t("vendor.fields.registered")} - Name + {t("vendor.fields.name")} - Comment + {t("vendor.fields.comment")} );