@takuhon/ui 0.4.0 → 0.6.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/dist/index.js CHANGED
@@ -1,6 +1,121 @@
1
1
  // src/components/TakuhonProfile.tsx
2
2
  import "./tokens-TEMWJS5E.css";
3
3
 
4
+ // src/lib/date-formatter.ts
5
+ var YEAR_MONTH = /^(\d{4})-(0[1-9]|1[0-2])$/;
6
+ function formatYearMonth(value, locale) {
7
+ const match = YEAR_MONTH.exec(value);
8
+ if (!match) return value;
9
+ const year = Number(match[1]);
10
+ const month = Number(match[2]);
11
+ const date = /* @__PURE__ */ new Date(0);
12
+ date.setUTCFullYear(year, month - 1, 1);
13
+ const options = {
14
+ year: "numeric",
15
+ month: "short",
16
+ timeZone: "UTC"
17
+ };
18
+ try {
19
+ return new Intl.DateTimeFormat(locale || "en", options).format(date);
20
+ } catch {
21
+ return new Intl.DateTimeFormat("en", options).format(date);
22
+ }
23
+ }
24
+
25
+ // src/lib/ui-labels.ts
26
+ var EN = {
27
+ "timeline.present": "Present",
28
+ "certification.noExpiration": "No expiration",
29
+ "patent.filed": "Filed",
30
+ "patent.granted": "Granted",
31
+ "patent.coInventorsPrefix": "with ",
32
+ "publication.coAuthorsPrefix": "with ",
33
+ "proficiency.native": "Native",
34
+ "proficiency.fluent": "Fluent",
35
+ "proficiency.professional": "Professional working",
36
+ "proficiency.intermediate": "Intermediate",
37
+ "proficiency.basic": "Basic",
38
+ "patentStatus.pending": "Pending",
39
+ "patentStatus.issued": "Issued",
40
+ "patentStatus.expired": "Expired",
41
+ "patentStatus.abandoned": "Abandoned",
42
+ "section.career": "Career",
43
+ "section.education": "Education",
44
+ "section.certifications": "Certifications",
45
+ "section.patents": "Patents",
46
+ "section.projects": "Projects",
47
+ "section.publications": "Publications",
48
+ "section.honors": "Honors & Awards",
49
+ "section.recommendations": "Recommendations",
50
+ "section.volunteering": "Volunteering",
51
+ "section.memberships": "Memberships",
52
+ "section.courses": "Courses",
53
+ "section.languages": "Languages",
54
+ "section.testScores": "Test Scores",
55
+ "section.skills": "Skills",
56
+ "section.contact": "Contact",
57
+ // a11y / chrome affixes. Status/Cause prefixes carry their trailing
58
+ // separator (a space in English) so callers concatenate prefix + value.
59
+ "a11y.statusPrefix": "Status: ",
60
+ "a11y.causePrefix": "Cause: ",
61
+ "a11y.profileLinks": "Profile links",
62
+ "a11y.tags": "Tags",
63
+ "a11y.skillsSuffix": "skills",
64
+ "a11y.selectLanguage": "Select language",
65
+ "contact.formLink": "Contact form",
66
+ "skills.uncategorized": "Other"
67
+ };
68
+ var JA = {
69
+ "timeline.present": "\u73FE\u5728",
70
+ "certification.noExpiration": "\u7121\u671F\u9650",
71
+ "patent.filed": "\u51FA\u9858",
72
+ "patent.granted": "\u767B\u9332",
73
+ "patent.coInventorsPrefix": "\u5171\u540C\u767A\u660E\u8005\uFF1A",
74
+ "publication.coAuthorsPrefix": "\u5171\u8457\u8005\uFF1A",
75
+ "proficiency.native": "\u30CD\u30A4\u30C6\u30A3\u30D6",
76
+ "proficiency.fluent": "\u6D41\u66A2",
77
+ "proficiency.professional": "\u5B9F\u52D9\u30EC\u30D9\u30EB",
78
+ "proficiency.intermediate": "\u4E2D\u7D1A",
79
+ "proficiency.basic": "\u521D\u7D1A",
80
+ "patentStatus.pending": "\u51FA\u9858\u4E2D",
81
+ "patentStatus.issued": "\u767B\u9332\u6E08",
82
+ "patentStatus.expired": "\u5931\u52B9",
83
+ "patentStatus.abandoned": "\u653E\u68C4",
84
+ "section.career": "\u8077\u6B74",
85
+ "section.education": "\u5B66\u6B74",
86
+ "section.certifications": "\u8CC7\u683C\u30FB\u8A8D\u5B9A",
87
+ "section.patents": "\u7279\u8A31",
88
+ "section.projects": "\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8",
89
+ "section.publications": "\u8AD6\u6587\u30FB\u51FA\u7248",
90
+ "section.honors": "\u53D7\u8CDE\u30FB\u6804\u8A89",
91
+ "section.recommendations": "\u63A8\u85A6",
92
+ "section.volunteering": "\u30DC\u30E9\u30F3\u30C6\u30A3\u30A2",
93
+ "section.memberships": "\u6240\u5C5E",
94
+ "section.courses": "\u8B1B\u5EA7",
95
+ "section.languages": "\u8A00\u8A9E",
96
+ "section.testScores": "\u30C6\u30B9\u30C8\u30B9\u30B3\u30A2",
97
+ "section.skills": "\u30B9\u30AD\u30EB",
98
+ "section.contact": "\u9023\u7D61\u5148",
99
+ // Status/Cause prefixes use a full-width colon (no trailing space), the
100
+ // conventional Japanese form, mirroring the co-author prefix convention.
101
+ "a11y.statusPrefix": "\u30B9\u30C6\u30FC\u30BF\u30B9\uFF1A",
102
+ "a11y.causePrefix": "\u5206\u91CE\uFF1A",
103
+ "a11y.profileLinks": "\u30D7\u30ED\u30D5\u30A3\u30FC\u30EB\u30EA\u30F3\u30AF",
104
+ "a11y.tags": "\u30BF\u30B0",
105
+ "a11y.skillsSuffix": "\u30B9\u30AD\u30EB",
106
+ "a11y.selectLanguage": "\u8A00\u8A9E\u3092\u9078\u629E",
107
+ "contact.formLink": "\u304A\u554F\u3044\u5408\u308F\u305B\u30D5\u30A9\u30FC\u30E0",
108
+ "skills.uncategorized": "\u305D\u306E\u4ED6"
109
+ };
110
+ var DICTIONARIES = {
111
+ en: EN,
112
+ ja: JA
113
+ };
114
+ function getUILabel(key, locale) {
115
+ const base = locale.split("-")[0] ?? locale;
116
+ return DICTIONARIES[locale]?.[key] ?? DICTIONARIES[base]?.[key] ?? EN[key];
117
+ }
118
+
4
119
  // src/components/CareerTimeline.tsx
5
120
  import styles from "./CareerTimeline.module-BGFX3LGA.module.css";
6
121
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -15,11 +130,14 @@ function sortCareers(careers) {
15
130
  function isOngoing(career) {
16
131
  return career.isCurrent === true || career.endDate === null || career.endDate === void 0;
17
132
  }
18
- function CareerTimeline({ careers }) {
133
+ function CareerTimeline({
134
+ careers,
135
+ locale = "en"
136
+ }) {
19
137
  if (careers.length === 0) return null;
20
138
  const ordered = sortCareers(careers);
21
139
  return /* @__PURE__ */ jsxs("section", { className: styles.section, "aria-labelledby": "takuhon-career-heading", children: [
22
- /* @__PURE__ */ jsx("h2", { id: "takuhon-career-heading", className: styles.heading, children: "Career" }),
140
+ /* @__PURE__ */ jsx("h2", { id: "takuhon-career-heading", className: styles.heading, children: getUILabel("section.career", locale) }),
23
141
  /* @__PURE__ */ jsx("ol", { className: styles.list, children: ordered.map((career) => /* @__PURE__ */ jsxs("li", { className: styles.item, children: [
24
142
  /* @__PURE__ */ jsx("div", { className: styles.timelineMarker, "aria-hidden": "true" }),
25
143
  /* @__PURE__ */ jsxs("div", { className: styles.content, children: [
@@ -42,12 +160,9 @@ function CareerTimeline({ careers }) {
42
160
  career.organization
43
161
  ] }) }),
44
162
  /* @__PURE__ */ jsxs("p", { className: styles.range, children: [
45
- /* @__PURE__ */ jsx("time", { dateTime: career.startDate, children: career.startDate }),
163
+ /* @__PURE__ */ jsx("time", { dateTime: career.startDate, children: formatYearMonth(career.startDate, locale) }),
46
164
  " \u2013 ",
47
- isOngoing(career) ? (
48
- // TODO(i18n-phase-2): Localize the 'Present' label via the locale resolver.
49
- "Present"
50
- ) : /* @__PURE__ */ jsx("time", { dateTime: career.endDate, children: career.endDate })
165
+ isOngoing(career) ? getUILabel("timeline.present", locale) : /* @__PURE__ */ jsx("time", { dateTime: career.endDate, children: formatYearMonth(career.endDate, locale) })
51
166
  ] }),
52
167
  career.location?.display ? /* @__PURE__ */ jsx("p", { className: styles.location, children: career.location.display }) : null,
53
168
  career.description ? /* @__PURE__ */ jsx("p", { className: styles.description, children: career.description }) : null
@@ -67,11 +182,14 @@ function sortCerts(certs) {
67
182
  return b.issueDate.localeCompare(a.issueDate);
68
183
  });
69
184
  }
70
- function Certifications({ certifications }) {
185
+ function Certifications({
186
+ certifications,
187
+ locale = "en"
188
+ }) {
71
189
  if (certifications.length === 0) return null;
72
190
  const ordered = sortCerts(certifications);
73
191
  return /* @__PURE__ */ jsxs2("section", { className: styles2.section, "aria-labelledby": "takuhon-certifications-heading", children: [
74
- /* @__PURE__ */ jsx2("h2", { id: "takuhon-certifications-heading", className: styles2.heading, children: "Certifications" }),
192
+ /* @__PURE__ */ jsx2("h2", { id: "takuhon-certifications-heading", className: styles2.heading, children: getUILabel("section.certifications", locale) }),
75
193
  /* @__PURE__ */ jsx2("ul", { className: styles2.list, children: ordered.map((cert) => /* @__PURE__ */ jsxs2("li", { className: styles2.item, children: [
76
194
  /* @__PURE__ */ jsx2("p", { className: styles2.title, children: cert.url ? /* @__PURE__ */ jsx2(
77
195
  "a",
@@ -85,13 +203,14 @@ function Certifications({ certifications }) {
85
203
  ) : cert.title }),
86
204
  /* @__PURE__ */ jsx2("p", { className: styles2.issuer, children: cert.issuingOrganization }),
87
205
  /* @__PURE__ */ jsxs2("p", { className: styles2.range, children: [
88
- /* @__PURE__ */ jsx2("time", { dateTime: cert.issueDate, children: cert.issueDate }),
89
- cert.expirationDate === null ? (
90
- // TODO(i18n-phase-2): Localize the 'No expiration' label via the locale resolver.
91
- /* @__PURE__ */ jsx2("span", { className: styles2.tag, children: " \xB7 No expiration" })
92
- ) : cert.expirationDate !== void 0 ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
206
+ /* @__PURE__ */ jsx2("time", { dateTime: cert.issueDate, children: formatYearMonth(cert.issueDate, locale) }),
207
+ cert.expirationDate === null ? /* @__PURE__ */ jsxs2("span", { className: styles2.tag, children: [
208
+ " ",
209
+ "\xB7 ",
210
+ getUILabel("certification.noExpiration", locale)
211
+ ] }) : cert.expirationDate !== void 0 ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
93
212
  " \u2013 ",
94
- /* @__PURE__ */ jsx2("time", { dateTime: cert.expirationDate, children: cert.expirationDate })
213
+ /* @__PURE__ */ jsx2("time", { dateTime: cert.expirationDate, children: formatYearMonth(cert.expirationDate, locale) })
95
214
  ] }) : null
96
215
  ] })
97
216
  ] }, cert.id)) })
@@ -101,11 +220,14 @@ function Certifications({ certifications }) {
101
220
  // src/components/ContactInfo.tsx
102
221
  import styles3 from "./ContactInfo.module-7SO24KHC.module.css";
103
222
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
104
- function ContactInfo({ contact }) {
223
+ function ContactInfo({
224
+ contact,
225
+ locale = "en"
226
+ }) {
105
227
  const showEmail = contact.showEmail === true && contact.email !== void 0;
106
228
  if (!showEmail && contact.formUrl === void 0) return null;
107
229
  return /* @__PURE__ */ jsxs3("section", { className: styles3.section, "aria-labelledby": "takuhon-contact-heading", children: [
108
- /* @__PURE__ */ jsx3("h2", { id: "takuhon-contact-heading", className: styles3.heading, children: "Contact" }),
230
+ /* @__PURE__ */ jsx3("h2", { id: "takuhon-contact-heading", className: styles3.heading, children: getUILabel("section.contact", locale) }),
109
231
  /* @__PURE__ */ jsxs3("ul", { className: styles3.list, children: [
110
232
  showEmail && contact.email !== void 0 ? /* @__PURE__ */ jsx3("li", { className: styles3.item, children: /* @__PURE__ */ jsx3("a", { className: styles3.link, href: `mailto:${contact.email}`, children: contact.email }) }) : null,
111
233
  contact.formUrl !== void 0 ? /* @__PURE__ */ jsx3("li", { className: styles3.item, children: /* @__PURE__ */ jsx3(
@@ -115,7 +237,7 @@ function ContactInfo({ contact }) {
115
237
  href: contact.formUrl,
116
238
  target: "_blank",
117
239
  rel: "noopener noreferrer",
118
- children: "Contact form"
240
+ children: getUILabel("contact.formLink", locale)
119
241
  }
120
242
  ) }) : null
121
243
  ] })
@@ -133,11 +255,11 @@ function sortCourses(entries) {
133
255
  return (b.completionDate ?? "").localeCompare(a.completionDate ?? "");
134
256
  });
135
257
  }
136
- function Courses({ courses }) {
258
+ function Courses({ courses, locale = "en" }) {
137
259
  if (courses.length === 0) return null;
138
260
  const ordered = sortCourses(courses);
139
261
  return /* @__PURE__ */ jsxs4("section", { className: styles4.section, "aria-labelledby": "takuhon-courses-heading", children: [
140
- /* @__PURE__ */ jsx4("h2", { id: "takuhon-courses-heading", className: styles4.heading, children: "Courses" }),
262
+ /* @__PURE__ */ jsx4("h2", { id: "takuhon-courses-heading", className: styles4.heading, children: getUILabel("section.courses", locale) }),
141
263
  /* @__PURE__ */ jsx4("ul", { className: styles4.list, children: ordered.map((entry) => /* @__PURE__ */ jsxs4("li", { className: styles4.item, children: [
142
264
  /* @__PURE__ */ jsx4("p", { className: styles4.title, children: entry.certificateUrl ? /* @__PURE__ */ jsx4(
143
265
  "a",
@@ -151,7 +273,7 @@ function Courses({ courses }) {
151
273
  ) : entry.title }),
152
274
  entry.provider ? /* @__PURE__ */ jsx4("p", { className: styles4.provider, children: entry.provider }) : null,
153
275
  entry.courseNumber ? /* @__PURE__ */ jsx4("p", { className: styles4.courseNumber, children: entry.courseNumber }) : null,
154
- entry.completionDate ? /* @__PURE__ */ jsx4("p", { className: styles4.date, children: /* @__PURE__ */ jsx4("time", { dateTime: entry.completionDate, children: entry.completionDate }) }) : null,
276
+ entry.completionDate ? /* @__PURE__ */ jsx4("p", { className: styles4.date, children: /* @__PURE__ */ jsx4("time", { dateTime: entry.completionDate, children: formatYearMonth(entry.completionDate, locale) }) }) : null,
155
277
  entry.description ? /* @__PURE__ */ jsx4("p", { className: styles4.description, children: entry.description }) : null
156
278
  ] }, entry.id)) })
157
279
  ] });
@@ -177,11 +299,14 @@ function composeStudyLine(entry) {
177
299
  }
178
300
  return entry.degree ?? entry.fieldOfStudy;
179
301
  }
180
- function EducationTimeline({ education }) {
302
+ function EducationTimeline({
303
+ education,
304
+ locale = "en"
305
+ }) {
181
306
  if (education.length === 0) return null;
182
307
  const ordered = sortEducation(education);
183
308
  return /* @__PURE__ */ jsxs5("section", { className: styles5.section, "aria-labelledby": "takuhon-education-heading", children: [
184
- /* @__PURE__ */ jsx5("h2", { id: "takuhon-education-heading", className: styles5.heading, children: "Education" }),
309
+ /* @__PURE__ */ jsx5("h2", { id: "takuhon-education-heading", className: styles5.heading, children: getUILabel("section.education", locale) }),
185
310
  /* @__PURE__ */ jsx5("ol", { className: styles5.list, children: ordered.map((entry) => {
186
311
  const study = composeStudyLine(entry);
187
312
  return /* @__PURE__ */ jsxs5("li", { className: styles5.item, children: [
@@ -199,12 +324,9 @@ function EducationTimeline({ education }) {
199
324
  ) : entry.institution }),
200
325
  study ? /* @__PURE__ */ jsx5("p", { className: styles5.study, children: study }) : null,
201
326
  /* @__PURE__ */ jsxs5("p", { className: styles5.range, children: [
202
- /* @__PURE__ */ jsx5("time", { dateTime: entry.startDate, children: entry.startDate }),
327
+ /* @__PURE__ */ jsx5("time", { dateTime: entry.startDate, children: formatYearMonth(entry.startDate, locale) }),
203
328
  " \u2013 ",
204
- isOngoing2(entry) ? (
205
- // TODO(i18n-phase-2): Localize the 'Present' label via the locale resolver.
206
- "Present"
207
- ) : /* @__PURE__ */ jsx5("time", { dateTime: entry.endDate, children: entry.endDate })
329
+ isOngoing2(entry) ? getUILabel("timeline.present", locale) : /* @__PURE__ */ jsx5("time", { dateTime: entry.endDate, children: formatYearMonth(entry.endDate, locale) })
208
330
  ] }),
209
331
  entry.grade ? /* @__PURE__ */ jsx5("p", { className: styles5.grade, children: entry.grade }) : null,
210
332
  entry.description ? /* @__PURE__ */ jsx5("p", { className: styles5.description, children: entry.description }) : null
@@ -245,11 +367,11 @@ function sortHonors(honors) {
245
367
  return b.date.localeCompare(a.date);
246
368
  });
247
369
  }
248
- function HonorsList({ honors }) {
370
+ function HonorsList({ honors, locale = "en" }) {
249
371
  if (honors.length === 0) return null;
250
372
  const ordered = sortHonors(honors);
251
373
  return /* @__PURE__ */ jsxs7("section", { className: styles7.section, "aria-labelledby": "takuhon-honors-heading", children: [
252
- /* @__PURE__ */ jsx7("h2", { id: "takuhon-honors-heading", className: styles7.heading, children: "Honors & Awards" }),
374
+ /* @__PURE__ */ jsx7("h2", { id: "takuhon-honors-heading", className: styles7.heading, children: getUILabel("section.honors", locale) }),
253
375
  /* @__PURE__ */ jsx7("ul", { className: styles7.list, children: ordered.map((honor) => /* @__PURE__ */ jsxs7("li", { className: styles7.item, children: [
254
376
  /* @__PURE__ */ jsx7("p", { className: styles7.title, children: honor.url ? /* @__PURE__ */ jsx7(
255
377
  "a",
@@ -264,7 +386,7 @@ function HonorsList({ honors }) {
264
386
  /* @__PURE__ */ jsxs7("p", { className: styles7.meta, children: [
265
387
  honor.issuer,
266
388
  " \xB7 ",
267
- /* @__PURE__ */ jsx7("time", { dateTime: honor.date, children: honor.date })
389
+ /* @__PURE__ */ jsx7("time", { dateTime: honor.date, children: formatYearMonth(honor.date, locale) })
268
390
  ] }),
269
391
  honor.description ? /* @__PURE__ */ jsx7("p", { className: styles7.description, children: honor.description }) : null
270
392
  ] }, honor.id)) })
@@ -274,13 +396,6 @@ function HonorsList({ honors }) {
274
396
  // src/components/Languages.tsx
275
397
  import styles8 from "./Languages.module-X6RQNFRY.module.css";
276
398
  import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
277
- var PROFICIENCY_LABEL = {
278
- native: "Native",
279
- fluent: "Fluent",
280
- professional: "Professional working",
281
- intermediate: "Intermediate",
282
- basic: "Basic"
283
- };
284
399
  function sortLanguages(languages) {
285
400
  return [...languages].sort((a, b) => {
286
401
  const aOrder = a.order ?? Number.POSITIVE_INFINITY;
@@ -288,14 +403,14 @@ function sortLanguages(languages) {
288
403
  return aOrder - bOrder;
289
404
  });
290
405
  }
291
- function Languages({ languages }) {
406
+ function Languages({ languages, locale = "en" }) {
292
407
  if (languages.length === 0) return null;
293
408
  const ordered = sortLanguages(languages);
294
409
  return /* @__PURE__ */ jsxs8("section", { className: styles8.section, "aria-labelledby": "takuhon-languages-heading", children: [
295
- /* @__PURE__ */ jsx8("h2", { id: "takuhon-languages-heading", className: styles8.heading, children: "Languages" }),
410
+ /* @__PURE__ */ jsx8("h2", { id: "takuhon-languages-heading", className: styles8.heading, children: getUILabel("section.languages", locale) }),
296
411
  /* @__PURE__ */ jsx8("ul", { className: styles8.list, children: ordered.map((entry) => /* @__PURE__ */ jsxs8("li", { className: styles8.item, children: [
297
412
  /* @__PURE__ */ jsx8("span", { className: styles8.name, lang: entry.language, children: entry.displayName ?? entry.language }),
298
- /* @__PURE__ */ jsx8("span", { className: styles8.proficiency, children: PROFICIENCY_LABEL[entry.proficiency] })
413
+ /* @__PURE__ */ jsx8("span", { className: styles8.proficiency, children: getUILabel(`proficiency.${entry.proficiency}`, locale) })
299
414
  ] }, entry.id)) })
300
415
  ] });
301
416
  }
@@ -318,10 +433,10 @@ function formatLinkLabel(link) {
318
433
  if (link.type === "custom") return link.id;
319
434
  return link.type;
320
435
  }
321
- function LinksList({ links }) {
436
+ function LinksList({ links, locale = "en" }) {
322
437
  if (links.length === 0) return null;
323
438
  const ordered = sortLinks(links);
324
- return /* @__PURE__ */ jsx9("nav", { "aria-label": "Profile links", className: styles9.nav, children: /* @__PURE__ */ jsx9("ul", { className: styles9.list, children: ordered.map((link) => /* @__PURE__ */ jsx9("li", { className: styles9.item, children: /* @__PURE__ */ jsxs9(
439
+ return /* @__PURE__ */ jsx9("nav", { "aria-label": getUILabel("a11y.profileLinks", locale), className: styles9.nav, children: /* @__PURE__ */ jsx9("ul", { className: styles9.list, children: ordered.map((link) => /* @__PURE__ */ jsx9("li", { className: styles9.item, children: /* @__PURE__ */ jsxs9(
325
440
  "a",
326
441
  {
327
442
  className: styles9.link,
@@ -351,11 +466,14 @@ function sortMemberships(entries) {
351
466
  function isOngoing3(entry) {
352
467
  return entry.isCurrent === true || entry.endDate === null || entry.endDate === void 0;
353
468
  }
354
- function Memberships({ memberships }) {
469
+ function Memberships({
470
+ memberships,
471
+ locale = "en"
472
+ }) {
355
473
  if (memberships.length === 0) return null;
356
474
  const ordered = sortMemberships(memberships);
357
475
  return /* @__PURE__ */ jsxs10("section", { className: styles10.section, "aria-labelledby": "takuhon-memberships-heading", children: [
358
- /* @__PURE__ */ jsx10("h2", { id: "takuhon-memberships-heading", className: styles10.heading, children: "Memberships" }),
476
+ /* @__PURE__ */ jsx10("h2", { id: "takuhon-memberships-heading", className: styles10.heading, children: getUILabel("section.memberships", locale) }),
359
477
  /* @__PURE__ */ jsx10("ol", { className: styles10.list, children: ordered.map((entry) => /* @__PURE__ */ jsxs10("li", { className: styles10.item, children: [
360
478
  /* @__PURE__ */ jsx10("div", { className: styles10.timelineMarker, "aria-hidden": "true" }),
361
479
  /* @__PURE__ */ jsxs10("div", { className: styles10.content, children: [
@@ -371,12 +489,9 @@ function Memberships({ memberships }) {
371
489
  ) : entry.organization }),
372
490
  entry.role ? /* @__PURE__ */ jsx10("p", { className: styles10.role, children: entry.role }) : null,
373
491
  /* @__PURE__ */ jsxs10("p", { className: styles10.range, children: [
374
- /* @__PURE__ */ jsx10("time", { dateTime: entry.startDate, children: entry.startDate }),
492
+ /* @__PURE__ */ jsx10("time", { dateTime: entry.startDate, children: formatYearMonth(entry.startDate, locale) }),
375
493
  " \u2013 ",
376
- isOngoing3(entry) ? (
377
- // TODO(i18n-phase-2): Localize the 'Present' label via the locale resolver.
378
- "Present"
379
- ) : /* @__PURE__ */ jsx10("time", { dateTime: entry.endDate, children: entry.endDate })
494
+ isOngoing3(entry) ? getUILabel("timeline.present", locale) : /* @__PURE__ */ jsx10("time", { dateTime: entry.endDate, children: formatYearMonth(entry.endDate, locale) })
380
495
  ] }),
381
496
  entry.description ? /* @__PURE__ */ jsx10("p", { className: styles10.description, children: entry.description }) : null
382
497
  ] })
@@ -387,12 +502,6 @@ function Memberships({ memberships }) {
387
502
  // src/components/Patents.tsx
388
503
  import styles11 from "./Patents.module-L3GWPB3V.module.css";
389
504
  import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
390
- var STATUS_LABEL = {
391
- pending: "Pending",
392
- issued: "Issued",
393
- expired: "Expired",
394
- abandoned: "Abandoned"
395
- };
396
505
  function sortPatents(entries) {
397
506
  return [...entries].sort((a, b) => {
398
507
  const aOrder = a.order ?? Number.POSITIVE_INFINITY;
@@ -403,11 +512,11 @@ function sortPatents(entries) {
403
512
  return bDate.localeCompare(aDate);
404
513
  });
405
514
  }
406
- function Patents({ patents }) {
515
+ function Patents({ patents, locale = "en" }) {
407
516
  if (patents.length === 0) return null;
408
517
  const ordered = sortPatents(patents);
409
518
  return /* @__PURE__ */ jsxs11("section", { className: styles11.section, "aria-labelledby": "takuhon-patents-heading", children: [
410
- /* @__PURE__ */ jsx11("h2", { id: "takuhon-patents-heading", className: styles11.heading, children: "Patents" }),
519
+ /* @__PURE__ */ jsx11("h2", { id: "takuhon-patents-heading", className: styles11.heading, children: getUILabel("section.patents", locale) }),
411
520
  /* @__PURE__ */ jsx11("ul", { className: styles11.list, children: ordered.map((entry) => /* @__PURE__ */ jsxs11("li", { className: styles11.item, children: [
412
521
  /* @__PURE__ */ jsxs11("p", { className: styles11.title, children: [
413
522
  entry.url ? /* @__PURE__ */ jsx11(
@@ -421,24 +530,24 @@ function Patents({ patents }) {
421
530
  }
422
531
  ) : entry.title,
423
532
  /* @__PURE__ */ jsxs11("span", { className: styles11.statusBadge, "data-status": entry.status, children: [
424
- /* @__PURE__ */ jsx11("span", { className: styles11.srOnly, children: "Status: " }),
425
- STATUS_LABEL[entry.status]
533
+ /* @__PURE__ */ jsx11("span", { className: styles11.srOnly, children: getUILabel("a11y.statusPrefix", locale) }),
534
+ getUILabel(`patentStatus.${entry.status}`, locale)
426
535
  ] })
427
536
  ] }),
428
537
  /* @__PURE__ */ jsx11("p", { className: styles11.patentNumber, children: entry.patentNumber }),
429
538
  entry.office ? /* @__PURE__ */ jsx11("p", { className: styles11.office, children: entry.office }) : null,
430
539
  entry.filingDate !== void 0 || entry.grantDate !== void 0 ? /* @__PURE__ */ jsxs11("p", { className: styles11.dates, children: [
431
540
  entry.filingDate ? /* @__PURE__ */ jsxs11(Fragment3, { children: [
432
- "Filed ",
433
- /* @__PURE__ */ jsx11("time", { dateTime: entry.filingDate, children: entry.filingDate })
541
+ `${getUILabel("patent.filed", locale)} `,
542
+ /* @__PURE__ */ jsx11("time", { dateTime: entry.filingDate, children: formatYearMonth(entry.filingDate, locale) })
434
543
  ] }) : null,
435
544
  entry.filingDate && entry.grantDate ? " \xB7 " : null,
436
545
  entry.grantDate ? /* @__PURE__ */ jsxs11(Fragment3, { children: [
437
- "Granted ",
438
- /* @__PURE__ */ jsx11("time", { dateTime: entry.grantDate, children: entry.grantDate })
546
+ `${getUILabel("patent.granted", locale)} `,
547
+ /* @__PURE__ */ jsx11("time", { dateTime: entry.grantDate, children: formatYearMonth(entry.grantDate, locale) })
439
548
  ] }) : null
440
549
  ] }) : null,
441
- entry.coInventors && entry.coInventors.length > 0 ? /* @__PURE__ */ jsx11(Fragment3, { children: /* @__PURE__ */ jsx11("p", { className: styles11.coInventors, children: `with ${entry.coInventors.join(", ")}` }) }) : null,
550
+ entry.coInventors && entry.coInventors.length > 0 ? /* @__PURE__ */ jsx11("p", { className: styles11.coInventors, children: `${getUILabel("patent.coInventorsPrefix", locale)}${entry.coInventors.join(", ")}` }) : null,
442
551
  entry.description ? /* @__PURE__ */ jsx11("p", { className: styles11.description, children: entry.description }) : null
443
552
  ] }, entry.id)) })
444
553
  ] });
@@ -482,11 +591,14 @@ function sortProjects(projects) {
482
591
  return aOrder - bOrder;
483
592
  });
484
593
  }
485
- function ProjectsList({ projects }) {
594
+ function ProjectsList({
595
+ projects,
596
+ locale = "en"
597
+ }) {
486
598
  if (projects.length === 0) return null;
487
599
  const ordered = sortProjects(projects);
488
600
  return /* @__PURE__ */ jsxs13("section", { className: styles13.section, "aria-labelledby": "takuhon-projects-heading", children: [
489
- /* @__PURE__ */ jsx13("h2", { id: "takuhon-projects-heading", className: styles13.heading, children: "Projects" }),
601
+ /* @__PURE__ */ jsx13("h2", { id: "takuhon-projects-heading", className: styles13.heading, children: getUILabel("section.projects", locale) }),
490
602
  /* @__PURE__ */ jsx13("ul", { className: styles13.list, children: ordered.map((project) => /* @__PURE__ */ jsxs13(
491
603
  "li",
492
604
  {
@@ -504,15 +616,12 @@ function ProjectsList({ projects }) {
504
616
  }
505
617
  ) : project.title }),
506
618
  project.startDate !== void 0 ? /* @__PURE__ */ jsxs13("p", { className: styles13.range, children: [
507
- /* @__PURE__ */ jsx13("time", { dateTime: project.startDate, children: project.startDate }),
619
+ /* @__PURE__ */ jsx13("time", { dateTime: project.startDate, children: formatYearMonth(project.startDate, locale) }),
508
620
  " \u2013 ",
509
- project.endDate ? /* @__PURE__ */ jsx13("time", { dateTime: project.endDate, children: project.endDate }) : (
510
- // TODO(i18n-phase-2): Localize the 'Present' label via the locale resolver.
511
- "Present"
512
- )
621
+ project.endDate ? /* @__PURE__ */ jsx13("time", { dateTime: project.endDate, children: formatYearMonth(project.endDate, locale) }) : getUILabel("timeline.present", locale)
513
622
  ] }) : null,
514
623
  project.description ? /* @__PURE__ */ jsx13("p", { className: styles13.description, children: project.description }) : null,
515
- project.tags && project.tags.length > 0 ? /* @__PURE__ */ jsx13("ul", { className: styles13.tags, "aria-label": "Tags", children: project.tags.map((tag) => /* @__PURE__ */ jsx13("li", { className: styles13.tag, children: tag }, tag)) }) : null
624
+ project.tags && project.tags.length > 0 ? /* @__PURE__ */ jsx13("ul", { className: styles13.tags, "aria-label": getUILabel("a11y.tags", locale), children: project.tags.map((tag) => /* @__PURE__ */ jsx13("li", { className: styles13.tag, children: tag }, tag)) }) : null
516
625
  ]
517
626
  },
518
627
  project.id
@@ -534,11 +643,14 @@ function sortPublications(entries) {
534
643
  function normalizeDoi(doi) {
535
644
  return doi.replace(/^https?:\/\/(?:dx\.)?doi\.org\//i, "");
536
645
  }
537
- function Publications({ publications }) {
646
+ function Publications({
647
+ publications,
648
+ locale = "en"
649
+ }) {
538
650
  if (publications.length === 0) return null;
539
651
  const ordered = sortPublications(publications);
540
652
  return /* @__PURE__ */ jsxs14("section", { className: styles14.section, "aria-labelledby": "takuhon-publications-heading", children: [
541
- /* @__PURE__ */ jsx14("h2", { id: "takuhon-publications-heading", className: styles14.heading, children: "Publications" }),
653
+ /* @__PURE__ */ jsx14("h2", { id: "takuhon-publications-heading", className: styles14.heading, children: getUILabel("section.publications", locale) }),
542
654
  /* @__PURE__ */ jsx14("ul", { className: styles14.list, children: ordered.map((entry) => /* @__PURE__ */ jsxs14("li", { className: styles14.item, children: [
543
655
  /* @__PURE__ */ jsx14("p", { className: styles14.title, children: entry.url ? /* @__PURE__ */ jsx14(
544
656
  "a",
@@ -551,8 +663,8 @@ function Publications({ publications }) {
551
663
  }
552
664
  ) : entry.title }),
553
665
  entry.publisher ? /* @__PURE__ */ jsx14("p", { className: styles14.publisher, children: entry.publisher }) : null,
554
- /* @__PURE__ */ jsx14("p", { className: styles14.date, children: /* @__PURE__ */ jsx14("time", { dateTime: entry.date, children: entry.date }) }),
555
- entry.coAuthors && entry.coAuthors.length > 0 ? /* @__PURE__ */ jsx14("p", { className: styles14.coAuthors, children: `with ${entry.coAuthors.join(", ")}` }) : null,
666
+ /* @__PURE__ */ jsx14("p", { className: styles14.date, children: /* @__PURE__ */ jsx14("time", { dateTime: entry.date, children: formatYearMonth(entry.date, locale) }) }),
667
+ entry.coAuthors && entry.coAuthors.length > 0 ? /* @__PURE__ */ jsx14("p", { className: styles14.coAuthors, children: `${getUILabel("publication.coAuthorsPrefix", locale)}${entry.coAuthors.join(", ")}` }) : null,
556
668
  entry.doi ? (() => {
557
669
  const bare = normalizeDoi(entry.doi);
558
670
  return /* @__PURE__ */ jsx14("p", { className: styles14.doi, children: /* @__PURE__ */ jsx14(
@@ -571,9 +683,50 @@ function Publications({ publications }) {
571
683
  ] });
572
684
  }
573
685
 
574
- // src/components/SkillsList.tsx
575
- import styles15 from "./SkillsList.module-XIAWE55H.module.css";
686
+ // src/components/Recommendations.tsx
687
+ import styles15 from "./Recommendations.module-HQSBHXLM.module.css";
576
688
  import { jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
689
+ function sortRecommendations(entries) {
690
+ return [...entries].sort((a, b) => {
691
+ const aOrder = a.order ?? Number.POSITIVE_INFINITY;
692
+ const bOrder = b.order ?? Number.POSITIVE_INFINITY;
693
+ if (aOrder !== bOrder) return aOrder - bOrder;
694
+ return (b.date ?? "").localeCompare(a.date ?? "");
695
+ });
696
+ }
697
+ function Recommendations({
698
+ recommendations,
699
+ locale = "en"
700
+ }) {
701
+ if (recommendations.length === 0) return null;
702
+ const ordered = sortRecommendations(recommendations);
703
+ return /* @__PURE__ */ jsxs15("section", { className: styles15.section, "aria-labelledby": "takuhon-recommendations-heading", children: [
704
+ /* @__PURE__ */ jsx15("h2", { id: "takuhon-recommendations-heading", className: styles15.heading, children: getUILabel("section.recommendations", locale) }),
705
+ /* @__PURE__ */ jsx15("ul", { className: styles15.list, children: ordered.map((entry) => /* @__PURE__ */ jsxs15("li", { className: styles15.item, children: [
706
+ /* @__PURE__ */ jsx15("blockquote", { className: styles15.body, children: /* @__PURE__ */ jsx15("p", { className: styles15.quote, children: entry.body }) }),
707
+ /* @__PURE__ */ jsxs15("p", { className: styles15.attribution, children: [
708
+ /* @__PURE__ */ jsx15("span", { className: styles15.author, children: entry.author.url ? /* @__PURE__ */ jsx15(
709
+ "a",
710
+ {
711
+ className: styles15.link,
712
+ href: entry.author.url,
713
+ target: "_blank",
714
+ rel: "noopener noreferrer",
715
+ children: entry.author.name
716
+ }
717
+ ) : entry.author.name }),
718
+ entry.author.headline ? /* @__PURE__ */ jsx15("span", { className: styles15.headline, children: entry.author.headline }) : null
719
+ ] }),
720
+ entry.relationship ? /* @__PURE__ */ jsx15("p", { className: styles15.meta, children: entry.relationship }) : null,
721
+ entry.date ? /* @__PURE__ */ jsx15("p", { className: styles15.meta, children: /* @__PURE__ */ jsx15("time", { dateTime: entry.date, children: formatYearMonth(entry.date, locale) }) }) : null
722
+ ] }, entry.id)) })
723
+ ] });
724
+ }
725
+
726
+ // src/components/SkillsList.tsx
727
+ import styles16 from "./SkillsList.module-XIAWE55H.module.css";
728
+ import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
729
+ var UNCATEGORIZED = "other";
577
730
  function groupSkills(skills) {
578
731
  const sorted = [...skills].sort((a, b) => {
579
732
  const aOrder = a.order ?? Number.POSITIVE_INFINITY;
@@ -582,7 +735,7 @@ function groupSkills(skills) {
582
735
  });
583
736
  const groups = /* @__PURE__ */ new Map();
584
737
  for (const skill of sorted) {
585
- const key = skill.category ?? "other";
738
+ const key = skill.category ?? UNCATEGORIZED;
586
739
  const bucket = groups.get(key);
587
740
  if (bucket) {
588
741
  bucket.push(skill);
@@ -592,24 +745,74 @@ function groupSkills(skills) {
592
745
  }
593
746
  return [...groups.entries()].map(([category, items]) => ({ category, skills: items }));
594
747
  }
595
- function SkillsList({ skills }) {
748
+ function SkillsList({ skills, locale = "en" }) {
596
749
  if (skills.length === 0) return null;
597
750
  const groups = groupSkills(skills);
598
- return /* @__PURE__ */ jsxs15("section", { className: styles15.section, "aria-labelledby": "takuhon-skills-heading", children: [
599
- /* @__PURE__ */ jsx15("h2", { id: "takuhon-skills-heading", className: styles15.heading, children: "Skills" }),
600
- /* @__PURE__ */ jsx15("div", { className: styles15.groups, children: groups.map((group) => /* @__PURE__ */ jsxs15("div", { className: styles15.group, children: [
601
- /* @__PURE__ */ jsx15("h3", { className: styles15.category, children: group.category }),
602
- /* @__PURE__ */ jsx15("ul", { className: styles15.list, "aria-label": `${group.category} skills`, children: group.skills.map((skill) => /* @__PURE__ */ jsx15("li", { className: styles15.item, children: skill.label }, skill.id)) })
603
- ] }, group.category)) })
751
+ return /* @__PURE__ */ jsxs16("section", { className: styles16.section, "aria-labelledby": "takuhon-skills-heading", children: [
752
+ /* @__PURE__ */ jsx16("h2", { id: "takuhon-skills-heading", className: styles16.heading, children: getUILabel("section.skills", locale) }),
753
+ /* @__PURE__ */ jsx16("div", { className: styles16.groups, children: groups.map((group) => {
754
+ const categoryLabel = group.category === UNCATEGORIZED ? getUILabel("skills.uncategorized", locale) : group.category;
755
+ return /* @__PURE__ */ jsxs16("div", { className: styles16.group, children: [
756
+ /* @__PURE__ */ jsx16("h3", { className: styles16.category, children: categoryLabel }),
757
+ /* @__PURE__ */ jsx16(
758
+ "ul",
759
+ {
760
+ className: styles16.list,
761
+ "aria-label": `${categoryLabel} ${getUILabel("a11y.skillsSuffix", locale)}`,
762
+ children: group.skills.map((skill) => /* @__PURE__ */ jsx16("li", { className: styles16.item, children: skill.label }, skill.id))
763
+ }
764
+ )
765
+ ] }, group.category);
766
+ }) })
604
767
  ] });
605
768
  }
606
769
 
607
770
  // src/components/TakuhonProfile.tsx
608
- import styles17 from "./TakuhonProfile.module-QUEORIHA.module.css";
771
+ import styles19 from "./TakuhonProfile.module-QUEORIHA.module.css";
772
+
773
+ // src/components/TestScores.tsx
774
+ import styles17 from "./TestScores.module-PG7VGFS6.module.css";
775
+ import { jsx as jsx17, jsxs as jsxs17 } from "react/jsx-runtime";
776
+ function sortTestScores(entries) {
777
+ return [...entries].sort((a, b) => {
778
+ const aOrder = a.order ?? Number.POSITIVE_INFINITY;
779
+ const bOrder = b.order ?? Number.POSITIVE_INFINITY;
780
+ if (aOrder !== bOrder) return aOrder - bOrder;
781
+ return b.date.localeCompare(a.date);
782
+ });
783
+ }
784
+ function TestScores({
785
+ testScores,
786
+ locale = "en"
787
+ }) {
788
+ if (testScores.length === 0) return null;
789
+ const ordered = sortTestScores(testScores);
790
+ return /* @__PURE__ */ jsxs17("section", { className: styles17.section, "aria-labelledby": "takuhon-test-scores-heading", children: [
791
+ /* @__PURE__ */ jsx17("h2", { id: "takuhon-test-scores-heading", className: styles17.heading, children: getUILabel("section.testScores", locale) }),
792
+ /* @__PURE__ */ jsx17("ul", { className: styles17.list, children: ordered.map((entry) => /* @__PURE__ */ jsxs17("li", { className: styles17.item, children: [
793
+ /* @__PURE__ */ jsx17("p", { className: styles17.title, children: entry.url ? /* @__PURE__ */ jsx17(
794
+ "a",
795
+ {
796
+ className: styles17.link,
797
+ href: entry.url,
798
+ target: "_blank",
799
+ rel: "noopener noreferrer",
800
+ children: entry.title
801
+ }
802
+ ) : entry.title }),
803
+ /* @__PURE__ */ jsxs17("p", { className: styles17.meta, children: [
804
+ /* @__PURE__ */ jsx17("span", { className: styles17.score, children: entry.score }),
805
+ " \xB7 ",
806
+ /* @__PURE__ */ jsx17("time", { dateTime: entry.date, children: formatYearMonth(entry.date, locale) })
807
+ ] }),
808
+ entry.description ? /* @__PURE__ */ jsx17("p", { className: styles17.description, children: entry.description }) : null
809
+ ] }, entry.id)) })
810
+ ] });
811
+ }
609
812
 
610
813
  // src/components/Volunteering.tsx
611
- import styles16 from "./Volunteering.module-TDRC6QVK.module.css";
612
- import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
814
+ import styles18 from "./Volunteering.module-TDRC6QVK.module.css";
815
+ import { jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
613
816
  function sortVolunteering(entries) {
614
817
  return [...entries].sort((a, b) => {
615
818
  const aOrder = a.order ?? Number.POSITIVE_INFINITY;
@@ -621,73 +824,75 @@ function sortVolunteering(entries) {
621
824
  function isOngoing4(entry) {
622
825
  return entry.isCurrent === true || entry.endDate === null || entry.endDate === void 0;
623
826
  }
624
- function Volunteering({ volunteering }) {
827
+ function Volunteering({
828
+ volunteering,
829
+ locale = "en"
830
+ }) {
625
831
  if (volunteering.length === 0) return null;
626
832
  const ordered = sortVolunteering(volunteering);
627
- return /* @__PURE__ */ jsxs16("section", { className: styles16.section, "aria-labelledby": "takuhon-volunteering-heading", children: [
628
- /* @__PURE__ */ jsx16("h2", { id: "takuhon-volunteering-heading", className: styles16.heading, children: "Volunteering" }),
629
- /* @__PURE__ */ jsx16("ol", { className: styles16.list, children: ordered.map((entry) => /* @__PURE__ */ jsxs16("li", { className: styles16.item, children: [
630
- /* @__PURE__ */ jsx16("div", { className: styles16.timelineMarker, "aria-hidden": "true" }),
631
- /* @__PURE__ */ jsxs16("div", { className: styles16.content, children: [
632
- /* @__PURE__ */ jsx16("p", { className: styles16.organization, children: entry.url ? /* @__PURE__ */ jsx16(
833
+ return /* @__PURE__ */ jsxs18("section", { className: styles18.section, "aria-labelledby": "takuhon-volunteering-heading", children: [
834
+ /* @__PURE__ */ jsx18("h2", { id: "takuhon-volunteering-heading", className: styles18.heading, children: getUILabel("section.volunteering", locale) }),
835
+ /* @__PURE__ */ jsx18("ol", { className: styles18.list, children: ordered.map((entry) => /* @__PURE__ */ jsxs18("li", { className: styles18.item, children: [
836
+ /* @__PURE__ */ jsx18("div", { className: styles18.timelineMarker, "aria-hidden": "true" }),
837
+ /* @__PURE__ */ jsxs18("div", { className: styles18.content, children: [
838
+ /* @__PURE__ */ jsx18("p", { className: styles18.organization, children: entry.url ? /* @__PURE__ */ jsx18(
633
839
  "a",
634
840
  {
635
- className: styles16.link,
841
+ className: styles18.link,
636
842
  href: entry.url,
637
843
  target: "_blank",
638
844
  rel: "noopener noreferrer",
639
845
  children: entry.organization
640
846
  }
641
847
  ) : entry.organization }),
642
- /* @__PURE__ */ jsxs16("p", { className: styles16.role, children: [
848
+ /* @__PURE__ */ jsxs18("p", { className: styles18.role, children: [
643
849
  entry.role,
644
- entry.cause ? /* @__PURE__ */ jsxs16("span", { className: styles16.cause, children: [
645
- /* @__PURE__ */ jsx16("span", { className: styles16.srOnly, children: "Cause: " }),
850
+ entry.cause ? /* @__PURE__ */ jsxs18("span", { className: styles18.cause, children: [
851
+ /* @__PURE__ */ jsx18("span", { className: styles18.srOnly, children: getUILabel("a11y.causePrefix", locale) }),
646
852
  entry.cause
647
853
  ] }) : null
648
854
  ] }),
649
- /* @__PURE__ */ jsxs16("p", { className: styles16.range, children: [
650
- /* @__PURE__ */ jsx16("time", { dateTime: entry.startDate, children: entry.startDate }),
855
+ /* @__PURE__ */ jsxs18("p", { className: styles18.range, children: [
856
+ /* @__PURE__ */ jsx18("time", { dateTime: entry.startDate, children: formatYearMonth(entry.startDate, locale) }),
651
857
  " \u2013 ",
652
- isOngoing4(entry) ? (
653
- // TODO(i18n-phase-2): Localize the 'Present' label via the locale resolver.
654
- "Present"
655
- ) : /* @__PURE__ */ jsx16("time", { dateTime: entry.endDate, children: entry.endDate })
858
+ isOngoing4(entry) ? getUILabel("timeline.present", locale) : /* @__PURE__ */ jsx18("time", { dateTime: entry.endDate, children: formatYearMonth(entry.endDate, locale) })
656
859
  ] }),
657
- entry.description ? /* @__PURE__ */ jsx16("p", { className: styles16.description, children: entry.description }) : null
860
+ entry.description ? /* @__PURE__ */ jsx18("p", { className: styles18.description, children: entry.description }) : null
658
861
  ] })
659
862
  ] }, entry.id)) })
660
863
  ] });
661
864
  }
662
865
 
663
866
  // src/components/TakuhonProfile.tsx
664
- import { jsx as jsx17, jsxs as jsxs17 } from "react/jsx-runtime";
867
+ import { jsx as jsx19, jsxs as jsxs19 } from "react/jsx-runtime";
665
868
  function TakuhonProfile({ data }) {
666
869
  const showFooter = data.settings.showPoweredBy !== false;
667
- return /* @__PURE__ */ jsxs17("article", { className: styles17.root, children: [
668
- /* @__PURE__ */ jsx17(ProfileHeader, { profile: data.profile }),
669
- /* @__PURE__ */ jsx17(LinksList, { links: data.links }),
670
- /* @__PURE__ */ jsx17(EducationTimeline, { education: data.education }),
671
- /* @__PURE__ */ jsx17(Courses, { courses: data.courses }),
672
- /* @__PURE__ */ jsx17(CareerTimeline, { careers: data.careers }),
673
- /* @__PURE__ */ jsx17(Memberships, { memberships: data.memberships }),
674
- /* @__PURE__ */ jsx17(Certifications, { certifications: data.certifications }),
675
- /* @__PURE__ */ jsx17(Patents, { patents: data.patents }),
676
- /* @__PURE__ */ jsx17(ProjectsList, { projects: data.projects }),
677
- /* @__PURE__ */ jsx17(Publications, { publications: data.publications }),
678
- /* @__PURE__ */ jsx17(HonorsList, { honors: data.honors }),
679
- /* @__PURE__ */ jsx17(Volunteering, { volunteering: data.volunteering }),
680
- /* @__PURE__ */ jsx17(SkillsList, { skills: data.skills }),
681
- /* @__PURE__ */ jsx17(Languages, { languages: data.languages }),
682
- /* @__PURE__ */ jsx17(ContactInfo, { contact: data.contact }),
683
- showFooter ? /* @__PURE__ */ jsx17(Footer, {}) : null
870
+ const locale = data.resolvedLocale;
871
+ return /* @__PURE__ */ jsxs19("article", { className: styles19.root, children: [
872
+ /* @__PURE__ */ jsx19(ProfileHeader, { profile: data.profile }),
873
+ /* @__PURE__ */ jsx19(LinksList, { links: data.links, locale }),
874
+ /* @__PURE__ */ jsx19(EducationTimeline, { education: data.education, locale }),
875
+ /* @__PURE__ */ jsx19(Courses, { courses: data.courses, locale }),
876
+ /* @__PURE__ */ jsx19(CareerTimeline, { careers: data.careers, locale }),
877
+ /* @__PURE__ */ jsx19(Memberships, { memberships: data.memberships, locale }),
878
+ /* @__PURE__ */ jsx19(Certifications, { certifications: data.certifications, locale }),
879
+ /* @__PURE__ */ jsx19(Patents, { patents: data.patents, locale }),
880
+ /* @__PURE__ */ jsx19(ProjectsList, { projects: data.projects, locale }),
881
+ /* @__PURE__ */ jsx19(Publications, { publications: data.publications, locale }),
882
+ /* @__PURE__ */ jsx19(HonorsList, { honors: data.honors, locale }),
883
+ /* @__PURE__ */ jsx19(Recommendations, { recommendations: data.recommendations, locale }),
884
+ /* @__PURE__ */ jsx19(Volunteering, { volunteering: data.volunteering, locale }),
885
+ /* @__PURE__ */ jsx19(SkillsList, { skills: data.skills, locale }),
886
+ /* @__PURE__ */ jsx19(Languages, { languages: data.languages, locale }),
887
+ /* @__PURE__ */ jsx19(TestScores, { testScores: data.testScores, locale }),
888
+ /* @__PURE__ */ jsx19(ContactInfo, { contact: data.contact, locale }),
889
+ showFooter ? /* @__PURE__ */ jsx19(Footer, {}) : null
684
890
  ] });
685
891
  }
686
892
 
687
893
  // src/components/LocaleSwitcher.tsx
688
- import styles18 from "./LocaleSwitcher.module-QVLPVGLG.module.css";
689
- import { jsx as jsx18 } from "react/jsx-runtime";
690
- var DEFAULT_ARIA_LABEL = "Select language";
894
+ import styles20 from "./LocaleSwitcher.module-QVLPVGLG.module.css";
895
+ import { jsx as jsx20 } from "react/jsx-runtime";
691
896
  function LocaleSwitcher({
692
897
  availableLocales,
693
898
  currentLocale,
@@ -695,18 +900,18 @@ function LocaleSwitcher({
695
900
  ariaLabel,
696
901
  formatLocale
697
902
  }) {
698
- const label = ariaLabel ?? DEFAULT_ARIA_LABEL;
903
+ const label = ariaLabel ?? getUILabel("a11y.selectLanguage", currentLocale);
699
904
  const format = formatLocale ?? ((locale) => locale);
700
- return /* @__PURE__ */ jsx18("div", { className: styles18.wrapper, children: /* @__PURE__ */ jsx18(
905
+ return /* @__PURE__ */ jsx20("div", { className: styles20.wrapper, children: /* @__PURE__ */ jsx20(
701
906
  "select",
702
907
  {
703
- className: styles18.select,
908
+ className: styles20.select,
704
909
  "aria-label": label,
705
910
  value: currentLocale,
706
911
  onChange: (event) => {
707
912
  onSelect(event.target.value);
708
913
  },
709
- children: availableLocales.map((locale) => /* @__PURE__ */ jsx18("option", { value: locale, children: format(locale) }, locale))
914
+ children: availableLocales.map((locale) => /* @__PURE__ */ jsx20("option", { value: locale, children: format(locale) }, locale))
710
915
  }
711
916
  ) });
712
917
  }
@@ -714,7 +919,7 @@ function LocaleSwitcher({
714
919
  // src/components/TakuhonHead.tsx
715
920
  import { generateJsonLd } from "@takuhon/core";
716
921
  import { useEffect } from "react";
717
- import { Fragment as Fragment4, jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
922
+ import { Fragment as Fragment4, jsx as jsx21, jsxs as jsxs20 } from "react/jsx-runtime";
718
923
  function primarySubtag(tag) {
719
924
  const dash = tag.indexOf("-");
720
925
  return (dash === -1 ? tag : tag.slice(0, dash)).toLowerCase();
@@ -799,20 +1004,20 @@ function TakuhonHead({ data, siteUrl, pageUrl }) {
799
1004
  script.remove();
800
1005
  };
801
1006
  }, [jsonLdPayload]);
802
- return /* @__PURE__ */ jsxs18(Fragment4, { children: [
803
- /* @__PURE__ */ jsx19("title", { children: profile.displayName }),
804
- description ? /* @__PURE__ */ jsx19("meta", { name: "description", content: description }) : null,
805
- canonical ? /* @__PURE__ */ jsx19("link", { rel: "canonical", href: canonical }) : null,
806
- /* @__PURE__ */ jsx19("meta", { property: "og:title", content: profile.displayName }),
807
- ogDescription ? /* @__PURE__ */ jsx19("meta", { property: "og:description", content: ogDescription }) : null,
808
- canonical ? /* @__PURE__ */ jsx19("meta", { property: "og:url", content: canonical }) : null,
809
- ogImage ? /* @__PURE__ */ jsx19("meta", { property: "og:image", content: ogImage }) : null,
810
- /* @__PURE__ */ jsx19("meta", { property: "og:type", content: "profile" }),
811
- /* @__PURE__ */ jsx19("meta", { property: "og:locale", content: resolvedLocale }),
812
- ogAlternates.map((loc) => /* @__PURE__ */ jsx19("meta", { property: "og:locale:alternate", content: loc }, loc)),
813
- /* @__PURE__ */ jsx19("meta", { name: "twitter:card", content: twitterCard }),
814
- alternates.map(({ locale, href }) => /* @__PURE__ */ jsx19("link", { rel: "alternate", hrefLang: locale, href }, locale)),
815
- xDefaultHref ? /* @__PURE__ */ jsx19("link", { rel: "alternate", hrefLang: "x-default", href: xDefaultHref }) : null
1007
+ return /* @__PURE__ */ jsxs20(Fragment4, { children: [
1008
+ /* @__PURE__ */ jsx21("title", { children: profile.displayName }),
1009
+ description ? /* @__PURE__ */ jsx21("meta", { name: "description", content: description }) : null,
1010
+ canonical ? /* @__PURE__ */ jsx21("link", { rel: "canonical", href: canonical }) : null,
1011
+ /* @__PURE__ */ jsx21("meta", { property: "og:title", content: profile.displayName }),
1012
+ ogDescription ? /* @__PURE__ */ jsx21("meta", { property: "og:description", content: ogDescription }) : null,
1013
+ canonical ? /* @__PURE__ */ jsx21("meta", { property: "og:url", content: canonical }) : null,
1014
+ ogImage ? /* @__PURE__ */ jsx21("meta", { property: "og:image", content: ogImage }) : null,
1015
+ /* @__PURE__ */ jsx21("meta", { property: "og:type", content: "profile" }),
1016
+ /* @__PURE__ */ jsx21("meta", { property: "og:locale", content: resolvedLocale }),
1017
+ ogAlternates.map((loc) => /* @__PURE__ */ jsx21("meta", { property: "og:locale:alternate", content: loc }, loc)),
1018
+ /* @__PURE__ */ jsx21("meta", { name: "twitter:card", content: twitterCard }),
1019
+ alternates.map(({ locale, href }) => /* @__PURE__ */ jsx21("link", { rel: "alternate", hrefLang: locale, href }, locale)),
1020
+ xDefaultHref ? /* @__PURE__ */ jsx21("link", { rel: "alternate", hrefLang: "x-default", href: xDefaultHref }) : null
816
1021
  ] });
817
1022
  }
818
1023
  export {
@@ -831,9 +1036,11 @@ export {
831
1036
  ProfileHeader,
832
1037
  ProjectsList,
833
1038
  Publications,
1039
+ Recommendations,
834
1040
  SkillsList,
835
1041
  TakuhonHead,
836
1042
  TakuhonProfile,
1043
+ TestScores,
837
1044
  Volunteering
838
1045
  };
839
1046
  //# sourceMappingURL=index.js.map