@iyulab/modern-app 0.39.0 → 0.40.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/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.40.0] - 2026-10-07
4
+
5
+ ### Changed
6
+
7
+ - **With `i18n` configured, i18next's language drives the whole app** — at load and on every `changeLanguage()` it is
8
+ handed to `@iyulab/components`' `Locale` (shell, primitives and component chrome follow, re-rendering on screen) and
9
+ to `<html lang>` (WCAG 3.1.1). Before, `changeLanguage('ko')` changed the app's own text while button names, empty
10
+ states and validation messages stayed in the old language, and a first load could show two languages when i18next's
11
+ `lng` differed from `<html lang>` or the browser's. Without `i18n` nothing changes — `Locale.set()` chooses.
12
+ - **Requires `@iyulab/components` 2.8.0** (peer `>=2.8.0`) — the version whose components re-render on a locale switch.
13
+
3
14
  ## [0.39.0] - 2026-10-06
4
15
 
5
16
  ### Fixed
package/README.md CHANGED
@@ -148,7 +148,9 @@ html`<p>${translate('common::greeting')}</p>`;
148
148
  The package's own strings — the page header's back link, empty-state copy, the shell's toggle and
149
149
  close labels, the wizard's buttons and step announcement — live in the `modern-app` namespace of
150
150
  `@iyulab/components`' `Locale`. English and Korean are built in, English being the default; pick the
151
- language once, with `Locale.set()` — the primitives and `@iyulab/flex-table` follow the same call.
151
+ language once, with `Locale.set()` — the primitives and `@iyulab/flex-table` follow the same call. With
152
+ `i18n` configured you do not call it: i18next's language (at load and on every `changeLanguage()`) is
153
+ handed to `Locale` and to `<html lang>`.
152
154
  Register other languages, or your own wording, in the same namespace:
153
155
 
154
156
  ```typescript
package/dist/App.d.ts CHANGED
@@ -14,6 +14,8 @@ declare class App {
14
14
  private _screen?;
15
15
  private _user?;
16
16
  private _gateTeardown?;
17
+ /** i18next 언어 → components `Locale` 동기화 해제(`unload`). */
18
+ private _languageSync?;
17
19
  private constructor();
18
20
  /** 싱글톤 인스턴스 반환 */
19
21
  static get instance(): App;
@@ -32,6 +34,13 @@ declare class App {
32
34
  /** 앱 로드 및 초기화 */
33
35
  load(config: AppConfig): Promise<void>;
34
36
  /** 앱 언로드 */
37
+ /**
38
+ * i18next 를 설정한 앱에서는 **i18next 가 언어의 정본**이다 — 그 언어를 셸·컴포넌트 chrome(`@iyulab/components` 의
39
+ * `Locale`, 이 패키지의 `modernAppLocale` 이 그것을 따른다)과 문서 `<html lang>`(WCAG 3.1.1, 낭독기 발음)에 잇는다.
40
+ * 종전에는 둘이 따로였다: `i18next.changeLanguage('ko')` 가 앱 문장만 바꾸고 버튼 이름·빈 상태·검증 메시지는 옛 언어로,
41
+ * 첫 로드에서도 i18next `lng` 와 `<html lang>`/브라우저 언어가 다르면 한 화면에 두 언어가 섰다.
42
+ */
43
+ private syncLanguage;
35
44
  unload(): void;
36
45
  /** 페이지 이동 */
37
46
  navigate(path: string): void;
package/dist/App.js CHANGED
@@ -4,23 +4,24 @@ import { Router as n } from "@iyulab/router";
4
4
  import { setDefaultBaseUrl as r } from "@iyulab/components/dist/utilities/icons.js";
5
5
  import { Theme as i } from "@iyulab/components/dist/utilities/Theme.js";
6
6
  import { Toast as a } from "@iyulab/components/dist/utilities/Toast.js";
7
+ import { Locale as o } from "@iyulab/components/dist/utilities/Locale.js";
7
8
  //#region src/App.ts
8
- var o = "\n:where(body) { margin: 0; }\n@media screen {\n :where(body) { width: 100vw; height: 100vh; }\n}\n", s;
9
- function c() {
10
- if (!s) {
9
+ var s = "\n:where(body) { margin: 0; }\n@media screen {\n :where(body) { width: 100vw; height: 100vh; }\n}\n", c;
10
+ function l() {
11
+ if (!c) {
11
12
  if ("adoptedStyleSheets" in document && typeof CSSStyleSheet < "u" && typeof CSSStyleSheet.prototype.replaceSync == "function") {
12
13
  let e = new CSSStyleSheet();
13
- e.replaceSync(o), document.adoptedStyleSheets = [...document.adoptedStyleSheets, e], s = e;
14
+ e.replaceSync(s), document.adoptedStyleSheets = [...document.adoptedStyleSheets, e], c = e;
14
15
  } else {
15
16
  let e = document.createElement("style");
16
- e.textContent = o, document.head.prepend(e), s = e;
17
+ e.textContent = s, document.head.prepend(e), c = e;
17
18
  }
18
19
  }
19
20
  }
20
- var l = class o {
21
+ var u = class s {
21
22
  constructor() {}
22
23
  static get instance() {
23
- return o._instance ||= new o(), o._instance;
24
+ return s._instance ||= new s(), s._instance;
24
25
  }
25
26
  get config() {
26
27
  return this._config;
@@ -43,7 +44,7 @@ var l = class o {
43
44
  async load(a) {
44
45
  if (this.unload(), this._config = a, await i.init(a.theme), a.iconBasepath && r(a.iconBasepath), a.i18n) {
45
46
  for (let e of a.i18n.plugins || []) t.use(e);
46
- await t.init(a.i18n);
47
+ await t.init(a.i18n), this.syncLanguage();
47
48
  }
48
49
  if (a.auth) {
49
50
  let e = a.root || document.body, t = () => {
@@ -88,8 +89,16 @@ var l = class o {
88
89
  useIntercept: a.useIntercept
89
90
  });
90
91
  }
92
+ syncLanguage() {
93
+ let e = (e) => {
94
+ e && e !== "cimode" && (o.set(e), typeof document < "u" && (document.documentElement.lang = e));
95
+ };
96
+ e(t.resolvedLanguage ?? t.language);
97
+ let n = (n) => e(t.resolvedLanguage ?? n);
98
+ t.on("languageChanged", n), this._languageSync = () => t.off("languageChanged", n);
99
+ }
91
100
  unload() {
92
- this._gateTeardown &&= (this._gateTeardown(), void 0), this._user = void 0, this._screen &&= (this._screen.destroy(), void 0), this._layout &&= (this._layout.remove(), void 0), this._router &&= (this._router.destroy(), void 0), this._config &&= void 0;
101
+ this._languageSync?.(), this._languageSync = void 0, this._gateTeardown &&= (this._gateTeardown(), void 0), this._user = void 0, this._screen &&= (this._screen.destroy(), void 0), this._layout &&= (this._layout.remove(), void 0), this._router &&= (this._router.destroy(), void 0), this._config &&= void 0;
93
102
  }
94
103
  navigate(e) {
95
104
  this._router?.go(e);
@@ -116,7 +125,7 @@ var l = class o {
116
125
  await a.error(e, { ...t });
117
126
  }
118
127
  async createLayout(e, t) {
119
- e === document.body && c();
128
+ e === document.body && l();
120
129
  let n;
121
130
  if (t.type === "sidebar") {
122
131
  let { SidebarLayout: e } = await import("./layouts/SidebarLayout.js"), r = new e();
@@ -126,4 +135,4 @@ var l = class o {
126
135
  }
127
136
  }.instance;
128
137
  //#endregion
129
- export { l as app };
138
+ export { u as app };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@iyulab/modern-app",
3
3
  "description": "web-framework by iyulab based on lit-element",
4
- "version": "0.39.0",
4
+ "version": "0.40.0",
5
5
  "keywords": [
6
6
  "iyulab",
7
7
  "web-framework",
@@ -65,7 +65,7 @@
65
65
  "lit": "^3.3.3"
66
66
  },
67
67
  "peerDependencies": {
68
- "@iyulab/components": ">=2.0.0",
68
+ "@iyulab/components": ">=2.8.0",
69
69
  "@lit/react": "^1.0.8",
70
70
  "react": "^18.0.0 || ^19.0.0"
71
71
  },
@@ -78,7 +78,7 @@
78
78
  }
79
79
  },
80
80
  "devDependencies": {
81
- "@iyulab/components": ">=2.0.0",
81
+ "@iyulab/components": ">=2.8.0",
82
82
  "@lit/react": "^1.0.8",
83
83
  "@types/node": "^26.1.1",
84
84
  "@types/react": "^19.2.14",