Skip to content

Commit 3664f5f

Browse files
committed
right name in localization
1 parent 1d9840b commit 3664f5f

5 files changed

Lines changed: 92 additions & 4 deletions

File tree

AGENTS.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ Rule format:
7777
- remove obsolete rules when a better one replaces them
7878

7979
- Use `PrompterOne` as the canonical product, solution, namespace, and folder name across code, docs, tests, and build paths; do not reintroduce legacy product-name variants after the rename.
80+
- Localized UI copy must keep the product brand as `PrompterOne` in every language; do not translate, transliterate, or inflect the app name into locale-specific variants such as `СуфлерOne`.
8081
- Repo-owned docs, README, ADRs, and AGENTS files must not contain local usernames, home-directory paths, or personal machine-specific references; use repo-relative paths or neutral wording instead.
8182
- Public-facing screenshots and any screenshot-generating or screenshot-asserting tests must use English-visible content so README, docs, and release assets stay globally readable and consistent.
8283
- Public-facing screenshots that include camera or preview feeds must not ship mirrored or reversed readable text; choose or configure the capture so visible text reads correctly in the final asset.
@@ -441,7 +442,7 @@ Repo-specific design rules:
441442
- Never introduce a non-SOLID design unless the exception is explicitly documented under `exception_policy`.
442443
- Never force-push to `main`.
443444
- Never approve or merge on behalf of a human maintainer.
444-
- When the task explicitly needs delivery, the agent may commit, push to `main` or a feature branch, open a PR, and merge it after the required tests and validation commands pass.
445+
- The agent may commit locally after the required tests and validation commands pass, but it must not push, merge, or otherwise publish repo changes until the user gives an explicit command to do so.
445446

446447
### Boundaries
447448

@@ -488,6 +489,7 @@ Ask first:
488489
- any continued xUnit ownership of the repo test stack; when the user asks for TUnit, remove xUnit packages and attributes instead of leaving a mixed long-term setup behind
489490
- any use of `--no-build` in repo commands, docs, or CI; test runs must rebuild against the current source and current WASM assets every time
490491
- mixed-language root README or public entry docs; keep them English-only unless the user explicitly asks otherwise
492+
- any push or publish action without the user's explicit command; local commits are fine, but network delivery must stay user-controlled
491493
- any reintroduction of a repo-local `design/` prototype folder as a parallel source of truth; the shipped Blazor UI must be the only product reference
492494
- fake `display_*` or other presentation-only script metrics that override real TPS-derived words, segments, speed, or duration in user-facing UI
493495
- made-up About/team content or stale attribution; About must point to real Managed Code ownership and official links

src/PrompterOne.Shared/Localization/SharedResource.es.resx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -859,7 +859,7 @@
859859
<value>Finalizar recorrido</value>
860860
</data>
861861
<data name="OnboardingStepLibrary" xml:space="preserve">
862-
<value>Biblioteca</value>
862+
<value>PrompterOne</value>
863863
</data>
864864
<data name="OnboardingStepTps" xml:space="preserve">
865865
<value>TPS</value>

src/PrompterOne.Shared/Localization/SharedResource.fr.resx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -859,7 +859,7 @@
859859
<value>Terminer la visite</value>
860860
</data>
861861
<data name="OnboardingStepLibrary" xml:space="preserve">
862-
<value>Bibliothèque</value>
862+
<value>PrompterOne</value>
863863
</data>
864864
<data name="OnboardingStepTps" xml:space="preserve">
865865
<value>TPS</value>

src/PrompterOne.Shared/Localization/SharedResource.uk.resx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -874,7 +874,7 @@
874874
<value>Завершити тур</value>
875875
</data>
876876
<data name="OnboardingStepLibrary" xml:space="preserve">
877-
<value>Бібліотека</value>
877+
<value>PrompterOne</value>
878878
</data>
879879
<data name="OnboardingStepTps" xml:space="preserve">
880880
<value>TPS</value>
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
using System.Xml.Linq;
2+
3+
namespace PrompterOne.Web.Tests;
4+
5+
public sealed class LocalizationBrandNameContractTests
6+
{
7+
private const string BrandToken = "PrompterOne";
8+
private const string DefaultResourceFileName = "SharedResource.resx";
9+
private static readonly string RepoRoot = ResolveRepoRoot();
10+
private static readonly string LocalizationDirectory = Path.Combine(RepoRoot, "src", "PrompterOne.Shared", "Localization");
11+
private static readonly string[] ResourcePaths = Directory.EnumerateFiles(LocalizationDirectory, "SharedResource*.resx", SearchOption.TopDirectoryOnly)
12+
.OrderBy(path => path, StringComparer.Ordinal)
13+
.ToArray();
14+
15+
private static readonly string[] ForbiddenBrandVariants =
16+
[
17+
"Prompter One",
18+
"SuflerOne",
19+
"СуфлерOne"
20+
];
21+
22+
private static readonly IReadOnlyDictionary<string, string> DefaultResourceValues = LoadResourceValues(
23+
Path.Combine(LocalizationDirectory, DefaultResourceFileName));
24+
25+
private static readonly string[] BrandBearingKeys = DefaultResourceValues
26+
.Where(entry => entry.Value.Contains(BrandToken, StringComparison.Ordinal))
27+
.Select(entry => entry.Key)
28+
.OrderBy(key => key, StringComparer.Ordinal)
29+
.ToArray();
30+
31+
public static IEnumerable<(string ResourcePath, string ForbiddenVariant)> ForbiddenVariantCases =>
32+
ResourcePaths.SelectMany(
33+
resourcePath => ForbiddenBrandVariants.Select(forbiddenVariant => (resourcePath, forbiddenVariant)));
34+
35+
public static IEnumerable<(string ResourcePath, string ResourceKey)> BrandBearingResourceCases =>
36+
ResourcePaths.SelectMany(
37+
resourcePath =>
38+
{
39+
var resourceValues = LoadResourceValues(resourcePath);
40+
return BrandBearingKeys
41+
.Where(resourceValues.ContainsKey)
42+
.Select(resourceKey => (resourcePath, resourceKey));
43+
});
44+
45+
[Test]
46+
[MethodDataSource(nameof(ForbiddenVariantCases))]
47+
public void SharedResources_DoNotContainLocalizedBrandVariants(string resourcePath, string forbiddenVariant)
48+
{
49+
var resourceFileText = File.ReadAllText(resourcePath);
50+
51+
Assert.DoesNotContain(forbiddenVariant, resourceFileText, StringComparison.Ordinal);
52+
}
53+
54+
[Test]
55+
[MethodDataSource(nameof(BrandBearingResourceCases))]
56+
public void SharedResources_KeepExactBrandToken_ForBrandBearingValues(string resourcePath, string resourceKey)
57+
{
58+
var resourceValues = LoadResourceValues(resourcePath);
59+
var resourceValue = resourceValues[resourceKey];
60+
61+
Assert.Contains(
62+
BrandToken,
63+
resourceValue,
64+
StringComparison.Ordinal);
65+
}
66+
67+
private static IReadOnlyDictionary<string, string> LoadResourceValues(string resourcePath)
68+
{
69+
var document = XDocument.Load(resourcePath);
70+
71+
return document.Root?
72+
.Elements("data")
73+
.Select(
74+
element => new
75+
{
76+
Name = element.Attribute("name")?.Value,
77+
Value = element.Element("value")?.Value
78+
})
79+
.Where(entry => !string.IsNullOrWhiteSpace(entry.Name) && entry.Value is not null)
80+
.ToDictionary(entry => entry.Name!, entry => entry.Value!, StringComparer.Ordinal)
81+
?? new Dictionary<string, string>(StringComparer.Ordinal);
82+
}
83+
84+
private static string ResolveRepoRoot() =>
85+
Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "../../../../../"));
86+
}

0 commit comments

Comments
 (0)