@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.
- package/dist/components/checkbox/CoarCheckbox.vue.d.ts +1 -1
- package/dist/components/checkbox/CoarCheckbox.vue.d.ts.map +1 -1
- package/dist/components/code-block/CoarCodeBlock.vue.d.ts +1 -1
- package/dist/components/code-block/CoarCodeBlock.vue.d.ts.map +1 -1
- package/dist/components/data-list/CoarDataList.vue.d.ts +158 -0
- package/dist/components/data-list/CoarDataList.vue.d.ts.map +1 -0
- package/dist/components/data-list/CoarDataListToolbar.vue.d.ts +30 -0
- package/dist/components/data-list/CoarDataListToolbar.vue.d.ts.map +1 -0
- package/dist/components/data-list/data-list-builder.d.ts +288 -0
- package/dist/components/data-list/data-list-builder.d.ts.map +1 -0
- package/dist/components/data-list/index.d.ts +12 -0
- package/dist/components/data-list/index.d.ts.map +1 -0
- package/dist/components/data-list/internal/compare.d.ts +14 -0
- package/dist/components/data-list/internal/compare.d.ts.map +1 -0
- package/dist/components/data-list/internal/reorder-core.d.ts +46 -0
- package/dist/components/data-list/internal/reorder-core.d.ts.map +1 -0
- package/dist/components/data-list/internal/search.d.ts +16 -0
- package/dist/components/data-list/internal/search.d.ts.map +1 -0
- package/dist/components/data-list/internal/useDataListLines.d.ts +92 -0
- package/dist/components/data-list/internal/useDataListLines.d.ts.map +1 -0
- package/dist/components/data-list/internal/useDataListReorder.d.ts +68 -0
- package/dist/components/data-list/internal/useDataListReorder.d.ts.map +1 -0
- package/dist/components/data-list/internal/useSearchHighlight.d.ts +19 -0
- package/dist/components/data-list/internal/useSearchHighlight.d.ts.map +1 -0
- package/dist/components/data-list/types.d.ts +185 -0
- package/dist/components/data-list/types.d.ts.map +1 -0
- package/dist/components/data-list/useDataListModel.d.ts +79 -0
- package/dist/components/data-list/useDataListModel.d.ts.map +1 -0
- package/dist/components/date-time/_shared/maskito-config.d.ts +7 -2
- package/dist/components/date-time/_shared/maskito-config.d.ts.map +1 -1
- package/dist/components/date-time/_shared/time-helpers.d.ts +11 -4
- package/dist/components/date-time/_shared/time-helpers.d.ts.map +1 -1
- package/dist/components/date-time/_shared/use-date-picker-base.d.ts +1 -1
- package/dist/components/date-time/plain-date-picker/CoarPlainDatePicker.vue.d.ts +1 -1
- package/dist/components/date-time/plain-date-picker/CoarPlainDatePicker.vue.d.ts.map +1 -1
- package/dist/components/date-time/plain-date-time-picker/CoarPlainDateTimePicker.vue.d.ts +1 -1
- package/dist/components/date-time/plain-date-time-picker/CoarPlainDateTimePicker.vue.d.ts.map +1 -1
- package/dist/components/date-time/zoned-date-time-picker/CoarZonedDateTimePicker.vue.d.ts +1 -1
- package/dist/components/date-time/zoned-date-time-picker/CoarZonedDateTimePicker.vue.d.ts.map +1 -1
- package/dist/components/dual-listbox/CoarDualListbox.vue.d.ts +3 -0
- package/dist/components/dual-listbox/CoarDualListbox.vue.d.ts.map +1 -1
- package/dist/components/form-field/CoarFormField.vue.d.ts +1 -1
- package/dist/components/form-field/CoarFormField.vue.d.ts.map +1 -1
- package/dist/components/icon/CoarIcon.vue.d.ts +2 -2
- package/dist/components/icon/CoarIcon.vue.d.ts.map +1 -1
- package/dist/components/listbox/CoarListbox.vue.d.ts +7 -0
- package/dist/components/listbox/CoarListbox.vue.d.ts.map +1 -1
- package/dist/components/number-input/CoarNumberInput.vue.d.ts +1 -1
- package/dist/components/number-input/CoarNumberInput.vue.d.ts.map +1 -1
- package/dist/components/otp-input/CoarOtpInput.vue.d.ts +1 -1
- package/dist/components/otp-input/CoarOtpInput.vue.d.ts.map +1 -1
- package/dist/components/password-input/CoarPasswordInput.vue.d.ts +1 -1
- package/dist/components/password-input/CoarPasswordInput.vue.d.ts.map +1 -1
- package/dist/components/switch/CoarSwitch.vue.d.ts +1 -1
- package/dist/components/switch/CoarSwitch.vue.d.ts.map +1 -1
- package/dist/components/text-input/CoarTextInput.vue.d.ts +1 -1
- package/dist/components/text-input/CoarTextInput.vue.d.ts.map +1 -1
- package/dist/composables/dragRegistry.d.ts +12 -0
- package/dist/composables/dragRegistry.d.ts.map +1 -1
- package/dist/composables/index.d.ts +2 -2
- package/dist/composables/index.d.ts.map +1 -1
- package/dist/composables/useDragDrop.d.ts +62 -6
- package/dist/composables/useDragDrop.d.ts.map +1 -1
- package/dist/composables/useVirtualList.d.ts +21 -1
- package/dist/composables/useVirtualList.d.ts.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4508 -2039
- package/package.json +5 -3
- package/skills/cocoar-vue-ui/SKILL.md +244 -0
- package/skills/cocoar-vue-ui/references/components/avatar.md +178 -0
- package/skills/cocoar-vue-ui/references/components/badge.md +290 -0
- package/skills/cocoar-vue-ui/references/components/breadcrumb.md +238 -0
- package/skills/cocoar-vue-ui/references/components/button.md +252 -0
- package/skills/cocoar-vue-ui/references/components/calendar/agenda-view.md +353 -0
- package/skills/cocoar-vue-ui/references/components/calendar/coar-calendar.md +1645 -0
- package/skills/cocoar-vue-ui/references/components/calendar/day-view.md +361 -0
- package/skills/cocoar-vue-ui/references/components/calendar/index.md +312 -0
- package/skills/cocoar-vue-ui/references/components/calendar/month-view.md +475 -0
- package/skills/cocoar-vue-ui/references/components/calendar/performance.md +186 -0
- package/skills/cocoar-vue-ui/references/components/calendar/timeline-view.md +301 -0
- package/skills/cocoar-vue-ui/references/components/calendar/week-view.md +382 -0
- package/skills/cocoar-vue-ui/references/components/calendar/work-week-view.md +145 -0
- package/skills/cocoar-vue-ui/references/components/calendar/year-view.md +63 -0
- package/skills/cocoar-vue-ui/references/components/card.md +151 -0
- package/skills/cocoar-vue-ui/references/components/checkbox-group.md +103 -0
- package/skills/cocoar-vue-ui/references/components/checkbox.md +279 -0
- package/skills/cocoar-vue-ui/references/components/code-block.md +241 -0
- package/skills/cocoar-vue-ui/references/components/context-menu.md +355 -0
- package/skills/cocoar-vue-ui/references/components/data-grid/checkbox.md +213 -0
- package/skills/cocoar-vue-ui/references/components/data-grid/date-columns.md +272 -0
- package/skills/cocoar-vue-ui/references/components/data-grid/editing.md +236 -0
- package/skills/cocoar-vue-ui/references/components/data-grid/multi-select.md +193 -0
- package/skills/cocoar-vue-ui/references/components/data-grid/number.md +147 -0
- package/skills/cocoar-vue-ui/references/components/data-grid/select.md +202 -0
- package/skills/cocoar-vue-ui/references/components/data-grid/text.md +127 -0
- package/skills/cocoar-vue-ui/references/components/data-grid.md +1225 -0
- package/skills/cocoar-vue-ui/references/components/data-list.md +2121 -0
- package/skills/cocoar-vue-ui/references/components/date-or-time-picker.md +200 -0
- package/skills/cocoar-vue-ui/references/components/date-picker.md +203 -0
- package/skills/cocoar-vue-ui/references/components/date-time-picker.md +154 -0
- package/skills/cocoar-vue-ui/references/components/date-views.md +212 -0
- package/skills/cocoar-vue-ui/references/components/dialog.md +148 -0
- package/skills/cocoar-vue-ui/references/components/divider.md +157 -0
- package/skills/cocoar-vue-ui/references/components/document-viewer/annotations.md +400 -0
- package/skills/cocoar-vue-ui/references/components/document-viewer/coar-document-viewer.md +327 -0
- package/skills/cocoar-vue-ui/references/components/document-viewer/index.md +234 -0
- package/skills/cocoar-vue-ui/references/components/document-viewer/toolbar.md +221 -0
- package/skills/cocoar-vue-ui/references/components/drag-drop.md +267 -0
- package/skills/cocoar-vue-ui/references/components/dual-listbox.md +449 -0
- package/skills/cocoar-vue-ui/references/components/file-explorer/asset-store.md +327 -0
- package/skills/cocoar-vue-ui/references/components/file-explorer/in-memory-store.md +176 -0
- package/skills/cocoar-vue-ui/references/components/file-explorer/index.md +311 -0
- package/skills/cocoar-vue-ui/references/components/file-explorer/use-file-explorer.md +300 -0
- package/skills/cocoar-vue-ui/references/components/form-field.md +1025 -0
- package/skills/cocoar-vue-ui/references/components/fragment-parser.md +298 -0
- package/skills/cocoar-vue-ui/references/components/link.md +235 -0
- package/skills/cocoar-vue-ui/references/components/listbox.md +575 -0
- package/skills/cocoar-vue-ui/references/components/map/editor.md +448 -0
- package/skills/cocoar-vue-ui/references/components/map/index.md +351 -0
- package/skills/cocoar-vue-ui/references/components/markdown-diagrams.md +210 -0
- package/skills/cocoar-vue-ui/references/components/markdown-editor.md +1478 -0
- package/skills/cocoar-vue-ui/references/components/markdown-embeds.md +387 -0
- package/skills/cocoar-vue-ui/references/components/markdown-form.md +447 -0
- package/skills/cocoar-vue-ui/references/components/markdown.md +276 -0
- package/skills/cocoar-vue-ui/references/components/menu.md +380 -0
- package/skills/cocoar-vue-ui/references/components/mermaid.md +172 -0
- package/skills/cocoar-vue-ui/references/components/navbar.md +147 -0
- package/skills/cocoar-vue-ui/references/components/note.md +96 -0
- package/skills/cocoar-vue-ui/references/components/notice.md +131 -0
- package/skills/cocoar-vue-ui/references/components/number-input.md +215 -0
- package/skills/cocoar-vue-ui/references/components/otp-input.md +324 -0
- package/skills/cocoar-vue-ui/references/components/page-builder/authoring-contract.md +271 -0
- package/skills/cocoar-vue-ui/references/components/page-builder/coar-page-builder.md +476 -0
- package/skills/cocoar-vue-ui/references/components/page-builder/coar-page-renderer.md +677 -0
- package/skills/cocoar-vue-ui/references/components/page-builder/custom-elements.md +441 -0
- package/skills/cocoar-vue-ui/references/components/page-builder/idp-integration.md +122 -0
- package/skills/cocoar-vue-ui/references/components/page-builder/index.md +573 -0
- package/skills/cocoar-vue-ui/references/components/pagination.md +150 -0
- package/skills/cocoar-vue-ui/references/components/panel-layout.md +372 -0
- package/skills/cocoar-vue-ui/references/components/password-input.md +175 -0
- package/skills/cocoar-vue-ui/references/components/popconfirm.md +186 -0
- package/skills/cocoar-vue-ui/references/components/popover.md +173 -0
- package/skills/cocoar-vue-ui/references/components/progress-bar.md +178 -0
- package/skills/cocoar-vue-ui/references/components/radio-group.md +225 -0
- package/skills/cocoar-vue-ui/references/components/script-editor.md +1271 -0
- package/skills/cocoar-vue-ui/references/components/segmented-control.md +238 -0
- package/skills/cocoar-vue-ui/references/components/select.md +463 -0
- package/skills/cocoar-vue-ui/references/components/sidebar.md +421 -0
- package/skills/cocoar-vue-ui/references/components/spinner.md +132 -0
- package/skills/cocoar-vue-ui/references/components/switch.md +195 -0
- package/skills/cocoar-vue-ui/references/components/table.md +170 -0
- package/skills/cocoar-vue-ui/references/components/tabs.md +382 -0
- package/skills/cocoar-vue-ui/references/components/tag.md +178 -0
- package/skills/cocoar-vue-ui/references/components/text-input.md +256 -0
- package/skills/cocoar-vue-ui/references/components/toast.md +160 -0
- package/skills/cocoar-vue-ui/references/components/tooltip.md +121 -0
- package/skills/cocoar-vue-ui/references/components/transitions.md +193 -0
- package/skills/cocoar-vue-ui/references/components/tree.md +2388 -0
- package/skills/cocoar-vue-ui/references/components/virtual-list.md +212 -0
- package/skills/cocoar-vue-ui/references/components/wizard.md +251 -0
- package/skills/cocoar-vue-ui/references/components/zoned-date-time-picker.md +177 -0
- package/skills/cocoar-vue-ui/references/foundations/colors.md +708 -0
- package/skills/cocoar-vue-ui/references/foundations/design-principles.md +115 -0
- package/skills/cocoar-vue-ui/references/foundations/icons.md +381 -0
- package/skills/cocoar-vue-ui/references/foundations/localization/formatting.md +530 -0
- package/skills/cocoar-vue-ui/references/foundations/localization/setup.md +86 -0
- package/skills/cocoar-vue-ui/references/foundations/localization/timezones.md +182 -0
- package/skills/cocoar-vue-ui/references/foundations/localization/translations.md +305 -0
- package/skills/cocoar-vue-ui/references/foundations/motion.md +549 -0
- package/skills/cocoar-vue-ui/references/foundations/spacing.md +330 -0
- package/skills/cocoar-vue-ui/references/foundations/theming.md +140 -0
- package/skills/cocoar-vue-ui/references/foundations/typography.md +206 -0
- package/skills/cocoar-vue-ui/references/guide/error-handling.md +162 -0
- package/skills/cocoar-vue-ui/references/guide/getting-started.md +119 -0
- package/skills/cocoar-vue-ui/references/guide/migration-page-builder-3.md +207 -0
- package/skills/cocoar-vue-ui/references/guide/migration.md +140 -0
- 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">→</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.
|