@zohodesk/i18n 1.0.0-beta.34 → 1.0.0-beta.36-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.
Files changed (51) hide show
  1. package/docs/murphy/01-MURPHY_OVERVIEW.md +148 -0
  2. package/docs/murphy/02-MURPHY_ARCHITECTURE.md +283 -0
  3. package/docs/murphy/03-MURPHY_BACKEND_CONFIG.md +337 -0
  4. package/docs/murphy/04-MURPHY_FRONTEND_INIT.md +437 -0
  5. package/docs/murphy/05-MURPHY_DESK_CLIENT_USAGE.md +467 -0
  6. package/docs/murphy/06-MURPHY_I18N_INTEGRATION.md +402 -0
  7. package/docs/murphy/07-MURPHY_WHY_I18N_APPROACH.md +391 -0
  8. package/es/components/DateTimeDiffFormat.js +5 -19
  9. package/es/components/FormatText.js +2 -2
  10. package/es/components/HOCI18N.js +32 -43
  11. package/es/components/I18N.js +2 -13
  12. package/es/components/I18NProvider.js +0 -9
  13. package/es/components/PluralFormat.js +3 -5
  14. package/es/components/UserTimeDiffFormat.js +5 -9
  15. package/es/components/__tests__/DateTimeDiffFormat.spec.js +157 -221
  16. package/es/components/__tests__/FormatText.spec.js +2 -2
  17. package/es/components/__tests__/HOCI18N.spec.js +2 -4
  18. package/es/components/__tests__/I18N.spec.js +6 -4
  19. package/es/components/__tests__/I18NProvider.spec.js +4 -4
  20. package/es/components/__tests__/PluralFormat.spec.js +2 -2
  21. package/es/components/__tests__/UserTimeDiffFormat.spec.js +249 -348
  22. package/es/index.js +1 -0
  23. package/es/utils/__tests__/jsxTranslations.spec.js +3 -7
  24. package/es/utils/errorReporter.js +35 -0
  25. package/es/utils/index.js +42 -92
  26. package/es/utils/jsxTranslations.js +34 -52
  27. package/lib/I18NContext.js +2 -7
  28. package/lib/components/DateTimeDiffFormat.js +46 -87
  29. package/lib/components/FormatText.js +18 -41
  30. package/lib/components/HOCI18N.js +24 -59
  31. package/lib/components/I18N.js +27 -64
  32. package/lib/components/I18NProvider.js +27 -63
  33. package/lib/components/PluralFormat.js +24 -50
  34. package/lib/components/UserTimeDiffFormat.js +43 -72
  35. package/lib/components/__tests__/DateTimeDiffFormat.spec.js +95 -165
  36. package/lib/components/__tests__/FormatText.spec.js +3 -10
  37. package/lib/components/__tests__/HOCI18N.spec.js +3 -14
  38. package/lib/components/__tests__/I18N.spec.js +4 -12
  39. package/lib/components/__tests__/I18NProvider.spec.js +8 -23
  40. package/lib/components/__tests__/PluralFormat.spec.js +3 -11
  41. package/lib/components/__tests__/UserTimeDiffFormat.spec.js +157 -225
  42. package/lib/index.js +25 -23
  43. package/lib/utils/__tests__/jsxTranslations.spec.js +1 -12
  44. package/lib/utils/errorReporter.js +44 -0
  45. package/lib/utils/index.js +49 -125
  46. package/lib/utils/jsxTranslations.js +61 -100
  47. package/package.json +1 -1
  48. package/src/index.js +5 -0
  49. package/src/utils/errorReporter.js +41 -0
  50. package/src/utils/index.js +8 -1
  51. package/src/utils/jsxTranslations.js +8 -1
@@ -0,0 +1,402 @@
1
+ # Murphy Integration in i18n Library
2
+
3
+ ## Overview
4
+
5
+ The i18n library (`@zohodesk/i18n`) integrates with Murphy to track translation failures - when users see fallback text instead of proper translations.
6
+
7
+ ---
8
+
9
+ ## Why Track i18n Errors?
10
+
11
+ ```
12
+ +-------------------------------------------------------------------+
13
+ | THE PROBLEM: SILENT TRANSLATION FAILURES |
14
+ +-------------------------------------------------------------------+
15
+ | |
16
+ | Without Tracking: |
17
+ | |
18
+ | User sees: "desk.tickets.save.button" |
19
+ | Developer: Has no idea this is happening! |
20
+ | |
21
+ | Causes: |
22
+ | +-- Missing translation key |
23
+ | +-- i18n object not initialized |
24
+ | +-- Wrong key name used |
25
+ | +-- Bundle not loaded |
26
+ | |
27
+ | With Murphy Tracking: |
28
+ | |
29
+ | User sees: "desk.tickets.save.button" |
30
+ | Murphy Dashboard: "I18N_MISSING_KEY: desk.tickets.save.button" |
31
+ | Developer: Can see and fix immediately! |
32
+ | |
33
+ +-------------------------------------------------------------------+
34
+ ```
35
+
36
+ ---
37
+
38
+ ## Implementation Architecture
39
+
40
+ ```
41
+ +-------------------------------------------------------------------+
42
+ | I18N LIBRARY MURPHY INTEGRATION |
43
+ +-------------------------------------------------------------------+
44
+ | |
45
+ | +-------------------------------------------------------------+ |
46
+ | | errorReporter.js (NEW FILE) | |
47
+ | | Path: /jsapps/i18n/src/utils/errorReporter.js | |
48
+ | | | |
49
+ | | Error Types: | |
50
+ | | +--------------------------------------------------------+ | |
51
+ | | | I18N_UNDEFINED_OBJECT | i18n object not initialized | | |
52
+ | | | I18N_MISSING_KEY | Translation key not found | | |
53
+ | | | I18N_NUMERIC_FALLBACK | Numeric ID showing fallback | | |
54
+ | | +--------------------------------------------------------+ | |
55
+ | | | |
56
+ | | Deduplication: | |
57
+ | | +-- Uses Set() to track reported keys | |
58
+ | | +-- Each key reported only once per session | |
59
+ | | | |
60
+ | | Safety Check: | |
61
+ | | +-- isMurphyAvailable() checks if global murphy exists | |
62
+ | | +-- Silently skips if Murphy not present | |
63
+ | +-------------------------------------------------------------+ |
64
+ | | |
65
+ | v |
66
+ | +-------------------------------------------------------------+ |
67
+ | | Integration Points | |
68
+ | | | |
69
+ | | getI18NValue() (utils/index.js) | |
70
+ | | +-- Called for text translations | |
71
+ | | +-- Reports MISSING_KEY or NUMERIC_FALLBACK | |
72
+ | | | |
73
+ | | getI18NComponent() (utils/jsxTranslations.js) | |
74
+ | | +-- Called for JSX translations with components | |
75
+ | | +-- Reports UNDEFINED_OBJECT or MISSING_KEY | |
76
+ | +-------------------------------------------------------------+ |
77
+ | |
78
+ +-------------------------------------------------------------------+
79
+ ```
80
+
81
+ ---
82
+
83
+ ## Code Implementation
84
+
85
+ ### 1. errorReporter.js
86
+
87
+ ```javascript
88
+ // Path: /jsapps/i18n/src/utils/errorReporter.js
89
+
90
+ // Error types for categorization in Murphy dashboard
91
+ export const I18N_ERROR_TYPES = {
92
+ UNDEFINED_OBJECT: 'I18N_UNDEFINED_OBJECT', // i18n not initialized
93
+ MISSING_KEY: 'I18N_MISSING_KEY', // Key not found
94
+ NUMERIC_FALLBACK: 'I18N_NUMERIC_FALLBACK' // Numeric ID as fallback
95
+ };
96
+
97
+ // Deduplication - report each unique key only once per session
98
+ const reportedKeys = new Set();
99
+
100
+ /**
101
+ * Check if Murphy SDK is available globally
102
+ */
103
+ function isMurphyAvailable() {
104
+ return typeof murphy !== 'undefined' && typeof murphy.error === 'function';
105
+ }
106
+
107
+ /**
108
+ * Report i18n error to Murphy
109
+ * @param {string} type - Error type from I18N_ERROR_TYPES
110
+ * @param {string} key - The i18n key that failed
111
+ */
112
+ export function reportI18NError(type, key) {
113
+ // Deduplicate: only report each unique key once per session
114
+ const dedupeKey = `${type}:${key}`;
115
+ if (reportedKeys.has(dedupeKey)) {
116
+ return;
117
+ }
118
+ reportedKeys.add(dedupeKey);
119
+
120
+ // Report to Murphy if available
121
+ if (isMurphyAvailable()) {
122
+ const error = new Error(`i18n ${type}: ${key}`);
123
+ error.name = type;
124
+
125
+ murphy.error(error, undefined, {
126
+ customTags: {
127
+ errorType: type,
128
+ i18nKey: key,
129
+ category: 'i18n'
130
+ },
131
+ preventClientGrouping: true
132
+ });
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Clear reported keys (useful for testing)
138
+ */
139
+ export function clearReportedKeys() {
140
+ reportedKeys.clear();
141
+ }
142
+ ```
143
+
144
+ ### 2. Integration in getI18NValue()
145
+
146
+ ```javascript
147
+ // Path: /jsapps/i18n/src/utils/index.js
148
+
149
+ import { reportI18NError, I18N_ERROR_TYPES } from './errorReporter';
150
+
151
+ export const getI18NValue = (i18n) => (key, values) => {
152
+ // Check if i18n object exists
153
+ if (typeof i18n === 'undefined') {
154
+ reportI18NError(I18N_ERROR_TYPES.UNDEFINED_OBJECT, 'i18n_object');
155
+ return key;
156
+ }
157
+
158
+ // Look up translation
159
+ let i18nStr = i18n[key];
160
+
161
+ // If not found, report error and return fallback
162
+ if (i18nStr === undefined) {
163
+ const isNumeric = /^\d+$/.test(key);
164
+ reportI18NError(
165
+ isNumeric ? I18N_ERROR_TYPES.NUMERIC_FALLBACK : I18N_ERROR_TYPES.MISSING_KEY,
166
+ key
167
+ );
168
+ return getFallbackText(key);
169
+ }
170
+
171
+ // Process and return translation
172
+ return replaceI18NValuesWithRegex(unescapeUnicode(i18nStr), values);
173
+ };
174
+ ```
175
+
176
+ ### 3. Integration in getI18NComponent()
177
+
178
+ ```javascript
179
+ // Path: /jsapps/i18n/src/utils/jsxTranslations.js
180
+
181
+ import { reportI18NError, I18N_ERROR_TYPES } from './errorReporter';
182
+
183
+ export function getI18NComponent(i18n) {
184
+ // Check if i18n object exists
185
+ if (typeof i18n === 'undefined') {
186
+ reportI18NError(I18N_ERROR_TYPES.UNDEFINED_OBJECT, 'i18n_object_jsx');
187
+ return (key) => key;
188
+ }
189
+
190
+ return (key) => {
191
+ let i18nStr = i18n[key];
192
+
193
+ // If not found, report error and return fallback
194
+ if (i18nStr === undefined) {
195
+ const isNumeric = /^\d+$/.test(key);
196
+ reportI18NError(
197
+ isNumeric ? I18N_ERROR_TYPES.NUMERIC_FALLBACK : I18N_ERROR_TYPES.MISSING_KEY,
198
+ key
199
+ );
200
+ return getFallbackText(key);
201
+ }
202
+
203
+ // Process JSX translation
204
+ if (typeof i18nStr === 'string') {
205
+ i18nStr = unescapeUnicode(i18nStr);
206
+ const value = prepareI18NFunc({ i18nKey: i18nStr });
207
+ // Cache processed value
208
+ window.loadI18nChunk && window.loadI18nChunk({ [key]: value });
209
+ i18nStr = value;
210
+ }
211
+
212
+ return i18nStr;
213
+ };
214
+ }
215
+ ```
216
+
217
+ ### 4. Exports from index.js
218
+
219
+ ```javascript
220
+ // Path: /jsapps/i18n/src/index.js
221
+
222
+ export {
223
+ reportI18NError,
224
+ clearReportedKeys,
225
+ I18N_ERROR_TYPES
226
+ } from './utils/errorReporter';
227
+ ```
228
+
229
+ ---
230
+
231
+ ## Error Types Explained
232
+
233
+ ```
234
+ +-------------------------------------------------------------------+
235
+ | I18N ERROR TYPES |
236
+ +-------------------------------------------------------------------+
237
+ | |
238
+ | I18N_UNDEFINED_OBJECT |
239
+ | +---------------------------------------------------------------+ |
240
+ | | When: i18n object is undefined | |
241
+ | | Cause: i18n not initialized before component render | |
242
+ | | Example: getI18NValue(undefined)('key') | |
243
+ | | Action: Check i18n initialization order | |
244
+ | +---------------------------------------------------------------+ |
245
+ | |
246
+ | I18N_MISSING_KEY |
247
+ | +---------------------------------------------------------------+ |
248
+ | | When: Translation key doesn't exist in i18n object | |
249
+ | | Cause: Key typo, missing translation, wrong bundle | |
250
+ | | Example: i18n['tickets.wrong.key'] === undefined | |
251
+ | | Action: Add translation or fix key name | |
252
+ | +---------------------------------------------------------------+ |
253
+ | |
254
+ | I18N_NUMERIC_FALLBACK |
255
+ | +---------------------------------------------------------------+ |
256
+ | | When: Key is purely numeric (e.g., "12345") | |
257
+ | | Cause: Likely using ID instead of i18n key | |
258
+ | | Example: getI18NValue(i18n)('12345') | |
259
+ | | Action: Check if ID was passed instead of key | |
260
+ | +---------------------------------------------------------------+ |
261
+ | |
262
+ +-------------------------------------------------------------------+
263
+ ```
264
+
265
+ ---
266
+
267
+ ## Deduplication Strategy
268
+
269
+ ```
270
+ +-------------------------------------------------------------------+
271
+ | DEDUPLICATION - WHY IT MATTERS |
272
+ +-------------------------------------------------------------------+
273
+ | |
274
+ | Without Deduplication: |
275
+ | +---------------------------------------------------------------+ |
276
+ | | Component renders 100 times | |
277
+ | | Each render: getI18NValue(i18n)('missing.key') | |
278
+ | | Result: 100 identical errors sent to Murphy! | |
279
+ | | Murphy quota: Exceeded quickly | |
280
+ | +---------------------------------------------------------------+ |
281
+ | |
282
+ | With Deduplication: |
283
+ | +---------------------------------------------------------------+ |
284
+ | | const reportedKeys = new Set(); | |
285
+ | | | |
286
+ | | First render: | |
287
+ | | reportI18NError('MISSING_KEY', 'missing.key') | |
288
+ | | -> Adds to Set, reports to Murphy | |
289
+ | | | |
290
+ | | Subsequent renders: | |
291
+ | | reportI18NError('MISSING_KEY', 'missing.key') | |
292
+ | | -> Already in Set, skip | |
293
+ | | | |
294
+ | | Result: Only 1 error reported per unique key per session! | |
295
+ | +---------------------------------------------------------------+ |
296
+ | |
297
+ +-------------------------------------------------------------------+
298
+ ```
299
+
300
+ ---
301
+
302
+ ## Murphy Dashboard View
303
+
304
+ When errors are reported, they appear in Murphy dashboard:
305
+
306
+ ```
307
+ +-------------------------------------------------------------------+
308
+ | MURPHY DASHBOARD - I18N ERRORS |
309
+ +-------------------------------------------------------------------+
310
+ | |
311
+ | Filter by: category = 'i18n' |
312
+ | |
313
+ | +---------------------------------------------------------------+ |
314
+ | | Error: I18N_MISSING_KEY: desk.tickets.save.button | |
315
+ | | Tags: | |
316
+ | | errorType: I18N_MISSING_KEY | |
317
+ | | i18nKey: desk.tickets.save.button | |
318
+ | | category: i18n | |
319
+ | | Count: 1,234 (unique users) | |
320
+ | | First seen: 2024-01-15 | |
321
+ | +---------------------------------------------------------------+ |
322
+ | |
323
+ | +---------------------------------------------------------------+ |
324
+ | | Error: I18N_NUMERIC_FALLBACK: 7890123 | |
325
+ | | Tags: | |
326
+ | | errorType: I18N_NUMERIC_FALLBACK | |
327
+ | | i18nKey: 7890123 | |
328
+ | | category: i18n | |
329
+ | | Count: 45 (unique users) | |
330
+ | | First seen: 2024-01-18 | |
331
+ | +---------------------------------------------------------------+ |
332
+ | |
333
+ +-------------------------------------------------------------------+
334
+ ```
335
+
336
+ ---
337
+
338
+ ## Files Modified
339
+
340
+ | File | Change |
341
+ |------|--------|
342
+ | `src/utils/errorReporter.js` | NEW - Error reporting module |
343
+ | `src/utils/index.js` | Added reportI18NError calls in getI18NValue |
344
+ | `src/utils/jsxTranslations.js` | Added reportI18NError calls in getI18NComponent |
345
+ | `src/index.js` | Added exports for error reporting functions |
346
+
347
+ ---
348
+
349
+ ## Testing
350
+
351
+ ```javascript
352
+ import {
353
+ reportI18NError,
354
+ clearReportedKeys,
355
+ I18N_ERROR_TYPES
356
+ } from '@zohodesk/i18n';
357
+
358
+ // Test: Manual error reporting
359
+ reportI18NError(I18N_ERROR_TYPES.MISSING_KEY, 'test.key');
360
+
361
+ // Test: Clear for fresh test
362
+ clearReportedKeys();
363
+
364
+ // Test: Deduplication
365
+ reportI18NError(I18N_ERROR_TYPES.MISSING_KEY, 'same.key');
366
+ reportI18NError(I18N_ERROR_TYPES.MISSING_KEY, 'same.key'); // Skipped
367
+ ```
368
+
369
+ ---
370
+
371
+ ## Summary
372
+
373
+ ```
374
+ +-------------------------------------------------------------------+
375
+ | I18N MURPHY INTEGRATION SUMMARY |
376
+ +-------------------------------------------------------------------+
377
+ | |
378
+ | What's Tracked: |
379
+ | +-- Missing translation keys |
380
+ | +-- Undefined i18n object |
381
+ | +-- Numeric fallback (potential ID misuse) |
382
+ | |
383
+ | Key Features: |
384
+ | +-- Automatic reporting on failure |
385
+ | +-- Deduplication per session |
386
+ | +-- Safe if Murphy not available |
387
+ | +-- Zero overhead on success |
388
+ | |
389
+ | Integration Points: |
390
+ | +-- getI18NValue() for text translations |
391
+ | +-- getI18NComponent() for JSX translations |
392
+ | |
393
+ +-------------------------------------------------------------------+
394
+ ```
395
+
396
+ ---
397
+
398
+ ## Related Documents
399
+
400
+ - [01-MURPHY_OVERVIEW.md](./01-MURPHY_OVERVIEW.md) - What is Murphy
401
+ - [05-MURPHY_DESK_CLIENT_USAGE.md](./05-MURPHY_DESK_CLIENT_USAGE.md) - Desk client usage
402
+ - [07-MURPHY_WHY_I18N_APPROACH.md](./07-MURPHY_WHY_I18N_APPROACH.md) - Why i18n approach