Files
marechai/Marechai.Database/Models/SoftwareAlternativeTitleCommentTranslation.cs
Natalia Portillo 3aecbf566b feat: Add Software alternative titles and use them in IGDB matching
Software entries (especially games) often have regional, script, or
otherwise alternative titles — e.g. a Japanese Kanji/Romaji title
alongside the Western release name. There was previously no way to
record these.

Database layer:
- New SoftwareAlternativeTitle entity (SoftwareId FK, Title, nullable
  Comment), cascade-deleted with its Software.
- New SoftwareAlternativeTitleCommentTranslation, a string-pool
  translation table keyed by (CommentText, LanguageCode), mirroring
  the existing SoftwareCoverCaptionTranslation pattern. Comments are
  free text that repeats often ("Japanese title", "European title"),
  so pooling avoids duplicate translation work.
- Migration AddSoftwareAlternativeTitles.

Translation pipeline:
- SoftwareAlternativeTitleCommentTranslationProvider, registered with
  the background TranslationWorker, anti-join translates missing
  comments into es/fr/de/it/nl.

API:
- GET /software/{id}/alternative-titles (public, language-aware, with
  an English fast path that skips the translation sub-query).
- POST/PUT/DELETE /software/alternative-titles (admin-only CRUD) on a
  new SoftwareAlternativeTitlesController.
- Kiota client regenerated against the updated OpenAPI schema.

Blazor admin UI:
- New "Alternative Titles" tab in SoftwareDialog.razor, with the same
  create-mode buffering/flush-after-create pattern used by the
  existing Genres tab.
- SoftwareService.cs client methods for list/add/remove.
- Localized resx strings added for en/es/fr/de/it/nl.

IGDB matching:
- Mirrored IGDB's /alternative_names endpoint into a new
  IgdbAlternativeName table (GameIgdbId, Name, Comment), via
  IgdbAlternativeNameMirrorService and a new `mirror-alternative-names`
  CLI command, following the existing resumable id-paged mirror
  pattern (e.g. InvolvedCompanyMirrorService).
- GameMatcher now builds a full name-variant set per side (IGDB
  canonical name + its mirrored alternative names; local Software
  name + its SoftwareAlternativeTitle rows) and matches across the
  full cross-product, both for exact equality and the Jaro-Winkler
  fuzzy sweep. This lets an IGDB alternate name match a local
  alternate title directly, not just canonical-vs-canonical.
- Match results record whether the winning pair was canonical-vs-
  canonical or involved an alternate, via a "-alt" suffix on
  MatchType (exact-alt, exact-platform-disambiguated-alt,
  jw-platform-corroborated-alt), keeping the match audit trail
  informative.
2026-06-26 02:16:39 +01:00

54 lines
2.2 KiB
C#

/******************************************************************************
// MARECHAI: Master repository of computing history artifacts information
// ----------------------------------------------------------------------------
//
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// --[ License ] --------------------------------------------------------------
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2003-2026 Natalia Portillo
*******************************************************************************/
using System.ComponentModel.DataAnnotations;
namespace Marechai.Database.Models;
/// <summary>
/// One translated copy of a <see cref="SoftwareAlternativeTitle.Comment" /> per
/// (<see cref="CommentText" />, <see cref="LanguageCode" />) pair. String-pool keyed by the
/// comment text itself (not by a parent FK) so multiple alternative titles sharing the same
/// comment (e.g. "Japanese title") reuse a single translation row per language, mirroring
/// <see cref="SoftwareCoverCaptionTranslation" />. English (<c>eng</c>) is the identity copy
/// and is never stored here.
/// </summary>
public class SoftwareAlternativeTitleCommentTranslation : BaseModel<int>
{
[StringLength(500)]
[Required]
public string CommentText { get; set; }
[StringLength(3)]
[Required]
public string LanguageCode { get; set; }
[StringLength(500)]
[Required]
public string Translation { get; set; }
public virtual Iso639 Language { get; set; }
}