feat: Ability to overwrite locale on contact level with locale key on data

This commit is contained in:
Dries Augustyns
2025-12-25 17:37:25 +01:00
parent fa9e9de032
commit 76155232a3
4 changed files with 45 additions and 4 deletions
+21
View File
@@ -1,4 +1,5 @@
import {type Contact, Prisma} from '@plunk/db';
import {isValidLanguageCode} from '@plunk/shared';
import type {FilterCondition, FilterGroup} from '@plunk/types';
import {prisma} from '../database/prisma.js';
@@ -229,6 +230,20 @@ export class ContactService {
continue;
}
// Validate locale field
if (key === 'locale') {
if (typeof value === 'string') {
if (!isValidLanguageCode(value)) {
throw new HttpException(
400,
`Invalid locale code: ${value}. Must be one of: en, nl, fr, hi, de`,
);
}
} else if (value !== null && value !== undefined) {
throw new HttpException(400, 'Locale must be a string');
}
}
// Handle non-persistent data format: { value: "...", persistent: false }
if (
typeof value === 'object' &&
@@ -297,6 +312,12 @@ export class ContactService {
Object.assign(mergedData, contact.data);
}
// Explicitly expose locale as a predefined field (available in templates)
// This ensures locale is always accessible even if not in contact.data
if (mergedData.locale === undefined) {
mergedData.locale = null;
}
// Add temporary (non-persistent) data
if (temporaryData) {
for (const [key, value] of Object.entries(temporaryData)) {
+12 -2
View File
@@ -604,8 +604,18 @@ export class EmailService {
const unsubscribeHtml = includeUnsubscribe
? (() => {
// Get translator for project's language
const translator = createTranslatorSync(project.language || 'en');
// Get contact-level locale (overrides project language)
const contactLocale =
contact.data &&
typeof contact.data === 'object' &&
!Array.isArray(contact.data) &&
'locale' in contact.data &&
typeof contact.data.locale === 'string'
? contact.data.locale
: null;
// Get translator for contact's locale or project's language
const translator = createTranslatorSync(contactLocale || project.language || 'en');
const unsubscribeText = translator.t('email.footer.unsubscribeText', {
projectName: project.name,
});