Skip to content

Commit 556a4d9

Browse files
authored
Merge pull request #23 from LibreCodeCoop/refactor/inline-t-in-vue-components
refactor(vue): inline t() directly, remove const label intermediaries
2 parents 7d1f9ea + 870f4b7 commit 556a4d9

8 files changed

Lines changed: 245 additions & 162 deletions

File tree

src/components/AdminSupportBanner.vue

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,27 @@ SPDX-License-Identifier: AGPL-3.0-or-later
77
<NcNoteCard v-if="isVisible" type="info" data-testid="profile-fields-admin-support-banner">
88
<div class="profile-fields-admin-support-banner">
99
<div class="profile-fields-admin-support-banner__copy">
10-
<p><strong>Help keep Profile Fields sustainable.</strong></p>
11-
<p>Profile Fields is open source under the AGPL license and maintained by the LibreCode team, creators of LibreSign.</p>
12-
<p>If your organization depends on it, please help us sustain its development and maintenance.</p>
10+
<p><strong>{{ t('profile_fields', 'Help keep Profile Fields sustainable.') }}</strong></p>
11+
<p>{{ t('profile_fields', 'Profile Fields is open source under the AGPL license and maintained by the LibreCode team, creators of LibreSign.') }}</p>
12+
<p>{{ t('profile_fields', 'If your organization depends on it, please help us sustain its development and maintenance.') }}</p>
1313

1414
<div class="profile-fields-admin-support-banner__actions">
1515
<NcButton class="profile-fields-admin-support-banner__action" variant="primary" @click="openSponsorPage">
16-
Sponsor LibreSign
16+
{{ t('profile_fields', 'Sponsor LibreSign') }}
1717
</NcButton>
1818

1919
<NcButton class="profile-fields-admin-support-banner__dismiss" variant="tertiary-no-background" @click="dismissBanner">
20-
Maybe later
20+
{{ t('profile_fields', 'Maybe later') }}
2121
</NcButton>
2222
</div>
2323

2424
<div class="profile-fields-admin-support-banner__links">
2525
<a href="https://github.com/LibreCodeCoop/profile_fields" target="_blank" rel="noopener noreferrer nofollow">
26-
Give Profile Fields a ⭐ on GitHub
26+
{{ t('profile_fields', 'Give Profile Fields a ⭐ on GitHub') }}
27+
</a>
28+
<a href="mailto:contact@librecode.coop">
29+
{{ t('profile_fields', 'Contact us for support or custom development') }}
2730
</a>
28-
<a href="mailto:contact@librecode.coop">Contact us for support or custom development</a>
2931
</div>
3032
</div>
3133
</div>
@@ -34,6 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
3436

3537
<script setup lang="ts">
3638
import { onMounted, ref } from 'vue'
39+
import { t } from '@nextcloud/l10n'
3740
import { NcButton, NcNoteCard } from '@nextcloud/vue'
3841
3942
const props = withDefaults(defineProps<{

src/components/AdminUserFieldsDialog.vue

Lines changed: 40 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
88
<NcDialog
99
:open="open"
1010
size="large"
11-
:name="title"
11+
:name="t('profile_fields', 'Edit additional profile fields')"
1212
content-classes="profile-fields-user-dialog__content"
1313
@update:open="updateOpen">
1414
<div class="profile-fields-user-dialog">
@@ -31,13 +31,13 @@ SPDX-License-Identifier: AGPL-3.0-or-later
3131

3232
<div v-if="isLoading" class="profile-fields-user-dialog__loading">
3333
<NcLoadingIcon :size="32" />
34-
<span>Loading profile fields for {{ userUid }}...</span>
34+
<span>{{ loadingMessage }}</span>
3535
</div>
3636

3737
<NcEmptyContent
3838
v-else-if="editableFields.length === 0"
39-
name="No editable fields"
40-
description="Create and enable fields in the admin catalog. They will appear here automatically." />
39+
:name="t('profile_fields', 'No editable fields')"
40+
:description="t('profile_fields', 'Create and enable fields in the admin catalog. They will appear here automatically.')" />
4141

4242
<div v-else class="profile-fields-user-dialog__list">
4343
<article v-for="field in editableFields" :key="field.definition.id" class="profile-fields-user-dialog__row" :class="{ 'profile-fields-user-dialog__row--error': fieldHasError(field) }">
@@ -75,7 +75,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
7575
/>
7676

7777
<div class="profile-fields-user-dialog__visibility-control" :class="{ 'profile-fields-user-dialog__visibility-control--error': fieldHasError(field) }">
78-
<label class="profile-fields-user-dialog__control-label" :for="`profile-fields-user-dialog-visibility-${field.definition.id}`">{{ visibilityControlLabel }}</label>
78+
<label class="profile-fields-user-dialog__control-label" :for="`profile-fields-user-dialog-visibility-${field.definition.id}`">{{ t('profile_fields', 'Who can see this') }}</label>
7979
<NcSelect
8080
:input-id="`profile-fields-user-dialog-visibility-${field.definition.id}`"
8181
:model-value="visibilityOptionFor(field.definition.id)"
@@ -94,18 +94,18 @@ SPDX-License-Identifier: AGPL-3.0-or-later
9494

9595
<template #actions>
9696
<NcButton @click="closeDialog">
97-
Cancel
97+
{{ t('profile_fields', 'Cancel') }}
9898
</NcButton>
9999
<NcButton variant="primary" :disabled="!hasPendingChanges || hasInvalidFields || isSavingAny || isLoading" @click="saveAllFields">
100-
{{ isSavingAny ? 'Saving changes...' : 'Save changes' }}
100+
{{ isSavingAny ? t('profile_fields', 'Saving changes...') : t('profile_fields', 'Save changes') }}
101101
</NcButton>
102102
</template>
103103
</NcDialog>
104104
</template>
105105

106106
<script lang="ts">
107107
import { computed, defineComponent, reactive, ref, watch } from 'vue'
108-
import { t } from '@nextcloud/l10n'
108+
import { n, t } from '@nextcloud/l10n'
109109
import NcAvatar from '@nextcloud/vue/components/NcAvatar'
110110
import { NcButton, NcDialog, NcEmptyContent, NcInputField, NcLoadingIcon, NcNoteCard, NcSelect } from '@nextcloud/vue'
111111
import { listAdminUserValues, listDefinitions, upsertAdminUserValue } from '../api'
@@ -144,7 +144,6 @@ export default defineComponent({
144144
'update:open': (value: boolean) => typeof value === 'boolean',
145145
},
146146
setup(props: { open: boolean, userUid: string, userDisplayName: string }, { emit }: { emit: (event: 'update:open', value: boolean) => void }) {
147-
const visibilityControlLabel = t('profile_fields', 'Who can see this')
148147
const definitions = ref<FieldDefinition[]>([])
149148
const userValues = ref<FieldValueRecord[]>([])
150149
const isLoading = ref(false)
@@ -156,18 +155,20 @@ export default defineComponent({
156155
const userDraftValues = reactive<Record<number, string>>({})
157156
const userDraftVisibilities = reactive<Record<number, FieldVisibility>>({})
158157
159-
const title = computed(() => 'Edit additional profile fields')
160158
const headerUserName = computed(() => props.userDisplayName.trim() !== '' ? props.userDisplayName : props.userUid)
159+
const loadingMessage = computed(() => t('profile_fields', 'Loading profile fields for {userUid}...', { userUid: props.userUid }))
161160
const editableFields = computed<AdminEditableField[]>(() => buildAdminEditableFields(definitions.value, userValues.value))
162161
const isSavingAny = computed(() => savingIds.value.length > 0)
163162
const headerDescription = computed(() => {
164163
if (props.userUid === '') {
165-
return 'Update additional profile fields for the selected account.'
164+
return t('profile_fields', 'Update additional profile fields for the selected account.')
166165
}
167166
168167
const count = editableFields.value.length
169-
const label = count === 1 ? '1 editable field' : `${count} editable fields`
170-
return `${label} for @${props.userUid}.`
168+
return n('profile_fields', '{count} editable field for @{userUid}.', '{count} editable fields for @{userUid}.', count, {
169+
count,
170+
userUid: props.userUid,
171+
})
171172
})
172173
173174
const clearRecord = (record: Record<string | number, unknown>) => {
@@ -177,15 +178,15 @@ export default defineComponent({
177178
}
178179
179180
const descriptionForType = (type: FieldType): string => ({
180-
text: 'Free text stored as a scalar value.',
181-
number: 'Only numeric values are accepted.',
182-
select: 'Choose one of the predefined options.',
181+
text: t('profile_fields', 'Free text stored as a scalar value.'),
182+
number: t('profile_fields', 'Only numeric values are accepted.'),
183+
select: t('profile_fields', 'Choose one of the predefined options.'),
183184
} as Record<FieldType, string>)[type]
184185
185186
const placeholderForField = (type: FieldType): string => ({
186-
text: 'Enter a value',
187-
number: 'Enter a number',
188-
select: 'Select an option',
187+
text: t('profile_fields', 'Enter a value'),
188+
number: t('profile_fields', 'Enter a number'),
189+
select: t('profile_fields', 'Select an option'),
189190
} as Record<FieldType, string>)[type]
190191
191192
const plainNumberPattern = /^-?\d+(\.\d+)?$/
@@ -219,13 +220,13 @@ export default defineComponent({
219220
}
220221
221222
if (field.definition.type === 'number' && !plainNumberPattern.test(rawValue)) {
222-
return `${field.definition.label} must be a plain numeric value.`
223+
return t('profile_fields', '{fieldLabel} must be a plain numeric value.', { fieldLabel: field.definition.label })
223224
}
224225
225226
if (field.definition.type === 'select') {
226227
const options = field.definition.options ?? []
227228
if (!options.includes(rawValue)) {
228-
return `${field.definition.label} must be one of the allowed options.`
229+
return t('profile_fields', '{fieldLabel} must be one of the allowed options.', { fieldLabel: field.definition.label })
229230
}
230231
}
231232
@@ -282,7 +283,7 @@ export default defineComponent({
282283
clearRecord(userDraftVisibilities)
283284
editableFields.value.forEach(normaliseDraft)
284285
} catch (error) {
285-
errorMessage.value = error instanceof Error ? error.message : 'Could not load profile fields for this user.'
286+
errorMessage.value = error instanceof Error ? error.message : t('profile_fields', 'Could not load profile fields for this user.')
286287
} finally {
287288
isLoading.value = false
288289
}
@@ -305,10 +306,12 @@ export default defineComponent({
305306
306307
const formatFieldErrorMessage = (field: AdminEditableField, message: string) => {
307308
return ({
308-
'text fields expect a scalar value': `${field.definition.label} must be plain text.`,
309-
'number fields expect a numeric value': `${field.definition.label} must be a numeric value.`,
310-
'current_visibility is not supported': 'The selected visibility is not supported.',
311-
}[message] ?? (message.includes('is not a valid option') ? `${field.definition.label}: invalid option selected.` : `${field.definition.label}: ${message}`))
309+
'text fields expect a scalar value': t('profile_fields', '{fieldLabel} must be plain text.', { fieldLabel: field.definition.label }),
310+
'number fields expect a numeric value': t('profile_fields', '{fieldLabel} must be a numeric value.', { fieldLabel: field.definition.label }),
311+
'current_visibility is not supported': t('profile_fields', 'The selected visibility is not supported.'),
312+
}[message] ?? (message.includes('is not a valid option')
313+
? t('profile_fields', '{fieldLabel}: invalid option selected.', { fieldLabel: field.definition.label })
314+
: t('profile_fields', '{fieldLabel}: {message}', { fieldLabel: field.definition.label, message })))
312315
}
313316
314317
const extractApiMessage = (error: unknown) => {
@@ -349,7 +352,7 @@ export default defineComponent({
349352
}
350353
351354
if (!plainNumberPattern.test(rawValue)) {
352-
throw new Error('Numeric fields only accept plain numbers.')
355+
throw new Error(t('profile_fields', 'Numeric fields only accept plain numbers.'))
353356
}
354357
355358
const numericValue = Number(rawValue)
@@ -397,7 +400,7 @@ export default defineComponent({
397400
userValues.value = nextValues
398401
normaliseDraft({ definition: field.definition, value: saved })
399402
} catch (error) {
400-
userValueErrors[fieldId] = formatFieldErrorMessage(field, extractApiMessage(error) ?? 'Could not save this field value. Please try again.')
403+
userValueErrors[fieldId] = formatFieldErrorMessage(field, extractApiMessage(error) ?? t('profile_fields', 'Could not save this field value. Please try again.'))
401404
} finally {
402405
savingIds.value = savingIds.value.filter((value: number) => value !== fieldId)
403406
}
@@ -429,9 +432,9 @@ export default defineComponent({
429432
}
430433
})
431434
432-
errorMessage.value = invalidChangedFields.length === 1
433-
? 'Fix the invalid field before saving.'
434-
: 'Fix the invalid fields before saving.'
435+
errorMessage.value = n('profile_fields', 'Fix the invalid field before saving.', 'Fix the invalid fields before saving.', invalidChangedFields.length, {
436+
count: invalidChangedFields.length,
437+
})
435438
return
436439
}
437440
@@ -441,11 +444,11 @@ export default defineComponent({
441444
442445
const hasFieldErrors = changedFields.some((field: AdminEditableField) => Boolean(userValueErrors[field.definition.id]))
443446
if (!hasFieldErrors) {
444-
successMessage.value = `Saved profile fields for ${props.userUid}.`
447+
successMessage.value = t('profile_fields', 'Saved profile fields for {userUid}.', { userUid: props.userUid })
445448
} else {
446-
errorMessage.value = changedFields.length === 1
447-
? 'The field could not be saved.'
448-
: 'Some fields could not be saved. Review the messages below.'
449+
errorMessage.value = n('profile_fields', 'The field could not be saved.', 'Some fields could not be saved. Review the messages below.', changedFields.length, {
450+
count: changedFields.length,
451+
})
449452
}
450453
}
451454
@@ -465,12 +468,13 @@ export default defineComponent({
465468
)
466469
467470
return {
471+
t,
468472
closeDialog,
469473
editableFields,
470474
errorMessage,
471475
headerDescription,
472476
headerUserName,
473-
visibilityControlLabel,
477+
loadingMessage,
474478
hasPendingChanges,
475479
hasInvalidFields,
476480
helperTextForField,
@@ -480,7 +484,6 @@ export default defineComponent({
480484
placeholderForField,
481485
saveAllFields,
482486
successMessage,
483-
title,
484487
updateOpen,
485488
updateVisibility,
486489
clearFieldError,

0 commit comments

Comments
 (0)