Fix hardcoded user-facing strings — localize email fallbacks, API errors, AI parse messages
- Frontend: update extractApiError to check body.code first via i18n 'error.<code>' keys; add en/ro translations for cv_file_missing, captcha_verification_failed, request_cancelled - email-data migration: seed 6 fallback template keys (match N/A, subject label, unknown IP, job search results empty states for keywords/providers/location) - EmailApiEmailSender: replace "N/A", "Job", "Unknown" literals with template lookups - CvSearchEmailSender: replace "none detected", "none", "-" literals with template lookups - cv-matcher-data migration: seed parse-error.summary and parse-error.recommendation in AiPrompts - CvMatcherService: look up localized parse-error messages from AiPrompts before calling ParseResult Closes #53 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -138,7 +138,7 @@ public sealed class EmailApiEmailSender : IEmailSender
|
||||
</tr>
|
||||
<tr style="background:#f8f9fa">
|
||||
<td style="font-weight:600;border:1px solid #dee2e6;color:#495057">IP Address</td>
|
||||
<td style="border:1px solid #dee2e6">{userIp ?? "Unknown"}</td>
|
||||
<td style="border:1px solid #dee2e6">{userIp ?? _emailTemplates.Get("email.notification.unknown-ip", "en")}</td>
|
||||
</tr>
|
||||
</table>
|
||||
""";
|
||||
@@ -215,8 +215,8 @@ public sealed class EmailApiEmailSender : IEmailSender
|
||||
// email.match.body is now stored as HTML in the database
|
||||
var body = _emailTemplates.Render("email.match.body", language,
|
||||
("cvDocumentId", cvDocumentId),
|
||||
("jobLabel", jobLabel ?? "N/A"),
|
||||
("jobUrl", result.JobUrl ?? "N/A"),
|
||||
("jobLabel", jobLabel ?? _emailTemplates.Get("email.match.fallback-na", language)),
|
||||
("jobUrl", result.JobUrl ?? _emailTemplates.Get("email.match.fallback-na", language)),
|
||||
("score", result.Score.ToString()),
|
||||
("summary", WebUtility.HtmlEncode(result.Summary ?? string.Empty)),
|
||||
("strengths", strengths),
|
||||
@@ -238,7 +238,7 @@ public sealed class EmailApiEmailSender : IEmailSender
|
||||
public string BuildMatchEmailSubject(int score, string? jobLabel, string language) =>
|
||||
_emailTemplates.Render("email.match.subject", language,
|
||||
("score", score.ToString()),
|
||||
("jobLabel", jobLabel ?? "Job"));
|
||||
("jobLabel", jobLabel ?? _emailTemplates.Get("email.match.subject-fallback-label", language)));
|
||||
|
||||
public string GetManualJobLabel(string language) =>
|
||||
_emailTemplates.Get("email.match.manual-job-label", language);
|
||||
|
||||
@@ -141,7 +141,9 @@ public sealed class CvMatcherService : ICvMatcherService
|
||||
""";
|
||||
|
||||
var json = await _ai.CreateChatCompletionAsync(systemPrompt, userPrompt, 0.2m, ct);
|
||||
var result = ParseResult(json);
|
||||
var errorSummary = await _aiPrompts.GetAsync("parse-error.summary", language, ct);
|
||||
var errorRec = await _aiPrompts.GetAsync("parse-error.recommendation", language, ct);
|
||||
var result = ParseResult(json, errorSummary, errorRec);
|
||||
result.JobDocumentId = job.Id;
|
||||
result.JobUrl = job.SourceUrl;
|
||||
result.Cached = false;
|
||||
@@ -153,7 +155,10 @@ public sealed class CvMatcherService : ICvMatcherService
|
||||
/// Deserialises the LLM's JSON output into a <see cref="JobMatchResponse"/>.
|
||||
/// Returns a safe fallback response instead of throwing when the JSON cannot be parsed.
|
||||
/// </summary>
|
||||
private static JobMatchResponse ParseResult(string json)
|
||||
private static JobMatchResponse ParseResult(
|
||||
string json,
|
||||
string? errorSummary = null,
|
||||
string? errorRec = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -168,8 +173,8 @@ public sealed class CvMatcherService : ICvMatcherService
|
||||
return new JobMatchResponse
|
||||
{
|
||||
Score = 0,
|
||||
Summary = "The AI response could not be parsed as structured JSON.",
|
||||
Recommendations = ["Inspect the raw model output and tune the scoring prompt."]
|
||||
Summary = errorSummary ?? "The AI response could not be parsed as structured JSON.",
|
||||
Recommendations = [errorRec ?? "Inspect the raw model output and tune the scoring prompt."]
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using CvMatcher.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CvMatcher.Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(CvMatcherDbContext))]
|
||||
[Migration("20260608193046_AddParseErrorPrompts")]
|
||||
partial class AddParseErrorPrompts
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasDefaultSchema("cvMatcher")
|
||||
.HasAnnotation("ProductVersion", "10.0.7")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("CvMatcher.Data.Entities.AiPromptEntity", b =>
|
||||
{
|
||||
b.Property<string>("Key")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)");
|
||||
|
||||
b.Property<string>("Language")
|
||||
.HasMaxLength(8)
|
||||
.HasColumnType("nvarchar(8)");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)")
|
||||
.HasDefaultValue("");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("SYSUTCDATETIME()");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Key", "Language");
|
||||
|
||||
b.ToTable("AiPrompts", "cvMatcher");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CvMatcher.Data.Entities.CvMatchResultEntity", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<string>("ClientIpAddress")
|
||||
.HasMaxLength(45)
|
||||
.HasColumnType("nvarchar(45)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("SYSUTCDATETIME()");
|
||||
|
||||
b.Property<string>("CvDocumentId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<string>("JobDocumentId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<string>("Language")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("ResultJson")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("Score")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CvDocumentId", "JobDocumentId", "Language")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Results", "cvMatcher");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CvMatcher.Data.Entities.CvMatcherChatCacheEntity", b =>
|
||||
{
|
||||
b.Property<string>("CacheKey")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("SYSUTCDATETIME()");
|
||||
|
||||
b.Property<string>("Model")
|
||||
.IsRequired()
|
||||
.HasMaxLength(120)
|
||||
.HasColumnType("nvarchar(120)");
|
||||
|
||||
b.Property<string>("ResponseText")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<decimal>("Temperature")
|
||||
.HasColumnType("decimal(4,2)");
|
||||
|
||||
b.HasKey("CacheKey");
|
||||
|
||||
b.ToTable("ChatCache", "cvMatcher");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
using CvMatcher.Data;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CvMatcher.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddParseErrorPrompts : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.InsertData(
|
||||
schema: MigrationConstants.SchemaName,
|
||||
table: "AiPrompts",
|
||||
columns: ["Key", "Language", "Value", "Description"],
|
||||
values: ["parse-error.summary", "en", "The AI response could not be parsed. Please try again.", "Summary shown in match email when the AI returns an unparseable response"]);
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
schema: MigrationConstants.SchemaName,
|
||||
table: "AiPrompts",
|
||||
columns: ["Key", "Language", "Value", "Description"],
|
||||
values: ["parse-error.summary", "ro", "Răspunsul AI nu a putut fi interpretat. Vă rugăm să încercați din nou.", "Sumar afișat în emailul de potrivire când AI returnează un răspuns care nu poate fi interpretat"]);
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
schema: MigrationConstants.SchemaName,
|
||||
table: "AiPrompts",
|
||||
columns: ["Key", "Language", "Value", "Description"],
|
||||
values: ["parse-error.recommendation", "en", "If the problem persists, try a different job link or description.", "Recommendation shown in match email when the AI returns an unparseable response"]);
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
schema: MigrationConstants.SchemaName,
|
||||
table: "AiPrompts",
|
||||
columns: ["Key", "Language", "Value", "Description"],
|
||||
values: ["parse-error.recommendation", "ro", "Dacă problema persistă, încercați un alt link sau descriere de job.", "Recomandare afișată în emailul de potrivire când AI returnează un răspuns care nu poate fi interpretat"]);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DeleteData(
|
||||
schema: MigrationConstants.SchemaName,
|
||||
table: "AiPrompts",
|
||||
keyColumns: ["Key", "Language"],
|
||||
keyValues: ["parse-error.summary", "en"]);
|
||||
|
||||
migrationBuilder.DeleteData(
|
||||
schema: MigrationConstants.SchemaName,
|
||||
table: "AiPrompts",
|
||||
keyColumns: ["Key", "Language"],
|
||||
keyValues: ["parse-error.summary", "ro"]);
|
||||
|
||||
migrationBuilder.DeleteData(
|
||||
schema: MigrationConstants.SchemaName,
|
||||
table: "AiPrompts",
|
||||
keyColumns: ["Key", "Language"],
|
||||
keyValues: ["parse-error.recommendation", "en"]);
|
||||
|
||||
migrationBuilder.DeleteData(
|
||||
schema: MigrationConstants.SchemaName,
|
||||
table: "AiPrompts",
|
||||
keyColumns: ["Key", "Language"],
|
||||
keyValues: ["parse-error.recommendation", "ro"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Email.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Email.Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(EmailDbContext))]
|
||||
[Migration("20260608192938_AddFallbackStringTemplates")]
|
||||
partial class AddFallbackStringTemplates
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasDefaultSchema("email")
|
||||
.HasAnnotation("ProductVersion", "10.0.7")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Email.Data.Entities.EmailTemplateEntity", b =>
|
||||
{
|
||||
b.Property<string>("Key")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)");
|
||||
|
||||
b.Property<string>("Language")
|
||||
.HasMaxLength(8)
|
||||
.HasColumnType("nvarchar(8)");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)")
|
||||
.HasDefaultValue("");
|
||||
|
||||
b.Property<string>("OperatorCopy")
|
||||
.IsRequired()
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)")
|
||||
.HasDefaultValue("");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("SYSUTCDATETIME()");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Key", "Language");
|
||||
|
||||
b.ToTable("Templates", "email");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
using Email.Data;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Email.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddFallbackStringTemplates : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.InsertData(
|
||||
schema: MigrationConstants.SchemaName,
|
||||
table: "Templates",
|
||||
columns: ["Key", "Language", "Value", "Description"],
|
||||
values: ["email.match.fallback-na", "en", "N/A", "Fallback when a match email field (job label or URL) has no value"]);
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
schema: MigrationConstants.SchemaName,
|
||||
table: "Templates",
|
||||
columns: ["Key", "Language", "Value", "Description"],
|
||||
values: ["email.match.fallback-na", "ro", "N/A", "Fallback când un câmp al emailului de potrivire (etichetă job sau URL) nu are valoare"]);
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
schema: MigrationConstants.SchemaName,
|
||||
table: "Templates",
|
||||
columns: ["Key", "Language", "Value", "Description"],
|
||||
values: ["email.match.subject-fallback-label", "en", "Job", "Fallback job label used in match email subject when no specific label is available"]);
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
schema: MigrationConstants.SchemaName,
|
||||
table: "Templates",
|
||||
columns: ["Key", "Language", "Value", "Description"],
|
||||
values: ["email.match.subject-fallback-label", "ro", "Job", "Etichetă fallback pentru subiectul emailului de potrivire când nu există o etichetă specifică"]);
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
schema: MigrationConstants.SchemaName,
|
||||
table: "Templates",
|
||||
columns: ["Key", "Language", "Value", "Description"],
|
||||
values: ["email.notification.unknown-ip", "en", "Unknown", "Fallback IP address label in operator notification emails"]);
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
schema: MigrationConstants.SchemaName,
|
||||
table: "Templates",
|
||||
columns: ["Key", "Language", "Value", "Description"],
|
||||
values: ["email.notification.unknown-ip", "ro", "Necunoscut", "Etichetă fallback pentru adresa IP în emailurile de notificare operator"]);
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
schema: MigrationConstants.SchemaName,
|
||||
table: "Templates",
|
||||
columns: ["Key", "Language", "Value", "Description"],
|
||||
values: ["email.search-results.keywords-empty", "en", "none detected", "Text shown in job search results email when no CV keywords were extracted"]);
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
schema: MigrationConstants.SchemaName,
|
||||
table: "Templates",
|
||||
columns: ["Key", "Language", "Value", "Description"],
|
||||
values: ["email.search-results.keywords-empty", "ro", "niciunul detectat", "Text afișat în emailul cu rezultatele căutării când nu au fost extrase cuvinte cheie din CV"]);
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
schema: MigrationConstants.SchemaName,
|
||||
table: "Templates",
|
||||
columns: ["Key", "Language", "Value", "Description"],
|
||||
values: ["email.search-results.providers-empty", "en", "none", "Text shown in job search results email when no providers were searched"]);
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
schema: MigrationConstants.SchemaName,
|
||||
table: "Templates",
|
||||
columns: ["Key", "Language", "Value", "Description"],
|
||||
values: ["email.search-results.providers-empty", "ro", "niciunul", "Text afișat în emailul cu rezultatele căutării când nu au fost căutați furnizori"]);
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
schema: MigrationConstants.SchemaName,
|
||||
table: "Templates",
|
||||
columns: ["Key", "Language", "Value", "Description"],
|
||||
values: ["email.search-results.location-empty", "en", "-", "Fallback location display in job search results email scan summary"]);
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
schema: MigrationConstants.SchemaName,
|
||||
table: "Templates",
|
||||
columns: ["Key", "Language", "Value", "Description"],
|
||||
values: ["email.search-results.location-empty", "ro", "-", "Afișaj fallback pentru locație în sumarului de scanare al emailului cu rezultatele căutării"]);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DeleteData(
|
||||
schema: MigrationConstants.SchemaName,
|
||||
table: "Templates",
|
||||
keyColumns: ["Key", "Language"],
|
||||
keyValues: ["email.match.fallback-na", "en"]);
|
||||
|
||||
migrationBuilder.DeleteData(
|
||||
schema: MigrationConstants.SchemaName,
|
||||
table: "Templates",
|
||||
keyColumns: ["Key", "Language"],
|
||||
keyValues: ["email.match.fallback-na", "ro"]);
|
||||
|
||||
migrationBuilder.DeleteData(
|
||||
schema: MigrationConstants.SchemaName,
|
||||
table: "Templates",
|
||||
keyColumns: ["Key", "Language"],
|
||||
keyValues: ["email.match.subject-fallback-label", "en"]);
|
||||
|
||||
migrationBuilder.DeleteData(
|
||||
schema: MigrationConstants.SchemaName,
|
||||
table: "Templates",
|
||||
keyColumns: ["Key", "Language"],
|
||||
keyValues: ["email.match.subject-fallback-label", "ro"]);
|
||||
|
||||
migrationBuilder.DeleteData(
|
||||
schema: MigrationConstants.SchemaName,
|
||||
table: "Templates",
|
||||
keyColumns: ["Key", "Language"],
|
||||
keyValues: ["email.notification.unknown-ip", "en"]);
|
||||
|
||||
migrationBuilder.DeleteData(
|
||||
schema: MigrationConstants.SchemaName,
|
||||
table: "Templates",
|
||||
keyColumns: ["Key", "Language"],
|
||||
keyValues: ["email.notification.unknown-ip", "ro"]);
|
||||
|
||||
migrationBuilder.DeleteData(
|
||||
schema: MigrationConstants.SchemaName,
|
||||
table: "Templates",
|
||||
keyColumns: ["Key", "Language"],
|
||||
keyValues: ["email.search-results.keywords-empty", "en"]);
|
||||
|
||||
migrationBuilder.DeleteData(
|
||||
schema: MigrationConstants.SchemaName,
|
||||
table: "Templates",
|
||||
keyColumns: ["Key", "Language"],
|
||||
keyValues: ["email.search-results.keywords-empty", "ro"]);
|
||||
|
||||
migrationBuilder.DeleteData(
|
||||
schema: MigrationConstants.SchemaName,
|
||||
table: "Templates",
|
||||
keyColumns: ["Key", "Language"],
|
||||
keyValues: ["email.search-results.providers-empty", "en"]);
|
||||
|
||||
migrationBuilder.DeleteData(
|
||||
schema: MigrationConstants.SchemaName,
|
||||
table: "Templates",
|
||||
keyColumns: ["Key", "Language"],
|
||||
keyValues: ["email.search-results.providers-empty", "ro"]);
|
||||
|
||||
migrationBuilder.DeleteData(
|
||||
schema: MigrationConstants.SchemaName,
|
||||
table: "Templates",
|
||||
keyColumns: ["Key", "Language"],
|
||||
keyValues: ["email.search-results.location-empty", "en"]);
|
||||
|
||||
migrationBuilder.DeleteData(
|
||||
schema: MigrationConstants.SchemaName,
|
||||
table: "Templates",
|
||||
keyColumns: ["Key", "Language"],
|
||||
keyValues: ["email.search-results.location-empty", "ro"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user