@case-framework/ui-localization 0.2.1 → 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 +19 -15
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +19 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -9
package/dist/index.d.mts
CHANGED
|
@@ -1,25 +1,29 @@
|
|
|
1
1
|
//#region src/index.d.ts
|
|
2
|
-
type LocalizationPrimitive = string | number | boolean | null | undefined;
|
|
3
|
-
type LocalizationValues = Record<string, LocalizationPrimitive>;
|
|
4
|
-
interface LocalizationMessages {
|
|
2
|
+
export type LocalizationPrimitive = string | number | boolean | null | undefined;
|
|
3
|
+
export type LocalizationValues = Record<string, LocalizationPrimitive>;
|
|
4
|
+
export interface LocalizationMessages {
|
|
5
5
|
[key: string]: string | LocalizationMessages;
|
|
6
6
|
}
|
|
7
|
-
type LocalizationCatalog = Record<string, LocalizationMessages>;
|
|
8
|
-
type LocalizationTranslate = (key: string, values?: LocalizationValues) => string;
|
|
9
|
-
declare const normalizeLocalizationLocale: (locale: string) => string;
|
|
10
|
-
declare const mergeLocalizationCatalogs: (defaults: LocalizationCatalog, ...sources: Array<LocalizationCatalog | undefined>) => LocalizationCatalog;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
7
|
+
export type LocalizationCatalog = Record<string, LocalizationMessages>;
|
|
8
|
+
export type LocalizationTranslate = (key: string, values?: LocalizationValues) => string;
|
|
9
|
+
export declare const normalizeLocalizationLocale: (locale: string) => string;
|
|
10
|
+
export declare const mergeLocalizationCatalogs: (defaults: LocalizationCatalog, ...sources: Array<LocalizationCatalog | undefined>) => LocalizationCatalog;
|
|
11
|
+
/** Resolve wording and its actual language using the same fallback chain as translation. */
|
|
12
|
+
export declare const resolveLocalizationMessage: ({ key, locale, fallbackLocale, localizations }: {
|
|
13
|
+
key: string;
|
|
14
|
+
locale: string;
|
|
15
|
+
fallbackLocale: string;
|
|
16
|
+
localizations: LocalizationCatalog;
|
|
17
|
+
}) => {
|
|
18
|
+
text: string;
|
|
19
|
+
locale: string;
|
|
20
|
+
} | undefined;
|
|
21
|
+
export declare const createLocalizationTranslator: ({ fallbackLocale, locale, localizations, onMissing }: {
|
|
17
22
|
fallbackLocale: string;
|
|
18
23
|
locale: string;
|
|
19
24
|
localizations: LocalizationCatalog;
|
|
20
25
|
onMissing?: (key: string, locale: string) => void;
|
|
21
26
|
}) => LocalizationTranslate;
|
|
22
|
-
declare const createNamespacedLocalizationTranslator: (translate: LocalizationTranslate, namespace?: string) => LocalizationTranslate;
|
|
27
|
+
export declare const createNamespacedLocalizationTranslator: (translate: LocalizationTranslate, namespace?: string) => LocalizationTranslate;
|
|
23
28
|
//#endregion
|
|
24
|
-
export { LocalizationCatalog, LocalizationMessages, LocalizationPrimitive, LocalizationTranslate, LocalizationValues, createLocalizationTranslator, createNamespacedLocalizationTranslator, mergeLocalizationCatalogs, normalizeLocalizationLocale };
|
|
25
29
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";YAAY;YAEA,qBAAqB,eAAe;iBAE/B;GACd,uBAAuB;;YAGd,sBAAsB,eAAe;YAErC,yBAAyB,aAAa,SAAS;qBA0C9C,8BAA+B;qBAG/B,4BACX,UAAU,wBACP,SAAS,MAAM,qCACjB;;qBA4DU,+BAA8B,KAAA,QAAA,gBAAA;EAMzC;EACA;EACA;EACA,eAAe;;EACX;EAAc;;qBAQP,iCAAgC,gBAAA,QAAA,eAAA;EAM3C;EACA;EACA,eAAe;EACf,aAAa,aAAa;MACxB;qBAUS,yCACX,WAAW,uBACX,uBACC"}
|
package/dist/index.mjs
CHANGED
|
@@ -34,7 +34,7 @@ const mergeLocalizationCatalogs = (defaults, ...sources) => {
|
|
|
34
34
|
const resolveLocaleCandidates = (locale, fallbackLocale) => {
|
|
35
35
|
const normalizedFallbackLocale = normalizeLocalizationLocale(fallbackLocale) || normalizeLocalizationLocale("en");
|
|
36
36
|
const normalizedLocale = normalizeLocalizationLocale(locale) || normalizedFallbackLocale;
|
|
37
|
-
return Array.from(new Set([
|
|
37
|
+
return Array.from(/* @__PURE__ */ new Set([
|
|
38
38
|
normalizedLocale,
|
|
39
39
|
normalizedLocale.split("-")[0],
|
|
40
40
|
normalizedFallbackLocale,
|
|
@@ -57,12 +57,25 @@ const interpolateMessage = (message, values) => {
|
|
|
57
57
|
return value === void 0 || value === null ? `{${token}}` : String(value);
|
|
58
58
|
});
|
|
59
59
|
};
|
|
60
|
+
/** Resolve wording and its actual language using the same fallback chain as translation. */
|
|
61
|
+
const resolveLocalizationMessage = ({ key, locale, fallbackLocale, localizations }) => {
|
|
62
|
+
for (const candidate of resolveLocaleCandidates(locale, fallbackLocale)) {
|
|
63
|
+
const text = getNestedMessage(localizations[candidate], key);
|
|
64
|
+
if (text !== void 0) return {
|
|
65
|
+
text,
|
|
66
|
+
locale: candidate
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
};
|
|
60
70
|
const createLocalizationTranslator = ({ fallbackLocale, locale, localizations, onMissing }) => {
|
|
61
71
|
return (key, values) => {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
72
|
+
const resolved = resolveLocalizationMessage({
|
|
73
|
+
key,
|
|
74
|
+
locale,
|
|
75
|
+
fallbackLocale,
|
|
76
|
+
localizations
|
|
77
|
+
});
|
|
78
|
+
if (resolved) return interpolateMessage(resolved.text, values);
|
|
66
79
|
onMissing?.(key, locale);
|
|
67
80
|
return key;
|
|
68
81
|
};
|
|
@@ -73,6 +86,6 @@ const createNamespacedLocalizationTranslator = (translate, namespace) => {
|
|
|
73
86
|
return (key, values) => translate(`${normalizedNamespace}.${key}`, values);
|
|
74
87
|
};
|
|
75
88
|
//#endregion
|
|
76
|
-
export { createLocalizationTranslator, createNamespacedLocalizationTranslator, mergeLocalizationCatalogs, normalizeLocalizationLocale };
|
|
89
|
+
export { createLocalizationTranslator, createNamespacedLocalizationTranslator, mergeLocalizationCatalogs, normalizeLocalizationLocale, resolveLocalizationMessage };
|
|
77
90
|
|
|
78
91
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["export type LocalizationPrimitive = string | number | boolean | null | undefined;\n\nexport type LocalizationValues = Record<string, LocalizationPrimitive>;\n\nexport interface LocalizationMessages {\n [key: string]: string | LocalizationMessages;\n}\n\nexport type LocalizationCatalog = Record<string, LocalizationMessages>;\n\nexport type LocalizationTranslate = (key: string, values?: LocalizationValues) => string;\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> =>\n value !== null && typeof value === \"object\" && !Array.isArray(value);\n\nconst cloneMessages = (messages: LocalizationMessages): LocalizationMessages => {\n const clone: LocalizationMessages = {};\n\n for (const [key, value] of Object.entries(messages)) {\n clone[key] = isPlainObject(value)\n ? cloneMessages(value as LocalizationMessages)\n : (value as string);\n }\n\n return clone;\n};\n\nconst mergeMessages = (\n base: LocalizationMessages,\n override: LocalizationMessages,\n): LocalizationMessages => {\n const result = cloneMessages(base);\n\n for (const [key, overrideValue] of Object.entries(override)) {\n const baseValue = result[key];\n\n if (isPlainObject(baseValue) && isPlainObject(overrideValue)) {\n result[key] = mergeMessages(\n baseValue as LocalizationMessages,\n overrideValue as LocalizationMessages,\n );\n continue;\n }\n\n result[key] = isPlainObject(overrideValue)\n ? cloneMessages(overrideValue as LocalizationMessages)\n : (overrideValue as string);\n }\n\n return result;\n};\n\nexport const normalizeLocalizationLocale = (locale: string): string =>\n locale.trim().replaceAll(\"_\", \"-\").toLowerCase();\n\nexport const mergeLocalizationCatalogs = (\n defaults: LocalizationCatalog,\n ...sources: Array<LocalizationCatalog | undefined>\n): LocalizationCatalog => {\n const merged: LocalizationCatalog = {};\n\n for (const source of [defaults, ...sources]) {\n if (!source) continue;\n\n for (const [locale, messages] of Object.entries(source)) {\n const normalizedLocale = normalizeLocalizationLocale(locale);\n if (!normalizedLocale) continue;\n\n const existingMessages = merged[normalizedLocale];\n merged[normalizedLocale] = existingMessages\n ? mergeMessages(existingMessages, messages)\n : cloneMessages(messages);\n }\n }\n\n return merged;\n};\n\nconst resolveLocaleCandidates = (locale: string, fallbackLocale: string): string[] => {\n const normalizedFallbackLocale =\n normalizeLocalizationLocale(fallbackLocale) || normalizeLocalizationLocale(\"en\");\n const normalizedLocale = normalizeLocalizationLocale(locale) || normalizedFallbackLocale;\n\n return Array.from(\n new Set([\n normalizedLocale,\n normalizedLocale.split(\"-\")[0],\n normalizedFallbackLocale,\n normalizedFallbackLocale.split(\"-\")[0],\n ]),\n );\n};\n\nconst getNestedMessage = (\n messages: LocalizationMessages | undefined,\n path: string,\n): string | undefined => {\n if (!messages) return undefined;\n\n let current: string | LocalizationMessages | undefined = messages;\n for (const segment of path.split(\".\")) {\n if (!isPlainObject(current) || !(segment in current)) return undefined;\n current = current[segment] as string | LocalizationMessages;\n }\n\n return typeof current === \"string\" ? current : undefined;\n};\n\nconst interpolateMessage = (message: string, values?: LocalizationValues): string => {\n if (!values) return message;\n\n return message.replace(/\\{([a-zA-Z0-9_]+)\\}/g, (_match, token: string) => {\n const value = values[token];\n return value === undefined || value === null ? `{${token}}` : String(value);\n });\n};\n\nexport const createLocalizationTranslator = ({\n fallbackLocale,\n locale,\n localizations,\n onMissing,\n}: {\n fallbackLocale: string;\n locale: string;\n localizations: LocalizationCatalog;\n onMissing?: (key: string, locale: string) => void;\n}): LocalizationTranslate => {\n return (key, values) => {\n
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["export type LocalizationPrimitive = string | number | boolean | null | undefined;\n\nexport type LocalizationValues = Record<string, LocalizationPrimitive>;\n\nexport interface LocalizationMessages {\n [key: string]: string | LocalizationMessages;\n}\n\nexport type LocalizationCatalog = Record<string, LocalizationMessages>;\n\nexport type LocalizationTranslate = (key: string, values?: LocalizationValues) => string;\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> =>\n value !== null && typeof value === \"object\" && !Array.isArray(value);\n\nconst cloneMessages = (messages: LocalizationMessages): LocalizationMessages => {\n const clone: LocalizationMessages = {};\n\n for (const [key, value] of Object.entries(messages)) {\n clone[key] = isPlainObject(value)\n ? cloneMessages(value as LocalizationMessages)\n : (value as string);\n }\n\n return clone;\n};\n\nconst mergeMessages = (\n base: LocalizationMessages,\n override: LocalizationMessages,\n): LocalizationMessages => {\n const result = cloneMessages(base);\n\n for (const [key, overrideValue] of Object.entries(override)) {\n const baseValue = result[key];\n\n if (isPlainObject(baseValue) && isPlainObject(overrideValue)) {\n result[key] = mergeMessages(\n baseValue as LocalizationMessages,\n overrideValue as LocalizationMessages,\n );\n continue;\n }\n\n result[key] = isPlainObject(overrideValue)\n ? cloneMessages(overrideValue as LocalizationMessages)\n : (overrideValue as string);\n }\n\n return result;\n};\n\nexport const normalizeLocalizationLocale = (locale: string): string =>\n locale.trim().replaceAll(\"_\", \"-\").toLowerCase();\n\nexport const mergeLocalizationCatalogs = (\n defaults: LocalizationCatalog,\n ...sources: Array<LocalizationCatalog | undefined>\n): LocalizationCatalog => {\n const merged: LocalizationCatalog = {};\n\n for (const source of [defaults, ...sources]) {\n if (!source) continue;\n\n for (const [locale, messages] of Object.entries(source)) {\n const normalizedLocale = normalizeLocalizationLocale(locale);\n if (!normalizedLocale) continue;\n\n const existingMessages = merged[normalizedLocale];\n merged[normalizedLocale] = existingMessages\n ? mergeMessages(existingMessages, messages)\n : cloneMessages(messages);\n }\n }\n\n return merged;\n};\n\nconst resolveLocaleCandidates = (locale: string, fallbackLocale: string): string[] => {\n const normalizedFallbackLocale =\n normalizeLocalizationLocale(fallbackLocale) || normalizeLocalizationLocale(\"en\");\n const normalizedLocale = normalizeLocalizationLocale(locale) || normalizedFallbackLocale;\n\n return Array.from(\n new Set([\n normalizedLocale,\n normalizedLocale.split(\"-\")[0],\n normalizedFallbackLocale,\n normalizedFallbackLocale.split(\"-\")[0],\n ]),\n );\n};\n\nconst getNestedMessage = (\n messages: LocalizationMessages | undefined,\n path: string,\n): string | undefined => {\n if (!messages) return undefined;\n\n let current: string | LocalizationMessages | undefined = messages;\n for (const segment of path.split(\".\")) {\n if (!isPlainObject(current) || !(segment in current)) return undefined;\n current = current[segment] as string | LocalizationMessages;\n }\n\n return typeof current === \"string\" ? current : undefined;\n};\n\nconst interpolateMessage = (message: string, values?: LocalizationValues): string => {\n if (!values) return message;\n\n return message.replace(/\\{([a-zA-Z0-9_]+)\\}/g, (_match, token: string) => {\n const value = values[token];\n return value === undefined || value === null ? `{${token}}` : String(value);\n });\n};\n\n/** Resolve wording and its actual language using the same fallback chain as translation. */\nexport const resolveLocalizationMessage = ({\n key,\n locale,\n fallbackLocale,\n localizations,\n}: {\n key: string;\n locale: string;\n fallbackLocale: string;\n localizations: LocalizationCatalog;\n}): { text: string; locale: string } | undefined => {\n for (const candidate of resolveLocaleCandidates(locale, fallbackLocale)) {\n const text = getNestedMessage(localizations[candidate], key);\n if (text !== undefined) return { text, locale: candidate };\n }\n return undefined;\n};\n\nexport const createLocalizationTranslator = ({\n fallbackLocale,\n locale,\n localizations,\n onMissing,\n}: {\n fallbackLocale: string;\n locale: string;\n localizations: LocalizationCatalog;\n onMissing?: (key: string, locale: string) => void;\n}): LocalizationTranslate => {\n return (key, values) => {\n const resolved = resolveLocalizationMessage({ key, locale, fallbackLocale, localizations });\n if (resolved) return interpolateMessage(resolved.text, values);\n\n onMissing?.(key, locale);\n return key;\n };\n};\n\nexport const createNamespacedLocalizationTranslator = (\n translate: LocalizationTranslate,\n namespace?: string,\n): LocalizationTranslate => {\n const normalizedNamespace = namespace?.trim();\n if (!normalizedNamespace) return translate;\n\n return (key, values) => translate(`${normalizedNamespace}.${key}`, values);\n};\n"],"mappings":";AAYA,MAAM,iBAAiB,UACrB,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK;AAErE,MAAM,iBAAiB,aAAyD;CAC9E,MAAM,QAA8B,CAAC;CAErC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,GAChD,MAAM,OAAO,cAAc,KAAK,IAC5B,cAAc,KAA6B,IAC1C;CAGP,OAAO;AACT;AAEA,MAAM,iBACJ,MACA,aACyB;CACzB,MAAM,SAAS,cAAc,IAAI;CAEjC,KAAK,MAAM,CAAC,KAAK,kBAAkB,OAAO,QAAQ,QAAQ,GAAG;EAC3D,MAAM,YAAY,OAAO;EAEzB,IAAI,cAAc,SAAS,KAAK,cAAc,aAAa,GAAG;GAC5D,OAAO,OAAO,cACZ,WACA,aACF;GACA;EACF;EAEA,OAAO,OAAO,cAAc,aAAa,IACrC,cAAc,aAAqC,IAClD;CACP;CAEA,OAAO;AACT;AAEA,MAAa,+BAA+B,WAC1C,OAAO,KAAK,CAAC,CAAC,WAAW,KAAK,GAAG,CAAC,CAAC,YAAY;AAEjD,MAAa,6BACX,UACA,GAAG,YACqB;CACxB,MAAM,SAA8B,CAAC;CAErC,KAAK,MAAM,UAAU,CAAC,UAAU,GAAG,OAAO,GAAG;EAC3C,IAAI,CAAC,QAAQ;EAEb,KAAK,MAAM,CAAC,QAAQ,aAAa,OAAO,QAAQ,MAAM,GAAG;GACvD,MAAM,mBAAmB,4BAA4B,MAAM;GAC3D,IAAI,CAAC,kBAAkB;GAEvB,MAAM,mBAAmB,OAAO;GAChC,OAAO,oBAAoB,mBACvB,cAAc,kBAAkB,QAAQ,IACxC,cAAc,QAAQ;EAC5B;CACF;CAEA,OAAO;AACT;AAEA,MAAM,2BAA2B,QAAgB,mBAAqC;CACpF,MAAM,2BACJ,4BAA4B,cAAc,KAAK,4BAA4B,IAAI;CACjF,MAAM,mBAAmB,4BAA4B,MAAM,KAAK;CAEhE,OAAO,MAAM,qBACX,IAAI,IAAI;EACN;EACA,iBAAiB,MAAM,GAAG,CAAC,CAAC;EAC5B;EACA,yBAAyB,MAAM,GAAG,CAAC,CAAC;CACtC,CAAC,CACH;AACF;AAEA,MAAM,oBACJ,UACA,SACuB;CACvB,IAAI,CAAC,UAAU,OAAO,KAAA;CAEtB,IAAI,UAAqD;CACzD,KAAK,MAAM,WAAW,KAAK,MAAM,GAAG,GAAG;EACrC,IAAI,CAAC,cAAc,OAAO,KAAK,EAAE,WAAW,UAAU,OAAO,KAAA;EAC7D,UAAU,QAAQ;CACpB;CAEA,OAAO,OAAO,YAAY,WAAW,UAAU,KAAA;AACjD;AAEA,MAAM,sBAAsB,SAAiB,WAAwC;CACnF,IAAI,CAAC,QAAQ,OAAO;CAEpB,OAAO,QAAQ,QAAQ,yBAAyB,QAAQ,UAAkB;EACxE,MAAM,QAAQ,OAAO;EACrB,OAAO,UAAU,KAAA,KAAa,UAAU,OAAO,IAAI,MAAM,KAAK,OAAO,KAAK;CAC5E,CAAC;AACH;;AAGA,MAAa,8BAA8B,EACzC,KACA,QACA,gBACA,oBAMkD;CAClD,KAAK,MAAM,aAAa,wBAAwB,QAAQ,cAAc,GAAG;EACvE,MAAM,OAAO,iBAAiB,cAAc,YAAY,GAAG;EAC3D,IAAI,SAAS,KAAA,GAAW,OAAO;GAAE;GAAM,QAAQ;EAAU;CAC3D;AAEF;AAEA,MAAa,gCAAgC,EAC3C,gBACA,QACA,eACA,gBAM2B;CAC3B,QAAQ,KAAK,WAAW;EACtB,MAAM,WAAW,2BAA2B;GAAE;GAAK;GAAQ;GAAgB;EAAc,CAAC;EAC1F,IAAI,UAAU,OAAO,mBAAmB,SAAS,MAAM,MAAM;EAE7D,YAAY,KAAK,MAAM;EACvB,OAAO;CACT;AACF;AAEA,MAAa,0CACX,WACA,cAC0B;CAC1B,MAAM,sBAAsB,WAAW,KAAK;CAC5C,IAAI,CAAC,qBAAqB,OAAO;CAEjC,QAAQ,KAAK,WAAW,UAAU,GAAG,oBAAoB,GAAG,OAAO,MAAM;AAC3E"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@case-framework/ui-localization",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Shared UI localization primitives for CASE Framework packages.",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"files": [
|
|
@@ -16,17 +16,15 @@
|
|
|
16
16
|
"./package.json": "./package.json"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"ts-jest": "29.4.9",
|
|
23
|
-
"tsdown": "0.21.10",
|
|
24
|
-
"typescript": "6.0.3"
|
|
19
|
+
"tsdown": "0.23.0",
|
|
20
|
+
"typescript": "6.0.3",
|
|
21
|
+
"vitest": "^5.0.1"
|
|
25
22
|
},
|
|
26
23
|
"scripts": {
|
|
27
24
|
"build": "tsdown",
|
|
28
25
|
"typecheck": "tsc --noEmit",
|
|
29
|
-
"test": "
|
|
30
|
-
"check": "pnpm typecheck && pnpm build && pnpm test"
|
|
26
|
+
"test": "pnpm typecheck:tests && vitest run",
|
|
27
|
+
"check": "pnpm typecheck && pnpm build && pnpm test",
|
|
28
|
+
"typecheck:tests": "tsc --noEmit -p tsconfig.test.json"
|
|
31
29
|
}
|
|
32
30
|
}
|