jamdesk 1.1.185 → 1.1.187

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 (29) hide show
  1. package/dist/__tests__/unit/dev-workspace-symlinks.test.d.ts +2 -0
  2. package/dist/__tests__/unit/dev-workspace-symlinks.test.d.ts.map +1 -0
  3. package/dist/__tests__/unit/dev-workspace-symlinks.test.js +112 -0
  4. package/dist/__tests__/unit/dev-workspace-symlinks.test.js.map +1 -0
  5. package/dist/__tests__/unit/language-filter.test.d.ts +2 -0
  6. package/dist/__tests__/unit/language-filter.test.d.ts.map +1 -0
  7. package/dist/__tests__/unit/language-filter.test.js +166 -0
  8. package/dist/__tests__/unit/language-filter.test.js.map +1 -0
  9. package/dist/__tests__/unit/vendored-sync.test.js +95 -1
  10. package/dist/__tests__/unit/vendored-sync.test.js.map +1 -1
  11. package/dist/commands/deploy.d.ts.map +1 -1
  12. package/dist/commands/deploy.js +4 -0
  13. package/dist/commands/deploy.js.map +1 -1
  14. package/dist/lib/language-filter.d.ts +31 -0
  15. package/dist/lib/language-filter.d.ts.map +1 -0
  16. package/dist/lib/language-filter.js +14 -0
  17. package/dist/lib/language-filter.js.map +1 -0
  18. package/package.json +1 -1
  19. package/vendored/components/theme/ThemeToggle.tsx +3 -1
  20. package/vendored/lib/email-templates/build-failure.tsx +1 -0
  21. package/vendored/lib/isr-build-executor.ts +4 -0
  22. package/vendored/lib/locale-fallback.ts +98 -0
  23. package/vendored/lib/regenerate-screenshot-handler.ts +3 -1
  24. package/vendored/lib/render-doc-page.tsx +117 -7
  25. package/vendored/lib/seo.ts +40 -5
  26. package/vendored/lib/ui-strings.ts +268 -23
  27. package/vendored/public/_jd/fonts/fontawesome/css/all.min.css +1 -1
  28. package/vendored/themes/base.css +28 -0
  29. package/vendored/workspace-package-lock.json +87 -3
@@ -19,7 +19,29 @@ const EN: UiStrings = {
19
19
  breadcrumbHome: 'Home',
20
20
  };
21
21
 
22
- const ZH: Partial<UiStrings> = {
22
+ // Aliased locales share one object: `fr-CA`/`fr-ca` are the same chrome as
23
+ // `fr`, `jp`/`ja-jp` the same as `ja`, `pt-BR` the same as `pt`. Only the
24
+ // Chinese pair is genuinely two tables — Traditional is a distinct script,
25
+ // not a `zh` alias, so it gets its own strings rather than a reference.
26
+ const FR: UiStrings = {
27
+ search: 'Rechercher',
28
+ askAi: "Demander à l'IA",
29
+ more: 'Plus',
30
+ toggleMenu: 'Basculer le menu',
31
+ selectLanguage: 'Choisir la langue',
32
+ breadcrumbHome: 'Accueil',
33
+ };
34
+
35
+ const ES: UiStrings = {
36
+ search: 'Buscar',
37
+ askAi: 'Preguntar a la IA',
38
+ more: 'Más',
39
+ toggleMenu: 'Alternar menú',
40
+ selectLanguage: 'Elegir idioma',
41
+ breadcrumbHome: 'Inicio',
42
+ };
43
+
44
+ const ZH_HANS: UiStrings = {
23
45
  search: '搜索',
24
46
  askAi: '询问 AI',
25
47
  more: '更多',
@@ -28,33 +50,256 @@ const ZH: Partial<UiStrings> = {
28
50
  breadcrumbHome: '首页',
29
51
  };
30
52
 
31
- const OVERRIDES: Partial<Record<LanguageCode, Partial<UiStrings>>> = {
32
- fr: {
33
- search: 'Rechercher',
34
- askAi: "Demander à l'IA",
35
- more: 'Plus',
36
- toggleMenu: 'Basculer le menu',
37
- selectLanguage: 'Choisir la langue',
38
- breadcrumbHome: 'Accueil',
53
+ const ZH_HANT: UiStrings = {
54
+ search: '搜尋',
55
+ askAi: '詢問 AI',
56
+ more: '更多',
57
+ toggleMenu: '切換選單',
58
+ selectLanguage: '選擇語言',
59
+ breadcrumbHome: '首頁',
60
+ };
61
+
62
+ const JA: UiStrings = {
63
+ search: '検索',
64
+ askAi: 'AI に質問',
65
+ more: 'その他',
66
+ toggleMenu: 'メニューを切り替え',
67
+ selectLanguage: '言語を選択',
68
+ breadcrumbHome: 'ホーム',
69
+ };
70
+
71
+ const PT: UiStrings = {
72
+ search: 'Pesquisar',
73
+ askAi: 'Perguntar à IA',
74
+ more: 'Mais',
75
+ toggleMenu: 'Alternar menu',
76
+ selectLanguage: 'Selecionar idioma',
77
+ breadcrumbHome: 'Início',
78
+ };
79
+
80
+ /**
81
+ * Chrome strings for every locale the platform offers.
82
+ *
83
+ * The type is a REQUIRED `Record` over every `LanguageCode` except `en`, and
84
+ * each value is a complete `UiStrings` — both deliberate. Until 2026-08-08
85
+ * this was a `Partial` map holding only `fr`, `es`, `zh` and `cn`, so the
86
+ * other 28 offered locales silently fell back to English: a reader on a fully
87
+ * translated German page still got a "Search" / "Ask AI" header. Nothing
88
+ * failed and no test noticed, because falling back to English is exactly what
89
+ * `getUiStrings` is written to do. Making the record total moves that from a
90
+ * silent runtime gap to a compile error the moment a locale is added to
91
+ * `LanguageCode` without its chrome.
92
+ *
93
+ * These are NOT AI-pipeline output — the pipeline never sees them. They are
94
+ * hand-written and have had no native-speaker review, the same caveat that
95
+ * applies to `PLATFORM_SCREENSHOT_NOTES` in `lib/translation/nav.ts`. Six
96
+ * short, highly conventional UI labels are a much lower-risk surface than a
97
+ * full sentence, but they are also the most-seen text on every page, so get
98
+ * them reviewed before leaning on any of these locales commercially.
99
+ */
100
+ const OVERRIDES: Record<Exclude<LanguageCode, 'en'>, UiStrings> = {
101
+ fr: FR,
102
+ 'fr-CA': FR,
103
+ 'fr-ca': FR,
104
+ es: ES,
105
+ zh: ZH_HANS,
106
+ cn: ZH_HANS,
107
+ 'zh-Hans': ZH_HANS,
108
+ 'zh-Hant': ZH_HANT,
109
+ ja: JA,
110
+ jp: JA,
111
+ 'ja-jp': JA,
112
+ pt: PT,
113
+ 'pt-BR': PT,
114
+ de: {
115
+ search: 'Suchen',
116
+ askAi: 'KI fragen',
117
+ more: 'Mehr',
118
+ toggleMenu: 'Menü umschalten',
119
+ selectLanguage: 'Sprache wählen',
120
+ breadcrumbHome: 'Startseite',
121
+ },
122
+ ko: {
123
+ search: '검색',
124
+ askAi: 'AI에게 질문',
125
+ more: '더 보기',
126
+ toggleMenu: '메뉴 전환',
127
+ selectLanguage: '언어 선택',
128
+ breadcrumbHome: '홈',
129
+ },
130
+ it: {
131
+ search: 'Cerca',
132
+ askAi: "Chiedi all'IA",
133
+ more: 'Altro',
134
+ toggleMenu: 'Attiva/disattiva menu',
135
+ selectLanguage: 'Scegli la lingua',
136
+ breadcrumbHome: 'Home',
137
+ },
138
+ ru: {
139
+ search: 'Поиск',
140
+ askAi: 'Спросить ИИ',
141
+ more: 'Ещё',
142
+ toggleMenu: 'Переключить меню',
143
+ selectLanguage: 'Выбрать язык',
144
+ breadcrumbHome: 'Главная',
145
+ },
146
+ ro: {
147
+ search: 'Caută',
148
+ askAi: 'Întreabă AI',
149
+ more: 'Mai multe',
150
+ toggleMenu: 'Comută meniul',
151
+ selectLanguage: 'Alege limba',
152
+ breadcrumbHome: 'Acasă',
153
+ },
154
+ cs: {
155
+ search: 'Hledat',
156
+ askAi: 'Zeptat se AI',
157
+ more: 'Více',
158
+ toggleMenu: 'Přepnout nabídku',
159
+ selectLanguage: 'Vybrat jazyk',
160
+ breadcrumbHome: 'Domů',
161
+ },
162
+ id: {
163
+ search: 'Cari',
164
+ askAi: 'Tanya AI',
165
+ more: 'Lainnya',
166
+ toggleMenu: 'Alihkan menu',
167
+ selectLanguage: 'Pilih bahasa',
168
+ breadcrumbHome: 'Beranda',
169
+ },
170
+ ar: {
171
+ search: 'بحث',
172
+ askAi: 'اسأل الذكاء الاصطناعي',
173
+ more: 'المزيد',
174
+ toggleMenu: 'تبديل القائمة',
175
+ selectLanguage: 'اختر اللغة',
176
+ breadcrumbHome: 'الرئيسية',
177
+ },
178
+ tr: {
179
+ search: 'Ara',
180
+ askAi: 'Yapay zekâya sor',
181
+ more: 'Daha fazla',
182
+ toggleMenu: 'Menüyü aç/kapat',
183
+ selectLanguage: 'Dil seçin',
184
+ breadcrumbHome: 'Ana sayfa',
185
+ },
186
+ hi: {
187
+ search: 'खोजें',
188
+ askAi: 'AI से पूछें',
189
+ more: 'और',
190
+ toggleMenu: 'मेन्यू टॉगल करें',
191
+ selectLanguage: 'भाषा चुनें',
192
+ breadcrumbHome: 'होम',
193
+ },
194
+ sv: {
195
+ search: 'Sök',
196
+ askAi: 'Fråga AI',
197
+ more: 'Mer',
198
+ toggleMenu: 'Växla meny',
199
+ selectLanguage: 'Välj språk',
200
+ breadcrumbHome: 'Hem',
201
+ },
202
+ no: {
203
+ search: 'Søk',
204
+ askAi: 'Spør AI',
205
+ more: 'Mer',
206
+ toggleMenu: 'Veksle meny',
207
+ selectLanguage: 'Velg språk',
208
+ breadcrumbHome: 'Hjem',
209
+ },
210
+ lv: {
211
+ search: 'Meklēt',
212
+ askAi: 'Jautāt MI',
213
+ more: 'Vairāk',
214
+ toggleMenu: 'Pārslēgt izvēlni',
215
+ selectLanguage: 'Izvēlēties valodu',
216
+ breadcrumbHome: 'Sākums',
217
+ },
218
+ nl: {
219
+ search: 'Zoeken',
220
+ askAi: 'AI vragen',
221
+ more: 'Meer',
222
+ toggleMenu: 'Menu schakelen',
223
+ selectLanguage: 'Taal kiezen',
224
+ breadcrumbHome: 'Home',
225
+ },
226
+ uk: {
227
+ search: 'Пошук',
228
+ askAi: 'Запитати ШІ',
229
+ more: 'Більше',
230
+ toggleMenu: 'Перемкнути меню',
231
+ selectLanguage: 'Вибрати мову',
232
+ breadcrumbHome: 'Головна',
233
+ },
234
+ vi: {
235
+ search: 'Tìm kiếm',
236
+ askAi: 'Hỏi AI',
237
+ more: 'Thêm',
238
+ toggleMenu: 'Bật/tắt menu',
239
+ selectLanguage: 'Chọn ngôn ngữ',
240
+ breadcrumbHome: 'Trang chủ',
241
+ },
242
+ pl: {
243
+ search: 'Szukaj',
244
+ askAi: 'Zapytaj AI',
245
+ more: 'Więcej',
246
+ toggleMenu: 'Przełącz menu',
247
+ selectLanguage: 'Wybierz język',
248
+ breadcrumbHome: 'Strona główna',
249
+ },
250
+ uz: {
251
+ search: 'Qidirish',
252
+ askAi: 'AI dan soʻrash',
253
+ more: 'Koʻproq',
254
+ toggleMenu: 'Menyuni almashtirish',
255
+ selectLanguage: 'Tilni tanlash',
256
+ breadcrumbHome: 'Bosh sahifa',
39
257
  },
40
- es: {
41
- search: 'Buscar',
42
- askAi: 'Preguntar a la IA',
43
- more: 'Más',
44
- toggleMenu: 'Alternar menú',
45
- selectLanguage: 'Elegir idioma',
46
- breadcrumbHome: 'Inicio',
258
+ he: {
259
+ search: 'חיפוש',
260
+ askAi: 'שאל את ה-AI',
261
+ more: 'עוד',
262
+ toggleMenu: 'החלף תפריט',
263
+ selectLanguage: 'בחר שפה',
264
+ breadcrumbHome: 'דף הבית',
47
265
  },
48
- zh: ZH,
49
- cn: ZH,
50
266
  };
51
267
 
52
- // Pre-merge so repeated calls return a stable object reference.
53
- const MERGED: Partial<Record<LanguageCode, UiStrings>> = {};
54
- for (const [code, overrides] of Object.entries(OVERRIDES) as [LanguageCode, Partial<UiStrings>][]) {
55
- MERGED[code] = { ...EN, ...overrides };
268
+ /**
269
+ * Pre-merged so repeated calls return a stable object reference.
270
+ *
271
+ * Keyed by LOWERCASED code, and looked up the same way. Canonical casing is
272
+ * not what every caller has: `buildJsonLd` (`lib/json-ld.ts`) resolves its
273
+ * locale through `normalizeLanguageList` (`lib/locale-helpers.ts`), which
274
+ * lowercases every code it reads out of docs.json. An exact-key map therefore
275
+ * missed `pt-BR` -> `pt-br`, `zh-Hans` -> `zh-hans` and `zh-Hant` -> `zh-hant`,
276
+ * and those tenants got an English "Home" in the JSON-LD BreadcrumbList on
277
+ * every page while their visible header was correctly translated. `fr-CA` hid
278
+ * the bug, because `fr-ca` happens to be a `LanguageCode` in its own right.
279
+ *
280
+ * Folded here rather than at the call site because the other two callers
281
+ * (`Header.tsx`, `LanguageSelector.tsx`) arrive canonically cased via
282
+ * `extractLanguageFromPath` — so the asymmetry is invisible from any single
283
+ * call site, which is exactly the shape of bug that comes back.
284
+ *
285
+ * Aliases differing only in case (`fr-CA`/`fr-ca`) collapse onto one entry;
286
+ * they already resolve to the same `UiStrings`, so no table loses content.
287
+ *
288
+ * `...EN` is retained deliberately even though `OVERRIDES`' required-Record
289
+ * typing makes it currently redundant. It is the runtime backstop if that type
290
+ * is ever loosened or an entry arrives through a cast: with it, a missing key
291
+ * renders the English word; without it, the UI renders `undefined` on every
292
+ * page. That asymmetry is worth one spread.
293
+ */
294
+ const MERGED: Record<string, UiStrings> = {};
295
+ for (const [code, overrides] of Object.entries(OVERRIDES) as [LanguageCode, UiStrings][]) {
296
+ MERGED[code.toLowerCase()] = { ...EN, ...overrides };
56
297
  }
57
298
 
58
299
  export function getUiStrings(lang: LanguageCode | undefined): UiStrings {
59
- return (lang && MERGED[lang]) || EN;
300
+ // `typeof` rather than a bare truthiness check: this value can originate in
301
+ // a customer-written docs.json, so a non-string must fall back to English
302
+ // rather than throw inside `.toLowerCase()` on every rendered page.
303
+ if (typeof lang !== 'string') return EN;
304
+ return MERGED[lang.toLowerCase()] ?? EN;
60
305
  }