From 9f2f9a23aac747f45884d29d64c731cd470f93a1 Mon Sep 17 00:00:00 2001 From: Your Date: Wed, 3 Jun 2026 05:03:10 +0200 Subject: [PATCH 1/2] =?UTF-8?q?docs(i18n):=20add=20translation=20consisten?= =?UTF-8?q?cy=20audit=20(#192=20=C3=A9tape=201)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Read-only terminological consistency analysis across 8 languages: - Fallacies: 0 inconsistencies (8 families, 21 sub-families, 63 sub-sub-families) - Scenarii: 25 inconsistencies (PT case/translation variants in categories) - Virtues: 21 inconsistencies (AR/FA/ZH/ES/RU outlier variants in families) - Rules: 0 inconsistencies (heading structure consistent across 8 languages) - Coverage: 100% across all content fields for all datasets Total real inconsistencies: 46 (outlier records in minority translations). Scenarii role columns (baratineur/piocheur) confirmed as FALSE POSITIVE (character names, not terminology). Co-Authored-By: Claude Opus 4.8 --- .../TranslationConsistencyReport.ps1 | 626 +++++ translation-consistency-audit-192.md | 2298 +++++++++++++++++ 2 files changed, 2924 insertions(+) create mode 100644 Generation/Converters/Argumentum.AssetConverter.Tests/TranslationAudit/TranslationConsistencyReport.ps1 create mode 100644 translation-consistency-audit-192.md diff --git a/Generation/Converters/Argumentum.AssetConverter.Tests/TranslationAudit/TranslationConsistencyReport.ps1 b/Generation/Converters/Argumentum.AssetConverter.Tests/TranslationAudit/TranslationConsistencyReport.ps1 new file mode 100644 index 000000000..ebc83d47f --- /dev/null +++ b/Generation/Converters/Argumentum.AssetConverter.Tests/TranslationAudit/TranslationConsistencyReport.ps1 @@ -0,0 +1,626 @@ +#!/usr/bin/env pwsh +# Translation Consistency Audit — #192 Étape 1 +# Read-only analysis: groups source FR terms and flags inconsistent translations across 8 languages. +# Output: Markdown report with worklist of cells to re-translate. + +param( + [string]$OutputDir = "." +) + +Set-StrictMode -Version 3.0 +$ErrorActionPreference = "Stop" + +# ── CSV helpers ────────────────────────────────────────────────────────────── + +function Import-CsvSafe { + param([string]$Path) + $content = [System.IO.File]::ReadAllText($Path, [System.Text.UTF8Encoding]::new($false)) + # Handle BOM + if ($content.StartsWith([char]0xFEFF)) { $content = $content.Substring(1) } + # Use ConvertFrom-Csv which accepts pipeline/InputObject + $csv = $content | ConvertFrom-Csv + return $csv +} + +# ── Consistency checker ───────────────────────────────────────────────────── + +function Find-InconsistentTranslations { + param( + [array]$Records, + [string]$SourceColumn, + [string[]]$TranslationColumns, + [string]$Dataset, + [string]$IdColumn = "PK" + ) + + $inconsistencies = @() + $totalTerms = 0 + $uniqueTerms = 0 + + # Group by source term + $groups = $Records | Where-Object { $_.$SourceColumn -and $_.$SourceColumn.Trim() -ne "" } | + Group-Object -Property { $_.$SourceColumn.Trim() } + + $uniqueTerms = $groups.Count + + foreach ($group in $groups) { + $sourceTerm = $group.Name + $totalTerms++ + + foreach ($transCol in $TranslationColumns) { + # Collect all unique translations for this source term in this column + $translations = @($group.Group | + Where-Object { $_.$transCol -and $_.$transCol.Trim() -ne "" } | + ForEach-Object { $_.$transCol.Trim() } | + Select-Object -Unique) + + if ($translations.Count -gt 1) { + # Extract language from column suffix + $lang = if ($transCol -match '_([a-z]{2})$') { $Matches[1] } + elseif ($transCol -match '^(\w+)$' -and $transCol -notmatch '_') { + # Bare EN columns (category, title, etc.) + "en" + } else { "unknown" } + + # Get IDs of records with each variant + $variants = @() + foreach ($trans in $translations) { + $ids = ($group.Group | + Where-Object { $_.$transCol -and $_.$transCol.Trim() -eq $trans } | + ForEach-Object { $_.$IdColumn }) -join ", " + $variants += @{ + Translation = $trans + Count = ($group.Group | Where-Object { $_.$transCol -and $_.$transCol.Trim() -eq $trans }).Count + IDs = $ids + } + } + + $inconsistencies += [PSCustomObject]@{ + Dataset = $Dataset + SourceTerm = $sourceTerm + Column = $transCol + Language = $lang + VariantCount = $translations.Count + Variants = $variants + } + } + } + } + + return @{ + Inconsistencies = $inconsistencies + TotalTerms = $totalTerms + UniqueTerms = $uniqueTerms + } +} + +# ── Main analysis ──────────────────────────────────────────────────────────── + +Write-Host "=== Translation Consistency Audit (#192 Étape 1) ===" -ForegroundColor Cyan +Write-Host "" + +$report = [System.Text.StringBuilder]::new() +[void]$report.AppendLine("# Translation Consistency Audit — #192 Étape 1") +[void]$report.AppendLine("") +[void]$report.AppendLine("> **Date**: $(Get-Date -Format 'yyyy-MM-dd HH:mm')") +[void]$report.AppendLine("> **Scope**: Read-only terminological consistency analysis") +[void]$report.AppendLine("> **Datasets**: Fallacies, Scenarii, Virtues, Rules (8 languages)") +[void]$report.AppendLine("") + +# ── 1. FALLACIES ───────────────────────────────────────────────────────────── + +Write-Host "Analyzing Fallacies..." -ForegroundColor Yellow +$fallaciesPath = "Cards\Fallacies\Argumentum Fallacies - Taxonomy.csv" +$fallacies = Import-CsvSafe $fallaciesPath +[void]$report.AppendLine("## 1. Fallacies — Taxonomy") +[void]$report.AppendLine("") +[void]$report.AppendLine("Records: $($fallacies.Count) | Columns: Fallacies") +[void]$report.AppendLine("") + +# Family columns +$fallaciesResult = Find-InconsistentTranslations -Records $fallacies ` + -SourceColumn "Famille" ` + -TranslationColumns @("Family", "Family_ru", "Family_pt", "Family_ar", "Family_es", "Family_zh", "Family_fa") ` + -Dataset "Fallacies" -IdColumn "PK" + +# Sub-family columns +$fallaciesSubResult = Find-InconsistentTranslations -Records $fallacies ` + -SourceColumn "Sous-Famille" ` + -TranslationColumns @("Subfamily", "Subfamily_ru", "Subfamily_pt", "Subfamily_ar", "Subfamily_es", "Subfamily_zh", "Subfamily_fa") ` + -Dataset "Fallacies" -IdColumn "PK" + +# Sub-sub-family columns +$fallaciesSubSubResult = Find-InconsistentTranslations -Records $fallacies ` + -SourceColumn "Soussousfamille" ` + -TranslationColumns @("Subsubfamily", "Subsubfamily_ru", "Subsubfamily_pt", "Subsubfamily_ar", "Subsubfamily_es", "Subsubfamily_zh", "Subsubfamily_fa") ` + -Dataset "Fallacies" -IdColumn "PK" + +$allFallacies = @($fallaciesResult.Inconsistencies) + @($fallaciesSubResult.Inconsistencies) + @($fallaciesSubSubResult.Inconsistencies) + +[void]$report.AppendLine("### Family taxonomy (Famille / Sous-Famille / Soussousfamille)") +[void]$report.AppendLine("") +[void]$report.AppendLine("Unique FR families: $($fallaciesResult.UniqueTerms) | Inconsistencies: $($fallaciesResult.Inconsistencies.Count)") +[void]$report.AppendLine("Unique FR sub-families: $($fallaciesSubResult.UniqueTerms) | Inconsistencies: $($fallaciesSubResult.Inconsistencies.Count)") +[void]$report.AppendLine("Unique FR sub-sub-families: $($fallaciesSubSubResult.UniqueTerms) | Inconsistencies: $($fallaciesSubSubResult.Inconsistencies.Count)") +[void]$report.AppendLine("") + +if ($allFallacies.Count -gt 0) { + [void]$report.AppendLine("| # | Source (FR) | Column | Lang | Variants |") + [void]$report.AppendLine("|---|-------------|--------|------|----------|") + $i = 0 + foreach ($inc in ($allFallacies | Sort-Object SourceTerm, Column)) { + $i++ + $variantStr = ($inc.Variants | ForEach-Object { + "$($_.Translation) ($($_.Count)x)" + }) -join " / " + # Truncate long variant strings + if ($variantStr.Length -gt 120) { + $variantStr = $variantStr.Substring(0, 117) + "..." + } + [void]$report.AppendLine("| $i | $($inc.SourceTerm) | $($inc.Column) | $($inc.Language) | $variantStr |") + } + [void]$report.AppendLine("") +} + +Write-Host " Fallacies: $($allFallacies.Count) inconsistencies found" -ForegroundColor $(if ($allFallacies.Count -gt 0) {"Yellow"} else {"Green"}) + +# ── 2. SCENARII ────────────────────────────────────────────────────────────── + +Write-Host "Analyzing Scenarii..." -ForegroundColor Yellow +$scenariiPath = "Cards\Scenarii\Argumentum Scenarii - Cards.csv" +$scenarii = Import-CsvSafe $scenariiPath +[void]$report.AppendLine("## 2. Scenarii — Cards") +[void]$report.AppendLine("") +[void]$report.AppendLine("Records: $($scenarii.Count)") +[void]$report.AppendLine("") + +# Category consistency +$scenariiCatResult = Find-InconsistentTranslations -Records $scenarii ` + -SourceColumn "catégorie" ` + -TranslationColumns @("category", "category_ru", "category_pt", "category_ar", "category_es", "category_zh", "category_fa") ` + -Dataset "Scenarii" -IdColumn "path" + +# Sub-category consistency +$scenariiSubCatResult = Find-InconsistentTranslations -Records $scenarii ` + -SourceColumn "sous-catégorie" ` + -TranslationColumns @("subcategory", "subcategory_ru", "subcategory_pt", "subcategory_ar", "subcategory_es", "subcategory_zh", "subcategory_fa") ` + -Dataset "Scenarii" -IdColumn "path" + +$allScenarii = @($scenariiCatResult.Inconsistencies) + @($scenariiSubCatResult.Inconsistencies) + +[void]$report.AppendLine("### Category taxonomy (catégorie / sous-catégorie)") +[void]$report.AppendLine("") +[void]$report.AppendLine("Unique FR categories: $($scenariiCatResult.UniqueTerms) | Inconsistencies: $($scenariiCatResult.Inconsistencies.Count)") +[void]$report.AppendLine("Unique FR sub-categories: $($scenariiSubCatResult.UniqueTerms) | Inconsistencies: $($scenariiSubCatResult.Inconsistencies.Count)") +[void]$report.AppendLine("") + +if ($allScenarii.Count -gt 0) { + [void]$report.AppendLine("| # | Source (FR) | Column | Lang | Variants |") + [void]$report.AppendLine("|---|-------------|--------|------|----------|") + $i = 0 + foreach ($inc in ($allScenarii | Sort-Object SourceTerm, Column)) { + $i++ + $variantStr = ($inc.Variants | ForEach-Object { + "$($_.Translation) ($($_.Count)x)" + }) -join " / " + if ($variantStr.Length -gt 120) { + $variantStr = $variantStr.Substring(0, 117) + "..." + } + [void]$report.AppendLine("| $i | $($inc.SourceTerm) | $($inc.Column) | $($inc.Language) | $variantStr |") + } + [void]$report.AppendLine("") +} + +Write-Host " Scenarii: $($allScenarii.Count) inconsistencies found" -ForegroundColor $(if ($allScenarii.Count -gt 0) {"Yellow"} else {"Green"}) + +# ── 3. VIRTUES ─────────────────────────────────────────────────────────────── + +Write-Host "Analyzing Virtues..." -ForegroundColor Yellow +$virtuesPath = "Cards\Fallacies\Argumentum Virtues - Taxonomy.csv" +$virtues = Import-CsvSafe $virtuesPath +[void]$report.AppendLine("## 3. Virtues — Taxonomy") +[void]$report.AppendLine("") +[void]$report.AppendLine("Records: $($virtues.Count)") +[void]$report.AppendLine("") + +$virtuesFamResult = Find-InconsistentTranslations -Records $virtues ` + -SourceColumn "family_fr" ` + -TranslationColumns @("family_en", "family_ru", "family_pt", "family_ar", "family_es", "family_zh", "family_fa") ` + -Dataset "Virtues" -IdColumn "pk" + +$virtuesSubResult = Find-InconsistentTranslations -Records $virtues ` + -SourceColumn "subfamily_fr" ` + -TranslationColumns @("subfamily_en", "subfamily_ru", "subfamily_pt", "subfamily_ar", "subfamily_es", "subfamily_zh", "subfamily_fa") ` + -Dataset "Virtues" -IdColumn "pk" + +$virtuesSubSubResult = Find-InconsistentTranslations -Records $virtues ` + -SourceColumn "subsubfamily_fr" ` + -TranslationColumns @("subsubfamily_en", "subsubfamily_ru", "subsubfamily_pt", "subsubfamily_ar", "subsubfamily_es", "subsubfamily_zh", "subsubfamily_fa") ` + -Dataset "Virtues" -IdColumn "pk" + +$allVirtues = @($virtuesFamResult.Inconsistencies) + @($virtuesSubResult.Inconsistencies) + @($virtuesSubSubResult.Inconsistencies) + +[void]$report.AppendLine("### Family taxonomy (family_fr / subfamily_fr / subsubfamily_fr)") +[void]$report.AppendLine("") +[void]$report.AppendLine("Unique FR families: $($virtuesFamResult.UniqueTerms) | Inconsistencies: $($virtuesFamResult.Inconsistencies.Count)") +[void]$report.AppendLine("Unique FR sub-families: $($virtuesSubResult.UniqueTerms) | Inconsistencies: $($virtuesSubResult.Inconsistencies.Count)") +[void]$report.AppendLine("Unique FR sub-sub-families: $($virtuesSubSubResult.UniqueTerms) | Inconsistencies: $($virtuesSubSubResult.Inconsistencies.Count)") +[void]$report.AppendLine("") + +if ($allVirtues.Count -gt 0) { + [void]$report.AppendLine("| # | Source (FR) | Column | Lang | Variants |") + [void]$report.AppendLine("|---|-------------|--------|------|----------|") + $i = 0 + foreach ($inc in ($allVirtues | Sort-Object SourceTerm, Column)) { + $i++ + $variantStr = ($inc.Variants | ForEach-Object { + "$($_.Translation) ($($_.Count)x)" + }) -join " / " + if ($variantStr.Length -gt 120) { + $variantStr = $variantStr.Substring(0, 117) + "..." + } + [void]$report.AppendLine("| $i | $($inc.SourceTerm) | $($inc.Column) | $($inc.Language) | $variantStr |") + } + [void]$report.AppendLine("") +} + +Write-Host " Virtues: $($allVirtues.Count) inconsistencies found" -ForegroundColor $(if ($allVirtues.Count -gt 0) {"Yellow"} else {"Green"}) + +# ── 4. RULES ───────────────────────────────────────────────────────────────── + +Write-Host "Analyzing Rules..." -ForegroundColor Yellow +$rulesPath = "Cards\Rules\Argumentum Rules - Cards.csv" +$rules = Import-CsvSafe $rulesPath +[void]$report.AppendLine("## 4. Rules — Cards") +[void]$report.AppendLine("") +[void]$report.AppendLine("Records: $($rules.Count)") +[void]$report.AppendLine("") +[void]$report.AppendLine("> Rules contain Markdown text blocks, not taxonomy labels. Consistency analysis focuses on heading structure and recurring terms across languages.") +[void]$report.AppendLine("") + +# For Rules, check heading consistency (extract ## headings from Text columns) +$headingInconsistencies = @() +$headingMap = @{} + +foreach ($rule in $rules) { + $pk = $rule.pk + if (-not $pk) { continue } + + foreach ($lang in @("", "_en", "_ru", "_pt", "_ar", "_es", "_zh", "_fa")) { + $col = "Text$lang" + $text = $rule.$col + if (-not $text) { continue } + + # Extract ## headings + $headings = ($text -split "`n" | Where-Object { $_ -match "^#{1,3}\s+" }) -join " | " + if ($headings) { + if (-not $headingMap.ContainsKey($pk)) { + $headingMap[$pk] = @{} + } + $langLabel = if ($lang -eq "") { "fr" } else { $lang.Substring(1) } + $headingMap[$pk][$langLabel] = $headings + } + } +} + +# Check: same PK should have same number of headings across languages +[void]$report.AppendLine("### Heading structure comparison") +[void]$report.AppendLine("") + +$headingIssues = @() +foreach ($pk in ($headingMap.Keys | Sort-Object)) { + $langs = $headingMap[$pk] + $headingCounts = @($langs.GetEnumerator() | ForEach-Object { + $count = @($_.Value -split "\|" | Where-Object { $_ -match "\S" }).Count + @{ Lang = $_.Key; Count = $count; Headings = $_.Value } + }) + + $counts = @(($headingCounts | ForEach-Object { $_.Count }) | Select-Object -Unique) + if ($counts.Count -gt 1) { + $detail = ($headingCounts | ForEach-Object { "$($_.Lang)=$($_.Count)" }) -join ", " + $headingIssues += [PSCustomObject]@{ + PK = $pk + Detail = $detail + } + } +} + +if ($headingIssues.Count -gt 0) { + [void]$report.AppendLine("**Heading count mismatches found:** $($headingIssues.Count)") + [void]$report.AppendLine("") + [void]$report.AppendLine("| PK | Heading counts per language |") + [void]$report.AppendLine("|----|----------------------------|") + foreach ($issue in $headingIssues) { + [void]$report.AppendLine("| $($issue.PK) | $($issue.Detail) |") + } + [void]$report.AppendLine("") +} else { + [void]$report.AppendLine("All Rules have consistent heading counts across 8 languages. ✅") + [void]$report.AppendLine("") +} + +Write-Host " Rules: $($headingIssues.Count) heading mismatches found" -ForegroundColor $(if ($headingIssues.Count -gt 0) {"Yellow"} else {"Green"}) + +# ── 5. CROSS-DATASET ROLE TERMS (Scenarii) ────────────────────────────────── + +Write-Host "Analyzing role terms..." -ForegroundColor Yellow +[void]$report.AppendLine("## 5. Cross-dataset Role Terms (Scenarii)") +[void]$report.AppendLine("") +[void]$report.AppendLine("> The Scenarii roles `baratineur` and `piocheur` appear in all 167 records. Each should have exactly ONE canonical translation per language.") +[void]$report.AppendLine("") + +# Check baratineur consistency +$baratineurVariants = @{} +foreach ($lang in @("en", "ru", "pt", "ar", "es", "zh", "fa")) { + $col = switch ($lang) { + "en" { "smoothTalker" } + default { "smoothTalker_$lang" } + } + $variants = @($scenarii | Where-Object { $_.$col -and $_.$col.Trim() -ne "" } | + ForEach-Object { $_.$col.Trim() } | Select-Object -Unique) + $baratineurVariants[$lang] = $variants +} + +# Check piocheur consistency +$piocheurVariants = @{} +foreach ($lang in @("en", "ru", "pt", "ar", "es", "zh", "fa")) { + $col = switch ($lang) { + "en" { "drawer" } + default { "drawer_$lang" } + } + $variants = @($scenarii | Where-Object { $_.$col -and $_.$col.Trim() -ne "" } | + ForEach-Object { $_.$col.Trim() } | Select-Object -Unique) + $piocheurVariants[$lang] = $variants +} + +[void]$report.AppendLine("### `baratineur` translations") +[void]$report.AppendLine("") +[void]$report.AppendLine("| Language | Unique translations | Consistent? |") +[void]$report.AppendLine("|----------|-------------------:|:-----------:|") +$baratineurIssues = 0 +foreach ($lang in @("en", "ru", "pt", "ar", "es", "zh", "fa")) { + $variants = $baratineurVariants[$lang] + $ok = ($variants.Count -le 1) + if (-not $ok) { $baratineurIssues++ } + $variantStr = ($variants -join ", ") + if ($variantStr.Length -gt 100) { $variantStr = $variantStr.Substring(0, 97) + "..." } + [void]$report.AppendLine("| $lang | $($variants.Count) | $(if ($ok) {'✅'} else {'⚠️'}) |") +} +[void]$report.AppendLine("") + +# Show baratineur detail if inconsistent +if ($baratineurIssues -gt 0) { + [void]$report.AppendLine("
Variant details") + [void]$report.AppendLine("") + foreach ($lang in @("en", "ru", "pt", "ar", "es", "zh", "fa")) { + $variants = $baratineurVariants[$lang] + if ($variants.Count -gt 1) { + [void]$report.AppendLine("**$lang**:") + foreach ($v in $variants) { + $count = ($scenarii | Where-Object { + $col = switch ($lang) { "en" { "smoothTalker" } default { "smoothTalker_$lang" } } + $_.$col -and $_.$col.Trim() -eq $v + }).Count + [void]$report.AppendLine("- ``$v`` ($count×)") + } + [void]$report.AppendLine("") + } + } + [void]$report.AppendLine("
") + [void]$report.AppendLine("") +} + +[void]$report.AppendLine("### `piocheur` translations") +[void]$report.AppendLine("") +[void]$report.AppendLine("| Language | Unique translations | Consistent? |") +[void]$report.AppendLine("|----------|-------------------:|:-----------:|") +$piocheurIssues = 0 +foreach ($lang in @("en", "ru", "pt", "ar", "es", "zh", "fa")) { + $variants = $piocheurVariants[$lang] + $ok = ($variants.Count -le 1) + if (-not $ok) { $piocheurIssues++ } + $variantStr = ($variants -join ", ") + if ($variantStr.Length -gt 100) { $variantStr = $variantStr.Substring(0, 97) + "..." } + [void]$report.AppendLine("| $lang | $($variants.Count) | $(if ($ok) {'✅'} else {'⚠️'}) |") +} +[void]$report.AppendLine("") + +if ($piocheurIssues -gt 0) { + [void]$report.AppendLine("
Variant details") + [void]$report.AppendLine("") + foreach ($lang in @("en", "ru", "pt", "ar", "es", "zh", "fa")) { + $variants = $piocheurVariants[$lang] + if ($variants.Count -gt 1) { + [void]$report.AppendLine("**$lang**:") + foreach ($v in $variants) { + $count = ($scenarii | Where-Object { + $col = switch ($lang) { "en" { "drawer" } default { "drawer_$lang" } } + $_.$col -and $_.$col.Trim() -eq $v + }).Count + [void]$report.AppendLine("- ``$v`` ($count×)") + } + [void]$report.AppendLine("") + } + } + [void]$report.AppendLine("
") + [void]$report.AppendLine("") +} + +Write-Host " baratineur: $baratineurIssues languages with inconsistencies" -ForegroundColor $(if ($baratineurIssues -gt 0) {"Yellow"} else {"Green"}) +Write-Host " piocheur: $piocheurIssues languages with inconsistencies" -ForegroundColor $(if ($piocheurIssues -gt 0) {"Yellow"} else {"Green"}) + +# ── 6. COVERAGE REPORT ────────────────────────────────────────────────────── + +Write-Host "Computing coverage..." -ForegroundColor Yellow +[void]$report.AppendLine("## 6. Translation Coverage Report") +[void]$report.AppendLine("") + +$coverageData = @() + +# Fallacies coverage +$fallaciesContentCols = @{ + "desc_fr" = @("desc_en", "desc_ru", "desc_pt", "desc_ar", "desc_es", "desc_zh", "desc_fa") + "example_fr" = @("example_en", "example_ru", "example_pt", "example_ar", "example_es", "example_zh", "example_fa") + "text_fr" = @("text_en", "text_ru", "text_pt", "text_ar", "text_es", "text_zh", "text_fa") +} + +[void]$report.AppendLine("### Fallacies — Content field coverage") +[void]$report.AppendLine("") +[void]$report.AppendLine("| Field | FR filled | EN | RU | PT | AR | ES | ZH | FA |") +[void]$report.AppendLine("|-------|----------:|---:|---:|---:|---:|---:|---:|---:|") + +foreach ($frCol in $fallaciesContentCols.Keys) { + $transCols = $fallaciesContentCols[$frCol] + $frFilled = ($fallacies | Where-Object { $_.$frCol -and $_.$frCol.Trim() -ne "" }).Count + + $langCounts = @() + foreach ($transCol in $transCols) { + $count = ($fallacies | Where-Object { + $_.$frCol -and $_.$frCol.Trim() -ne "" -and + $_.$transCol -and $_.$transCol.Trim() -ne "" + }).Count + $langCounts += $count + } + $langStr = $langCounts -join " | " + [void]$report.AppendLine("| $frCol | $frFilled | $langStr |") +} +[void]$report.AppendLine("") + +# Scenarii coverage +$scenariiContentCols = @{ + "contexte" = @("context", "context_ru", "context_pt", "context_ar", "context_es", "context_zh", "context_fa") + "enjeu" = @("issue", "issue_ru", "issue_pt", "issue_ar", "issue_es", "issue_zh", "issue_fa") + "baratineur" = @("smoothTalker", "smoothTalker_ru", "smoothTalker_pt", "smoothTalker_ar", "smoothTalker_es", "smoothTalker_zh", "smoothTalker_fa") + "piocheur" = @("drawer", "drawer_ru", "drawer_pt", "drawer_ar", "drawer_es", "drawer_zh", "drawer_fa") + "suggestion" = @("suggestion_en", "suggestion_ru", "suggestion_pt", "suggestion_ar", "suggestion_es", "suggestion_zh", "suggestion_fa") +} + +[void]$report.AppendLine("### Scenarii — Content field coverage") +[void]$report.AppendLine("") +[void]$report.AppendLine("| Field | FR filled | EN | RU | PT | AR | ES | ZH | FA |") +[void]$report.AppendLine("|-------|----------:|---:|---:|---:|---:|---:|---:|---:|") + +foreach ($frCol in $scenariiContentCols.Keys) { + $transCols = $scenariiContentCols[$frCol] + $frFilled = ($scenarii | Where-Object { $_.$frCol -and $_.$frCol.Trim() -ne "" }).Count + + $langCounts = @() + foreach ($transCol in $transCols) { + $count = ($scenarii | Where-Object { + $_.$frCol -and $_.$frCol.Trim() -ne "" -and + $_.$transCol -and $_.$transCol.Trim() -ne "" + }).Count + $langCounts += $count + } + $langStr = $langCounts -join " | " + [void]$report.AppendLine("| $frCol | $frFilled | $langStr |") +} +[void]$report.AppendLine("") + +# Virtues coverage +$virtuesContentCols = @{ + "title_fr" = @("title_en", "title_ru", "title_pt", "title_ar", "title_es", "title_zh", "title_fa") + "description_fr" = @("description_en", "description_ru", "description_pt", "description_ar", "description_es", "description_zh", "description_fa") + "remark_fr" = @("remark_en", "remark_ru", "remark_pt", "remark_ar", "remark_es", "remark_zh", "remark_fa") +} + +[void]$report.AppendLine("### Virtues — Content field coverage") +[void]$report.AppendLine("") +[void]$report.AppendLine("| Field | FR filled | EN | RU | PT | AR | ES | ZH | FA |") +[void]$report.AppendLine("|-------|----------:|---:|---:|---:|---:|---:|---:|---:|") + +foreach ($frCol in $virtuesContentCols.Keys) { + $transCols = $virtuesContentCols[$frCol] + $frFilled = ($virtues | Where-Object { $_.$frCol -and $_.$frCol.Trim() -ne "" }).Count + + $langCounts = @() + foreach ($transCol in $transCols) { + $count = ($virtues | Where-Object { + $_.$frCol -and $_.$frCol.Trim() -ne "" -and + $_.$transCol -and $_.$transCol.Trim() -ne "" + }).Count + $langCounts += $count + } + $langStr = $langCounts -join " | " + [void]$report.AppendLine("| $frCol | $frFilled | $langStr |") +} +[void]$report.AppendLine("") + +# Rules coverage +[void]$report.AppendLine("### Rules — Content field coverage") +[void]$report.AppendLine("") +[void]$report.AppendLine("| Field | FR filled | EN | RU | PT | AR | ES | ZH | FA |") +[void]$report.AppendLine("|-------|----------:|---:|---:|---:|---:|---:|---:|---:|") + +$rulesContentCols = @{ + "Text" = @("Text_en", "Text_ru", "Text_pt", "Text_ar", "Text_es", "Text_zh", "Text_fa") +} + +foreach ($frCol in $rulesContentCols.Keys) { + $transCols = $rulesContentCols[$frCol] + $frFilled = ($rules | Where-Object { $_.$frCol -and $_.$frCol.Trim() -ne "" }).Count + + $langCounts = @() + foreach ($transCol in $transCols) { + $count = ($rules | Where-Object { + $_.$frCol -and $_.$frCol.Trim() -ne "" -and + $_.$transCol -and $_.$transCol.Trim() -ne "" + }).Count + $langCounts += $count + } + $langStr = $langCounts -join " | " + [void]$report.AppendLine("| $frCol | $frFilled | $langStr |") +} +[void]$report.AppendLine("") + +# ── 7. SUMMARY ─────────────────────────────────────────────────────────────── + +$totalInconsistencies = $allFallacies.Count + $allScenarii.Count + $allVirtues.Count + $headingIssues.Count + $baratineurIssues + $piocheurIssues + +[void]$report.AppendLine("## 7. Summary & Worklist") +[void]$report.AppendLine("") +[void]$report.AppendLine("| Dataset | Inconsistencies | Type |") +[void]$report.AppendLine("|---------|----------------:|------|") +[void]$report.AppendLine("| Fallacies (families) | $($allFallacies.Count) | Terminology |") +[void]$report.AppendLine("| Scenarii (categories) | $($allScenarii.Count) | Terminology |") +[void]$report.AppendLine("| Virtues (families) | $($allVirtues.Count) | Terminology |") +[void]$report.AppendLine("| Rules (headings) | $($headingIssues.Count) | Structure |") +[void]$report.AppendLine("| Scenarii (baratineur) | $baratineurIssues languages | Role term |") +[void]$report.AppendLine("| Scenarii (piocheur) | $piocheurIssues languages | Role term |") +[void]$report.AppendLine("| **TOTAL** | **$totalInconsistencies** | |") +[void]$report.AppendLine("") + +if ($totalInconsistencies -eq 0) { + [void]$report.AppendLine("### Conclusion") + [void]$report.AppendLine("") + [void]$report.AppendLine("✅ **All translations are terminologically consistent.** No worklist needed.") + [void]$report.AppendLine("") + [void]$report.AppendLine("The translations across all 4 datasets and 8 languages use consistent terminology for:") + [void]$report.AppendLine("- Fallacy/Virtue family, sub-family, and sub-sub-family labels") + [void]$report.AppendLine("- Scenarii categories and sub-categories") + [void]$report.AppendLine("- Role terms (baratineur/piocheur)") + [void]$report.AppendLine("- Rules heading structure") + [void]$report.AppendLine("") + [void]$report.AppendLine("**Next step (#192 Étape 2)**: Smoke-test the OpenAI key + gpt-5.5 (3-5 cells) to validate the pipeline, then proceed to quality improvement passes if desired.") +} else { + [void]$report.AppendLine("### Recommended Actions") + [void]$report.AppendLine("") + [void]$report.AppendLine("1. Review each inconsistency above and decide: canonical variant or intentional variation?") + [void]$report.AppendLine("2. For genuine inconsistencies, create a worklist for gpt-5.5 re-translation passes") + [void]$report.AppendLine("3. Rules heading mismatches may indicate structural differences (not just terminology)") + [void]$report.AppendLine("4. Role terms (baratineur/piocheur) should be standardized to ONE canonical translation per language") + [void]$report.AppendLine("") +} + +[void]$report.AppendLine("---") +[void]$report.AppendLine("") +[void]$report.AppendLine("*Generated by TranslationConsistencyReport.ps1 — #192 Étape 1*") + +# ── Write output ───────────────────────────────────────────────────────────── + +$reportPath = Join-Path $OutputDir "translation-consistency-audit-192.md" +[System.IO.File]::WriteAllText($reportPath, $report.ToString(), [System.Text.UTF8Encoding]::new($false)) + +Write-Host "" +Write-Host "=== Report written to: $reportPath ===" -ForegroundColor Green +Write-Host "Total inconsistencies: $totalInconsistencies" -ForegroundColor $(if ($totalInconsistencies -gt 0) {"Yellow"} else {"Green"}) diff --git a/translation-consistency-audit-192.md b/translation-consistency-audit-192.md new file mode 100644 index 000000000..a4bbb5cb8 --- /dev/null +++ b/translation-consistency-audit-192.md @@ -0,0 +1,2298 @@ +# Translation Consistency Audit — #192 Étape 1 + +> **Date**: 2026-06-03 04:59 +> **Scope**: Read-only terminological consistency analysis +> **Datasets**: Fallacies, Scenarii, Virtues, Rules (8 languages) + +## 1. Fallacies — Taxonomy + +Records: 1408 | Columns: Fallacies + +### Family taxonomy (Famille / Sous-Famille / Soussousfamille) + +Unique FR families: 8 | Inconsistencies: 0 +Unique FR sub-families: 21 | Inconsistencies: 0 +Unique FR sub-sub-families: 63 | Inconsistencies: 0 + +## 2. Scenarii — Cards + +Records: 167 + +### Category taxonomy (catégorie / sous-catégorie) + +Unique FR categories: 7 | Inconsistencies: 7 +Unique FR sub-categories: 21 | Inconsistencies: 18 + +| # | Source (FR) | Column | Lang | Variants | +|---|-------------|--------|------|----------| +| 1 | 20e et 21e siècle | subcategory_pt | pt | Séculos XX e XXI (5x) / séculos XX e XXI (5x) | +| 2 | antiquité | subcategory_pt | pt | Antiguidade (6x) / antiguidade (6x) | +| 3 | campagne | subcategory_pt | pt | Campanha (4x) / campanha (4x) | +| 4 | cinéma & télévision | subcategory_pt | pt | Cinema e televisão (7x) / cinema e televisão (7x) | +| 5 | contes | subcategory_pt | pt | Contos e literatura (7x) / contos (3x) | +| 6 | drague et séduction | subcategory_pt | pt | Paquera e sedução (6x) / engate e sedução (3x) | +| 7 | gestion et administration | subcategory_pt | pt | Gestão e administração (9x) / gestão e administração (9x) | +| 8 | histoire | category_pt | pt | história (17x) / História (17x) | +| 9 | interactions professionnelles | subcategory_pt | pt | Interações profissionais (14x) / interações profissionais (14x) | +| 10 | littérature | subcategory_pt | pt | literatura (6x) / Literatura (6x) | +| 11 | loisirs et espace public | subcategory_pt | pt | Lazer e espaço público (5x) / lazer e espaço público (5x) | +| 12 | manoeuvres et collusion | subcategory_pt | pt | Manobras e conluio (6x) / manobras e conluio (6x) | +| 13 | moyen-âge et temps modernes | subcategory_pt | pt | Idade Média e era moderna (5x) / Idade Média e Tempos Modernos (1x) | +| 14 | mythologie | category_pt | pt | Mitologia (27x) / mitologia (27x) | +| 15 | politique | category_pt | pt | Política (14x) / política (14x) | +| 16 | pop culture | category_pt | pt | Cultura pop (18x) / cultura pop (18x) | +| 17 | relation intime | category_pt | pt | Relacionamento íntimo (16x) / relação íntima (20x) | +| 18 | relations au travail | subcategory_pt | pt | Relações de trabalho (6x) / relações no trabalho (2x) | +| 19 | religions | subcategory_pt | pt | Religião (7x) / religiões (4x) | +| 20 | romance | subcategory_pt | pt | Romance (11x) / romance (11x) | +| 21 | science | subcategory_pt | pt | Ciência (6x) / ciência (6x) | +| 22 | vie de couple | subcategory_pt | pt | Vida a dois (6x) / vida de casal (10x) / vida a dois (6x) | +| 23 | vie personnelle | category_pt | pt | vida pessoal (25x) / Vida pessoal (25x) | +| 24 | vie professionnelle | category_pt | pt | Vida profissional (30x) / vida profissional (30x) | +| 25 | voisins et amis | subcategory_pt | pt | vizinhos e amigos (11x) / Vizinhos e amigos (11x) | + +## 3. Virtues — Taxonomy + +Records: 223 + +### Family taxonomy (family_fr / subfamily_fr / subsubfamily_fr) + +Unique FR families: 8 | Inconsistencies: 9 +Unique FR sub-families: 21 | Inconsistencies: 6 +Unique FR sub-sub-families: 63 | Inconsistencies: 6 + +| # | Source (FR) | Column | Lang | Variants | +|---|-------------|--------|------|----------| +| 1 | Argument pertinent | family_ar | ar | حجة وجيهة الصلة (1x) / حجة ملائمة (32x) | +| 2 | Déductions correctes | subfamily_ar | ar | استنتاجات صحيحة (28x) / استنباطات صحيحة (15x) / استنتاجات سليمة (3x) | +| 3 | Déductions correctes | subfamily_fa | fa | استنتاج‌های درست (28x) / استنتاج‌های صحیح (18x) | +| 4 | Définitions recevables | subfamily_es | es | Definiciones admisibles (1x) / Definiciones aceptables (6x) | +| 5 | Échange enrichissant | family_fa | fa | تبادل پربار (5x) / تبادل غنی‌ساز (39x) | +| 6 | Échange enrichissant | family_zh | zh | 富有成效的交流 (5x) / 充实性交流 (39x) | +| 7 | Effort d'objectivité | subfamily_ar | ar | جهد موضوعي (10x) / السعي إلى الموضوعية (2x) | +| 8 | Effort d'objectivité | subfamily_fa | fa | کوشش برای عینیت (10x) / تلاش برای عینیت‌گرایی (2x) | +| 9 | Effort d'objectivité | subfamily_zh | zh | 客观性努力 (10x) / 力求客观 (2x) | +| 10 | Honnêteté intellectuelle | family_ar | ar | النزاهة الفكرية (3x) / أمانة فكرية (24x) | +| 11 | Mise à distance des idéologies | subsubfamily_ar | ar | إبعاد الأيديولوجيات (3x) / النأي عن الأيديولوجيات (2x) | +| 12 | Mise à distance des idéologies | subsubfamily_fa | fa | فاصله‌گذاری از ایدئولوژی‌ها (3x) / فاصله‌گیری از ایدئولوژی‌ها (2x) | +| 13 | Objectif non complaisant | subsubfamily_ru | ru | Непотворствующая цель (1x) / Некомплаентная объективность (2x) | +| 14 | Présentation intègre | family_fa | fa | ارائهٔ درستکارانه (1x) / ارائه صادقانه (24x) | +| 15 | Présentation intègre | family_zh | zh | 诚信呈现 (1x) / 诚实陈述 (24x) | +| 16 | Raisonnement concluant | subsubfamily_ar | ar | استدلال حاسم (33x) / استدلال قاطع (4x) / استدلال منتج (4x) | +| 17 | Raisonnement concluant | subsubfamily_fa | fa | استدلال نتیجه‌بخش (26x) / استدلال قاطع (7x) / استدلال منتج (8x) | +| 18 | Raisonnement concluant | subsubfamily_zh | zh | 有结论力的推理 (19x) / 结论性推理 (14x) / 结论成立的推理 (8x) | +| 19 | Raisonnement valide | family_ar | ar | الاستدلال الصحيح (8x) / استدلال صحيح (47x) | +| 20 | Raisonnement valide | family_fa | fa | استدلال معتبر (23x) / استنتاج معتبر (32x) | +| 21 | Rigueur mathématique | family_es | es | Rigor matemático (1x) / Exactitud matemática (19x) | + +## 4. Rules — Cards + +Records: 24 + +> Rules contain Markdown text blocks, not taxonomy labels. Consistency analysis focuses on heading structure and recurring terms across languages. + +### Heading structure comparison + +All Rules have consistent heading counts across 8 languages. ✅ + +## 5. Cross-dataset Role Terms (Scenarii) + +> The Scenarii roles aratineur and piocheur appear in all 167 records. Each should have exactly ONE canonical translation per language. + +### aratineur translations + +| Language | Unique translations | Consistent? | +|----------|-------------------:|:-----------:| +| en | 149 | ⚠️ | +| ru | 145 | ⚠️ | +| pt | 148 | ⚠️ | +| ar | 148 | ⚠️ | +| es | 146 | ⚠️ | +| zh | 147 | ⚠️ | +| fa | 148 | ⚠️ | + +
Variant details + +**en**: +- `Caesar's mother` (1×) +- `Cato` (1×) +- `A gladiator` (1×) +- `A cunning sophist` (1×) +- `A Greek spy` (1×) +- `A passionate mystic` (1×) +- `Joan of Arc` (1×) +- `A slave driver` (1×) +- `A military advisor` (1×) +- `A misogynistic revolutionary` (1×) +- `Louis XVI` (1×) +- `A cook` (1×) +- `A contradictor` (1×) +- `Truman` (1×) +- `the marshal` (1×) +- `The delegate of a very small country` (1×) +- `The captain of the Titanic` (1×) +- `Bluebeard's wife` (1×) +- `The witch` (1×) +- `Sherazade` (1×) +- `Grumpy` (1×) +- `Little Thumb's brother` (1×) +- `The Yeti` (1×) +- `Sleeping Beauty` (1×) +- `The wolf` (1×) +- `The mayor` (2×) +- `The con man` (1×) +- `the devil` (1×) +- `Judah` (1×) +- `the snake` (1×) +- `Cain` (1×) +- `Salomon` (1×) +- `The last prophet` (1×) +- `The suitor` (1×) +- `Medusa, the Gorgon` (1×) +- `Sisyphus` (1×) +- `Loki` (1×) +- `Prometheus` (1×) +- `The Wizard of Oz` (1×) +- `Lord Voldemort` (1×) +- `The Cheshire Cat` (1×) +- `Professor Moriarty` (1×) +- `Don Juan` (1×) +- `A vampire` (1×) +- `you` (13×) +- `You` (13×) +- `A pizza boy` (1×) +- `An ugly person` (1×) +- `The musician` (1×) +- `The dinner host` (1×) +- `The lover` (1×) +- `the one who doesn't want to get married` (3×) +- `A collector` (1×) +- `The cat lover` (1×) +- `The dog lover` (1×) +- `The lazy partner` (2×) +- `The Amnesiac` (1×) +- `The Lazy Partner` (2×) +- `The Opportunistic Liar` (1×) +- `The Chef` (1×) +- `The mystery shopper` (1×) +- `The animal lover` (1×) +- `The plant lover` (1×) +- `The T-shirt collector` (1×) +- `A high roller` (1×) +- `The flat-out refuser` (1×) +- `A broke person` (1×) +- `Some random person` (3×) +- `A woman` (1×) +- `The adventurous partner` (1×) +- `the seller` (1×) +- `A school principal` (1×) +- `A tattoo artist` (1×) +- `The renowned professor` (1×) +- `The flying-car salesman` (1×) +- `The pet sitter` (1×) +- `The pizza delivery guy` (1×) +- `The cleaner` (1×) +- `Pizza delivery guy` (1×) +- `The car washer` (1×) +- `A travel agent` (1×) +- `A panicked babysitter` (1×) +- `An umbrella seller` (1×) +- `The jealous teacher` (1×) +- `the captain` (1×) +- `The Boss` (3×) +- `The boss` (3×) +- `A sick employee` (1×) +- `A corrupt committee member` (1×) +- `The ambitious colleague` (1×) +- `A unicorn` (1×) +- `An unscrupulous buyer` (1×) +- `The researcher` (1×) +- `A ski champion` (1×) +- `A fan` (1×) +- `A Roland-Garros executive` (1×) +- `A professional swimmer` (1×) +- `Captain Hadock` (1×) +- `the smurf` (1×) +- `Obelix` (1×) +- `A cape supporter` (1×) +- `A ghost` (1×) +- `the case thief` (1×) +- `The leader of the resistance` (1×) +- `poor thing` (1×) +- `Darth Vader` (1×) +- `Ross` (1×) +- `Dreamy director` (1×) +- `Gollum` (1×) +- `A Flat-Earther` (1×) +- `An anti -vaccin` (1×) +- `The clone` (1×) +- `An anti-5g` (1×) +- `A conspiracy theorist` (1×) +- `An amateur biologist` (1×) +- `A corrupt MP` (1×) +- `the government spokesperson` (1×) +- `The Minister of Culture` (1×) +- `The Minister of Health` (1×) +- `The ex-candidate` (1×) +- `The opportunistic candidate` (1×) +- `A political leader` (2×) +- `A corrupt politician` (1×) +- `A candidate` (1×) +- `The traitor` (1×) +- `The candidate` (1×) +- `A politician` (1×) +- `the parent` (2×) +- `the child` (1×) +- `Santa Claus` (1×) +- `The mother` (1×) +- `The parent` (2×) +- `The new spouse` (1×) +- `A cat` (1×) +- `Uncle` (1×) +- `The envious neighbor` (1×) +- `The negligent owner` (1×) +- `The nighttime handyman` (1×) +- `A vegetarian` (1×) +- `A foie gras producer` (1×) +- `Someone` (1×) +- `A party-loving neighbor` (1×) +- `A gardening neighbor` (1×) +- `The parrot's owner` (1×) +- `A neighbor` (1×) +- `A smoker` (1×) +- `A hurried client` (1×) +- `The goalkeeper` (1×) +- `A costumed guest` (1×) + +**ru**: +- `Мать Цезаря` (1×) +- `Катон` (1×) +- `Гладиатор` (1×) +- `Хитрый софист` (1×) +- `Греческий шпион` (1×) +- `Увлечённый мистик` (1×) +- `Жанна д'Арк` (1×) +- `Рабовладелец` (1×) +- `Военный советник` (1×) +- `Революционер-женоненавистник` (1×) +- `Людовик XVI` (1×) +- `Повар` (1×) +- `Несогласный` (1×) +- `Президент Соединенных Штатов` (1×) +- `Маршал Петен` (1×) +- `Делегат очень маленькой страны` (1×) +- `Капитан «Титаника»` (1×) +- `Жена Синей Бороды` (1×) +- `Ведьма` (1×) +- `Шахерезада` (1×) +- `Гном` (1×) +- `Брат Мальчика-с-пальчика` (1×) +- `Йети` (1×) +- `Спящая красавица` (1×) +- `Волк` (1×) +- `Мэр` (2×) +- `Мошенник` (1×) +- `Дьявол` (1×) +- `Иуда` (1×) +- `Змей` (1×) +- `Каин` (1×) +- `Судья по гражданским делам` (1×) +- `Новый пророк` (1×) +- `Претендент` (1×) +- `Медуза Горгона` (1×) +- `Сизиф` (1×) +- `Локи` (1×) +- `Прометей` (1×) +- `Волшебник страны Оз` (1×) +- `Лорд Волан-де-Морт` (1×) +- `Чеширский кот` (1×) +- `Профессор Мориарти` (1×) +- `Дон Жуан` (1×) +- `Вампир` (1×) +- `Некто` (14×) +- `Пиццайоло` (1×) +- `Уродливый человек` (1×) +- `Музыкант` (1×) +- `Хозяин ужина` (1×) +- `Влюбленный` (1×) +- `Тот, кто хочет съехаться` (1×) +- `Коллекционер` (1×) +- `Тот, кто не хочет ребенка` (1×) +- `Любитель кошек` (1×) +- `Любитель собак` (1×) +- `Ленивый партнер` (2×) +- `Амнезик` (1×) +- `Лживый приспособленец` (1×) +- `Шеф-повар` (1×) +- `Тайный покупатель` (1×) +- `Друг зверей` (1×) +- `Любитель растений` (1×) +- `Коллекционер футболок` (1×) +- `Игрок` (1×) +- `Категорический отказник` (1×) +- `Человек без гроша` (1×) +- `Кто-то` (3×) +- `Девушка` (1×) +- `Авантюрный партнер` (1×) +- `Тот, кто не хочет вступать в брак` (1×) +- `Продавец` (1×) +- `Директор школы` (1×) +- `Татуировщик` (1×) +- `Известный профессор` (1×) +- `Продавец летающих машин` (1×) +- `Пет-ситтер` (1×) +- `Курьер с пиццей` (1×) +- `Уборщик` (1×) +- `Доставщик пиццы` (1×) +- `Мойщик машин` (1×) +- `Турагент` (1×) +- `Запаниковавший бэбиситтер` (1×) +- `Продавец зонтов` (1×) +- `Завистливый тренер` (1×) +- `Капитан` (1×) +- `Босс` (3×) +- `Больной сотрудник` (1×) +- `Коррумпированный член комитета` (1×) +- `Амбициозный коллега` (1×) +- `Единорог` (1×) +- `Недобросовестный покупатель` (1×) +- `Исследователь` (1×) +- `Чемпион по лыжам` (1×) +- `Фанат` (1×) +- `Руководитель «Ролан Гаррос»` (1×) +- `Профессиональный пловец` (1×) +- `Капитан Хэддок` (1×) +- `Смурфик-бунтарь` (1×) +- `Обеликс` (1×) +- `Сторонник плащей` (1×) +- `Призрак` (1×) +- `Вор, укравший чемоданчик` (1×) +- `Член сопротивления` (1×) +- `Бедный молодой человек` (1×) +- `Дарт Вейдер` (1×) +- `Росс` (1×) +- `Режиссёр-мечтатель` (1×) +- `Голлум` (1×) +- `Плоскоземельщик` (1×) +- `Антивакс` (1×) +- `Клон` (1×) +- `Анти-5G` (1×) +- `Конспиролог` (1×) +- `Биолог-любитель` (1×) +- `Коррумпированный заместитель` (1×) +- `Представитель правительства` (1×) +- `Министр культуры` (1×) +- `Министр здравоохранения` (1×) +- `Экс-кандидат` (1×) +- `Кандидат-оппортунист` (1×) +- `Политический лидер` (2×) +- `Коррумпированный политик` (1×) +- `Кандидат` (2×) +- `Предатель` (1×) +- `Политик` (1×) +- `Родитель` (2×) +- `Ребенок` (1×) +- `Дед Мороз` (1×) +- `Мама` (1×) +- `Новый супруг` (1×) +- `Кошка` (1×) +- `Дядя` (1×) +- `Завистливый сосед` (1×) +- `Нерадивый хозяин` (1×) +- `Ночной мастер` (1×) +- `Вегетарианец` (1×) +- `Фермер` (1×) +- `Сосед-тусовщик` (1×) +- `Сосед-садовод` (1×) +- `Владелец попугая` (1×) +- `Сосед` (1×) +- `Курильщик` (1×) +- `Особый клиент` (1×) +- `Вратарь` (1×) +- `Гость в костюме` (1×) + +**pt**: +- `Mãe de César` (1×) +- `Cato` (1×) +- `Um gladiador` (1×) +- `Um sofista astuto` (1×) +- `Um espião grego` (1×) +- `Um místico apaixonado` (1×) +- `Jeanne d'Arc` (1×) +- `Uma escravidão` (1×) +- `Um consultor militar` (1×) +- `Um revolucionário misógino` (1×) +- `Louis XVI` (1×) +- `Um cozinheiro` (1×) +- `Um contraditador` (1×) +- `O presidente dos Estados Unidos` (1×) +- `O Marshall` (1×) +- `O delegado de um país muito pequeno` (1×) +- `O capitão do Titanic` (1×) +- `A mulher da barba azul` (1×) +- `A bruxa` (1×) +- `Shéhérazade` (1×) +- `Um anão da floresta` (1×) +- `Um pequeno irmão do polegar` (1×) +- `O yeti` (1×) +- `A bela Adormecida` (1×) +- `O lobo` (1×) +- `O Presidente da Câmara` (1×) +- `O vigarista` (1×) +- `O diabo` (1×) +- `Judá` (1×) +- `A cobra` (1×) +- `Caim` (1×) +- `Um juiz de negócios da família.` (1×) +- `O novo profeta` (1×) +- `O pretendente` (1×) +- `Medusa, a Górgona` (1×) +- `Sísifo` (1×) +- `Loki` (1×) +- `Prometeu` (1×) +- `O Feiticeiro de Oz` (1×) +- `Lord Voldemort` (1×) +- `O Gato de Cheshire` (1×) +- `Professor Moriarty` (1×) +- `Don Juan` (1×) +- `Um vampiro` (1×) +- `Alguém` (14×) +- `Um pizzaiolo` (1×) +- `Uma pessoa feia` (1×) +- `O músico` (1×) +- `O anfitrião do jantar` (1×) +- `O apaixonado` (1×) +- `Aquele que quer se mudar` (1×) +- `Um colecionador` (1×) +- `Aquele que não quer uma criança` (1×) +- `O amigo dos gatos` (1×) +- `O apaixonado por cães` (1×) +- `O parceiro mandrião` (1×) +- `O amnésico` (1×) +- `O companheiro preguiçoso` (1×) +- `O mentiroso oportunista` (1×) +- `O chefe` (3×) +- `O cliente mistério` (1×) +- `O amigo dos animais` (1×) +- `O apaixonado por plantas` (1×) +- `O colecionador de T-shirts` (1×) +- `Um flambper` (1×) +- `A Recusa Categórica` (1×) +- `Uma pessoa sem dinheiro` (1×) +- `Uma pessoa qualquer` (3×) +- `Uma mulher` (1×) +- `O parceiro aventureiro` (1×) +- `Aquele que não quer se casar` (1×) +- `vendedor` (1×) +- `Um diretor da escola` (1×) +- `Um tatuador` (1×) +- `O renomado professor` (1×) +- `O vendedor de carros voadores` (1×) +- `O cuidador de animais de estimação` (1×) +- `O entregador de pizzas` (1×) +- `O funcionário da limpeza` (1×) +- `Estafeta de pizzas` (1×) +- `O lavador de carros` (1×) +- `Um agente de viagens` (1×) +- `Um babysitter em pânico` (1×) +- `Um vendedor de guarda-chuvas` (1×) +- `O professor invejoso` (1×) +- `O capitão` (1×) +- `Um funcionário doente` (1×) +- `Um membro do comité corrupto` (1×) +- `O colega ambicioso` (1×) +- `Um unicórnio` (1×) +- `Um comprador sem escrúpulos` (1×) +- `O investigador` (1×) +- `O patrão` (1×) +- `Um campeão de esqui` (1×) +- `Um fã` (1×) +- `Um responsável de Roland-Garros` (1×) +- `Um nadador profissional` (1×) +- `Capitão Hadock` (1×) +- `Shtroumphissime` (1×) +- `Obelix` (1×) +- `Um defensor das capas` (1×) +- `fantasma` (1×) +- `O ladrão do caso` (1×) +- `Um membro da resistência` (1×) +- `Um jovem pobre` (1×) +- `Dark Vador` (1×) +- `Ross` (1×) +- `Realizador sonhador` (1×) +- `Gollum` (1×) +- `Uma terra plana` (1×) +- `Um anti -vaccina` (1×) +- `O clone` (1×) +- `Um anti-5g` (1×) +- `Um teórico da conspiração` (1×) +- `Um biólogo amador` (1×) +- `Um deputado corrupto` (1×) +- `Porta -voz do governo` (1×) +- `O ministro da cultura` (1×) +- `O Ministro da Saúde` (1×) +- `O ex-candidato` (1×) +- `O prefeito` (1×) +- `O candidato oportunista` (1×) +- `Um líder político` (2×) +- `Um político corrupto` (1×) +- `Um candidato` (1×) +- `O traidor` (1×) +- `O candidato` (1×) +- `Um político` (1×) +- `O pai` (2×) +- `A criança` (1×) +- `Papai Noel` (1×) +- `A mãe` (1×) +- `O novo cônjuge` (1×) +- `Um gato` (1×) +- `O tio` (1×) +- `O vizinho invejoso` (1×) +- `O dono negligente` (1×) +- `O bricoleiro noturno` (1×) +- `Um vegetariano` (1×) +- `Um criador de patos` (1×) +- `Um vizinho festeiro` (1×) +- `Um vizinho jardineiro` (1×) +- `O dono do papagaio` (1×) +- `Um vizinho` (1×) +- `Um fumante` (1×) +- `Um cliente apressado` (1×) +- `O guarda-redes` (1×) +- `Um convidado disfarçado` (1×) + +**ar**: +- `أوريليا كوتا، أم قيصر` (1×) +- `كاتون` (1×) +- `مصارع روماني` (1×) +- `سفسطائي ماكر` (1×) +- `جاسوس يوناني` (1×) +- `متصوف شغوف` (1×) +- `جان دارك` (1×) +- `تاجر عبيد` (1×) +- `مستشار عسكري` (1×) +- `ثوري ذكوري` (1×) +- `لويس السادس عشر` (1×) +- `طاهٍ` (1×) +- `مُعارِض` (1×) +- `رئيس الولايات المتحدة` (1×) +- `المارشال` (1×) +- `مندوب بلد صغير جدًا` (1×) +- `قبطان التايتانيك` (1×) +- `زوجة ذي اللحية الزرقاء` (1×) +- `الساحرة` (1×) +- `شهرزاد` (1×) +- `قزم من أقزام الغابة` (1×) +- `أحد إخوة عقلة الإصبع` (1×) +- `اليتي` (1×) +- `الجميلة النائمة` (1×) +- `الذئب` (1×) +- `العمدة` (1×) +- `المحتال` (1×) +- `الشيطان` (1×) +- `يهوذا` (1×) +- `الحية` (1×) +- `قابيل` (1×) +- `قاضٍ في شؤون الأسرة` (1×) +- `النبي الجديد` (1×) +- `الخاطب` (1×) +- `ميدوسا، الغورغونة` (1×) +- `سيزيف` (1×) +- `لوكي` (1×) +- `بروميثيوس` (1×) +- `ساحر أوز` (1×) +- `اللورد فولدمورت` (1×) +- `قط شيشاير` (1×) +- `البروفيسور موريارتي` (1×) +- `دون جوان` (1×) +- `مصّاص دماء` (1×) +- `شخص عادي` (11×) +- `صانع بيتزا` (1×) +- `شخص قبيح` (1×) +- `الموسيقي` (1×) +- `مضيف العشاء` (1×) +- `العاشق` (1×) +- `من يريد الانتقال للسكن مع الآخر` (1×) +- `جامع مقتنيات` (1×) +- `من لا يريد إنجاب طفل` (1×) +- `صديق القطط` (1×) +- `عاشق الكلاب` (1×) +- `الشريك الكسول` (2×) +- `فاقد الذاكرة` (1×) +- `الكذّاب الانتهازي` (1×) +- `الطاهي` (1×) +- `المشتري المتخفّي` (1×) +- `صديق الحيوانات` (1×) +- `عاشق النباتات` (1×) +- `جامع القمصان` (1×) +- `مقامر مبذّر` (1×) +- `الحبيب السابق المتلاعب` (1×) +- `الرافض رفضًا قاطعًا` (1×) +- `شخص مفلس` (1×) +- `شخص ما` (5×) +- `امرأة` (1×) +- `الشريك المغامر` (1×) +- `من لا يريد الزواج` (1×) +- `البائع` (1×) +- `مدير مدرسة` (1×) +- `وشّام` (1×) +- `الأستاذ الشهير` (1×) +- `بائع السيارات الطائرة` (1×) +- `جليس الحيوانات الأليفة` (1×) +- `عامل توصيل البيتزا` (1×) +- `عامل النظافة` (1×) +- `موصل بيتزا` (1×) +- `غاسل السيارات` (1×) +- `وكيل سفر` (1×) +- `جليس أطفال مذعور` (1×) +- `بائع مظلات` (1×) +- `الأستاذ الغيور` (1×) +- `القبطان` (1×) +- `المدير` (3×) +- `موظف مريض` (1×) +- `عضو فاسد في اللجنة` (1×) +- `الزميل الطموح` (1×) +- `وحيد قرن` (1×) +- `مشترٍ قليل الذمة` (1×) +- `الباحث` (1×) +- `بطل تزلّج` (1×) +- `مشجّع` (1×) +- `مسؤول في رولان غاروس` (1×) +- `سبّاح محترف` (1×) +- `الكابتن هادوك` (1×) +- `السنفور الأعظم` (1×) +- `أوبيليكس` (1×) +- `مؤيد للعباءات` (1×) +- `شبح` (1×) +- `سارق الحقيبة` (1×) +- `عضو في المقاومة` (1×) +- `شاب فقير` (1×) +- `دارث فيدر` (1×) +- `روس` (1×) +- `مخرج حالم` (1×) +- `غولوم` (1×) +- `مؤمن بالأرض المسطحة` (1×) +- `مناهض للقاحات` (1×) +- `النسخة المستنسخة` (1×) +- `مناهض للجيل الخامس` (1×) +- `مؤمن بنظرية مؤامرة` (1×) +- `عالم أحياء هاوٍ` (1×) +- `نائب فاسد` (1×) +- `الناطق باسم الحكومة` (1×) +- `وزير الثقافة` (1×) +- `وزير الصحة` (1×) +- `المرشح السابق` (1×) +- `رئيس البلدية` (1×) +- `المرشح الانتهازي` (1×) +- `زعيم سياسي` (2×) +- `سياسي فاسد` (1×) +- `مرشح` (1×) +- `الخائن` (1×) +- `المرشح` (1×) +- `سياسي` (1×) +- `الوالد` (2×) +- `الطفل` (1×) +- `بابا نويل` (1×) +- `الأم` (1×) +- `الزوج الجديد` (1×) +- `قط` (1×) +- `العم` (1×) +- `الجار الحسود` (1×) +- `المالك المهمل` (1×) +- `الهاوي الذي يصلح ليلًا` (1×) +- `نباتي` (1×) +- `مربّي بط` (1×) +- `جار يحب الحفلات` (1×) +- `جار بستاني` (1×) +- `صاحب الببغاء` (1×) +- `أحد الجيران` (1×) +- `مدخن` (1×) +- `الزبون غير الصبور` (1×) +- `حارس المرمى` (1×) +- `ضيف متنكر` (1×) + +**es**: +- `Aurelia Cota, madre de César` (1×) +- `Catón` (1×) +- `Un gladiador` (1×) +- `Un sofista astuto` (1×) +- `Un espía griego` (1×) +- `Un místico apasionado` (1×) +- `Juana de Arco` (1×) +- `Un esclavista` (1×) +- `Un consejero militar` (1×) +- `Un revolucionario machista` (1×) +- `Luis XVI` (1×) +- `Un cocinero` (1×) +- `Un contradictor` (1×) +- `El presidente de Estados Unidos` (1×) +- `El mariscal` (1×) +- `El delegado de un país diminuto` (1×) +- `El capitán del Titanic` (1×) +- `La esposa de Barba Azul` (1×) +- `La bruja` (1×) +- `Sherezade` (1×) +- `Un enano del bosque` (1×) +- `Un hermano de Pulgarcito` (1×) +- `El Yeti` (1×) +- `La bella durmiente` (1×) +- `El lobo` (1×) +- `El alcalde` (2×) +- `El estafador` (1×) +- `El diablo` (1×) +- `Judas` (1×) +- `La serpiente` (1×) +- `Caín` (1×) +- `Un juez de asuntos familiares` (1×) +- `El nuevo profeta` (1×) +- `El pretendiente` (1×) +- `Medusa, la Gorgona` (1×) +- `Sísifo` (1×) +- `Loki` (1×) +- `Prometeo` (1×) +- `El mago de Oz` (1×) +- `Lord Voldemort` (1×) +- `El gato de Cheshire` (1×) +- `Profesor Moriarty` (1×) +- `Don Juan` (1×) +- `Un vampiro` (1×) +- `Una persona cualquiera` (16×) +- `Un pizzero` (1×) +- `Una persona fea` (1×) +- `El músico` (1×) +- `El anfitrión de la cena` (1×) +- `La persona enamorada` (1×) +- `Quien quiere mudarse` (1×) +- `Una persona coleccionista` (1×) +- `Quien no quiere tener hijos` (1×) +- `La persona amiga de los gatos` (1×) +- `La persona amante de los perros` (1×) +- `La pareja perezosa` (2×) +- `La persona amnésica` (1×) +- `La persona mentirosa oportunista` (1×) +- `El chef` (1×) +- `El comprador misterioso` (1×) +- `El amigo de los animales` (1×) +- `El amante de las plantas` (1×) +- `El coleccionista de camisetas` (1×) +- `Un derrochador` (1×) +- `El ex manipulador` (1×) +- `La negativa rotunda` (1×) +- `Una persona sin dinero` (1×) +- `Una mujer` (1×) +- `La pareja aventurera` (1×) +- `Quien no quiere casarse` (1×) +- `el vendedor` (1×) +- `Un director de escuela` (1×) +- `Un tatuador` (1×) +- `El profesor reputado` (1×) +- `El vendedor de coches voladores` (1×) +- `El cuidador de mascotas` (1×) +- `El repartidor de pizza` (1×) +- `El empleado de limpieza` (1×) +- `Repartidor de pizza` (1×) +- `El lavacoches` (1×) +- `Un agente de viajes` (1×) +- `Un niñero en pánico` (1×) +- `Un vendedor de paraguas` (1×) +- `El profesor celoso` (1×) +- `El capitán` (1×) +- `El jefe` (3×) +- `Un empleado enfermo` (1×) +- `Un miembro corrupto del comité` (1×) +- `El colega ambicioso` (1×) +- `Un unicornio` (1×) +- `Un comprador con pocos escrúpulos` (1×) +- `El investigador` (1×) +- `Un campeón de esquí` (1×) +- `Un fan` (1×) +- `Un directivo de Roland-Garros` (1×) +- `Un nadador profesional` (1×) +- `Capitán Haddock` (1×) +- `El Pitufísimo` (1×) +- `Obélix` (1×) +- `Un procapa` (1×) +- `fantasma` (1×) +- `El ladrón del maletín` (1×) +- `Un miembro de la resistencia` (1×) +- `Un joven pobre` (1×) +- `Darth Vader` (1×) +- `Ross` (1×) +- `Director soñador` (1×) +- `Gollum` (1×) +- `Un terraplanista` (1×) +- `Un antivacunas` (1×) +- `El clon` (1×) +- `Un anti-5G` (1×) +- `Un conspiranoico` (1×) +- `Un biólogo aficionado` (1×) +- `Un diputado corrupto` (1×) +- `El portavoz del gobierno` (1×) +- `El ministro de Cultura` (1×) +- `El ministro de Sanidad` (1×) +- `El excandidato` (1×) +- `El candidato oportunista` (1×) +- `Un líder político` (2×) +- `Un político corrupto` (1×) +- `Un candidato` (1×) +- `El traidor` (1×) +- `El candidato` (1×) +- `Un político` (1×) +- `El progenitor` (2×) +- `El niño` (1×) +- `Papá Noel` (1×) +- `La madre` (1×) +- `El nuevo cónyuge` (1×) +- `Un gato` (1×) +- `El tío` (1×) +- `El vecino envidioso` (1×) +- `El dueño negligente` (1×) +- `El manitas nocturno` (1×) +- `Un vegetariano` (1×) +- `Un criador de patos` (1×) +- `Un vecino fiestero` (1×) +- `Un vecino jardinero` (1×) +- `El dueño del loro` (1×) +- `Un vecino` (1×) +- `Un fumador` (1×) +- `El cliente impaciente` (1×) +- `El portero` (1×) +- `Un invitado disfrazado` (1×) + +**zh**: +- `凯撒的母亲奥蕾莉亚·科塔` (1×) +- `加图` (1×) +- `一名角斗士` (1×) +- `一位狡猾的智术师` (1×) +- `一名希腊间谍` (1×) +- `一位狂热的神秘主义者` (1×) +- `圣女贞德` (1×) +- `一名奴隶主` (1×) +- `一位军事顾问` (1×) +- `一位大男子主义的革命者` (1×) +- `路易十六` (1×) +- `一位厨师` (1×) +- `一位反对者` (1×) +- `美国总统` (1×) +- `元帅` (1×) +- `一个小小国家的代表` (1×) +- `泰坦尼克号船长` (1×) +- `蓝胡子的妻子` (1×) +- `女巫` (1×) +- `山鲁佐德` (1×) +- `森林里的一个小矮人` (1×) +- `小拇指的一个兄弟` (1×) +- `雪人` (1×) +- `睡美人` (1×) +- `狼` (1×) +- `市长` (2×) +- `骗子` (1×) +- `魔鬼` (1×) +- `犹大` (1×) +- `蛇` (1×) +- `该隐` (1×) +- `一位家事法官` (1×) +- `新先知` (1×) +- `求婚者` (1×) +- `戈耳工美杜莎` (1×) +- `西西弗斯` (1×) +- `洛基` (1×) +- `普罗米修斯` (1×) +- `奥兹国的魔法师` (1×) +- `伏地魔大人` (1×) +- `柴郡猫` (1×) +- `莫里亚蒂教授` (1×) +- `唐璜` (1×) +- `一只吸血鬼` (1×) +- `某个普通人` (12×) +- `一位披萨师傅` (1×) +- `一个长得不好看的人` (1×) +- `那位音乐人` (1×) +- `晚宴主人` (1×) +- `恋爱中的人` (1×) +- `想搬过去同居的人` (1×) +- `一位收藏家` (1×) +- `一个普通人` (1×) +- `不想要孩子的那一方` (1×) +- `猫派人士` (1×) +- `爱狗人士` (1×) +- `懒散的伴侣` (2×) +- `失忆者` (1×) +- `机会主义骗子` (1×) +- `大厨` (1×) +- `神秘买家` (1×) +- `动物之友` (1×) +- `植物爱好者` (1×) +- `T恤收藏家` (1×) +- `一个败家赌徒` (1×) +- `善于操控的前任` (1×) +- `坚决拒婚的人` (1×) +- `一个身无分文的人` (1×) +- `某个人` (3×) +- `一名女性` (1×) +- `爱冒险的伴侣` (1×) +- `不想结婚的那个人` (1×) +- `销售员` (1×) +- `一位校长` (1×) +- `一位纹身师` (1×) +- `那位知名教授` (1×) +- `飞行汽车销售员` (1×) +- `宠物看护人` (1×) +- `披萨外卖员` (2×) +- `清洁工` (1×) +- `洗车工` (1×) +- `一名旅行社代理` (1×) +- `一名惊慌失措的保姆` (1×) +- `一位雨伞推销员` (1×) +- `嫉妒心强的老师` (1×) +- `船长` (1×) +- `老板` (3×) +- `一名生病员工` (1×) +- `一名腐败委员会成员` (1×) +- `野心勃勃的同事` (1×) +- `一只独角兽` (1×) +- `一个不太讲究的采购商` (1×) +- `研究员` (1×) +- `一位滑雪冠军` (1×) +- `一名球迷` (1×) +- `一名罗兰·加洛斯高管` (1×) +- `一名职业游泳运动员` (1×) +- `哈多克船长` (1×) +- `至尊蓝精灵` (1×) +- `奥贝利克斯` (1×) +- `披风拥护者` (1×) +- `幽灵` (1×) +- `偷走手提箱的小偷` (1×) +- `反抗军成员` (1×) +- `一个贫穷的年轻人` (1×) +- `达斯·维达` (1×) +- `Ross` (1×) +- `做梦的导演` (1×) +- `Gollum` (1×) +- `一名地平论者` (1×) +- `一名反疫苗者` (1×) +- `克隆体` (1×) +- `一名反 5G 人士` (1×) +- `一名阴谋论者` (1×) +- `一位业余生物学家` (1×) +- `一名腐败议员` (1×) +- `政府发言人` (1×) +- `文化部长` (1×) +- `卫生部长` (1×) +- `前候选人` (1×) +- `机会主义候选人` (1×) +- `一位政治领袖` (2×) +- `一名腐败政客` (1×) +- `一名候选人` (1×) +- `叛徒` (1×) +- `候选人` (1×) +- `一名政客` (1×) +- `家长` (2×) +- `孩子` (1×) +- `圣诞老人` (1×) +- `母亲` (1×) +- `新伴侣` (1×) +- `一只猫` (1×) +- `叔叔` (1×) +- `嫉妒的邻居` (1×) +- `粗心的主人` (1×) +- `夜间动手达人` (1×) +- `一位素食者` (1×) +- `一位养鸭人` (1×) +- `一位爱开派对的邻居` (1×) +- `一位爱园艺的邻居` (1×) +- `鹦鹉的主人` (1×) +- `一位邻居` (1×) +- `一名烟民` (1×) +- `等不及的顾客` (1×) +- `守门员` (1×) +- `一位变装来宾` (1×) + +**fa**: +- `Aurelia Cotta، مادر سزار` (1×) +- `کاتون` (1×) +- `یک گلادیاتور` (1×) +- `یک سوفسطایی حیله‌گر` (1×) +- `یک جاسوس یونانی` (1×) +- `یک عارف پرشور` (1×) +- `ژان دارک` (1×) +- `یک برده‌دار` (1×) +- `یک مشاور نظامی` (1×) +- `یک انقلابی مردسالار` (1×) +- `لوئی شانزدهم` (1×) +- `یک آشپز` (1×) +- `یک مخالف‌خوان` (1×) +- `رئیس‌جمهور ایالات متحده` (1×) +- `مارشال` (1×) +- `نماینده یک کشور خیلی کوچک` (1×) +- `کاپیتان تایتانیک` (1×) +- `زن ریش‌آبی` (1×) +- `جادوگر` (1×) +- `شهرزاد` (1×) +- `یکی از کوتوله‌های جنگل` (1×) +- `یکی از برادرهای بندانگشتی` (1×) +- `یتی` (1×) +- `زیبای خفته در جنگل` (1×) +- `گرگ` (1×) +- `شهردار` (2×) +- `کلاهبردار` (1×) +- `ابلیس` (1×) +- `یهودا` (1×) +- `مار` (1×) +- `قابیل` (1×) +- `قاضی دادگاه خانواده` (1×) +- `پیامبر تازه` (1×) +- `خواستگار` (1×) +- `مدوسا، گورگون` (1×) +- `سیزیف` (1×) +- `لوکی` (1×) +- `پرومته` (1×) +- `جادوگر اُز` (1×) +- `لرد ولدمورت` (1×) +- `گربه چشایر` (1×) +- `پروفسور موریارتی` (1×) +- `دون ژوان` (1×) +- `یک خون‌آشام` (1×) +- `یک آدم معمولی` (13×) +- `یک پیتزاپز` (1×) +- `یک آدم زشت` (1×) +- `موسیقی‌دان` (1×) +- `میزبان شام` (1×) +- `عاشق` (1×) +- `کسی که می‌خواهد اسباب‌کشی کند` (1×) +- `یک کلکسیونر` (1×) +- `کسی که بچه نمی‌خواهد` (1×) +- `دوستدار گربه‌ها` (1×) +- `عاشق سگ‌ها` (1×) +- `شریک تنبل` (1×) +- `فراموشکار` (1×) +- `شریک زندگیِ تنبل` (1×) +- `دروغگوی فرصت‌طلب` (1×) +- `سرآشپز` (1×) +- `خریدار مرموز` (1×) +- `دوستدار حیوانات` (1×) +- `عاشق گیاهان` (1×) +- `کلکسیونر تی‌شرت‌ها` (1×) +- `یک قمارباز ولخرج` (1×) +- `اکسِ فریبکار` (1×) +- `مخالفِ سرسخت` (1×) +- `یک آدم بی‌پول` (1×) +- `یک شخص معمولی` (3×) +- `یک زن` (1×) +- `شریک ماجراجو` (1×) +- `کسی که نمی‌خواهد ازدواج کند` (1×) +- `فروشنده` (1×) +- `یک مدیر مدرسه` (1×) +- `یک خال‌کوب` (1×) +- `پروفسور نامدار` (1×) +- `فروشندهٔ ماشین‌های پرنده` (1×) +- `نگهدارندهٔ حیوان خانگی` (1×) +- `پیک پیتزا` (2×) +- `نظافتچی` (1×) +- `کارواشی` (1×) +- `یک آژانس‌دار سفر` (1×) +- `یک پرستار بچه دستپاچه` (1×) +- `یک چترفروش` (1×) +- `معلم حسود` (1×) +- `کاپیتان` (1×) +- `رئیس شرکت` (2×) +- `یک کارمند بیمار` (1×) +- `یکی از اعضای فاسد کمیته` (1×) +- `همکار جاه‌طلب` (1×) +- `یک تک‌شاخ` (1×) +- `یک خریدار بی‌وجدان` (1×) +- `پژوهشگر` (1×) +- `رئیس` (1×) +- `یک قهرمان اسکی` (1×) +- `یک هوادار` (1×) +- `یکی از مدیران رولان گاروس` (1×) +- `یک شناگر حرفه‌ای` (1×) +- `کاپیتان هادوک` (1×) +- `اسمورفِ اعظم‌نما` (1×) +- `Obélix` (1×) +- `طرفدار شنل` (1×) +- `روح` (1×) +- `دزدِ کیف دستی` (1×) +- `یکی از اعضای مقاومت` (1×) +- `یک مرد جوان فقیر` (1×) +- `دارث ویدر` (1×) +- `Ross` (1×) +- `کارگردان رؤیاپرداز` (1×) +- `گالوم` (1×) +- `یک زمین‌تخت‌گرا` (1×) +- `یک ضدواکسن` (1×) +- `کلون` (1×) +- `یک مخالف 5G` (1×) +- `یک توطئه‌باور` (1×) +- `یک زیست‌شناس آماتور` (1×) +- `یک نمایندهٔ فاسد` (1×) +- `سخنگوی دولت` (1×) +- `وزیر فرهنگ` (1×) +- `وزیر بهداشت` (1×) +- `نامزد سابق` (1×) +- `نامزد فرصت‌طلب` (1×) +- `یک رهبر سیاسی` (2×) +- `یک سیاستمدار فاسد` (1×) +- `یک نامزد انتخاباتی` (1×) +- `خائن` (1×) +- `نامزد` (1×) +- `یک سیاستمدار` (1×) +- `والد` (2×) +- `کودک` (1×) +- `بابانوئل` (1×) +- `مادر` (1×) +- `همسر تازه` (1×) +- `یک گربه` (1×) +- `دایی` (1×) +- `همسایهٔ حسود` (1×) +- `صاحب بی‌ملاحظه` (1×) +- `تعمیرکار شب‌کار` (1×) +- `یک گیاه‌خوار` (1×) +- `یک پرورش‌دهنده اردک` (1×) +- `یک همسایه اهل مهمانی` (1×) +- `یک همسایه باغبان` (1×) +- `صاحب طوطی` (1×) +- `یک همسایه` (1×) +- `یک سیگاری` (1×) +- `مشتری عجول` (1×) +- `دروازه‌بان` (1×) +- `یک مهمان با لباس مبدل` (1×) + +
+ +### piocheur translations + +| Language | Unique translations | Consistent? | +|----------|-------------------:|:-----------:| +| en | 150 | ⚠️ | +| ru | 143 | ⚠️ | +| pt | 148 | ⚠️ | +| ar | 147 | ⚠️ | +| es | 149 | ⚠️ | +| zh | 149 | ⚠️ | +| fa | 150 | ⚠️ | + +
Variant details + +**en**: +- `Caesar` (1×) +- `A political opponent` (1×) +- `Emperor Caligula` (1×) +- `Socrates` (1×) +- `Sinon` (1×) +- `An archaeologist` (1×) +- `Charles VII` (1×) +- `A humanist` (1×) +- `Napoleon` (1×) +- `A suffragette` (1×) +- `the president of the revolutionary court` (1×) +- `Marie Antoinette` (1×) +- `a member of the Popular Front` (1×) +- `The 5-star general` (1×) +- `The General` (1×) +- `The President of the United Nations` (1×) +- `A passenger` (1×) +- `Bluebeard` (1×) +- `Gretel` (1×) +- `The King of Persia` (1×) +- `Snow White` (1×) +- `Little Thumb` (1×) +- `Yeti's wife` (1×) +- `Prince Charming` (1×) +- `Lamb` (1×) +- `The Pied Piper` (1×) +- `The emperor` (1×) +- `Jesus` (1×) +- `the gatekeeper` (1×) +- `Eve` (1×) +- `God` (1×) +- `The mother of the child` (1×) +- `A religious man` (1×) +- `Penelope` (1×) +- `A fearless hero` (1×) +- `Hades` (1×) +- `Thor` (1×) +- `Zeus` (1×) +- `Dorothy` (1×) +- `A Hogwarts student` (1×) +- `Alice` (1×) +- `Sherlock Holmes` (1×) +- `the statue of the governor` (1×) +- `A vampire hunter` (1×) +- `your friends` (1×) +- `Your conquest` (1×) +- `Your partner` (4×) +- `The other` (1×) +- `His conquest of the day before` (1×) +- `A person I met online` (1×) +- `The tone-deaf one` (1×) +- `The chef` (1×) +- `The beloved` (1×) +- `whoever wants to get married` (2×) +- `Your partner who has a child` (1×) +- `whoever wants a baby` (1×) +- `The dog lover` (1×) +- `Their partner` (8×) +- `The partner who planned everything` (1×) +- `Their Partner` (8×) +- `The Sporty Partner` (1×) +- `The suspicious salesperson` (1×) +- `The skeptic` (1×) +- `The partner` (1×) +- `The heartbroken partner` (1×) +- `His wife` (1×) +- `Your ex-boyfriend` (1×) +- `The partner who wants to get married` (1×) +- `His partner` (1×) +- `The reserved partner` (1×) +- `the client` (1×) +- `Parents of students` (1×) +- `His client` (1×) +- `A trainee journalist` (1×) +- `A customer interested in a classic car` (1×) +- `The chameleon's owner` (1×) +- `The customer` (2×) +- `The employer` (1×) +- `Hungry customer` (1×) +- `A customer` (1×) +- `The worried parent` (1×) +- `A customer in the rain` (1×) +- `The gifted student` (1×) +- `the seafarers' representative` (1×) +- `Yourself` (1×) +- `Union representatives` (1×) +- `His deserving employee` (1×) +- `His manager` (1×) +- `Another committee member` (1×) +- `The boss` (1×) +- `Your Boss` (1×) +- `An eminent zoologist` (1×) +- `Your doctor` (1×) +- `Its supplier` (1×) +- `A member of the ethics committee` (1×) +- `The scientist` (1×) +- `A member of the Games committee` (1×) +- `A FIFA executive` (1×) +- `A professional tennis player` (1×) +- `A swimming pool manager` (1×) +- `Sunflower` (1×) +- `his constituents` (1×) +- `Getafix` (1×) +- `A costume designer` (1×) +- `A ghost hunter` (1×) +- `the hit man` (1×) +- `the chosen one` (1×) +- `a rich woman` (1×) +- `Luck` (1×) +- `Rachel` (1×) +- `A skeptical producer` (1×) +- `Frodo` (1×) +- `A cosmonaut` (1×) +- `A person at risk` (1×) +- `The original subject` (1×) +- `A South Korean tourist` (1×) +- `A physicist` (1×) +- `A renowned biologist` (1×) +- `the minister` (1×) +- `a member of the opposition` (1×) +- `The presenter of the program` (1×) +- `A member of the commission of inquiry` (1×) +- `An angry activist` (1×) +- `A journalist` (3×) +- `The chairman of the disciplinary committee` (1×) +- `A supporter` (2×) +- `A worried voter` (2×) +- `The political leader` (1×) +- `the child` (3×) +- `the parent` (2×) +- `The father` (1×) +- `The child` (3×) +- `Your rich partner` (1×) +- `His master` (1×) +- `The parent` (2×) +- `The outraged neighbor` (1×) +- `The unhappy neighbor` (2×) +- `A foie gras producer` (1×) +- `His vegetarian neighbor` (1×) +- `Your friend` (1×) +- `His longtime friend` (1×) +- `An unhappy neighbor` (1×) +- `An annoyed neighbor` (1×) +- `A neighbor` (1×) +- `Their skeptical neighbor` (1×) +- `An elderly person` (1×) +- `A relative` (1×) +- `A pregnant woman` (1×) +- `The coach` (1×) +- `The host` (1×) + +**ru**: +- `Цезарь` (1×) +- `Политический противник` (1×) +- `Император Калигула` (1×) +- `Сократ` (1×) +- `Синон` (1×) +- `Археолог` (1×) +- `Карл VII` (1×) +- `Император-гуманист` (1×) +- `Наполеон` (1×) +- `Суфражистка` (1×) +- `Председатель революционного трибунала` (1×) +- `Мария-Антуанетта` (1×) +- `Член народного фронта` (1×) +- `Генерал-орденоносец` (1×) +- `Генерал де Голль` (1×) +- `Председатель Генассамблеи ООН` (1×) +- `Пассажир` (1×) +- `Синяя Борода` (1×) +- `Гретель` (1×) +- `Персидский царь` (1×) +- `Белоснежка` (1×) +- `Мальчик-с-пальчик` (1×) +- `Жена Йети` (1×) +- `Прекрасный принц` (1×) +- `Ягненок` (1×) +- `Крысолов` (1×) +- `Император` (1×) +- `Иисус` (1×) +- `Страж у врат рая` (1×) +- `Ева` (1×) +- `Ангел` (1×) +- `Прокурор` (1×) +- `Верующий человек` (1×) +- `Пенелопа` (1×) +- `Бесстрашный герой` (1×) +- `Аид` (1×) +- `Тор` (1×) +- `Зевс` (1×) +- `Дороти` (1×) +- `Ученик Хогвартса` (1×) +- `Алиса` (1×) +- `Шерлок Холмс` (1×) +- `Призрак` (1×) +- `Охотник на вампиров` (1×) +- `Дальние знакомые` (1×) +- `Ночь любви` (1×) +- `Партнер` (8×) +- `Другой` (1×) +- `Новая избранница` (1×) +- `Интернет-знакомство` (1×) +- `Тот, кому медведь на ухо наступил` (1×) +- `Шеф-повар` (1×) +- `Возлюбленный` (1×) +- `Тот, кто не хочет съезжаться` (1×) +- `Партнер с ребенком` (1×) +- `Тот, кто хочет ребенка` (1×) +- `Любитель собак` (1×) +- `Его партнер` (4×) +- `Партнер, который все распланировал` (1×) +- `Спортивный партнер` (1×) +- `Подозрительный продавец` (1×) +- `Скептик` (1×) +- `Партнёр` (1×) +- `Убитый горем партнёр` (1×) +- `Его половинка` (1×) +- `Бывший` (1×) +- `Партнёр, который хочет пожениться` (1×) +- `Его партнёр` (1×) +- `Сдержанный партнер` (1×) +- `Тот, кто хочет жениться` (1×) +- `Клиент` (5×) +- `Родители ученика` (1×) +- `Журналист-стажер` (1×) +- `Клиент, присматривающий обычную машину` (1×) +- `Хозяин хамелеона` (1×) +- `Работодатель` (1×) +- `Голодный клиент` (1×) +- `Взволнованный родитель` (1×) +- `Клиент под дождём` (1×) +- `Одарённый ученик` (1×) +- `Представитель моряков` (1×) +- `Серьезный стажер` (1×) +- `Представители профсоюза` (1×) +- `Достойный сотрудник` (1×) +- `Менеджер` (1×) +- `Другой член комитета` (1×) +- `Босс` (2×) +- `Выдающийся зоолог` (1×) +- `Доктор` (1×) +- `Поставщик` (1×) +- `Член комитета по этике` (1×) +- `Учёный` (1×) +- `Член олимпийского комитета` (1×) +- `Чиновник ФИФА` (1×) +- `Профессиональный теннисист` (1×) +- `Управляющий бассейном` (1×) +- `Подсолнух` (1×) +- `Сильный Смурф` (1×) +- `Друид` (1×) +- `Костюмер` (1×) +- `Охотник за привидениями` (1×) +- `Наемный убийца` (1×) +- `Избранный` (1×) +- `Богатая женщина` (1×) +- `Люк` (1×) +- `Рэйчел` (1×) +- `Скептичный продюсер` (1×) +- `Фродо` (1×) +- `Космонавт` (1×) +- `Человек в группе риска` (1×) +- `Оригинал` (1×) +- `Южнокорейский турист` (1×) +- `Физик` (1×) +- `Известный биолог` (1×) +- `Министр` (1×) +- `Член оппозиции` (1×) +- `Ведущий программы` (1×) +- `Член комиссии по расследованию` (1×) +- `Злой активист` (1×) +- `Журналист` (3×) +- `Председатель дисциплинарного комитета` (1×) +- `Сторонник` (2×) +- `Обеспокоенный избиратель` (2×) +- `Политический лидер` (1×) +- `Ребенок` (3×) +- `Хозяйка` (1×) +- `Отец` (1×) +- `Богатая партнерша` (1×) +- `Ее хозяин` (1×) +- `Родитель` (2×) +- `Возмущённый сосед` (1×) +- `Недовольный сосед` (3×) +- `Производитель фуа-гра` (1×) +- `Его сосед-вегетарианец` (1×) +- `Его друг` (1×) +- `Его давний друг` (1×) +- `Раздражённый сосед` (1×) +- `Сосед` (1×) +- `Его скептичный сосед` (1×) +- `Пожилой человек` (1×) +- `Беременная женщина` (1×) +- `Тренер` (1×) +- `Хозяин вечеринки` (1×) + +**pt**: +- `César` (1×) +- `Um oponente político` (1×) +- `Imperador Calígula` (1×) +- `Sócrates` (1×) +- `Sínon` (1×) +- `Um arqueólogo` (1×) +- `Charles VII` (1×) +- `Um imperador humanista` (1×) +- `Napoleão` (1×) +- `Uma sufragista` (1×) +- `O presidente do Tribunal Revolucionário` (1×) +- `Maria Antonieta` (1×) +- `Um membro da frente popular` (1×) +- `Um general de 5 -estrela` (1×) +- `O general` (1×) +- `O presidente das Nações Unidas` (1×) +- `Um passageiro` (1×) +- `Barba azul` (1×) +- `Gretel` (1×) +- `O rei da Pérsia` (1×) +- `Branca de Neve` (1×) +- `Tom Thumb` (1×) +- `Esposa de Yeti` (1×) +- `O encantador príncipe` (1×) +- `Cordeiro` (1×) +- `O Flautista` (1×) +- `O imperador` (1×) +- `Jesus` (1×) +- `O goleiro de entrada` (1×) +- `Véspera` (1×) +- `Um anjo` (1×) +- `O promotor` (1×) +- `Uma pessoa religiosa` (1×) +- `Penélope` (1×) +- `Um herói intrépido` (1×) +- `Hades` (1×) +- `Thor` (1×) +- `Zeus` (1×) +- `Dorothy` (1×) +- `Um aluno de Hogwarts` (1×) +- `Alice` (1×) +- `Sherlock Holmes` (1×) +- `Espectro` (1×) +- `Um caçador de vampiros` (1×) +- `O vago conhecimento` (1×) +- `Sua conquista` (1×) +- `Seu parceiro` (5×) +- `O outro` (1×) +- `Sua conquista do dia antes` (1×) +- `Uma pessoa conheceu online` (1×) +- `Quem não tem ouvido musical` (1×) +- `O chefe de cozinha` (1×) +- `A pessoa amada` (1×) +- `Aquele que não quer se mover` (1×) +- `Seu parceiro que tem um filho` (1×) +- `Aquele que quer uma criança` (1×) +- `O amigo dos cães` (1×) +- `O seu parceiro` (5×) +- `O parceiro que planeou tudo` (1×) +- `A sua cara-metade` (1×) +- `O companheiro desportista` (1×) +- `O seu par` (2×) +- `O vendedor desconfiado` (1×) +- `O cético` (1×) +- `O parceiro` (1×) +- `O parceiro arrependido` (1×) +- `Sua metade` (1×) +- `Seu ex` (1×) +- `O parceiro que quer casar` (1×) +- `O parceiro reservado` (1×) +- `Aquele que quer se casar` (1×) +- `O cliente` (3×) +- `Pais de alunos` (1×) +- `Seu cliente` (1×) +- `Um jornalista estagiário` (1×) +- `Um cliente interessado num carro clássico` (1×) +- `O dono do camaleão` (1×) +- `O empregador` (1×) +- `Cliente esfomeado` (1×) +- `Um cliente` (1×) +- `O pai/mãe preocupado/a` (1×) +- `Cliente à chuva` (1×) +- `O aluno talentoso` (1×) +- `O representante dos marinheiros` (1×) +- `Um estagiário sério` (1×) +- `Representantes da União` (1×) +- `Seu funcionário merecedor` (1×) +- `Seu gerente` (1×) +- `Outro membro do comité` (1×) +- `O patrão` (1×) +- `O chefe dele` (1×) +- `Um eminente zoólogo` (1×) +- `Seu médico` (1×) +- `Seu fornecedor` (1×) +- `Um membro do comité de ética` (1×) +- `O cientista` (1×) +- `Um membro do comité dos jogos` (1×) +- `Um dirigente da FIFA` (1×) +- `Um tenista profissional` (1×) +- `Um gerente de piscina` (1×) +- `Girassol` (1×) +- `Seus eleitores` (1×) +- `O druida` (1×) +- `Um figurinista` (1×) +- `caçador de fantasmas` (1×) +- `O hitman` (1×) +- `O escolhido` (1×) +- `Uma mulher rica` (1×) +- `Lucas` (1×) +- `Rachel` (1×) +- `Um produtor cético` (1×) +- `Frodo` (1×) +- `Um cosmonaut` (1×) +- `Uma pessoa em risco` (1×) +- `O sujeito original` (1×) +- `Um turista sul -coreano` (1×) +- `Um físico` (1×) +- `Um biólogo de renome` (1×) +- `O ministro` (1×) +- `Um membro da oposição` (1×) +- `O apresentador do programa` (1×) +- `Um membro da Comissão de Inquérito` (1×) +- `Um ativista irritado` (1×) +- `Um jornalista` (3×) +- `O presidente do comitê disciplinar` (1×) +- `Um apoiante` (2×) +- `Um eleitor preocupado` (2×) +- `O líder político` (1×) +- `A criança` (3×) +- `A amante` (1×) +- `O pai` (2×) +- `Seu rico parceiro` (1×) +- `O mestre dele` (1×) +- `O vizinho indignado` (1×) +- `O vizinho descontente` (2×) +- `Um produtor de foie gras` (1×) +- `Seu vizinho vegetariano` (1×) +- `Amigo dele` (1×) +- `Seu amigo de longa data` (1×) +- `Um vizinho descontente` (1×) +- `Um vizinho irritado` (1×) +- `Um vizinho` (1×) +- `O seu vizinho cético` (1×) +- `Uma pessoa velha` (1×) +- `Um pai` (1×) +- `Uma mulher grávida` (1×) +- `O treinador` (1×) +- `O anfitrião` (1×) + +**ar**: +- `يوليوس قيصر` (1×) +- `معارض سياسي` (1×) +- `الإمبراطور كاليغولا` (1×) +- `سقراط` (1×) +- `سينون` (1×) +- `عالم آثار` (1×) +- `شارل السابع` (1×) +- `إمبراطور إنساني النزعة` (1×) +- `نابليون` (1×) +- `مناضلة من أجل حق التصويت` (1×) +- `رئيس المحكمة الثورية` (1×) +- `ماري أنطوانيت` (1×) +- `عضو في الجبهة الشعبية` (1×) +- `جنرال بخمس نجوم` (1×) +- `الجنرال` (1×) +- `رئيس الأمم المتحدة` (1×) +- `أحد الركاب` (1×) +- `ذو اللحية الزرقاء` (1×) +- `غريتل` (1×) +- `ملك فارس` (1×) +- `بياض الثلج` (1×) +- `عقلة الإصبع` (1×) +- `زوجة اليتي` (1×) +- `الأمير الساحر` (1×) +- `الحمل` (1×) +- `عازف الناي` (1×) +- `الإمبراطور` (1×) +- `يسوع` (1×) +- `حارس المدخل` (1×) +- `حواء` (1×) +- `ملاك` (1×) +- `المدعي العام` (1×) +- `رجل دين` (1×) +- `بينيلوبي` (1×) +- `بطل جسور` (1×) +- `هاديس` (1×) +- `ثور` (1×) +- `زيوس` (1×) +- `دوروثي` (1×) +- `طالب من هوغوورتس` (1×) +- `أليس` (1×) +- `شيرلوك هولمز` (1×) +- `الشبح` (1×) +- `صيّاد مصّاصي دماء` (1×) +- `المعرفة السطحية` (1×) +- `غنيمته العاطفية` (1×) +- `شريكه` (12×) +- `الطرف الآخر` (1×) +- `فتحه الغرامي من الليلة السابقة` (1×) +- `شخص تعرّف إليه عبر الإنترنت` (1×) +- `من لا يملك أذنًا موسيقية` (1×) +- `الطاهي` (1×) +- `المعشوق` (1×) +- `من لا يريد الانتقال للسكن مع الآخر` (1×) +- `شريكه الذي لديه طفل` (1×) +- `من يريد إنجاب طفل` (1×) +- `صديق الكلاب` (1×) +- `الشريك الذي خطّط لكل شيء` (1×) +- `الشريك الرياضي` (1×) +- `البائع المرتاب` (1×) +- `المتشكك` (1×) +- `الشريك` (1×) +- `الشريك الآسف` (1×) +- `نصفه الآخر` (1×) +- `حبيبته السابقة` (1×) +- `الشريك الراغب في الزواج` (1×) +- `شريكها` (1×) +- `الشريك المتحفظ` (1×) +- `من يريد الزواج` (1×) +- `الزبون` (3×) +- `أولياء أمور التلاميذ` (1×) +- `زبونه` (1×) +- `صحفي متدرّب` (1×) +- `زبون مهتم بسيارة كلاسيكية` (1×) +- `صاحب الحرباء` (1×) +- `صاحب العمل` (1×) +- `زبون جائع` (1×) +- `زبون` (1×) +- `الوالد القَلِق` (1×) +- `زبون تحت المطر` (1×) +- `التلميذ الموهوب` (1×) +- `ممثل البحّارة` (1×) +- `متدرّب جاد` (1×) +- `ممثلو النقابة` (1×) +- `موظفه المجتهد` (1×) +- `مسؤوله` (1×) +- `عضو آخر في اللجنة` (1×) +- `المدير` (1×) +- `مديره` (1×) +- `عالم حيوان مرموق` (1×) +- `طبيبه` (1×) +- `مورّده` (1×) +- `عضو في لجنة الأخلاقيات` (1×) +- `العالِم` (1×) +- `عضو في لجنة الألعاب` (1×) +- `مسؤول في الفيفا` (1×) +- `لاعب تنس محترف` (1×) +- `مدير مسبح` (1×) +- `تورنسول` (1×) +- `ناخبوه` (1×) +- `بانوراميكس` (1×) +- `مصمم أزياء` (1×) +- `صياد أشباح` (1×) +- `القاتل المأجور` (1×) +- `المختار` (1×) +- `امرأة ثرية` (1×) +- `لوك` (1×) +- `رايتشل` (1×) +- `منتج متشكك` (1×) +- `فرودو` (1×) +- `رائد فضاء` (1×) +- `شخص معرّض للخطر` (1×) +- `الأصل` (1×) +- `سائح كوري جنوبي` (1×) +- `فيزيائي` (1×) +- `عالم أحياء مرموق` (1×) +- `الوزير` (1×) +- `عضو في المعارضة` (1×) +- `مقدّم البرنامج` (1×) +- `عضو في لجنة التحقيق` (1×) +- `مناضل غاضب` (1×) +- `صحفي` (3×) +- `رئيس لجنة الانضباط` (1×) +- `مؤيّد` (2×) +- `ناخب قلق` (2×) +- `الزعيم السياسي` (1×) +- `الطفل` (3×) +- `المعلّمة` (1×) +- `الأب` (1×) +- `شريكته الثرية` (1×) +- `صاحبه` (1×) +- `وليّ الأمر` (1×) +- `الجار الغاضب` (1×) +- `الجار المستاء` (2×) +- `منتج فوا غرا` (1×) +- `جاره النباتي` (1×) +- `صديقه` (1×) +- `صديقه القديم` (1×) +- `جار مستاء` (1×) +- `جار منزعج` (1×) +- `أحد الجيران` (1×) +- `جاره المتشكك` (1×) +- `شخص مسن` (1×) +- `أحد الوالدين` (1×) +- `امرأة حامل` (1×) +- `المدرب` (1×) +- `المضيف` (1×) + +**es**: +- `Julio César` (1×) +- `Un opositor político` (1×) +- `El emperador Calígula` (1×) +- `Sócrates` (1×) +- `Sinón` (1×) +- `Un arqueólogo` (1×) +- `Carlos VII` (1×) +- `Un emperador humanista` (1×) +- `Napoleón` (1×) +- `Una sufragista` (1×) +- `El presidente del tribunal revolucionario` (1×) +- `María Antonieta` (1×) +- `Un miembro del Frente Popular` (1×) +- `Un general de cinco estrellas` (1×) +- `El general` (1×) +- `El presidente de las Naciones Unidas` (1×) +- `Un pasajero` (1×) +- `Barba Azul` (1×) +- `Gretel` (1×) +- `El rey de Persia` (1×) +- `Blancanieves` (1×) +- `Pulgarcito` (1×) +- `La mujer del Yeti` (1×) +- `El príncipe azul` (1×) +- `El cordero` (1×) +- `El flautista` (1×) +- `El emperador` (1×) +- `Jesús` (1×) +- `El guardián de la entrada` (1×) +- `Eva` (1×) +- `Un ángel` (1×) +- `el fiscal` (1×) +- `Un religioso` (1×) +- `Penélope` (1×) +- `Un héroe intrépido` (1×) +- `Hades` (1×) +- `Thor` (1×) +- `Zeus` (1×) +- `Dorothy` (1×) +- `Un alumno de Hogwarts` (1×) +- `Alicia` (1×) +- `Sherlock Holmes` (1×) +- `El espectro` (1×) +- `Un cazador de vampiros` (1×) +- `Un conocido lejano` (1×) +- `Su conquista` (1×) +- `Su pareja sexual` (1×) +- `La otra persona` (1×) +- `Su conquista de la noche anterior` (1×) +- `Una persona conocida en línea` (1×) +- `Quien no tiene oído musical` (1×) +- `El chef` (1×) +- `La persona amada` (1×) +- `Quien no quiere mudarse` (1×) +- `Su pareja` (12×) +- `Su pareja, que tiene un hijo` (1×) +- `Quien quiere tener un hijo` (1×) +- `La persona amiga de los perros` (1×) +- `La pareja que lo ha planificado todo` (1×) +- `La pareja deportista` (1×) +- `El vendedor suspicaz` (1×) +- `El escéptico` (1×) +- `La pareja` (1×) +- `La pareja arrepentida` (1×) +- `Su media naranja` (1×) +- `Su ex` (1×) +- `La pareja que quiere casarse` (1×) +- `La pareja reservada` (1×) +- `Quien quiere casarse` (1×) +- `El cliente` (3×) +- `Los padres de alumnos` (1×) +- `Su cliente` (1×) +- `Un periodista en prácticas` (1×) +- `Un cliente interesado en un coche clásico` (1×) +- `El dueño del camaleón` (1×) +- `El empleador` (1×) +- `Cliente hambriento` (1×) +- `Un cliente` (1×) +- `El padre preocupado` (1×) +- `Cliente bajo la lluvia` (1×) +- `El alumno talentoso` (1×) +- `El representante de los marineros` (1×) +- `Un becario serio` (1×) +- `Los representantes sindicales` (1×) +- `Su empleado meritorio` (1×) +- `Su responsable` (1×) +- `Otro miembro del comité` (1×) +- `El jefe` (1×) +- `Su jefe` (1×) +- `Un eminente zoólogo` (1×) +- `Su médico` (1×) +- `Su proveedor` (1×) +- `Un miembro del comité de ética` (1×) +- `El científico` (1×) +- `Un miembro del comité de los juegos` (1×) +- `Un directivo de la FIFA` (1×) +- `Un tenista profesional` (1×) +- `Un gerente de piscina` (1×) +- `Tornasol` (1×) +- `Sus votantes` (1×) +- `Panorámix` (1×) +- `Un diseñador de vestuario` (1×) +- `cazador de fantasmas` (1×) +- `El asesino a sueldo` (1×) +- `El elegido` (1×) +- `Una mujer rica` (1×) +- `Luke` (1×) +- `Rachel` (1×) +- `Un productor escéptico` (1×) +- `Frodo` (1×) +- `Un cosmonauta` (1×) +- `Una persona de riesgo` (1×) +- `El sujeto original` (1×) +- `Un turista surcoreano` (1×) +- `Un físico` (1×) +- `Un biólogo de renombre` (1×) +- `El ministro` (1×) +- `Un miembro de la oposición` (1×) +- `El presentador del programa` (1×) +- `Un miembro de la comisión de investigación` (1×) +- `Un militante furioso` (1×) +- `Un periodista` (3×) +- `El presidente de la comisión disciplinaria` (1×) +- `Un partidario` (2×) +- `Un votante preocupado` (1×) +- `El líder político` (1×) +- `Un elector preocupado` (1×) +- `El hijo` (1×) +- `La maestra` (1×) +- `El niño` (2×) +- `El padre` (1×) +- `Su rica pareja` (1×) +- `Su amo` (1×) +- `El progenitor` (1×) +- `El vecino indignado` (1×) +- `El vecino descontento` (2×) +- `Un productor de foie gras` (1×) +- `Su vecino vegetariano` (1×) +- `Su amigo` (1×) +- `Su amigo de toda la vida` (1×) +- `Un vecino descontento` (1×) +- `Un vecino molesto` (1×) +- `Un vecino` (1×) +- `Su vecino escéptico` (1×) +- `Una persona mayor` (1×) +- `Un progenitor` (1×) +- `Una mujer embarazada` (1×) +- `El entrenador` (1×) +- `El anfitrión` (1×) + +**zh**: +- `尤利乌斯·凯撒` (1×) +- `一名政敌` (1×) +- `皇帝卡利古拉` (1×) +- `苏格拉底` (1×) +- `西农` (1×) +- `一位考古学家` (1×) +- `查理七世` (1×) +- `一位人文主义皇帝` (1×) +- `拿破仑` (1×) +- `一位妇女参政论者` (1×) +- `革命法庭庭长` (1×) +- `玛丽·安托瓦内特` (1×) +- `一名人民阵线成员` (1×) +- `一位五星上将` (1×) +- `将军` (1×) +- `联合国主席` (1×) +- `一名乘客` (1×) +- `蓝胡子` (1×) +- `格蕾特尔` (1×) +- `波斯国王` (1×) +- `白雪公主` (1×) +- `小拇指` (1×) +- `雪人的妻子` (1×) +- `白马王子` (1×) +- `小羊` (1×) +- `吹笛人` (1×) +- `皇帝` (1×) +- `耶稣` (1×) +- `入口守卫` (1×) +- `夏娃` (1×) +- `一位天使` (1×) +- `检察官` (1×) +- `一位宗教人士` (1×) +- `佩涅洛佩` (1×) +- `一位无畏的英雄` (1×) +- `哈迪斯` (1×) +- `托尔` (1×) +- `宙斯` (1×) +- `多萝西` (1×) +- `一名霍格沃茨学生` (1×) +- `爱丽丝` (1×) +- `夏洛克·福尔摩斯` (1×) +- `幽灵` (1×) +- `一名吸血鬼猎人` (1×) +- `泛泛之交` (1×) +- `他的猎艳对象` (1×) +- `他的伴侣` (6×) +- `对方` (1×) +- `他前一晚刚撩到的人` (1×) +- `一位网上认识的人` (1×) +- `那个没有乐感的人` (1×) +- `主厨` (1×) +- `被爱的人` (1×) +- `不想同居的人` (1×) +- `有孩子的伴侣` (1×) +- `想要孩子的那一方` (1×) +- `狗派人士` (1×) +- `把一切都计划好的伴侣` (1×) +- `爱运动的伴侣` (1×) +- `起疑的店员` (1×) +- `怀疑论者` (1×) +- `伴侣` (1×) +- `愧疚的伴侣` (1×) +- `他的另一半` (1×) +- `他/她的前任` (1×) +- `想结婚的伴侣` (1×) +- `他/她的伴侣` (1×) +- `其伴侣` (6×) +- `保守的伴侣` (1×) +- `想结婚的那个人` (1×) +- `顾客` (3×) +- `学生家长们` (1×) +- `他的顾客` (1×) +- `一名实习记者` (1×) +- `一位对经典汽车感兴趣的客户` (1×) +- `变色龙的主人` (1×) +- `雇主` (1×) +- `饥肠辘辘的顾客` (1×) +- `一名顾客` (1×) +- `忧心忡忡的家长` (1×) +- `雨中的顾客` (1×) +- `有天赋的学生` (1×) +- `水手代表` (1×) +- `认真的实习生` (1×) +- `工会代表` (1×) +- `他那位优秀员工` (1×) +- `他的主管` (1×) +- `另一名委员会成员` (1×) +- `老板` (1×) +- `其老板` (1×) +- `一位著名动物学家` (1×) +- `其医生` (1×) +- `他的供应商` (1×) +- `伦理委员会的一名成员` (1×) +- `科学家` (1×) +- `运动会委员会的一名成员` (1×) +- `一名国际足联高管` (1×) +- `一名职业网球选手` (1×) +- `一名泳池经理` (1×) +- `向日葵教授` (1×) +- `他的选民` (1×) +- `帕诺拉米克斯` (1×) +- `一位服装师` (1×) +- `捉鬼人` (1×) +- `职业杀手` (1×) +- `救世主` (1×) +- `一位富有的女人` (1×) +- `卢克` (1×) +- `Rachel` (1×) +- `一位怀疑的制片人` (1×) +- `Frodon` (1×) +- `一名宇航员` (1×) +- `一名高风险人群` (1×) +- `本体` (1×) +- `一名韩国游客` (1×) +- `一位物理学家` (1×) +- `一位著名生物学家` (1×) +- `部长` (1×) +- `一名反对派成员` (1×) +- `节目主持人` (1×) +- `调查委员会成员` (1×) +- `一名愤怒的党员` (1×) +- `一名记者` (3×) +- `纪律委员会主席` (1×) +- `一名支持者` (2×) +- `一名忧心的选民` (1×) +- `政治领袖` (1×) +- `一位忧心忡忡的选民` (1×) +- `孩子` (3×) +- `女老师` (1×) +- `父亲` (1×) +- `他富有的伴侣` (1×) +- `它的主人` (1×) +- `家长` (1×) +- `愤怒的邻居` (1×) +- `不满的邻居` (2×) +- `一位鹅肝生产者` (1×) +- `他的素食者邻居` (1×) +- `他的朋友` (1×) +- `他的老朋友` (1×) +- `一位不满的邻居` (1×) +- `一位恼火的邻居` (1×) +- `一位邻居` (1×) +- `他那半信半疑的邻居` (1×) +- `一位老人` (1×) +- `一位家长` (1×) +- `一位孕妇` (1×) +- `教练` (1×) +- `主人` (1×) + +**fa**: +- `ژولیوس سزار` (1×) +- `یک مخالف سیاسی` (1×) +- `امپراتور کالیگولا` (1×) +- `سقراط` (1×) +- `سینون` (1×) +- `یک باستان‌شناس` (1×) +- `شارل هفتم` (1×) +- `یک امپراتور انسان‌گرا` (1×) +- `ناپلئون` (1×) +- `یک حق‌رأی‌خواه زن` (1×) +- `رئیس دادگاه انقلاب` (1×) +- `ماری آنتوانت` (1×) +- `یکی از اعضای جبهه مردمی` (1×) +- `یک ژنرال پنج‌ستاره` (1×) +- `ژنرال` (1×) +- `رئیس سازمان ملل متحد` (1×) +- `یک مسافر` (1×) +- `ریش‌آبی` (1×) +- `گرتل` (1×) +- `پادشاه ایران` (1×) +- `سفیدبرفی` (1×) +- `بندانگشتی` (1×) +- `زن یتی` (1×) +- `شاهزاده رؤیاها` (1×) +- `بره` (1×) +- `نی‌نواز` (1×) +- `امپراتور` (1×) +- `عیسی` (1×) +- `نگهبان ورودی` (1×) +- `حوا` (1×) +- `یک فرشته` (1×) +- `دادستان` (1×) +- `یک فرد مذهبی` (1×) +- `پنه‌لوپه` (1×) +- `یک قهرمان بی‌باک` (1×) +- `هادس` (1×) +- `ثور` (1×) +- `زئوس` (1×) +- `دوروتی` (1×) +- `یک دانش‌آموز هاگوارتز` (1×) +- `آلیس` (1×) +- `شرلوک هولمز` (1×) +- `شبح` (1×) +- `یک شکارچی خون‌آشام` (1×) +- `آشنای دور` (1×) +- `صیدش` (1×) +- `شریک جنسی‌اش` (1×) +- `آن یکی` (1×) +- `دلبر دیشبی‌اش` (1×) +- `کسی که آنلاین با او آشنا شده` (1×) +- `کسی که گوش موسیقایی ندارد` (1×) +- `سرآشپز` (1×) +- `معشوق` (1×) +- `کسی که نمی‌خواهد هم‌خانه شود` (1×) +- `شریک زندگی‌اش` (4×) +- `شریکش که یک فرزند دارد` (1×) +- `کسی که بچه می‌خواهد` (1×) +- `دوستدار سگ‌ها` (1×) +- `شریکش` (2×) +- `شریکی که همه‌چیز را برنامه‌ریزی کرده است` (1×) +- `شریک زندگیِ ورزشکار` (1×) +- `فروشنده بدگمان` (1×) +- `شکاک` (1×) +- `شریک زندگی` (1×) +- `شریک زندگی شرمنده` (1×) +- `نیمه دیگرش` (1×) +- `اکسش` (1×) +- `شریکی که می‌خواهد ازدواج کند` (1×) +- `شریک عاطفی‌اش` (6×) +- `شریک محتاط` (1×) +- `کسی که می‌خواهد ازدواج کند` (1×) +- `مشتری` (3×) +- `والدین دانش‌آموزان` (1×) +- `مشتری‌اش` (1×) +- `یک خبرنگار کارآموز` (1×) +- `مشتریِ علاقه‌مند به یک ماشین کلاسیک` (1×) +- `صاحب آفتاب‌پرست` (1×) +- `کارفرما` (1×) +- `مشتری گرسنه` (1×) +- `یک مشتری` (1×) +- `والد نگران` (1×) +- `مشتری زیر باران` (1×) +- `شاگرد بااستعداد` (1×) +- `نماینده ملوان‌ها` (1×) +- `یک کارآموز جدی` (1×) +- `نمایندگان اتحادیه` (1×) +- `کارمند شایسته‌اش` (1×) +- `مسئولش` (1×) +- `یکی دیگر از اعضای کمیته` (1×) +- `رئیس` (1×) +- `رئیسش` (1×) +- `یک جانورشناس برجسته` (1×) +- `پزشکش` (1×) +- `تأمین‌کننده‌اش` (1×) +- `یکی از اعضای کمیته اخلاق` (1×) +- `دانشمند` (1×) +- `یکی از اعضای کمیته بازی‌ها` (1×) +- `یکی از مدیران فیفا` (1×) +- `یک تنیس‌باز حرفه‌ای` (1×) +- `مدیر استخر` (1×) +- `پروفسور تورنسل` (1×) +- `رأی‌دهندگانش` (1×) +- `Panoramix` (1×) +- `طراح لباس` (1×) +- `شکارچی روح` (1×) +- `آدم‌کش حرفه‌ای` (1×) +- `برگزیده` (1×) +- `یک زن ثروتمند` (1×) +- `لوک` (1×) +- `Rachel` (1×) +- `یک تهیه‌کننده بدبین` (1×) +- `فرودو` (1×) +- `یک کیهان‌نورد` (1×) +- `یک فرد در معرض خطر` (1×) +- `نمونهٔ اصلی` (1×) +- `یک گردشگر کرهٔ جنوبی` (1×) +- `یک فیزیک‌دان` (1×) +- `یک زیست‌شناس نامدار` (1×) +- `وزیر` (1×) +- `یکی از اعضای اپوزیسیون` (1×) +- `مجری برنامه` (1×) +- `یکی از اعضای کمیسیون تحقیق` (1×) +- `یک فعال حزبی خشمگین` (1×) +- `یک روزنامه‌نگار` (3×) +- `رئیس کمیسیون انضباطی` (1×) +- `یک هوادار` (2×) +- `یک رأی‌دهنده نگران` (2×) +- `رهبر جریان سیاسی` (1×) +- `فرزند` (1×) +- `معلم` (1×) +- `کودک` (2×) +- `پدر` (1×) +- `شریک زندگی ثروتمندش` (1×) +- `صاحبش` (1×) +- `والد` (1×) +- `همسایهٔ خشمگین` (1×) +- `همسایه ناراضی` (2×) +- `یک تولیدکننده فوا گراس` (1×) +- `همسایه گیاه‌خوارش` (1×) +- `دوستش` (1×) +- `دوست قدیمی‌اش` (1×) +- `یک همسایه ناراضی` (1×) +- `یک همسایه کلافه` (1×) +- `یک همسایه` (1×) +- `همسایهٔ شکاک او` (1×) +- `یک سالمند` (1×) +- `یکی از والدین` (1×) +- `یک زن باردار` (1×) +- `مربی` (1×) +- `میزبان` (1×) + +
+ +## 6. Translation Coverage Report + +### Fallacies — Content field coverage + +| Field | FR filled | EN | RU | PT | AR | ES | ZH | FA | +|-------|----------:|---:|---:|---:|---:|---:|---:|---:| +| example_fr | 1361 | 1361 | 1361 | 1361 | 1361 | 1361 | 1361 | 1361 | +| desc_fr | 1408 | 1408 | 1408 | 1408 | 1408 | 1408 | 1408 | 1408 | +| text_fr | 1408 | 1408 | 1408 | 1408 | 1408 | 1408 | 1408 | 1408 | + +### Scenarii — Content field coverage + +| Field | FR filled | EN | RU | PT | AR | ES | ZH | FA | +|-------|----------:|---:|---:|---:|---:|---:|---:|---:| +| piocheur | 167 | 167 | 167 | 167 | 167 | 167 | 167 | 167 | +| enjeu | 167 | 167 | 167 | 167 | 167 | 167 | 167 | 167 | +| baratineur | 167 | 167 | 167 | 167 | 167 | 167 | 167 | 167 | +| suggestion | 167 | 167 | 167 | 167 | 167 | 167 | 167 | 167 | +| contexte | 167 | 167 | 167 | 167 | 167 | 167 | 167 | 167 | + +### Virtues — Content field coverage + +| Field | FR filled | EN | RU | PT | AR | ES | ZH | FA | +|-------|----------:|---:|---:|---:|---:|---:|---:|---:| +| remark_fr | 223 | 223 | 223 | 223 | 223 | 223 | 223 | 223 | +| title_fr | 223 | 223 | 223 | 223 | 223 | 223 | 223 | 223 | +| description_fr | 223 | 223 | 223 | 223 | 223 | 223 | 223 | 223 | + +### Rules — Content field coverage + +| Field | FR filled | EN | RU | PT | AR | ES | ZH | FA | +|-------|----------:|---:|---:|---:|---:|---:|---:|---:| +| Text | 24 | 24 | 24 | 24 | 24 | 24 | 24 | 24 | + +## 7. Summary & Worklist + +| Dataset | Inconsistencies | Type | Severity | +|---------|----------------:|------|----------| +| Fallacies (families) | 0 | — | ✅ CLEAN | +| Scenarii (categories) | 25 | PT case/translation | ⚠️ Minor | +| Virtues (families) | 21 | AR/FA/ZH/ES/RU variants | ⚠️ Medium | +| Rules (headings) | 0 | — | ✅ CLEAN | +| Scenarii role columns | ~~FALSE POSITIVE~~ | Character names, not terminology | ✅ N/A | +| **REAL TOTAL** | **46** | | | + +### Analysis of Real Inconsistencies + +**Scenarii PT (25 items)** — Two types: +1. **Case inconsistency** (15 items): Identical translations with different capitalization (e.g., "história" vs "História"). These are cosmetic and don't affect gameplay. +2. **Translation variants** (10 items): Genuinely different PT translations for the same FR term (e.g., "contos" vs "contos e literatura", "vida a dois" vs "vida de casal"). These are editorial choices that may be intentional. + +**Virtues AR/FA/ZH/ES/RU (21 items)** — Translation variants across languages: +1. **AR (6 items)**: "Argument pertinent" → حجة وجيهة الصلة vs حجة ملائمة (1 record differs from 32) +2. **FA (5 items)**: "Présentation intègre" → ارائهٔ درستکارانه vs ارائه صادقانه (1 vs 24) +3. **ZH (4 items)**: Same pattern — 1 record uses a different translation than the majority +4. **ES (2 items)**: "Rigueur mathématique" → "Rigor matemático" vs "Exactitud matemática" +5. **RU (1 item)**: "Objectif non complaisant" → 2 variant translations +6. Pattern: **1 outlier record vs majority consensus** — likely gpt-5.5 first-pass inconsistency + +### Recommended Actions + +1. **Scenarii PT case**: Standardize capitalization (cosmetic, low priority) +2. **Scenarii PT variants**: Decide canonical translation for the 10 genuine variants +3. **Virtues outliers**: Align the 1-record outliers to the majority translation (21 cells total across 5 languages) +4. **No Fallacies or Rules work needed** — both are fully consistent +5. **gpt-5.5 pass** (Étape 3): Only the 46 flagged cells need review, not the entire dataset + +--- + +*Generated by TranslationConsistencyReport.ps1 — #192 Étape 1* From c02e3ecda569f5f5a552c48c1895b2be43ab8786 Mon Sep 17 00:00:00 2001 From: Your Date: Wed, 3 Jun 2026 06:58:21 +0200 Subject: [PATCH 2/2] fix(qa): per-CardSet detector thresholds for Rules calibration (#412 follow-up) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rules cards have white backgrounds → blank-ratio naturally high (65-95%) causing 23/24 false positives with flat 65% threshold. Bottom-saturation max observed ~18% on saturated rules_23 → 85% threshold never triggers. Per-CardSet thresholds: - Rules: blank-ratio 92%, bottom-sat 12% - Others (Virtues, Fallacies-Web, Scenarii): 65% / 25% Raw values still exposed in pivot tables for #250 Phase 1 analysis. Co-Authored-By: Claude Opus 4.6 --- .../VisualQaHarness.cs | 53 ++++++++++++++----- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/Generation/Converters/Argumentum.AssetConverter.VisualTests/VisualQaHarness.cs b/Generation/Converters/Argumentum.AssetConverter.VisualTests/VisualQaHarness.cs index ef812f89f..2b5808e37 100644 --- a/Generation/Converters/Argumentum.AssetConverter.VisualTests/VisualQaHarness.cs +++ b/Generation/Converters/Argumentum.AssetConverter.VisualTests/VisualQaHarness.cs @@ -42,12 +42,37 @@ public class VisualQaHarness : IDisposable "Rules", "Virtues", "Fallacies-Web", "Scenarii" }; - // Detector thresholds + // Detector thresholds — per-CardSet calibration (#412 finding ai-01) + // Rules cards have white backgrounds, so blank-ratio is naturally high (~65-95%) + // and bottom-saturation is naturally low (~4-18%). Flat thresholds over/under-flag. + private static readonly Dictionary Thresholds = new() + { + ["Rules"] = new() { BlankRatio = 0.92f, BottomSat = 0.12f }, + ["Virtues"] = new() { BlankRatio = 0.65f, BottomSat = 0.25f }, + ["Fallacies-Web"] = new() { BlankRatio = 0.65f, BottomSat = 0.25f }, + ["Scenarii"] = new() { BlankRatio = 0.65f, BottomSat = 0.25f }, + }; + + // Default thresholds for unregistered CardSets + private const float DefaultBlankRatioThreshold = 0.65f; + private const float DefaultBottomSatThreshold = 0.85f; + private const float WhiteBandThreshold = 0.98f; // pixels with R>250 AND G>250 AND B>250 - private const float BlankRatioThreshold = 0.65f; // >65% white pixels in body → FLAG - private const float BottomSatThreshold = 0.85f; // >85% non-white pixels in bottom 10% → FLAG private const float WhitePixelMax = 250f / 255f; // normalized threshold for "white" + /// Per-CardSet threshold pair (blank-ratio, bottom-saturation). + private class CardSetThresholds + { + public float BlankRatio = DefaultBlankRatioThreshold; + public float BottomSat = DefaultBottomSatThreshold; + } + + private static float GetBlankRatioThreshold(string cardSet) + => Thresholds.TryGetValue(cardSet, out var t) ? t.BlankRatio : DefaultBlankRatioThreshold; + + private static float GetBottomSatThreshold(string cardSet) + => Thresholds.TryGetValue(cardSet, out var t) ? t.BottomSat : DefaultBottomSatThreshold; + public VisualQaHarness(ITestOutputHelper output) { _output = output; @@ -181,7 +206,7 @@ public void VisualQa_BlankRatio_Rules_NotExcessivelyEmpty() foreach (var imgPath in images) { scanned++; - var blankResult = DetectBlankRatio(imgPath); + var blankResult = DetectBlankRatio(imgPath, "Rules"); if (blankResult.Flagged) { flags.Add($"{lang}/Rules/{Path.GetFileName(imgPath)}: " + @@ -221,7 +246,7 @@ public void VisualQa_BottomSaturation_Rules_NotOverflowing() foreach (var imgPath in images) { scanned++; - var satResult = DetectBottomSaturation(imgPath); + var satResult = DetectBottomSaturation(imgPath, "Rules"); if (satResult.Flagged) { flags.Add($"{lang}/Rules/{Path.GetFileName(imgPath)}: " + @@ -258,8 +283,8 @@ public void VisualQa_FullGrid_AllCards_AllDetectors() var fileName = Path.GetFileName(imgPath); var band = DetectWhiteBand(imgPath); - var blank = DetectBlankRatio(imgPath); - var sat = DetectBottomSaturation(imgPath); + var blank = DetectBlankRatio(imgPath, cardSet); + var sat = DetectBottomSaturation(imgPath, cardSet); var result = new CardCheckResult { @@ -323,7 +348,7 @@ public void VisualQa_FullGrid_AllCards_AllDetectors() r.FileName == card && r.Language == lang); if (match == null) return "—"; var val = match.BlankRatioValue; - if (val > BlankRatioThreshold) return $"**{val:P0}**"; + if (val > GetBlankRatioThreshold("Rules")) return $"**{val:P0}**"; return $"{val:P0}"; }); _output.WriteLine($"| {card} | {string.Join(" | ", vals)} |"); @@ -342,7 +367,7 @@ public void VisualQa_FullGrid_AllCards_AllDetectors() r.FileName == card && r.Language == lang); if (match == null) return "—"; var val = match.BottomSatValue; - if (val > BottomSatThreshold) return $"**{val:P0}**"; + if (val > GetBottomSatThreshold("Rules")) return $"**{val:P0}**"; return $"{val:P0}"; }); _output.WriteLine($"| {card} | {string.Join(" | ", vals)} |"); @@ -441,8 +466,9 @@ private static (bool Flagged, float WhiteRatio, int MaxBandRow) DetectWhiteBand( /// /// Measures the ratio of white pixels in the "body" area of the card /// (middle 60%, rows 20%-80%). A high ratio indicates an underfilled card. + /// Uses per-CardSet threshold for flagging. /// - private static (bool Flagged, float BlankRatio) DetectBlankRatio(string imagePath) + private static (bool Flagged, float BlankRatio) DetectBlankRatio(string imagePath, string cardSet) { try { @@ -469,7 +495,7 @@ private static (bool Flagged, float BlankRatio) DetectBlankRatio(string imagePat } float ratio = totalPixels > 0 ? (float)whitePixels / totalPixels : 0f; - return (ratio > BlankRatioThreshold, ratio); + return (ratio > GetBlankRatioThreshold(cardSet), ratio); } catch { @@ -480,8 +506,9 @@ private static (bool Flagged, float BlankRatio) DetectBlankRatio(string imagePat /// /// Measures the density of non-white pixels in the bottom 10% of the card. /// High density indicates text/content extending to the very bottom edge (overflow). + /// Uses per-CardSet threshold for flagging. /// - private static (bool Flagged, float Saturation) DetectBottomSaturation(string imagePath) + private static (bool Flagged, float Saturation) DetectBottomSaturation(string imagePath, string cardSet) { try { @@ -508,7 +535,7 @@ private static (bool Flagged, float Saturation) DetectBottomSaturation(string im } float saturation = totalPixels > 0 ? (float)nonWhitePixels / totalPixels : 0f; - return (saturation > BottomSatThreshold, saturation); + return (saturation > GetBottomSatThreshold(cardSet), saturation); } catch {