@messagevisor/vue 0.0.1 → 0.1.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.
Files changed (56) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/LICENSE +21 -0
  3. package/README.md +9 -0
  4. package/jest.config.js +13 -0
  5. package/lib/MessagevisorContext.d.ts +21 -0
  6. package/lib/MessagevisorContext.js +5 -0
  7. package/lib/MessagevisorContext.js.map +1 -0
  8. package/lib/MessagevisorProvider.d.ts +44 -0
  9. package/lib/MessagevisorProvider.js +45 -0
  10. package/lib/MessagevisorProvider.js.map +1 -0
  11. package/lib/api.d.ts +20 -0
  12. package/lib/api.js +60 -0
  13. package/lib/api.js.map +1 -0
  14. package/lib/components.d.ts +86 -0
  15. package/lib/components.js +95 -0
  16. package/lib/components.js.map +1 -0
  17. package/lib/createMessagevisorProvider.d.ts +18 -0
  18. package/lib/createMessagevisorProvider.js +24 -0
  19. package/lib/createMessagevisorProvider.js.map +1 -0
  20. package/lib/index.d.ts +10 -0
  21. package/lib/index.js +41 -0
  22. package/lib/index.js.map +1 -0
  23. package/lib/richText.d.ts +16 -0
  24. package/lib/richText.js +87 -0
  25. package/lib/richText.js.map +1 -0
  26. package/lib/useMessagevisor.d.ts +1 -0
  27. package/lib/useMessagevisor.js +11 -0
  28. package/lib/useMessagevisor.js.map +1 -0
  29. package/lib/useMessagevisorSnapshot.d.ts +3 -0
  30. package/lib/useMessagevisorSnapshot.js +18 -0
  31. package/lib/useMessagevisorSnapshot.js.map +1 -0
  32. package/lib/useReactiveMessagevisor.d.ts +36 -0
  33. package/lib/useReactiveMessagevisor.js +169 -0
  34. package/lib/useReactiveMessagevisor.js.map +1 -0
  35. package/lib/useSdk.d.ts +2 -0
  36. package/lib/useSdk.js +17 -0
  37. package/lib/useSdk.js.map +1 -0
  38. package/package.json +49 -13
  39. package/src/MessagevisorContext.ts +28 -0
  40. package/src/MessagevisorProvider.spec.ts +99 -0
  41. package/src/MessagevisorProvider.ts +48 -0
  42. package/src/api.ts +119 -0
  43. package/src/components.spec.ts +134 -0
  44. package/src/components.ts +111 -0
  45. package/src/createMessagevisorProvider.ts +43 -0
  46. package/src/index.ts +41 -0
  47. package/src/richText.ts +130 -0
  48. package/src/testUtils.ts +82 -0
  49. package/src/useMessagevisor.spec.ts +70 -0
  50. package/src/useMessagevisor.ts +10 -0
  51. package/src/useMessagevisorSnapshot.ts +20 -0
  52. package/src/useReactiveMessagevisor.spec.ts +185 -0
  53. package/src/useReactiveMessagevisor.ts +353 -0
  54. package/src/useSdk.ts +19 -0
  55. package/tsconfig.cjs.json +12 -0
  56. package/tsconfig.typecheck.json +4 -0
package/src/api.ts ADDED
@@ -0,0 +1,119 @@
1
+ import type {
2
+ EvaluationOptions,
3
+ MessageFormatResult,
4
+ MessagePrimitiveValue,
5
+ MessageValues,
6
+ Messagevisor,
7
+ TranslateOptions,
8
+ } from "@messagevisor/sdk";
9
+ import type { MessageKey } from "@messagevisor/types";
10
+
11
+ import type { MessagevisorVueContextValue, VueMessageChunk } from "./MessagevisorContext";
12
+ import { createRichTextTools, type VueMessageValues, type VueRichMessageValues } from "./richText";
13
+
14
+ const BOUND_METHODS = [
15
+ "formatNumber",
16
+ "formatNumberToParts",
17
+ "formatDate",
18
+ "formatDateToParts",
19
+ "formatTime",
20
+ "formatTimeToParts",
21
+ "formatDateTimeRange",
22
+ "formatRelativeTime",
23
+ "formatPlural",
24
+ "formatList",
25
+ "formatListToParts",
26
+ "formatDisplayName",
27
+ "setLocale",
28
+ "getLocale",
29
+ "getDirection",
30
+ "setContext",
31
+ "getContext",
32
+ "setCurrency",
33
+ "getCurrency",
34
+ "setTimeZone",
35
+ "getTimeZone",
36
+ "setDatafile",
37
+ "getRevision",
38
+ ] as const satisfies readonly (keyof Messagevisor)[];
39
+
40
+ type VueTranslationMethod = {
41
+ (
42
+ messageKey: MessageKey,
43
+ values?: Record<string, MessagePrimitiveValue>,
44
+ options?: TranslateOptions,
45
+ ): string;
46
+ (
47
+ messageKey: MessageKey,
48
+ values: VueRichMessageValues,
49
+ options?: TranslateOptions,
50
+ ): MessageFormatResult<VueMessageChunk>;
51
+ };
52
+
53
+ type VueFormatMessageMethod = {
54
+ (
55
+ message: string,
56
+ values?: Record<string, MessagePrimitiveValue>,
57
+ options?: EvaluationOptions,
58
+ ): string;
59
+ (
60
+ message: string,
61
+ values: VueRichMessageValues,
62
+ options?: EvaluationOptions,
63
+ ): MessageFormatResult<VueMessageChunk>;
64
+ };
65
+
66
+ export type MessagevisorApi = {
67
+ t: VueTranslationMethod;
68
+ translate: VueTranslationMethod;
69
+ formatMessage: VueFormatMessageMethod;
70
+ } & Pick<Messagevisor, (typeof BOUND_METHODS)[number]>;
71
+
72
+ function bindMethod<K extends keyof Messagevisor>(instance: Messagevisor, key: K): Messagevisor[K] {
73
+ return (instance[key] as (...args: never[]) => unknown).bind(instance) as Messagevisor[K];
74
+ }
75
+
76
+ export function createMessagevisorApi(context: MessagevisorVueContextValue): MessagevisorApi {
77
+ const sdk = context.instance;
78
+ const richText = createRichTextTools(context);
79
+
80
+ const translate = ((messageKey, values, options) => {
81
+ const message = sdk.getRawTranslation(messageKey, options);
82
+ const translation = sdk.translate<VueMessageChunk>(
83
+ messageKey,
84
+ richText.mergeValues(values as VueMessageValues, message) as MessageValues<VueMessageChunk>,
85
+ options,
86
+ );
87
+
88
+ return richText.wrapResult(
89
+ richText.runModules(translation, {
90
+ source: "translation",
91
+ messageKey,
92
+ }),
93
+ );
94
+ }) as VueTranslationMethod;
95
+
96
+ const result = {
97
+ t: translate,
98
+ translate,
99
+ formatMessage: ((message, values, options) => {
100
+ const translation = sdk.formatMessage<VueMessageChunk>(
101
+ message,
102
+ richText.mergeValues(values as VueMessageValues, message) as MessageValues<VueMessageChunk>,
103
+ options,
104
+ );
105
+
106
+ return richText.wrapResult(
107
+ richText.runModules(translation, {
108
+ source: "formatMessage",
109
+ }),
110
+ );
111
+ }) as VueFormatMessageMethod,
112
+ } as MessagevisorApi;
113
+
114
+ for (const key of BOUND_METHODS) {
115
+ result[key] = bindMethod(sdk, key) as never;
116
+ }
117
+
118
+ return result;
119
+ }
@@ -0,0 +1,134 @@
1
+ import { mount } from "@vue/test-utils";
2
+ import { defineComponent, h } from "vue";
3
+
4
+ import { MessagevisorProvider } from "./MessagevisorProvider";
5
+ import { FormatMessage, MessageTranslation } from "./components";
6
+ import { createRichTestInstance } from "./testUtils";
7
+
8
+ describe("Vue components", function () {
9
+ it("renders translations and raw formatted messages", function () {
10
+ const m = createRichTestInstance();
11
+ const wrapper = mount(MessagevisorProvider, {
12
+ props: { instance: m },
13
+ slots: {
14
+ default: () =>
15
+ h("div", [
16
+ h(MessageTranslation, {
17
+ class: "translation",
18
+ messageKey: "greeting",
19
+ tag: "span",
20
+ values: { name: "Ada" },
21
+ }),
22
+ h(FormatMessage, {
23
+ class: "format",
24
+ message: "Hi {name}",
25
+ tag: "span",
26
+ values: { name: "Grace" },
27
+ }),
28
+ ]),
29
+ },
30
+ });
31
+
32
+ expect(wrapper.find(".translation").text()).toEqual("Hello Ada");
33
+ expect(wrapper.find(".format").text()).toEqual("Hi Grace");
34
+ });
35
+
36
+ it("renders rich text with provider defaults", function () {
37
+ const m = createRichTestInstance();
38
+ const wrapper = mount(MessagevisorProvider, {
39
+ props: {
40
+ instance: m,
41
+ defaultRichTextElements: {
42
+ strong: (chunks) => h("strong", chunks),
43
+ link: (chunks) => h("a", { href: "/terms" }, chunks),
44
+ },
45
+ },
46
+ slots: {
47
+ default: () =>
48
+ h(MessageTranslation, {
49
+ messageKey: "richTerms",
50
+ values: { product: "Messagevisor" },
51
+ }),
52
+ },
53
+ });
54
+
55
+ expect(wrapper.find("a").attributes("href")).toEqual("/terms");
56
+ expect(wrapper.find("a").text()).toEqual("terms");
57
+ expect(wrapper.find("strong").text()).toEqual("Messagevisor");
58
+ expect(wrapper.text()).toContain("Read our");
59
+ });
60
+
61
+ it("lets local rich text slots override provider defaults", function () {
62
+ const m = createRichTestInstance();
63
+ const Host = defineComponent({
64
+ render() {
65
+ return h(
66
+ MessageTranslation,
67
+ {
68
+ messageKey: "richTerms",
69
+ values: { product: "Messagevisor" },
70
+ },
71
+ {
72
+ link: ({ chunks }: { chunks: any[] }) => h("button", { type: "button" }, chunks),
73
+ },
74
+ );
75
+ },
76
+ });
77
+
78
+ const wrapper = mount(MessagevisorProvider, {
79
+ props: {
80
+ instance: m,
81
+ defaultRichTextElements: {
82
+ strong: (chunks) => h("strong", chunks),
83
+ link: (chunks) => h("a", { href: "/terms" }, chunks),
84
+ },
85
+ },
86
+ slots: { default: () => h(Host) },
87
+ });
88
+
89
+ expect(wrapper.find("button").text()).toEqual("terms");
90
+ expect(wrapper.find("a").exists()).toEqual(false);
91
+ expect(wrapper.find("strong").text()).toEqual("Messagevisor");
92
+ });
93
+
94
+ it("runs provider modules for component output", function () {
95
+ const m = createRichTestInstance();
96
+ const wrapper = mount(MessagevisorProvider, {
97
+ props: {
98
+ instance: m,
99
+ modules: [
100
+ {
101
+ transform({ translation }) {
102
+ return typeof translation === "string" ? h("mark", translation) : undefined;
103
+ },
104
+ },
105
+ ],
106
+ },
107
+ slots: {
108
+ default: () =>
109
+ h(FormatMessage, {
110
+ message: "Hello {name}",
111
+ values: { name: "Ada" },
112
+ }),
113
+ },
114
+ });
115
+
116
+ expect(wrapper.find("mark").text()).toEqual("Hello Ada");
117
+ });
118
+
119
+ it("can render without a wrapper when no tag is provided", function () {
120
+ const m = createRichTestInstance();
121
+ const wrapper = mount(MessagevisorProvider, {
122
+ props: { instance: m },
123
+ slots: {
124
+ default: () =>
125
+ h(MessageTranslation, {
126
+ messageKey: "greeting",
127
+ values: { name: "Ada" },
128
+ }),
129
+ },
130
+ });
131
+
132
+ expect(wrapper.text()).toEqual("Hello Ada");
133
+ });
134
+ });
@@ -0,0 +1,111 @@
1
+ import type { EvaluationOptions, MessageValues, TranslateOptions } from "@messagevisor/sdk";
2
+ import type { MessageKey } from "@messagevisor/types";
3
+ import { defineComponent, h, type PropType } from "vue";
4
+
5
+ import type { VueMessageChunk } from "./MessagevisorContext";
6
+ import { createRichTextTools, resolveRecord, type VueMessageValues } from "./richText";
7
+ import { useMessagevisorContextValue } from "./useSdk";
8
+
9
+ function getSlotValues(slots: Record<string, any>) {
10
+ return Object.fromEntries(
11
+ Object.keys(slots)
12
+ .filter((key) => key !== "default")
13
+ .map((key) => [key, (chunks: Array<string | VueMessageChunk>) => slots[key]?.({ chunks })]),
14
+ ) as VueMessageValues;
15
+ }
16
+
17
+ export const MessageTranslation = defineComponent({
18
+ name: "MessageTranslation",
19
+ inheritAttrs: false,
20
+ props: {
21
+ messageKey: {
22
+ type: String as PropType<MessageKey>,
23
+ required: true,
24
+ },
25
+ values: {
26
+ type: Object as PropType<VueMessageValues>,
27
+ default: undefined,
28
+ },
29
+ options: {
30
+ type: Object as PropType<TranslateOptions>,
31
+ default: undefined,
32
+ },
33
+ tag: {
34
+ type: [String, Object] as PropType<string | object | false>,
35
+ default: undefined,
36
+ },
37
+ },
38
+ setup(props, { attrs, slots }) {
39
+ const context = useMessagevisorContextValue();
40
+ const richText = createRichTextTools(context);
41
+
42
+ return () => {
43
+ const message = context.instance.getRawTranslation(props.messageKey, props.options);
44
+ const values = {
45
+ ...(resolveRecord(props.values) || {}),
46
+ ...getSlotValues(slots),
47
+ };
48
+ const translation = context.instance.translate<VueMessageChunk>(
49
+ props.messageKey,
50
+ richText.mergeValues(values, message) as MessageValues<VueMessageChunk>,
51
+ props.options,
52
+ );
53
+
54
+ const result = richText.wrapResult(
55
+ richText.runModules(translation, {
56
+ source: "translation",
57
+ messageKey: props.messageKey,
58
+ }),
59
+ );
60
+
61
+ return props.tag ? h(props.tag as string, attrs, result as any) : result;
62
+ };
63
+ },
64
+ });
65
+
66
+ export const FormatMessage = defineComponent({
67
+ name: "FormatMessage",
68
+ inheritAttrs: false,
69
+ props: {
70
+ message: {
71
+ type: String,
72
+ required: true,
73
+ },
74
+ values: {
75
+ type: Object as PropType<VueMessageValues>,
76
+ default: undefined,
77
+ },
78
+ options: {
79
+ type: Object as PropType<EvaluationOptions>,
80
+ default: undefined,
81
+ },
82
+ tag: {
83
+ type: [String, Object] as PropType<string | object | false>,
84
+ default: undefined,
85
+ },
86
+ },
87
+ setup(props, { attrs, slots }) {
88
+ const context = useMessagevisorContextValue();
89
+ const richText = createRichTextTools(context);
90
+
91
+ return () => {
92
+ const values = {
93
+ ...(resolveRecord(props.values) || {}),
94
+ ...getSlotValues(slots),
95
+ };
96
+ const translation = context.instance.formatMessage<VueMessageChunk>(
97
+ props.message,
98
+ richText.mergeValues(values, props.message) as MessageValues<VueMessageChunk>,
99
+ props.options,
100
+ );
101
+
102
+ const result = richText.wrapResult(
103
+ richText.runModules(translation, {
104
+ source: "formatMessage",
105
+ }),
106
+ );
107
+
108
+ return props.tag ? h(props.tag as string, attrs, result as any) : result;
109
+ };
110
+ },
111
+ });
@@ -0,0 +1,43 @@
1
+ import type { Messagevisor } from "@messagevisor/sdk";
2
+ import type { App, Plugin } from "vue";
3
+
4
+ import {
5
+ MessagevisorInjectionKey,
6
+ type MessagevisorProviderModule,
7
+ type VueRichTextElementHandler,
8
+ } from "./MessagevisorContext";
9
+ import { createMessagevisorApi, type MessagevisorApi } from "./api";
10
+
11
+ export interface MessagevisorProviderOptions {
12
+ instance: Messagevisor;
13
+ defaultRichTextElements?: Record<string, VueRichTextElementHandler>;
14
+ wrapRichTextChunksInFragment?: boolean;
15
+ modules?: MessagevisorProviderModule[];
16
+ }
17
+
18
+ export function createMessagevisorProvider(options: MessagevisorProviderOptions): Plugin {
19
+ return {
20
+ install(app: App) {
21
+ const context = {
22
+ instance: options.instance,
23
+ defaultRichTextElements: options.defaultRichTextElements || {},
24
+ wrapRichTextChunksInFragment: options.wrapRichTextChunksInFragment ?? true,
25
+ modules: options.modules || [],
26
+ };
27
+ const api = createMessagevisorApi(context);
28
+
29
+ app.provide(MessagevisorInjectionKey, context);
30
+ app.config.globalProperties.$messagevisor = api;
31
+ app.config.globalProperties.$m = api;
32
+ app.config.globalProperties.$t = api.t;
33
+ },
34
+ };
35
+ }
36
+
37
+ declare module "vue" {
38
+ interface ComponentCustomProperties {
39
+ $messagevisor: MessagevisorApi;
40
+ $m: MessagevisorApi;
41
+ $t: MessagevisorApi["t"];
42
+ }
43
+ }
package/src/index.ts ADDED
@@ -0,0 +1,41 @@
1
+ export {
2
+ MessagevisorInjectionKey,
3
+ type MessagevisorProviderModule,
4
+ type MessagevisorVueContextValue,
5
+ type VueMessageChunk,
6
+ type VueRichTextElementHandler,
7
+ } from "./MessagevisorContext";
8
+ export { MessagevisorProvider } from "./MessagevisorProvider";
9
+ export { MessageTranslation, FormatMessage } from "./components";
10
+ export {
11
+ createMessagevisorProvider,
12
+ type MessagevisorProviderOptions,
13
+ } from "./createMessagevisorProvider";
14
+ export { useMessagevisor } from "./useMessagevisor";
15
+ export { useMessagevisorSnapshot } from "./useMessagevisorSnapshot";
16
+ export { useSdk, useMessagevisorContextValue } from "./useSdk";
17
+ export {
18
+ useCurrency,
19
+ useDirection,
20
+ useFormatDate,
21
+ useFormatDateTimeRange,
22
+ useFormatDateToParts,
23
+ useFormatDisplayName,
24
+ useFormatList,
25
+ useFormatListToParts,
26
+ useFormatMessage,
27
+ useFormatNumber,
28
+ useFormatNumberToParts,
29
+ useFormatPlural,
30
+ useFormatRelativeTime,
31
+ useFormatTime,
32
+ useFormatTimeToParts,
33
+ useLocale,
34
+ useLocaleInfo,
35
+ useMessagevisorContext,
36
+ useTimeZone,
37
+ useTranslation,
38
+ type LocaleInfo,
39
+ } from "./useReactiveMessagevisor";
40
+ export { type MessagevisorApi } from "./api";
41
+ export { type VueMessageValues, type VueRichMessageValues } from "./richText";
@@ -0,0 +1,130 @@
1
+ import type {
2
+ MessagePrimitiveValue,
3
+ MessageValue,
4
+ MessagevisorTranslationSource,
5
+ } from "@messagevisor/sdk";
6
+ import type { MessageKey } from "@messagevisor/types";
7
+ import { Fragment, h, isRef } from "vue";
8
+
9
+ import type {
10
+ MessagevisorVueContextValue,
11
+ VueMessageChunk,
12
+ VueRichTextElementHandler,
13
+ } from "./MessagevisorContext";
14
+
15
+ export type VueMessageValues = Record<string, MessageValue<VueMessageChunk>>;
16
+ export type VueRichMessageValues = Record<
17
+ string,
18
+ MessagePrimitiveValue | VueRichTextElementHandler
19
+ >;
20
+
21
+ const RICH_TAG_NAME_PATTERN = /<([A-Za-z][A-Za-z0-9_-]*)\b[^>]*>/g;
22
+
23
+ function getRichTagNames(message: string) {
24
+ const tags = new Set<string>();
25
+ let match: RegExpExecArray | null;
26
+
27
+ while ((match = RICH_TAG_NAME_PATTERN.exec(message))) {
28
+ tags.add(match[1]);
29
+ }
30
+
31
+ return tags;
32
+ }
33
+
34
+ function maybeResolveRecordEntry(value: unknown) {
35
+ if (isRef(value)) {
36
+ return value.value;
37
+ }
38
+
39
+ if (typeof value === "function" && value.length === 0) {
40
+ return (value as () => unknown)();
41
+ }
42
+
43
+ return value;
44
+ }
45
+
46
+ export function resolveRecord<TRecord extends Record<string, unknown>>(
47
+ value?: TRecord | (() => TRecord),
48
+ ) {
49
+ const record = typeof value === "function" ? value() : value;
50
+
51
+ if (!record) {
52
+ return record;
53
+ }
54
+
55
+ return Object.fromEntries(
56
+ Object.entries(record).map(([key, entry]) => [key, maybeResolveRecordEntry(entry)]),
57
+ ) as TRecord;
58
+ }
59
+
60
+ export function createRichTextTools(context: MessagevisorVueContextValue) {
61
+ function mergeValues(values?: VueMessageValues, message?: string) {
62
+ const defaults = context.defaultRichTextElements;
63
+ const defaultKeys = Object.keys(defaults);
64
+
65
+ if (!message || defaultKeys.length === 0) {
66
+ return values;
67
+ }
68
+
69
+ const tagNames = getRichTagNames(message);
70
+ const matchingDefaults = Object.fromEntries(
71
+ defaultKeys.filter((key) => tagNames.has(key)).map((key) => [key, defaults[key]]),
72
+ ) as VueMessageValues;
73
+
74
+ if (Object.keys(matchingDefaults).length === 0) {
75
+ return values;
76
+ }
77
+
78
+ return {
79
+ ...matchingDefaults,
80
+ ...(values || {}),
81
+ };
82
+ }
83
+
84
+ function wrapResult<T>(result: T) {
85
+ if (!context.wrapRichTextChunksInFragment || !Array.isArray(result)) {
86
+ return result;
87
+ }
88
+
89
+ return h(
90
+ Fragment,
91
+ null,
92
+ result.map((chunk, index) => h(Fragment, { key: index }, [chunk])),
93
+ );
94
+ }
95
+
96
+ function runModules<TTranslation>(
97
+ translation: TTranslation,
98
+ payload: {
99
+ source: MessagevisorTranslationSource;
100
+ messageKey?: MessageKey;
101
+ },
102
+ ) {
103
+ let currentTranslation = translation as VueMessageChunk;
104
+ const locale = context.instance.getLocale();
105
+
106
+ if (!locale) {
107
+ return translation;
108
+ }
109
+
110
+ for (const module of context.modules) {
111
+ const nextTranslation = module.transform?.({
112
+ translation: currentTranslation,
113
+ locale,
114
+ ...payload,
115
+ });
116
+
117
+ if (typeof nextTranslation !== "undefined") {
118
+ currentTranslation = nextTranslation;
119
+ }
120
+ }
121
+
122
+ return currentTranslation;
123
+ }
124
+
125
+ return {
126
+ mergeValues,
127
+ runModules,
128
+ wrapResult,
129
+ };
130
+ }
@@ -0,0 +1,82 @@
1
+ import { createMessagevisor } from "@messagevisor/sdk";
2
+ import { createICUModule } from "@messagevisor/module-icu";
3
+ import type { DatafileContent } from "@messagevisor/types";
4
+
5
+ export const enDatafile: DatafileContent = {
6
+ schemaVersion: "1",
7
+ messagevisorVersion: "0.0.1",
8
+ revision: "1-en",
9
+ target: "web",
10
+ locale: "en-US",
11
+ direction: "ltr",
12
+ formats: {
13
+ number: {
14
+ money: { style: "currency", currency: "USD", currencyDisplay: "symbol" },
15
+ },
16
+ date: {
17
+ short: { year: "2-digit", month: "numeric", day: "numeric", timeZone: "UTC" },
18
+ },
19
+ time: {
20
+ short: { hour: "numeric", minute: "2-digit", timeZone: "UTC" },
21
+ },
22
+ dateTimeRange: {
23
+ short: { month: "short", day: "numeric", timeZone: "UTC" },
24
+ },
25
+ relative: {
26
+ short: { numeric: "auto", style: "short" },
27
+ },
28
+ },
29
+ segments: {
30
+ pro: {
31
+ conditions: [{ attribute: "plan", operator: "equals", value: "pro" }],
32
+ },
33
+ },
34
+ messages: {
35
+ greeting: {
36
+ overrides: [
37
+ {
38
+ key: "pro",
39
+ segments: "pro",
40
+ translation: "Welcome back, pro {name}",
41
+ },
42
+ ],
43
+ },
44
+ total: {},
45
+ richTerms: {},
46
+ raw: {},
47
+ },
48
+ translations: {
49
+ greeting: "Hello {name}",
50
+ total: "Total: {amount, number, money}",
51
+ richTerms: "Read our <link>terms</link> for <strong>{product}</strong>.",
52
+ raw: "Raw {name}",
53
+ },
54
+ };
55
+
56
+ export const nlDatafile: DatafileContent = {
57
+ ...enDatafile,
58
+ revision: "1-nl",
59
+ locale: "nl-NL",
60
+ translations: {
61
+ ...enDatafile.translations,
62
+ greeting: "Hallo {name}",
63
+ },
64
+ };
65
+
66
+ export function createTestInstance() {
67
+ return createMessagevisor({
68
+ datafile: enDatafile,
69
+ timeZone: "UTC",
70
+ logLevel: "fatal",
71
+ modules: [createICUModule()],
72
+ });
73
+ }
74
+
75
+ export function createRichTestInstance() {
76
+ return createMessagevisor({
77
+ datafile: enDatafile,
78
+ timeZone: "UTC",
79
+ logLevel: "fatal",
80
+ modules: [createICUModule({ ignoreTags: false })],
81
+ });
82
+ }