We are using Tapestry 5.4-beta-4. My problem is:
I need to keep files with locale data in an external location and under different file name then tapestry usual app.properties or pageName_locale.properties. Those files pool messages that should be then used on all pages as required (so no tapestry usual one_page-one_message_file). The files are retrieved and loaded into tapestry during application startup. Currently i am doing it like this:
#Contribute(ComponentMessagesSource.class)
public void contributeComponentMessagesSource(OrderedConfiguration<Resource> configuration, List<String> localeFiles, List<String> languages) {
for(String language: languages){
for(String fileName : localeFiles){
String localeFileName = fileName + "_" + language + ".properties";
Resource resource = new Resource(localeFileName );
configuration.add(localeFileName, resource, "before:AppCatalog");
}
}
}
The above code works in that the message object injected into pages is populated with all the messages. Unfortunatly these are only the messages that are in the default ( first on the tapestry.supported-locales list) locale. This never changes.
We want the locale to be set to the browser locale, send to the service in the header. This works for those messages passed to tapestry in the traditional way (through app.properties) but not for those set in the above code. Actually, if the browser language changes, the Messages object changes too but only those keys that were in the app.properties are assigned new values. Keys that were from external files always have the default values.
My guess is that tapestry doesn't know which keys from Messages object it should refresh (the keys from external files ale not beeing linked to any page).
Is there some way that this could be solved with us keeping the current file structure?
I think the problem is that you add the language (locale) to the file name that you contribute to ComponentMessagesSource.
For example if you contribute
example_de.properties
Tapestry tries to load
example_de_<locale>.properties
If that file does not exist, it will fall back to the original file (i.e. example_de.properties).
Instead you should contribute
example.properties
and Tapestry will add the language to the file name automatically (see MessagesSourceImpl.findBundleProperties() for actual implementation).
#Contribute(ComponentMessagesSource.class)
public void contributeComponentMessagesSource(OrderedConfiguration<Resource> configuration, List<String> localeFiles, List<String> languages) {
for(String language: languages){
for(String fileName : localeFiles){
String localeFileName = fileName + ".properties";
Resource resource = new Resource(localeFileName );
configuration.add(localeFileName, resource, "before:AppCatalog");
}
}
}
Related
I want to add key value pair to GlobalResources.en-US.resx file. This is the file used by localization in my Blazor Server app.
public void Add()
{
if(!string.IsNullOrEmpty(Key) && !string.IsNullOrEmpty(Value))
{
IResourceWriter resWriter = new ResourceWriter(
#".\my_path\Resources\GlobalResources.en-US.resx");
resWriter.AddResource(Key, Value);
resWriter.Close();
}
}
I have replaced the correct path with placeholder my_path to make it shorter here.
But after executing this method, I can't view GlobalResources.en-US.resxfile in Visual studio.It says ResX file Data at the root level is invalid. Line 1, position 1. cannot be parsed. So I can't get to know whether that key, value is correctly added or not. Also localization service do not pick that value.
How can I programmatically add Language translation key value pair to this resource file in Blazor server?
I'm trying to localize my Blazor app (.NET 5.0) using IStringLocalizer and user UI selection based on cookies. Following the documentation, it seems to work if I create a .resx file for each page in my Resources/Pages folder.
I'd like to group all key-value pairs within a single file as follows :
MyApp
|-- Resources
| MyResources.resx <--- set to public modifiers to generate class!!!
| MyResources.es.resx
| MyResources.fr.resx
...
|-- Resources
| Index.razor
So in Startup.cs I register the Resources folder:
services.AddControllers();
services.AddLocalization(options => options.ResourcesPath = "Resources");
I also added MapControllers in the configure method, as well as registering supported cultures. I also added the controller:
[Route("[controller]/[action]")]
public class CultureController : Controller {
public IActionResult SetCulture(string culture, string redirectUri) {
if (culture != null) {
HttpContext.Response.Cookies.Append(
CookieRequestCultureProvider.DefaultCookieName,
CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture)));
}
return LocalRedirect(redirectUri);
}
}
Now, on my main "index.razor" page I inject the IStringLocalizer<MyResources> as follows:
#page "/index"
#inject IStringLocalizer<Language> _localizer
<h2>#_localizer["hello_world"]</h2>
<h2>#DateTime.Now.ToShortDateString()</h2>
<h2>#DateTime.Now.ToLongDateString()</h2>
I make sure that all resx files actually contain the "hello_world" key with some values and added a language selector.
When I run the app, changing the language does change the displayed dates, however, the hello_world key is not found so the app displays the key instead. When exploring the _localizer variable, I see that the container is empty - none of the key-value pairs of the .resx are actually loaded.
Is it possible to get this to work? If so, what am I doing wrong here?
The mistake is because of naming here:
#inject IStringLocalizer<Language> _localizer
should be
#inject IStringLocalizer<MyResources> _localizer
And important is to add an empty file MyResources.razor at the root of the project.
Edit:
Another mistake I made is to add the myApp.Resources to _Imports.razor
...
#using myApp.Resources <==== do NOT add this
#neggenbe
Adding an empty razor(AppLang.razor) component at the root of my project with same name with my resource(AppLang.resx) file solved the challenge. I stumbled accross #neggenbe after over 48 hours trying to figure out what was wrong with my code.
If your project is purely blazor, you wont come accross this challenge but if its a mix of MVC and razor(blazor) components, then you'll definately need to do this.
Tapestry provides a great localization support for their pages and components.
I would like to send localized e-mail corresponding to the users language preference.
Anybody has come across a solution for localizing e-mail templates that integrates well with Tapestry?
(I don't mind if it is a not using Tapestry's templating engine)
I have created a separate directory/package for each language and retrieve the template file. And then used ThreadLocale..getLocale().getLanguage() to retrieve the current user's so I can send the e-mail on that language too.
Here is the relevant code snippet:
public class MailSender {
private static final String EMAIL_TEMPLATE_ROOT = "com/xxx/emailtemplate/";
#Inject
private ThreadLocale locale;
public void sendEmail(..., final String emailTemplateFileName) {
String emailTemplateFilePath = getEmailTemplateFilePath(emailTemplateFileName);
....
}
private String getEmailTemplateFilePath(String templateLocation) {
String language = locale.getLocale().getLanguage();
return EMAIL_TEMPLATE_ROOT + language + "/" + templateLocation;
}
}
This is not specific to a templating engine. You can use this technique with your favourite. (I kept Velocity as it was used by the project already)
public string AsyncUpload()
{
return _fileStore.SaveUploadedFile(Request.Files[0]);
}
in the SaveUpload
private string _uploadsOriginalFolder = HostingEnvironment.MapPath("~/Content/Images/OriginalImages");
private string _uploadsThumbnailFolder = HostingEnvironment.MapPath("~/Content/Images/Thumbnails");
var identifier = Guid.NewGuid();
var originalFileNameToSave = identifier.ToString() +".jpg" ;
fileBase.SaveAs( Path.Combine(_uploadsOriginalFolder, originalFileNameToSave));
imagingService.ResizeImage(Path.Combine(_uploadsOriginalFolder, originalFileNameToSave),
Path.Combine(_uploadsThumbnailFolder,originalFileNameToSave),
160,
120,
false);
return originalFileNameToSave;
I want to have Domain.Com/Thumbnails/guid.jpg and Domain/OriginalImages/guid.jpg
i want this to be stored on to a \dfs.domain.com\Scanning[project]\Images directory.
how do i save it to that location and what rights do i need to provide on it.
has any body done this before. i guess many?
You can store the image wherever you want on your server as long as you provide write permissions to that folder. Obviously if you want to access it from another ASP.NET application you can no longer use relative paths. So if the dfs.domain.com site root is physically mapped toc:\foobar you could save the file as absolute path at c:\foobar\Scanning[project]\Images folder. The MVC application that is saving the file has no way of knowing how is this other site configured so you could externalize the other site root path at your web.config.
The best is to map phisical path to virtual folder in iis, and then allow write permisson on it. This is how I done when I have multiple web apps, which are using same folder for static content.
I have my target language in Session["lang"], which is either "en" or "it". I have added this to the Site.master:
<script runat="server">
void Page_Load(object sender, EventArgs e) {
string lang = Session["lang"].ToString();
System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.CreateSpecificCulture(lang);
System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.CreateSpecificCulture(lang);
}
</script>
Then I'd like to invoke a resource string like this:
<asp:Label ID="Label1" runat="server" Text="<%$ Resources:Global, test %>"></asp:Label>
I have two files in the App_GlobalResources, named Global.resx and Global.en.resx.
The problems is that no matter what is in the lang variable, I always get the results from the main Global.resx, and I never get the english version from Global.en.resx
I am doing this wrong entirely??
I tried putting the System.Threading... part in the Application_PreRequestHandlerExecute method in Global.asax.cs but the result was the same.
Thanks
PS: I am asking about a way to make this work in a simple way. If I was to use the complicate way, I'd go with this: http://helios.ca/2009/05/27/aspnet-mvc-and-localization/
i had the same dilema(how to implement localization) in my asp.net mvc app.
I followed the instructions posted here and it works like a charm.
So i created a folder named Localization under Content and then i create Resources resx files for each language i want to translate. Keep in mind that there is a convention for the resx file names. ie
Resources.resx is the default fall back for everything.
Resources.en-GB.resx is for english GB
Resources.en-US.resx is for english US
etc.
Just make sure you follow the instructions posted in the link to embed and make the Resources available in all places in your app (views, controllers etc)
Edit:
I want to add that i ommited this line from web.config since i wanted to manually set the local from my app.
<globalization uiCulture="auto" culture="auto"/>
Instead i have created the following class:
public class SmartController : Controller
{
public SmartController()
{
System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("en-US");
System.Threading.Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en-US");
}
}
All controllers inherit from this class.
Since this is an administrative set of the locale i have to set it from my apps settings. You could read it from Cookies and set it, or otherwise. This is imo the simplest solution for localization that i have encountered so far.
Once implemented you can refer to any string you add by the following simple line of code, no extra code needed.
<%= Resources.Strings.TranslatedTerm %>
I bet this one is a duplicate.
Anyway - all you need is here (assuming that you are using webforms viewengine (might work with others too, haven't investigated)).
Oh well... here goes my 'summary':
Helpers are just a part. You need to do some modifications with your default view engine too . On createview/createpartialview it should return localizationwebformview which adds a path key to viewdata which is used by htmlhelper to find resourceexpressionsfields and pass them to localizationhelpers class which retrieves desired value.
Little bonus=>
This might be handy if you don't want to recreate resource folders for view subfolders
(in case you modify viewengine.view/partialviewlocationformats):
private static string ReformatVirtualPath(string virtualPath)
{
//This allows NOT to duplicate App_localResources directory
// ~/Views/Shared/Partial/Some/BulltihS/_View.ascx
// turns into =>
// ~/Views/Shared/_View.ascx
var start = #"(~(/?\w*/?){2})";
var end = #"(\w*.as(c|p)x)";
start = Regex.Match(virtualPath, start).Value;
end = Regex.Match(virtualPath, end).Value;
return start + end;
}
usage:
internal static ResourceExpressionFields GetResourceFields
(string expression, string virtualPath)
{
virtualPath = ReformatVirtualPath(virtualPath);
var context = new ExpressionBuilderContext(virtualPath);
var builder = new ResourceExpressionBuilder();
return (ResourceExpressionFields)
builder.ParseExpression(expression, typeof(string), context);
}
EDIT:
but it might be a good idea to avoid App_GlobalResources and App_LocalResources as K. Scott Allen suggests (check Konstantinos answer).