@salesforce/b2c-dx-mcp 0.4.4 → 0.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +82 -370
- package/content/pwav3/components.md +400 -0
- package/content/pwav3/config.md +124 -0
- package/content/pwav3/data-fetching.md +213 -0
- package/content/pwav3/extensibility.md +167 -0
- package/content/pwav3/i18n.md +214 -0
- package/content/pwav3/quick-reference.md +169 -0
- package/content/pwav3/routing.md +107 -0
- package/content/pwav3/state-management.md +193 -0
- package/content/pwav3/styling.md +248 -0
- package/content/pwav3/testing.md +124 -0
- package/content/site-theming/theming-accessibility.md +126 -0
- package/content/site-theming/theming-questions.md +208 -0
- package/content/site-theming/theming-validation.md +174 -0
- package/dist/commands/mcp.d.ts +3 -3
- package/dist/commands/mcp.js +7 -7
- package/dist/registry.js +1 -1
- package/dist/services.d.ts +15 -15
- package/dist/services.js +21 -14
- package/dist/tools/adapter.d.ts +2 -2
- package/dist/tools/adapter.js +2 -2
- package/dist/tools/cartridges/index.js +1 -6
- package/dist/tools/index.d.ts +1 -4
- package/dist/tools/index.js +1 -4
- package/dist/tools/mrt/index.js +4 -9
- package/dist/tools/pwav3/index.d.ts +12 -3
- package/dist/tools/pwav3/index.js +5 -63
- package/dist/tools/pwav3/pwa-kit-development-guidelines.d.ts +9 -0
- package/dist/tools/pwav3/pwa-kit-development-guidelines.js +151 -0
- package/dist/tools/scapi/index.d.ts +1 -1
- package/dist/tools/scapi/index.js +6 -1
- package/dist/tools/scapi/scapi-custom-api-scaffold.d.ts +60 -0
- package/dist/tools/scapi/scapi-custom-api-scaffold.js +175 -0
- package/dist/tools/storefrontnext/figma/figma-to-component/figma-url-parser.d.ts +24 -0
- package/dist/tools/storefrontnext/figma/figma-to-component/figma-url-parser.js +53 -0
- package/dist/tools/storefrontnext/figma/figma-to-component/index.d.ts +42 -0
- package/dist/tools/storefrontnext/figma/figma-to-component/index.js +325 -0
- package/dist/tools/storefrontnext/figma/generate-component/decision.d.ts +40 -0
- package/dist/tools/storefrontnext/figma/generate-component/decision.js +312 -0
- package/dist/tools/storefrontnext/figma/generate-component/formatter.d.ts +9 -0
- package/dist/tools/storefrontnext/figma/generate-component/formatter.js +92 -0
- package/dist/tools/storefrontnext/figma/generate-component/index.d.ts +114 -0
- package/dist/tools/storefrontnext/figma/generate-component/index.js +98 -0
- package/dist/tools/storefrontnext/figma/map-tokens/css-parser.d.ts +71 -0
- package/dist/tools/storefrontnext/figma/map-tokens/css-parser.js +260 -0
- package/dist/tools/storefrontnext/figma/map-tokens/index.d.ts +61 -0
- package/dist/tools/storefrontnext/figma/map-tokens/index.js +234 -0
- package/dist/tools/storefrontnext/figma/map-tokens/token-matcher.d.ts +65 -0
- package/dist/tools/storefrontnext/figma/map-tokens/token-matcher.js +268 -0
- package/dist/tools/storefrontnext/index.d.ts +17 -0
- package/dist/tools/storefrontnext/index.js +10 -60
- package/dist/tools/storefrontnext/page-designer-decorator/analyzer.js +15 -0
- package/dist/tools/storefrontnext/page-designer-decorator/index.js +3 -3
- package/dist/tools/storefrontnext/{developer-guidelines.js → sfnext-development-guidelines.js} +3 -3
- package/dist/tools/storefrontnext/site-theming/color-contrast.d.ts +92 -0
- package/dist/tools/storefrontnext/site-theming/color-contrast.js +186 -0
- package/dist/tools/storefrontnext/site-theming/color-mapping.d.ts +16 -0
- package/dist/tools/storefrontnext/site-theming/color-mapping.js +131 -0
- package/dist/tools/storefrontnext/site-theming/guidance-merger.d.ts +11 -0
- package/dist/tools/storefrontnext/site-theming/guidance-merger.js +78 -0
- package/dist/tools/storefrontnext/site-theming/index.d.ts +14 -0
- package/dist/tools/storefrontnext/site-theming/index.js +122 -0
- package/dist/tools/storefrontnext/site-theming/response-builder.d.ts +16 -0
- package/dist/tools/storefrontnext/site-theming/response-builder.js +316 -0
- package/dist/tools/storefrontnext/site-theming/theming-store.d.ts +62 -0
- package/dist/tools/storefrontnext/site-theming/theming-store.js +410 -0
- package/dist/tools/storefrontnext/site-theming/types.d.ts +35 -0
- package/dist/tools/storefrontnext/site-theming/types.js +7 -0
- package/oclif.manifest.json +8 -5
- package/package.json +9 -6
- /package/content/{auth.md → sfnext/auth.md} +0 -0
- /package/content/{components.md → sfnext/components.md} +0 -0
- /package/content/{config.md → sfnext/config.md} +0 -0
- /package/content/{data-fetching.md → sfnext/data-fetching.md} +0 -0
- /package/content/{extensions.md → sfnext/extensions.md} +0 -0
- /package/content/{i18n.md → sfnext/i18n.md} +0 -0
- /package/content/{page-designer.md → sfnext/page-designer.md} +0 -0
- /package/content/{performance.md → sfnext/performance.md} +0 -0
- /package/content/{pitfalls.md → sfnext/pitfalls.md} +0 -0
- /package/content/{quick-reference.md → sfnext/quick-reference.md} +0 -0
- /package/content/{state-management.md → sfnext/state-management.md} +0 -0
- /package/content/{styling.md → sfnext/styling.md} +0 -0
- /package/content/{testing.md → sfnext/testing.md} +0 -0
- /package/dist/tools/storefrontnext/{developer-guidelines.d.ts → sfnext-development-guidelines.d.ts} +0 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# Data Fetching and Commerce API Integration
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
PWA Kit applications fetch data from Salesforce Commerce Cloud APIs using the `@salesforce/commerce-sdk-react` library. This library provides React hooks built on top of React Query for data fetching, caching, and state management.
|
|
6
|
+
|
|
7
|
+
## Commerce SDK React Hooks
|
|
8
|
+
|
|
9
|
+
### Standard Commerce API Hooks
|
|
10
|
+
|
|
11
|
+
The commerce-sdk-react library provides hooks for all standard Commerce APIs:
|
|
12
|
+
|
|
13
|
+
```jsx
|
|
14
|
+
import {
|
|
15
|
+
useProduct,
|
|
16
|
+
useProducts,
|
|
17
|
+
useCategory,
|
|
18
|
+
useCustomer,
|
|
19
|
+
useBasket,
|
|
20
|
+
useCustomerBaskets,
|
|
21
|
+
useProductSearch,
|
|
22
|
+
useShopperBasketsMutation
|
|
23
|
+
} from '@salesforce/commerce-sdk-react';
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Basic Usage Pattern
|
|
27
|
+
|
|
28
|
+
**Reference:** See `app/pages/product-detail/index.jsx` for useProduct with useParams, loading/error states. Pattern: `const {data, isLoading, error, refetch} = useProduct({parameters: {id, allImages, perPricebook}})`.
|
|
29
|
+
|
|
30
|
+
## Custom API Implementation
|
|
31
|
+
|
|
32
|
+
For custom Commerce Cloud APIs (OCAPI Data API or SCAPI custom endpoints), use `useCustomQuery` and `useCustomMutation`.
|
|
33
|
+
|
|
34
|
+
### useCustomQuery for GET Requests
|
|
35
|
+
|
|
36
|
+
```jsx
|
|
37
|
+
import {useCustomQuery} from '@salesforce/commerce-sdk-react';
|
|
38
|
+
|
|
39
|
+
const MyComponent = () => {
|
|
40
|
+
const {data, isLoading, error} = useCustomQuery({
|
|
41
|
+
options: {
|
|
42
|
+
method: 'GET',
|
|
43
|
+
customApiPathParameters: {
|
|
44
|
+
apiName: 'store-locations',
|
|
45
|
+
apiVersion: 'v1',
|
|
46
|
+
endpointPath: 'locations'
|
|
47
|
+
},
|
|
48
|
+
parameters: {
|
|
49
|
+
c_latitude: 37.7749,
|
|
50
|
+
c_longitude: -122.4194,
|
|
51
|
+
c_radius: 50
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
if (isLoading) return <Spinner />;
|
|
57
|
+
if (error) return <ErrorMessage error={error} />;
|
|
58
|
+
|
|
59
|
+
return <StoreList stores={data?.stores} />;
|
|
60
|
+
};
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Critical Rules for customApiPathParameters
|
|
64
|
+
|
|
65
|
+
**DO NOT include extra '/' before or after path parameters:**
|
|
66
|
+
|
|
67
|
+
```javascript
|
|
68
|
+
// ✅ CORRECT
|
|
69
|
+
customApiPathParameters: {
|
|
70
|
+
apiName: 'store-locations',
|
|
71
|
+
apiVersion: 'v1',
|
|
72
|
+
endpointPath: 'locations'
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ❌ WRONG - Extra slashes
|
|
76
|
+
customApiPathParameters: {
|
|
77
|
+
apiName: '/store-locations/',
|
|
78
|
+
apiVersion: '/v1/',
|
|
79
|
+
endpointPath: '/locations'
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### useCustomMutation for POST/PUT/PATCH/DELETE
|
|
84
|
+
|
|
85
|
+
```jsx
|
|
86
|
+
import {useCustomMutation} from '@salesforce/commerce-sdk-react';
|
|
87
|
+
|
|
88
|
+
const StoreLocator = () => {
|
|
89
|
+
const mutation = useCustomMutation({
|
|
90
|
+
options: {
|
|
91
|
+
method: 'POST',
|
|
92
|
+
customApiPathParameters: {
|
|
93
|
+
apiName: 'store-locator',
|
|
94
|
+
apiVersion: 'v1',
|
|
95
|
+
endpointPath: 'find-nearest'
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const findNearestStore = () => {
|
|
101
|
+
mutation.mutate({
|
|
102
|
+
body: {
|
|
103
|
+
latitude: 37.7749,
|
|
104
|
+
longitude: -122.4194
|
|
105
|
+
},
|
|
106
|
+
parameters: {
|
|
107
|
+
c_maxResults: 5
|
|
108
|
+
},
|
|
109
|
+
headers: {
|
|
110
|
+
'X-Custom-Header': 'value'
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<div>
|
|
117
|
+
<Button onClick={findNearestStore} isLoading={mutation.isLoading}>
|
|
118
|
+
Find Nearest Store
|
|
119
|
+
</Button>
|
|
120
|
+
{mutation.isSuccess && <StoreList stores={mutation.data.stores} />}
|
|
121
|
+
{mutation.isError && <ErrorMessage error={mutation.error} />}
|
|
122
|
+
</div>
|
|
123
|
+
);
|
|
124
|
+
};
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### mutation.mutate() Arguments
|
|
128
|
+
|
|
129
|
+
The `mutation.mutate(args)` function accepts:
|
|
130
|
+
- **body** (Object): Request payload for POST/PUT/PATCH methods
|
|
131
|
+
- **parameters** (Object): Optional query parameters
|
|
132
|
+
- **headers** (Object): Optional custom headers
|
|
133
|
+
|
|
134
|
+
## Custom Hooks Pattern
|
|
135
|
+
|
|
136
|
+
Create custom hooks to encapsulate data fetching logic. **Reference:** See `app/hooks/use-current-customer.js` in the template.
|
|
137
|
+
|
|
138
|
+
## React Query Integration
|
|
139
|
+
|
|
140
|
+
PWA Kit uses React Query under the hood. Access the query client for advanced operations:
|
|
141
|
+
|
|
142
|
+
```jsx
|
|
143
|
+
import {useQueryClient} from '@tanstack/react-query';
|
|
144
|
+
import {useParams} from 'react-router-dom';
|
|
145
|
+
|
|
146
|
+
const ProductDetail = () => {
|
|
147
|
+
const {productId} = useParams();
|
|
148
|
+
const queryClient = useQueryClient();
|
|
149
|
+
|
|
150
|
+
const handleUpdate = async () => {
|
|
151
|
+
// Invalidate and refetch
|
|
152
|
+
await queryClient.invalidateQueries(['product', productId]);
|
|
153
|
+
|
|
154
|
+
// Or manually update cache
|
|
155
|
+
queryClient.setQueryData(['product', productId], (old) => ({
|
|
156
|
+
...old,
|
|
157
|
+
viewed: true
|
|
158
|
+
}));
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
return <div>...</div>;
|
|
162
|
+
};
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Caching Strategies
|
|
166
|
+
|
|
167
|
+
### Default Cache Behavior (Template Override)
|
|
168
|
+
|
|
169
|
+
The Retail React App _app-config overrides React Query defaults:
|
|
170
|
+
- **staleTime**: 10 seconds
|
|
171
|
+
- **retry**: false
|
|
172
|
+
- **refetchOnWindowFocus**: false
|
|
173
|
+
|
|
174
|
+
### Custom Cache Configuration
|
|
175
|
+
|
|
176
|
+
```jsx
|
|
177
|
+
const {data} = useProduct({
|
|
178
|
+
parameters: {id: productId},
|
|
179
|
+
staleTime: 10 * 60 * 1000, // 10 minutes
|
|
180
|
+
cacheTime: 30 * 60 * 1000, // 30 minutes (React Query v4; gcTime in v5)
|
|
181
|
+
refetchOnWindowFocus: false
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Error Handling
|
|
186
|
+
|
|
187
|
+
```jsx
|
|
188
|
+
import {useProduct} from '@salesforce/commerce-sdk-react';
|
|
189
|
+
import {useParams} from 'react-router-dom';
|
|
190
|
+
import {Box, Alert, AlertIcon, AlertTitle, AlertDescription, Button} from '@salesforce/retail-react-app/app/components/shared/ui';
|
|
191
|
+
|
|
192
|
+
const ProductDetail = () => {
|
|
193
|
+
const {productId} = useParams();
|
|
194
|
+
const {data, error, isError, refetch} = useProduct({
|
|
195
|
+
parameters: {id: productId}
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
if (isError) {
|
|
199
|
+
return (
|
|
200
|
+
<Alert status="error">
|
|
201
|
+
<AlertIcon />
|
|
202
|
+
<Box flex="1">
|
|
203
|
+
<AlertTitle>Failed to load product</AlertTitle>
|
|
204
|
+
<AlertDescription>{error.message}</AlertDescription>
|
|
205
|
+
</Box>
|
|
206
|
+
<Button size="sm" onClick={() => refetch()}>Retry</Button>
|
|
207
|
+
</Alert>
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return <ProductView product={data} />;
|
|
212
|
+
};
|
|
213
|
+
```
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# PWA Kit Extensibility
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
PWA Kit v3 provides an extensibility system to extend a base template (like @salesforce/retail-react-app) and override specific files while inheriting the rest.
|
|
6
|
+
|
|
7
|
+
## Configuration
|
|
8
|
+
|
|
9
|
+
Configure extensibility in `package.json`:
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"ccExtensibility": {
|
|
14
|
+
"extends": "@salesforce/retail-react-app",
|
|
15
|
+
"overridesDir": "overrides"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@salesforce/retail-react-app": "^3.0.0"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Configuration Properties
|
|
24
|
+
|
|
25
|
+
- **extends**: The npm package name of the base template
|
|
26
|
+
- **overridesDir**: Directory containing override files (default: "overrides")
|
|
27
|
+
|
|
28
|
+
## Overriding Files
|
|
29
|
+
|
|
30
|
+
To override a file, recreate its exact path in your overrides directory:
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
Base template:
|
|
34
|
+
@salesforce/retail-react-app/app/components/header/index.jsx
|
|
35
|
+
|
|
36
|
+
Your override:
|
|
37
|
+
overrides/app/components/header/index.jsx
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Example Override Structure
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
my-custom-storefront/
|
|
44
|
+
├── overrides/
|
|
45
|
+
│ ├── app/
|
|
46
|
+
│ │ ├── components/
|
|
47
|
+
│ │ │ ├── header/
|
|
48
|
+
│ │ │ │ └── index.jsx # Overrides base header
|
|
49
|
+
│ │ │ └── footer/
|
|
50
|
+
│ │ │ └── index.jsx # Overrides base footer
|
|
51
|
+
│ │ └── theme/
|
|
52
|
+
│ │ └── index.js # Overrides base theme
|
|
53
|
+
│ └── config/
|
|
54
|
+
│ └── default.js # Overrides base config
|
|
55
|
+
├── package.json
|
|
56
|
+
└── README.md
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Overriding Components
|
|
60
|
+
|
|
61
|
+
### Simple Component Override
|
|
62
|
+
|
|
63
|
+
```jsx
|
|
64
|
+
// overrides/app/components/header/index.jsx
|
|
65
|
+
import {Box, Heading} from '@salesforce/retail-react-app/app/components/shared/ui';
|
|
66
|
+
|
|
67
|
+
const Header = () => {
|
|
68
|
+
return (
|
|
69
|
+
<Box bg="brand.500" p={4}>
|
|
70
|
+
<Heading color="white">My Custom Store</Heading>
|
|
71
|
+
</Box>
|
|
72
|
+
);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export default Header;
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Importing from Base Template
|
|
79
|
+
|
|
80
|
+
```jsx
|
|
81
|
+
// overrides/app/components/header/index.jsx
|
|
82
|
+
import {Box} from '@salesforce/retail-react-app/app/components/shared/ui';
|
|
83
|
+
import Navigation from '@salesforce/retail-react-app/app/components/list-menu';
|
|
84
|
+
import SearchBar from '@salesforce/retail-react-app/app/components/search';
|
|
85
|
+
|
|
86
|
+
const Header = () => {
|
|
87
|
+
return (
|
|
88
|
+
<Box>
|
|
89
|
+
<SearchBar />
|
|
90
|
+
<Navigation />
|
|
91
|
+
</Box>
|
|
92
|
+
);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export default Header;
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Extending Base Component
|
|
99
|
+
|
|
100
|
+
```jsx
|
|
101
|
+
// overrides/app/components/product-tile/index.jsx
|
|
102
|
+
import BaseProductTile from '@salesforce/retail-react-app/app/components/product-tile';
|
|
103
|
+
import {Badge, Box} from '@salesforce/retail-react-app/app/components/shared/ui';
|
|
104
|
+
|
|
105
|
+
const ProductTile = (props) => {
|
|
106
|
+
return (
|
|
107
|
+
<Box position="relative">
|
|
108
|
+
{props.product.isNew && (
|
|
109
|
+
<Badge position="absolute" top={2} right={2} colorScheme="green">
|
|
110
|
+
New
|
|
111
|
+
</Badge>
|
|
112
|
+
)}
|
|
113
|
+
<BaseProductTile {...props} />
|
|
114
|
+
</Box>
|
|
115
|
+
);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export default ProductTile;
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Overriding Configuration
|
|
122
|
+
|
|
123
|
+
### Custom Theme
|
|
124
|
+
|
|
125
|
+
```javascript
|
|
126
|
+
// overrides/app/theme/index.js
|
|
127
|
+
import baseTheme from '@salesforce/retail-react-app/app/theme';
|
|
128
|
+
import {extendTheme} from '@salesforce/retail-react-app/app/components/shared/ui';
|
|
129
|
+
|
|
130
|
+
const customTheme = extendTheme(baseTheme, {
|
|
131
|
+
colors: {
|
|
132
|
+
brand: {
|
|
133
|
+
50: '#e6f2ff',
|
|
134
|
+
500: '#0066cc',
|
|
135
|
+
900: '#003366'
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
export default customTheme;
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Custom Configuration
|
|
144
|
+
|
|
145
|
+
```javascript
|
|
146
|
+
// overrides/config/default.js
|
|
147
|
+
const baseConfig = require('@salesforce/retail-react-app/config/default');
|
|
148
|
+
|
|
149
|
+
module.exports = {
|
|
150
|
+
...baseConfig,
|
|
151
|
+
app: {
|
|
152
|
+
...baseConfig.app,
|
|
153
|
+
defaultSite: 'MyCustomSite',
|
|
154
|
+
storeLocatorEnabled: true,
|
|
155
|
+
customFeatureEnabled: true
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Best Practices
|
|
161
|
+
|
|
162
|
+
1. **Minimize Overrides** - Only override what you need
|
|
163
|
+
2. **Import from Base** - Reuse components from base template
|
|
164
|
+
3. **Extend, Don't Replace** - Wrap or extend base components
|
|
165
|
+
4. **Document Overrides** - Keep clear records of changes
|
|
166
|
+
5. **Test Thoroughly** - Ensure overrides work correctly
|
|
167
|
+
6. **Keep Up to Date** - Review base template updates
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# Internationalization (i18n)
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
PWA Kit applications use React Intl for internationalization and localization. The system supports multiple locales, currency formats, date formats, and translated messages.
|
|
6
|
+
|
|
7
|
+
## React Intl Integration
|
|
8
|
+
|
|
9
|
+
```jsx
|
|
10
|
+
import {IntlProvider, FormattedMessage, FormattedNumber, FormattedDate} from 'react-intl';
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Translation Files
|
|
14
|
+
|
|
15
|
+
Translations live at project root; compiled output goes to `app/static/translations/compiled/`:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
project-root/
|
|
19
|
+
├── translations/
|
|
20
|
+
│ ├── en-US.json
|
|
21
|
+
│ ├── en-GB.json
|
|
22
|
+
│ └── fr-FR.json
|
|
23
|
+
└── app/
|
|
24
|
+
└── static/
|
|
25
|
+
└── translations/
|
|
26
|
+
└── compiled/
|
|
27
|
+
├── en-US.json
|
|
28
|
+
├── en-GB.json
|
|
29
|
+
└── en-XA.json # pseudo-locale for testing
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Message Format
|
|
33
|
+
|
|
34
|
+
```json
|
|
35
|
+
{
|
|
36
|
+
"product.add_to_cart": "Add to Cart",
|
|
37
|
+
"product.price": "Price: {price}",
|
|
38
|
+
"cart.item_count": "{count, plural, =0 {No items} one {1 item} other {# items}}"
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Defining Messages
|
|
43
|
+
|
|
44
|
+
### Using defineMessage
|
|
45
|
+
|
|
46
|
+
```jsx
|
|
47
|
+
import {defineMessage, useIntl} from 'react-intl';
|
|
48
|
+
import {Button} from '@salesforce/retail-react-app/app/components/shared/ui';
|
|
49
|
+
|
|
50
|
+
const messages = {
|
|
51
|
+
addToCart: defineMessage({
|
|
52
|
+
id: 'product.add_to_cart',
|
|
53
|
+
defaultMessage: 'Add to Cart'
|
|
54
|
+
})
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const ProductTile = ({product}) => {
|
|
58
|
+
const intl = useIntl();
|
|
59
|
+
return (
|
|
60
|
+
<Button>{intl.formatMessage(messages.addToCart)}</Button>
|
|
61
|
+
);
|
|
62
|
+
};
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Using FormattedMessage
|
|
66
|
+
|
|
67
|
+
```jsx
|
|
68
|
+
import {FormattedMessage} from 'react-intl';
|
|
69
|
+
|
|
70
|
+
<FormattedMessage
|
|
71
|
+
id="product.add_to_cart"
|
|
72
|
+
defaultMessage="Add to Cart"
|
|
73
|
+
/>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Message Formatting
|
|
77
|
+
|
|
78
|
+
### With Variables
|
|
79
|
+
|
|
80
|
+
```jsx
|
|
81
|
+
<FormattedMessage
|
|
82
|
+
id="product.price"
|
|
83
|
+
defaultMessage="Price: {price}"
|
|
84
|
+
values={{price: product.price}}
|
|
85
|
+
/>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Pluralization
|
|
89
|
+
|
|
90
|
+
```jsx
|
|
91
|
+
<FormattedMessage
|
|
92
|
+
id="cart.item_count"
|
|
93
|
+
defaultMessage="{count, plural, =0 {No items} one {1 item} other {# items}}"
|
|
94
|
+
values={{count: itemCount}}
|
|
95
|
+
/>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Date and Number Formatting
|
|
99
|
+
|
|
100
|
+
```jsx
|
|
101
|
+
import {FormattedDate, FormattedNumber} from 'react-intl';
|
|
102
|
+
|
|
103
|
+
<FormattedDate
|
|
104
|
+
value={new Date()}
|
|
105
|
+
year="numeric"
|
|
106
|
+
month="long"
|
|
107
|
+
day="numeric"
|
|
108
|
+
/>
|
|
109
|
+
|
|
110
|
+
<FormattedNumber
|
|
111
|
+
value={product.price}
|
|
112
|
+
style="currency"
|
|
113
|
+
currency="USD"
|
|
114
|
+
/>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## useIntl Hook
|
|
118
|
+
|
|
119
|
+
```jsx
|
|
120
|
+
import {useIntl} from 'react-intl';
|
|
121
|
+
import {Text} from '@salesforce/retail-react-app/app/components/shared/ui';
|
|
122
|
+
|
|
123
|
+
const ProductDetail = ({product}) => {
|
|
124
|
+
const intl = useIntl();
|
|
125
|
+
|
|
126
|
+
const formattedPrice = intl.formatNumber(product.price, {
|
|
127
|
+
style: 'currency',
|
|
128
|
+
currency: 'USD'
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
return <Text>{formattedPrice}</Text>;
|
|
132
|
+
};
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Translation Extraction
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
# Extract default messages (en-US, en-GB)
|
|
139
|
+
npm run extract-default-translations
|
|
140
|
+
|
|
141
|
+
# Compile translations
|
|
142
|
+
npm run compile-translations
|
|
143
|
+
|
|
144
|
+
# Compile pseudo-locale for testing
|
|
145
|
+
npm run compile-translations:pseudo
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Multi-Locale Configuration
|
|
149
|
+
|
|
150
|
+
Sites and locales are defined in `config/sites.js`:
|
|
151
|
+
|
|
152
|
+
```javascript
|
|
153
|
+
// config/sites.js
|
|
154
|
+
module.exports = [
|
|
155
|
+
{
|
|
156
|
+
id: 'RefArch',
|
|
157
|
+
l10n: {
|
|
158
|
+
supportedCurrencies: ['USD'],
|
|
159
|
+
defaultCurrency: 'USD',
|
|
160
|
+
defaultLocale: 'en-US',
|
|
161
|
+
supportedLocales: [
|
|
162
|
+
{ id: 'en-US', preferredCurrency: 'USD' },
|
|
163
|
+
{ id: 'en-CA', preferredCurrency: 'USD' }
|
|
164
|
+
]
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
];
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Locale Switcher
|
|
171
|
+
|
|
172
|
+
```jsx
|
|
173
|
+
import {useIntl} from 'react-intl';
|
|
174
|
+
import {useHistory, useLocation} from 'react-router-dom';
|
|
175
|
+
import {Select} from '@salesforce/retail-react-app/app/components/shared/ui';
|
|
176
|
+
|
|
177
|
+
const LocaleSwitcher = ({supportedLocales}) => {
|
|
178
|
+
const intl = useIntl();
|
|
179
|
+
const history = useHistory();
|
|
180
|
+
const location = useLocation();
|
|
181
|
+
|
|
182
|
+
const handleChange = (e) => {
|
|
183
|
+
const newLocale = e.target.value;
|
|
184
|
+
const newPath = location.pathname.replace(
|
|
185
|
+
`/${intl.locale}/`,
|
|
186
|
+
`/${newLocale}/`
|
|
187
|
+
);
|
|
188
|
+
history.push(newPath);
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
return (
|
|
192
|
+
<Select value={intl.locale} onChange={handleChange}>
|
|
193
|
+
{supportedLocales.map((locale) => (
|
|
194
|
+
<option key={locale.id} value={locale.id}>
|
|
195
|
+
{locale.id}
|
|
196
|
+
</option>
|
|
197
|
+
))}
|
|
198
|
+
</Select>
|
|
199
|
+
);
|
|
200
|
+
};
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Best Practices
|
|
204
|
+
|
|
205
|
+
1. Always use translation keys
|
|
206
|
+
2. Provide defaultMessage
|
|
207
|
+
3. Use semantic key names
|
|
208
|
+
4. Extract regularly
|
|
209
|
+
5. Compile for production
|
|
210
|
+
6. Test with pseudo-locale
|
|
211
|
+
7. Consider text expansion
|
|
212
|
+
8. Format dates and numbers
|
|
213
|
+
9. Handle pluralization
|
|
214
|
+
10. Document context
|