Skip to content

Commit 7e0f561

Browse files
author
Nate McMaster
committed
Merge source code from aspnet/Localization
2 parents 04fc8ae + d93796f commit 7e0f561

File tree

63 files changed

+5985
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+5985
-0
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<Product>Microsoft ASP.NET Core</Product>
5+
<Description>Provides a request culture provider which gets culture and ui-culture from request's route data.</Description>
6+
<TargetFramework>netstandard2.0</TargetFramework>
7+
<NoWarn>$(NoWarn);CS1591</NoWarn>
8+
<GenerateDocumentationFile>true</GenerateDocumentationFile>
9+
<PackageTags>aspnetcore;localization</PackageTags>
10+
</PropertyGroup>
11+
12+
<ItemGroup>
13+
<Reference Include="Microsoft.AspNetCore.Localization" />
14+
<Reference Include="Microsoft.AspNetCore.Routing.Abstractions" />
15+
</ItemGroup>
16+
17+
</Project>
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Threading.Tasks;
6+
using Microsoft.AspNetCore.Http;
7+
using Microsoft.AspNetCore.Routing;
8+
using Microsoft.Extensions.Internal;
9+
10+
namespace Microsoft.AspNetCore.Localization.Routing
11+
{
12+
/// <summary>
13+
/// Determines the culture information for a request via values in the route data.
14+
/// </summary>
15+
public class RouteDataRequestCultureProvider : RequestCultureProvider
16+
{
17+
/// <summary>
18+
/// The key that contains the culture name.
19+
/// Defaults to "culture".
20+
/// </summary>
21+
public string RouteDataStringKey { get; set; } = "culture";
22+
23+
/// <summary>
24+
/// The key that contains the UI culture name. If not specified or no value is found,
25+
/// <see cref="RouteDataStringKey"/> will be used.
26+
/// Defaults to "ui-culture".
27+
/// </summary>
28+
public string UIRouteDataStringKey { get; set; } = "ui-culture";
29+
30+
/// <inheritdoc />
31+
public override Task<ProviderCultureResult> DetermineProviderCultureResult(HttpContext httpContext)
32+
{
33+
if (httpContext == null)
34+
{
35+
throw new ArgumentNullException(nameof(httpContext));
36+
}
37+
38+
string culture = null;
39+
string uiCulture = null;
40+
41+
if (!string.IsNullOrEmpty(RouteDataStringKey))
42+
{
43+
culture = httpContext.GetRouteValue(RouteDataStringKey)?.ToString();
44+
}
45+
46+
if (!string.IsNullOrEmpty(UIRouteDataStringKey))
47+
{
48+
uiCulture = httpContext.GetRouteValue(UIRouteDataStringKey)?.ToString();
49+
}
50+
51+
if (culture == null && uiCulture == null)
52+
{
53+
// No values specified for either so no match
54+
return NullProviderCultureResult;
55+
}
56+
57+
if (culture != null && uiCulture == null)
58+
{
59+
// Value for culture but not for UI culture so default to culture value for both
60+
uiCulture = culture;
61+
}
62+
63+
if (culture == null && uiCulture != null)
64+
{
65+
// Value for UI culture but not for culture so default to UI culture value for both
66+
culture = uiCulture;
67+
}
68+
69+
var providerResultCulture = new ProviderCultureResult(culture, uiCulture);
70+
71+
return Task.FromResult(providerResultCulture);
72+
}
73+
}
74+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
{
2+
"AssemblyIdentity": "Microsoft.AspNetCore.Localization.Routing, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60",
3+
"Types": [
4+
{
5+
"Name": "Microsoft.AspNetCore.Localization.Routing.RouteDataRequestCultureProvider",
6+
"Visibility": "Public",
7+
"Kind": "Class",
8+
"BaseType": "Microsoft.AspNetCore.Localization.RequestCultureProvider",
9+
"ImplementedInterfaces": [],
10+
"Members": [
11+
{
12+
"Kind": "Method",
13+
"Name": "DetermineProviderCultureResult",
14+
"Parameters": [
15+
{
16+
"Name": "httpContext",
17+
"Type": "Microsoft.AspNetCore.Http.HttpContext"
18+
}
19+
],
20+
"ReturnType": "System.Threading.Tasks.Task<Microsoft.AspNetCore.Localization.ProviderCultureResult>",
21+
"Virtual": true,
22+
"Override": true,
23+
"ImplementedInterface": "Microsoft.AspNetCore.Localization.IRequestCultureProvider",
24+
"Visibility": "Public",
25+
"GenericParameter": []
26+
},
27+
{
28+
"Kind": "Method",
29+
"Name": "get_RouteDataStringKey",
30+
"Parameters": [],
31+
"ReturnType": "System.String",
32+
"Visibility": "Public",
33+
"GenericParameter": []
34+
},
35+
{
36+
"Kind": "Method",
37+
"Name": "set_RouteDataStringKey",
38+
"Parameters": [
39+
{
40+
"Name": "value",
41+
"Type": "System.String"
42+
}
43+
],
44+
"ReturnType": "System.Void",
45+
"Visibility": "Public",
46+
"GenericParameter": []
47+
},
48+
{
49+
"Kind": "Method",
50+
"Name": "get_UIRouteDataStringKey",
51+
"Parameters": [],
52+
"ReturnType": "System.String",
53+
"Visibility": "Public",
54+
"GenericParameter": []
55+
},
56+
{
57+
"Kind": "Method",
58+
"Name": "set_UIRouteDataStringKey",
59+
"Parameters": [
60+
{
61+
"Name": "value",
62+
"Type": "System.String"
63+
}
64+
],
65+
"ReturnType": "System.Void",
66+
"Visibility": "Public",
67+
"GenericParameter": []
68+
},
69+
{
70+
"Kind": "Constructor",
71+
"Name": ".ctor",
72+
"Parameters": [],
73+
"Visibility": "Public",
74+
"GenericParameter": []
75+
}
76+
],
77+
"GenericParameters": []
78+
}
79+
]
80+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
5+
</PropertyGroup>
6+
7+
<ItemGroup>
8+
<Reference Include="Microsoft.AspNetCore.Localization.Routing" />
9+
<Reference Include="Microsoft.AspNetCore.Routing" />
10+
<Reference Include="Microsoft.AspNetCore.TestHost" />
11+
</ItemGroup>
12+
13+
</Project>
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System.Collections.Generic;
5+
using System.Globalization;
6+
using System.Net;
7+
using System.Threading.Tasks;
8+
using Microsoft.AspNetCore.Builder;
9+
using Microsoft.AspNetCore.Hosting;
10+
using Microsoft.AspNetCore.Http;
11+
using Microsoft.AspNetCore.Routing;
12+
using Microsoft.AspNetCore.TestHost;
13+
using Microsoft.Extensions.DependencyInjection;
14+
using Xunit;
15+
16+
namespace Microsoft.AspNetCore.Localization.Routing
17+
{
18+
public class RouteDataRequestCultureProviderTest
19+
{
20+
[Theory]
21+
[InlineData("{culture}/{ui-culture}/hello", "ar-SA/ar-YE/hello", "ar-SA", "ar-YE")]
22+
[InlineData("{CULTURE}/{UI-CULTURE}/hello", "ar-SA/ar-YE/hello", "ar-SA", "ar-YE")]
23+
[InlineData("{culture}/{ui-culture}/hello", "unsupported/unsupported/hello", "en-US", "en-US")]
24+
[InlineData("{culture}/hello", "ar-SA/hello", "ar-SA", "en-US")]
25+
[InlineData("hello", "hello", "en-US", "en-US")]
26+
[InlineData("{ui-culture}/hello", "ar-YE/hello", "en-US", "ar-YE")]
27+
public async Task GetCultureInfo_FromRouteData(
28+
string routeTemplate,
29+
string requestUrl,
30+
string expectedCulture,
31+
string expectedUICulture)
32+
{
33+
var builder = new WebHostBuilder()
34+
.Configure(app =>
35+
{
36+
app.UseRouter(routes =>
37+
{
38+
routes.MapMiddlewareRoute(routeTemplate, fork =>
39+
{
40+
var options = new RequestLocalizationOptions
41+
{
42+
DefaultRequestCulture = new RequestCulture("en-US"),
43+
SupportedCultures = new List<CultureInfo>
44+
{
45+
new CultureInfo("ar-SA")
46+
},
47+
SupportedUICultures = new List<CultureInfo>
48+
{
49+
new CultureInfo("ar-YE")
50+
}
51+
};
52+
options.RequestCultureProviders = new[]
53+
{
54+
new RouteDataRequestCultureProvider()
55+
{
56+
Options = options
57+
}
58+
};
59+
fork.UseRequestLocalization(options);
60+
61+
fork.Run(context =>
62+
{
63+
var requestCultureFeature = context.Features.Get<IRequestCultureFeature>();
64+
var requestCulture = requestCultureFeature.RequestCulture;
65+
return context.Response.WriteAsync(
66+
$"{requestCulture.Culture.Name},{requestCulture.UICulture.Name}");
67+
});
68+
});
69+
});
70+
})
71+
.ConfigureServices(services =>
72+
{
73+
services.AddRouting();
74+
});
75+
76+
using (var server = new TestServer(builder))
77+
{
78+
var client = server.CreateClient();
79+
var response = await client.GetAsync(requestUrl);
80+
81+
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
82+
var data = await response.Content.ReadAsStringAsync();
83+
Assert.Equal($"{expectedCulture},{expectedUICulture}", data);
84+
}
85+
}
86+
87+
[Fact]
88+
public async Task GetDefaultCultureInfo_IfCultureKeysAreMissing()
89+
{
90+
var builder = new WebHostBuilder()
91+
.Configure(app =>
92+
{
93+
var options = new RequestLocalizationOptions
94+
{
95+
DefaultRequestCulture = new RequestCulture("en-US")
96+
};
97+
options.RequestCultureProviders = new[]
98+
{
99+
new RouteDataRequestCultureProvider()
100+
{
101+
Options = options
102+
}
103+
};
104+
app.UseRequestLocalization(options);
105+
app.Run(context =>
106+
{
107+
var requestCultureFeature = context.Features.Get<IRequestCultureFeature>();
108+
var requestCulture = requestCultureFeature.RequestCulture;
109+
110+
return context.Response.WriteAsync(
111+
$"{requestCulture.Culture.Name},{requestCulture.UICulture.Name}");
112+
});
113+
});
114+
115+
using (var server = new TestServer(builder))
116+
{
117+
var client = server.CreateClient();
118+
var response = await client.GetAsync("/page");
119+
120+
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
121+
var data = await response.Content.ReadAsStringAsync();
122+
Assert.Equal("en-US,en-US", data);
123+
}
124+
}
125+
126+
[Theory]
127+
[InlineData("{c}/{uic}/hello", "ar-SA/ar-YE/hello", "ar-SA", "ar-YE")]
128+
[InlineData("{C}/{UIC}/hello", "ar-SA/ar-YE/hello", "ar-SA", "ar-YE")]
129+
[InlineData("{c}/hello", "ar-SA/hello", "ar-SA", "en-US")]
130+
[InlineData("hello", "hello", "en-US", "en-US")]
131+
[InlineData("{uic}/hello", "ar-YE/hello", "en-US", "ar-YE")]
132+
public async Task GetCultureInfo_FromRouteData_WithCustomKeys(
133+
string routeTemplate,
134+
string requestUrl,
135+
string expectedCulture,
136+
string expectedUICulture)
137+
{
138+
var builder = new WebHostBuilder()
139+
.Configure(app =>
140+
{
141+
app.UseRouter(routes =>
142+
{
143+
routes.MapMiddlewareRoute(routeTemplate, fork =>
144+
{
145+
var options = new RequestLocalizationOptions
146+
{
147+
DefaultRequestCulture = new RequestCulture("en-US"),
148+
SupportedCultures = new List<CultureInfo>
149+
{
150+
new CultureInfo("ar-SA")
151+
},
152+
SupportedUICultures = new List<CultureInfo>
153+
{
154+
new CultureInfo("ar-YE")
155+
}
156+
};
157+
options.RequestCultureProviders = new[]
158+
{
159+
new RouteDataRequestCultureProvider()
160+
{
161+
Options = options,
162+
RouteDataStringKey = "c",
163+
UIRouteDataStringKey = "uic"
164+
}
165+
};
166+
fork.UseRequestLocalization(options);
167+
168+
fork.Run(context =>
169+
{
170+
var requestCultureFeature = context.Features.Get<IRequestCultureFeature>();
171+
var requestCulture = requestCultureFeature.RequestCulture;
172+
173+
return context.Response.WriteAsync(
174+
$"{requestCulture.Culture.Name},{requestCulture.UICulture.Name}");
175+
});
176+
});
177+
});
178+
})
179+
.ConfigureServices(services =>
180+
{
181+
services.AddRouting();
182+
});
183+
184+
using (var server = new TestServer(builder))
185+
{
186+
var client = server.CreateClient();
187+
var response = await client.GetAsync(requestUrl);
188+
189+
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
190+
var data = await response.Content.ReadAsStringAsync();
191+
Assert.Equal($"{expectedCulture},{expectedUICulture}", data);
192+
}
193+
}
194+
}
195+
}

0 commit comments

Comments
 (0)