From 96762a648eb9f7d0f4c69a9bd1cef1396c3249e2 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 4 Nov 2026 16:42:50 +0000 Subject: [PATCH 1/2] Let the listing drop games that are only an address A game that publishes no name is listed under host:port (CatalogueBinder.NameOf). Crawlers and API clients asked for a way to skip those rows. New facet `unnamed`, shared by /api/games and the /games panel through the one binding: `?unnamed=!yes` drops them, `?unnamed=yes` keeps only them, unset changes nothing. It reads the name rather than a flag (AddressName), so a rename takes a game out of the set with nothing else to update. Labelled derived: the name is ours, and a game listed by address may have a name it never published. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Rqqt7UgM5rnGnNp9CTLXRK --- src/MUI.Catalog/Games/AddressName.cs | 55 ++++++++++ src/MUI.Catalog/Games/Views.cs | 8 ++ .../Search/Facets.FacetedSearch.Choices.cs | 9 ++ src/MUI.Catalog/Search/Facets.cs | 11 ++ src/MUI.Web/Api/ApiModels.cs | 11 +- src/MUI.Web/Api/GameFilterBinding.cs | 4 +- src/MUI.Web/Api/OpenApiDocument.cs | 6 ++ src/MUI.Web/Components/Text/FacetWords.cs | 3 + src/MUI.Web/IndexableFacet.cs | 1 + src/MUI.Web/Localization/Messages.Chrome.cs | 6 ++ src/MUI.Web/Resources/Messages.resx | 12 +++ .../Queries/FacetQueriesPostgresTests.cs | 1 + .../Search/AddressNameTests.cs | 29 +++++ .../Search/FacetedSearchTests.cs | 35 ++++++ tests/MUI.Web.Tests/FacetSurfaceTests.cs | 1 + tests/MUI.Web.Tests/UnnamedFacetTests.cs | 100 ++++++++++++++++++ 16 files changed, 289 insertions(+), 3 deletions(-) create mode 100644 src/MUI.Catalog/Games/AddressName.cs create mode 100644 tests/MUI.Catalog.Tests/Search/AddressNameTests.cs create mode 100644 tests/MUI.Web.Tests/UnnamedFacetTests.cs diff --git a/src/MUI.Catalog/Games/AddressName.cs b/src/MUI.Catalog/Games/AddressName.cs new file mode 100644 index 00000000..23acb108 --- /dev/null +++ b/src/MUI.Catalog/Games/AddressName.cs @@ -0,0 +1,55 @@ +using System.Globalization; +using System.Text.RegularExpressions; + +namespace MUI.Catalog; + +/// +/// Whether a game's name is only the address we found it at — mud.example.org:4000, +/// 203.0.113.7:23 — because the game told us no name of its own. +/// +/// +/// +/// CatalogueBinder.NameOf falls back to {host}:{port} when neither MSSP nor a login +/// command gave a meaningful name, and that name stays until a probe, an owner or staff supplies a +/// better one. This reads the name rather than a flag, so a rename takes the game out of the set with +/// nothing else to update. +/// +/// +/// A statement about the name we list the game under, never that the game has no name — +/// it may well have one it did not publish (rule 5). +/// +/// +/// The host has to look like an address: a dotted name, an IPv4 literal, or an IPv6 literal (bare, +/// which is how the fallback writes it, or bracketed). A name such as Babylon:5 carries no dot +/// and stays a name. +/// +/// +public static partial class AddressName +{ + public static bool Is(string? name) + { + if (string.IsNullOrWhiteSpace(name)) + { + return false; + } + + var match = Shape().Match(name.Trim()); + + if (!match.Success + || !int.TryParse( + match.Groups["port"].ValueSpan, NumberStyles.None, CultureInfo.InvariantCulture, out var port) + || port is < 1 or > 65535) + { + return false; + } + + var host = match.Groups["host"].Value; + + return host.Contains('.', StringComparison.Ordinal) || host.Contains(':', StringComparison.Ordinal); + } + + [GeneratedRegex( + @"^(?\[[0-9A-Fa-f:.]+\]|[A-Za-z0-9.-]+|[0-9A-Fa-f:.]+):(?[0-9]{1,5})$", + RegexOptions.CultureInvariant)] + private static partial Regex Shape(); +} diff --git a/src/MUI.Catalog/Games/Views.cs b/src/MUI.Catalog/Games/Views.cs index f3056f20..f4459083 100644 --- a/src/MUI.Catalog/Games/Views.cs +++ b/src/MUI.Catalog/Games/Views.cs @@ -396,6 +396,14 @@ public sealed record GameFilter public FacetChoice? Language { get; init; } + /// + /// The games listed under their address rather than a name (). + /// + /// + /// !yes drops them. Unset by default, so every existing caller still gets them. + /// + public FacetChoice? Unnamed { get; init; } + /// /// What order the answer comes back in. /// diff --git a/src/MUI.Catalog/Search/Facets.FacetedSearch.Choices.cs b/src/MUI.Catalog/Search/Facets.FacetedSearch.Choices.cs index 5ae594a0..83c65964 100644 --- a/src/MUI.Catalog/Search/Facets.FacetedSearch.Choices.cs +++ b/src/MUI.Catalog/Search/Facets.FacetedSearch.Choices.cs @@ -84,5 +84,14 @@ private sealed record ChoiceFacet( FacetTokens.GrowthDirections), new(FacetKeys.Genre, FacetEvidence.Declared, r => [r.Genre], f => f.Genre), new(FacetKeys.Language, FacetEvidence.Declared, r => [r.Language], f => f.Language), + + // Last: it is not a question about the game's character but about the row's label, and a + // reader browsing for a game meets it after everything that is. + new( + FacetKeys.Unnamed, + FacetEvidence.Derived, + r => [AddressName.Is(r.Summary.Name) ? FacetTokens.Yes : null], + f => f.Unnamed, + FacetTokens.YesOnly), ]; } diff --git a/src/MUI.Catalog/Search/Facets.cs b/src/MUI.Catalog/Search/Facets.cs index 9dd5739a..1bed4980 100644 --- a/src/MUI.Catalog/Search/Facets.cs +++ b/src/MUI.Catalog/Search/Facets.cs @@ -99,6 +99,17 @@ public static class FacetKeys /// public const string Unreachable = "unreachable"; + /// + /// Games listed under their address because they told us no name — see . + /// + /// + /// What crawlers and API clients reach for to skip rows like mud.example.org:4000: + /// ?unnamed=!yes. Ours, labelled : the name is the one + /// we list the game under, and a game listed by address may have a name it never + /// published, so this never says a game is nameless (rule 5). + /// + public const string Unnamed = "unnamed"; + /// /// What order the listing comes back in. A filter parameter by spelling and by plumbing, so the /// panel, the page and the read API cannot grow two words for one question. diff --git a/src/MUI.Web/Api/ApiModels.cs b/src/MUI.Web/Api/ApiModels.cs index 967b9759..bc57da78 100644 --- a/src/MUI.Web/Api/ApiModels.cs +++ b/src/MUI.Web/Api/ApiModels.cs @@ -278,7 +278,13 @@ public sealed record FilterView( GameSort Sort, /// The old name for , at the same value. Prefer the new one. - string? CodebaseFamily = null) + string? CodebaseFamily = null, + + /// + /// Whether the games listed under their address rather than a name were asked for, or dropped. + /// + /// Last and defaulted, so the positional shape every earlier field has is unchanged. + string? Unnamed = null) { public static FilterView Of(GameFilter filter) { @@ -303,7 +309,8 @@ public static FilterView Of(GameFilter filter) filter.Genre?.Token, filter.Language?.Token, filter.Sort, - filter.Codebase?.Token); + filter.Codebase?.Token, + filter.Unnamed?.Token); } } diff --git a/src/MUI.Web/Api/GameFilterBinding.cs b/src/MUI.Web/Api/GameFilterBinding.cs index f3e96ba2..7d86ae95 100644 --- a/src/MUI.Web/Api/GameFilterBinding.cs +++ b/src/MUI.Web/Api/GameFilterBinding.cs @@ -59,6 +59,7 @@ private static bool TryRead( || !TryLastSeen(read, out var seen, out error) || !TryToggle(read, FacetKeys.Uncounted, out var uncounted, out error) || !TryToggle(read, FacetKeys.Unreachable, out var unreachable, out error) + || !TryToggle(read, FacetKeys.Unnamed, out var unnamed, out error) || !TryTrending(read, out var trending, out error) || !TrySort(read, out var sort, out error)) { @@ -98,6 +99,7 @@ private static bool TryRead( Trending = trending, Genre = Choice(read, FacetKeys.Genre), Language = Choice(read, FacetKeys.Language), + Unnamed = unnamed, Sort = sort, }; @@ -135,7 +137,7 @@ .. read(FacetKeys.Protocol) } /// - /// One of the two measurement switches: yes, !yes, or not asked. + /// One of the yes-or-nothing switches (could not count, could not reach, listed by address): yes, !yes, or not asked. /// /// /// Refused rather than ignored: its vocabulary is one word, so anything else is a typo, and a typo diff --git a/src/MUI.Web/Api/OpenApiDocument.cs b/src/MUI.Web/Api/OpenApiDocument.cs index dfb3064a..facd0fbf 100644 --- a/src/MUI.Web/Api/OpenApiDocument.cs +++ b/src/MUI.Web/Api/OpenApiDocument.cs @@ -178,6 +178,12 @@ private static JsonArray ListingParameters() { parameters.Add(Parameter(key, "yes selects, !yes excludes; ~unknown is an alias for !yes.", values: ["yes", "!yes", "~unknown"])); } + parameters.Add(Parameter( + FacetKeys.Unnamed, + "Games listed under their address (host:port) because they published no name of their own. " + + "!yes excludes them, yes selects only them; ~unknown is an alias for !yes. " + + "Unset, they are included.", + values: ["yes", "!yes", "~unknown"])); parameters.Add(Parameter(FacetKeys.Trending, "Weekly count trend; prefix ! to exclude, or ~unknown for no trend.")); return parameters; } diff --git a/src/MUI.Web/Components/Text/FacetWords.cs b/src/MUI.Web/Components/Text/FacetWords.cs index bb30e704..e3a42037 100644 --- a/src/MUI.Web/Components/Text/FacetWords.cs +++ b/src/MUI.Web/Components/Text/FacetWords.cs @@ -38,6 +38,7 @@ public static class FacetWords FacetKeys.Trending => "facet.group.trending", FacetKeys.Genre => "facet.group.genre", FacetKeys.Language => "facet.group.language", + FacetKeys.Unnamed => "facet.group.unnamed", // Named here, not in the chip builder, so a facet is put into words in one place. FacetKeys.Text => "facet.group.search", @@ -176,6 +177,7 @@ public static string Value(string tag, string key, FacetValue value) // the same claim. FacetKeys.Uncounted => Messages.For(tag, "state.uncounted"), FacetKeys.Unreachable => Messages.For(tag, "state.unreachable"), + FacetKeys.Unnamed => Messages.For(tag, "facet.unnamed.yes"), // Everything else IS the value — a codebase name, version string, protocol acronym — and // translating it would destroy the evidence rather than localize anything. @@ -205,6 +207,7 @@ public static string Excluded(string tag, string key, FacetValue value) // say what the reader actually did — hid some rows. FacetKeys.Uncounted => Messages.For(tag, "facet.excluded.uncounted"), FacetKeys.Unreachable => Messages.For(tag, "facet.excluded.unreachable"), + FacetKeys.Unnamed => Messages.For(tag, "facet.excluded.unnamed"), // A message rather than string concatenation: negation word order varies by language, and // concatenating here would fix English's order for every translator. diff --git a/src/MUI.Web/IndexableFacet.cs b/src/MUI.Web/IndexableFacet.cs index 77763bf1..72737d2b 100644 --- a/src/MUI.Web/IndexableFacet.cs +++ b/src/MUI.Web/IndexableFacet.cs @@ -88,6 +88,7 @@ .. Dimensions.Where(key => key != FacetKeys.Band), || filter.LastSeen is not null || filter.Uncounted is not null || filter.Unreachable is not null + || filter.Unnamed is not null || filter.CodebaseVersion is not null || filter.Family is not null || filter.Trending is not null diff --git a/src/MUI.Web/Localization/Messages.Chrome.cs b/src/MUI.Web/Localization/Messages.Chrome.cs index e56c86c5..3f78e9a7 100644 --- a/src/MUI.Web/Localization/Messages.Chrome.cs +++ b/src/MUI.Web/Localization/Messages.Chrome.cs @@ -243,6 +243,12 @@ public static partial class Messages ["facet.group.genre"] = "genre", ["facet.group.language"] = "language", + // Games listed under host:port because they published no name. The name is ours, so the + // words describe our label, never that the game has none (rule 5). + ["facet.group.unnamed"] = "name", + ["facet.unnamed.yes"] = "listed by address", + ["facet.excluded.unnamed"] = "hidden from this listing", + // ── facet values ────────────────────────────────────────────────────────────────────── ["facet.band.playersNow"] = "connected now", ["facet.band.activeThisWeek"] = "active this week", diff --git a/src/MUI.Web/Resources/Messages.resx b/src/MUI.Web/Resources/Messages.resx index d1513cec..d1c523af 100644 --- a/src/MUI.Web/Resources/Messages.resx +++ b/src/MUI.Web/Resources/Messages.resx @@ -808,6 +808,18 @@ language facet groups + + name + facet over games listed under their host:port because they published no name + + + listed by address + facet over games listed under their host:port because they published no name + + + hidden from this listing + facet over games listed under their host:port because they published no name + connected now facet values diff --git a/tests/MUI.Catalog.Tests/Persistence/Queries/FacetQueriesPostgresTests.cs b/tests/MUI.Catalog.Tests/Persistence/Queries/FacetQueriesPostgresTests.cs index 6591ba2e..99c8584a 100644 --- a/tests/MUI.Catalog.Tests/Persistence/Queries/FacetQueriesPostgresTests.cs +++ b/tests/MUI.Catalog.Tests/Persistence/Queries/FacetQueriesPostgresTests.cs @@ -353,6 +353,7 @@ await Assert.That((await queries.ListAsync(Choose(key, value.Token))).Count) FacetKeys.LastSeen => new GameFilter { LastSeen = Seen(token) }, FacetKeys.Uncounted => new GameFilter { Uncounted = FacetChoice.Parse(token) }, FacetKeys.Unreachable => new GameFilter { Unreachable = FacetChoice.Parse(token) }, + FacetKeys.Unnamed => new GameFilter { Unnamed = FacetChoice.Parse(token) }, FacetKeys.Protocol => new GameFilter { MeasuredProtocols = [token] }, FacetKeys.Tls => new GameFilter { Tls = true }, FacetKeys.Charset => new GameFilter { Charset = FacetChoice.Parse(token) }, diff --git a/tests/MUI.Catalog.Tests/Search/AddressNameTests.cs b/tests/MUI.Catalog.Tests/Search/AddressNameTests.cs new file mode 100644 index 00000000..84917960 --- /dev/null +++ b/tests/MUI.Catalog.Tests/Search/AddressNameTests.cs @@ -0,0 +1,29 @@ +namespace MUI.Catalog.Tests; + +/// +/// Which names are only the address a game was found at — the fallback CatalogueBinder.NameOf +/// writes when a game published no name. +/// +public class AddressNameTests +{ + [Test] + [Arguments("mud.example.org:4000")] + [Arguments("203.0.113.7:23")] + [Arguments("2001:db8::1:4000")] + [Arguments("[2001:db8::1]:4000")] + [Arguments(" fs.twkang.net:5555 ")] + public async Task AnAddressIsAnAddress(string name) => + await Assert.That(AddressName.Is(name)).IsTrue(); + + [Test] + [Arguments("Convergence MUSH")] + [Arguments("Babylon:5")] + [Arguments("Star Wars: 1977")] + [Arguments("mud.example.org")] + [Arguments("mud.example.org:0")] + [Arguments("mud.example.org:70000")] + [Arguments("")] + [Arguments(null)] + public async Task ANameIsNot(string? name) => + await Assert.That(AddressName.Is(name)).IsFalse(); +} diff --git a/tests/MUI.Catalog.Tests/Search/FacetedSearchTests.cs b/tests/MUI.Catalog.Tests/Search/FacetedSearchTests.cs index 3bd84dd2..4cd2c6dd 100644 --- a/tests/MUI.Catalog.Tests/Search/FacetedSearchTests.cs +++ b/tests/MUI.Catalog.Tests/Search/FacetedSearchTests.cs @@ -467,12 +467,47 @@ public async Task ASwitchNothingMatchesIsNotDrawnAtAll() await Assert.That(listing.Facets.Any(f => f.Key == FacetKeys.Unreachable)).IsFalse(); } + [Test] + public async Task AGameListedByItsAddressCanBeDroppedFromTheListing() + { + // What crawlers asked for: rows whose name is only host:port, because the game published + // none, are one parameter away from gone — and only on request. + GameFacetRow[] rows = + [ + Row("Convergence MUSH"), + Row("mud.example.org:4000"), + Row("203.0.113.7:23"), + ]; + + var all = FacetedSearch.Search(rows, new GameFilter()); + var named = FacetedSearch.Search(rows, new GameFilter { Unnamed = FacetChoice.Not(FacetTokens.Yes) }); + var unnamed = FacetedSearch.Search(rows, new GameFilter { Unnamed = FacetChoice.Of(FacetTokens.Yes) }); + + await Assert.That(all.Games.Count).IsEqualTo(3); + await Assert.That(named.Games.Select(g => g.Slug)).IsEquivalentTo(new[] { "Convergence MUSH" }); + await Assert.That(unnamed.Games.Count).IsEqualTo(2); + + var value = Value(all, FacetKeys.Unnamed, FacetTokens.Yes); + + await Assert.That(value.Count).IsEqualTo(2); + await Assert.That(Group(all, FacetKeys.Unnamed).Evidence).IsEqualTo(FacetEvidence.Derived); + } + + [Test] + public async Task AFullyNamedCatalogueOffersNoUnnamedSwitch() + { + var listing = FacetedSearch.Search([Row("busy"), Row("also")], new GameFilter()); + + await Assert.That(listing.Facets.Any(f => f.Key == FacetKeys.Unnamed)).IsFalse(); + } + private static GameFilter Choose(string key, string token) => key switch { FacetKeys.Band => new GameFilter { Band = Band(token) }, FacetKeys.LastSeen => new GameFilter { LastSeen = Seen(token) }, FacetKeys.Uncounted => new GameFilter { Uncounted = FacetChoice.Parse(token) }, FacetKeys.Unreachable => new GameFilter { Unreachable = FacetChoice.Parse(token) }, + FacetKeys.Unnamed => new GameFilter { Unnamed = FacetChoice.Parse(token) }, FacetKeys.Protocol => new GameFilter { MeasuredProtocols = [token] }, FacetKeys.Tls => new GameFilter { Tls = true }, FacetKeys.Charset => new GameFilter { Charset = FacetChoice.Parse(token) }, diff --git a/tests/MUI.Web.Tests/FacetSurfaceTests.cs b/tests/MUI.Web.Tests/FacetSurfaceTests.cs index 48618e86..1ae9d3b0 100644 --- a/tests/MUI.Web.Tests/FacetSurfaceTests.cs +++ b/tests/MUI.Web.Tests/FacetSurfaceTests.cs @@ -43,6 +43,7 @@ .. typeof(FacetKeys) // narrowing a listing to nothing on a typo. FacetKeys.Uncounted => "yes", FacetKeys.Unreachable => "yes", + FacetKeys.Unnamed => "yes", FacetKeys.Protocol => "GMCP", FacetKeys.Trending => "up", // Not the default. The point of the check below is that the parameter changed something, diff --git a/tests/MUI.Web.Tests/UnnamedFacetTests.cs b/tests/MUI.Web.Tests/UnnamedFacetTests.cs new file mode 100644 index 00000000..576e711b --- /dev/null +++ b/tests/MUI.Web.Tests/UnnamedFacetTests.cs @@ -0,0 +1,100 @@ +using MUI.Catalog; +using MUI.Web.Api; +using MUI.Web.Components; +using MUI.Web.Localization; + +namespace MUI.Web.Tests; + +/// +/// Games listed under their address because they published no name: one parameter drops them from +/// the API, and the browse panel offers the same switch. +/// +public class UnnamedFacetTests +{ + private static readonly DateTimeOffset Now = new(2026, 7, 30, 12, 0, 0, TimeSpan.Zero); + + private static GameFacetRow Row(string name) => + new( + new GameSummary( + Guid.NewGuid(), name.Replace(':', '-'), name, Tagline: null, LifecycleState.Active, + IsClaimed: false, PlayersNow: 1, Codebase: null, MeasuredProtocols: [], + LastReachableAt: Now), + ActivityBand.PlayersNow, + FacetedSearch.LastSeenOf(Now, Now), + TlsMeasured: false, + Charset: null, + Language: null, + Codebase: null, + Family: null, + Genre: null, + IsAdult: false, + Uncounted: false, + Unreachable: false); + + private static readonly GameFacetRow[] Rows = + [Row("Convergence MUSH"), Row("mud.example.org:4000"), Row("203.0.113.7:23")]; + + [Test] + public async Task TheApiReadsTheExclusionAndEchoesIt() + { + await Assert.That(GameFilterBinding.TryRead("?unnamed=!yes", out var query, out var error)) + .IsTrue().Because(error ?? string.Empty); + + await Assert.That(query.Filter.Unnamed).IsEqualTo(FacetChoice.Not(FacetTokens.Yes)); + await Assert.That(query.Echo.Unnamed).IsEqualTo("!yes"); + + var listing = FacetedSearch.Search(Rows, query.Filter); + + await Assert.That(listing.Games.Select(g => g.Name)).IsEquivalentTo(new[] { "Convergence MUSH" }); + } + + [Test] + public async Task UnsetTheyAreStillListed() + { + await Assert.That(GameFilterBinding.TryRead(string.Empty, out var query, out _)).IsTrue(); + + await Assert.That(query.Filter.Unnamed).IsNull(); + await Assert.That(query.Echo.Unnamed).IsNull(); + await Assert.That(FacetedSearch.Search(Rows, query.Filter).Games.Count).IsEqualTo(3); + } + + [Test] + public async Task AWordOutsideTheVocabularyIsRefused() + { + await Assert.That(GameFilterBinding.TryRead("?unnamed=no", out _, out var error)).IsFalse(); + await Assert.That(error).Contains("unnamed"); + } + + [Test] + public async Task ThePanelOffersTheSwitchWithALinkThatHidesThem() + { + var listing = FacetedSearch.Search(Rows, new GameFilter()); + + var html = await Render.ComponentAsync(new() + { + ["Facets"] = listing.Facets, + ["Filter"] = new GameFilter(), + }); + + await Assert.That(Render.Words(html)) + .Contains(Messages.For(Locales.SourceTag, "facet.unnamed.yes")); + await Assert.That(html).Contains("href=\"/games?unnamed=%21yes\""); + } + + [Test] + public async Task HidingThemIsAChipThatSaysItIsOurListing() + { + var filter = new GameFilter { Unnamed = FacetChoice.Not(FacetTokens.Yes) }; + var listing = FacetedSearch.Search(Rows, filter); + + var html = await Render.ComponentAsync(new() + { + ["Facets"] = listing.Facets, + ["Filter"] = filter, + ["Query"] = "?unnamed=!yes", + }); + + await Assert.That(Render.Words(html)) + .Contains(Messages.For(Locales.SourceTag, "facet.excluded.unnamed")); + } +} From e3ede32f2fd73ea4804f9dbef07c96915ee08835 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 4 Nov 2026 16:47:14 +0000 Subject: [PATCH 2/2] Give the unnamed facet its words in every offered locale EveryOfferedLocaleHasItsOwnWordsForTheSurfacesAddedWithTheCategoryPages requires every facet.* id in de, ja, nl and zh-Hans. The exclusion reuses each locale's existing "hidden from this listing" wording. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Rqqt7UgM5rnGnNp9CTLXRK --- src/MUI.Web/Resources/Messages.de.resx | 9 +++++++++ src/MUI.Web/Resources/Messages.ja.resx | 9 +++++++++ src/MUI.Web/Resources/Messages.nl.resx | 9 +++++++++ src/MUI.Web/Resources/Messages.zh-Hans.resx | 9 +++++++++ 4 files changed, 36 insertions(+) diff --git a/src/MUI.Web/Resources/Messages.de.resx b/src/MUI.Web/Resources/Messages.de.resx index aea8b79a..8a8f2eb1 100644 --- a/src/MUI.Web/Resources/Messages.de.resx +++ b/src/MUI.Web/Resources/Messages.de.resx @@ -637,6 +637,15 @@ Sprache + + Name + + + unter der Adresse gelistet + + + aus dieser Auflistung ausgeblendet + jetzt verbunden diff --git a/src/MUI.Web/Resources/Messages.ja.resx b/src/MUI.Web/Resources/Messages.ja.resx index c561701d..ead1406e 100644 --- a/src/MUI.Web/Resources/Messages.ja.resx +++ b/src/MUI.Web/Resources/Messages.ja.resx @@ -637,6 +637,15 @@ 言語 + + 名前 + + + アドレスで掲載 + + + この一覧では非表示 + 現在接続あり diff --git a/src/MUI.Web/Resources/Messages.nl.resx b/src/MUI.Web/Resources/Messages.nl.resx index 8c88c00d..2cb50bf5 100644 --- a/src/MUI.Web/Resources/Messages.nl.resx +++ b/src/MUI.Web/Resources/Messages.nl.resx @@ -637,6 +637,15 @@ taal + + naam + + + vermeld onder adres + + + niet getoond in deze lijst + nu verbonden diff --git a/src/MUI.Web/Resources/Messages.zh-Hans.resx b/src/MUI.Web/Resources/Messages.zh-Hans.resx index 992ecafa..944039e9 100644 --- a/src/MUI.Web/Resources/Messages.zh-Hans.resx +++ b/src/MUI.Web/Resources/Messages.zh-Hans.resx @@ -637,6 +637,15 @@ 语言 + + 名称 + + + 以地址列出 + + + 已从此列表中隐藏 + 当前有连接