@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.
Files changed (84) hide show
  1. package/README.md +82 -370
  2. package/content/pwav3/components.md +400 -0
  3. package/content/pwav3/config.md +124 -0
  4. package/content/pwav3/data-fetching.md +213 -0
  5. package/content/pwav3/extensibility.md +167 -0
  6. package/content/pwav3/i18n.md +214 -0
  7. package/content/pwav3/quick-reference.md +169 -0
  8. package/content/pwav3/routing.md +107 -0
  9. package/content/pwav3/state-management.md +193 -0
  10. package/content/pwav3/styling.md +248 -0
  11. package/content/pwav3/testing.md +124 -0
  12. package/content/site-theming/theming-accessibility.md +126 -0
  13. package/content/site-theming/theming-questions.md +208 -0
  14. package/content/site-theming/theming-validation.md +174 -0
  15. package/dist/commands/mcp.d.ts +3 -3
  16. package/dist/commands/mcp.js +7 -7
  17. package/dist/registry.js +1 -1
  18. package/dist/services.d.ts +15 -15
  19. package/dist/services.js +21 -14
  20. package/dist/tools/adapter.d.ts +2 -2
  21. package/dist/tools/adapter.js +2 -2
  22. package/dist/tools/cartridges/index.js +1 -6
  23. package/dist/tools/index.d.ts +1 -4
  24. package/dist/tools/index.js +1 -4
  25. package/dist/tools/mrt/index.js +4 -9
  26. package/dist/tools/pwav3/index.d.ts +12 -3
  27. package/dist/tools/pwav3/index.js +5 -63
  28. package/dist/tools/pwav3/pwa-kit-development-guidelines.d.ts +9 -0
  29. package/dist/tools/pwav3/pwa-kit-development-guidelines.js +151 -0
  30. package/dist/tools/scapi/index.d.ts +1 -1
  31. package/dist/tools/scapi/index.js +6 -1
  32. package/dist/tools/scapi/scapi-custom-api-scaffold.d.ts +60 -0
  33. package/dist/tools/scapi/scapi-custom-api-scaffold.js +175 -0
  34. package/dist/tools/storefrontnext/figma/figma-to-component/figma-url-parser.d.ts +24 -0
  35. package/dist/tools/storefrontnext/figma/figma-to-component/figma-url-parser.js +53 -0
  36. package/dist/tools/storefrontnext/figma/figma-to-component/index.d.ts +42 -0
  37. package/dist/tools/storefrontnext/figma/figma-to-component/index.js +325 -0
  38. package/dist/tools/storefrontnext/figma/generate-component/decision.d.ts +40 -0
  39. package/dist/tools/storefrontnext/figma/generate-component/decision.js +312 -0
  40. package/dist/tools/storefrontnext/figma/generate-component/formatter.d.ts +9 -0
  41. package/dist/tools/storefrontnext/figma/generate-component/formatter.js +92 -0
  42. package/dist/tools/storefrontnext/figma/generate-component/index.d.ts +114 -0
  43. package/dist/tools/storefrontnext/figma/generate-component/index.js +98 -0
  44. package/dist/tools/storefrontnext/figma/map-tokens/css-parser.d.ts +71 -0
  45. package/dist/tools/storefrontnext/figma/map-tokens/css-parser.js +260 -0
  46. package/dist/tools/storefrontnext/figma/map-tokens/index.d.ts +61 -0
  47. package/dist/tools/storefrontnext/figma/map-tokens/index.js +234 -0
  48. package/dist/tools/storefrontnext/figma/map-tokens/token-matcher.d.ts +65 -0
  49. package/dist/tools/storefrontnext/figma/map-tokens/token-matcher.js +268 -0
  50. package/dist/tools/storefrontnext/index.d.ts +17 -0
  51. package/dist/tools/storefrontnext/index.js +10 -60
  52. package/dist/tools/storefrontnext/page-designer-decorator/analyzer.js +15 -0
  53. package/dist/tools/storefrontnext/page-designer-decorator/index.js +3 -3
  54. package/dist/tools/storefrontnext/{developer-guidelines.js → sfnext-development-guidelines.js} +3 -3
  55. package/dist/tools/storefrontnext/site-theming/color-contrast.d.ts +92 -0
  56. package/dist/tools/storefrontnext/site-theming/color-contrast.js +186 -0
  57. package/dist/tools/storefrontnext/site-theming/color-mapping.d.ts +16 -0
  58. package/dist/tools/storefrontnext/site-theming/color-mapping.js +131 -0
  59. package/dist/tools/storefrontnext/site-theming/guidance-merger.d.ts +11 -0
  60. package/dist/tools/storefrontnext/site-theming/guidance-merger.js +78 -0
  61. package/dist/tools/storefrontnext/site-theming/index.d.ts +14 -0
  62. package/dist/tools/storefrontnext/site-theming/index.js +122 -0
  63. package/dist/tools/storefrontnext/site-theming/response-builder.d.ts +16 -0
  64. package/dist/tools/storefrontnext/site-theming/response-builder.js +316 -0
  65. package/dist/tools/storefrontnext/site-theming/theming-store.d.ts +62 -0
  66. package/dist/tools/storefrontnext/site-theming/theming-store.js +410 -0
  67. package/dist/tools/storefrontnext/site-theming/types.d.ts +35 -0
  68. package/dist/tools/storefrontnext/site-theming/types.js +7 -0
  69. package/oclif.manifest.json +8 -5
  70. package/package.json +9 -6
  71. /package/content/{auth.md → sfnext/auth.md} +0 -0
  72. /package/content/{components.md → sfnext/components.md} +0 -0
  73. /package/content/{config.md → sfnext/config.md} +0 -0
  74. /package/content/{data-fetching.md → sfnext/data-fetching.md} +0 -0
  75. /package/content/{extensions.md → sfnext/extensions.md} +0 -0
  76. /package/content/{i18n.md → sfnext/i18n.md} +0 -0
  77. /package/content/{page-designer.md → sfnext/page-designer.md} +0 -0
  78. /package/content/{performance.md → sfnext/performance.md} +0 -0
  79. /package/content/{pitfalls.md → sfnext/pitfalls.md} +0 -0
  80. /package/content/{quick-reference.md → sfnext/quick-reference.md} +0 -0
  81. /package/content/{state-management.md → sfnext/state-management.md} +0 -0
  82. /package/content/{styling.md → sfnext/styling.md} +0 -0
  83. /package/content/{testing.md → sfnext/testing.md} +0 -0
  84. /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