@murumets-ee/i18n 0.3.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.d.mts +79 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -0
- package/dist/messages/en.json +22 -0
- package/dist/messages/et.json +22 -0
- package/dist/messages/ru.json +22 -0
- package/package.json +25 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
//#region src/loader.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Factory for creating message loader functions.
|
|
4
|
+
*
|
|
5
|
+
* Eliminates the per-package boilerplate of switching on locale,
|
|
6
|
+
* catching import errors, and falling back to a default locale.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* // ticketing/src/i18n.ts
|
|
11
|
+
* import { createMessageLoader } from '@murumets-ee/i18n'
|
|
12
|
+
*
|
|
13
|
+
* export const getTicketingMessages = createMessageLoader(
|
|
14
|
+
* (locale) => import(`./messages/${locale}.json`).then(m => m.default),
|
|
15
|
+
* )
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
declare function createMessageLoader(importFn: (locale: string) => Promise<Record<string, unknown>>, options?: {
|
|
19
|
+
fallbackLocale?: string;
|
|
20
|
+
}): (locale: string) => Promise<Record<string, unknown>>;
|
|
21
|
+
//#endregion
|
|
22
|
+
//#region src/merge.d.ts
|
|
23
|
+
/**
|
|
24
|
+
* Deep-merge multiple message catalogs.
|
|
25
|
+
*
|
|
26
|
+
* Unlike `{ ...a, ...b }`, this recursively merges nested objects so
|
|
27
|
+
* that namespaced catalogs from different packages don't clobber each
|
|
28
|
+
* other's keys.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* const merged = mergeMessages(
|
|
33
|
+
* { Common: { save: 'Save' } },
|
|
34
|
+
* { Ticketing: { status: { open: 'Open' } } },
|
|
35
|
+
* { Common: { cancel: 'Cancel' } }, // merges into Common, doesn't overwrite
|
|
36
|
+
* )
|
|
37
|
+
* // → { Common: { save: 'Save', cancel: 'Cancel' }, Ticketing: { status: { open: 'Open' } } }
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
declare function mergeMessages(...catalogs: Record<string, unknown>[]): Record<string, unknown>;
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/options.d.ts
|
|
43
|
+
/**
|
|
44
|
+
* Build the i18n key for a select field option.
|
|
45
|
+
*
|
|
46
|
+
* Convention: `{Namespace}.{field}.{value}`
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```ts
|
|
50
|
+
* optionKey('Ticketing', 'status', '0_open')
|
|
51
|
+
* // → 'Ticketing.status.0_open'
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
declare function optionKey(namespace: string, field: string, value: string): string;
|
|
55
|
+
/**
|
|
56
|
+
* Strip the numeric sort prefix from a select value.
|
|
57
|
+
*
|
|
58
|
+
* Values like `'0_open'`, `'2_high'` become `'open'`, `'high'`.
|
|
59
|
+
* Values without a prefix are returned as-is.
|
|
60
|
+
*
|
|
61
|
+
* Useful as a display fallback when translations aren't available.
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```ts
|
|
65
|
+
* stripPrefix('0_open') // → 'open'
|
|
66
|
+
* stripPrefix('hello') // → 'hello'
|
|
67
|
+
* stripPrefix('3_urgent') // → 'urgent'
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
declare function stripPrefix(value: string): string;
|
|
71
|
+
//#endregion
|
|
72
|
+
//#region src/types.d.ts
|
|
73
|
+
/** Nested message catalog — string leaves, object branches. */
|
|
74
|
+
type MessageCatalog = {
|
|
75
|
+
[key: string]: string | MessageCatalog;
|
|
76
|
+
};
|
|
77
|
+
//#endregion
|
|
78
|
+
export { type MessageCatalog, createMessageLoader, mergeMessages, optionKey, stripPrefix };
|
|
79
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/loader.ts","../src/merge.ts","../src/options.ts","../src/types.ts"],"mappings":";;AAgBA;;;;;;;;;;;;;;;iBAAgB,mBAAA,CACd,QAAA,GAAW,MAAA,aAAmB,OAAA,CAAQ,MAAA,oBACtC,OAAA;EAAY,cAAA;AAAA,KACV,MAAA,aAAmB,OAAA,CAAQ,MAAA;;;;AAH/B;;;;;;;;;;;;;;;;iBCCgB,aAAA,CAAA,GACX,QAAA,EAAU,MAAA,sBACZ,MAAA;;;;ADHH;;;;;;;;;;iBELgB,SAAA,CAAU,SAAA,UAAmB,KAAA,UAAe,KAAA;;;;;;;;;;;;;ADM5D;;;iBCagB,WAAA,CAAY,KAAA;;;;KC7BhB,cAAA;EAAA,CACT,GAAA,oBAAuB,cAAA;AAAA"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
function e(e,t){let n=t?.fallbackLocale??`en`;return async t=>{try{return await e(t)}catch{if(t!==n)try{return await e(n)}catch{return{}}return{}}}}function t(...e){let n={};for(let r of e)for(let[e,i]of Object.entries(r)){let r=n[e];typeof r==`object`&&r&&!Array.isArray(r)&&typeof i==`object`&&i&&!Array.isArray(i)?n[e]=t(r,i):n[e]=i}return n}function n(e,t,n){return`${e}.${t}.${n}`}function r(e){return e.replace(/^\d+_/,``)}export{e as createMessageLoader,t as mergeMessages,n as optionKey,r as stripPrefix};
|
|
2
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/loader.ts","../src/merge.ts","../src/options.ts"],"sourcesContent":["/**\n * Factory for creating message loader functions.\n *\n * Eliminates the per-package boilerplate of switching on locale,\n * catching import errors, and falling back to a default locale.\n *\n * @example\n * ```ts\n * // ticketing/src/i18n.ts\n * import { createMessageLoader } from '@murumets-ee/i18n'\n *\n * export const getTicketingMessages = createMessageLoader(\n * (locale) => import(`./messages/${locale}.json`).then(m => m.default),\n * )\n * ```\n */\nexport function createMessageLoader(\n importFn: (locale: string) => Promise<Record<string, unknown>>,\n options?: { fallbackLocale?: string },\n): (locale: string) => Promise<Record<string, unknown>> {\n const fallback = options?.fallbackLocale ?? 'en'\n\n return async (locale: string): Promise<Record<string, unknown>> => {\n try {\n return await importFn(locale)\n } catch {\n // Locale not found — fall back\n if (locale !== fallback) {\n try {\n return await importFn(fallback)\n } catch {\n return {}\n }\n }\n return {}\n }\n }\n}\n","/**\n * Deep-merge multiple message catalogs.\n *\n * Unlike `{ ...a, ...b }`, this recursively merges nested objects so\n * that namespaced catalogs from different packages don't clobber each\n * other's keys.\n *\n * @example\n * ```ts\n * const merged = mergeMessages(\n * { Common: { save: 'Save' } },\n * { Ticketing: { status: { open: 'Open' } } },\n * { Common: { cancel: 'Cancel' } }, // merges into Common, doesn't overwrite\n * )\n * // → { Common: { save: 'Save', cancel: 'Cancel' }, Ticketing: { status: { open: 'Open' } } }\n * ```\n */\nexport function mergeMessages(\n ...catalogs: Record<string, unknown>[]\n): Record<string, unknown> {\n const result: Record<string, unknown> = {}\n\n for (const catalog of catalogs) {\n for (const [key, value] of Object.entries(catalog)) {\n const existing = result[key]\n if (\n existing != null &&\n typeof existing === 'object' &&\n !Array.isArray(existing) &&\n value != null &&\n typeof value === 'object' &&\n !Array.isArray(value)\n ) {\n // Both are objects — recurse\n result[key] = mergeMessages(\n existing as Record<string, unknown>,\n value as Record<string, unknown>,\n )\n } else {\n // Leaf or new key — last catalog wins\n result[key] = value\n }\n }\n }\n\n return result\n}\n","/**\n * Build the i18n key for a select field option.\n *\n * Convention: `{Namespace}.{field}.{value}`\n *\n * @example\n * ```ts\n * optionKey('Ticketing', 'status', '0_open')\n * // → 'Ticketing.status.0_open'\n * ```\n */\nexport function optionKey(namespace: string, field: string, value: string): string {\n return `${namespace}.${field}.${value}`\n}\n\n/**\n * Strip the numeric sort prefix from a select value.\n *\n * Values like `'0_open'`, `'2_high'` become `'open'`, `'high'`.\n * Values without a prefix are returned as-is.\n *\n * Useful as a display fallback when translations aren't available.\n *\n * @example\n * ```ts\n * stripPrefix('0_open') // → 'open'\n * stripPrefix('hello') // → 'hello'\n * stripPrefix('3_urgent') // → 'urgent'\n * ```\n */\nexport function stripPrefix(value: string): string {\n return value.replace(/^\\d+_/, '')\n}\n"],"mappings":"AAgBA,SAAgB,EACd,EACA,EACsD,CACtD,IAAM,EAAW,GAAS,gBAAkB,KAE5C,OAAO,KAAO,IAAqD,CACjE,GAAI,CACF,OAAO,MAAM,EAAS,EAAO,MACvB,CAEN,GAAI,IAAW,EACb,GAAI,CACF,OAAO,MAAM,EAAS,EAAS,MACzB,CACN,MAAO,EAAE,CAGb,MAAO,EAAE,GCjBf,SAAgB,EACd,GAAG,EACsB,CACzB,IAAM,EAAkC,EAAE,CAE1C,IAAK,IAAM,KAAW,EACpB,IAAK,GAAM,CAAC,EAAK,KAAU,OAAO,QAAQ,EAAQ,CAAE,CAClD,IAAM,EAAW,EAAO,GAGtB,OAAO,GAAa,UADpB,GAEA,CAAC,MAAM,QAAQ,EAAS,EAExB,OAAO,GAAU,UADjB,GAEA,CAAC,MAAM,QAAQ,EAAM,CAGrB,EAAO,GAAO,EACZ,EACA,EACD,CAGD,EAAO,GAAO,EAKpB,OAAO,EClCT,SAAgB,EAAU,EAAmB,EAAe,EAAuB,CACjF,MAAO,GAAG,EAAU,GAAG,EAAM,GAAG,IAkBlC,SAAgB,EAAY,EAAuB,CACjD,OAAO,EAAM,QAAQ,QAAS,GAAG"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Common": {
|
|
3
|
+
"save": "Save",
|
|
4
|
+
"cancel": "Cancel",
|
|
5
|
+
"delete": "Delete",
|
|
6
|
+
"confirm": "Confirm",
|
|
7
|
+
"close": "Close",
|
|
8
|
+
"create": "Create",
|
|
9
|
+
"edit": "Edit",
|
|
10
|
+
"search": "Search...",
|
|
11
|
+
"loading": "Loading...",
|
|
12
|
+
"noResults": "No results found",
|
|
13
|
+
"required": "Required",
|
|
14
|
+
"selected": "{count} selected",
|
|
15
|
+
"items": "{count, plural, one {# item} other {# items}}",
|
|
16
|
+
"yes": "Yes",
|
|
17
|
+
"no": "No",
|
|
18
|
+
"back": "Back",
|
|
19
|
+
"next": "Next",
|
|
20
|
+
"previous": "Previous"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Common": {
|
|
3
|
+
"save": "Salvesta",
|
|
4
|
+
"cancel": "Tühista",
|
|
5
|
+
"delete": "Kustuta",
|
|
6
|
+
"confirm": "Kinnita",
|
|
7
|
+
"close": "Sulge",
|
|
8
|
+
"create": "Loo",
|
|
9
|
+
"edit": "Muuda",
|
|
10
|
+
"search": "Otsi...",
|
|
11
|
+
"loading": "Laadimine...",
|
|
12
|
+
"noResults": "Tulemusi ei leitud",
|
|
13
|
+
"required": "Kohustuslik",
|
|
14
|
+
"selected": "{count} valitud",
|
|
15
|
+
"items": "{count, plural, one {# kirje} other {# kirjet}}",
|
|
16
|
+
"yes": "Jah",
|
|
17
|
+
"no": "Ei",
|
|
18
|
+
"back": "Tagasi",
|
|
19
|
+
"next": "Edasi",
|
|
20
|
+
"previous": "Eelmine"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Common": {
|
|
3
|
+
"save": "Сохранить",
|
|
4
|
+
"cancel": "Отмена",
|
|
5
|
+
"delete": "Удалить",
|
|
6
|
+
"confirm": "Подтвердить",
|
|
7
|
+
"close": "Закрыть",
|
|
8
|
+
"create": "Создать",
|
|
9
|
+
"edit": "Редактировать",
|
|
10
|
+
"search": "Поиск...",
|
|
11
|
+
"loading": "Загрузка...",
|
|
12
|
+
"noResults": "Ничего не найдено",
|
|
13
|
+
"required": "Обязательно",
|
|
14
|
+
"selected": "{count} выбрано",
|
|
15
|
+
"items": "{count, plural, one {# элемент} few {# элемента} other {# элементов}}",
|
|
16
|
+
"yes": "Да",
|
|
17
|
+
"no": "Нет",
|
|
18
|
+
"back": "Назад",
|
|
19
|
+
"next": "Далее",
|
|
20
|
+
"previous": "Предыдущий"
|
|
21
|
+
}
|
|
22
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@murumets-ee/i18n",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"license": "Elastic-2.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.mts",
|
|
9
|
+
"import": "./dist/index.mjs"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsdown && cp -r src/messages dist/messages",
|
|
17
|
+
"dev": "tsdown --watch",
|
|
18
|
+
"test": "vitest"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"tsdown": "^0.21.7",
|
|
22
|
+
"typescript": "^5.7.3",
|
|
23
|
+
"vitest": "^2.1.8"
|
|
24
|
+
}
|
|
25
|
+
}
|