@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,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 |
|