I'm making a UWP application with Template10, and I currently have this folder structure inside my project for localization:
Localization
|_en
| |_Resources.resw
|_it-IT
|_Resources.resw
The default app language is en, and my current culture is it-IT.
The resource files are correct and the behavior is consistent through the app, showing always the Italian version first.
I'm using the ResourceLoader class inside View's code behind just fine, but as long as I use it inside the ViewModel it always returns the English localization, even if it doesn't exist (= empty string, although it exists in Italian file).
I'm using it inside the VM is because I'm creating a new Character and giving it a default name ("No name"), as a placeholder for the user, binding the name inside the View.
This is the code I use:
public class CharacterManagerViewModel : ViewModelBase
{
public Character Character { get; set; }
private ResourceLoader loader = new ResourceLoader();
public override async void OnNavigatedTo(object parameter, NavigationMode mode, IDictionary<string, object> state)
{
if (parameter == null)
{
//Creating a default empty character
Character = new Character();
Character.Name = loader.GetString("NewCharacterName");
}
else
{
//Loading from the Model
}
}
}
As I call the GetString() method, the current culture is it-IT but somehow the framework ignores it.
What am I doing wrong?
I found the origin of my problem:
inside the Model, in a method I cut in the code for simplicity, I load some data for the "Character" object from XML files included in the app.
Inside that method I change the culture to InvariantCulture to ensure the format is independent from the user's one.
At the end of that method, I change it back to user's culture but somewhat something goes wrong.
The issue happens after I make the double swap, and it is reflected afterwards to the entire software, even for localized strings from the View.
The issue is not dependent to the ViewModel, I'll work on the issue and if I cannot find anything I'll post a new question.
Related
#Self
Resource resource;
#Inject
#Named(value = "key")
private String key;
#PostConstruct
protected void init() throws PersistenceException {
ResourceResolver rr = resource.getResourceResolver();
ModifiableValueMap map = resource.adaptTo(ModifiableValueMap.class);
String value = fetchValue();
map.put("key", value);
rr.commit();
}
I'm trying to give the property "Key" the default value "value". Unfortunately it has to be returned by a function and can't be simply set to a primitive type with #Default(..). The above code seems to do nothing, the value of the component is not updated. I'm pretty new to the whole AEM stack so I feel like I might be going at this the wrong way.
Please remove revolver.commit() line . Unless you’ve obtained the resolver using the system user , you don’t have to commit it . You can simple use Session session=resolver.adaptTo(Session.class) and then say session.save ().
Did you try setting the value of the field key to the value you were trying to fetch instead of modifying the JCR property value directly?
#Inject
private String key;
#PostConstruct
protected void init() {
if (StringUtils.isBlank(key)) {
key = fetchValue();
}
}
public String getKey() {
return key;
}
On a different note, the #Self injector is discouraged due to reasons mentioned here.
Apparently I was using the #PostConstruct wrong: I thought it would trigger upon creation of the node, but the trigger is instead related to the rendering of the component.
To set values in a sling model when rendering a component is an anti-pattern. The repository shouldn't be modified when the page is being rendered. If you need to have default values when component node is created use cq:template feature of AEM. This copies over properties defined in the template when a new component instance is created. [template != page template in this context]
Its usage has been answered in this question AEM DefaultValue written to JCR
Additional reference :
http://blogs.adobe.com/experiencedelivers/experience-management/defaults-in-your-component/
According to the documentation:
The runtime doesn’t look up localized strings for non-validation attributes. In the code above, “Email” (from [Display(Name = "Email")]) will not be localized.
I'm looking for a way to localize text in DisplayAttribute. Any suggestions to do it in a proper way(s)?
You can set the ResourceType on the DisplayAttribute which can be used to localize your text.
Add a resource .resx file to your project e.g. MyResources.resx, and add a resource for your field:
Then reference the name of the field and the MyResources type in your DisplayAttribute
[Display(Name = "RememberMe", ResourceType = typeof(MyResources))]
public bool RememberMe { get; set; }
The localized resource will be pulled through automatically (see the text box)
Having a Central location for all your localization whether in view or dataannotations is the best approach I can think of, and this how I got to work.
In Startup.cs file after you installed nuget packages for localization add the following code
services.AddMvc().AddViewLocalization().AddDataAnnotationsLocalization(options =>
options.DataAnnotationLocalizerProvider = (type, factory) => new StringLocalizer<Resources>(factory));
services.Configure<RequestLocalizationOptions>(options => {
var cultures = new[]
{
new CultureInfo("en"),
new CultureInfo("ar")
};
options.DefaultRequestCulture = new RequestCulture("en", "en");
options.SupportedCultures = cultures;
options.SupportedUICultures = cultures;
});
This way the DataAnnotationLocalizerProvider will be from the Resources.{culture}.rex -( The Resource file must have an access modifier of No code gen)- assuming that no resources will be needed for the default language, and to be able to access the resource file since no code will be generated and empty class with the same name must be created.
and in _ViewImports.cshtml file inject the following
#inject IHtmlLocalizer<Resources> Localizer
by doing this you now have a global variable Localizer to be used in any of the views for localization purposes.
you can find further information on Globalization and localization in ASP.NET Core
For those who struggle (#lucius, #vladislav) with error:
Cannot retrieve property 'Name' because localization failed. Type 'Xxxx.EmployeeResx' is not public or does not contain a public static string property with the name 'FirstName'.
It is caused by access modifier on .resx files which is by default set to Internal (in my case it was No code generation). Change it to public in Access Modifier dropdown in the resource file toolbar.
After that you should be able to see the properties from the resource type:
Also, consider not using special signs in field names as they are a basis for auto-generated C# property names. The field names are converted into C# friendly names and that is why you can end up with inconsistency between name of resource file field and name of auto-generated property. Best to avoid any hyphens - or dots . Underscores _ are fine. You can always look up how the auto-generated properties look like in resource_file_name.Designer.cs class under the related resource file.
Many thanks to Bala Murugan who wrote a good article concerning this topic on Code Digest.
Actually I found an simple solution for the followers. The display name in most of time is used in the label of an input field. So do this if you like:
<label asp-for="Email">#Localizer["Email"]</label>
of course, you can pass the property name by #Html.DisplayNameFor, but most of time, this one already works well.
I have just created a project which demonstrates localization including localization of Display attribute for class properties as well as enums.
The project can be found here https://github.com/feradz/ASPNetCoreLocalization/wiki
The Display attribute has to be localized using the approach prior to ASP.NET Core 1.0. Have a look at the DataAnnotations.resx file in the project.
The Name property of Display cannot contain empty spaces, and special characters.
[Display(Name = "NoSpacesAndSpecialChanractersHere", ResourceType = typeof(Resources.DataAnnotations))]
public string FirstName { get; set; }
ResourceType should be the fully qualified resource class name (i.e. including the name space).
Im trying to create sample data for WP project in Expression Blend.
It works fine with simple classes, but not with custom generics classes. It can define structure of datasource, display correct structure of my ViewModel, but cannot generate values ie SampleData.xaml is empty.
How can I solve this, press some generate button or is there any other easy way to create design time data without writing everything manually?
I used a bit modified generic class NotifyTaskCompletion from here http://msdn.microsoft.com/en-us/magazine/dn605875.aspx and it is a root of problem. Here is result of generated data schema
The easiest thing to do is probably to create "dummy" types that are used by the designer. They would have the same public properties as your real types, but using concrete types and without any real code. They'd also have a different name. Since data-binding uses duck typing, the fact that the design-time object is a different type than the runtime object doesn't matter. For example, say your real class is:
public class GenericObject<T>
{
public T Thing { get; set; }
/* Lots of other complex code here... */
}
then you might also add:
#if DEBUG
public class GenericObjectDesigner
{
public string Thing { get; set; }
/* No need for any complex code */
}
#endif
Now in Blend, create a data source from the GenericObjectDesigner type and set the Thing property to be some string (eg, Lorum ipsum). You can now drag and drop that onto your design surface.
And in your actual code, you use the non-Designer version of the class, eg:
public MainPage()
{
InitializeComponent();
this.DataContext = new GenericObject<int> { Thing = 42 };
}
This will work fine as long as VS is in Debug mode. In Release mode, the app will still compile and run correctly, but you will see errors about GenericObjectDesigner not existing in your XAML files (you can safely ignore them).
MVC 4 does present me some strange behaviour at the moment.
Imagine the following Code:
TestController.cs
public class TestController : Controller
{
public ActionResult Index(Function function, string action)
{
return View();
}
public class Function
{
public string Action { get; set; }
}
}
It seems, that when I call the URL directly through the browser (localhost:PORT/Test), the Action-Property gets automatically filled with "Index".
If the Action would be named "MySuperDuperActionWhichGetsInvokedQuiteOften", exactly this Methodname would be in the property.
Can somebody explain what MVC is doing here?
The Problem is, that I of course want to fill that stuff myself, for example through an AJAX-Query. But if MVC is filling in this property all by itself, this breaks some behaviour.
I could, of course, just rename my property and it would be working, but it would still be quite interesting what's going on.
EDIT
I would understand it that my second parameter, string action, get's filled with the method-name. But why on earth would MVC bind any property/parameter that is named the same to the request-value of it?
It is problem with default model binder. It "maps" request fields to properties in your class. There is an article of MSDN describing how does it works but to simply this situation the code will be like this:
Action = Request["action"] //where of course Request["action"] equals to name of your action
I have a base class ReportElement which has type property:
public abstract class ReportElement {
private ReportElementType type;
public ReportElementType getType() {
return type;
}
public void setType(ReportElementType type) {
this.type = type;
}
}
ReportElementType is just an enum with specified code and i18nKey properties for each element. I have a couple of subclasses of ReportElement, each of them introducing their own properties. One of them is Plot:
public class Plot extends ReportElement {
public Plot() {
setType(ReportElementType.PLOT);
}
private Collection<Parameter> parameters = new ArrayList<Parameter>();
public Collection<Parameter> getParameters() {
return parameters;
}
}
On some page I needed to display a collection of different ReportElement instances, so I just used struts2 select tag:
<s:select list="myElements" listKey="type.code" listValue="type.i18nKey" size="20"/>
This worked like a charm for every element except for Plot instaces. Instead of invoking getType().getCode() or getType().getI18nKey() plain toString() was invoked on every instance of Plot! After several hours of fun debugging I noticed that during tag evaluation Plot's getParameters() method is called! So it seems struts was trying to evaluate type.code and type.i18nKey using getParameters() method! Failing to do that it ignored the existence of the properties, that I have clearly specified for usage!
After renaming getParameters to a kind of odd name like getParamms the problem gone. Also the problem hasn't occured when using iterator tag together with property tag instead of select tag.
Does anyone have an idea WHY struts select tag uses parameters property of my bean, when I have clearly specified what property should be used? Is it some "cool" feature or a bug?
P.S. I use struts 2.2.3.1
The argument used in all the FreeMarker templates representing a tag's parameters is called parameters. By providing a parameters property that takes precedence, S2 was unable to get to the object on the stack containing the tag's parameters.
It's neither a cool feature nor a bug, it's just how the templates are implemented. Checking the template source may have saved the few hours of debugging.
Found corresponding issue in struts JIRA: https://issues.apache.org/jira/browse/WW-3268
2.3 is specified as fix version.