@naeil/ui 0.8.0 → 0.10.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 (68) hide show
  1. package/CHANGELOG.md +24 -1
  2. package/DESIGN.md +5 -1
  3. package/README.md +6 -6
  4. package/dist/{chunk-ULZPJI5X.js → chunk-F7XM5HJ4.js} +40 -154
  5. package/dist/{chunk-WA5D7UI2.js → chunk-GQO6NMXK.js} +267 -2
  6. package/dist/components/index.js +12 -3
  7. package/dist/components/naeil-footer.d.ts +3 -3
  8. package/dist/components/naeil-footer.js +14 -11
  9. package/dist/components/naeil-header.d.ts +7 -9
  10. package/dist/components/naeil-header.js +2 -2
  11. package/dist/lib/ui-release.d.ts +1 -1
  12. package/dist/lib/ui-release.js +1 -1
  13. package/dist/naeil-locale/checker.d.ts +16 -0
  14. package/dist/naeil-locale/checker.js +91 -0
  15. package/dist/naeil-locale/chrome.d.ts +150 -0
  16. package/dist/naeil-locale/chrome.js +2385 -0
  17. package/dist/naeil-locale/chunk-D37JHNUN.js +56 -0
  18. package/dist/naeil-locale/chunk-N3ETBM74.js +24 -0
  19. package/dist/naeil-locale/chunk-QWHIELL2.js +161 -0
  20. package/dist/naeil-locale/chunk-YQ5OJWBF.js +53 -0
  21. package/dist/naeil-locale/client.d.ts +70 -0
  22. package/dist/naeil-locale/client.js +382 -0
  23. package/dist/naeil-locale/en-ZC66VDHP.js +86 -0
  24. package/dist/naeil-locale/index.d.ts +40 -0
  25. package/dist/naeil-locale/index.js +111 -0
  26. package/dist/naeil-locale/ja-DKIEQUG7.js +86 -0
  27. package/dist/naeil-locale/ko-CY6663RZ.js +86 -0
  28. package/dist/naeil-locale/messages-BQ-fnCAJ.d.ts +166 -0
  29. package/dist/naeil-locale/messages.d.ts +1 -0
  30. package/dist/naeil-locale/messages.js +14 -0
  31. package/dist/naeil-locale/server.d.ts +75 -0
  32. package/dist/naeil-locale/server.js +172 -0
  33. package/dist/source/components/naeil-footer-core.d.ts +16 -0
  34. package/dist/source/components/naeil-footer-core.js +93 -0
  35. package/dist/source/components/naeil-footer.d.ts +6 -11
  36. package/dist/source/components/naeil-footer.js +4 -75
  37. package/dist/source/components/naeil-header-account-core.d.ts +26 -0
  38. package/dist/source/components/naeil-header-account-core.js +98 -0
  39. package/dist/source/components/naeil-header-account.d.ts +6 -22
  40. package/dist/source/components/naeil-header-account.js +5 -81
  41. package/dist/source/components/naeil-header-content.d.ts +102 -109
  42. package/dist/source/components/naeil-header-content.js +6 -142
  43. package/dist/source/components/naeil-header-core.d.ts +59 -0
  44. package/dist/source/components/naeil-header-core.js +231 -0
  45. package/dist/source/components/naeil-header-data.d.ts +11 -0
  46. package/dist/source/components/naeil-header-data.js +14 -0
  47. package/dist/source/components/naeil-header.d.ts +10 -51
  48. package/dist/source/components/naeil-header.js +9 -200
  49. package/dist/source/components/naeil-messages-context.d.ts +94 -0
  50. package/dist/source/components/naeil-messages-context.js +16 -0
  51. package/dist/source/components/naeil-standalone-messages.d.ts +11 -0
  52. package/dist/source/components/naeil-standalone-messages.js +13 -0
  53. package/dist/source/i18n/routing.d.ts +22 -20
  54. package/dist/source/lib/ui-release.d.ts +1 -1
  55. package/dist/source/lib/ui-release.js +1 -1
  56. package/dist/source/naeil-locale/messages/en.json +78 -0
  57. package/dist/source/naeil-locale/messages/ja.json +78 -0
  58. package/dist/source/naeil-locale/messages/ko.json +78 -0
  59. package/docs/components/README.md +1 -1
  60. package/docs/components/application-layout.md +24 -6
  61. package/docs/components/common.md +2 -2
  62. package/docs/components/naeil-header.md +10 -4
  63. package/docs/components/naeil-locale.md +46 -0
  64. package/docs/design/v2-migration.md +8 -4
  65. package/docs/package-release.md +27 -6
  66. package/package.json +34 -6
  67. package/src/styles/components.css +17 -0
  68. /package/dist/{naeil-header-content-BvISJ4RV.d.ts → naeil-header-data-BvISJ4RV.d.ts} +0 -0
@@ -0,0 +1,11 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+ import { NaeilHeaderLocale } from './naeil-header-data.js';
4
+
5
+ /** Only the standalone compatibility entry includes all three small dictionaries. */
6
+ declare function NaeilStandaloneMessages({ locale, children }: {
7
+ locale: NaeilHeaderLocale;
8
+ children: ReactNode;
9
+ }): react_jsx_runtime.JSX.Element;
10
+
11
+ export { NaeilStandaloneMessages };
@@ -0,0 +1,13 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import en from "../naeil-locale/messages/en.json";
4
+ import ko from "../naeil-locale/messages/ko.json";
5
+ import ja from "../naeil-locale/messages/ja.json";
6
+ import { NaeilMessagesProvider } from "./naeil-messages-context.js";
7
+ const messages = { en: en.naeil, ko: ko.naeil, ja: ja.naeil };
8
+ function NaeilStandaloneMessages({ locale, children }) {
9
+ return /* @__PURE__ */ jsx(NaeilMessagesProvider, { locale, messages: messages[locale], children });
10
+ }
11
+ export {
12
+ NaeilStandaloneMessages
13
+ };
@@ -1,5 +1,5 @@
1
1
  import * as next_dist_shared_lib_app_router_context_shared_runtime from 'next/dist/shared/lib/app-router-context.shared-runtime';
2
- import * as next_navigation from 'next/navigation';
2
+ import * as next_dist_client_components_redirect_error from 'next/dist/client/components/redirect-error';
3
3
  import * as next_intl_navigation from 'next-intl/navigation';
4
4
  import * as url from 'url';
5
5
  import * as react from 'react';
@@ -31,8 +31,6 @@ declare const Link: react.ForwardRefExoticComponent<Omit<{
31
31
  style?: react.CSSProperties | undefined;
32
32
  title?: string | undefined | undefined;
33
33
  locale?: string | undefined;
34
- onError?: react.ReactEventHandler<HTMLAnchorElement> | undefined;
35
- children?: react.ReactNode;
36
34
  ref?: react.Ref<HTMLAnchorElement> | undefined;
37
35
  prefix?: string | undefined | undefined;
38
36
  key?: react.Key | null | undefined;
@@ -42,12 +40,10 @@ declare const Link: react.ForwardRefExoticComponent<Omit<{
42
40
  passHref?: boolean | undefined;
43
41
  prefetch?: boolean | "auto" | null | undefined;
44
42
  legacyBehavior?: boolean | undefined;
45
- onMouseEnter?: React.MouseEventHandler<HTMLAnchorElement> | undefined;
46
- onTouchStart?: React.TouchEventHandler<HTMLAnchorElement> | undefined;
47
- onClick?: React.MouseEventHandler<HTMLAnchorElement> | undefined;
48
43
  onNavigate?: ((event: {
49
44
  preventDefault: () => void;
50
45
  }) => void) | undefined;
46
+ transitionTypes?: string[] | undefined;
51
47
  download?: any;
52
48
  hrefLang?: string | undefined | undefined;
53
49
  media?: string | undefined | undefined;
@@ -104,7 +100,6 @@ declare const Link: react.ForwardRefExoticComponent<Omit<{
104
100
  is?: string | undefined | undefined;
105
101
  exportparts?: string | undefined | undefined;
106
102
  part?: string | undefined | undefined;
107
- tw?: string | undefined;
108
103
  "aria-activedescendant"?: string | undefined | undefined;
109
104
  "aria-atomic"?: (boolean | "true" | "false") | undefined;
110
105
  "aria-autocomplete"?: "none" | "inline" | "list" | "both" | undefined | undefined;
@@ -158,6 +153,8 @@ declare const Link: react.ForwardRefExoticComponent<Omit<{
158
153
  "aria-valuemin"?: number | undefined | undefined;
159
154
  "aria-valuenow"?: number | undefined | undefined;
160
155
  "aria-valuetext"?: string | undefined | undefined;
156
+ href: string | url.UrlObject;
157
+ children?: react.ReactNode;
161
158
  dangerouslySetInnerHTML?: {
162
159
  __html: string | TrustedHTML;
163
160
  } | undefined | undefined;
@@ -177,20 +174,21 @@ declare const Link: react.ForwardRefExoticComponent<Omit<{
177
174
  onFocusCapture?: react.FocusEventHandler<HTMLAnchorElement> | undefined;
178
175
  onBlur?: react.FocusEventHandler<HTMLAnchorElement> | undefined;
179
176
  onBlurCapture?: react.FocusEventHandler<HTMLAnchorElement> | undefined;
180
- onChange?: react.FormEventHandler<HTMLAnchorElement> | undefined;
181
- onChangeCapture?: react.FormEventHandler<HTMLAnchorElement> | undefined;
177
+ onChange?: react.ChangeEventHandler<HTMLAnchorElement, Element> | undefined;
178
+ onChangeCapture?: react.ChangeEventHandler<HTMLAnchorElement, Element> | undefined;
182
179
  onBeforeInput?: react.InputEventHandler<HTMLAnchorElement> | undefined;
183
- onBeforeInputCapture?: react.FormEventHandler<HTMLAnchorElement> | undefined;
184
- onInput?: react.FormEventHandler<HTMLAnchorElement> | undefined;
185
- onInputCapture?: react.FormEventHandler<HTMLAnchorElement> | undefined;
186
- onReset?: react.FormEventHandler<HTMLAnchorElement> | undefined;
187
- onResetCapture?: react.FormEventHandler<HTMLAnchorElement> | undefined;
188
- onSubmit?: react.FormEventHandler<HTMLAnchorElement> | undefined;
189
- onSubmitCapture?: react.FormEventHandler<HTMLAnchorElement> | undefined;
190
- onInvalid?: react.FormEventHandler<HTMLAnchorElement> | undefined;
191
- onInvalidCapture?: react.FormEventHandler<HTMLAnchorElement> | undefined;
180
+ onBeforeInputCapture?: react.InputEventHandler<HTMLAnchorElement> | undefined;
181
+ onInput?: react.InputEventHandler<HTMLAnchorElement> | undefined;
182
+ onInputCapture?: react.InputEventHandler<HTMLAnchorElement> | undefined;
183
+ onReset?: react.ReactEventHandler<HTMLAnchorElement> | undefined;
184
+ onResetCapture?: react.ReactEventHandler<HTMLAnchorElement> | undefined;
185
+ onSubmit?: react.SubmitEventHandler<HTMLAnchorElement> | undefined;
186
+ onSubmitCapture?: react.SubmitEventHandler<HTMLAnchorElement> | undefined;
187
+ onInvalid?: react.ReactEventHandler<HTMLAnchorElement> | undefined;
188
+ onInvalidCapture?: react.ReactEventHandler<HTMLAnchorElement> | undefined;
192
189
  onLoad?: react.ReactEventHandler<HTMLAnchorElement> | undefined;
193
190
  onLoadCapture?: react.ReactEventHandler<HTMLAnchorElement> | undefined;
191
+ onError?: react.ReactEventHandler<HTMLAnchorElement> | undefined;
194
192
  onErrorCapture?: react.ReactEventHandler<HTMLAnchorElement> | undefined;
195
193
  onKeyDown?: react.KeyboardEventHandler<HTMLAnchorElement> | undefined;
196
194
  onKeyDownCapture?: react.KeyboardEventHandler<HTMLAnchorElement> | undefined;
@@ -244,6 +242,7 @@ declare const Link: react.ForwardRefExoticComponent<Omit<{
244
242
  onWaitingCapture?: react.ReactEventHandler<HTMLAnchorElement> | undefined;
245
243
  onAuxClick?: react.MouseEventHandler<HTMLAnchorElement> | undefined;
246
244
  onAuxClickCapture?: react.MouseEventHandler<HTMLAnchorElement> | undefined;
245
+ onClick?: react.MouseEventHandler<HTMLAnchorElement> | undefined;
247
246
  onClickCapture?: react.MouseEventHandler<HTMLAnchorElement> | undefined;
248
247
  onContextMenu?: react.MouseEventHandler<HTMLAnchorElement> | undefined;
249
248
  onContextMenuCapture?: react.MouseEventHandler<HTMLAnchorElement> | undefined;
@@ -267,6 +266,7 @@ declare const Link: react.ForwardRefExoticComponent<Omit<{
267
266
  onDropCapture?: react.DragEventHandler<HTMLAnchorElement> | undefined;
268
267
  onMouseDown?: react.MouseEventHandler<HTMLAnchorElement> | undefined;
269
268
  onMouseDownCapture?: react.MouseEventHandler<HTMLAnchorElement> | undefined;
269
+ onMouseEnter?: react.MouseEventHandler<HTMLAnchorElement> | undefined;
270
270
  onMouseLeave?: react.MouseEventHandler<HTMLAnchorElement> | undefined;
271
271
  onMouseMove?: react.MouseEventHandler<HTMLAnchorElement> | undefined;
272
272
  onMouseMoveCapture?: react.MouseEventHandler<HTMLAnchorElement> | undefined;
@@ -284,6 +284,7 @@ declare const Link: react.ForwardRefExoticComponent<Omit<{
284
284
  onTouchEndCapture?: react.TouchEventHandler<HTMLAnchorElement> | undefined;
285
285
  onTouchMove?: react.TouchEventHandler<HTMLAnchorElement> | undefined;
286
286
  onTouchMoveCapture?: react.TouchEventHandler<HTMLAnchorElement> | undefined;
287
+ onTouchStart?: react.TouchEventHandler<HTMLAnchorElement> | undefined;
287
288
  onTouchStartCapture?: react.TouchEventHandler<HTMLAnchorElement> | undefined;
288
289
  onPointerDown?: react.PointerEventHandler<HTMLAnchorElement> | undefined;
289
290
  onPointerDownCapture?: react.PointerEventHandler<HTMLAnchorElement> | undefined;
@@ -325,7 +326,6 @@ declare const Link: react.ForwardRefExoticComponent<Omit<{
325
326
  onTransitionRunCapture?: react.TransitionEventHandler<HTMLAnchorElement> | undefined;
326
327
  onTransitionStart?: react.TransitionEventHandler<HTMLAnchorElement> | undefined;
327
328
  onTransitionStartCapture?: react.TransitionEventHandler<HTMLAnchorElement> | undefined;
328
- href: string | url.UrlObject;
329
329
  }, "ref"> & react.RefAttributes<HTMLAnchorElement>>;
330
330
  declare const redirect: (args: {
331
331
  href: string | {
@@ -334,7 +334,7 @@ declare const redirect: (args: {
334
334
  };
335
335
  locale: string;
336
336
  forcePrefix?: boolean;
337
- }, type?: next_navigation.RedirectType | undefined) => never;
337
+ }, type?: next_dist_client_components_redirect_error.RedirectType | undefined) => never;
338
338
  declare const usePathname: () => string;
339
339
  declare const useRouter: () => {
340
340
  push: (href: string | {
@@ -358,6 +358,8 @@ declare const useRouter: () => {
358
358
  back(): void;
359
359
  forward(): void;
360
360
  refresh(): void;
361
+ experimental_gesturePush?(href: string, options?: next_dist_shared_lib_app_router_context_shared_runtime.NavigateOptions): void;
362
+ bfcacheId: string;
361
363
  };
362
364
  declare const getPathname: (args: {
363
365
  href: string | {
@@ -1,5 +1,5 @@
1
1
  declare const uiRelease: {
2
- readonly version: "0.8.0";
2
+ readonly version: "0.10.0";
3
3
  readonly contractVersion: "1";
4
4
  };
5
5
 
@@ -1,4 +1,4 @@
1
- const uiRelease = { version: "0.8.0", contractVersion: "1" };
1
+ const uiRelease = { version: "0.10.0", contractVersion: "1" };
2
2
  export {
3
3
  uiRelease
4
4
  };
@@ -0,0 +1,78 @@
1
+ {
2
+ "naeil": {
3
+ "header": {
4
+ "nav": {
5
+ "home": "Home",
6
+ "projects": "Projects",
7
+ "blog": "Blog",
8
+ "navigation": "Main navigation",
9
+ "menu": "Menu",
10
+ "closeMenu": "Close menu"
11
+ },
12
+ "theme": {
13
+ "switchToLight": "Switch to light theme",
14
+ "switchToDark": "Switch to dark theme"
15
+ },
16
+ "locale": {
17
+ "switchLabel": "Language"
18
+ },
19
+ "projects": {
20
+ "cc": {
21
+ "name": "Content Collector",
22
+ "description": "Automated content collection from RSS, YouTube, Reddit + AI summarization"
23
+ },
24
+ "pkm": {
25
+ "name": "PKM",
26
+ "description": "Personal knowledge management — document parsing, embeddings, semantic search"
27
+ },
28
+ "naeilUi": {
29
+ "name": "naeil-ui",
30
+ "description": "Minimal design system — OKLCH tokens, Tailwind v4, shadcn"
31
+ },
32
+ "sa": {
33
+ "name": "Sustainability Analyzer",
34
+ "description": "AI-powered ESG report analysis — PDF parsing, qualitative assessment, comparison tables"
35
+ },
36
+ "babyAgent": {
37
+ "name": "Baby Agent",
38
+ "description": "Parenting AI agent — LINE-based Q&A, records, reminders"
39
+ },
40
+ "status": {
41
+ "active": "Active",
42
+ "coming": "Coming Soon"
43
+ }
44
+ }
45
+ },
46
+ "account": {
47
+ "menu": "Account menu",
48
+ "account": "Account",
49
+ "login": "Log in",
50
+ "logout": "Log out",
51
+ "busy": "Logging out…"
52
+ },
53
+ "footer": {
54
+ "navigation": "Navigation",
55
+ "projects": "Projects",
56
+ "blog": "Blog",
57
+ "social": "Social",
58
+ "description": "Tomorrow starts here."
59
+ },
60
+ "locale": {
61
+ "names": {
62
+ "en": "English",
63
+ "ko": "한국어",
64
+ "ja": "日本語"
65
+ },
66
+ "pending": "Changing language…",
67
+ "failed": "The language could not be changed. Your work is still here.",
68
+ "retry": "Try again",
69
+ "errors": {
70
+ "badRequest": "This address is not valid.",
71
+ "notFound": "Page not found.",
72
+ "unauthenticated": "Sign in to continue.",
73
+ "forbidden": "You do not have access to this page.",
74
+ "unavailable": "The service is temporarily unavailable. Please try again."
75
+ }
76
+ }
77
+ }
78
+ }
@@ -0,0 +1,78 @@
1
+ {
2
+ "naeil": {
3
+ "header": {
4
+ "nav": {
5
+ "home": "ホーム",
6
+ "projects": "プロジェクト",
7
+ "blog": "ブログ",
8
+ "navigation": "メインナビゲーション",
9
+ "menu": "メニュー",
10
+ "closeMenu": "メニューを閉じる"
11
+ },
12
+ "theme": {
13
+ "switchToLight": "ライトテーマに切り替える",
14
+ "switchToDark": "ダークテーマに切り替える"
15
+ },
16
+ "locale": {
17
+ "switchLabel": "言語"
18
+ },
19
+ "projects": {
20
+ "cc": {
21
+ "name": "Content Collector",
22
+ "description": "RSS、YouTube、Redditの自動収集 + AI要約"
23
+ },
24
+ "pkm": {
25
+ "name": "PKM",
26
+ "description": "個人ナレッジ管理 — ドキュメント解析、埋め込み、セマンティック検索"
27
+ },
28
+ "naeilUi": {
29
+ "name": "naeil-ui",
30
+ "description": "ミニマルデザインシステム — OKLCHトークン、Tailwind v4、shadcn"
31
+ },
32
+ "sa": {
33
+ "name": "Sustainability Analyzer",
34
+ "description": "ESGレポートAI分析 — PDF解析、定性評価、比較表の自動生成"
35
+ },
36
+ "babyAgent": {
37
+ "name": "Baby Agent",
38
+ "description": "育児AIエージェント — LINE Q&A、記録、リマインダー"
39
+ },
40
+ "status": {
41
+ "active": "アクティブ",
42
+ "coming": "準備中"
43
+ }
44
+ }
45
+ },
46
+ "account": {
47
+ "menu": "アカウントメニュー",
48
+ "account": "アカウント",
49
+ "login": "ログイン",
50
+ "logout": "ログアウト",
51
+ "busy": "ログアウト中…"
52
+ },
53
+ "footer": {
54
+ "navigation": "ナビゲーション",
55
+ "projects": "プロジェクト",
56
+ "blog": "ブログ",
57
+ "social": "ソーシャル",
58
+ "description": "Tomorrow starts here."
59
+ },
60
+ "locale": {
61
+ "names": {
62
+ "en": "English",
63
+ "ko": "한국어",
64
+ "ja": "日本語"
65
+ },
66
+ "pending": "言語を変更しています…",
67
+ "failed": "言語を変更できませんでした。作業内容は保持されています。",
68
+ "retry": "再試行",
69
+ "errors": {
70
+ "badRequest": "このアドレスは無効です。",
71
+ "notFound": "ページが見つかりません。",
72
+ "unauthenticated": "続けるにはログインしてください。",
73
+ "forbidden": "このページへのアクセス権限がありません。",
74
+ "unavailable": "サービスを一時的に利用できません。もう一度お試しください。"
75
+ }
76
+ }
77
+ }
78
+ }
@@ -0,0 +1,78 @@
1
+ {
2
+ "naeil": {
3
+ "header": {
4
+ "nav": {
5
+ "home": "홈",
6
+ "projects": "프로젝트",
7
+ "blog": "블로그",
8
+ "navigation": "주요 메뉴",
9
+ "menu": "메뉴",
10
+ "closeMenu": "메뉴 닫기"
11
+ },
12
+ "theme": {
13
+ "switchToLight": "밝은 테마로 전환",
14
+ "switchToDark": "어두운 테마로 전환"
15
+ },
16
+ "locale": {
17
+ "switchLabel": "언어"
18
+ },
19
+ "projects": {
20
+ "cc": {
21
+ "name": "Content Collector",
22
+ "description": "RSS, YouTube, Reddit 자동 수집 + AI 요약"
23
+ },
24
+ "pkm": {
25
+ "name": "PKM",
26
+ "description": "개인 지식 관리 — 문서 파싱, 임베딩, 시맨틱 검색"
27
+ },
28
+ "naeilUi": {
29
+ "name": "naeil-ui",
30
+ "description": "미니멀 디자인 시스템 — OKLCH 토큰, Tailwind v4, shadcn"
31
+ },
32
+ "sa": {
33
+ "name": "Sustainability Analyzer",
34
+ "description": "ESG 리포트 AI 분석 — PDF 파싱, 정성평가, 비교표 자동 생성"
35
+ },
36
+ "babyAgent": {
37
+ "name": "Baby Agent",
38
+ "description": "육아 AI 에이전트 — LINE 기반 Q&A, 기록, 리마인더"
39
+ },
40
+ "status": {
41
+ "active": "활성",
42
+ "coming": "준비 중"
43
+ }
44
+ }
45
+ },
46
+ "account": {
47
+ "menu": "계정 메뉴",
48
+ "account": "계정",
49
+ "login": "로그인",
50
+ "logout": "로그아웃",
51
+ "busy": "로그아웃 중…"
52
+ },
53
+ "footer": {
54
+ "navigation": "네비게이션",
55
+ "projects": "프로젝트",
56
+ "blog": "블로그",
57
+ "social": "소셜",
58
+ "description": "Tomorrow starts here."
59
+ },
60
+ "locale": {
61
+ "names": {
62
+ "en": "English",
63
+ "ko": "한국어",
64
+ "ja": "日本語"
65
+ },
66
+ "pending": "언어를 변경하는 중…",
67
+ "failed": "언어를 변경하지 못했습니다. 작업 내용은 유지됩니다.",
68
+ "retry": "다시 시도",
69
+ "errors": {
70
+ "badRequest": "올바르지 않은 주소입니다.",
71
+ "notFound": "페이지를 찾을 수 없습니다.",
72
+ "unauthenticated": "계속하려면 로그인하세요.",
73
+ "forbidden": "이 페이지에 접근할 권한이 없습니다.",
74
+ "unavailable": "일시적으로 서비스를 이용할 수 없습니다. 다시 시도하세요."
75
+ }
76
+ }
77
+ }
78
+ }
@@ -1,6 +1,6 @@
1
1
  # Component usage guides
2
2
 
3
- These maintained guides cover **published and registry-verified @naeil/ui 0.7.1**: canonical footer and release identity from the unpublished 0.7.0 candidate, plus shared account presentation. The current published baseline is 0.7.1; see [release policy and identities](../package-release.md#publication-and-deployment). [Setup, themes, fonts and shared responsibilities](common.md). Run `pnpm storybook` for the current 17 Docs pages (15 UI and 2 Brand) and choose Docs or Usage. The [hosted Storybook](https://naeil-dev.github.io/naeil-ui/) has recorded [0.5.0 verification](https://github.com/naeil-dev/naeil-ui/releases/tag/v0.5.0); each subsequent deployment requires its own receipt. Select chooses a value; DropdownMenu chooses an action.
3
+ These maintained guides cover published exact **@naeil/ui 0.8.0** and the unpublished **0.9.0 frame candidate**. On 2026-10-29 registry tags were latest=0.7.2 and next=0.8.0; see [release policy and identities](../package-release.md#publication-and-deployment). [Setup, themes, fonts and shared responsibilities](common.md). Run `pnpm storybook` for the current 17 Docs pages (15 UI and 2 Brand) and choose Docs or Usage. The [hosted Storybook](https://naeil-dev.github.io/naeil-ui/) has recorded [0.5.0 verification](https://github.com/naeil-dev/naeil-ui/releases/tag/v0.5.0); each subsequent deployment requires its own receipt. Select chooses a value; DropdownMenu chooses an action.
4
4
 
5
5
  Start application layouts with the [shared navigation/footer selection and frame guide](application-layout.md). It includes a starter, the 1600px frame contract and instructions to place in consumer repositories so agents reuse the shared implementation.
6
6
 
@@ -1,6 +1,6 @@
1
1
  # Application frame and shared navigation/footer
2
2
 
3
- The published registry baseline is **@naeil/ui 0.7.2**; the new Home profile is an **unpublished 0.8.0 candidate**. Install exact `@naeil/ui@0.7.2` for published consumers; hosted Docs and service activation have separate receipts. See [release identities and status](../package-release.md#publication-and-deployment) and [consumer setup](../design/v2-migration.md) for CSS, Tailwind sources and fonts.
3
+ Exact **@naeil/ui 0.8.0 is published** (`next` tag; `latest` remains 0.7.2 at the 2026-10-29 verification). The **0.9.0 candidate** adds shared root reservation. Install exact `@naeil/ui@0.8.0` for the published Home APIs; hosted Docs and service activation have separate receipts. See [release identities and status](../package-release.md#publication-and-deployment) and [consumer setup](../design/v2-migration.md) for CSS, Tailwind sources and fonts.
4
4
 
5
5
  ## Choose the shared composition first
6
6
 
@@ -8,7 +8,7 @@ The published registry baseline is **@naeil/ui 0.7.2**; the new Home profile is
8
8
  | --- | --- |
9
9
  | The actual naeil.dev brand, project catalog and controls | `NaeilHeader` from `@naeil/ui/naeil-header` |
10
10
  | Owned-service login/account/logout presentation | `NaeilHeaderAccountControl` from `@naeil/ui/naeil-header` in `authSlot` |
11
- | Owned naeil service with product menu data (0.8.0 candidate) | Complete `NaeilHeader`, with `appNavigation`, catalog/blog hidden, optional `brandSuffix` and `statusSlot` |
11
+ | Owned naeil service with product menu data (0.8.0) | Complete `NaeilHeader`, with `appNavigation`, catalog/blog hidden, optional `brandSuffix` and `statusSlot` |
12
12
  | A different product menu without canonical naeil chrome | `Navigation` from `@naeil/ui/ui` |
13
13
  | Owned naeil service footer | `NaeilFooter` from `@naeil/ui/naeil-footer` |
14
14
  | Different product footer or explicit content override | `SiteFooter` from `@naeil/ui/ui` |
@@ -29,13 +29,30 @@ Choose one header per application frame. Supply menu data, links, route guards,
29
29
 
30
30
  Use `.ui-app-frame` on main. Shared header/footer already apply the frame internally; **do not wrap them in another padded frame**. Put purpose-specific sections inside main. At 1920px the default frame has 1520px available after its 40px gutters. Small screens shrink the frame and reduce gutters to at least 16px. A narrow form must not constrain or move navigation.
31
31
 
32
- Reserve a consistent scrollbar gutter in the consumer's global CSS when routes can alternate between short and tall content:
32
+ The **0.9.0 candidate** supplies `html { scrollbar-gutter: stable }` once in `components.css`, reached by `globals.css` or `theme.css + components.css`. Classic mode reserves one browser-sized gutter on short and long pages; overlay mode reserves none. The shared lock correction removes duplicate Radix body right-margin/gap compensation and retains its scroll prevention, focus and nested cleanup. Keep header/main/footer and their ordinary content horizontally stable. Compare matching root policies and measure before any fullPage capture. See the [adopted acceptance contract](https://github.com/naeil-dev/naeil-ui/blob/434b7d07235316097ca3a55265ee02c1422b0cfd/docs/superpowers/specs/2026-10-29-page-root-scrollbar-gutter.md).
33
+
34
+ For a different document policy, declare this on the root **before first paint**:
35
+
36
+ ```html
37
+ <html data-naeil-scrollbar-gutter="auto">
38
+ ```
39
+
40
+ This sets `scrollbar-gutter: auto` and disables the shared lock correction together. Apply custom root/scroller CSS only after opting out; changing only gutter CSS leaves an unsupported compensation combination. The default full-page profile has zero body margin/outer padding; no new body reset is supplied. Nonzero body edge spacing, custom scroll locks and react-remove-scroll-bar helper classes need consumer integration or opt-out. Theme-only and JS-only imports do not set root policy. Unsupported engines keep Radix's original compensation.
41
+
42
+ Import `globals.css`, or the split `theme.css` and `components.css`, without an enclosing named layer (`@import "@naeil/ui/components.css";`, not `@import "@naeil/ui/components.css" layer(ui);`). The package already declares its own layers. Wrapping the import in a named layer lets the dependency's unlayered `--removed-body-scroll-bar-size` win: it can remain 15px in classic mode even though the important margin correction still wins. Elements using that variable can then shift.
43
+
44
+ After opting out, a plain `@layer base { html { scrollbar-gutter: stable; } }` loses to the package's more specific root attribute selector in the same layer. Write the custom rule outside any layer, for example:
33
45
 
34
46
  ```css
35
- html { scrollbar-gutter: stable; }
47
+ /* The root has data-naeil-scrollbar-gutter="auto"; this policy owns its lock integration. */
48
+ html[data-naeil-scrollbar-gutter="auto"] { scrollbar-gutter: stable; }
36
49
  ```
37
50
 
38
- This keeps a centered frame from shifting when a classic scrollbar appears. The document scroll policy belongs to the application; the shared components do not set it implicitly. In a 1920px Chromium viewport with a 15px scrollbar, the frame starts at 152.5px with this policy in both short and tall states. Without it, the start changes from 160px to 152.5px. Overlay-scrollbar platforms may reserve no extra space.
51
+ Alternatively, use that attribute selector in `@layer base` after the package import, so equal specificity resolves to the later consumer rule. The shared lock correction stays disabled in either case; the consumer owns matching compensation.
52
+
53
+ In classic mode, a fixed modal scrim covers the page's available width, leaving the reserved gutter strip undimmed (15px in the measured macOS Chromium profile). This is expected with a reserved root; 0.9.0 leaves the scrim styles unchanged. Overlay mode has no reserved strip. A full-viewport scrim redesign is outside this release.
54
+
55
+ Frame and control tokens stay unchanged. At 1920px with a 15px classic gutter the centered 1600px frame starts at 152.5px on both short and long pages (overlay: 160px). Japanese signed-out canonical login content at width 320 has 257px inside the 305px classic sheet; its 261px toolbar wraps to 104px. Overlay remains 44px. Preserve all labels, 44px targets and keyboard access. Home Workspace/account and LoginGate/no-login-slot retain their 44px toolbars; this specific F3 profile difference is documented, not a new Home chrome exception.
39
56
 
40
57
  ## Canonical naeil application starter
41
58
 
@@ -79,7 +96,7 @@ export function ApplicationLayout({ children, locale, Link, currentHref,
79
96
 
80
97
  `NaeilHeader` is fixed; reserve its 65px height and account for it in sticky areas and focus/anchor scroll margins. Default destinations are official naeil.dev URLs. Preserve routing/unsaved-change guards using the [header contract](naeil-header.md) and [footer interception contract](naeil-footer.md); a Link adapter must handle absolute official and GitHub destinations as well as local routes. Only pass `onLocaleChange` when the application supports its complete language set.
81
98
 
82
- For owned naeil product navigation, keep the whole header and pass menu data through `appNavigation`; see the [Home profile and host conditions](naeil-header.md#host-typography-and-mobile-handoff-080-candidate). `mobileOpen`/`onMobileOpenChange` can coordinate the sheet with a consumer confirmation, while busy state and request locking remain above the header. For a different product menu without canonical chrome, use the [generic Navigation composition](navigation.md) with the same main/footer structure. Generic Navigation is in document flow by default and needs no fixed-header clearance. The [standalone React example](https://github.com/naeil-dev/naeil-ui/tree/main/examples/react) exercises the installed header, narrow form and SiteFooter together.
99
+ For owned naeil product navigation, keep the whole header and pass menu data through `appNavigation`; see the [Home profile and host conditions](naeil-header.md#host-typography-and-mobile-handoff-080). `mobileOpen`/`onMobileOpenChange` can coordinate the sheet with a consumer confirmation, while busy state and request locking remain above the header. For a different product menu without canonical chrome, use the [generic Navigation composition](navigation.md) with the same main/footer structure. Generic Navigation is in document flow by default and needs no fixed-header clearance. The [standalone React example](https://github.com/naeil-dev/naeil-ui/tree/main/examples/react) exercises the installed header, narrow form and SiteFooter together.
83
100
 
84
101
  An empty desktop menu preserves B1's limitation: resizing alone leaves the sheet open; Escape/close/overlay/route change dismiss it. Controlled-mobile account callbacks wait for both closing layers and their focus handoff; desktop and uncontrolled-mobile account callbacks retain B1's selection-time behavior. A consumer confirmation owns cancellation focus, including a visible desktop account fallback after resize.
85
102
 
@@ -109,6 +126,7 @@ Put this in the **consumer application's** `AGENTS.md` (or equivalent repository
109
126
  - Use @naeil/ui/naeil-header for the canonical naeil header; use Navigation from @naeil/ui/ui for a different product menu. Use NaeilFooter from @naeil/ui/naeil-footer for owned service footers; SiteFooter remains the generic primitive. Keep existing Next Nav/Footer compatibility adapters when applicable.
110
127
  - Put NaeilHeaderAccountControl from @naeil/ui/naeil-header in authSlot for owned apps. Supply real session state, the busy flag and guarded logout callback. Keep pending state and the request lock above the header so desktop/mobile copies share them; do not copy account/avatar/menu markup.
111
128
  - Local wrappers supply menu data, routing/guards, locale, auth and slots. Do not copy the menu, mobile sheet, controls or footer implementation. Extend the shared component for reusable missing behavior.
129
+ - The 0.9.0 candidate CSS reserves a stable root gutter through globals or theme+components; opt out on html with data-naeil-scrollbar-gutter="auto" before first paint to disable both root reservation and lock correction. Compare classic/overlay and short/long body content through menus/dialogs; measure before fullPage screenshots.
112
130
  - Use ui-app-frame on main; navigation/footer own their internal frames. Keep reading/settings/short-form sections inside that frame. Apply frame overrides consistently across routes.
113
131
  - Check the installed package version and CSS before creating a replacement for an apparently missing component.
114
132
  - Verify actual shared navigation/footer rendering and stable anchors across routes, themes and supported sizes, including signed-out, signed-in, menu, busy, cancellation/failure and retry states. An import or anonymous-only check is not adoption evidence. Record intentional route exceptions.
@@ -1,12 +1,12 @@
1
1
  # Shared usage contract
2
2
 
3
- These guides target the **@naeil/ui 0.6.0**; release identities and publication evidence are recorded in the [release record](https://github.com/naeil-dev/naeil-ui/releases/tag/v0.6.0). The [hosted Docs](https://naeil-dev.github.io/naeil-ui/) were last verified for [0.5.0 on 2026-10-15](https://github.com/naeil-dev/naeil-ui/releases/tag/v0.5.0), while `pnpm storybook` shows this checkout. Start with the [consumer setup and migration](../design/v2-migration.md). Primitives and generic Navigation/SiteFooter are exported from `@naeil/ui/ui`; the actual branded whole header uses the opt-in [NaeilHeader](naeil-header.md) entry `@naeil/ui/naeil-header`. Both are React-only. The legacy root needs the optional Next/next-intl peers. Examples import real wrappers; product state, validation and network requests belong to consumers.
3
+ These maintained guides cover published exact **@naeil/ui 0.8.0** and the unpublished **0.9.0 frame candidate**; see [release identities and status](../package-release.md#publication-and-deployment). The [hosted Docs](https://naeil-dev.github.io/naeil-ui/) were last verified for [0.5.0 on 2026-10-15](https://github.com/naeil-dev/naeil-ui/releases/tag/v0.5.0), while `pnpm storybook` shows this checkout. Start with the [consumer setup and migration](../design/v2-migration.md). Primitives and generic Navigation/SiteFooter are exported from `@naeil/ui/ui`; the actual branded whole header uses the opt-in [NaeilHeader](naeil-header.md) entry `@naeil/ui/naeil-header`. Both are React-only. The legacy root needs the optional Next/next-intl peers. Examples import real wrappers; product state, validation and network requests belong to consumers.
4
4
 
5
5
  ## Styling, themes and overrides
6
6
 
7
7
  Import `@naeil/ui/globals.css` once and register `@source` for the installed package's `dist` in your Tailwind 4 build. Alternatively load both theme.css and components.css alongside your existing Tailwind setup. Deliver Pretendard and, for Japanese, Noto Sans JP locally through your app's font pipeline; the package does not fetch fonts. Keep font OFL notices with redistributed binaries. The independent React example demonstrates consumer-owned delivery.
8
8
 
9
- Use `.light`/`.dark` on the document root to override OS theme. Root `data-ui-density="compact"` changes spacing, not text size: default controls are 40px, compact 36px, small screens/coarse pointers at least 44px. Portals use document-level styles/density, so a locally themed subtree alone does not theme portaled content. Header, main and footer share a 1600px maximum outer frame and `clamp(16px, 2.4vw, 40px)` gutters. Use `ui-app-frame` on main; shared header/footer components apply that frame internally. Reading/settings sections remain capped at 640px, short forms at 480px, and lists at 1600px within the available frame space. Product root token overrides and explicit component classes remain supported. The [application layout guide](application-layout.md) explains composition, adoption and consistent overrides.
9
+ Use `.light`/`.dark` on the document root to override OS theme. Root `data-ui-density="compact"` changes spacing, not text size: default controls are 40px, compact 36px, small screens/coarse pointers at least 44px. Portals use document-level styles/density, so a locally themed subtree alone does not theme portaled content. Header, main and footer share a 1600px maximum outer frame and `clamp(16px, 2.4vw, 40px)` gutters. Use `ui-app-frame` on main; shared header/footer components apply that frame internally. Reading/settings sections remain capped at 640px, short forms at 480px, and lists at 1600px within the available frame space. Product root token overrides and explicit component classes remain supported. The [application layout guide](application-layout.md) explains composition, adoption and consistent overrides, including the 0.9.0 default stable root gutter, the coupled html opt-out and unchanged classic/overlay toolbar tokens. Theme-only/JS-only imports do not set document policy.
10
10
 
11
11
  `className` is merged by `cn` (clsx + tailwind-merge). Consumer classes and primitive props remain available. Recheck specificity, text/control/focus contrast, mobile targets and portaled content after overriding. Override semantic color companions together; do not replace only an action hue or remove focus outlines. Reduced-motion rules remove shared transitions/animations. Custom consumer animation still needs its own reduced-motion treatment.
12
12
 
@@ -1,6 +1,6 @@
1
1
  # NaeilHeader — branded whole-header composition
2
2
 
3
- The published registry baseline is **@naeil/ui 0.7.2**. This guide also describes the **unpublished 0.8.0 candidate** APIs below. Install exact `@naeil/ui@0.7.2` for the published API; the new Home props require the reviewed 0.8.0 archive. Hosted Docs and service activation have separate receipts. See [release identities and publication status](../package-release.md#publication-and-deployment). Import this opt-in React-only entry when an application inherits the actual naeil.dev header. Generic `Navigation` from `/ui` remains available for different product menus.
3
+ Exact **@naeil/ui 0.8.0 is published** (registry `next`; `latest=0.7.2` verified 2026-10-29). These Home APIs are available in that exact version. The **0.9.0 candidate** adds shared root reservation and remains unpublished. Hosted Docs and service activation have separate receipts. See [release identities](../package-release.md#publication-and-deployment).
4
4
 
5
5
  ```tsx
6
6
  import { NaeilHeader, NaeilHeaderAccountControl } from '@naeil/ui/naeil-header';
@@ -71,16 +71,16 @@ This entry requires no Next.js, next-intl, site session/auth backend, full appli
71
71
 
72
72
  Verify actual packed consumption, Storybook behavior and rendered comparisons at matching viewport/language/theme. Source imports alone do not prove visual parity. Native Safari, devices, assistive technology, full OAuth and unresolved accessibility observations retain the [documented support limits](../design/public-ui-support.md).
73
73
 
74
- Pair owned-service headers with [NaeilFooter](naeil-footer.md). The 0.7.2 source is `40a3928e24cd2f0141be3e605eed0a75260938fd`. Both 0.7.2 and the 0.8.0 candidate emit package version and chrome contract `1`; archive identities, maintained guidance and hosted Docs remain separate evidence.
74
+ Pair owned-service headers with [NaeilFooter](naeil-footer.md). The 0.7.2 source is `40a3928e24cd2f0141be3e605eed0a75260938fd`. Both 0.7.2 and 0.8.0 emit package version and chrome contract `1`; archive identities, maintained guidance and hosted Docs remain separate evidence.
75
75
 
76
76
 
77
- ## Host typography and mobile handoff (0.8.0 candidate)
77
+ ## Host typography and mobile handoff (0.8.0)
78
78
 
79
79
  Import either `globals.css` or `theme.css` plus `components.css`, and register the installed `dist` with Tailwind `@source`. Header/footer roots and the actual navigation popover, sheet, account menu and locale menu Content elements carry `data-naeil-chrome` and `lang=locale`; the locale Select Content also carries `data-naeil-locale-menu`. Generic Navigation/Select/DropdownMenu do not acquire these markers. Do not identify chrome by option text or by the presence of a Japanese body option.
80
80
 
81
81
  Marker-and-language-scoped final typography uses a package-local font stack and explicit sizes, weights, line heights, letter spacing and smoothing. It supports a host `@theme` / `@theme inline` with different text tokens/font stacks and unlayered element/role rules at Home's specificity, including `(0,3,0)` `:root[attr] [role="menuitem"]` and Home's `[role="dialog"] p` rules. Host rules can load before or after either supported package CSS entry; owned text declarations outrank them. Rules with `!important` or IDs aimed at chrome are outside this guarantee. Ordinary body Select/menu/dialog typography remains host-owned; custom slot content remains consumer-owned. Font files are still consumer-supplied; Japanese chrome and portals use Noto Sans JP even when the document language differs.
82
82
 
83
- A controlled mobile sheet defers its shared account action until the account menu and sheet finish closing and restore a surviving header control. The consumer still owns dirty state, confirmation, logout, busy state and the request lock; `onLogout` must tolerate this asynchronous handoff. When a confirmation saves its opener, its restoration policy must also handle a viewport change hiding that opener. Verify normal and reduced motion, cancellation after a desktop transition, pending requests, reopen and retry in the actual application; the candidate's unresolved acceptance gates are not a production guarantee.
83
+ A controlled mobile sheet defers its shared account action until the account menu and sheet finish closing and restore a surviving header control. The consumer still owns dirty state, confirmation, logout, busy state and the request lock; `onLogout` must tolerate this asynchronous handoff. When a confirmation saves its opener, its restoration policy must also handle a viewport change hiding that opener. Verify normal and reduced motion, cancellation after a desktop transition, pending requests, reopen and retry in the actual application; package verification does not replace actual product acceptance.
84
84
 
85
85
  The empty-menu desktop exception is intentional B1 compatibility: a zero-width desktop navigation cannot signal the crossing, so the sheet stays open. Escape, its close button, the overlay or a `currentHref` change still close it. Home owns the confirmation's desktop cancel fallback to the visible row account trigger; the package supplies a visible, focusable control and does not implement the confirmation's restoration policy.
86
86
 
@@ -94,3 +94,9 @@ The empty-menu desktop exception is intentional B1 compatibility: a zero-width d
94
94
  ```
95
95
 
96
96
  Keep the default theme/locale/separator/account toolbar for this profile; `toolbarSlot` is a compatibility override, not the Home migration path. Omitting all new props preserves the 0.7.2 canonical profile, apart from markers, language correction, release identity and Radix-generated IDs. SVG/Logo IDs and consumer-supplied IDs are not normalized away. See the `docs/superpowers/specs/2026-10-28-canonical-home-chrome.md` acceptance contract for gates and the four allowed Home differences.
97
+
98
+ ## Page-root frame policy (0.9.0 candidate)
99
+
100
+ Both supported CSS paths obtain the stable root rule and scoped Radix duplicate-compensation correction from `components.css`. Theme-only and JS-only imports do not set document scrolling. Declare `<html data-naeil-scrollbar-gutter="auto">` before first paint to disable both pieces for a custom policy. See [application layout](application-layout.md) for custom-body/scroller/helper-class limits, fallback and the [adopted contract](https://github.com/naeil-dev/naeil-ui/blob/434b7d07235316097ca3a55265ee02c1422b0cfd/docs/superpowers/specs/2026-10-29-page-root-scrollbar-gutter.md) for required probes and capture ordering.
101
+
102
+ Keep frame/toolbar tokens unchanged. The Japanese signed-out canonical login toolbar at 320px is 104px tall under a 15px classic reservation and 44px with overlay. The owner's F3 decision accepts this wrap on naeil.dev itself. Home Workspace uses the shared account control/full footer; its signed-out LoginGate omits the auth slot/separator and footer (F1/F2). Neither Home profile is required to wrap. D1–D4 and the exact F3 auth-composition boundary remain; compare matching roots and auth-slot compositions before applying tolerances.
@@ -0,0 +1,46 @@
1
+ # Owned-service language runtime
2
+
3
+ The opt-in `@naeil/ui/naeil-locale` entries support the registered naeil Next applications. English uses `/`, Korean `/ko`, and Japanese `/ja`, including deep routes. The URL determines the language. Cookie and browser preferences never redirect or overwrite it. The existing public `i18n/routing`, `Nav` and `Footer` APIs retain their previous behavior.
4
+
5
+ Use exact `next-intl@4.14.8` with the shared server/client entries. Next and React remain external peers. Standalone header/footer imports do not require Next or next-intl.
6
+
7
+ ## Entries and ownership
8
+
9
+ | Import | Responsibility |
10
+ | --- | --- |
11
+ | `@naeil/ui/naeil-locale` | Supported locales, strict service/route registry, `localePath`, `localeHref`, legacy receiving aliases and logical-route comparison |
12
+ | `/naeil-locale/server` | `createNaeilLocaleProxy`, `createNaeilRequestConfig`, request access markers and `localeMetadata` |
13
+ | `/naeil-locale/client` | Root `NaeilLocaleController`, locale `NaeilLocaleProvider`, `useLocaleController`, `useLocaleBeforeUnload` |
14
+ | `/naeil-locale/messages` | Selected common/application JSON loading and namespace merge |
15
+ | `/naeil-locale/checker` | Three-language key, nonempty value, ICU syntax and argument compatibility checks |
16
+ | `/naeil-locale/chrome` | Existing header/account/footer presentation using the provider's selected common dictionary |
17
+
18
+ Place the controller and app-owned session/business store in `app/layout.tsx`, above `[locale]`. Place the shared provider in `app/[locale]/layout.tsx` using the validated locale and server-loaded messages. Keep raw input, dialogs, undo, save/run controllers, stream readers and document-departure guards in the root lifetime. Locale remounts must not save, abort, reset, or seed a retained draft from older server props. Translate message keys at the rendering boundary, not inside persistent business state.
19
+
20
+ The shared config sets `localePrefix: "as-needed"`, `localeDetection: false`, `localeCookie: false`, and `alternateLinks: false`. Consumers set Next's `skipProxyUrlNormalize: true` and `skipTrailingSlashRedirect: true`; normalization follows the existing protocol/auth checks. A trusted ingress consumer may pass `requestOrigin: request => existingValidatedRequestUrl(request).origin` to the proxy. This keeps alias redirects and internal rewrites on the validated authority, including local127.0.0.1 deployments. Never derive it from an unvalidated forwarded host.
21
+
22
+ ```tsx
23
+ // app/layout.tsx — inside the existing html/body
24
+ <NaeilLocaleController service="estimate" preferenceWritesEnabled={false}>
25
+ <BusinessStateProvider>{children}</BusinessStateProvider>
26
+ </NaeilLocaleController>
27
+
28
+ // app/[locale]/layout.tsx — validate locale and setRequestLocale on the server
29
+ <NaeilLocaleProvider locale={locale} messages={await getMessages()}>
30
+ {children}
31
+ </NaeilLocaleProvider>
32
+ ```
33
+
34
+ Use `createNaeilRequestConfig({namespace: "estimate", messages: {en: () => import("../../messages/en.json"), ko: () => import("../../messages/ko.json"), ja: () => import("../../messages/ja.json")}})` in the next-intl request config. Every app JSON has exactly its own namespace. The package owns `naeil`; do not copy it into an app. The shared checker is a required build gate. The loader imports only the selected app dictionary and shared dictionary; an observable English recovery handles unexpected runtime omissions. Such omissions still fail acceptance.
35
+
36
+ Render complete paths with `localePath` for same-service navigation and `localeHref` for owned service links. Pass those paths to base Next Link/router or anchors without another locale option. The controller performs `router.replace(href, {scroll: false})`, serializes requests and waits for the matching provider arrival. It restores a stable `data-naeil-focus-id`, input `name`, or `id`. Only a successful explicit selection may write preference; history, prefetch and direct visits do not. Storage failure cannot block a path change.
37
+
38
+ The canonical chrome can be imported from `/naeil-locale/chrome` under the shared provider. Existing `/naeil-header` and `/naeil-footer` remain standalone compatibility adapters and include the small three-language common dictionary. Consumer Link, auth, save guards, frame overrides, desktop/mobile account locks and project data remain consumer-owned.
39
+
40
+ ## Security and rollout
41
+
42
+ The proxy runs exact existing protocol boundaries first, strips external private markers, strictly classifies UI paths, applies the existing app authorizer, and then runs next-intl. Protected entries without an authorizer fail closed. A marker is only a rendering hint: every protected server data read must independently authorize. RSC denial renders a valid localized access boundary and retains the root draft; it must not send login HTML or redirect an in-progress editor to another document. Keep the root beforeunload guard active during RSC failure and access recovery.
43
+
44
+ `localePath` is not an auth-return validator. Keep existing exact return/invite/callback/relay origin and envelope validation. Protocol paths stay outside `[locale]`; localized protocol aliases are rejected. Public metadata advertises only available translations. Protected/auth/error UI is noindex without SEO alternates.
45
+
46
+ Receiver deployment and sending are separate gates. `preferenceWritesEnabled` defaults to false. Before first exposure, validate a separate receiver-compatible fallback or the previous build's safe rejection of new paths. After all required receiver and auth receipts pass, enable the reviewed cross-service links and explicit preference writes. Keep package, source, hosted Docs, app identities, activation times and rollback receipts separate. Home and Baby are outside this migration.