We have a huge project with more than 100 Custom Controls and XPages in place and now we intend to provide the project in multiple languages.
Localization in XPages, provides this beautiful and a very simple way to convert the entire project in the other language via the properties file. However, in our case, many custom controls are kind of carbon copies of others and many of the translations/keywords are the same, it becomes kind of redundant to change the same thing again and again.
So the question is, is there a simpler approach, where we can probably do a bulk of translation together? Something, where we can export the entire translation as one file and import it back?
Any suggestion/help in the right direction would really be appreciated.
Don't use XPage's build-in localization. It might work for a first translation but it's very difficult to maintain after (a lot of) changes in XPages.
Use a managed Java bean instead.
It manages own property files and implements Map interface.
You'd use the bean to get a string usually with EL.
Example:
Get name's label with str['name'] for following entry in property file
name=Name
Use java.text.MessageFormat for messages with data.
Create a method like getMessage(String name, Object arg1) in your bean.
Example:
Get the message for a missing view in JavaScript with
str.getMessage('message.view.not.found', viewName) for following entry in property file
message.view.not.found=Could not find view {0}
Another approach would be to use manually created property-files
Here an example for two languages:
First of all you have to create the following two files under Resources/Files
messages.properties
global.welcome = Willkommen {0} auf meiner Webseite
messages_en.properties
global.welcome = Welcome {0} on my website
Now you can reference your message properties on every place in your code
<xp:this.resources>
<xp:bundle src="/messages.properties" var="resMessage"></xp:bundle>
</xp:this.resources>
<xp:text escape="true" id="cfUser" themeId="Text.User">
<xp:this.value><![CDATA[${javascript:I18n.format(resMessage['global.welcome'], sessionScope.commonUserName)}]]></xp:this.value>
</xp:text>
Related
I Would like to know if the Genexus Extension SDK already implements something to store persistent data locally (KB Independant and per KB), something like PersistentDictionary from ManagedEsent
I know that genexus uses SQL Server to store KB Related information, is there an interface for me to extend that?
I want to save data per genexus instance (locally) and use that data to load my extension config, everytime the users executes Genexus.
We don't use PersistentDictionary. I would advice not to use it, as it's a Windows specific API, and we are trying make everything new cross platform, as part of our journey of making GeneXus BL run on other OS.
There are different options of persistence, depending on the specific details of your scenario.
If you want to store something like configuration settings for your extension, you can use the ConfigurationHelper class located in Artech.Common.Helpers. This class provides read access to the configurations defined in the GeneXus.exe.config file in the GeneXus installation folder, as well as read/write access to the Environment.config file located in %AppData%\GeneXus\GeneXus\<version>\Environment.config. Note this file depends on the current user, and is shared between different GeneXus instances of a same main version.
The ConfigurationHelper class provides operations to read and save settings of basic types string, int and bool.
const string MY_EXTENSION = "MyExtensionSettings";
const string SETTING1 = "Setting1";
const string SETTING1_DEFAULT_VALUE = "This is the default value";
const string SETTING2 = "Setting2";
const int SETTING2_DEFAULT_VALUE = 20;
string setting1Value = ConfigurationHelper.GetUserSetting(MY_EXTENSION, SETTING1, SETTING1_DEFAULT_VALUE);
int setting2Value = ConfigurationHelper.GetUserSetting(MY_EXTENSION, SETTING2, SETTING2_DEFAULT_VALUE);
// Do something and maybe change the setting values
ConfigurationHelper.SetUserSetting(MY_EXTENSION, SETTING1, setting1Value);
ConfigurationHelper.SetUserSetting(MY_EXTENSION, SETTING2, setting2Value);
If you want to store something in a file based on the current opened KB, there's no specific API that'll help you handle the persistence. You can use the properties Location and UserDirectory of the KnowledgeBase class to access the KB location or a directory for the current user under the KB location, but it's up to you the handling of the file. You'll have to decide on the file format (binary or text), file encoding in case of text files, and handle all read and write operations to that file.
We use the kb.UserDirectory path to store non-critical stuff, such as the set of objects that were opened the last time the KB was closed, or the filter values for different dialogs.
In case you'd like to store settings inside the KB, there are plenty of options.
You can add properties to existing objects, KB version or environment. Making it a property doesn't necessary mean you'll have to edit the value in the property grid, although it's usually the way to go.
You can define a new kind of entity. Entities are the basic elements that can be stored in a KB. The entity may be stored depending on the active version of the KB, or may be independent of the current version. Entities can have properties, whose serialization is handled by the property engine, and also can read and store a byte array whose format and content will be handled by you.
You can add a part to an existing object. For instance you may want to add a part to Procedure objects. In order to do this you'll have to extend KBObjectPart, define your part in a BL package, declare that the part composes objects of certain type, and provide an editor for your new part in a UI package. KBObjectPart extends Entity so the serialization of the part is similar as in the previous case. A caveat of this option is that you'll also have to handle how the part content is imported, exported, and compared.
You can add a new kind of object. Objects extend the KBObject class, which extends Entity. Objects are not obliged to have parts (for instance the Folder object doesn't have any). When choosing to provide a new kind of object you have to consider a couple of things, such as:
Do you want to be able to create new instances from the new object dialog?
Will it be shown in the folder view?
Can it be added into modules?
Can it have the same name as other objects of different types?
As a general guideline, if you choose to add a new property, add it to objects, versions, or environments, not parts. Adding properties to parts is not so good for discoverability. Also if you choose to add a new kind of object, even though it inherits from Entity which as mentioned earlier can read and store a byte array, it's preferred to don't use the byte array in KBObject and add a KBObjectPart to it instead. That way the KBObject remains as lightweight as possible, and loading the object definition from the DB remains fast, and the blob content is loaded only when truly needed.
There's no rule of thumb. Depending on the specifics of the scenario, one option may be more suited than others.
On one MVC 4 project, we have a lot of Pages.cshtml that receive a collection of Models (generally hundreds of rows), which we serialize as JSON via
#Html.Raw(Json.Encode(Model));
The problem is that on some of those pages we are receiving an exception (The length of the string exceeds the value set on the maxJsonLength).
We know what is the reason and how to fix it. The thing is that I would like to create a similar Json.Encode() method (using Json.Net), so we do not need to modify all the cshtml pages. If I create a Json.Encode() method, Razor complains about ambiguous reference between My.Namespace.Json and System.Web.Helpers.Json.
Again, we know how to solve this by adding an alias on the page:
#using Json = My.Alias.Json
What I'm trying to do is to transparently instruct Razor to choose this alias for all the cshtml pages that uses this Json.Encode. The reason is that I want this to be transparent, so later if somebody adds a new page, automatically he will start to use our custom JSON implementation.
I think on Views/Web.config you can add namespaces and some configurations for Razor, but I don't see how to explicitly set the aliases.
Interesting issue, but as far as I know that's not possible. See e.g. this: C#: Globally alias a generic class name?
As an alternative you could create a helper method Html.JsonEncode() and teach, drill or entice everyone to use that.
If you make it also do the Raw() call and return IHtmlString, then you can do #Html.JsonEncode(Model) as opposed to #Html.Raw(Html.JsonEncode(Model)) and before you know it everybody is a fan of your new method.
Since Rails is using Ruby (dynamic language), would it be possible to create a very flexible
'configuration' class that has properties that you use throughout the website, AND have the ability to add new class properties (in the db for web modification) and then just use it in the code.
Each property would be of a specific type like a string, integer, bool etc.
You can't do this in a strongly typed language, but it must be possible with Ruby!
So say my class is like:
globalConfig.is_active
globalConfig.admin_email
I guess to make this work, I would loop through all the properties in the db, create properties in the class and assign the value right?
I actually have a settings plugin on GitHub you can use:
http://github.com/bellmyer/settings
It makes this easier for you. Right now it's not rails3-ready, so let me know if you need that. I also need to put in the time to roll it into a gem, instead of a plugin.
If you end up using it, let me know and I'll get it up to date. Otherwise, you can look at the code to see how I did things, and use that to help build your own custom solution.
When I generate a view from the List template I notice that the names of the columns are not based on the DisplayName() annotation. I know how to edit the list.tt code template but I have no idea how to retrieve the DisplayName attributes from the class properties.
The common way to get the DisplayName attribute is via reflection. The issue your going to have is .tt templates and reflection don't play nice together. Reflection relies on code being loaded into the AppDomain. Since .tt files don't actually load code you can't reflect over them.
More information about this issue, and a possible solution here:
http://www.olegsych.com/2007/12/how-to-use-t4-to-generate-decorator-classes/
MVC and Visual Studio must use some type of code inspection to generate some of the generated so I'd look along that path, maybe some crazy regex, if your not into solving the reflection issue.
This is probably a very "dumb" question for whoever knows VirtualShellTools but I only started using it and couldn't find my answer in the demos' code. Please note that I'm also unfamiliar with virtualtreeview.
I use a VirtualExplorerTreeview to display a directory structure, linked with a VirtualExplorerListview to display a certain type of files in the selected directory as well as specific informations about them
I've been able to point them at the right place, link them as I wanted, filter everything in the listview, and looking at the demos I have a pretty good idea about how to add my own columns and draw it to display my custom data.
My issue lies with the Treeview: I would like to sort the directories displayed in the order I want; specifically, I want "My Docs" and other folder to appears first, then drives, then removable media. Looking in the TNamespace property I found how to distinguish them (Directory and Removable properties), but I don't know how to implement my own sort/what event I need. I tried CompareNode but that doesn't even seem to be called.
If you want to do everything yourself, then set toUserSort in the TVirtualExplorerTree.TreeOptions.VETMiscOptions property. That causes the control to just use the DoCompare method inherited from the virtual tree view, and that should call the OnCompareNodes event handler.
A better way is to provide a custom TShellSortHelper. Make a descendant of that class and override whichever methods you need. Create an instance of that class and assign it to the tree's SortHelper property. (The tree takes ownership of the helper; free the old one, but not the new one.) If the items are being sorted on a column that that class doesn't know how to compare, then handle the tree's OnCustomColumnCompare event.
To help you figure out exactly which methods you need to override or events you need to handle, set a breakpoint in TCustomVirtualExplorerTree.DoCompare and step through to see what gets called in various situations.