@iyulab/components 1.0.10 → 1.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.
@@ -23,6 +23,8 @@ export declare abstract class UFormControlElement<T> extends UElement {
23
23
  description?: string;
24
24
  /** 커스텀 유효성 검증 메시지 */
25
25
  validationMessage?: string;
26
+ /** chrome 문자열(검증 메시지 등)에 사용할 로케일 태그 (예: 'ko-KR'). 미지정 시 전역 기본/문서 언어를 따른다. */
27
+ locale?: string;
26
28
  /** 폼 제출 시 사용되는 이름 */
27
29
  name?: string;
28
30
  /** 폼 제출 시 사용되는 값 */
@@ -62,6 +62,7 @@ __decorate([property({ type: Boolean }), __decorateMetadata("design:type", Boole
62
62
  __decorate([property({ type: String }), __decorateMetadata("design:type", String)], UFormControlElement.prototype, "label", void 0);
63
63
  __decorate([property({ type: String }), __decorateMetadata("design:type", String)], UFormControlElement.prototype, "description", void 0);
64
64
  __decorate([property({ type: String }), __decorateMetadata("design:type", String)], UFormControlElement.prototype, "validationMessage", void 0);
65
+ __decorate([property({ type: String }), __decorateMetadata("design:type", String)], UFormControlElement.prototype, "locale", void 0);
65
66
  __decorate([property({ type: String }), __decorateMetadata("design:type", String)], UFormControlElement.prototype, "name", void 0);
66
67
  __decorate([property({ type: Object }), __decorateMetadata("design:type", Object)], UFormControlElement.prototype, "value", void 0);
67
68
  //#endregion
@@ -1,3 +1,4 @@
1
+ import { formatTemplate, getLocaleStrings, resolveLocale } from "../../core/locale.js";
1
2
  import { __decorateMetadata } from "../../_virtual/_@oxc-project_runtime@0.133.0/helpers/esm/decorateMetadata.js";
2
3
  import { __decorate } from "../../_virtual/_@oxc-project_runtime@0.133.0/helpers/esm/decorate.js";
3
4
  import { UFormControlElement } from "../UFormControlElement.js";
@@ -153,23 +154,24 @@ var URating = class URating extends UFormControlElement {
153
154
  }
154
155
  getValidity() {
155
156
  const v = this.value || 0;
157
+ const s = getLocaleStrings(resolveLocale(this.locale));
156
158
  if (this.required && !v) return {
157
159
  flags: { valueMissing: true },
158
- message: "This field is required"
160
+ message: s.required
159
161
  };
160
162
  if (v && v < this.min) return {
161
163
  flags: { rangeUnderflow: true },
162
- message: `Value must be at least ${this.min}`
164
+ message: formatTemplate(s.minValue, { min: this.min })
163
165
  };
164
166
  if (v && v > this.max) return {
165
167
  flags: { rangeOverflow: true },
166
- message: `Value must be no more than ${this.max}`
168
+ message: formatTemplate(s.maxValue, { max: this.max })
167
169
  };
168
170
  if (v && this.precision < 1) {
169
171
  const remainder = Math.abs(v % this.precision);
170
172
  if (remainder > .001 && Math.abs(remainder - this.precision) > .001) return {
171
173
  flags: { stepMismatch: true },
172
- message: `Value must be a multiple of ${this.precision}`
174
+ message: formatTemplate(s.stepMismatch, { step: this.precision })
173
175
  };
174
176
  }
175
177
  return {
@@ -1,3 +1,4 @@
1
+ import { formatTemplate, getLocaleStrings, resolveLocale } from "../../core/locale.js";
1
2
  import { __decorateMetadata } from "../../_virtual/_@oxc-project_runtime@0.133.0/helpers/esm/decorateMetadata.js";
2
3
  import { __decorate } from "../../_virtual/_@oxc-project_runtime@0.133.0/helpers/esm/decorate.js";
3
4
  import { UFormControlElement } from "../UFormControlElement.js";
@@ -261,17 +262,18 @@ var USelect = class USelect extends UFormControlElement {
261
262
  }));
262
263
  }
263
264
  getValidity(values) {
265
+ const s = getLocaleStrings(resolveLocale(this.locale));
264
266
  if (this.required && !values.length) return {
265
267
  flags: { valueMissing: true },
266
- message: "This field is required"
268
+ message: s.required
267
269
  };
268
270
  if (this.multiple && this.minCount != null && values.length > 0 && values.length < this.minCount) return {
269
271
  flags: { rangeUnderflow: true },
270
- message: `Please select at least ${this.minCount} items`
272
+ message: formatTemplate(s.minCount, { min: this.minCount })
271
273
  };
272
274
  if (this.multiple && this.maxCount != null && values.length > this.maxCount) return {
273
275
  flags: { rangeOverflow: true },
274
- message: `Please select no more than ${this.maxCount} items`
276
+ message: formatTemplate(s.maxCount, { max: this.maxCount })
275
277
  };
276
278
  return {
277
279
  flags: {},
@@ -1,3 +1,4 @@
1
+ import { formatTemplate, getLocaleStrings, resolveLocale } from "../../core/locale.js";
1
2
  import { __decorateMetadata } from "../../_virtual/_@oxc-project_runtime@0.133.0/helpers/esm/decorateMetadata.js";
2
3
  import { __decorate } from "../../_virtual/_@oxc-project_runtime@0.133.0/helpers/esm/decorate.js";
3
4
  import { UFormControlElement } from "../UFormControlElement.js";
@@ -194,17 +195,18 @@ var USlider = class USlider extends UFormControlElement {
194
195
  }
195
196
  getValidity() {
196
197
  const v = this.valueAsNumber;
198
+ const s = getLocaleStrings(resolveLocale(this.locale));
197
199
  if (this.required && !v) return {
198
200
  flags: { valueMissing: true },
199
- message: "This field is required"
201
+ message: s.required
200
202
  };
201
203
  if (v < this.min) return {
202
204
  flags: { rangeUnderflow: true },
203
- message: `Value must be at least ${this.min}`
205
+ message: formatTemplate(s.minValue, { min: this.min })
204
206
  };
205
207
  if (v > this.max) return {
206
208
  flags: { rangeOverflow: true },
207
- message: `Value must be at most ${this.max}`
209
+ message: formatTemplate(s.maxValue, { max: this.max })
208
210
  };
209
211
  return {
210
212
  flags: {},
@@ -0,0 +1,69 @@
1
+ /**
2
+ * @iyulab/components 의 경량 로케일 레지스트리.
3
+ *
4
+ * 범위: 라이브러리가 스스로 생성하는 chrome 문자열(검증 메시지 등)만 대상.
5
+ * 일반 i18n 프레임워크가 아니다. 앱 콘텐츠 번역은 consumer 의 i18n 계층(예:
6
+ * modern-app 의 i18next)이 담당한다.
7
+ *
8
+ * 영어가 내장 기본값이며, 한국어 등 도메인 로케일은 consumer 가 등록한다:
9
+ * registerLocale('ko', { required: '필수 항목입니다.', ... });
10
+ * setDefaultLocale('ko');
11
+ *
12
+ * 모노레포 로케일 표준(packages/u-widgets/src/core/locale.ts)과 동일한 계약을 따른다.
13
+ */
14
+ export interface UComponentsLocaleStrings {
15
+ required: string;
16
+ minValue: string;
17
+ maxValue: string;
18
+ stepMismatch: string;
19
+ minCount: string;
20
+ maxCount: string;
21
+ }
22
+ /**
23
+ * 로케일을 등록한다. 부분 등록은 영어 기본값에 병합된다.
24
+ */
25
+ export declare function registerLocale(lang: string, strings: Partial<UComponentsLocaleStrings>): void;
26
+ /**
27
+ * 주어진 언어 태그에 대한 로케일 문자열을 해석한다.
28
+ *
29
+ * 해석 순서:
30
+ * 1. 정확히 일치 (예: 'ko-KR')
31
+ * 2. 기반 언어 (예: 'ko')
32
+ * 3. 영어 폴백
33
+ */
34
+ export declare function getLocaleStrings(lang?: string): UComponentsLocaleStrings;
35
+ /**
36
+ * 템플릿 문자열의 {key} 플레이스홀더를 치환한다.
37
+ */
38
+ export declare function formatTemplate(template: string, params: Record<string, string | number>): string;
39
+ /**
40
+ * 영어 기본값을 반환한다 (테스트/참조용).
41
+ */
42
+ export declare function getDefaultLocale(): UComponentsLocaleStrings;
43
+ /**
44
+ * 모든 컴포넌트에 적용되는 전역 기본 로케일을 설정한다.
45
+ *
46
+ * 해석 순서(우선순위 높은 순):
47
+ * 1. 컴포넌트별 `locale` 속성/프로퍼티
48
+ * 2. setDefaultLocale() 값
49
+ * 3. document.documentElement.lang (브라우저)
50
+ * 4. 영어 폴백
51
+ *
52
+ * @example
53
+ * ```ts
54
+ * import { setDefaultLocale } from '@iyulab/components';
55
+ * setDefaultLocale('ko-KR');
56
+ * ```
57
+ */
58
+ export declare function setDefaultLocale(lang: string | undefined): void;
59
+ /**
60
+ * setDefaultLocale() 로 설정된 전역 기본 로케일을 반환한다.
61
+ * 설정되지 않았으면 undefined 를 반환한다.
62
+ */
63
+ export declare function getEffectiveLocale(): string | undefined;
64
+ /**
65
+ * 컴포넌트가 사용할 로케일을 해석한다.
66
+ *
67
+ * 우선순위: widgetLocale > setDefaultLocale() > document.lang > undefined
68
+ */
69
+ export declare function resolveLocale(widgetLocale?: string | null): string | undefined;
@@ -0,0 +1,85 @@
1
+ //#region src/core/locale.ts
2
+ var EN = {
3
+ required: "This field is required",
4
+ minValue: "Value must be at least {min}",
5
+ maxValue: "Value must be at most {max}",
6
+ stepMismatch: "Value must be a multiple of {step}",
7
+ minCount: "Please select at least {min} items",
8
+ maxCount: "Please select no more than {max} items"
9
+ };
10
+ var registry = /* @__PURE__ */ new Map();
11
+ /**
12
+ * 로케일을 등록한다. 부분 등록은 영어 기본값에 병합된다.
13
+ */
14
+ function registerLocale(lang, strings) {
15
+ registry.set(lang.toLowerCase(), {
16
+ ...EN,
17
+ ...strings
18
+ });
19
+ }
20
+ /**
21
+ * 주어진 언어 태그에 대한 로케일 문자열을 해석한다.
22
+ *
23
+ * 해석 순서:
24
+ * 1. 정확히 일치 (예: 'ko-KR')
25
+ * 2. 기반 언어 (예: 'ko')
26
+ * 3. 영어 폴백
27
+ */
28
+ function getLocaleStrings(lang) {
29
+ if (!lang) return EN;
30
+ const key = lang.toLowerCase();
31
+ if (registry.has(key)) return registry.get(key);
32
+ const base = key.split("-")[0];
33
+ if (base !== key && registry.has(base)) return registry.get(base);
34
+ return EN;
35
+ }
36
+ /**
37
+ * 템플릿 문자열의 {key} 플레이스홀더를 치환한다.
38
+ */
39
+ function formatTemplate(template, params) {
40
+ return template.replace(/\{(\w+)\}/g, (_, key) => params[key] != null ? String(params[key]) : `{${key}}`);
41
+ }
42
+ /**
43
+ * 영어 기본값을 반환한다 (테스트/참조용).
44
+ */
45
+ function getDefaultLocale() {
46
+ return { ...EN };
47
+ }
48
+ var _defaultLocale;
49
+ /**
50
+ * 모든 컴포넌트에 적용되는 전역 기본 로케일을 설정한다.
51
+ *
52
+ * 해석 순서(우선순위 높은 순):
53
+ * 1. 컴포넌트별 `locale` 속성/프로퍼티
54
+ * 2. setDefaultLocale() 값
55
+ * 3. document.documentElement.lang (브라우저)
56
+ * 4. 영어 폴백
57
+ *
58
+ * @example
59
+ * ```ts
60
+ * import { setDefaultLocale } from '@iyulab/components';
61
+ * setDefaultLocale('ko-KR');
62
+ * ```
63
+ */
64
+ function setDefaultLocale(lang) {
65
+ _defaultLocale = lang;
66
+ }
67
+ /**
68
+ * setDefaultLocale() 로 설정된 전역 기본 로케일을 반환한다.
69
+ * 설정되지 않았으면 undefined 를 반환한다.
70
+ */
71
+ function getEffectiveLocale() {
72
+ return _defaultLocale;
73
+ }
74
+ /**
75
+ * 컴포넌트가 사용할 로케일을 해석한다.
76
+ *
77
+ * 우선순위: widgetLocale > setDefaultLocale() > document.lang > undefined
78
+ */
79
+ function resolveLocale(widgetLocale) {
80
+ if (widgetLocale) return widgetLocale;
81
+ if (_defaultLocale) return _defaultLocale;
82
+ if (typeof document !== "undefined" && document.documentElement?.lang) return document.documentElement.lang;
83
+ }
84
+ //#endregion
85
+ export { formatTemplate, getDefaultLocale, getEffectiveLocale, getLocaleStrings, registerLocale, resolveLocale, setDefaultLocale };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * from './core/locale.js';
1
2
  export * from './components/UFormControlElement.js';
2
3
  export * from './components/UDataElement.js';
3
4
  export * from './components/UElement.js';
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { formatTemplate, getDefaultLocale, getEffectiveLocale, getLocaleStrings, registerLocale, resolveLocale, setDefaultLocale } from "./core/locale.js";
1
2
  import { UElement } from "./components/UElement.js";
2
3
  import { UFormControlElement } from "./components/UFormControlElement.js";
3
4
  import { IconCache, IconRegistry, getDefaultBaseUrl, setDefaultBaseUrl } from "./utilities/icons.js";
@@ -53,4 +54,4 @@ import { BrowserStorage } from "./utilities/BrowserStorage.js";
53
54
  import { Dialog } from "./utilities/Dialog.js";
54
55
  import { Theme } from "./utilities/Theme.js";
55
56
  import { Toast } from "./utilities/Toast.js";
56
- export { BrowserStorage, Dialog, IconCache, IconRegistry, OverlayManager, Theme, Toast, UAlert, UAvatar, UBadge, UBreadcrumb, UBreadcrumbItem, UButton, UButtonGroup, UCard, UCarousel, UCheckbox, UChip, UDataElement, UDialog, UDivider, UDrawer, UElement, UField, UFloatingElement, UForm, UFormControlElement, UIcon, UIconButton, UInput, UMenu, UMenuItem, UOption, UOverlayElement, UPanel, UPopover, UProgressBar, UProgressRing, URadio, URating, USelect, USkeleton, USlider, USpinner, USplitPanel, USwitch, UTab, UTabPanel, UTag, UTextarea, UTooltip, UTree, UTreeItem, arrayAttrConverter, booleanAttrConverter, buildElementHTML, dateAttrConverter, escapeHtmlAttr, escapeHtmlHref, escapeHtmlText, getDefaultBaseUrl, getParentElement, jsonAttrConverter, querySelectorAllWithin, querySelectorWithin, setDefaultBaseUrl, stripZeroWidth, urlAttrConverter };
57
+ export { BrowserStorage, Dialog, IconCache, IconRegistry, OverlayManager, Theme, Toast, UAlert, UAvatar, UBadge, UBreadcrumb, UBreadcrumbItem, UButton, UButtonGroup, UCard, UCarousel, UCheckbox, UChip, UDataElement, UDialog, UDivider, UDrawer, UElement, UField, UFloatingElement, UForm, UFormControlElement, UIcon, UIconButton, UInput, UMenu, UMenuItem, UOption, UOverlayElement, UPanel, UPopover, UProgressBar, UProgressRing, URadio, URating, USelect, USkeleton, USlider, USpinner, USplitPanel, USwitch, UTab, UTabPanel, UTag, UTextarea, UTooltip, UTree, UTreeItem, arrayAttrConverter, booleanAttrConverter, buildElementHTML, dateAttrConverter, escapeHtmlAttr, escapeHtmlHref, escapeHtmlText, formatTemplate, getDefaultBaseUrl, getDefaultLocale, getEffectiveLocale, getLocaleStrings, getParentElement, jsonAttrConverter, querySelectorAllWithin, querySelectorWithin, registerLocale, resolveLocale, setDefaultBaseUrl, setDefaultLocale, stripZeroWidth, urlAttrConverter };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@iyulab/components",
3
3
  "description": "web-components library based on lit-element made by iyulab",
4
- "version": "1.0.10",
4
+ "version": "1.1.0",
5
5
  "keywords": [
6
6
  "iyulab",
7
7
  "components",
@@ -39,7 +39,9 @@
39
39
  "./plugins/*": "./plugins/*"
40
40
  },
41
41
  "scripts": {
42
- "test": "vite --force",
42
+ "dev": "vite --force",
43
+ "test": "vitest run",
44
+ "test:watch": "vitest",
43
45
  "build:plugins": "tsc -p plugins/tsconfig.json",
44
46
  "build": "eslint && vite build && npm run build:plugins"
45
47
  },
@@ -72,6 +74,7 @@
72
74
  "typescript-eslint": "^8.59.3",
73
75
  "vite": "^8.0.13",
74
76
  "vite-plugin-dts": "^5.0.0",
75
- "vite-plugin-static-copy": "^4.1.0"
77
+ "vite-plugin-static-copy": "^4.1.0",
78
+ "vitest": "^4.1.8"
76
79
  }
77
80
  }