From 60ce203a118f0c5b3d40328397696608f1ce2860 Mon Sep 17 00:00:00 2001 From: Valentyne Stigloher Date: Mon, 15 Sep 2025 09:47:14 +0200 Subject: [PATCH] feat(profile): configurable voice for pronunciation (defaulting to the first voice of the locale or gb) --- app/components/Opinion.vue | 3 +- app/components/Profile.vue | 10 ++- app/components/Pronunciation.vue | 19 +++--- app/components/PronunciationInput.vue | 63 ++++++++++++++----- app/pages/profile/editor.vue | 7 ++- locale/_base/translations.suml | 2 + locale/de/config.suml | 6 +- locale/de/translations.suml | 2 + locale/en/translations.suml | 2 + .../095-profile-pronunciation-voice.sql | 20 ++++++ server/express/profile.ts | 2 +- server/index.ts | 2 - shared/profile.ts | 2 + 13 files changed, 101 insertions(+), 39 deletions(-) create mode 100644 migrations/095-profile-pronunciation-voice.sql diff --git a/app/components/Opinion.vue b/app/components/Opinion.vue index 66454aa11..2190645e9 100644 --- a/app/components/Opinion.vue +++ b/app/components/Opinion.vue @@ -4,7 +4,6 @@ import type { Opinion } from '#shared/opinions.ts'; const props = withDefaults(defineProps<{ word: string; - pronunciation?: string | null; opinion: string; link?: unknown; escape?: boolean; @@ -42,7 +41,7 @@ const op = computed((): (Opinion & { description: string }) | null => { - + diff --git a/app/components/Profile.vue b/app/components/Profile.vue index f7910274f..3ace9cdfe 100644 --- a/app/components/Profile.vue +++ b/app/components/Profile.vue @@ -186,10 +186,16 @@ const usedOpinions = computed(() => { :opinion="s.el.opinion" :escape="false" :markdown="profile.markdown" - :pronunciation="s.el.pronunciation" :link="config.locale === 'tok' && config.pronouns.enabled ? `${config.pronouns.prefix}/${s.el.value}` : null" :custom-opinions="profile.opinions" - /> + > + + diff --git a/app/components/Pronunciation.vue b/app/components/Pronunciation.vue index 043d3d528..0857c8b59 100644 --- a/app/components/Pronunciation.vue +++ b/app/components/Pronunciation.vue @@ -2,18 +2,19 @@ import type { VoiceKey } from '#shared/pronunciation/voices.ts'; import useConfig from '~/composables/useConfig.ts'; -defineProps<{ +withDefaults(defineProps<{ pronunciation: string; text?: boolean; -}>(); + voices?: VoiceKey[]; +}>(), { + voices: () => { + const config = useConfig(); -const config = useConfig(); - -const voices = computed((): VoiceKey[] => { - if (!config.pronunciation?.enabled) { - return []; - } - return config.pronunciation.voices; + if (!config.pronunciation?.enabled) { + return []; + } + return config.pronunciation.voices; + }, }); diff --git a/app/components/PronunciationInput.vue b/app/components/PronunciationInput.vue index aebfb07d4..4cc930c47 100644 --- a/app/components/PronunciationInput.vue +++ b/app/components/PronunciationInput.vue @@ -1,16 +1,18 @@ diff --git a/app/pages/profile/editor.vue b/app/pages/profile/editor.vue index 424ed40c5..cf6156f13 100644 --- a/app/pages/profile/editor.vue +++ b/app/pages/profile/editor.vue @@ -465,13 +465,16 @@ const propagateChanged = (field: string, checked: boolean): void => {

diff --git a/locale/_base/translations.suml b/locale/_base/translations.suml index e0e72629c..764621469 100644 --- a/locale/_base/translations.suml +++ b/locale/_base/translations.suml @@ -728,6 +728,8 @@ profile: names: 'Names' pronunciation: ipa: 'Pronunciation using IPA' + voice: + without: '(without voice)' pronouns: 'Pronouns' pronounsInfo: > You can enter a pronoun (eg. “they” or “she/her”) diff --git a/locale/de/config.suml b/locale/de/config.suml index 2d7dcc294..aa2738d73 100644 --- a/locale/de/config.suml +++ b/locale/de/config.suml @@ -488,11 +488,7 @@ pronouns: pronunciation: enabled: true - voices: - DE: - language: 'de-DE' - voice: 'Vicki' - engine: 'standard' + voices: ['de'] sources: enabled: true diff --git a/locale/de/translations.suml b/locale/de/translations.suml index 035f1f44a..cb06c3ee6 100644 --- a/locale/de/translations.suml +++ b/locale/de/translations.suml @@ -844,6 +844,8 @@ profile: names: 'Namen' pronunciation: ipa: 'Aussprache in IPA' + voice: + without: '(ohne Sprachausgabe)' pronouns: 'Pronomen' pronounsInfo: > Du kannst entweder ein Pronomen (z.B. „sier“ oder „sie/ihr“) oder einen Link (z.B. „https://pronomen.net/dey“) diff --git a/locale/en/translations.suml b/locale/en/translations.suml index a19b88cf6..00bc218a8 100644 --- a/locale/en/translations.suml +++ b/locale/en/translations.suml @@ -944,6 +944,8 @@ profile: names: 'Names' pronunciation: ipa: 'Pronunciation using IPA' + voice: + without: '(without voice)' pronouns: 'Pronouns' pronounsInfo: > You can enter a pronoun (eg. “they” or “she/her”) diff --git a/migrations/095-profile-pronunciation-voice.sql b/migrations/095-profile-pronunciation-voice.sql new file mode 100644 index 000000000..2a358f545 --- /dev/null +++ b/migrations/095-profile-pronunciation-voice.sql @@ -0,0 +1,20 @@ +-- Up + +update profiles +set names = (select json_group_array(case + when json_type(names.value, '$.pronunciation') is 'text' + then json_set(names.value, '$.voice', + case + when locale in ('en', 'eo', 'fo', 'hu', 'tok', 'ua', 'vi') + then 'gb' + when locale = 'et' then 'fi' + when locale = 'lad' then 'es' + when locale = 'nn' then 'nb' + when locale = 'sv' then 'se' + when locale = 'zh' then 'cn' + else locale end) + else json(names.value) end) + from json_each(profiles.names) as names) +where 1 = 1; + +-- Down diff --git a/server/express/profile.ts b/server/express/profile.ts index 69e6da914..70cb0cd07 100644 --- a/server/express/profile.ts +++ b/server/express/profile.ts @@ -348,7 +348,7 @@ const fetchProfiles = async ( opinions: propv('opinions', () => JSON.parse(profile.opinions)), names: propv('names', () => { return JSON.parse(profile.names).map((name: ValueOpinion) => { - return { pronunciation: null, ...name }; + return { pronunciation: null, voice: null, ...name }; }); }), pronouns: propv('pronouns', () => JSON.parse(profile.pronouns)), diff --git a/server/index.ts b/server/index.ts index 7d64226e6..3c1c77c55 100644 --- a/server/index.ts +++ b/server/index.ts @@ -19,7 +19,6 @@ import grantOverridesRoute from './express/grantOverrides.ts'; import imagesRoute from './express/images.ts'; import mfaRoute from './express/mfa.ts'; import profileRoute from './express/profile.ts'; -import pronounceRoute from './express/pronounce.ts'; import sentryRoute from './express/sentry.ts'; import subscriptionRoute from './express/subscription.ts'; import translationsRoute from './express/translations.ts'; @@ -148,7 +147,6 @@ router.use(userRoute); router.use(profileRoute); router.use(adminRoute); router.use(mfaRoute); -router.use(pronounceRoute); router.use(censusRoute); router.use(imagesRoute); router.use(calendarRoute); diff --git a/shared/profile.ts b/shared/profile.ts index 2e3e58a0f..afb30d396 100644 --- a/shared/profile.ts +++ b/shared/profile.ts @@ -2,6 +2,7 @@ import type { CustomEvent } from './calendar/helpers.ts'; import type { Opinion } from './opinions.ts'; import type { User } from './user.ts'; +import type { VoiceKey } from '#shared/pronunciation/voices.ts'; import type { LocaleCode } from '~~/locale/locales.ts'; export interface UserWithProfiles { @@ -63,6 +64,7 @@ export interface ValueOpinion { export interface NameOpinion extends ValueOpinion { pronunciation: string; + voice: VoiceKey | null; } export interface Timezone {