@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,182 @@
1
+ <!-- Generated from apps/docs/foundations/localization/timezones.md by apps/docs/scripts/sync-skill.mjs. Do not edit; edit the docs page. -->
2
+
3
+ # Timezones
4
+
5
+ The `useTimezone()` composable provides the browser's detected IANA timezone identifier as a reactive ref. The date/time picker components in `@cocoar/vue-ui` use this automatically.
6
+
7
+ **Demo — `localization/demos/TimezoneDemo.vue`**
8
+
9
+ ```vue
10
+ <template>
11
+ <div class="demo">
12
+ <div class="tz-card">
13
+ <div class="tz-row">
14
+ <span class="tz-label">Detected timezone</span>
15
+ <code class="tz-value">{{ timezone }}</code>
16
+ </div>
17
+ <div class="tz-row">
18
+ <span class="tz-label">UTC offset</span>
19
+ <code class="tz-value">{{ utcOffset }}</code>
20
+ </div>
21
+ <div class="tz-row">
22
+ <span class="tz-label">Current time</span>
23
+ <code class="tz-value">{{ currentTime }}</code>
24
+ </div>
25
+ </div>
26
+
27
+ <button class="refresh-btn" @click="onRefresh">
28
+ Refresh timezone
29
+ </button>
30
+
31
+ <p class="tz-note">
32
+ The timezone is auto-detected from your browser using <code>Intl.DateTimeFormat</code>.
33
+ Call <code>refresh()</code> to re-detect if it changes (e.g. VPN or system settings update).
34
+ </p>
35
+ </div>
36
+ </template>
37
+
38
+ <script setup lang="ts">
39
+ import { computed, ref, onMounted, onUnmounted } from 'vue';
40
+ import { useTimezone } from '@cocoar/vue-localization';
41
+
42
+ const { timezone, refresh } = useTimezone();
43
+
44
+ const now = ref(new Date());
45
+ let timer: ReturnType<typeof setInterval>;
46
+
47
+ onMounted(() => {
48
+ timer = setInterval(() => {
49
+ now.value = new Date();
50
+ }, 1000);
51
+ });
52
+
53
+ onUnmounted(() => {
54
+ clearInterval(timer);
55
+ });
56
+
57
+ const utcOffset = computed(() => {
58
+ const offset = now.value.getTimezoneOffset();
59
+ const sign = offset <= 0 ? '+' : '-';
60
+ const hours = String(Math.floor(Math.abs(offset) / 60)).padStart(2, '0');
61
+ const mins = String(Math.abs(offset) % 60).padStart(2, '0');
62
+ return `UTC${sign}${hours}:${mins}`;
63
+ });
64
+
65
+ const currentTime = computed(() => {
66
+ return now.value.toLocaleTimeString('en-GB', {
67
+ timeZone: timezone.value,
68
+ hour: '2-digit',
69
+ minute: '2-digit',
70
+ second: '2-digit',
71
+ });
72
+ });
73
+
74
+ function onRefresh() {
75
+ refresh();
76
+ }
77
+ </script>
78
+
79
+ <style scoped>
80
+ .demo {
81
+ display: flex;
82
+ flex-direction: column;
83
+ gap: 16px;
84
+ }
85
+
86
+ .tz-card {
87
+ display: flex;
88
+ flex-direction: column;
89
+ gap: 8px;
90
+ padding: 16px;
91
+ border-radius: 8px;
92
+ border: 1px solid var(--coar-border-neutral-primary);
93
+ background: var(--coar-bg-neutral-primary);
94
+ }
95
+
96
+ .tz-row {
97
+ display: flex;
98
+ align-items: center;
99
+ justify-content: space-between;
100
+ gap: 16px;
101
+ }
102
+
103
+ .tz-label {
104
+ font-size: 14px;
105
+ color: var(--coar-text-neutral-secondary);
106
+ }
107
+
108
+ .tz-value {
109
+ font-size: 14px;
110
+ font-weight: 600;
111
+ color: var(--coar-text-accent-primary);
112
+ }
113
+
114
+ .refresh-btn {
115
+ align-self: flex-start;
116
+ padding: 6px 14px;
117
+ border: 1px solid var(--coar-border-neutral-primary);
118
+ border-radius: 6px;
119
+ background: var(--coar-bg-neutral-primary);
120
+ color: var(--coar-text-neutral-primary);
121
+ cursor: pointer;
122
+ font-size: 13px;
123
+ transition: all 0.15s ease;
124
+ }
125
+
126
+ .refresh-btn:hover {
127
+ border-color: var(--coar-border-accent-primary);
128
+ color: var(--coar-text-accent-primary);
129
+ }
130
+
131
+ .tz-note {
132
+ font-size: 13px;
133
+ color: var(--coar-text-neutral-tertiary);
134
+ margin: 0;
135
+ line-height: 1.5;
136
+ }
137
+ </style>
138
+ ```
139
+
140
+ ## Usage
141
+
142
+ ```vue
143
+ <script setup lang="ts">
144
+ import { useTimezone } from '@cocoar/vue-localization';
145
+
146
+ const { timezone, refresh } = useTimezone();
147
+
148
+ // timezone.value → "Europe/Berlin", "America/New_York", etc.
149
+ </script>
150
+
151
+ <template>
152
+ <p>Your timezone: {{ timezone }}</p>
153
+ <button @click="refresh()">Re-detect</button>
154
+ </template>
155
+ ```
156
+
157
+ ## Custom Timezone Providers
158
+
159
+ You can supply custom providers (e.g. from a user profile API) that are checked before the browser default.
160
+
161
+ ```ts
162
+ import { createCoarLocalization } from '@cocoar/vue-localization';
163
+ import type { CoarTimezoneProvider } from '@cocoar/vue-localization';
164
+
165
+ const userTimezone: CoarTimezoneProvider = {
166
+ getTimezone() {
167
+ // Return from user settings, or null to defer to next provider
168
+ return userStore.timezone ?? null;
169
+ },
170
+ };
171
+
172
+ app.use(createCoarLocalization({
173
+ timezoneProviders: [userTimezone],
174
+ }));
175
+ ```
176
+
177
+ ## `useTimezone()` API
178
+
179
+ | Property | Type | Description |
180
+ |----------|------|-------------|
181
+ | `timezone` | `Ref<string>` | Current IANA timezone identifier (reactive) |
182
+ | `refresh()` | `() => void` | Re-resolve timezone from providers |
@@ -0,0 +1,305 @@
1
+ <!-- Generated from apps/docs/foundations/localization/translations.md by apps/docs/scripts/sync-skill.mjs. Do not edit; edit the docs page. -->
2
+
3
+ # Translations
4
+
5
+ The `useI18n()` composable provides translation lookup with parameter interpolation. Translations can come from HTTP sources (configured via `i18nUrl`) or be registered directly in code using the service's `i18nStore`.
6
+
7
+ Parameters use `{name}` syntax and are replaced at runtime. Nested translation objects are automatically flattened to dot-separated keys.
8
+
9
+ **Demo — `localization/demos/TranslationDemo.vue`**
10
+
11
+ ```vue
12
+ <template>
13
+ <div class="demo">
14
+ <div class="locale-switcher">
15
+ <button
16
+ v-for="loc in locales"
17
+ :key="loc.code"
18
+ :class="['locale-btn', { active: currentLocale === loc.code }]"
19
+ @click="switchLocale(loc.code)"
20
+ >
21
+ {{ loc.label }}
22
+ </button>
23
+ </div>
24
+
25
+ <div class="translated-ui">
26
+ <div class="greeting">{{ t('greeting', { name: 'Alice' }) }}</div>
27
+ <div class="message">{{ t('items.count', { count: 3 }) }}</div>
28
+ <div class="button-row">
29
+ <button class="action-btn primary">{{ t('actions.save') }}</button>
30
+ <button class="action-btn secondary">{{ t('actions.cancel') }}</button>
31
+ <button class="action-btn danger">{{ t('actions.delete') }}</button>
32
+ </div>
33
+ <div class="status">{{ t('status.lastSaved', { time: '14:30' }) }}</div>
34
+ </div>
35
+
36
+ <div class="code-hint">
37
+ <code>t('greeting', { name: 'Alice' })</code>
38
+ <span class="arrow">&rarr;</span>
39
+ <code class="result">{{ t('greeting', { name: 'Alice' }) }}</code>
40
+ </div>
41
+ </div>
42
+ </template>
43
+
44
+ <script setup lang="ts">
45
+ import { ref, onMounted } from 'vue';
46
+ import { useLocalization, useI18n } from '@cocoar/vue-localization';
47
+
48
+ const service = useLocalization()!;
49
+ const { t } = useI18n();
50
+
51
+ const locales = [
52
+ { code: 'en', label: 'English' },
53
+ { code: 'de', label: 'Deutsch' },
54
+ { code: 'fr', label: 'Francais' },
55
+ { code: 'es', label: 'Espanol' },
56
+ ];
57
+
58
+ const currentLocale = ref('en');
59
+
60
+ // Register inline translations for the demo
61
+ onMounted(() => {
62
+ service.i18nStore.setTranslations('en', {
63
+ greeting: 'Hello, {name}!',
64
+ items: { count: 'You have {count} items in your cart.' },
65
+ actions: { save: 'Save', cancel: 'Cancel', delete: 'Delete' },
66
+ status: { lastSaved: 'Last saved at {time}' },
67
+ });
68
+ service.i18nStore.setTranslations('de', {
69
+ greeting: 'Hallo, {name}!',
70
+ items: { count: 'Sie haben {count} Artikel im Warenkorb.' },
71
+ actions: { save: 'Speichern', cancel: 'Abbrechen', delete: 'Loeschen' },
72
+ status: { lastSaved: 'Zuletzt gespeichert um {time}' },
73
+ });
74
+ service.i18nStore.setTranslations('fr', {
75
+ greeting: 'Bonjour, {name} !',
76
+ items: { count: 'Vous avez {count} articles dans votre panier.' },
77
+ actions: { save: 'Enregistrer', cancel: 'Annuler', delete: 'Supprimer' },
78
+ status: { lastSaved: 'Derniere sauvegarde a {time}' },
79
+ });
80
+ service.i18nStore.setTranslations('es', {
81
+ greeting: 'Hola, {name}!',
82
+ items: { count: 'Tienes {count} articulos en tu carrito.' },
83
+ actions: { save: 'Guardar', cancel: 'Cancelar', delete: 'Eliminar' },
84
+ status: { lastSaved: 'Guardado por ultima vez a las {time}' },
85
+ });
86
+ });
87
+
88
+ async function switchLocale(code: string) {
89
+ currentLocale.value = code;
90
+ await service.setLanguage(code);
91
+ }
92
+ </script>
93
+
94
+ <style scoped>
95
+ .demo {
96
+ display: flex;
97
+ flex-direction: column;
98
+ gap: 16px;
99
+ }
100
+
101
+ .locale-switcher {
102
+ display: flex;
103
+ gap: 8px;
104
+ flex-wrap: wrap;
105
+ }
106
+
107
+ .locale-btn {
108
+ padding: 6px 14px;
109
+ border: 1px solid var(--coar-border-neutral-primary);
110
+ border-radius: 6px;
111
+ background: var(--coar-bg-neutral-primary);
112
+ color: var(--coar-text-neutral-primary);
113
+ cursor: pointer;
114
+ font-size: 13px;
115
+ transition: all 0.15s ease;
116
+ }
117
+
118
+ .locale-btn:hover {
119
+ border-color: var(--coar-border-accent-primary);
120
+ }
121
+
122
+ .locale-btn.active {
123
+ background: var(--coar-bg-accent-primary);
124
+ color: var(--coar-text-constant-white);
125
+ border-color: var(--coar-bg-accent-primary);
126
+ }
127
+
128
+ .translated-ui {
129
+ display: flex;
130
+ flex-direction: column;
131
+ gap: 12px;
132
+ padding: 16px;
133
+ border-radius: 8px;
134
+ border: 1px solid var(--coar-border-neutral-primary);
135
+ background: var(--coar-bg-neutral-primary);
136
+ }
137
+
138
+ .greeting {
139
+ font-size: 18px;
140
+ font-weight: 600;
141
+ color: var(--coar-text-neutral-primary);
142
+ }
143
+
144
+ .message {
145
+ font-size: 14px;
146
+ color: var(--coar-text-neutral-secondary);
147
+ }
148
+
149
+ .button-row {
150
+ display: flex;
151
+ gap: 8px;
152
+ }
153
+
154
+ .action-btn {
155
+ padding: 6px 16px;
156
+ border-radius: 6px;
157
+ border: none;
158
+ cursor: pointer;
159
+ font-size: 13px;
160
+ font-weight: 500;
161
+ }
162
+
163
+ .action-btn.primary {
164
+ background: var(--coar-bg-accent-primary);
165
+ color: var(--coar-text-constant-white);
166
+ }
167
+
168
+ .action-btn.secondary {
169
+ background: var(--coar-bg-neutral-secondary);
170
+ color: var(--coar-text-neutral-primary);
171
+ }
172
+
173
+ .action-btn.danger {
174
+ background: var(--coar-bg-error-primary);
175
+ color: var(--coar-text-constant-white);
176
+ }
177
+
178
+ .status {
179
+ font-size: 12px;
180
+ color: var(--coar-text-neutral-tertiary);
181
+ font-style: italic;
182
+ }
183
+
184
+ .code-hint {
185
+ display: flex;
186
+ align-items: center;
187
+ gap: 8px;
188
+ font-size: 13px;
189
+ flex-wrap: wrap;
190
+ }
191
+
192
+ .arrow {
193
+ color: var(--coar-text-neutral-tertiary);
194
+ }
195
+
196
+ .result {
197
+ color: var(--coar-text-accent-primary);
198
+ font-weight: 600;
199
+ }
200
+ </style>
201
+ ```
202
+
203
+ ## Usage
204
+
205
+ ```vue
206
+ <script setup lang="ts">
207
+ import { useI18n } from '@cocoar/vue-localization';
208
+
209
+ const { t, tRef, language } = useI18n();
210
+
211
+ // Immediate value (call in template for reactivity)
212
+ // t('greeting', { name: 'Alice' }) → "Hello, Alice!"
213
+
214
+ // Computed ref (reacts to language changes automatically)
215
+ const title = tRef('app.title');
216
+ </script>
217
+
218
+ <template>
219
+ <h1>{{ title }}</h1>
220
+ <p>{{ t('greeting', { name: 'Alice' }) }}</p>
221
+ <p>{{ t('missing.key', {}, 'Fallback text') }}</p>
222
+ </template>
223
+ ```
224
+
225
+ ## Registering Translations in Code
226
+
227
+ For cases where HTTP loading is not appropriate (tests, demos, embedded apps), you can register translations directly on the service.
228
+
229
+ ```ts
230
+ import { useLocalization } from '@cocoar/vue-localization';
231
+
232
+ const service = useLocalization()!;
233
+
234
+ service.i18nStore.setTranslations('en', {
235
+ greeting: 'Hello, {name}!',
236
+ actions: {
237
+ save: 'Save',
238
+ cancel: 'Cancel',
239
+ },
240
+ });
241
+
242
+ service.i18nStore.setTranslations('de', {
243
+ greeting: 'Hallo, {name}!',
244
+ actions: {
245
+ save: 'Speichern',
246
+ cancel: 'Abbrechen',
247
+ },
248
+ });
249
+ ```
250
+
251
+ Nested keys are automatically flattened: `actions.save` resolves `'Save'` for English.
252
+
253
+ ## `useI18n()` API
254
+
255
+ | Property | Type | Description |
256
+ |----------|------|-------------|
257
+ | `language` | `Ref<string>` | Current language (reactive) |
258
+ | `t(key, params?, fallback?)` | `(string, Record?, string?) => string` | Translate a key with optional interpolation |
259
+ | `tRef(key, params?, fallback?)` | `(string, Record?, string?) => ComputedRef<string>` | Computed translation that reacts to language changes |
260
+
261
+ ## Translation Fallback Behavior
262
+
263
+ 1. Look up the key in the current language (e.g. `de-AT`)
264
+ 2. If not found and locale is regional, try the base language (`de`)
265
+ 3. If still not found, use the provided `fallback` argument
266
+ 4. If no fallback, return the key itself
267
+
268
+ ## Translating Component Strings
269
+
270
+ All built-in text in Cocoar UI components -- `aria-label`s, button labels, empty state messages, screen-reader announcements -- defaults to English and can be translated by providing a `coar.ui.*` namespace in your translation JSON.
271
+
272
+ If the localization plugin is **not** installed, every string falls back to its English default automatically. Nothing breaks, nothing needs to be configured.
273
+
274
+ ```
275
+ Plugin not installed → English fallbacks (default)
276
+ Plugin installed → Translated strings, if the key exists in your JSON
277
+ Plugin installed, key missing → English fallback
278
+ ```
279
+
280
+ Your translation file only needs to contain the keys you want to override:
281
+
282
+ ```json
283
+ {
284
+ "coar": {
285
+ "ui": {
286
+ "dialog": { "dialog": "Dialog", "close": "Schließen" },
287
+ "select": { "noResults": "Keine Ergebnisse", "noOptions": "Keine Optionen verfügbar" },
288
+ "datePicker": {
289
+ "dialog": "Datumsauswahl",
290
+ "clearDate": "Datum löschen",
291
+ "previousYear": "Vorheriges Jahr",
292
+ "nextYear": "Nächstes Jahr",
293
+ "months": "Monate"
294
+ },
295
+ "toast": { "dismiss": "Benachrichtigung schließen" }
296
+ }
297
+ }
298
+ }
299
+ ```
300
+
301
+ Each component's documentation page lists its translatable keys under an **i18n Keys** section. Check the component page you're working with for the exact keys available.
302
+
303
+ > **Tip: Props as alternative**
304
+ >
305
+ > If you only need to change a single string in one place, some components offer direct props -- for example `CoarSpinner` has a `label` prop, `CoarPopconfirm` has `confirmText` and `cancelText`. Check the component's **Props** table first before reaching for i18n.