@zohodesk/i18n 1.0.0-beta.34 → 1.0.0-beta.35-murphy
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/docs/murphy/01-MURPHY_OVERVIEW.md +148 -0
- package/docs/murphy/02-MURPHY_ARCHITECTURE.md +283 -0
- package/docs/murphy/03-MURPHY_BACKEND_CONFIG.md +337 -0
- package/docs/murphy/04-MURPHY_FRONTEND_INIT.md +437 -0
- package/docs/murphy/05-MURPHY_DESK_CLIENT_USAGE.md +467 -0
- package/docs/murphy/06-MURPHY_I18N_INTEGRATION.md +402 -0
- package/docs/murphy/07-MURPHY_WHY_I18N_APPROACH.md +391 -0
- package/es/components/DateTimeDiffFormat.js +5 -19
- package/es/components/FormatText.js +2 -2
- package/es/components/HOCI18N.js +32 -43
- package/es/components/I18N.js +2 -13
- package/es/components/I18NProvider.js +0 -9
- package/es/components/PluralFormat.js +3 -5
- package/es/components/UserTimeDiffFormat.js +5 -9
- package/es/components/__tests__/DateTimeDiffFormat.spec.js +157 -221
- package/es/components/__tests__/FormatText.spec.js +2 -2
- package/es/components/__tests__/HOCI18N.spec.js +2 -4
- package/es/components/__tests__/I18N.spec.js +6 -4
- package/es/components/__tests__/I18NProvider.spec.js +4 -4
- package/es/components/__tests__/PluralFormat.spec.js +2 -2
- package/es/components/__tests__/UserTimeDiffFormat.spec.js +249 -348
- package/es/index.js +1 -0
- package/es/utils/__tests__/jsxTranslations.spec.js +3 -7
- package/es/utils/errorReporter.js +31 -0
- package/es/utils/index.js +42 -92
- package/es/utils/jsxTranslations.js +34 -52
- package/lib/I18NContext.js +2 -7
- package/lib/components/DateTimeDiffFormat.js +46 -87
- package/lib/components/FormatText.js +18 -41
- package/lib/components/HOCI18N.js +24 -59
- package/lib/components/I18N.js +27 -64
- package/lib/components/I18NProvider.js +27 -63
- package/lib/components/PluralFormat.js +24 -50
- package/lib/components/UserTimeDiffFormat.js +43 -72
- package/lib/components/__tests__/DateTimeDiffFormat.spec.js +95 -165
- package/lib/components/__tests__/FormatText.spec.js +3 -10
- package/lib/components/__tests__/HOCI18N.spec.js +3 -14
- package/lib/components/__tests__/I18N.spec.js +4 -12
- package/lib/components/__tests__/I18NProvider.spec.js +8 -23
- package/lib/components/__tests__/PluralFormat.spec.js +3 -11
- package/lib/components/__tests__/UserTimeDiffFormat.spec.js +157 -225
- package/lib/index.js +25 -23
- package/lib/utils/__tests__/jsxTranslations.spec.js +1 -12
- package/lib/utils/errorReporter.js +39 -0
- package/lib/utils/index.js +49 -125
- package/lib/utils/jsxTranslations.js +61 -100
- package/package.json +1 -1
- package/src/index.js +5 -0
- package/src/utils/errorReporter.js +36 -0
- package/src/utils/index.js +8 -1
- package/src/utils/jsxTranslations.js +8 -1
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
# Murphy Usage in Desk Client App
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The desk client application provides utility wrappers around Murphy to standardize error tracking and logging across the codebase.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Utility Wrappers
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
+-------------------------------------------------------------------+
|
|
13
|
+
| MURPHY UTILITY WRAPPERS |
|
|
14
|
+
| Path: src/library/murphy/Utils.js |
|
|
15
|
+
+-------------------------------------------------------------------+
|
|
16
|
+
| |
|
|
17
|
+
| Error Tracking |
|
|
18
|
+
| +---------------------------------------------------------------+ |
|
|
19
|
+
| | deskCustomError(ErrorDetails, Message, envData) | |
|
|
20
|
+
| | +-- Wraps murphy.error() | |
|
|
21
|
+
| | +-- Adds custom tracking with murphy.addCustomTracking() | |
|
|
22
|
+
| | +-- Supports Error objects or plain objects | |
|
|
23
|
+
| | +-- Handles error details formatting | |
|
|
24
|
+
| +---------------------------------------------------------------+ |
|
|
25
|
+
| |
|
|
26
|
+
| Application Logging |
|
|
27
|
+
| +---------------------------------------------------------------+ |
|
|
28
|
+
| | deskLogger(info, details) | |
|
|
29
|
+
| | +-- Wraps murphy.appLog() | |
|
|
30
|
+
| | +-- Supports INFO, WARN, SEVERE levels | |
|
|
31
|
+
| | +-- Adds custom tags with _c_ prefix | |
|
|
32
|
+
| +---------------------------------------------------------------+ |
|
|
33
|
+
| |
|
|
34
|
+
| Performance Tracking |
|
|
35
|
+
| +---------------------------------------------------------------+ |
|
|
36
|
+
| | endInitialLoadForMurphyPerformanceLogs() | |
|
|
37
|
+
| | startSoftNavigationMurphyPerformanceLogs({ fromUrl, toUrl }) | |
|
|
38
|
+
| | endSoftNavigationMurphyPerformanceLogs() | |
|
|
39
|
+
| | setClientMetricTags(value) | |
|
|
40
|
+
| +---------------------------------------------------------------+ |
|
|
41
|
+
| |
|
|
42
|
+
| Presence Checks |
|
|
43
|
+
| +---------------------------------------------------------------+ |
|
|
44
|
+
| | isMurphyPresent() -> Check if murphy exists | |
|
|
45
|
+
| | isMurphyClientMetricsPresent() -> Check client metrics | |
|
|
46
|
+
| | isMurphyClientMetricsInstalled() -> Check if installed | |
|
|
47
|
+
| +---------------------------------------------------------------+ |
|
|
48
|
+
| |
|
|
49
|
+
+-------------------------------------------------------------------+
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 1. Presence Checks
|
|
55
|
+
|
|
56
|
+
Before using Murphy, always check if it's available:
|
|
57
|
+
|
|
58
|
+
```javascript
|
|
59
|
+
// Path: src/library/murphy/Utils.js
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Check if Murphy SDK is loaded
|
|
63
|
+
*/
|
|
64
|
+
export function isMurphyPresent() {
|
|
65
|
+
return typeof murphy !== 'undefined';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Check if Murphy client metrics and telemetry are available
|
|
70
|
+
*/
|
|
71
|
+
export function isMurphyClientMetricsPresent() {
|
|
72
|
+
return (
|
|
73
|
+
isMurphyPresent() &&
|
|
74
|
+
typeof murphy.clientmetrics !== 'undefined' &&
|
|
75
|
+
typeof murphy.telemetry !== 'undefined'
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Check if client metrics are installed
|
|
81
|
+
*/
|
|
82
|
+
export function isMurphyClientMetricsInstalled() {
|
|
83
|
+
return murphy.clientmetrics.isClientMetricsInstalled();
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## 2. Error Tracking (deskCustomError)
|
|
90
|
+
|
|
91
|
+
The primary function for reporting errors:
|
|
92
|
+
|
|
93
|
+
```javascript
|
|
94
|
+
/**
|
|
95
|
+
* Report custom error to Murphy
|
|
96
|
+
*
|
|
97
|
+
* @param {Object|Error} ErrorDetails - Error details or Error object
|
|
98
|
+
* @param {string} ErrorDisplayMessage - Human-readable error message
|
|
99
|
+
* @param {Object} envData - Additional environment/context data
|
|
100
|
+
*/
|
|
101
|
+
export function deskCustomError(ErrorDetails, ErrorDisplayMessage, envData) {
|
|
102
|
+
try {
|
|
103
|
+
if (isMurphyPresent()) {
|
|
104
|
+
let error;
|
|
105
|
+
|
|
106
|
+
// Handle Error objects
|
|
107
|
+
if (ErrorDetails instanceof Error) {
|
|
108
|
+
error = ErrorDetails;
|
|
109
|
+
error.message = ErrorDisplayMessage
|
|
110
|
+
? `${ErrorDisplayMessage} - ${error.message}`
|
|
111
|
+
: error.message || 'An error occurred';
|
|
112
|
+
} else {
|
|
113
|
+
// Create new Error from message
|
|
114
|
+
error = new Error(ErrorDisplayMessage || 'An error occurred');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Add tracking data
|
|
118
|
+
murphy && murphy.addCustomTracking(ErrorDetails);
|
|
119
|
+
|
|
120
|
+
// Build custom tags
|
|
121
|
+
let customErrorDataObj;
|
|
122
|
+
if (envData && typeof envData === 'object') {
|
|
123
|
+
const { errorType, ...errorData } = envData;
|
|
124
|
+
customErrorDataObj = {
|
|
125
|
+
customTags: {
|
|
126
|
+
errordetail: errorData,
|
|
127
|
+
errortype: errorType
|
|
128
|
+
},
|
|
129
|
+
preventClientGrouping: true
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Report to Murphy
|
|
134
|
+
murphy && murphy.error(error, undefined, customErrorDataObj);
|
|
135
|
+
}
|
|
136
|
+
} catch (error) {
|
|
137
|
+
console.error('error in desk custom error', error);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Also exposed globally for error handling
|
|
142
|
+
window._windowDeskCustomError = deskCustomError;
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Usage Examples
|
|
146
|
+
|
|
147
|
+
```javascript
|
|
148
|
+
// Basic error reporting
|
|
149
|
+
deskCustomError(
|
|
150
|
+
{ type: 'api_error', endpoint: '/tickets' },
|
|
151
|
+
'Failed to load tickets'
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
// With Error object
|
|
155
|
+
try {
|
|
156
|
+
await saveTicket(data);
|
|
157
|
+
} catch (err) {
|
|
158
|
+
deskCustomError(err, 'Ticket save failed', {
|
|
159
|
+
errorType: 'save_error',
|
|
160
|
+
ticketId: data.id
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// HTTP error tracking (resource load failure)
|
|
165
|
+
deskCustomError(
|
|
166
|
+
{
|
|
167
|
+
type: 'http',
|
|
168
|
+
category: 'xhr',
|
|
169
|
+
data: {
|
|
170
|
+
method: 'script',
|
|
171
|
+
url: loadFile,
|
|
172
|
+
status_code: 404
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
`Resource entry failed - ${JSON.stringify(loadFile)}`
|
|
176
|
+
);
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## 3. Application Logging (deskLogger)
|
|
182
|
+
|
|
183
|
+
Structured logging to Murphy:
|
|
184
|
+
|
|
185
|
+
```javascript
|
|
186
|
+
import { LOG_LEVEL } from './Constants';
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Send application logs to Murphy
|
|
190
|
+
*
|
|
191
|
+
* @param {string|Object} info - Log level/message or {type, message}
|
|
192
|
+
* @param {Object} details - Additional log details
|
|
193
|
+
*/
|
|
194
|
+
export function deskLogger(info, details) {
|
|
195
|
+
if (isMurphyPresent()) {
|
|
196
|
+
let type;
|
|
197
|
+
let message;
|
|
198
|
+
|
|
199
|
+
// Handle object input
|
|
200
|
+
if (info && typeof info === 'object') {
|
|
201
|
+
({ type = LOG_LEVEL.INFO, message } = info);
|
|
202
|
+
} else {
|
|
203
|
+
type = LOG_LEVEL.INFO;
|
|
204
|
+
message = info;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Build custom tags with _c_ prefix
|
|
208
|
+
let customTagObj = {};
|
|
209
|
+
if (details && typeof details === 'object') {
|
|
210
|
+
Object.keys(details).forEach((key) => {
|
|
211
|
+
customTagObj[`_c_${key}`] = details[key];
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Send to Murphy
|
|
216
|
+
message && murphy.appLog(type, message, customTagObj);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Log Levels
|
|
222
|
+
|
|
223
|
+
```
|
|
224
|
+
+-----------------------------------+
|
|
225
|
+
| LOG_LEVEL Constants |
|
|
226
|
+
+-----------------------------------+
|
|
227
|
+
| INFO -> Informational logs |
|
|
228
|
+
| WARN -> Warning conditions |
|
|
229
|
+
| SEVERE -> Critical errors |
|
|
230
|
+
+-----------------------------------+
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Usage Examples
|
|
234
|
+
|
|
235
|
+
```javascript
|
|
236
|
+
// Simple info log
|
|
237
|
+
deskLogger('User opened ticket detail view');
|
|
238
|
+
|
|
239
|
+
// With details
|
|
240
|
+
deskLogger('Ticket updated', {
|
|
241
|
+
ticketId: '12345',
|
|
242
|
+
field: 'status',
|
|
243
|
+
oldValue: 'Open',
|
|
244
|
+
newValue: 'Closed'
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
// With log level
|
|
248
|
+
deskLogger(
|
|
249
|
+
{ type: LOG_LEVEL.WARN, message: 'Slow API response detected' },
|
|
250
|
+
{ endpoint: '/tickets', duration: 5000 }
|
|
251
|
+
);
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## 4. Performance Tracking
|
|
257
|
+
|
|
258
|
+
### Time to Interactive
|
|
259
|
+
|
|
260
|
+
```javascript
|
|
261
|
+
/**
|
|
262
|
+
* Mark end of initial load
|
|
263
|
+
*/
|
|
264
|
+
export function endInitialLoadForMurphyPerformanceLogs({
|
|
265
|
+
isCancelled = false,
|
|
266
|
+
...value
|
|
267
|
+
} = {}) {
|
|
268
|
+
if (!isMurphyClientMetricsPresent()) {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const modeOfOperation = isCancelled
|
|
273
|
+
? 'Cancelled_route_navigation'
|
|
274
|
+
: 'Success_route_navigation';
|
|
275
|
+
|
|
276
|
+
setClientMetricTags({
|
|
277
|
+
routerState: modeOfOperation,
|
|
278
|
+
...value
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
murphy.clientmetrics.setTimeToInteractive();
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### Soft Navigation Tracking
|
|
286
|
+
|
|
287
|
+
```javascript
|
|
288
|
+
/**
|
|
289
|
+
* Start tracking route change
|
|
290
|
+
*/
|
|
291
|
+
export function startSoftNavigationMurphyPerformanceLogs({ fromUrl, toUrl }) {
|
|
292
|
+
if (!isMurphyClientMetricsPresent()) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
murphy.clientmetrics.startSoftNavigation({
|
|
297
|
+
from_url: fromUrl,
|
|
298
|
+
to_url: toUrl,
|
|
299
|
+
from_normalised_url: fromUrl,
|
|
300
|
+
to_normalised_url: toUrl
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* End tracking route change
|
|
306
|
+
*/
|
|
307
|
+
export function endSoftNavigationMurphyPerformanceLogs(isCancelled = false) {
|
|
308
|
+
if (!isMurphyClientMetricsPresent()) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const modeOfOperation = isCancelled
|
|
313
|
+
? 'Cancelled_route_navigation'
|
|
314
|
+
: 'Success_route_navigation';
|
|
315
|
+
|
|
316
|
+
setClientMetricTags({
|
|
317
|
+
routerState: modeOfOperation
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
murphy.clientmetrics.endSoftNavigation();
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Usage in Router
|
|
325
|
+
|
|
326
|
+
```javascript
|
|
327
|
+
// In route change handler
|
|
328
|
+
const handleRouteChange = (fromUrl, toUrl) => {
|
|
329
|
+
// Start tracking
|
|
330
|
+
startSoftNavigationMurphyPerformanceLogs({ fromUrl, toUrl });
|
|
331
|
+
|
|
332
|
+
// ... perform navigation ...
|
|
333
|
+
|
|
334
|
+
// End tracking when component mounts
|
|
335
|
+
endSoftNavigationMurphyPerformanceLogs();
|
|
336
|
+
};
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
---
|
|
340
|
+
|
|
341
|
+
## 5. Failed Resource Tracking
|
|
342
|
+
|
|
343
|
+
Automatically tracks failed resource loads:
|
|
344
|
+
|
|
345
|
+
```javascript
|
|
346
|
+
/**
|
|
347
|
+
* Monkey-patches document.createElement to track failed resources
|
|
348
|
+
*/
|
|
349
|
+
export function failedResourcesEntry() {
|
|
350
|
+
if (isMurphyPresent()) {
|
|
351
|
+
document.createElement = (function (create) {
|
|
352
|
+
return function () {
|
|
353
|
+
var ret = create.apply(this, arguments);
|
|
354
|
+
|
|
355
|
+
if (
|
|
356
|
+
ret.tagName.toLowerCase() === 'script' ||
|
|
357
|
+
ret.tagName.toLowerCase() === 'link'
|
|
358
|
+
) {
|
|
359
|
+
// Track load start time
|
|
360
|
+
ret.setAttribute('X-MURPHY-LOAD-START', Date.now());
|
|
361
|
+
|
|
362
|
+
// Handle successful load
|
|
363
|
+
ret.addEventListener('load', function () {
|
|
364
|
+
var loadFile = getLoadFile(this);
|
|
365
|
+
setCors(loadFile, ret);
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
// Handle failed load
|
|
369
|
+
ret.addEventListener('error', function () {
|
|
370
|
+
var loadFile = getLoadFile(this);
|
|
371
|
+
|
|
372
|
+
deskCustomError(
|
|
373
|
+
{
|
|
374
|
+
type: 'http',
|
|
375
|
+
category: 'xhr',
|
|
376
|
+
data: {
|
|
377
|
+
method: this.tagName.toLowerCase(),
|
|
378
|
+
url: loadFile,
|
|
379
|
+
startimestamp: this.getAttribute('X-MURPHY-LOAD-START'),
|
|
380
|
+
timeTaken: Date.now() - this.getAttribute('X-MURPHY-LOAD-START'),
|
|
381
|
+
status_code: 404
|
|
382
|
+
}
|
|
383
|
+
},
|
|
384
|
+
`Resource entry failed - ${JSON.stringify(loadFile)}`
|
|
385
|
+
);
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
return ret;
|
|
390
|
+
};
|
|
391
|
+
})(document.createElement);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
---
|
|
397
|
+
|
|
398
|
+
## Integration Pattern
|
|
399
|
+
|
|
400
|
+
```
|
|
401
|
+
+-------------------------------------------------------------------+
|
|
402
|
+
| TYPICAL MURPHY INTEGRATION IN DESK CLIENT |
|
|
403
|
+
+-------------------------------------------------------------------+
|
|
404
|
+
| |
|
|
405
|
+
| Component/Module Code |
|
|
406
|
+
| +---------------------------------------------------------------+ |
|
|
407
|
+
| | import { | |
|
|
408
|
+
| | deskCustomError, | |
|
|
409
|
+
| | deskLogger | |
|
|
410
|
+
| | } from 'library/murphy/Utils'; | |
|
|
411
|
+
| | | |
|
|
412
|
+
| | async function saveTicket(data) { | |
|
|
413
|
+
| | try { | |
|
|
414
|
+
| | deskLogger('Saving ticket', { ticketId: data.id }); | |
|
|
415
|
+
| | await api.save(data); | |
|
|
416
|
+
| | deskLogger('Ticket saved successfully'); | |
|
|
417
|
+
| | } catch (error) { | |
|
|
418
|
+
| | deskCustomError(error, 'Failed to save ticket', { | |
|
|
419
|
+
| | errorType: 'api_error', | |
|
|
420
|
+
| | ticketId: data.id | |
|
|
421
|
+
| | }); | |
|
|
422
|
+
| | throw error; | |
|
|
423
|
+
| | } | |
|
|
424
|
+
| | } | |
|
|
425
|
+
| +---------------------------------------------------------------+ |
|
|
426
|
+
| |
|
|
427
|
+
+-------------------------------------------------------------------+
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
---
|
|
431
|
+
|
|
432
|
+
## Summary
|
|
433
|
+
|
|
434
|
+
```
|
|
435
|
+
+-------------------------------------------------------------------+
|
|
436
|
+
| DESK CLIENT MURPHY USAGE SUMMARY |
|
|
437
|
+
+-------------------------------------------------------------------+
|
|
438
|
+
| |
|
|
439
|
+
| Error Tracking: |
|
|
440
|
+
| +-- deskCustomError() -> Standardized error reporting |
|
|
441
|
+
| +-- Supports Error objects and plain objects |
|
|
442
|
+
| +-- Adds custom tags and tracking data |
|
|
443
|
+
| |
|
|
444
|
+
| Logging: |
|
|
445
|
+
| +-- deskLogger() -> Structured application logs |
|
|
446
|
+
| +-- Supports INFO, WARN, SEVERE levels |
|
|
447
|
+
| +-- Auto-prefixes custom tags with _c_ |
|
|
448
|
+
| |
|
|
449
|
+
| Performance: |
|
|
450
|
+
| +-- Initial load tracking |
|
|
451
|
+
| +-- Soft navigation tracking |
|
|
452
|
+
| +-- Resource load failure detection |
|
|
453
|
+
| |
|
|
454
|
+
| Safety: |
|
|
455
|
+
| +-- All functions check isMurphyPresent() first |
|
|
456
|
+
| +-- Graceful degradation if Murphy unavailable |
|
|
457
|
+
| |
|
|
458
|
+
+-------------------------------------------------------------------+
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
---
|
|
462
|
+
|
|
463
|
+
## Related Documents
|
|
464
|
+
|
|
465
|
+
- [04-MURPHY_FRONTEND_INIT.md](./04-MURPHY_FRONTEND_INIT.md) - Frontend initialization
|
|
466
|
+
- [06-MURPHY_I18N_INTEGRATION.md](./06-MURPHY_I18N_INTEGRATION.md) - i18n library integration
|
|
467
|
+
- [07-MURPHY_WHY_I18N_APPROACH.md](./07-MURPHY_WHY_I18N_APPROACH.md) - Why i18n approach
|