@cocoar/vue-ui 3.1.0 → 3.2.0-beta.6

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 (179) hide show
  1. package/dist/components/checkbox/CoarCheckbox.vue.d.ts +1 -1
  2. package/dist/components/checkbox/CoarCheckbox.vue.d.ts.map +1 -1
  3. package/dist/components/code-block/CoarCodeBlock.vue.d.ts +1 -1
  4. package/dist/components/code-block/CoarCodeBlock.vue.d.ts.map +1 -1
  5. package/dist/components/data-list/CoarDataList.vue.d.ts +158 -0
  6. package/dist/components/data-list/CoarDataList.vue.d.ts.map +1 -0
  7. package/dist/components/data-list/CoarDataListToolbar.vue.d.ts +30 -0
  8. package/dist/components/data-list/CoarDataListToolbar.vue.d.ts.map +1 -0
  9. package/dist/components/data-list/data-list-builder.d.ts +288 -0
  10. package/dist/components/data-list/data-list-builder.d.ts.map +1 -0
  11. package/dist/components/data-list/index.d.ts +12 -0
  12. package/dist/components/data-list/index.d.ts.map +1 -0
  13. package/dist/components/data-list/internal/compare.d.ts +14 -0
  14. package/dist/components/data-list/internal/compare.d.ts.map +1 -0
  15. package/dist/components/data-list/internal/reorder-core.d.ts +46 -0
  16. package/dist/components/data-list/internal/reorder-core.d.ts.map +1 -0
  17. package/dist/components/data-list/internal/search.d.ts +16 -0
  18. package/dist/components/data-list/internal/search.d.ts.map +1 -0
  19. package/dist/components/data-list/internal/useDataListLines.d.ts +92 -0
  20. package/dist/components/data-list/internal/useDataListLines.d.ts.map +1 -0
  21. package/dist/components/data-list/internal/useDataListReorder.d.ts +68 -0
  22. package/dist/components/data-list/internal/useDataListReorder.d.ts.map +1 -0
  23. package/dist/components/data-list/internal/useSearchHighlight.d.ts +19 -0
  24. package/dist/components/data-list/internal/useSearchHighlight.d.ts.map +1 -0
  25. package/dist/components/data-list/types.d.ts +185 -0
  26. package/dist/components/data-list/types.d.ts.map +1 -0
  27. package/dist/components/data-list/useDataListModel.d.ts +79 -0
  28. package/dist/components/data-list/useDataListModel.d.ts.map +1 -0
  29. package/dist/components/date-time/_shared/maskito-config.d.ts +7 -2
  30. package/dist/components/date-time/_shared/maskito-config.d.ts.map +1 -1
  31. package/dist/components/date-time/_shared/time-helpers.d.ts +11 -4
  32. package/dist/components/date-time/_shared/time-helpers.d.ts.map +1 -1
  33. package/dist/components/date-time/_shared/use-date-picker-base.d.ts +1 -1
  34. package/dist/components/date-time/plain-date-picker/CoarPlainDatePicker.vue.d.ts +1 -1
  35. package/dist/components/date-time/plain-date-picker/CoarPlainDatePicker.vue.d.ts.map +1 -1
  36. package/dist/components/date-time/plain-date-time-picker/CoarPlainDateTimePicker.vue.d.ts +1 -1
  37. package/dist/components/date-time/plain-date-time-picker/CoarPlainDateTimePicker.vue.d.ts.map +1 -1
  38. package/dist/components/date-time/zoned-date-time-picker/CoarZonedDateTimePicker.vue.d.ts +1 -1
  39. package/dist/components/date-time/zoned-date-time-picker/CoarZonedDateTimePicker.vue.d.ts.map +1 -1
  40. package/dist/components/dual-listbox/CoarDualListbox.vue.d.ts +3 -0
  41. package/dist/components/dual-listbox/CoarDualListbox.vue.d.ts.map +1 -1
  42. package/dist/components/form-field/CoarFormField.vue.d.ts +1 -1
  43. package/dist/components/form-field/CoarFormField.vue.d.ts.map +1 -1
  44. package/dist/components/icon/CoarIcon.vue.d.ts +2 -2
  45. package/dist/components/icon/CoarIcon.vue.d.ts.map +1 -1
  46. package/dist/components/listbox/CoarListbox.vue.d.ts +7 -0
  47. package/dist/components/listbox/CoarListbox.vue.d.ts.map +1 -1
  48. package/dist/components/number-input/CoarNumberInput.vue.d.ts +1 -1
  49. package/dist/components/number-input/CoarNumberInput.vue.d.ts.map +1 -1
  50. package/dist/components/otp-input/CoarOtpInput.vue.d.ts +1 -1
  51. package/dist/components/otp-input/CoarOtpInput.vue.d.ts.map +1 -1
  52. package/dist/components/password-input/CoarPasswordInput.vue.d.ts +1 -1
  53. package/dist/components/password-input/CoarPasswordInput.vue.d.ts.map +1 -1
  54. package/dist/components/switch/CoarSwitch.vue.d.ts +1 -1
  55. package/dist/components/switch/CoarSwitch.vue.d.ts.map +1 -1
  56. package/dist/components/text-input/CoarTextInput.vue.d.ts +1 -1
  57. package/dist/components/text-input/CoarTextInput.vue.d.ts.map +1 -1
  58. package/dist/composables/dragRegistry.d.ts +12 -0
  59. package/dist/composables/dragRegistry.d.ts.map +1 -1
  60. package/dist/composables/index.d.ts +2 -2
  61. package/dist/composables/index.d.ts.map +1 -1
  62. package/dist/composables/useDragDrop.d.ts +62 -6
  63. package/dist/composables/useDragDrop.d.ts.map +1 -1
  64. package/dist/composables/useVirtualList.d.ts +21 -1
  65. package/dist/composables/useVirtualList.d.ts.map +1 -1
  66. package/dist/index.css +1 -1
  67. package/dist/index.d.ts +4 -2
  68. package/dist/index.d.ts.map +1 -1
  69. package/dist/index.js +4508 -2039
  70. package/package.json +5 -3
  71. package/skills/cocoar-vue-ui/SKILL.md +244 -0
  72. package/skills/cocoar-vue-ui/references/components/avatar.md +178 -0
  73. package/skills/cocoar-vue-ui/references/components/badge.md +290 -0
  74. package/skills/cocoar-vue-ui/references/components/breadcrumb.md +238 -0
  75. package/skills/cocoar-vue-ui/references/components/button.md +252 -0
  76. package/skills/cocoar-vue-ui/references/components/calendar/agenda-view.md +353 -0
  77. package/skills/cocoar-vue-ui/references/components/calendar/coar-calendar.md +1645 -0
  78. package/skills/cocoar-vue-ui/references/components/calendar/day-view.md +361 -0
  79. package/skills/cocoar-vue-ui/references/components/calendar/index.md +312 -0
  80. package/skills/cocoar-vue-ui/references/components/calendar/month-view.md +475 -0
  81. package/skills/cocoar-vue-ui/references/components/calendar/performance.md +186 -0
  82. package/skills/cocoar-vue-ui/references/components/calendar/timeline-view.md +301 -0
  83. package/skills/cocoar-vue-ui/references/components/calendar/week-view.md +382 -0
  84. package/skills/cocoar-vue-ui/references/components/calendar/work-week-view.md +145 -0
  85. package/skills/cocoar-vue-ui/references/components/calendar/year-view.md +63 -0
  86. package/skills/cocoar-vue-ui/references/components/card.md +151 -0
  87. package/skills/cocoar-vue-ui/references/components/checkbox-group.md +103 -0
  88. package/skills/cocoar-vue-ui/references/components/checkbox.md +279 -0
  89. package/skills/cocoar-vue-ui/references/components/code-block.md +241 -0
  90. package/skills/cocoar-vue-ui/references/components/context-menu.md +355 -0
  91. package/skills/cocoar-vue-ui/references/components/data-grid/checkbox.md +213 -0
  92. package/skills/cocoar-vue-ui/references/components/data-grid/date-columns.md +272 -0
  93. package/skills/cocoar-vue-ui/references/components/data-grid/editing.md +236 -0
  94. package/skills/cocoar-vue-ui/references/components/data-grid/multi-select.md +193 -0
  95. package/skills/cocoar-vue-ui/references/components/data-grid/number.md +147 -0
  96. package/skills/cocoar-vue-ui/references/components/data-grid/select.md +202 -0
  97. package/skills/cocoar-vue-ui/references/components/data-grid/text.md +127 -0
  98. package/skills/cocoar-vue-ui/references/components/data-grid.md +1225 -0
  99. package/skills/cocoar-vue-ui/references/components/data-list.md +2121 -0
  100. package/skills/cocoar-vue-ui/references/components/date-or-time-picker.md +200 -0
  101. package/skills/cocoar-vue-ui/references/components/date-picker.md +203 -0
  102. package/skills/cocoar-vue-ui/references/components/date-time-picker.md +154 -0
  103. package/skills/cocoar-vue-ui/references/components/date-views.md +212 -0
  104. package/skills/cocoar-vue-ui/references/components/dialog.md +148 -0
  105. package/skills/cocoar-vue-ui/references/components/divider.md +157 -0
  106. package/skills/cocoar-vue-ui/references/components/document-viewer/annotations.md +400 -0
  107. package/skills/cocoar-vue-ui/references/components/document-viewer/coar-document-viewer.md +327 -0
  108. package/skills/cocoar-vue-ui/references/components/document-viewer/index.md +234 -0
  109. package/skills/cocoar-vue-ui/references/components/document-viewer/toolbar.md +221 -0
  110. package/skills/cocoar-vue-ui/references/components/drag-drop.md +267 -0
  111. package/skills/cocoar-vue-ui/references/components/dual-listbox.md +449 -0
  112. package/skills/cocoar-vue-ui/references/components/file-explorer/asset-store.md +327 -0
  113. package/skills/cocoar-vue-ui/references/components/file-explorer/in-memory-store.md +176 -0
  114. package/skills/cocoar-vue-ui/references/components/file-explorer/index.md +311 -0
  115. package/skills/cocoar-vue-ui/references/components/file-explorer/use-file-explorer.md +300 -0
  116. package/skills/cocoar-vue-ui/references/components/form-field.md +1025 -0
  117. package/skills/cocoar-vue-ui/references/components/fragment-parser.md +298 -0
  118. package/skills/cocoar-vue-ui/references/components/link.md +235 -0
  119. package/skills/cocoar-vue-ui/references/components/listbox.md +575 -0
  120. package/skills/cocoar-vue-ui/references/components/map/editor.md +448 -0
  121. package/skills/cocoar-vue-ui/references/components/map/index.md +351 -0
  122. package/skills/cocoar-vue-ui/references/components/markdown-diagrams.md +210 -0
  123. package/skills/cocoar-vue-ui/references/components/markdown-editor.md +1478 -0
  124. package/skills/cocoar-vue-ui/references/components/markdown-embeds.md +387 -0
  125. package/skills/cocoar-vue-ui/references/components/markdown-form.md +447 -0
  126. package/skills/cocoar-vue-ui/references/components/markdown.md +276 -0
  127. package/skills/cocoar-vue-ui/references/components/menu.md +380 -0
  128. package/skills/cocoar-vue-ui/references/components/mermaid.md +172 -0
  129. package/skills/cocoar-vue-ui/references/components/navbar.md +147 -0
  130. package/skills/cocoar-vue-ui/references/components/note.md +96 -0
  131. package/skills/cocoar-vue-ui/references/components/notice.md +131 -0
  132. package/skills/cocoar-vue-ui/references/components/number-input.md +215 -0
  133. package/skills/cocoar-vue-ui/references/components/otp-input.md +324 -0
  134. package/skills/cocoar-vue-ui/references/components/page-builder/authoring-contract.md +271 -0
  135. package/skills/cocoar-vue-ui/references/components/page-builder/coar-page-builder.md +476 -0
  136. package/skills/cocoar-vue-ui/references/components/page-builder/coar-page-renderer.md +677 -0
  137. package/skills/cocoar-vue-ui/references/components/page-builder/custom-elements.md +441 -0
  138. package/skills/cocoar-vue-ui/references/components/page-builder/idp-integration.md +122 -0
  139. package/skills/cocoar-vue-ui/references/components/page-builder/index.md +573 -0
  140. package/skills/cocoar-vue-ui/references/components/pagination.md +150 -0
  141. package/skills/cocoar-vue-ui/references/components/panel-layout.md +372 -0
  142. package/skills/cocoar-vue-ui/references/components/password-input.md +175 -0
  143. package/skills/cocoar-vue-ui/references/components/popconfirm.md +186 -0
  144. package/skills/cocoar-vue-ui/references/components/popover.md +173 -0
  145. package/skills/cocoar-vue-ui/references/components/progress-bar.md +178 -0
  146. package/skills/cocoar-vue-ui/references/components/radio-group.md +225 -0
  147. package/skills/cocoar-vue-ui/references/components/script-editor.md +1271 -0
  148. package/skills/cocoar-vue-ui/references/components/segmented-control.md +238 -0
  149. package/skills/cocoar-vue-ui/references/components/select.md +463 -0
  150. package/skills/cocoar-vue-ui/references/components/sidebar.md +421 -0
  151. package/skills/cocoar-vue-ui/references/components/spinner.md +132 -0
  152. package/skills/cocoar-vue-ui/references/components/switch.md +195 -0
  153. package/skills/cocoar-vue-ui/references/components/table.md +170 -0
  154. package/skills/cocoar-vue-ui/references/components/tabs.md +382 -0
  155. package/skills/cocoar-vue-ui/references/components/tag.md +178 -0
  156. package/skills/cocoar-vue-ui/references/components/text-input.md +256 -0
  157. package/skills/cocoar-vue-ui/references/components/toast.md +160 -0
  158. package/skills/cocoar-vue-ui/references/components/tooltip.md +121 -0
  159. package/skills/cocoar-vue-ui/references/components/transitions.md +193 -0
  160. package/skills/cocoar-vue-ui/references/components/tree.md +2388 -0
  161. package/skills/cocoar-vue-ui/references/components/virtual-list.md +212 -0
  162. package/skills/cocoar-vue-ui/references/components/wizard.md +251 -0
  163. package/skills/cocoar-vue-ui/references/components/zoned-date-time-picker.md +177 -0
  164. package/skills/cocoar-vue-ui/references/foundations/colors.md +708 -0
  165. package/skills/cocoar-vue-ui/references/foundations/design-principles.md +115 -0
  166. package/skills/cocoar-vue-ui/references/foundations/icons.md +381 -0
  167. package/skills/cocoar-vue-ui/references/foundations/localization/formatting.md +530 -0
  168. package/skills/cocoar-vue-ui/references/foundations/localization/setup.md +86 -0
  169. package/skills/cocoar-vue-ui/references/foundations/localization/timezones.md +182 -0
  170. package/skills/cocoar-vue-ui/references/foundations/localization/translations.md +305 -0
  171. package/skills/cocoar-vue-ui/references/foundations/motion.md +549 -0
  172. package/skills/cocoar-vue-ui/references/foundations/spacing.md +330 -0
  173. package/skills/cocoar-vue-ui/references/foundations/theming.md +140 -0
  174. package/skills/cocoar-vue-ui/references/foundations/typography.md +206 -0
  175. package/skills/cocoar-vue-ui/references/guide/error-handling.md +162 -0
  176. package/skills/cocoar-vue-ui/references/guide/getting-started.md +119 -0
  177. package/skills/cocoar-vue-ui/references/guide/migration-page-builder-3.md +207 -0
  178. package/skills/cocoar-vue-ui/references/guide/migration.md +140 -0
  179. package/skills/cocoar-vue-ui/references/guide/theming.md +98 -0
@@ -0,0 +1,530 @@
1
+ <!-- Generated from apps/docs/foundations/localization/formatting.md by apps/docs/scripts/sync-skill.mjs. Do not edit; edit the docs page. -->
2
+
3
+ # Formatting
4
+
5
+ Locale-aware formatting for numbers, currencies, percentages, and dates. All formatters react to language changes automatically via the `useL10n()` composable from `@cocoar/vue-localization`.
6
+
7
+ ## Number and Currency Formatting
8
+
9
+ Toggle between locales to see how separators, grouping, and currency symbols adapt.
10
+
11
+ **Demo — `localization/demos/NumberFormatter.vue`**
12
+
13
+ ```vue
14
+ <template>
15
+ <div class="demo">
16
+ <div class="locale-switcher">
17
+ <button
18
+ v-for="loc in locales"
19
+ :key="loc.code"
20
+ :class="['locale-btn', { active: currentLocale === loc.code }]"
21
+ @click="switchLocale(loc.code)"
22
+ >
23
+ {{ loc.label }}
24
+ </button>
25
+ </div>
26
+
27
+ <table class="format-table">
28
+ <thead>
29
+ <tr>
30
+ <th>Format</th>
31
+ <th>Input</th>
32
+ <th>Output</th>
33
+ </tr>
34
+ </thead>
35
+ <tbody>
36
+ <tr>
37
+ <td><code>fmtNumber</code></td>
38
+ <td>1234567.89</td>
39
+ <td class="output">{{ fmtNumber(1234567.89) }}</td>
40
+ </tr>
41
+ <tr>
42
+ <td><code>fmtNumber</code> (0 decimals)</td>
43
+ <td>1234567.89</td>
44
+ <td class="output">{{ fmtNumber(1234567.89, 0) }}</td>
45
+ </tr>
46
+ <tr>
47
+ <td><code>fmtCurrency</code></td>
48
+ <td>9999.99</td>
49
+ <td class="output">{{ fmtCurrency(9999.99) }}</td>
50
+ </tr>
51
+ <tr>
52
+ <td><code>fmtCurrency</code> (EUR)</td>
53
+ <td>9999.99</td>
54
+ <td class="output">{{ fmtCurrency(9999.99, 'EUR') }}</td>
55
+ </tr>
56
+ <tr>
57
+ <td><code>fmtPercent</code></td>
58
+ <td>0.256</td>
59
+ <td class="output">{{ fmtPercent(0.256) }}</td>
60
+ </tr>
61
+ <tr>
62
+ <td><code>fmtPercent</code> (2 decimals)</td>
63
+ <td>0.256</td>
64
+ <td class="output">{{ fmtPercent(0.256, 2) }}</td>
65
+ </tr>
66
+ </tbody>
67
+ </table>
68
+
69
+ <p class="locale-note">
70
+ Current language: <code>{{ language }}</code>
71
+ </p>
72
+ </div>
73
+ </template>
74
+
75
+ <script setup lang="ts">
76
+ import { ref, onMounted } from 'vue';
77
+ import { useLocalization, useL10n } from '@cocoar/vue-localization';
78
+
79
+ const service = useLocalization()!;
80
+ const { language, fmtNumber, fmtCurrency, fmtPercent } = useL10n();
81
+
82
+ const locales = [
83
+ { code: 'en', label: 'English' },
84
+ { code: 'de', label: 'Deutsch' },
85
+ { code: 'fr', label: 'Francais' },
86
+ { code: 'ja', label: 'Japanese' },
87
+ ];
88
+
89
+ const currentLocale = ref('en');
90
+
91
+ onMounted(() => {
92
+ service.setLanguage('en');
93
+ });
94
+
95
+ async function switchLocale(code: string) {
96
+ currentLocale.value = code;
97
+ await service.setLanguage(code);
98
+ }
99
+ </script>
100
+
101
+ <style scoped>
102
+ .demo {
103
+ display: flex;
104
+ flex-direction: column;
105
+ gap: 16px;
106
+ }
107
+
108
+ .locale-switcher {
109
+ display: flex;
110
+ gap: 8px;
111
+ flex-wrap: wrap;
112
+ }
113
+
114
+ .locale-btn {
115
+ padding: 6px 14px;
116
+ border: 1px solid var(--coar-border-neutral-primary);
117
+ border-radius: 6px;
118
+ background: var(--coar-bg-neutral-primary);
119
+ color: var(--coar-text-neutral-primary);
120
+ cursor: pointer;
121
+ font-size: 13px;
122
+ transition: all 0.15s ease;
123
+ }
124
+
125
+ .locale-btn:hover {
126
+ border-color: var(--coar-border-accent-primary);
127
+ }
128
+
129
+ .locale-btn.active {
130
+ background: var(--coar-bg-accent-primary);
131
+ color: var(--coar-text-constant-white);
132
+ border-color: var(--coar-bg-accent-primary);
133
+ }
134
+
135
+ .format-table {
136
+ width: 100%;
137
+ border-collapse: collapse;
138
+ font-size: 14px;
139
+ }
140
+
141
+ .format-table th,
142
+ .format-table td {
143
+ text-align: left;
144
+ padding: 8px 12px;
145
+ border-bottom: 1px solid var(--coar-border-neutral-primary);
146
+ }
147
+
148
+ .format-table th {
149
+ font-weight: 600;
150
+ color: var(--coar-text-neutral-secondary);
151
+ font-size: 12px;
152
+ text-transform: uppercase;
153
+ letter-spacing: 0.5px;
154
+ }
155
+
156
+ .output {
157
+ font-family: monospace;
158
+ font-weight: 600;
159
+ color: var(--coar-text-accent-primary);
160
+ }
161
+
162
+ .locale-note {
163
+ font-size: 13px;
164
+ color: var(--coar-text-neutral-secondary);
165
+ margin: 0;
166
+ }
167
+ </style>
168
+ ```
169
+
170
+ ### Usage
171
+
172
+ ```vue
173
+ <script setup lang="ts">
174
+ import { useL10n } from '@cocoar/vue-localization';
175
+
176
+ const { language, fmtNumber, fmtCurrency, fmtPercent } = useL10n();
177
+ </script>
178
+
179
+ <template>
180
+ <p>{{ fmtNumber(1234567.89) }}</p> <!-- "1,234,567.89" in en -->
181
+ <p>{{ fmtNumber(1234567.89, 0) }}</p> <!-- "1,234,568" in en -->
182
+ <p>{{ fmtCurrency(9999.99) }}</p> <!-- "$9,999.99" in en -->
183
+ <p>{{ fmtCurrency(9999.99, 'EUR') }}</p> <!-- "EUR9,999.99" in en -->
184
+ <p>{{ fmtPercent(0.256) }}</p> <!-- "26%" in en -->
185
+ <p>{{ fmtPercent(0.256, 2) }}</p> <!-- "25.60%" in en -->
186
+ </template>
187
+ ```
188
+
189
+ ### `useL10n()` API
190
+
191
+ | Property | Type | Description |
192
+ |----------|------|-------------|
193
+ | `language` | `Ref<string>` | Current language (reactive) |
194
+ | `localeData` | `ComputedRef<CoarLocalizationData \| undefined>` | Full locale data for the current language |
195
+ | `fmtNumber(value, decimals?)` | `(number, number?) => string` | Format a number with locale separators |
196
+ | `fmtCurrency(value, currencyCode?)` | `(number, string?) => string` | Format as currency (defaults to locale's currency) |
197
+ | `fmtPercent(value, decimals?)` | `(number, number?) => string` | Format as percentage (0.25 becomes "25%") |
198
+ | `fmtDate(value, includeTime?)` | `(Date \| string, boolean?) => string` | Format a date (optionally with time) |
199
+
200
+ ## Date Formatting
201
+
202
+ Dates are formatted according to the locale's date pattern. The system detects whether the locale uses `dd/mm/yyyy`, `mm/dd/yyyy`, `dd.mm.yyyy`, or `yyyy-mm-dd` from the browser's `Intl` API. Switch locales to see both the formatted output and the underlying locale metadata.
203
+
204
+ **Demo — `localization/demos/DateFormatter.vue`**
205
+
206
+ ```vue
207
+ <template>
208
+ <div class="demo">
209
+ <div class="locale-switcher">
210
+ <button
211
+ v-for="loc in locales"
212
+ :key="loc.code"
213
+ :class="['locale-btn', { active: currentLocale === loc.code }]"
214
+ @click="switchLocale(loc.code)"
215
+ >
216
+ {{ loc.label }}
217
+ </button>
218
+ </div>
219
+
220
+ <table class="format-table">
221
+ <thead>
222
+ <tr>
223
+ <th>Description</th>
224
+ <th>Output</th>
225
+ </tr>
226
+ </thead>
227
+ <tbody>
228
+ <tr>
229
+ <td>Date only</td>
230
+ <td class="output">{{ fmtDate(sampleDate) }}</td>
231
+ </tr>
232
+ <tr>
233
+ <td>Date + time</td>
234
+ <td class="output">{{ fmtDate(sampleDate, true) }}</td>
235
+ </tr>
236
+ <tr>
237
+ <td>From ISO string</td>
238
+ <td class="output">{{ fmtDate('2025-12-31T23:59:00') }}</td>
239
+ </tr>
240
+ </tbody>
241
+ </table>
242
+
243
+ <div class="locale-details" v-if="localeData">
244
+ <div class="detail-row">
245
+ <span class="detail-label">Pattern:</span>
246
+ <code>{{ localeData.date.pattern }}</code>
247
+ </div>
248
+ <div class="detail-row">
249
+ <span class="detail-label">Decimal separator:</span>
250
+ <code>{{ localeData.number.decimal }}</code>
251
+ </div>
252
+ <div class="detail-row">
253
+ <span class="detail-label">Group separator:</span>
254
+ <code>"{{ localeData.number.group }}"</code>
255
+ </div>
256
+ <div class="detail-row">
257
+ <span class="detail-label">Default currency:</span>
258
+ <code>{{ localeData.currency.default }}</code>
259
+ </div>
260
+ <div class="detail-row">
261
+ <span class="detail-label">First day of week:</span>
262
+ <code>{{ localeData.date.dayNames[localeData.date.firstDayOfWeek] }}</code>
263
+ </div>
264
+ </div>
265
+ </div>
266
+ </template>
267
+
268
+ <script setup lang="ts">
269
+ import { ref, onMounted } from 'vue';
270
+ import { useLocalization, useL10n } from '@cocoar/vue-localization';
271
+
272
+ const service = useLocalization()!;
273
+ const { fmtDate, localeData } = useL10n();
274
+
275
+ const sampleDate = new Date(2025, 2, 22, 14, 30);
276
+
277
+ const locales = [
278
+ { code: 'en', label: 'English' },
279
+ { code: 'de', label: 'Deutsch' },
280
+ { code: 'fr', label: 'Francais' },
281
+ { code: 'ja', label: 'Japanese' },
282
+ ];
283
+
284
+ const currentLocale = ref('en');
285
+
286
+ onMounted(() => {
287
+ service.setLanguage('en');
288
+ });
289
+
290
+ async function switchLocale(code: string) {
291
+ currentLocale.value = code;
292
+ await service.setLanguage(code);
293
+ }
294
+ </script>
295
+
296
+ <style scoped>
297
+ .demo {
298
+ display: flex;
299
+ flex-direction: column;
300
+ gap: 16px;
301
+ }
302
+
303
+ .locale-switcher {
304
+ display: flex;
305
+ gap: 8px;
306
+ flex-wrap: wrap;
307
+ }
308
+
309
+ .locale-btn {
310
+ padding: 6px 14px;
311
+ border: 1px solid var(--coar-border-neutral-primary);
312
+ border-radius: 6px;
313
+ background: var(--coar-bg-neutral-primary);
314
+ color: var(--coar-text-neutral-primary);
315
+ cursor: pointer;
316
+ font-size: 13px;
317
+ transition: all 0.15s ease;
318
+ }
319
+
320
+ .locale-btn:hover {
321
+ border-color: var(--coar-border-accent-primary);
322
+ }
323
+
324
+ .locale-btn.active {
325
+ background: var(--coar-bg-accent-primary);
326
+ color: var(--coar-text-constant-white);
327
+ border-color: var(--coar-bg-accent-primary);
328
+ }
329
+
330
+ .format-table {
331
+ width: 100%;
332
+ border-collapse: collapse;
333
+ font-size: 14px;
334
+ }
335
+
336
+ .format-table th,
337
+ .format-table td {
338
+ text-align: left;
339
+ padding: 8px 12px;
340
+ border-bottom: 1px solid var(--coar-border-neutral-primary);
341
+ }
342
+
343
+ .format-table th {
344
+ font-weight: 600;
345
+ color: var(--coar-text-neutral-secondary);
346
+ font-size: 12px;
347
+ text-transform: uppercase;
348
+ letter-spacing: 0.5px;
349
+ }
350
+
351
+ .output {
352
+ font-family: monospace;
353
+ font-weight: 600;
354
+ color: var(--coar-text-accent-primary);
355
+ }
356
+
357
+ .locale-details {
358
+ display: flex;
359
+ flex-direction: column;
360
+ gap: 6px;
361
+ padding: 12px;
362
+ border-radius: 8px;
363
+ background: var(--coar-bg-neutral-secondary);
364
+ }
365
+
366
+ .detail-row {
367
+ display: flex;
368
+ align-items: center;
369
+ gap: 8px;
370
+ font-size: 13px;
371
+ }
372
+
373
+ .detail-label {
374
+ color: var(--coar-text-neutral-secondary);
375
+ min-width: 140px;
376
+ }
377
+ </style>
378
+ ```
379
+
380
+ ### Usage
381
+
382
+ ```vue
383
+ <script setup lang="ts">
384
+ import { useL10n } from '@cocoar/vue-localization';
385
+
386
+ const { fmtDate } = useL10n();
387
+ </script>
388
+
389
+ <template>
390
+ <p>{{ fmtDate(new Date()) }}</p> <!-- date only -->
391
+ <p>{{ fmtDate(new Date(), true) }}</p> <!-- date + time -->
392
+ <p>{{ fmtDate('2025-12-31T23:59:00') }}</p> <!-- from ISO string -->
393
+ </template>
394
+ ```
395
+
396
+ ## Regional Locales
397
+
398
+ Same language, different region — `en-US` vs `en-GB`, `de-DE` vs `de-AT`, `fr-FR` vs `fr-CH`. The system loads the base locale first, then merges regional overrides on top. This means currency symbols, date patterns, and number formatting automatically adapt to the user's region without duplicating the entire locale definition.
399
+
400
+ **Demo — `localization/demos/RegionalDemo.vue`**
401
+
402
+ ```vue
403
+ <template>
404
+ <div>
405
+ <div style="display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 16px;">
406
+ <button
407
+ v-for="loc in locales"
408
+ :key="loc.code"
409
+ style="padding: 6px 14px; border-radius: 6px; font-size: 13px; cursor: pointer; transition: all 0.15s;"
410
+ :style="{
411
+ background: current === loc.code ? 'var(--coar-background-accent-primary)' : 'var(--coar-background-neutral-secondary)',
412
+ color: current === loc.code ? '#fff' : 'var(--coar-text-neutral-primary)',
413
+ border: 'none',
414
+ }"
415
+ @click="switchTo(loc.code)"
416
+ >
417
+ {{ loc.label }}
418
+ </button>
419
+ </div>
420
+
421
+ <table style="width: 100%; border-collapse: collapse; font-size: 13px;">
422
+ <thead>
423
+ <tr>
424
+ <th style="text-align: left; padding: 8px 12px; border-bottom: 1px solid var(--coar-border-neutral-tertiary); color: var(--coar-text-neutral-tertiary); font-weight: 600; text-transform: uppercase; font-size: 11px;">What</th>
425
+ <th style="text-align: left; padding: 8px 12px; border-bottom: 1px solid var(--coar-border-neutral-tertiary); color: var(--coar-text-neutral-tertiary); font-weight: 600; text-transform: uppercase; font-size: 11px;">Output</th>
426
+ </tr>
427
+ </thead>
428
+ <tbody>
429
+ <tr>
430
+ <td style="padding: 8px 12px; color: var(--coar-text-neutral-secondary);">Number (1234567.89)</td>
431
+ <td style="padding: 8px 12px; font-family: var(--coar-font-family-mono, monospace); color: var(--coar-text-accent-primary);">{{ fmtNumber(1234567.89) }}</td>
432
+ </tr>
433
+ <tr>
434
+ <td style="padding: 8px 12px; color: var(--coar-text-neutral-secondary);">Currency (9999.99)</td>
435
+ <td style="padding: 8px 12px; font-family: var(--coar-font-family-mono, monospace); color: var(--coar-text-accent-primary);">{{ fmtCurrency(9999.99) }}</td>
436
+ </tr>
437
+ <tr>
438
+ <td style="padding: 8px 12px; color: var(--coar-text-neutral-secondary);">Date</td>
439
+ <td style="padding: 8px 12px; font-family: var(--coar-font-family-mono, monospace); color: var(--coar-text-accent-primary);">{{ fmtDate(new Date()) }}</td>
440
+ </tr>
441
+ <tr>
442
+ <td style="padding: 8px 12px; color: var(--coar-text-neutral-secondary);">Date pattern</td>
443
+ <td style="padding: 8px 12px; font-family: var(--coar-font-family-mono, monospace); color: var(--coar-text-accent-primary);">{{ localeData?.date?.pattern || '—' }}</td>
444
+ </tr>
445
+ <tr>
446
+ <td style="padding: 8px 12px; color: var(--coar-text-neutral-secondary);">Default currency</td>
447
+ <td style="padding: 8px 12px; font-family: var(--coar-font-family-mono, monospace); color: var(--coar-text-accent-primary);">{{ localeData?.currency?.default || '—' }}</td>
448
+ </tr>
449
+ <tr>
450
+ <td style="padding: 8px 12px; color: var(--coar-text-neutral-secondary);">Decimal separator</td>
451
+ <td style="padding: 8px 12px; font-family: var(--coar-font-family-mono, monospace); color: var(--coar-text-accent-primary);">{{ localeData?.number?.decimal || '—' }}</td>
452
+ </tr>
453
+ </tbody>
454
+ </table>
455
+
456
+ <p style="margin-top: 12px; font-size: 12px; color: var(--coar-text-neutral-tertiary);">
457
+ Notice how <strong>de-DE</strong> and <strong>de-AT</strong> share the same language but differ in currency (EUR vs EUR with different formatting).
458
+ The system loads the base locale (<code>de</code>) first, then merges regional overrides on top.
459
+ </p>
460
+ </div>
461
+ </template>
462
+
463
+ <script setup lang="ts">
464
+ import { ref, onMounted } from 'vue';
465
+ import { useL10n, useLocalization } from '@cocoar/vue-localization';
466
+
467
+ const service = useLocalization()!;
468
+ const { fmtNumber, fmtCurrency, fmtDate, localeData } = useL10n();
469
+
470
+ const locales = [
471
+ { code: 'en-US', label: 'English (US)' },
472
+ { code: 'en-GB', label: 'English (UK)' },
473
+ { code: 'de-DE', label: 'Deutsch (DE)' },
474
+ { code: 'de-AT', label: 'Deutsch (AT)' },
475
+ { code: 'fr-FR', label: 'Français (FR)' },
476
+ { code: 'fr-CH', label: 'Français (CH)' },
477
+ ];
478
+
479
+ const current = ref('en-US');
480
+
481
+ onMounted(() => {
482
+ service.setLanguage('en-US');
483
+ });
484
+
485
+ async function switchTo(code: string) {
486
+ current.value = code;
487
+ await service.setLanguage(code);
488
+ }
489
+ </script>
490
+ ```
491
+
492
+ ## Locale Data Structure
493
+
494
+ When providing locale data via HTTP (the `l10nUrl` option), the JSON should follow the `CoarLocalizationData` shape:
495
+
496
+ ```json
497
+ {
498
+ "code": "de",
499
+ "date": {
500
+ "pattern": "dd.mm.yyyy",
501
+ "firstDayOfWeek": 1,
502
+ "monthNames": ["Januar", "Februar", "..."],
503
+ "monthNamesShort": ["Jan", "Feb", "..."],
504
+ "dayNames": ["Sonntag", "Montag", "..."],
505
+ "dayNamesShort": ["So", "Mo", "..."],
506
+ "amPm": ["AM", "PM"]
507
+ },
508
+ "number": {
509
+ "decimal": ",",
510
+ "group": ".",
511
+ "grouping": [3]
512
+ },
513
+ "currency": {
514
+ "default": "EUR",
515
+ "symbols": { "EUR": "\u20ac", "USD": "$" },
516
+ "position": "after",
517
+ "spacing": true,
518
+ "decimals": 2
519
+ },
520
+ "percent": {
521
+ "symbol": "%",
522
+ "spacing": true,
523
+ "decimals": 0
524
+ }
525
+ }
526
+ ```
527
+
528
+ > **Tip**
529
+ >
530
+ > You typically do not need to provide locale JSON files. The built-in `IntlLocaleDataSource` derives all of this from the browser's `Intl` API. HTTP sources are useful when you need to override specific values (e.g. custom currency symbols or non-standard grouping).
@@ -0,0 +1,86 @@
1
+ <!-- Generated from apps/docs/foundations/localization/setup.md by apps/docs/scripts/sync-skill.mjs. Do not edit; edit the docs page. -->
2
+
3
+ # Localization Setup
4
+
5
+ A complete localization system for Vue 3 covering locale-aware formatting (l10n), translations (i18n), and timezone detection. Provided by the optional `@cocoar/vue-localization` package.
6
+
7
+ > **Info: Separate Package**
8
+ >
9
+ > The localization system is shipped separately from `@cocoar/vue-ui` to keep the core bundle lean. Install it only when you need it.
10
+ >
11
+ ```bash
12
+ pnpm add @cocoar/vue-localization
13
+ ```
14
+
15
+ ## Setup
16
+
17
+ Register the plugin in your app entry point. The `createCoarLocalization()` factory creates both the plugin and the underlying service instance.
18
+
19
+ ```ts
20
+ // main.ts
21
+ import { createApp } from 'vue';
22
+ import { createCoarLocalization } from '@cocoar/vue-localization';
23
+ import App from './App.vue';
24
+
25
+ const app = createApp(App);
26
+
27
+ app.use(createCoarLocalization({
28
+ defaultLanguage: 'en',
29
+ // Optional: load locale data from your server
30
+ l10nUrl: (lang) => `/locales/${lang}.json`,
31
+ // Optional: load translations from your server
32
+ i18nUrl: (lang) => `/i18n/${lang}.json`,
33
+ }));
34
+
35
+ app.mount('#app');
36
+ ```
37
+
38
+ The `defaultLanguage` is loaded automatically on startup using the browser's `Intl` API as a data source. No JSON files are needed for basic formatting -- the system derives number separators, date patterns, currency symbols, and more directly from `Intl`.
39
+
40
+ ### Configuration Options
41
+
42
+ | Option | Type | Default | Description |
43
+ |--------|------|---------|-------------|
44
+ | `defaultLanguage` | `string` | `'en'` | Initial language code |
45
+ | `l10nUrl` | `(lang: string) => string` | `undefined` | URL builder for locale data JSON (overrides Intl defaults) |
46
+ | `i18nUrl` | `(lang: string) => string` | `undefined` | URL builder for translation JSON |
47
+ | `timezoneProviders` | `CoarTimezoneProvider[]` | `[]` | Custom timezone providers (checked before browser default) |
48
+
49
+ ## Changing Language at Runtime
50
+
51
+ Use the service directly to switch languages. Data is loaded on demand and cached.
52
+
53
+ ```ts
54
+ import { useLocalization } from '@cocoar/vue-localization';
55
+
56
+ const service = useLocalization()!;
57
+
58
+ // Switch language (loads locale data + translations if not cached)
59
+ await service.setLanguage('de');
60
+
61
+ // Preload without switching (useful for instant switching later)
62
+ await service.preloadLanguage('fr');
63
+
64
+ // Force reload from all sources (e.g. after server-side data changes)
65
+ await service.reloadLanguage();
66
+ ```
67
+
68
+ ## Service API
69
+
70
+ The `useLocalization()` composable returns the full `CoarLocalizationService` instance for advanced use cases.
71
+
72
+ | Method / Property | Type | Description |
73
+ |-------------------|------|-------------|
74
+ | `language` | `Ref<string>` | Current language (reactive) |
75
+ | `loading` | `Ref<boolean>` | Whether data is currently being loaded |
76
+ | `localeData` | `ComputedRef<CoarLocalizationData \| undefined>` | Locale data for the current language |
77
+ | `setLanguage(lang)` | `(string) => Promise<void>` | Switch language and load data |
78
+ | `preloadLanguage(lang)` | `(string) => Promise<void>` | Preload data without switching |
79
+ | `reloadLanguage(lang?)` | `(string?) => Promise<void>` | Force reload data from all sources |
80
+ | `t(key, params?, fallback?)` | `(string, Record?, string?) => string` | Translate a key |
81
+ | `getDefaultLanguage()` | `() => string` | Get the configured default language |
82
+ | `i18nStore` | `CoarTranslationStore` | Direct access to the translation store |
83
+ | `l10nStore` | `CoarLocalizationDataStore` | Direct access to the locale data store |
84
+ | `timezoneService` | `CoarTimezoneService` | Direct access to the timezone service |
85
+ | `addLocaleDataSource(source)` | `(CoarLocaleDataSource) => void` | Add a custom locale data source |
86
+ | `addTranslationSource(source)` | `(CoarTranslationSource) => void` | Add a custom translation source |