diff --git a/package-lock.json b/package-lock.json index 97f41e770..8bf2228c5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2862,12 +2862,6 @@ "hoist-non-react-statics": "^3.3.0" } }, - "@types/i18next": { - "version": "8.4.6", - "resolved": "https://registry.npmjs.org/@types/i18next/-/i18next-8.4.6.tgz", - "integrity": "sha512-ZiSCqW8j9/gQCYixz1nMhyCprSGh3rwdyX+FHAzEN+bMCmc7yCYjNutl6jvMYSxSIGeL0CLEXPM8Nlk2lE0t5w==", - "dev": true - }, "@types/invariant": { "version": "2.2.30", "resolved": "https://registry.npmjs.org/@types/invariant/-/invariant-2.2.30.tgz", @@ -10921,21 +10915,6 @@ "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.3.tgz", "integrity": "sha512-EcuixamT82oplpoJ2XU4pDtKGWQ7b00CD9f1ug9IaQ3p1bkHMiKCZ9ut9QDI6qsa6cpUuB+A/I+zLtdNK4n2DQ==" }, - "i18next": { - "version": "11.10.2", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-11.10.2.tgz", - "integrity": "sha512-1rowdX8PqrvsdFhYb3v0A/LlIHLQL1HTa4ia29IzhvNAg2fesNV7R1jXibWLmLQdz3FfTB8RuqSqDEjIawXruA==" - }, - "i18next-browser-languagedetector": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-2.2.4.tgz", - "integrity": "sha512-wPbtH18FdOuB245I8Bhma5/XSDdN/HpYlX+wga1eMy+slhaFQSnrWX6fp+aYSL2eEuj0RlfHeEVz6Fo/lxAj6A==" - }, - "i18next-xhr-backend": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/i18next-xhr-backend/-/i18next-xhr-backend-1.5.1.tgz", - "integrity": "sha512-9OLdC/9YxDvTFcgsH5t2BHCODHEotHCa6h7Ly0EUlUC7Y2GS09UeoHOGj3gWKQ3HCqXz8NlH4gOrK3NNc9vPuw==" - }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", diff --git a/package.json b/package.json index e82b33d49..44d166876 100644 --- a/package.json +++ b/package.json @@ -39,9 +39,6 @@ "fuzzaldrin": "^2.1.0", "graphql": "^14.4.2", "graphql-tag": "^2.10.1", - "i18next": "^11.10.2", - "i18next-browser-languagedetector": "^2.2.4", - "i18next-xhr-backend": "^1.5.1", "is-url": "^1.2.4", "jss": "^9.8.7", "keycode": "^2.2.0", @@ -88,7 +85,6 @@ "@types/draft-js": "^0.10.34", "@types/enzyme": "^3.10.2", "@types/fuzzaldrin": "^2.1.2", - "@types/i18next": "^8.4.6", "@types/jest": "^23.3.14", "@types/lodash-es": "^4.17.3", "@types/moment-timezone": "^0.5.12", diff --git a/src/attributes/components/AttributeList/AttributeList.tsx b/src/attributes/components/AttributeList/AttributeList.tsx index a889c653c..d3edbcf6e 100644 --- a/src/attributes/components/AttributeList/AttributeList.tsx +++ b/src/attributes/components/AttributeList/AttributeList.tsx @@ -6,15 +6,15 @@ import TableFooter from "@material-ui/core/TableFooter"; import TableRow from "@material-ui/core/TableRow"; import makeStyles from "@material-ui/styles/makeStyles"; import React from "react"; -import { FormattedMessage } from "react-intl"; +import { FormattedMessage, useIntl } from "react-intl"; import Checkbox from "@saleor/components/Checkbox"; import Skeleton from "@saleor/components/Skeleton"; import TableHead from "@saleor/components/TableHead"; import TablePagination from "@saleor/components/TablePagination"; +import { translateBoolean } from "@saleor/intl"; import { renderCollection } from "@saleor/misc"; import { ListActions, ListProps } from "@saleor/types"; -import { translateBoolean } from "@saleor/utils/i18n"; import { AttributeList_attributes_edges_node } from "../../types/AttributeList"; export interface AttributeListProps extends ListProps, ListActions { @@ -71,6 +71,7 @@ const AttributeList: React.StatelessComponent = ({ toolbar }) => { const classes = useStyles({}); + const intl = useIntl(); return ( @@ -153,21 +154,21 @@ const AttributeList: React.StatelessComponent = ({ {attribute ? ( - translateBoolean(attribute.visibleInStorefront) + translateBoolean(attribute.visibleInStorefront, intl) ) : ( )} {attribute ? ( - translateBoolean(attribute.filterableInDashboard) + translateBoolean(attribute.filterableInDashboard, intl) ) : ( )} {attribute ? ( - translateBoolean(attribute.filterableInStorefront) + translateBoolean(attribute.filterableInStorefront, intl) ) : ( )} diff --git a/src/auth/components/LoginPage/LoginPage.tsx b/src/auth/components/LoginPage/LoginPage.tsx index 644f59fa2..c361257d1 100644 --- a/src/auth/components/LoginPage/LoginPage.tsx +++ b/src/auth/components/LoginPage/LoginPage.tsx @@ -9,6 +9,7 @@ import TextField from "@material-ui/core/TextField"; import Typography from "@material-ui/core/Typography"; import React from "react"; import SVG from "react-inlinesvg"; +import { FormattedMessage, useIntl } from "react-intl"; import backgroundArt from "@assets/images/login-background.svg"; import saleorDarkLogo from "@assets/images/logo-dark.svg"; @@ -17,7 +18,7 @@ import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox"; import Form from "@saleor/components/Form"; import { FormSpacer } from "@saleor/components/FormSpacer"; import useTheme from "@saleor/hooks/useTheme"; -import i18n from "@saleor/i18n"; +import { commonMessages } from "@saleor/intl"; export interface FormData { email: string; @@ -117,6 +118,7 @@ export interface LoginCardProps extends WithStyles { const LoginCard = withStyles(styles, { name: "LoginCard" })( ({ classes, error, disableLoginButton, onSubmit }: LoginCardProps) => { const { isDark } = useTheme(); + const intl = useIntl(); return (
{error && (
- Please try again." - ) - }} - /> + + +
)} @@ -187,7 +189,10 @@ const LoginCard = withStyles(styles, { name: "LoginCard" })( type="submit" data-tc="submit" > - {i18n.t("Login")} + {/* diff --git a/src/categories/index.tsx b/src/categories/index.tsx index 01178b737..5c120659c 100644 --- a/src/categories/index.tsx +++ b/src/categories/index.tsx @@ -1,8 +1,10 @@ import { parse as parseQs } from "qs"; import React from "react"; +import { useIntl } from "react-intl"; import { Route, RouteComponentProps, Switch } from "react-router-dom"; + +import { sectionNames } from "@saleor/intl"; import { WindowTitle } from "../components/WindowTitle"; -import i18n from "../i18n"; import { categoryAddPath, categoryListPath, @@ -56,16 +58,20 @@ const CategoryList: React.StatelessComponent> = ({ return ; }; -const Component = () => ( - <> - - - - - - - - -); +const Component = () => { + const intl = useIntl(); + + return ( + <> + + + + + + + + + ); +}; export default Component; diff --git a/src/components/AppLayout/AppLayout.tsx b/src/components/AppLayout/AppLayout.tsx index e1d51819c..01651ebc4 100644 --- a/src/components/AppLayout/AppLayout.tsx +++ b/src/components/AppLayout/AppLayout.tsx @@ -17,7 +17,7 @@ import { import classNames from "classnames"; import React from "react"; import SVG from "react-inlinesvg"; -import { FormattedMessage } from "react-intl"; +import { FormattedMessage, useIntl } from "react-intl"; import { RouteComponentProps, withRouter } from "react-router"; import saleorDarkLogoSmall from "@assets/images/logo-dark-small.svg"; @@ -34,7 +34,7 @@ import AppActionContext from "./AppActionContext"; import AppHeaderContext from "./AppHeaderContext"; import { appLoaderHeight, drawerWidth, drawerWidthExpanded } from "./consts"; import MenuList from "./MenuList"; -import menuStructure from "./menuStructure"; +import createMenuStructure from "./menuStructure"; import ResponsiveDrawer from "./ResponsiveDrawer"; import ThemeSwitch from "./ThemeSwitch"; @@ -108,9 +108,7 @@ const styles = (theme: Theme) => }, isMenuSmallDark: { "&:hover": { - background: `linear-gradient(0deg, rgba(25, 195, 190, 0.1), rgba(25, 195, 190, 0.1)), ${ - theme.palette.background.paper - }` + background: `linear-gradient(0deg, rgba(25, 195, 190, 0.1), rgba(25, 195, 190, 0.1)), ${theme.palette.background.paper}` }, border: `solid 1px #252728`, transition: `background ${theme.transitions.duration.shorter}ms` @@ -277,6 +275,9 @@ const AppLayout = withStyles(styles, { const anchor = React.useRef(); const { logout, user } = useUser(); const navigate = useNavigator(); + const intl = useIntl(); + + const menuStructure = createMenuStructure(intl); const handleLogout = () => { close(); diff --git a/src/components/AppLayout/menuStructure.ts b/src/components/AppLayout/menuStructure.ts index 1c13c5dfa..3e1c46da2 100644 --- a/src/components/AppLayout/menuStructure.ts +++ b/src/components/AppLayout/menuStructure.ts @@ -2,7 +2,6 @@ import { categoryListUrl } from "../../categories/urls"; import { collectionListUrl } from "../../collections/urls"; import { customerListUrl } from "../../customers/urls"; import { saleListUrl, voucherListUrl } from "../../discounts/urls"; -import i18n from "../../i18n"; import { orderDraftListUrl, orderListUrl } from "../../orders/urls"; import { productListUrl } from "../../products/urls"; import { languageListUrl } from "../../translations/urls"; @@ -14,6 +13,8 @@ import discountsIcon from "@assets/images/menu-discounts-icon.svg"; import homeIcon from "@assets/images/menu-home-icon.svg"; import ordersIcon from "@assets/images/menu-orders-icon.svg"; import translationIcon from "@assets/images/menu-translation-icon.svg"; +import { commonMessages, sectionNames } from "@saleor/intl"; +import { IntlShape } from "react-intl"; export interface IMenuItem { ariaLabel: string; @@ -24,88 +25,91 @@ export interface IMenuItem { url?: string; } -const menuStructure: IMenuItem[] = [ - { - ariaLabel: "home", - icon: homeIcon, - label: i18n.t("Home", { context: "Menu label" }), - url: "/" - }, - { - ariaLabel: "catalogue", - children: [ - { - ariaLabel: "products", - label: i18n.t("Products", { context: "Menu label" }), - url: productListUrl() - }, - { - ariaLabel: "categories", - label: i18n.t("Categories", { context: "Menu label" }), - url: categoryListUrl() - }, - { - ariaLabel: "collections", - label: i18n.t("Collections", { context: "Menu label" }), - url: collectionListUrl() - } - ], - icon: catalogIcon, - label: i18n.t("Catalog", { context: "Menu label" }), - permission: PermissionEnum.MANAGE_PRODUCTS - }, - { - ariaLabel: "orders", - children: [ - { - ariaLabel: "orders", - label: i18n.t("Orders", { context: "Menu label" }), - permission: PermissionEnum.MANAGE_ORDERS, - url: orderListUrl() - }, - { - ariaLabel: "order drafts", - label: i18n.t("Drafts", { context: "Menu label" }), - permission: PermissionEnum.MANAGE_ORDERS, - url: orderDraftListUrl() - } - ], - icon: ordersIcon, - label: i18n.t("Orders", { context: "Menu label" }), - permission: PermissionEnum.MANAGE_ORDERS - }, - { - ariaLabel: "customers", - icon: customerIcon, - label: i18n.t("Customers", { context: "Menu label" }), - permission: PermissionEnum.MANAGE_USERS, - url: customerListUrl() - }, +function createMenuStructure(intl: IntlShape): IMenuItem[] { + return [ + { + ariaLabel: "home", + icon: homeIcon, + label: intl.formatMessage(sectionNames.home), + url: "/" + }, + { + ariaLabel: "catalogue", + children: [ + { + ariaLabel: "products", + label: intl.formatMessage(sectionNames.products), + url: productListUrl() + }, + { + ariaLabel: "categories", + label: intl.formatMessage(sectionNames.categories), + url: categoryListUrl() + }, + { + ariaLabel: "collections", + label: intl.formatMessage(sectionNames.collections), + url: collectionListUrl() + } + ], + icon: catalogIcon, + label: intl.formatMessage(commonMessages.catalog), + permission: PermissionEnum.MANAGE_PRODUCTS + }, + { + ariaLabel: "orders", + children: [ + { + ariaLabel: "orders", + label: intl.formatMessage(sectionNames.orders), + permission: PermissionEnum.MANAGE_ORDERS, + url: orderListUrl() + }, + { + ariaLabel: "order drafts", + label: intl.formatMessage(commonMessages.drafts), + permission: PermissionEnum.MANAGE_ORDERS, + url: orderDraftListUrl() + } + ], + icon: ordersIcon, + label: intl.formatMessage(sectionNames.orders), + permission: PermissionEnum.MANAGE_ORDERS + }, + { + ariaLabel: "customers", + icon: customerIcon, + label: intl.formatMessage(sectionNames.customers), + permission: PermissionEnum.MANAGE_USERS, + url: customerListUrl() + }, - { - ariaLabel: "discounts", - children: [ - { - ariaLabel: "sales", - label: i18n.t("Sales", { context: "Menu label" }), - url: saleListUrl() - }, - { - ariaLabel: "vouchers", - label: i18n.t("Vouchers", { context: "Menu label" }), - url: voucherListUrl() - } - ], - icon: discountsIcon, - label: i18n.t("Discounts", { context: "Menu label" }), - permission: PermissionEnum.MANAGE_DISCOUNTS - }, - { - ariaLabel: "translations", - icon: translationIcon, - label: i18n.t("Translations", { context: "Menu label" }), - permission: PermissionEnum.MANAGE_TRANSLATIONS, - url: languageListUrl - } -]; -export default menuStructure; + { + ariaLabel: "discounts", + children: [ + { + ariaLabel: "sales", + label: intl.formatMessage(sectionNames.sales), + url: saleListUrl() + }, + { + ariaLabel: "vouchers", + label: intl.formatMessage(sectionNames.vouchers), + url: voucherListUrl() + } + ], + icon: discountsIcon, + label: intl.formatMessage(commonMessages.discounts), + permission: PermissionEnum.MANAGE_DISCOUNTS + }, + { + ariaLabel: "translations", + icon: translationIcon, + label: intl.formatMessage(sectionNames.translations), + permission: PermissionEnum.MANAGE_TRANSLATIONS, + url: languageListUrl + } + ]; +} + +export default createMenuStructure; diff --git a/src/components/TableFilter/index.ts b/src/components/TableFilter/index.ts index d2d907900..5cb8830ab 100644 --- a/src/components/TableFilter/index.ts +++ b/src/components/TableFilter/index.ts @@ -1,5 +1,4 @@ export { default } from "./FilterTabs"; -export { Filter } from "./FilterChips"; export * from "./FilterTabs"; export * from "./FilterTab"; export * from "./FilterChips"; diff --git a/src/customers/components/CustomerOrders/CustomerOrders.tsx b/src/customers/components/CustomerOrders/CustomerOrders.tsx index 931b23e1e..1fc5e0d26 100644 --- a/src/customers/components/CustomerOrders/CustomerOrders.tsx +++ b/src/customers/components/CustomerOrders/CustomerOrders.tsx @@ -44,7 +44,7 @@ const CustomerOrders = withStyles(styles, { name: "CustomerOrders" })( const orderList = orders ? orders.map(order => ({ ...order, - paymentStatus: transformPaymentStatus(order.paymentStatus) + paymentStatus: transformPaymentStatus(order.paymentStatus, intl) })) : undefined; return ( diff --git a/src/discounts/components/VoucherSummary/VoucherSummary.tsx b/src/discounts/components/VoucherSummary/VoucherSummary.tsx index 9ddf81889..50cbba67d 100644 --- a/src/discounts/components/VoucherSummary/VoucherSummary.tsx +++ b/src/discounts/components/VoucherSummary/VoucherSummary.tsx @@ -29,7 +29,7 @@ const VoucherSummary: React.StatelessComponent = ({ }) => { const intl = useIntl(); - const translatedVoucherTypes = translateVoucherTypes(); + const translatedVoucherTypes = translateVoucherTypes(intl); return ( diff --git a/src/discounts/components/VoucherValue/VoucherValue.tsx b/src/discounts/components/VoucherValue/VoucherValue.tsx index d6c1ade56..224d41153 100644 --- a/src/discounts/components/VoucherValue/VoucherValue.tsx +++ b/src/discounts/components/VoucherValue/VoucherValue.tsx @@ -39,7 +39,7 @@ const VoucherValue = ({ }: VoucherValueProps) => { const intl = useIntl(); - const translatedVoucherTypes = translateVoucherTypes(); + const translatedVoucherTypes = translateVoucherTypes(intl); const voucherTypeChoices = Object.values(VoucherType).map(type => ({ label: translatedVoucherTypes[type], value: type diff --git a/src/discounts/translations.ts b/src/discounts/translations.ts index 5d1b91f4e..323b45af1 100644 --- a/src/discounts/translations.ts +++ b/src/discounts/translations.ts @@ -1,8 +1,24 @@ -import i18n from "../i18n"; +import { defineMessages, IntlShape } from "react-intl"; + import { VoucherTypeEnum } from "../types/globalTypes"; -export const translateVoucherTypes = () => ({ - [VoucherTypeEnum.SHIPPING]: i18n.t("Shipment"), - [VoucherTypeEnum.ENTIRE_ORDER]: i18n.t("Entire order"), - [VoucherTypeEnum.SPECIFIC_PRODUCT]: i18n.t("Specific Products") +const messages = defineMessages({ + order: { + defaultMessage: "Entire order", + description: "voucher discount" + }, + products: { + defaultMessage: "Specific products", + description: "voucher discount" + }, + shipment: { + defaultMessage: "Shipment", + description: "voucher discount" + } +}); + +export const translateVoucherTypes = (intl: IntlShape) => ({ + [VoucherTypeEnum.SHIPPING]: intl.formatMessage(messages.shipment), + [VoucherTypeEnum.ENTIRE_ORDER]: intl.formatMessage(messages.order), + [VoucherTypeEnum.SPECIFIC_PRODUCT]: intl.formatMessage(messages.products) }); diff --git a/src/home/components/HomeActivityCard/HomeActivityCard.tsx b/src/home/components/HomeActivityCard/HomeActivityCard.tsx index 880106a6f..252ee2317 100644 --- a/src/home/components/HomeActivityCard/HomeActivityCard.tsx +++ b/src/home/components/HomeActivityCard/HomeActivityCard.tsx @@ -50,7 +50,9 @@ const HomeActivityCard = withStyles(styles, { name: "HomeActivityCard" })( {activity ? ( {getActivityMessage(activity)} + + {getActivityMessage(activity, intl)} + } secondary={} /> diff --git a/src/home/components/HomeActivityCard/activityMessages.ts b/src/home/components/HomeActivityCard/activityMessages.ts index b195ccdc2..d89cb350c 100644 --- a/src/home/components/HomeActivityCard/activityMessages.ts +++ b/src/home/components/HomeActivityCard/activityMessages.ts @@ -1,25 +1,38 @@ -import i18n from "../../../i18n"; +import { defineMessages, IntlShape } from "react-intl"; + import { OrderEventsEnum } from "../../../types/globalTypes"; import { Home_activities_edges_node } from "../../types/Home"; -export const getActivityMessage = (activity: Home_activities_edges_node) => { +const messages = defineMessages({ + draft: { + defaultMessage: "Order #{orderId} was placed from draft by {userEmail}" + }, + paid: { + defaultMessage: "Order #{orderId} was fully paid" + }, + placed: { + defaultMessage: "Order #{orderId} was placed" + } +}); + +export const getActivityMessage = ( + activity: Home_activities_edges_node, + intl: IntlShape +) => { switch (activity.type) { case OrderEventsEnum.ORDER_FULLY_PAID: - return i18n.t("Order #{{ orderId }} was fully paid", { + return intl.formatMessage(messages.paid, { orderId: activity.orderNumber }); case OrderEventsEnum.PLACED: - return i18n.t("Order #{{ orderId }} was placed", { + return intl.formatMessage(messages.placed, { orderId: activity.orderNumber }); case OrderEventsEnum.PLACED_FROM_DRAFT: - return i18n.t( - "Order #{{ orderId }} was placed from draft by {{ user }}", - { - orderId: activity.orderNumber, - user: activity.user.email - } - ); + return intl.formatMessage(messages.draft, { + orderId: activity.orderNumber, + userEmail: activity.user.email + }); default: return activity.message; } diff --git a/src/i18n.ts b/src/i18n.ts deleted file mode 100644 index 91d59c28c..000000000 --- a/src/i18n.ts +++ /dev/null @@ -1,18 +0,0 @@ -import i18n from "i18next"; -import LanguageDetector from "i18next-browser-languagedetector"; -import XHR from "i18next-xhr-backend"; - -i18n.use(XHR); -i18n.use(LanguageDetector); -i18n.init({ - defaultNS: "dashboard", - fallbackLng: "en", - interpolation: { - escapeValue: false - }, - keySeparator: false, - ns: ["dashboard"], - nsSeparator: false -}); - -export default i18n; diff --git a/src/intl.ts b/src/intl.ts index 38883a5fd..41d827359 100644 --- a/src/intl.ts +++ b/src/intl.ts @@ -1,15 +1,24 @@ -import { defineMessages } from "react-intl"; +import { defineMessages, IntlShape } from "react-intl"; export const commonMessages = defineMessages({ availability: { defaultMessage: "Availability" }, + catalog: { + defaultMessage: "Catalog" + }, dashboard: { defaultMessage: "Dashboard" }, description: { defaultMessage: "Description" }, + discounts: { + defaultMessage: "Discounts" + }, + drafts: { + defaultMessage: "Drafts" + }, email: { defaultMessage: "E-mail Address" }, @@ -28,6 +37,9 @@ export const commonMessages = defineMessages({ lastName: { defaultMessage: "Last Name" }, + no: { + defaultMessage: "No" + }, optionalField: { defaultMessage: "Optional", description: "field is optional" @@ -50,6 +62,9 @@ export const commonMessages = defineMessages({ uploadImage: { defaultMessage: "Upload image", description: "button" + }, + yes: { + defaultMessage: "Yes" } }); @@ -121,6 +136,10 @@ export const sectionNames = defineMessages({ defaultMessage: "Draft Orders", description: "draft orders section name" }, + home: { + defaultMessage: "Home", + description: "home section name" + }, navigation: { defaultMessage: "Navigation", description: "navigation section name" @@ -170,3 +189,9 @@ export const sectionNames = defineMessages({ description: "vouchers section name" } }); + +export function translateBoolean(value: boolean, intl: IntlShape): string { + return value + ? intl.formatMessage(commonMessages.yes) + : intl.formatMessage(commonMessages.no); +} diff --git a/src/misc.ts b/src/misc.ts index 1cfbf99cb..122e77459 100644 --- a/src/misc.ts +++ b/src/misc.ts @@ -2,10 +2,10 @@ import moment from "moment-timezone"; import { MutationFunction, MutationResult } from "react-apollo"; import urlJoin from "url-join"; +import { defineMessages, IntlShape } from "react-intl"; import { ConfirmButtonTransitionState } from "./components/ConfirmButton/ConfirmButton"; import { APP_MOUNT_URI } from "./config"; import { AddressType } from "./customers/types"; -import i18n from "./i18n"; import { PartialMutationProviderOutput, UserError } from "./types"; import { AuthorizationKeyType, @@ -26,7 +26,7 @@ export type RequireOnlyOne = Pick< > & { [K in Keys]-?: Required> & - Partial, undefined>> + Partial, undefined>>; }[Keys]; export function renderCollection( @@ -57,33 +57,109 @@ export function decimal(value: string | number) { export const removeDoubleSlashes = (url: string) => url.replace(/([^:]\/)\/+/g, "$1"); -export const transformPaymentStatus = (status: string) => { +const paymentStatusMessages = defineMessages({ + paid: { + defaultMessage: "Fully paid", + description: "payment status" + }, + partiallyPaid: { + defaultMessage: "Partially paid", + description: "payment status" + }, + partiallyRefunded: { + defaultMessage: "Partially refunded", + description: "payment status" + }, + refunded: { + defaultMessage: "Fully refunded", + description: "payment status" + }, + unpaid: { + defaultMessage: "Unpaid", + description: "payment status" + } +}); + +export const transformPaymentStatus = (status: string, intl: IntlShape) => { switch (status) { case PaymentChargeStatusEnum.PARTIALLY_CHARGED: - return { localized: i18n.t("Partially paid"), status: "error" }; + return { + localized: intl.formatMessage(paymentStatusMessages.partiallyPaid), + status: "error" + }; case PaymentChargeStatusEnum.FULLY_CHARGED: - return { localized: i18n.t("Fully paid"), status: "success" }; + return { + localized: intl.formatMessage(paymentStatusMessages.paid), + status: "success" + }; case PaymentChargeStatusEnum.PARTIALLY_REFUNDED: - return { localized: i18n.t("Partially refunded"), status: "error" }; + return { + localized: intl.formatMessage(paymentStatusMessages.partiallyRefunded), + status: "error" + }; case PaymentChargeStatusEnum.FULLY_REFUNDED: - return { localized: i18n.t("Fully refunded"), status: "success" }; + return { + localized: intl.formatMessage(paymentStatusMessages.refunded), + status: "success" + }; default: - return { localized: i18n.t("Unpaid"), status: "error" }; + return { + localized: intl.formatMessage(paymentStatusMessages.unpaid), + status: "error" + }; } }; -export const transformOrderStatus = (status: string) => { +const orderStatusMessages = defineMessages({ + cancelled: { + defaultMessage: "Cancelled", + description: "order status" + }, + draft: { + defaultMessage: "Draft", + description: "order status" + }, + fulfilled: { + defaultMessage: "Fulfilled", + description: "order status" + }, + partiallyFulfilled: { + defaultMessage: "Partially fulfilled", + description: "order status" + }, + unfulfilled: { + defaultMessage: "Unfulfilled", + description: "order status" + } +}); + +export const transformOrderStatus = (status: string, intl: IntlShape) => { switch (status) { case OrderStatus.FULFILLED: - return { localized: i18n.t("Fulfilled"), status: "success" }; + return { + localized: intl.formatMessage(orderStatusMessages.fulfilled), + status: "success" + }; case OrderStatus.PARTIALLY_FULFILLED: - return { localized: i18n.t("Partially fulfilled"), status: "neutral" }; + return { + localized: intl.formatMessage(orderStatusMessages.partiallyFulfilled), + status: "neutral" + }; case OrderStatus.UNFULFILLED: - return { localized: i18n.t("Unfulfilled"), status: "error" }; + return { + localized: intl.formatMessage(orderStatusMessages.unfulfilled), + status: "error" + }; case OrderStatus.CANCELED: - return { localized: i18n.t("Cancelled"), status: "error" }; + return { + localized: intl.formatMessage(orderStatusMessages.cancelled), + status: "error" + }; case OrderStatus.DRAFT: - return { localized: i18n.t("Draft"), status: "error" }; + return { + localized: intl.formatMessage(orderStatusMessages.draft), + status: "error" + }; } return { localized: status, @@ -105,44 +181,163 @@ export const transformAddressToForm = (data: AddressType) => ({ streetAddress2: maybe(() => data.streetAddress2, "") }); -export const translatedTaxRates = () => ({ - [TaxRateType.ACCOMMODATION]: i18n.t("Accommodation"), - [TaxRateType.ADMISSION_TO_CULTURAL_EVENTS]: i18n.t( - "Admission to cultural events" - ), - [TaxRateType.ADMISSION_TO_ENTERTAINMENT_EVENTS]: i18n.t( - "Admission to entertainment events" - ), - [TaxRateType.ADMISSION_TO_SPORTING_EVENTS]: i18n.t( - "Admission to sporting events" - ), - [TaxRateType.ADVERTISING]: i18n.t("Advertising"), - [TaxRateType.AGRICULTURAL_SUPPLIES]: i18n.t("Agricultural supplies"), - [TaxRateType.BABY_FOODSTUFFS]: i18n.t("Baby foodstuffs"), - [TaxRateType.BIKES]: i18n.t("Bikes"), - [TaxRateType.BOOKS]: i18n.t("Books"), - [TaxRateType.CHILDRENS_CLOTHING]: i18n.t("Children's clothing"), - [TaxRateType.DOMESTIC_FUEL]: i18n.t("Domestic fuel"), - [TaxRateType.DOMESTIC_SERVICES]: i18n.t("Domestic services"), - [TaxRateType.E_BOOKS]: i18n.t("E-books"), - [TaxRateType.FOODSTUFFS]: i18n.t("Foodstuffs"), - [TaxRateType.HOTELS]: i18n.t("Hotels"), - [TaxRateType.MEDICAL]: i18n.t("Medical"), - [TaxRateType.NEWSPAPERS]: i18n.t("Newspapers"), - [TaxRateType.PASSENGER_TRANSPORT]: i18n.t("Passenger transport"), - [TaxRateType.PHARMACEUTICALS]: i18n.t("Pharmaceuticals"), - [TaxRateType.PROPERTY_RENOVATIONS]: i18n.t("Property renovations"), - [TaxRateType.RESTAURANTS]: i18n.t("Restaurants"), - [TaxRateType.SOCIAL_HOUSING]: i18n.t("Social housing"), - [TaxRateType.STANDARD]: i18n.t("Standard"), - [TaxRateType.WATER]: i18n.t("Water") +const taxRatesMessages = defineMessages({ + accommodation: { + defaultMessage: "Accommodation", + description: "tax rate" + }, + admissionToCulturalEvents: { + defaultMessage: "Admission to cultural events", + description: "tax rate" + }, + admissionToEntertainmentEvents: { + defaultMessage: "Admission to entertainment events", + description: "tax rate" + }, + admissionToSportingEvents: { + defaultMessage: "Admission to sporting events", + description: "tax rate" + }, + advertising: { + defaultMessage: "Advertising", + description: "tax rate" + }, + agriculturalSupplies: { + defaultMessage: "Agricultural supplies", + description: "tax rate" + }, + babyFoodstuffs: { + defaultMessage: "Baby foodstuffs", + description: "tax rate" + }, + bikes: { + defaultMessage: "Bikes", + description: "tax rate" + }, + books: { + defaultMessage: "Books", + description: "tax rate" + }, + childrensClothing: { + defaultMessage: "Children's clothing", + description: "tax rate" + }, + domesticFuel: { + defaultMessage: "Domestic fuel", + description: "tax rate" + }, + domesticServices: { + defaultMessage: "Domestic services", + description: "tax rate" + }, + ebooks: { + defaultMessage: "E-books", + description: "tax rate" + }, + foodstuffs: { + defaultMessage: "Foodstuffs", + description: "tax rate" + }, + hotels: { + defaultMessage: "Hotels", + description: "tax rate" + }, + medical: { + defaultMessage: "Medical", + description: "tax rate" + }, + newspapers: { + defaultMessage: "Newspapers", + description: "tax rate" + }, + passengerTransport: { + defaultMessage: "Passenger transport", + description: "tax rate" + }, + pharmaceuticals: { + defaultMessage: "Pharmaceuticals", + description: "tax rate" + }, + propertyRenovations: { + defaultMessage: "Property renovations", + description: "tax rate" + }, + restaurants: { + defaultMessage: "Restaurants", + description: "tax rate" + }, + socialHousing: { + defaultMessage: "Social housing", + description: "tax rate" + }, + standard: { + defaultMessage: "Standard", + description: "tax rate" + }, + water: { + defaultMessage: "Water", + description: "tax rate" + } }); -export const translatedAuthorizationKeyTypes = () => ({ - [AuthorizationKeyType.FACEBOOK]: i18n.t("Facebook"), - [AuthorizationKeyType.GOOGLE_OAUTH2]: i18n.t("Google OAuth2") +export const translatedTaxRates = (intl: IntlShape) => ({ + [TaxRateType.ACCOMMODATION]: intl.formatMessage( + taxRatesMessages.accommodation + ), + [TaxRateType.ADMISSION_TO_CULTURAL_EVENTS]: intl.formatMessage( + taxRatesMessages.admissionToCulturalEvents + ), + [TaxRateType.ADMISSION_TO_ENTERTAINMENT_EVENTS]: intl.formatMessage( + taxRatesMessages.admissionToEntertainmentEvents + ), + [TaxRateType.ADMISSION_TO_SPORTING_EVENTS]: intl.formatMessage( + taxRatesMessages.admissionToSportingEvents + ), + [TaxRateType.ADVERTISING]: intl.formatMessage(taxRatesMessages.advertising), + [TaxRateType.AGRICULTURAL_SUPPLIES]: intl.formatMessage( + taxRatesMessages.agriculturalSupplies + ), + [TaxRateType.BABY_FOODSTUFFS]: intl.formatMessage( + taxRatesMessages.babyFoodstuffs + ), + [TaxRateType.BIKES]: intl.formatMessage(taxRatesMessages.bikes), + [TaxRateType.BOOKS]: intl.formatMessage(taxRatesMessages.books), + [TaxRateType.CHILDRENS_CLOTHING]: intl.formatMessage( + taxRatesMessages.childrensClothing + ), + [TaxRateType.DOMESTIC_FUEL]: intl.formatMessage( + taxRatesMessages.domesticFuel + ), + [TaxRateType.DOMESTIC_SERVICES]: intl.formatMessage( + taxRatesMessages.domesticServices + ), + [TaxRateType.E_BOOKS]: intl.formatMessage(taxRatesMessages.ebooks), + [TaxRateType.FOODSTUFFS]: intl.formatMessage(taxRatesMessages.foodstuffs), + [TaxRateType.HOTELS]: intl.formatMessage(taxRatesMessages.hotels), + [TaxRateType.MEDICAL]: intl.formatMessage(taxRatesMessages.medical), + [TaxRateType.NEWSPAPERS]: intl.formatMessage(taxRatesMessages.newspapers), + [TaxRateType.PASSENGER_TRANSPORT]: intl.formatMessage( + taxRatesMessages.passengerTransport + ), + [TaxRateType.PHARMACEUTICALS]: intl.formatMessage( + taxRatesMessages.pharmaceuticals + ), + [TaxRateType.PROPERTY_RENOVATIONS]: intl.formatMessage( + taxRatesMessages.propertyRenovations + ), + [TaxRateType.RESTAURANTS]: intl.formatMessage(taxRatesMessages.restaurants), + [TaxRateType.SOCIAL_HOUSING]: intl.formatMessage( + taxRatesMessages.socialHousing + ), + [TaxRateType.STANDARD]: intl.formatMessage(taxRatesMessages.standard), + [TaxRateType.WATER]: intl.formatMessage(taxRatesMessages.water) }); +export const authorizationKeyTypes = { + [AuthorizationKeyType.FACEBOOK]: "Facebook", + [AuthorizationKeyType.GOOGLE_OAUTH2]: "Google OAuth2" +}; + export function maybe(exp: () => T): T | undefined; export function maybe(exp: () => T, d: T): T; export function maybe(exp: any, d?: any) { diff --git a/src/mutations.tsx b/src/mutations.tsx index 84bf465b1..f6611b837 100644 --- a/src/mutations.tsx +++ b/src/mutations.tsx @@ -2,9 +2,9 @@ import { ApolloError, MutationUpdaterFn } from "apollo-client"; import { DocumentNode } from "graphql"; import React from "react"; import { Mutation, MutationFunction, MutationResult } from "react-apollo"; +import { useIntl } from "react-intl"; import useNotifier from "./hooks/useNotifier"; -import i18n from "./i18n"; export interface TypedMutationInnerProps { children: ( @@ -23,6 +23,7 @@ export function TypedMutation( ) { return (props: TypedMutationInnerProps) => { const notify = useNotifier(); + const intl = useIntl(); const { children, onCompleted, onError, variables } = props; return ( @@ -30,9 +31,15 @@ export function TypedMutation( mutation={mutation} onCompleted={onCompleted} onError={err => { - const msg = i18n.t("Something went wrong: {{ message }}", { - message: err.message - }); + const msg = intl.formatMessage( + { + defaultMessage: "Something went wrong. {errorMessage}", + description: "error message" + }, + { + errorMessage: err.message + } + ); notify({ text: msg }); if (onError) { onError(err); diff --git a/src/navigation/views/MenuDetails/index.tsx b/src/navigation/views/MenuDetails/index.tsx index fa6b7f866..7f12ba8e3 100644 --- a/src/navigation/views/MenuDetails/index.tsx +++ b/src/navigation/views/MenuDetails/index.tsx @@ -136,13 +136,13 @@ const MenuDetails: React.FC = ({ id, params }) => { return ( - handleDelete(data, navigate, notify) + handleDelete(data, navigate, notify, intl) } > {(menuDelete, menuDeleteOpts) => ( - handleUpdate(data, notify, refetch) + handleUpdate(data, notify, refetch, intl) } > {(menuUpdate, menuUpdateOpts) => { @@ -263,7 +263,12 @@ const MenuDetails: React.FC = ({ id, params }) => { - handleItemCreate(data, notify, closeModal) + handleItemCreate( + data, + notify, + closeModal, + intl + ) } > {(menuItemCreate, menuItemCreateOpts) => { @@ -323,7 +328,8 @@ const MenuDetails: React.FC = ({ id, params }) => { data, id, navigate, - notify + notify, + intl ) } > diff --git a/src/navigation/views/MenuDetails/successHandlers.ts b/src/navigation/views/MenuDetails/successHandlers.ts index 995211a6f..1dce13307 100644 --- a/src/navigation/views/MenuDetails/successHandlers.ts +++ b/src/navigation/views/MenuDetails/successHandlers.ts @@ -1,6 +1,8 @@ +import { IntlShape } from "react-intl"; + +import { commonMessages } from "@saleor/intl"; import { UseNavigatorResult } from "../../../hooks/useNavigator"; import { UseNotifierResult } from "../../../hooks/useNotifier"; -import i18n from "../../../i18n"; import { MenuDelete } from "../../types/MenuDelete"; import { MenuItemCreate } from "../../types/MenuItemCreate"; import { MenuItemUpdate } from "../../types/MenuItemUpdate"; @@ -10,14 +12,13 @@ import { menuListUrl, menuUrl } from "../../urls"; export function handleItemCreate( data: MenuItemCreate, notify: UseNotifierResult, - closeModal: () => void + closeModal: () => void, + intl: IntlShape ) { if (data.menuItemCreate.errors.length === 0) { closeModal(); notify({ - text: i18n.t("Created menu item", { - context: "notification" - }) + text: intl.formatMessage(commonMessages.savedChanges) }); } } @@ -26,13 +27,12 @@ export function handleItemUpdate( data: MenuItemUpdate, id: string, navigate: UseNavigatorResult, - notify: UseNotifierResult + notify: UseNotifierResult, + intl: IntlShape ) { if (data.menuItemUpdate.errors.length === 0) { notify({ - text: i18n.t("Updated menu item", { - context: "notification" - }) + text: intl.formatMessage(commonMessages.savedChanges) }); navigate( menuUrl(id, { @@ -46,13 +46,12 @@ export function handleItemUpdate( export function handleDelete( data: MenuDelete, navigate: UseNavigatorResult, - notify: UseNotifierResult + notify: UseNotifierResult, + intl: IntlShape ) { if (data.menuDelete.errors.length === 0) { notify({ - text: i18n.t("Removed menu", { - context: "notification" - }) + text: intl.formatMessage(commonMessages.savedChanges) }); navigate(menuListUrl(), true); } @@ -61,7 +60,8 @@ export function handleDelete( export function handleUpdate( data: MenuUpdate, notify: UseNotifierResult, - refetch: () => void + refetch: () => void, + intl: IntlShape ) { if ( data.menuItemBulkDelete.errors.length === 0 && @@ -69,9 +69,7 @@ export function handleUpdate( data.menuUpdate.errors.length === 0 ) { notify({ - text: i18n.t("Updated menu", { - context: "notification" - }) + text: intl.formatMessage(commonMessages.savedChanges) }); refetch(); } diff --git a/src/orders/components/OrderDraftList/OrderDraftList.tsx b/src/orders/components/OrderDraftList/OrderDraftList.tsx index 969c57d4a..70946e36d 100644 --- a/src/orders/components/OrderDraftList/OrderDraftList.tsx +++ b/src/orders/components/OrderDraftList/OrderDraftList.tsx @@ -10,7 +10,7 @@ import TableCell from "@material-ui/core/TableCell"; import TableFooter from "@material-ui/core/TableFooter"; import TableRow from "@material-ui/core/TableRow"; import React from "react"; -import { FormattedMessage } from "react-intl"; +import { FormattedMessage, useIntl } from "react-intl"; import Checkbox from "@saleor/components/Checkbox"; import { DateTime } from "@saleor/components/Date"; @@ -78,11 +78,13 @@ export const OrderDraftList = withStyles(styles, { name: "OrderDraftList" })( toggleAll, toolbar }: OrderDraftListProps) => { + const intl = useIntl(); + const orderDraftList = orders ? orders.map(order => ({ ...order, - paymentStatus: transformPaymentStatus(order.paymentStatus), - status: transformOrderStatus(order.status) + paymentStatus: transformPaymentStatus(order.paymentStatus, intl), + status: transformOrderStatus(order.status, intl) })) : undefined; diff --git a/src/orders/components/OrderList/OrderList.tsx b/src/orders/components/OrderList/OrderList.tsx index 2a38c947c..fac1f596d 100644 --- a/src/orders/components/OrderList/OrderList.tsx +++ b/src/orders/components/OrderList/OrderList.tsx @@ -10,7 +10,7 @@ import TableCell from "@material-ui/core/TableCell"; import TableFooter from "@material-ui/core/TableFooter"; import TableRow from "@material-ui/core/TableRow"; import React from "react"; -import { FormattedMessage } from "react-intl"; +import { FormattedMessage, useIntl } from "react-intl"; import Checkbox from "@saleor/components/Checkbox"; import { DateTime } from "@saleor/components/Date"; @@ -85,11 +85,13 @@ export const OrderList = withStyles(styles, { name: "OrderList" })( toggleAll, toolbar }: OrderListProps) => { + const intl = useIntl(); + const orderList = orders ? orders.map(order => ({ ...order, - paymentStatus: transformPaymentStatus(order.paymentStatus), - status: transformOrderStatus(order.status) + paymentStatus: transformPaymentStatus(order.paymentStatus, intl), + status: transformOrderStatus(order.status, intl) })) : undefined; return ( diff --git a/src/orders/components/OrderPayment/OrderPayment.tsx b/src/orders/components/OrderPayment/OrderPayment.tsx index 34d1617b8..80fc40273 100644 --- a/src/orders/components/OrderPayment/OrderPayment.tsx +++ b/src/orders/components/OrderPayment/OrderPayment.tsx @@ -64,7 +64,10 @@ const OrderPayment = withStyles(styles, { name: "OrderPayment" })( const canMarkAsPaid = maybe(() => order.actions, []).includes( OrderAction.MARK_AS_PAID ); - const payment = transformPaymentStatus(maybe(() => order.paymentStatus)); + const payment = transformPaymentStatus( + maybe(() => order.paymentStatus), + intl + ); return ( ({ }); export const flatOrders = orders.map(order => ({ ...order, - orderStatus: transformOrderStatus(order.status), - paymentStatus: transformPaymentStatus(order.paymentStatus) + orderStatus: transformOrderStatus(order.status, { + formatMessage: (message: MessageDescriptor) => message.defaultMessage + } as any), + paymentStatus: transformPaymentStatus(order.paymentStatus, { + formatMessage: (message: MessageDescriptor) => message.defaultMessage + } as any) })); export const variants = [ { id: "p1", name: "Product 1: variant 1", sku: "12345", stockQuantity: 3 }, diff --git a/src/orders/views/OrderList/OrderList.tsx b/src/orders/views/OrderList/OrderList.tsx index 6c1ef2c32..a8fe9c47b 100644 --- a/src/orders/views/OrderList/OrderList.tsx +++ b/src/orders/views/OrderList/OrderList.tsx @@ -199,7 +199,8 @@ export const OrderList: React.StatelessComponent = ({ { formatDate }, - changeFilterField + changeFilterField, + intl )} currentTab={currentTab} disabled={loading} diff --git a/src/orders/views/OrderList/filters.ts b/src/orders/views/OrderList/filters.ts index 39bc73341..980a1119f 100644 --- a/src/orders/views/OrderList/filters.ts +++ b/src/orders/views/OrderList/filters.ts @@ -1,6 +1,7 @@ +import { defineMessages, IntlShape } from "react-intl"; + import { FilterContentSubmitData } from "../../../components/Filter"; import { Filter } from "../../../components/TableFilter"; -import i18n from "../../../i18n"; import { OrderFilterInput, OrderStatusFilter @@ -18,16 +19,43 @@ import { export const ORDER_FILTERS_KEY = "orderFilters"; -function getStatusLabel(status: string): string { +const filterMessages = defineMessages({ + dateFrom: { + defaultMessage: "Date from {date}", + description: "filter by date" + }, + dateIs: { + defaultMessage: "Date is {date}", + description: "filter by date" + }, + dateTo: { + defaultMessage: "Date to {date}", + description: "filter by date" + }, + fulfilled: { + defaultMessage: "Fulfilled", + description: "order status" + }, + partiallyFulfilled: { + defaultMessage: "Partially Fulfilled", + description: "order status" + }, + unfulfilled: { + defaultMessage: "Unfulfilled", + description: "order status" + } +}); + +function getStatusLabel(status: string, intl: IntlShape): string { switch (status) { case OrderStatusFilter.FULFILLED.toString(): - return i18n.t("Fulfilled"); + return intl.formatMessage(filterMessages.fulfilled); case OrderStatusFilter.PARTIALLY_FULFILLED.toString(): - return i18n.t("Partially Fulfilled"); + return intl.formatMessage(filterMessages.partiallyFulfilled); case OrderStatusFilter.UNFULFILLED.toString(): - return i18n.t("Unfulfilled"); + return intl.formatMessage(filterMessages.unfulfilled); } return ""; @@ -90,7 +118,8 @@ interface OrderListChipFormatData { export function createFilterChips( filters: OrderListUrlFilters, formatData: OrderListChipFormatData, - onFilterDelete: (filters: OrderListUrlFilters) => void + onFilterDelete: (filters: OrderListUrlFilters) => void, + intl: IntlShape ): Filter[] { let filterChips: Filter[] = []; @@ -99,7 +128,7 @@ export function createFilterChips( filterChips = [ ...filterChips, { - label: i18n.t("Date is {{ date }}", { + label: intl.formatMessage(filterMessages.dateIs, { date: formatData.formatDate(filters.dateFrom) }), onClick: () => @@ -115,7 +144,7 @@ export function createFilterChips( filterChips = [ ...filterChips, { - label: i18n.t("Date from {{ date }}", { + label: intl.formatMessage(filterMessages.dateFrom, { date: formatData.formatDate(filters.dateFrom) }), onClick: () => @@ -131,7 +160,7 @@ export function createFilterChips( filterChips = [ ...filterChips, { - label: i18n.t("Date to {{ date }}", { + label: intl.formatMessage(filterMessages.dateTo, { date: formatData.formatDate(filters.dateTo) }), onClick: () => @@ -149,7 +178,7 @@ export function createFilterChips( filterChips = [ ...filterChips, { - label: getStatusLabel(filters.status), + label: getStatusLabel(filters.status, intl), onClick: () => onFilterDelete({ ...filters, diff --git a/src/products/views/ProductList/ProductList.tsx b/src/products/views/ProductList/ProductList.tsx index 8dc070057..39db0c7e6 100644 --- a/src/products/views/ProductList/ProductList.tsx +++ b/src/products/views/ProductList/ProductList.tsx @@ -217,7 +217,8 @@ export const ProductList: React.StatelessComponent = ({ currencySymbol, locale }, - changeFilterField + changeFilterField, + intl )} onAdd={() => navigate(productAddUrl)} disabled={loading} diff --git a/src/products/views/ProductList/filters.ts b/src/products/views/ProductList/filters.ts index 79f6ab112..3368e19b3 100644 --- a/src/products/views/ProductList/filters.ts +++ b/src/products/views/ProductList/filters.ts @@ -1,6 +1,6 @@ +import { defineMessages, IntlShape } from "react-intl"; import { FilterContentSubmitData } from "../../../components/Filter"; import { Filter } from "../../../components/TableFilter"; -import i18n from "../../../i18n"; import { ProductFilterInput, StockAvailability @@ -61,6 +61,37 @@ export function createFilter( } } +const filterMessages = defineMessages({ + available: { + defaultMessage: "Available", + description: "filter products by stock" + }, + hidden: { + defaultMessage: "Hidden", + description: "filter products by visibility" + }, + outOfStock: { + defaultMessage: "Out of stock", + description: "filter products by stock" + }, + priceFrom: { + defaultMessage: "Price from {price}", + description: "filter by price" + }, + priceIs: { + defaultMessage: "Price is {price}", + description: "filter by price" + }, + priceTo: { + defaultMessage: "Price to {price}", + description: "filter by price" + }, + published: { + defaultMessage: "Published", + description: "filter products by visibility" + } +}); + interface ProductListChipFormatData { currencySymbol: string; locale: string; @@ -68,7 +99,8 @@ interface ProductListChipFormatData { export function createFilterChips( filters: ProductListUrlFilters, formatData: ProductListChipFormatData, - onFilterDelete: (filters: ProductListUrlFilters) => void + onFilterDelete: (filters: ProductListUrlFilters) => void, + intl: IntlShape ): Filter[] { let filterChips: Filter[] = []; @@ -77,7 +109,7 @@ export function createFilterChips( filterChips = [ ...filterChips, { - label: i18n.t("Price is {{ price }}", { + label: intl.formatMessage(filterMessages.priceIs, { price: parseFloat(filters.priceFrom).toLocaleString( formatData.locale, { @@ -99,7 +131,7 @@ export function createFilterChips( filterChips = [ ...filterChips, { - label: i18n.t("Price from {{ price }}", { + label: intl.formatMessage(filterMessages.priceFrom, { price: parseFloat(filters.priceFrom).toLocaleString( formatData.locale, { @@ -121,7 +153,7 @@ export function createFilterChips( filterChips = [ ...filterChips, { - label: i18n.t("Price to {{ price }}", { + label: intl.formatMessage(filterMessages.priceTo, { price: parseFloat(filters.priceTo).toLocaleString( formatData.locale, { @@ -147,8 +179,8 @@ export function createFilterChips( { label: filters.status === StockAvailability.IN_STOCK.toString() - ? i18n.t("Available") - : i18n.t("Out Of Stock"), + ? intl.formatMessage(filterMessages.available) + : intl.formatMessage(filterMessages.outOfStock), onClick: () => onFilterDelete({ ...filters, @@ -162,10 +194,9 @@ export function createFilterChips( filterChips = [ ...filterChips, { - label: - filters.isPublished === StockAvailability.IN_STOCK.toString() - ? i18n.t("Published") - : i18n.t("Hidden"), + label: !!filters.isPublished + ? intl.formatMessage(filterMessages.published) + : intl.formatMessage(filterMessages.hidden), onClick: () => onFilterDelete({ ...filters, diff --git a/src/queries.tsx b/src/queries.tsx index 58266d99e..bde4cdb04 100644 --- a/src/queries.tsx +++ b/src/queries.tsx @@ -3,12 +3,12 @@ import { DocumentNode } from "graphql"; import gql from "graphql-tag"; import React from "react"; import { Query, QueryResult } from "react-apollo"; +import { useIntl } from "react-intl"; import AppProgress from "./components/AppProgress"; import ErrorPage from "./components/ErrorPage/ErrorPage"; import useNavigator from "./hooks/useNavigator"; import useNotifier from "./hooks/useNotifier"; -import i18n from "./i18n"; import { RequireAtLeastOne } from "./misc"; export interface LoadMore { @@ -69,6 +69,7 @@ export function TypedQuery( return ({ children, displayLoader, skip, variables, require }) => { const navigate = useNavigator(); const pushMessage = useNotifier(); + const intl = useIntl(); return ( @@ -82,9 +83,15 @@ export function TypedQuery( > {queryData => { if (queryData.error) { - const msg = i18n.t("Something went wrong: {{ message }}", { - message: queryData.error.message - }); + const msg = intl.formatMessage( + { + defaultMessage: "Something went wrong. {errorMessage}", + description: "error message" + }, + { + message: queryData.error.message + } + ); pushMessage({ text: msg }); } diff --git a/src/siteSettings/components/SiteSettingsKeyDialog/SiteSettingsKeyDialog.tsx b/src/siteSettings/components/SiteSettingsKeyDialog/SiteSettingsKeyDialog.tsx index 95b9ba140..73545589b 100644 --- a/src/siteSettings/components/SiteSettingsKeyDialog/SiteSettingsKeyDialog.tsx +++ b/src/siteSettings/components/SiteSettingsKeyDialog/SiteSettingsKeyDialog.tsx @@ -11,7 +11,7 @@ import Form, { FormProps } from "@saleor/components/Form"; import { FormSpacer } from "@saleor/components/FormSpacer"; import SingleSelectField from "@saleor/components/SingleSelectField"; import { buttonMessages } from "@saleor/intl"; -import { translatedAuthorizationKeyTypes } from "../../../misc"; +import { authorizationKeyTypes } from "../../../misc"; import { AuthorizationKeyType } from "../../../types/globalTypes"; export interface SiteSettingsKeyDialogForm { @@ -33,7 +33,6 @@ const SiteSettingsKeyDialog: React.StatelessComponent< SiteSettingsKeyDialogProps > = ({ errors, initial, open, onClose, onSubmit }) => { const intl = useIntl(); - const keyTypes = translatedAuthorizationKeyTypes(); return ( @@ -48,8 +47,8 @@ const SiteSettingsKeyDialog: React.StatelessComponent< ({ - label: keyTypes[key], + choices={Object.keys(authorizationKeyTypes).map(key => ({ + label: authorizationKeyTypes[key], value: key }))} error={!!errors.keyType} diff --git a/src/siteSettings/components/SiteSettingsKeys/SiteSettingsKeys.tsx b/src/siteSettings/components/SiteSettingsKeys/SiteSettingsKeys.tsx index acb4b50e0..a2eaaeadc 100644 --- a/src/siteSettings/components/SiteSettingsKeys/SiteSettingsKeys.tsx +++ b/src/siteSettings/components/SiteSettingsKeys/SiteSettingsKeys.tsx @@ -18,11 +18,7 @@ import { FormattedMessage, useIntl } from "react-intl"; import CardTitle from "@saleor/components/CardTitle"; import Skeleton from "@saleor/components/Skeleton"; -import { - maybe, - renderCollection, - translatedAuthorizationKeyTypes -} from "../../../misc"; +import { authorizationKeyTypes, maybe, renderCollection } from "../../../misc"; import { ICONBUTTON_SIZE } from "../../../theme"; import { AuthorizationKeyType } from "../../../types/globalTypes"; import { SiteSettings_shop_authorizationKeys } from "../../types/SiteSettings"; @@ -48,8 +44,6 @@ const SiteSettingsKeys = withStyles(styles, { name: "SiteSettingsKeys" })( ({ classes, disabled, keys, onAdd, onRemove }: SiteSettingsKeysProps) => { const intl = useIntl(); - const keyTypes = translatedAuthorizationKeyTypes(); - return ( {maybe( - () => keyTypes[key.name], + () => authorizationKeyTypes[key.name], )} diff --git a/src/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap index 4dabea725..0b6f69afe 100644 --- a/src/storybook/__snapshots__/Stories.test.ts.snap +++ b/src/storybook/__snapshots__/Stories.test.ts.snap @@ -11889,7 +11889,7 @@ exports[`Storyshots Views / Authentication / Log in default 1`] = ` class="MuiFormLabel-root-id MuiInputLabel-root-id MuiInputLabel-formControl-id MuiInputLabel-animated-id MuiInputLabel-outlined-id" data-shrink="false" > - Email + E-mail Address
- Sorry, your username and/or password are incorrect. -
- Please try again. + Sorry, your username and/or password are incorrect. Please try again.
- Email + E-mail Address
- Email + E-mail Address
- Specific Products + Specific products
@@ -43736,7 +43734,7 @@ exports[`Storyshots Views / Discounts / Voucher details form errors 1`] = ` - Specific Products + Specific products
@@ -44736,7 +44734,7 @@ exports[`Storyshots Views / Discounts / Voucher details loading 1`] = ` - Specific Products + Specific products
diff --git a/src/taxes/components/CountryTaxesPage/CountryTaxesPage.tsx b/src/taxes/components/CountryTaxesPage/CountryTaxesPage.tsx index 3c4f4d1c5..b1ddf1f72 100644 --- a/src/taxes/components/CountryTaxesPage/CountryTaxesPage.tsx +++ b/src/taxes/components/CountryTaxesPage/CountryTaxesPage.tsx @@ -38,7 +38,7 @@ const CountryTaxesPage = withStyles(styles, { name: "CountryTaxesPage" })( }: CountryTaxesPageProps & WithStyles) => { const intl = useIntl(); - const taxRates = translatedTaxRates(); + const taxRates = translatedTaxRates(intl); return ( diff --git a/src/utils/i18n.ts b/src/utils/i18n.ts deleted file mode 100644 index 2781c0056..000000000 --- a/src/utils/i18n.ts +++ /dev/null @@ -1,5 +0,0 @@ -import i18n from "@saleor/i18n"; - -export function translateBoolean(value: boolean): string { - return value ? i18n.t("Yes") : i18n.t("No"); -}