@elsahafy/ux-mcp-server 2.0.0
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 +190 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1766 -0
- package/knowledge/animation.json +469 -0
- package/knowledge/dark-mode.json +212 -0
- package/knowledge/design-tokens.json +286 -0
- package/knowledge/error-messages.json +307 -0
- package/knowledge/i18n.json +464 -0
- package/knowledge/nielsen-heuristics.json +253 -0
- package/knowledge/performance.json +347 -0
- package/knowledge/react-patterns.json +353 -0
- package/knowledge/responsive-design.json +258 -0
- package/knowledge/seo.json +518 -0
- package/knowledge/ui-patterns.json +344 -0
- package/knowledge/wcag-guidelines.json +201 -0
- package/package.json +62 -0
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Internationalization (i18n) Patterns",
|
|
3
|
+
"description": "Guidelines for building multilingual, globally accessible applications",
|
|
4
|
+
"core_concepts": {
|
|
5
|
+
"i18n_vs_l10n": {
|
|
6
|
+
"internationalization": {
|
|
7
|
+
"abbreviation": "i18n (18 letters between i and n)",
|
|
8
|
+
"definition": "Designing software to support multiple languages/regions without code changes",
|
|
9
|
+
"examples": [
|
|
10
|
+
"Externalizing strings to resource files",
|
|
11
|
+
"Supporting Unicode",
|
|
12
|
+
"Designing flexible layouts for text expansion",
|
|
13
|
+
"Using locale-aware formatting"
|
|
14
|
+
]
|
|
15
|
+
},
|
|
16
|
+
"localization": {
|
|
17
|
+
"abbreviation": "l10n (10 letters between l and n)",
|
|
18
|
+
"definition": "Adapting software for a specific language/region",
|
|
19
|
+
"examples": [
|
|
20
|
+
"Translating text",
|
|
21
|
+
"Adapting date/number formats",
|
|
22
|
+
"Localizing images and media",
|
|
23
|
+
"Cultural customization"
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"locale": {
|
|
28
|
+
"definition": "Language and regional variant identifier",
|
|
29
|
+
"format": "language-REGION (e.g., en-US, es-MX, zh-CN)",
|
|
30
|
+
"components": {
|
|
31
|
+
"language": "ISO 639-1 code (en, es, zh, ar)",
|
|
32
|
+
"region": "ISO 3166-1 alpha-2 code (US, MX, CN, SA)",
|
|
33
|
+
"script": "Optional - ISO 15924 (zh-Hans, zh-Hant)"
|
|
34
|
+
},
|
|
35
|
+
"examples": {
|
|
36
|
+
"en-US": "English (United States)",
|
|
37
|
+
"en-GB": "English (United Kingdom)",
|
|
38
|
+
"es-ES": "Spanish (Spain)",
|
|
39
|
+
"es-MX": "Spanish (Mexico)",
|
|
40
|
+
"zh-CN": "Chinese (Simplified, China)",
|
|
41
|
+
"zh-TW": "Chinese (Traditional, Taiwan)",
|
|
42
|
+
"ar-SA": "Arabic (Saudi Arabia)",
|
|
43
|
+
"pt-BR": "Portuguese (Brazil)",
|
|
44
|
+
"pt-PT": "Portuguese (Portugal)"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"text_handling": {
|
|
49
|
+
"string_externalization": {
|
|
50
|
+
"principle": "Never hardcode user-facing text in code",
|
|
51
|
+
"approach": "Store all text in translation files",
|
|
52
|
+
"formats": {
|
|
53
|
+
"json": {
|
|
54
|
+
"structure": "{\n \"welcome\": \"Welcome\",\n \"login\": {\n \"button\": \"Sign In\",\n \"error\": \"Invalid credentials\"\n }\n}",
|
|
55
|
+
"pros": ["Simple", "Widely supported", "Human-readable"],
|
|
56
|
+
"cons": ["No comments", "Potential nesting complexity"]
|
|
57
|
+
},
|
|
58
|
+
"yaml": {
|
|
59
|
+
"pros": ["Comments supported", "Clean syntax"],
|
|
60
|
+
"use": "Rails, some JS frameworks"
|
|
61
|
+
},
|
|
62
|
+
"gettext_po": {
|
|
63
|
+
"pros": ["Industry standard", "Rich tooling"],
|
|
64
|
+
"use": "PHP, Python, C/C++"
|
|
65
|
+
},
|
|
66
|
+
"icu_messageformat": {
|
|
67
|
+
"pros": ["Handles plurals, genders, complex cases"],
|
|
68
|
+
"use": "React-intl, FormatJS"
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"keys": {
|
|
72
|
+
"naming": "Use descriptive, hierarchical keys",
|
|
73
|
+
"good": "checkout.payment.creditCard.label",
|
|
74
|
+
"bad": "text1, label_a"
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"text_expansion": {
|
|
78
|
+
"problem": "Translated text can be 30-40% longer",
|
|
79
|
+
"examples": {
|
|
80
|
+
"english": "Save (4 chars)",
|
|
81
|
+
"german": "Speichern (10 chars)",
|
|
82
|
+
"finnish": "Tallentaa (9 chars)"
|
|
83
|
+
},
|
|
84
|
+
"design_strategies": [
|
|
85
|
+
"Design with flexible layouts (no fixed widths)",
|
|
86
|
+
"Use overflow: ellipsis for long text",
|
|
87
|
+
"Test with longest language (usually German/Finnish)",
|
|
88
|
+
"Allow text wrapping where appropriate",
|
|
89
|
+
"Avoid tight character limits",
|
|
90
|
+
"Use abbreviations carefully (may not translate)"
|
|
91
|
+
],
|
|
92
|
+
"average_expansion": {
|
|
93
|
+
"english_to_german": "+30%",
|
|
94
|
+
"english_to_french": "+20%",
|
|
95
|
+
"english_to_spanish": "+25%",
|
|
96
|
+
"english_to_finnish": "+30%"
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
"text_direction": {
|
|
100
|
+
"ltr": {
|
|
101
|
+
"languages": "Most languages (English, Spanish, French, etc.)",
|
|
102
|
+
"abbreviation": "Left-to-Right"
|
|
103
|
+
},
|
|
104
|
+
"rtl": {
|
|
105
|
+
"languages": "Arabic, Hebrew, Persian, Urdu",
|
|
106
|
+
"abbreviation": "Right-to-Left",
|
|
107
|
+
"css": "dir='rtl' attribute or :dir(rtl) selector",
|
|
108
|
+
"considerations": [
|
|
109
|
+
"Mirror entire layout",
|
|
110
|
+
"Icons may need flipping (arrows, etc.)",
|
|
111
|
+
"Numbers stay LTR",
|
|
112
|
+
"Keep logos/brand elements unchanged"
|
|
113
|
+
],
|
|
114
|
+
"css_logical_properties": {
|
|
115
|
+
"instead_of": "margin-left, margin-right",
|
|
116
|
+
"use": "margin-inline-start, margin-inline-end",
|
|
117
|
+
"benefit": "Automatically adapts to text direction"
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
"bidi": {
|
|
121
|
+
"name": "Bidirectional text",
|
|
122
|
+
"problem": "Mixing LTR and RTL in same line",
|
|
123
|
+
"solution": "Use Unicode bidi algorithm, HTML dir attribute",
|
|
124
|
+
"example": "Arabic text with English brand name"
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
"unicode": {
|
|
128
|
+
"required": "Always use UTF-8 encoding",
|
|
129
|
+
"html": "<meta charset='UTF-8'>",
|
|
130
|
+
"considerations": [
|
|
131
|
+
"Store data in Unicode (UTF-8)",
|
|
132
|
+
"Avoid ASCII-only assumptions",
|
|
133
|
+
"Support emoji and special characters",
|
|
134
|
+
"Be aware of combining characters",
|
|
135
|
+
"String length !== visual characters"
|
|
136
|
+
]
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
"formatting": {
|
|
140
|
+
"dates": {
|
|
141
|
+
"variations": {
|
|
142
|
+
"US": "MM/DD/YYYY (01/24/2026)",
|
|
143
|
+
"EU": "DD/MM/YYYY (24/01/2026)",
|
|
144
|
+
"ISO_8601": "YYYY-MM-DD (2026-01-24) - recommended for storage",
|
|
145
|
+
"japan": "YYYY年MM月DD日"
|
|
146
|
+
},
|
|
147
|
+
"implementation": {
|
|
148
|
+
"javascript": "new Intl.DateTimeFormat('en-US').format(date)",
|
|
149
|
+
"libraries": ["date-fns with locale", "Luxon", "Day.js with i18n"]
|
|
150
|
+
},
|
|
151
|
+
"best_practices": [
|
|
152
|
+
"Always display dates in user's locale",
|
|
153
|
+
"Store dates in UTC or ISO format",
|
|
154
|
+
"Include timezone information",
|
|
155
|
+
"Be explicit about date formats when needed"
|
|
156
|
+
]
|
|
157
|
+
},
|
|
158
|
+
"numbers": {
|
|
159
|
+
"variations": {
|
|
160
|
+
"US": "1,234.56 (comma thousands, period decimal)",
|
|
161
|
+
"EU": "1.234,56 (period thousands, comma decimal)",
|
|
162
|
+
"india": "1,23,456.78 (different grouping)",
|
|
163
|
+
"switzerland": "1'234.56 (apostrophe thousands)"
|
|
164
|
+
},
|
|
165
|
+
"implementation": {
|
|
166
|
+
"javascript": "new Intl.NumberFormat('en-US').format(number)",
|
|
167
|
+
"currencies": "new Intl.NumberFormat('en-US', {style: 'currency', currency: 'USD'}).format(amount)"
|
|
168
|
+
},
|
|
169
|
+
"best_practices": [
|
|
170
|
+
"Never hardcode number formats",
|
|
171
|
+
"Use locale-aware formatting",
|
|
172
|
+
"Be clear about currency",
|
|
173
|
+
"Round appropriately for currency"
|
|
174
|
+
]
|
|
175
|
+
},
|
|
176
|
+
"currencies": {
|
|
177
|
+
"display": {
|
|
178
|
+
"us": "$1,234.56",
|
|
179
|
+
"eu": "1.234,56 €",
|
|
180
|
+
"uk": "£1,234.56",
|
|
181
|
+
"japan": "¥1,234",
|
|
182
|
+
"india": "₹1,23,456.78"
|
|
183
|
+
},
|
|
184
|
+
"symbol_placement": "Varies by locale (before/after number)",
|
|
185
|
+
"implementation": "Use Intl.NumberFormat with currency style",
|
|
186
|
+
"considerations": [
|
|
187
|
+
"Some currencies don't use decimals (JPY, KRW)",
|
|
188
|
+
"Exchange rates needed for multi-currency",
|
|
189
|
+
"Tax and pricing varies by region"
|
|
190
|
+
]
|
|
191
|
+
},
|
|
192
|
+
"time": {
|
|
193
|
+
"formats": {
|
|
194
|
+
"12_hour": "3:45 PM (US, UK, some others)",
|
|
195
|
+
"24_hour": "15:45 (Most of world)",
|
|
196
|
+
"japan": "午後3:45"
|
|
197
|
+
},
|
|
198
|
+
"implementation": {
|
|
199
|
+
"javascript": "new Intl.DateTimeFormat('en-US', {timeStyle: 'short'}).format(date)"
|
|
200
|
+
},
|
|
201
|
+
"timezones": [
|
|
202
|
+
"Always store in UTC",
|
|
203
|
+
"Display in user's timezone",
|
|
204
|
+
"Show timezone when ambiguous",
|
|
205
|
+
"Handle DST transitions"
|
|
206
|
+
]
|
|
207
|
+
},
|
|
208
|
+
"names": {
|
|
209
|
+
"variations": [
|
|
210
|
+
"Western: Given Middle Family (John Adam Smith)",
|
|
211
|
+
"Eastern: Family Given (Zhang Wei 张伟)",
|
|
212
|
+
"Icelandic: Given Patronymic (Jón Einarsson)",
|
|
213
|
+
"Spanish: Given Paternal Maternal (Juan García López)",
|
|
214
|
+
"Single name cultures: Madonna, Cher"
|
|
215
|
+
],
|
|
216
|
+
"best_practices": [
|
|
217
|
+
"Don't assume name structure",
|
|
218
|
+
"Use single 'Full Name' field when possible",
|
|
219
|
+
"If splitting: 'Given Name' and 'Family Name' (not First/Last)",
|
|
220
|
+
"Allow long names (some are 50+ characters)",
|
|
221
|
+
"Support special characters (accents, etc.)",
|
|
222
|
+
"Don't require middle name"
|
|
223
|
+
],
|
|
224
|
+
"form_design": "One field preferred, or Given + Family (not First/Middle/Last)"
|
|
225
|
+
},
|
|
226
|
+
"addresses": {
|
|
227
|
+
"variations": "Each country has different format",
|
|
228
|
+
"components": {
|
|
229
|
+
"us": "Street, City, State, ZIP",
|
|
230
|
+
"uk": "Street, Town, County, Postcode",
|
|
231
|
+
"japan": "Prefecture, City, Town, Block, Building",
|
|
232
|
+
"china": "Province, City, District, Street, Building"
|
|
233
|
+
},
|
|
234
|
+
"best_practices": [
|
|
235
|
+
"Use international address format",
|
|
236
|
+
"Don't enforce state/province for all countries",
|
|
237
|
+
"Postal code formats vary widely",
|
|
238
|
+
"Some addresses don't have street numbers",
|
|
239
|
+
"Use address validation service (Google Places API)"
|
|
240
|
+
],
|
|
241
|
+
"order": "Display/input in local convention"
|
|
242
|
+
},
|
|
243
|
+
"phone_numbers": {
|
|
244
|
+
"format": "Varies by country",
|
|
245
|
+
"examples": {
|
|
246
|
+
"us": "+1 (555) 123-4567",
|
|
247
|
+
"uk": "+44 20 1234 5678",
|
|
248
|
+
"germany": "+49 30 12345678",
|
|
249
|
+
"india": "+91 98765 43210"
|
|
250
|
+
},
|
|
251
|
+
"best_practices": [
|
|
252
|
+
"Accept various formats (with/without spaces, parentheses)",
|
|
253
|
+
"Store in E.164 format (+15551234567)",
|
|
254
|
+
"Display in local format",
|
|
255
|
+
"Use libraries (libphonenumber)",
|
|
256
|
+
"Include country code selector"
|
|
257
|
+
]
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
"content_considerations": {
|
|
261
|
+
"images_and_media": {
|
|
262
|
+
"text_in_images": "Avoid - hard to translate",
|
|
263
|
+
"alternatives": [
|
|
264
|
+
"Use HTML text over images",
|
|
265
|
+
"Create separate images per locale",
|
|
266
|
+
"Use SVG with text elements"
|
|
267
|
+
],
|
|
268
|
+
"cultural_sensitivity": [
|
|
269
|
+
"Colors have different meanings (white = mourning in some cultures)",
|
|
270
|
+
"Hand gestures vary",
|
|
271
|
+
"Religious symbols",
|
|
272
|
+
"Avoid culturally specific imagery"
|
|
273
|
+
]
|
|
274
|
+
},
|
|
275
|
+
"colors": {
|
|
276
|
+
"cultural_meanings": {
|
|
277
|
+
"red": "Luck/celebration (China), danger (Western), mourning (South Africa)",
|
|
278
|
+
"white": "Purity (Western), mourning (East Asia)",
|
|
279
|
+
"green": "Nature (most), sacred (Islam)",
|
|
280
|
+
"yellow": "Happiness (China), cowardice (Western in some contexts)"
|
|
281
|
+
},
|
|
282
|
+
"best_practice": "Don't rely on color alone to convey meaning (accessibility + i18n)"
|
|
283
|
+
},
|
|
284
|
+
"icons_and_symbols": {
|
|
285
|
+
"considerations": [
|
|
286
|
+
"Mailbox icon differs by country",
|
|
287
|
+
"Phone icons (landline vs mobile)",
|
|
288
|
+
"Shopping carts vs baskets",
|
|
289
|
+
"Check marks (not universal approval symbol)"
|
|
290
|
+
],
|
|
291
|
+
"best_practice": "Combine icons with text labels"
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
"technical_implementation": {
|
|
295
|
+
"language_detection": {
|
|
296
|
+
"methods": [
|
|
297
|
+
"Browser language (navigator.language)",
|
|
298
|
+
"IP geolocation",
|
|
299
|
+
"User preference (saved)",
|
|
300
|
+
"URL structure (/en/, /es/)",
|
|
301
|
+
"Domain (example.com vs example.fr)"
|
|
302
|
+
],
|
|
303
|
+
"best_practice": "Detect but always allow manual override",
|
|
304
|
+
"fallback": "Have a default language when detection fails"
|
|
305
|
+
},
|
|
306
|
+
"url_structure": {
|
|
307
|
+
"subdirectory": {
|
|
308
|
+
"format": "example.com/en/, example.com/es/",
|
|
309
|
+
"pros": ["Easy setup", "Consolidated domain authority"],
|
|
310
|
+
"cons": ["Can't target by country in Search Console"]
|
|
311
|
+
},
|
|
312
|
+
"subdomain": {
|
|
313
|
+
"format": "en.example.com, es.example.com",
|
|
314
|
+
"pros": ["Easy to set up on different servers"],
|
|
315
|
+
"cons": ["Split domain authority", "Separate SSL certs needed"]
|
|
316
|
+
},
|
|
317
|
+
"ccTLD": {
|
|
318
|
+
"format": "example.com, example.es, example.fr",
|
|
319
|
+
"pros": ["Best for SEO in specific countries", "Clear to users"],
|
|
320
|
+
"cons": ["Expensive", "Complex management"]
|
|
321
|
+
},
|
|
322
|
+
"parameter": {
|
|
323
|
+
"format": "example.com?lang=en",
|
|
324
|
+
"pros": ["Easy to implement"],
|
|
325
|
+
"cons": ["Not SEO-friendly", "Poor UX", "Not recommended"]
|
|
326
|
+
},
|
|
327
|
+
"recommended": "Subdirectory or ccTLD",
|
|
328
|
+
"seo": "Use hreflang tags to indicate language variants"
|
|
329
|
+
},
|
|
330
|
+
"hreflang": {
|
|
331
|
+
"purpose": "Tell search engines about language/regional variants",
|
|
332
|
+
"tag": "<link rel='alternate' hreflang='en-US' href='https://example.com/en-us/'>",
|
|
333
|
+
"example": "<!-- English US -->\n<link rel='alternate' hreflang='en-US' href='https://example.com/en-us/'>\n<!-- Spanish -->\n<link rel='alternate' hreflang='es' href='https://example.com/es/'>\n<!-- Default fallback -->\n<link rel='alternate' hreflang='x-default' href='https://example.com/'>",
|
|
334
|
+
"best_practices": [
|
|
335
|
+
"Include self-reference",
|
|
336
|
+
"Add x-default for fallback",
|
|
337
|
+
"Include all language variants",
|
|
338
|
+
"Use in <head> or sitemap"
|
|
339
|
+
]
|
|
340
|
+
},
|
|
341
|
+
"language_switcher": {
|
|
342
|
+
"placement": "Header or footer (consistent location)",
|
|
343
|
+
"display": {
|
|
344
|
+
"native_language": "English, Español, Français (Recommended)",
|
|
345
|
+
"flags": "Avoid - flags represent countries, not languages",
|
|
346
|
+
"current_language": "Show in current language or native"
|
|
347
|
+
},
|
|
348
|
+
"ux": [
|
|
349
|
+
"Make it obvious and accessible",
|
|
350
|
+
"Maintain current page when switching",
|
|
351
|
+
"Remember user's choice",
|
|
352
|
+
"Use globe icon + text"
|
|
353
|
+
]
|
|
354
|
+
},
|
|
355
|
+
"pluralization": {
|
|
356
|
+
"problem": "Different languages have different plural rules",
|
|
357
|
+
"examples": {
|
|
358
|
+
"english": "2 rules (1 item, 2 items)",
|
|
359
|
+
"polish": "3 rules (different forms for 1, 2-4, 5+)",
|
|
360
|
+
"arabic": "6 rules",
|
|
361
|
+
"chinese": "1 rule (no plurals)"
|
|
362
|
+
},
|
|
363
|
+
"solution": "Use ICU MessageFormat or library",
|
|
364
|
+
"icu_example": "{count, plural, =0 {No items} =1 {One item} other {# items}}",
|
|
365
|
+
"libraries": ["FormatJS", "react-intl", "i18next"]
|
|
366
|
+
},
|
|
367
|
+
"gender": {
|
|
368
|
+
"problem": "Some languages have gendered nouns/adjectives",
|
|
369
|
+
"examples": {
|
|
370
|
+
"spanish": "Bienvenido (male) vs Bienvenida (female)",
|
|
371
|
+
"french": "Nouveau (masculine) vs Nouvelle (feminine)"
|
|
372
|
+
},
|
|
373
|
+
"solution": [
|
|
374
|
+
"Use gender-neutral language when possible",
|
|
375
|
+
"Use ICU MessageFormat for gendered variants",
|
|
376
|
+
"Allow users to specify gender preference"
|
|
377
|
+
]
|
|
378
|
+
}
|
|
379
|
+
},
|
|
380
|
+
"testing": {
|
|
381
|
+
"pseudo_localization": {
|
|
382
|
+
"description": "Test with fake translation to find i18n issues",
|
|
383
|
+
"method": "Replace text with accented characters and extend length",
|
|
384
|
+
"example": "Save → Śávë",
|
|
385
|
+
"finds": [
|
|
386
|
+
"Text that can't be translated (hardcoded)",
|
|
387
|
+
"UI that breaks with longer text",
|
|
388
|
+
"Character encoding issues",
|
|
389
|
+
"Truncation problems"
|
|
390
|
+
]
|
|
391
|
+
},
|
|
392
|
+
"checklist": [
|
|
393
|
+
"Test with longest language (German/Finnish)",
|
|
394
|
+
"Test with RTL language (Arabic/Hebrew)",
|
|
395
|
+
"Test with non-Latin script (Japanese, Thai)",
|
|
396
|
+
"Verify all text is translatable",
|
|
397
|
+
"Check UI doesn't break with text expansion",
|
|
398
|
+
"Test date/number formatting in different locales",
|
|
399
|
+
"Verify currency displays correctly",
|
|
400
|
+
"Test language switcher",
|
|
401
|
+
"Check hreflang tags",
|
|
402
|
+
"Validate translations (hire native speakers)"
|
|
403
|
+
]
|
|
404
|
+
},
|
|
405
|
+
"libraries": {
|
|
406
|
+
"javascript": [
|
|
407
|
+
{
|
|
408
|
+
"name": "i18next",
|
|
409
|
+
"features": ["Comprehensive", "Framework agnostic", "Plugins"],
|
|
410
|
+
"use": "Most versatile option"
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
"name": "react-intl (FormatJS)",
|
|
414
|
+
"features": ["React-specific", "ICU MessageFormat", "Date/number formatting"],
|
|
415
|
+
"use": "React applications"
|
|
416
|
+
},
|
|
417
|
+
{
|
|
418
|
+
"name": "vue-i18n",
|
|
419
|
+
"features": ["Vue-specific", "Component localization"],
|
|
420
|
+
"use": "Vue applications"
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
"name": "Intl (native)",
|
|
424
|
+
"features": ["Built-in browser API", "Date/number formatting"],
|
|
425
|
+
"use": "Formatting only, no translation"
|
|
426
|
+
}
|
|
427
|
+
],
|
|
428
|
+
"services": [
|
|
429
|
+
"Lokalise - Translation management",
|
|
430
|
+
"Crowdin - Collaborative translation",
|
|
431
|
+
"Phrase - Localization platform",
|
|
432
|
+
"POEditor - Translation management"
|
|
433
|
+
]
|
|
434
|
+
},
|
|
435
|
+
"best_practices": [
|
|
436
|
+
"Internationalize from the start (retrofitting is expensive)",
|
|
437
|
+
"Externalize all user-facing strings",
|
|
438
|
+
"Use Unicode (UTF-8) everywhere",
|
|
439
|
+
"Design flexible layouts for text expansion",
|
|
440
|
+
"Use locale-aware formatting (dates, numbers, currency)",
|
|
441
|
+
"Support RTL languages from the beginning",
|
|
442
|
+
"Don't make assumptions about names, addresses, phone numbers",
|
|
443
|
+
"Use ICU MessageFormat for complex translations",
|
|
444
|
+
"Include language switcher in prominent location",
|
|
445
|
+
"Use hreflang tags for SEO",
|
|
446
|
+
"Test with pseudo-localization",
|
|
447
|
+
"Hire native speakers for translation and testing",
|
|
448
|
+
"Consider cultural differences (colors, images, gestures)",
|
|
449
|
+
"Provide context to translators",
|
|
450
|
+
"Use translation memory to maintain consistency"
|
|
451
|
+
],
|
|
452
|
+
"common_mistakes": [
|
|
453
|
+
"Hardcoding text in UI",
|
|
454
|
+
"Assuming Western name structure",
|
|
455
|
+
"Using flags for language selection",
|
|
456
|
+
"Concatenating strings for translation",
|
|
457
|
+
"Fixed-width layouts that break with longer text",
|
|
458
|
+
"Not supporting RTL languages",
|
|
459
|
+
"Assuming all currencies have 2 decimal places",
|
|
460
|
+
"Using English-specific pluralization",
|
|
461
|
+
"Not providing context to translators",
|
|
462
|
+
"Translating too late in development cycle"
|
|
463
|
+
]
|
|
464
|
+
}
|