@navikt/ds-react 7.5.0 → 7.5.2

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 (133) hide show
  1. package/cjs/expansion-card/ExpansionCardContent.js +1 -1
  2. package/cjs/expansion-card/ExpansionCardContent.js.map +1 -1
  3. package/cjs/expansion-card/ExpansionCardHeader.js +1 -1
  4. package/cjs/expansion-card/ExpansionCardHeader.js.map +1 -1
  5. package/cjs/form/combobox/FilteredOptions/useVirtualFocus.js +1 -1
  6. package/cjs/form/combobox/FilteredOptions/useVirtualFocus.js.map +1 -1
  7. package/cjs/form/file-upload/parts/dropzone/Dropzone.js +1 -1
  8. package/cjs/form/file-upload/parts/dropzone/Dropzone.js.map +1 -1
  9. package/cjs/help-text/HelpText.js +3 -1
  10. package/cjs/help-text/HelpText.js.map +1 -1
  11. package/cjs/help-text/HelpTextIcon.js +3 -1
  12. package/cjs/help-text/HelpTextIcon.js.map +1 -1
  13. package/cjs/pagination/Pagination.d.ts +5 -0
  14. package/cjs/pagination/Pagination.js +5 -0
  15. package/cjs/pagination/Pagination.js.map +1 -1
  16. package/cjs/pagination/PaginationItem.js +3 -1
  17. package/cjs/pagination/PaginationItem.js.map +1 -1
  18. package/cjs/popover/Popover.js +4 -2
  19. package/cjs/popover/Popover.js.map +1 -1
  20. package/cjs/timeline/AxisLabels.d.ts +4 -3
  21. package/cjs/timeline/AxisLabels.js +22 -13
  22. package/cjs/timeline/AxisLabels.js.map +1 -1
  23. package/cjs/timeline/Pin.js +5 -1
  24. package/cjs/timeline/Pin.js.map +1 -1
  25. package/cjs/timeline/TimelineRow.js +7 -2
  26. package/cjs/timeline/TimelineRow.js.map +1 -1
  27. package/cjs/timeline/period/ClickablePeriod.d.ts +1 -1
  28. package/cjs/timeline/period/ClickablePeriod.js +3 -1
  29. package/cjs/timeline/period/ClickablePeriod.js.map +1 -1
  30. package/cjs/timeline/period/NonClickablePeriod.d.ts +1 -1
  31. package/cjs/timeline/period/NonClickablePeriod.js +3 -1
  32. package/cjs/timeline/period/NonClickablePeriod.js.map +1 -1
  33. package/cjs/timeline/period/index.d.ts +1 -1
  34. package/cjs/timeline/period/types.d.ts +2 -1
  35. package/cjs/timeline/utils/period.d.ts +3 -1
  36. package/cjs/timeline/utils/period.js +9 -20
  37. package/cjs/timeline/utils/period.js.map +1 -1
  38. package/cjs/timeline/zoom/ZoomButton.js +8 -2
  39. package/cjs/timeline/zoom/ZoomButton.js.map +1 -1
  40. package/cjs/util/i18n/get.d.ts +1 -1
  41. package/cjs/util/i18n/get.js +2 -2
  42. package/cjs/util/i18n/get.js.map +1 -1
  43. package/cjs/util/i18n/i18n.context.d.ts +4 -1
  44. package/cjs/util/i18n/i18n.context.js +21 -3
  45. package/cjs/util/i18n/i18n.context.js.map +1 -1
  46. package/cjs/util/i18n/locales/en.d.ts +26 -0
  47. package/cjs/util/i18n/locales/en.js +27 -0
  48. package/cjs/util/i18n/locales/en.js.map +1 -1
  49. package/cjs/util/i18n/locales/nb.d.ts +27 -0
  50. package/cjs/util/i18n/locales/nb.js +27 -0
  51. package/cjs/util/i18n/locales/nb.js.map +1 -1
  52. package/cjs/util/i18n/locales/nn.d.ts +26 -0
  53. package/cjs/util/i18n/locales/nn.js +27 -0
  54. package/cjs/util/i18n/locales/nn.js.map +1 -1
  55. package/esm/expansion-card/ExpansionCardContent.js +1 -1
  56. package/esm/expansion-card/ExpansionCardContent.js.map +1 -1
  57. package/esm/expansion-card/ExpansionCardHeader.js +1 -1
  58. package/esm/expansion-card/ExpansionCardHeader.js.map +1 -1
  59. package/esm/form/combobox/FilteredOptions/useVirtualFocus.js +1 -1
  60. package/esm/form/combobox/FilteredOptions/useVirtualFocus.js.map +1 -1
  61. package/esm/form/file-upload/parts/dropzone/Dropzone.js +1 -1
  62. package/esm/form/file-upload/parts/dropzone/Dropzone.js.map +1 -1
  63. package/esm/help-text/HelpText.js +3 -1
  64. package/esm/help-text/HelpText.js.map +1 -1
  65. package/esm/help-text/HelpTextIcon.js +3 -1
  66. package/esm/help-text/HelpTextIcon.js.map +1 -1
  67. package/esm/pagination/Pagination.d.ts +5 -0
  68. package/esm/pagination/Pagination.js +5 -0
  69. package/esm/pagination/Pagination.js.map +1 -1
  70. package/esm/pagination/PaginationItem.js +3 -1
  71. package/esm/pagination/PaginationItem.js.map +1 -1
  72. package/esm/popover/Popover.js +4 -2
  73. package/esm/popover/Popover.js.map +1 -1
  74. package/esm/timeline/AxisLabels.d.ts +4 -3
  75. package/esm/timeline/AxisLabels.js +22 -13
  76. package/esm/timeline/AxisLabels.js.map +1 -1
  77. package/esm/timeline/Pin.js +5 -1
  78. package/esm/timeline/Pin.js.map +1 -1
  79. package/esm/timeline/TimelineRow.js +7 -2
  80. package/esm/timeline/TimelineRow.js.map +1 -1
  81. package/esm/timeline/period/ClickablePeriod.d.ts +1 -1
  82. package/esm/timeline/period/ClickablePeriod.js +3 -1
  83. package/esm/timeline/period/ClickablePeriod.js.map +1 -1
  84. package/esm/timeline/period/NonClickablePeriod.d.ts +1 -1
  85. package/esm/timeline/period/NonClickablePeriod.js +3 -1
  86. package/esm/timeline/period/NonClickablePeriod.js.map +1 -1
  87. package/esm/timeline/period/index.d.ts +1 -1
  88. package/esm/timeline/period/types.d.ts +2 -1
  89. package/esm/timeline/utils/period.d.ts +3 -1
  90. package/esm/timeline/utils/period.js +9 -20
  91. package/esm/timeline/utils/period.js.map +1 -1
  92. package/esm/timeline/zoom/ZoomButton.js +8 -2
  93. package/esm/timeline/zoom/ZoomButton.js.map +1 -1
  94. package/esm/util/i18n/get.d.ts +1 -1
  95. package/esm/util/i18n/get.js +2 -2
  96. package/esm/util/i18n/get.js.map +1 -1
  97. package/esm/util/i18n/i18n.context.d.ts +4 -1
  98. package/esm/util/i18n/i18n.context.js +20 -3
  99. package/esm/util/i18n/i18n.context.js.map +1 -1
  100. package/esm/util/i18n/locales/en.d.ts +26 -0
  101. package/esm/util/i18n/locales/en.js +27 -0
  102. package/esm/util/i18n/locales/en.js.map +1 -1
  103. package/esm/util/i18n/locales/nb.d.ts +27 -0
  104. package/esm/util/i18n/locales/nb.js +27 -0
  105. package/esm/util/i18n/locales/nb.js.map +1 -1
  106. package/esm/util/i18n/locales/nn.d.ts +26 -0
  107. package/esm/util/i18n/locales/nn.js +27 -0
  108. package/esm/util/i18n/locales/nn.js.map +1 -1
  109. package/package.json +3 -3
  110. package/src/expansion-card/ExpansionCardContent.tsx +1 -0
  111. package/src/expansion-card/ExpansionCardHeader.tsx +1 -0
  112. package/src/form/combobox/FilteredOptions/useVirtualFocus.ts +1 -1
  113. package/src/form/file-upload/parts/dropzone/Dropzone.tsx +1 -0
  114. package/src/help-text/HelpText.tsx +5 -1
  115. package/src/help-text/HelpTextIcon.tsx +5 -1
  116. package/src/pagination/Pagination.tsx +6 -0
  117. package/src/pagination/PaginationItem.tsx +5 -1
  118. package/src/popover/Popover.tsx +6 -2
  119. package/src/timeline/AxisLabels.tsx +30 -16
  120. package/src/timeline/Pin.tsx +5 -1
  121. package/src/timeline/TimelineRow.tsx +7 -5
  122. package/src/timeline/period/ClickablePeriod.tsx +4 -2
  123. package/src/timeline/period/NonClickablePeriod.tsx +5 -2
  124. package/src/timeline/period/index.tsx +1 -1
  125. package/src/timeline/period/types.ts +3 -1
  126. package/src/timeline/utils/period.ts +13 -24
  127. package/src/timeline/zoom/ZoomButton.tsx +8 -5
  128. package/src/util/i18n/get.ts +2 -2
  129. package/src/util/i18n/i18n.context.ts +35 -13
  130. package/src/util/i18n/locales/en.ts +27 -0
  131. package/src/util/i18n/locales/nb.ts +29 -5
  132. package/src/util/i18n/locales/nn.ts +27 -0
  133. package/src/util/i18n/locales.test.tsx +4 -1
@@ -1,4 +1,5 @@
1
1
  import {
2
+ Locale,
2
3
  addDays,
3
4
  addMonths,
4
5
  addYears,
@@ -13,9 +14,9 @@ import {
13
14
  startOfYear,
14
15
  subDays,
15
16
  } from "date-fns";
16
- import { nb as nbLocale } from "date-fns/locale";
17
17
  import React from "react";
18
18
  import { Detail } from "../typography/Detail";
19
+ import { TFunction, useDateLocale, useI18n } from "../util/i18n/i18n.context";
19
20
  import { useTimelineContext } from "./hooks/useTimelineContext";
20
21
  import { isVisible } from "./utils";
21
22
  import { horizontalPositionAndWidth } from "./utils/calc";
@@ -26,7 +27,8 @@ export const dayLabels = (
26
27
  end: Date,
27
28
  totalDays: number,
28
29
  direction: "left" | "right",
29
- template: string = "dd.MM",
30
+ template: string,
31
+ locale: Locale,
30
32
  ): AxisLabel[] => {
31
33
  const increment = Math.ceil(totalDays / 10);
32
34
  const lastDay = startOfDay(end);
@@ -44,7 +46,7 @@ export const dayLabels = (
44
46
  return {
45
47
  direction,
46
48
  horizontalPosition,
47
- label: format(day, template, { locale: nbLocale }),
49
+ label: format(day, template, { locale }),
48
50
  date: day,
49
51
  width,
50
52
  };
@@ -56,7 +58,8 @@ export const monthLabels = (
56
58
  start: Date,
57
59
  end: Date,
58
60
  direction: "left" | "right",
59
- template: string = "MMM yy",
61
+ template: string,
62
+ locale: Locale,
60
63
  ): AxisLabel[] => {
61
64
  const startMonth = startOfMonth(start);
62
65
  const endMonth = endOfMonth(end);
@@ -72,7 +75,7 @@ export const monthLabels = (
72
75
  return {
73
76
  direction,
74
77
  horizontalPosition,
75
- label: format(month, template, { locale: nbLocale }),
78
+ label: format(month, template, { locale }),
76
79
  date: month,
77
80
  width,
78
81
  };
@@ -83,7 +86,8 @@ export const yearLabels = (
83
86
  start: Date,
84
87
  end: Date,
85
88
  direction: "left" | "right",
86
- template: string = "yyyy",
89
+ template: string,
90
+ locale: Locale,
87
91
  ): AxisLabel[] => {
88
92
  const firstYear = startOfYear(start);
89
93
  const lastYear = endOfYear(end);
@@ -99,29 +103,33 @@ export const yearLabels = (
99
103
  return {
100
104
  direction,
101
105
  horizontalPosition,
102
- label: format(year, template, { locale: nbLocale }),
106
+ label: format(year, template, { locale }),
103
107
  date: year,
104
108
  width,
105
109
  };
106
110
  });
107
111
  };
108
112
 
109
- const axisLabels = (
113
+ const getLabels = (
110
114
  start: Date,
111
115
  end: Date,
112
116
  direction: "left" | "right",
113
- templates?: AxisLabelTemplates,
117
+ locale: Locale,
118
+ translate: TFunction<"Timeline">,
114
119
  ): AxisLabel[] => {
115
120
  const totalDays = differenceInDays(end, start);
116
121
  if (totalDays < 40) {
117
- return dayLabels(start, end, totalDays, direction, templates?.day);
122
+ const dayTemplate = translate("dayFormat");
123
+ return dayLabels(start, end, totalDays, direction, dayTemplate, locale);
118
124
  }
119
125
 
120
126
  if (totalDays < 370) {
121
- return monthLabels(start, end, direction, templates?.month);
127
+ const monthTemplate = translate("monthFormat");
128
+ return monthLabels(start, end, direction, monthTemplate, locale);
122
129
  }
123
130
 
124
- return yearLabels(start, end, direction, templates?.year);
131
+ const yearTemplate = translate("yearFormat");
132
+ return yearLabels(start, end, direction, yearTemplate, locale);
125
133
  };
126
134
 
127
135
  export const AxisLabels = ({
@@ -130,13 +138,19 @@ export const AxisLabels = ({
130
138
  templates?: AxisLabelTemplates;
131
139
  }) => {
132
140
  const { endDate, startDate, direction } = useTimelineContext();
133
- const labels = axisLabels(startDate, endDate, direction, templates).filter(
134
- isVisible,
135
- );
141
+
142
+ const translate = useI18n("Timeline", {
143
+ dayFormat: templates?.day,
144
+ monthFormat: templates?.month,
145
+ yearFormat: templates?.year,
146
+ });
147
+ const locale = useDateLocale();
148
+
149
+ const labels = getLabels(startDate, endDate, direction, locale, translate);
136
150
 
137
151
  return (
138
152
  <div className="navds-timeline__axislabels" aria-hidden="true">
139
- {labels.map((etikett) => (
153
+ {labels.filter(isVisible).map((etikett) => (
140
154
  <Detail
141
155
  className="navds-timeline__axislabels-label"
142
156
  as="div"
@@ -16,6 +16,7 @@ import {
16
16
  import { format } from "date-fns";
17
17
  import React, { forwardRef, useRef, useState } from "react";
18
18
  import { useMergeRefs } from "../util/hooks/useMergeRefs";
19
+ import { useI18n } from "../util/i18n/i18n.context";
19
20
  import { useTimelineContext } from "./hooks/useTimelineContext";
20
21
  import { position } from "./utils/calc";
21
22
  import { TimelineComponentTypes } from "./utils/types.internal";
@@ -44,6 +45,7 @@ export const Pin = forwardRef<HTMLButtonElement, TimelinePinProps>(
44
45
  const { startDate, endDate, direction } = useTimelineContext();
45
46
  const [open, setOpen] = useState(false);
46
47
  const arrowRef = useRef<HTMLDivElement | null>(null);
48
+ const translate = useI18n("Timeline");
47
49
 
48
50
  const {
49
51
  context,
@@ -100,7 +102,9 @@ export const Pin = forwardRef<HTMLButtonElement, TimelinePinProps>(
100
102
  {...rest}
101
103
  ref={mergedRef}
102
104
  className="navds-timeline__pin-button"
103
- aria-label={`pin:${format(date, "dd.MM.yyyy")}`}
105
+ aria-label={translate("Pin.pin", {
106
+ date: format(date, translate("dateFormat")),
107
+ })}
104
108
  type="button"
105
109
  aria-expanded={children ? open : undefined}
106
110
  {...getReferenceProps({
@@ -2,6 +2,7 @@ import cl from "clsx";
2
2
  import { format } from "date-fns";
3
3
  import React, { forwardRef } from "react";
4
4
  import { BodyShort } from "../typography/BodyShort";
5
+ import { useI18n } from "../util/i18n/i18n.context";
5
6
  import { PeriodContext } from "./hooks/usePeriodContext";
6
7
  import { useRowContext } from "./hooks/useRowContext";
7
8
  import { useTimelineContext } from "./hooks/useTimelineContext";
@@ -39,6 +40,7 @@ export const TimelineRow = forwardRef<HTMLOListElement, TimelineRowProps>(
39
40
  ({ label, className, headingTag = "h3", icon, ...rest }, ref) => {
40
41
  const { periods, id, active } = useRowContext();
41
42
  const { setActiveRow } = useTimelineContext();
43
+ const translate = useI18n("Timeline");
42
44
 
43
45
  const latest = periods.reduce((a, b) => {
44
46
  return a.end > b.end ? a : b;
@@ -77,11 +79,11 @@ export const TimelineRow = forwardRef<HTMLOListElement, TimelineRowProps>(
77
79
  ref={ref}
78
80
  aria-label={
79
81
  periods.length === 0
80
- ? "Ingen perioder"
81
- : `${format(earliest.start, "dd.MM.yyyy")} til ${format(
82
- latest.end,
83
- "dd.MM.yyyy",
84
- )}`
82
+ ? translate("Row.noPeriods")
83
+ : translate("Row.period", {
84
+ start: format(earliest.start, translate("dateFormat")),
85
+ end: format(latest.end, translate("dateFormat")),
86
+ })
85
87
  }
86
88
  className={cl("navds-timeline__row-periods", className)}
87
89
  onKeyDown={(e) => {
@@ -16,11 +16,12 @@ import {
16
16
  import cl from "clsx";
17
17
  import React, { useRef, useState } from "react";
18
18
  import { useMergeRefs } from "../../util/hooks/useMergeRefs";
19
+ import { useI18n } from "../../util/i18n/i18n.context";
19
20
  import { usePeriodContext } from "../hooks/usePeriodContext";
20
21
  import { useRowContext } from "../hooks/useRowContext";
21
22
  import { useTimelineContext } from "../hooks/useTimelineContext";
22
23
  import { ariaLabel, getConditionalClasses } from "../utils/period";
23
- import { PeriodProps } from "./types";
24
+ import type { PeriodProps } from "./types";
24
25
 
25
26
  interface TimelineClickablePeriodProps extends PeriodProps {
26
27
  onSelectPeriod?: (
@@ -52,6 +53,7 @@ const ClickablePeriod = React.memo(
52
53
  const { firstFocus } = usePeriodContext();
53
54
  const { initiate, addFocusable } = useTimelineContext();
54
55
  const arrowRef = useRef<HTMLDivElement | null>(null);
56
+ const translate = useI18n("Timeline");
55
57
 
56
58
  const {
57
59
  context,
@@ -107,7 +109,7 @@ const ClickablePeriod = React.memo(
107
109
  firstFocus && addFocusable(r, index);
108
110
  mergedRef(r);
109
111
  }}
110
- aria-label={ariaLabel(start, end, status, statusLabel)}
112
+ aria-label={ariaLabel(start, end, status, statusLabel, translate)}
111
113
  className={cl(
112
114
  "navds-timeline__period--clickable",
113
115
  getConditionalClasses(cropped, direction, status),
@@ -1,7 +1,8 @@
1
1
  import cl from "clsx";
2
2
  import React from "react";
3
+ import { useI18n } from "../../util/i18n/i18n.context";
3
4
  import { ariaLabel, getConditionalClasses } from "../utils/period";
4
- import { PeriodProps } from "./types";
5
+ import type { PeriodProps } from "./types";
5
6
 
6
7
  interface TimelineNonClickablePeriodProps extends PeriodProps {
7
8
  periodRef?: React.ForwardedRef<HTMLDivElement>;
@@ -20,6 +21,8 @@ const NonClickablePeriod = ({
20
21
  restProps,
21
22
  periodRef,
22
23
  }: TimelineNonClickablePeriodProps) => {
24
+ const translate = useI18n("Timeline");
25
+
23
26
  return (
24
27
  <div
25
28
  ref={periodRef}
@@ -36,7 +39,7 @@ const NonClickablePeriod = ({
36
39
  <span className="navds-timeline__period--inner">
37
40
  {icon}
38
41
  <span className="sr-only">
39
- {ariaLabel(start, end, status, statusLabel)}
42
+ {ariaLabel(start, end, status, statusLabel, translate)}
40
43
  </span>
41
44
  </span>
42
45
  </div>
@@ -1,7 +1,7 @@
1
1
  import React, { forwardRef } from "react";
2
2
  import { usePeriodContext } from "../hooks/usePeriodContext";
3
3
  import { useRowContext } from "../hooks/useRowContext";
4
- import { TimelineComponentTypes } from "../utils/types.internal";
4
+ import type { TimelineComponentTypes } from "../utils/types.internal";
5
5
  import ClickablePeriod from "./ClickablePeriod";
6
6
  import NonClickablePeriod from "./NonClickablePeriod";
7
7
 
@@ -1,7 +1,9 @@
1
+ import type { TimelinePeriodProps } from ".";
2
+
1
3
  export interface PeriodProps {
2
4
  start: Date;
3
5
  end: Date;
4
- status: string;
6
+ status: Exclude<TimelinePeriodProps["status"], undefined>;
5
7
  cropped: string;
6
8
  direction: string;
7
9
  width: number;
@@ -1,5 +1,7 @@
1
1
  import cl from "clsx";
2
2
  import { format } from "date-fns";
3
+ import type { TFunction } from "../../util/i18n/i18n.context";
4
+ import type { PeriodProps } from "../period/types";
3
5
 
4
6
  export const getConditionalClasses = (
5
7
  cropped: string,
@@ -17,32 +19,19 @@ export const getConditionalClasses = (
17
19
  });
18
20
  };
19
21
 
20
- const translateStatus = (status: string): string => {
21
- switch (status) {
22
- case "success":
23
- return "Suksess";
24
- case "warning":
25
- return "Advarsel";
26
- case "danger":
27
- return "Fare";
28
- case "info":
29
- return "Info";
30
- case "neutral":
31
- return "Nøytral";
32
- default:
33
- return status;
34
- }
35
- };
36
-
37
22
  export const ariaLabel = (
38
23
  startDate: Date,
39
24
  endDate: Date,
40
- status: string,
41
- statusLabel?: string,
25
+ status: PeriodProps["status"],
26
+ statusLabel: string | undefined,
27
+ translate: TFunction<"Timeline">,
42
28
  ): string => {
43
- const start = format(startDate, "dd.MM.yyyy");
44
- const end = format(endDate, "dd.MM.yyyy");
45
- return `${
46
- statusLabel ? statusLabel : translateStatus(status)
47
- } fra ${start} til ${end}`;
29
+ const dateFormat = translate("dateFormat");
30
+ const start = format(startDate, dateFormat);
31
+ const end = format(endDate, dateFormat);
32
+ return translate("Period.period", {
33
+ status: statusLabel || translate(`Period.${status}`),
34
+ start,
35
+ end,
36
+ });
48
37
  };
@@ -8,6 +8,7 @@ import {
8
8
  } from "date-fns";
9
9
  import React, { forwardRef } from "react";
10
10
  import { Detail } from "../../typography/Detail";
11
+ import { useI18n } from "../../util/i18n/i18n.context";
11
12
  import { useTimelineContext } from "../hooks/useTimelineContext";
12
13
 
13
14
  export interface TimelineZoomButtonProps {
@@ -33,6 +34,8 @@ export type ZoomButtonType = React.ForwardRefExoticComponent<
33
34
  export const ZoomButton: ZoomButtonType = forwardRef(
34
35
  ({ label, interval, count, ...rest }, ref) => {
35
36
  const { setStart, endDate, startDate, direction } = useTimelineContext();
37
+ const translate = useI18n("Timeline");
38
+ const dateFormat = translate("dateFormat");
36
39
 
37
40
  let startOfRange: Date;
38
41
 
@@ -59,11 +62,11 @@ export const ZoomButton: ZoomButtonType = forwardRef(
59
62
  type="button"
60
63
  aria-label={
61
64
  !currentZoom
62
- ? `Zoom tidslinjen ${format(
63
- startOfRange,
64
- "dd.MM.yyyy",
65
- )} til ${format(endDate, "dd.MM.yyyy")}`
66
- : "Tilbakestill til initiell tidsperspektiv"
65
+ ? translate("Zoom.zoom", {
66
+ start: format(startOfRange, dateFormat),
67
+ end: format(endDate, dateFormat),
68
+ })
69
+ : translate("Zoom.reset")
67
70
  }
68
71
  ref={ref}
69
72
  {...rest}
@@ -7,7 +7,7 @@ const OBJECT_NOTATION_MATCHER = /(\w+)/g;
7
7
 
8
8
  export function get(
9
9
  keypath: string | string[],
10
- ...objs: (PartialTranslations | undefined)[]
10
+ objs: (PartialTranslations | undefined)[],
11
11
  ) {
12
12
  const keys = Array.isArray(keypath) ? keypath : getKeypath(keypath);
13
13
 
@@ -32,7 +32,7 @@ export function get(
32
32
  }
33
33
 
34
34
  throw new Error(
35
- "Error translating key. The keypath does not resolve to a string.",
35
+ `Error translating key. Keypath '${keypath}' does not resolve to a string.`,
36
36
  );
37
37
  }
38
38
 
@@ -1,7 +1,13 @@
1
+ import { Locale } from "date-fns";
1
2
  import { useContext } from "react";
2
3
  import { LanguageProviderContext } from "../../provider/i18n/LanguageProvider";
3
4
  import { get } from "./get";
4
- import { Component, ComponentTranslation, Translations } from "./i18n.types";
5
+ import {
6
+ Component,
7
+ ComponentTranslation,
8
+ PartialTranslations,
9
+ Translations,
10
+ } from "./i18n.types";
5
11
 
6
12
  /**
7
13
  * https://regex101.com/r/LYKWi3/1
@@ -15,27 +21,29 @@ type NestedKeyOf<ObjectType extends object> = {
15
21
  : `${Key}`;
16
22
  }[keyof ObjectType & (string | number)];
17
23
 
24
+ export type TFunction<T extends Component> = (
25
+ keypath: NestedKeyOf<Translations[T]>,
26
+ replacements?: Record<string, string | number>,
27
+ ) => string;
28
+
18
29
  export function useI18n<T extends Component>(
19
30
  componentName: T,
20
31
  ...local: (ComponentTranslation<T> | undefined)[]
21
32
  ) {
22
33
  const languageProviderContext = useContext(LanguageProviderContext);
23
34
  const i18n = languageProviderContext.translations;
35
+ const i18nObjects: (PartialTranslations | undefined)[] = [
36
+ ...local,
37
+ ...(Array.isArray(i18n)
38
+ ? i18n.map((t) => t[componentName])
39
+ : [i18n[componentName]]),
40
+ ];
24
41
 
25
42
  /**
26
43
  * https://github.com/Shopify/polaris/blob/2115f9ba2f5bcbf2ad15745233501bff2db81ecf/polaris-react/src/utilities/i18n/I18n.ts#L24
27
44
  */
28
- const translate = (
29
- keypath: NestedKeyOf<Translations[T]>,
30
- replacements?: Record<string, string | number>,
31
- ) => {
32
- const text = get(
33
- keypath,
34
- ...local,
35
- ...(Array.isArray(i18n)
36
- ? i18n.map((t) => t[componentName])
37
- : [i18n[componentName]]),
38
- );
45
+ const translate: TFunction<T> = (keypath, replacements) => {
46
+ const text = get(keypath, i18nObjects);
39
47
 
40
48
  if (replacements) {
41
49
  return text.replace(REPLACE_REGEX, (match) => {
@@ -43,7 +51,6 @@ export function useI18n<T extends Component>(
43
51
 
44
52
  if (replacements[replacement] === undefined) {
45
53
  const replacementData = JSON.stringify(replacements);
46
-
47
54
  throw new Error(
48
55
  `Error translating key '${keypath}'. No replacement syntax ({}) found for key '${replacement}'. The following replacements were passed: '${replacementData}'`,
49
56
  );
@@ -58,3 +65,18 @@ export function useI18n<T extends Component>(
58
65
 
59
66
  return translate;
60
67
  }
68
+
69
+ export function useDateLocale() {
70
+ const languageProviderContext = useContext(LanguageProviderContext);
71
+ const i18n = languageProviderContext.translations;
72
+ const i18nObjects = Array.isArray(i18n)
73
+ ? i18n.map((t) => t.global)
74
+ : [i18n.global];
75
+
76
+ for (const obj of i18nObjects) {
77
+ if (obj?.dateLocale) {
78
+ return obj.dateLocale as Locale;
79
+ }
80
+ }
81
+ throw new Error("dateLocale not found.");
82
+ }
@@ -1,7 +1,9 @@
1
+ import { enGB } from "date-fns/locale";
1
2
  import type { Translations } from "../i18n.types";
2
3
 
3
4
  export default {
4
5
  global: {
6
+ dateLocale: enGB,
5
7
  showMore: "Show more",
6
8
  showLess: "Show less",
7
9
  readOnly: "Read-only",
@@ -70,4 +72,29 @@ export default {
70
72
  charsTooMany: "{chars} characters too many",
71
73
  charsLeft: "{chars} characters left",
72
74
  },
75
+ Timeline: {
76
+ dateFormat: "yyyy-MM-dd",
77
+ dayFormat: "d MMM",
78
+ monthFormat: "MMM yyyy",
79
+ yearFormat: "yyyy",
80
+ Row: {
81
+ noPeriods: "No periods",
82
+ period: "{start} to {end}",
83
+ },
84
+ Period: {
85
+ success: "Success",
86
+ warning: "Warning",
87
+ danger: "Danger",
88
+ info: "Info",
89
+ neutral: "Neutral",
90
+ period: "{status} from {start} to {end}",
91
+ },
92
+ Pin: {
93
+ pin: "Pin: {date}",
94
+ },
95
+ Zoom: {
96
+ zoom: "Zoom timeline {start} to {end}",
97
+ reset: "Reset zoom",
98
+ },
99
+ },
73
100
  } satisfies Translations;
@@ -1,13 +1,12 @@
1
+ import { Locale, nb } from "date-fns/locale";
2
+
1
3
  interface TranslationMap {
2
- [component: string]:
3
- | Record<string, string>
4
- | {
5
- [subComponent: string]: Record<string, string>;
6
- };
4
+ [component: string]: Record<string, string | Record<string, string> | Locale>;
7
5
  }
8
6
 
9
7
  export default {
10
8
  global: {
9
+ dateLocale: nb,
11
10
  showMore: "Vis mer",
12
11
  showLess: "Vis mindre",
13
12
  readOnly: "Skrivebeskyttet",
@@ -78,4 +77,29 @@ export default {
78
77
  charsTooMany: "{chars} tegn for mye",
79
78
  charsLeft: "{chars} tegn igjen",
80
79
  },
80
+ Timeline: {
81
+ dateFormat: "dd.MM.yyyy",
82
+ dayFormat: "dd.MM",
83
+ monthFormat: "MMM yy",
84
+ yearFormat: "yyyy",
85
+ Row: {
86
+ noPeriods: "Ingen perioder",
87
+ period: "{start} til {end}",
88
+ },
89
+ Period: {
90
+ success: "Suksess",
91
+ warning: "Advarsel",
92
+ danger: "Fare",
93
+ info: "Info",
94
+ neutral: "Nøytral", // TODO: Consider making these global
95
+ period: "{status} fra {start} til {end}",
96
+ },
97
+ Pin: {
98
+ pin: "Pin: {date}",
99
+ },
100
+ Zoom: {
101
+ zoom: "Zoom tidslinjen {start} til {end}",
102
+ reset: "Tilbakestill tidsperspektiv",
103
+ },
104
+ },
81
105
  } satisfies TranslationMap;
@@ -1,7 +1,9 @@
1
+ import { nn } from "date-fns/locale";
1
2
  import type { Translations } from "../i18n.types";
2
3
 
3
4
  export default {
4
5
  global: {
6
+ dateLocale: nn,
5
7
  showMore: "Vis meir",
6
8
  showLess: "Vis mindre",
7
9
  readOnly: "Skrivebeskytta",
@@ -70,4 +72,29 @@ export default {
70
72
  charsTooMany: "{chars} teikn for mykje",
71
73
  charsLeft: "{chars} teikn igjen",
72
74
  },
75
+ Timeline: {
76
+ dateFormat: "dd.MM.yyyy",
77
+ dayFormat: "dd.MM",
78
+ monthFormat: "MMM yy",
79
+ yearFormat: "yyyy",
80
+ Row: {
81
+ noPeriods: "Ingen periodar",
82
+ period: "{start} til {end}",
83
+ },
84
+ Period: {
85
+ success: "Suksess",
86
+ warning: "Åtvaring",
87
+ danger: "Fare",
88
+ info: "Info",
89
+ neutral: "Nøytral",
90
+ period: "{status} frå {start} til {end}",
91
+ },
92
+ Pin: {
93
+ pin: "Pin: {date}",
94
+ },
95
+ Zoom: {
96
+ zoom: "Zoom tidslina {start} til {end}",
97
+ reset: "Tilbakestill tidsperspektiv",
98
+ },
99
+ },
73
100
  } satisfies Translations;
@@ -6,7 +6,10 @@ import nb from "./locales/nb";
6
6
  import nn from "./locales/nn";
7
7
 
8
8
  function checkValues(obj: Translations | Record<string, string>) {
9
- Object.values(obj).forEach((value) => {
9
+ Object.entries(obj).forEach(([key, value]) => {
10
+ if (key === "dateLocale") {
11
+ return;
12
+ }
10
13
  if (typeof value === "object") {
11
14
  checkValues(value);
12
15
  } else {