asp.net Get all Resources from .resx Without Specifying Culture (ResourceManager.GetResourceSet ) - asp.net

I first came across a question similar to mine here at stack overflow: Loop through all Resources in ResourceManager - C#. It only solved part of what I need to do. When you request an entry in a resource file for a specific culture, if there is not one present it will default back on the neutral culture resource file.
I need to loop through each entry for a given resource file and GetResourceSet requires a culture. For example I have a neutral resource file with 3 entries in it and a culture specific resource file accompanying the neutral file with 1 entry.
My neutral resource example file is MyResource.resx and my culture specific resource example file is MyResource.en-gb.resx. The following code shows how I am currently trying to loop through and access all of the resource entries.
Dim cultInfo as New CultureInfo(culture)
For Each entry As System.Collections.DictionaryEntry In myResourceManager.GetResourceSet(cultInfo, True, True)
Next
Neutral Resource File Entries
FullName / Full Name
PhoneNumber / Phone Number
State / State
Culture Specific Resource File Entry
State / County
When I call GetResourceSet for the specific culture I only get back 1 entry. I was expecting (and want) to get back all 3 entries with the one culture specific entry overridden. Here is what I want returned:
FullName / Full Name
PhoneNumber / Phone Number
State / County
Is there anyway that I can do this? Thanks.

The GetString method of a ResourceManager object properly handles the traversing of resource files to locate the correct Value for a given key based on a culture. The base/neutral/default resource file can be obtained using the CultureInfo.InvariantCulture, which gives you all the possible keys for the resource file (assuming you setup your resource files this way).
Looping on the DictionaryEntry objects found in the GetResourceSet method of a ResourceManager, based on the Invariant Culture and then calling GetString for each Key using the specific culture passed in, you will get the correct Value for a given key based on the culture.
For Each entry As DictionaryEntry In myResourceManager.GetResourceSet(CultureInfo.InvariantCulture, True, True)
Dim strKey as String = entry.Key.ToString()
Dim strValue as String = myResourceManager.GetString(entry.Key.ToString(), cultInfo)
Next
Hope this helps!

Related

Classic ASP - Website localization

I need to add Languages support to an existing classic asp website
The "best" solution I found is to encapsulate every text in a function,
create a database table where store, for every page, the translations and use a dictionary object to retrieve the right value.
example:
<div>Welcome to xy website</div>
<button class="btn green">Login</button>
becomes
<div><%=TL("Welcome to xy website")%></div>
<button class="btn" ><%=TL("Login")%></button>
then TL function should be like this
Function TL(strInput)
Dim strTargetLanguage, strPageURL,objDict,strTmp1,strTmp2
if strInput<>"" then
' First check if customer has set language.. else uses browser language
if request.cookies("culture")="" then
strTargetLanguage=lcase(left(request.servervariables("HTTP_ACCEPT_LANGUAGE"),2))
else
strTargetLanguage=lcase(left(request.cookies("culture"),2))
end if
' if User's Language is not supported....
if instr(strAcceptedLanguages,strTargetLanguage)= 0 then
strTargetlanguage="en"
end if
strPageURL=Request.ServerVariables("URL")
Set objDict=Server.CreateObject("Scripting.Dictionary")
objDict.Add "strPageUrl",strPageUrl
'Stored Procedure to load translation in the required language and for the target Page
cmd.CommandText="spDictionaryRead"
cmd.CommandType=4
cmd.Parameters("#LanguageID")=strTargetLanguage
cmd.Parameters("#PageUrl")=strPageURL
set rst=cmd.Execute()
if not rst.eof then
while not rst.eof
objDict.Add rst("txt"),rst(strTargetLanguage)
rst.movenext()
wend
end if
rst.close
if objDict.Exists(strInput)=true then
TL=objDict.Item(strInput)
else
' Custom Function to translate using google
TL=Translate(strInput,"en",strTargetLanguage)
TL=Replace(TL,"'","''")
strInput=replace(strInput,"'","''")
'Add new Sentence to Dictionary
cmd.CommandText="spDictionaryWrite"
cmd.CommandType=4
cmd.Parameters("#PageUrl")=strPageURL
cmd.Parameters("#TXT")=strInput
cmd.Parameters("#TargetLanguage")= strTargetLanguage
cmd.Parameters("#TargetText")=TL
cmd.Execute()
set objDict=nothing
end if
else
TL=""
end if
End Function
The function is not ready since at present every time it is called it access the DB and load all the translations of the page and create the Dictionary:
in this situation would be better to avoid the Dictionary and directly Query the DB for the sentence required.
I need "ONLY" to find a wise way to store the dictionary "somewhere" so to avoid to rebuild it
But which to choose? Application, Session, objVariable into the page, ???
googling a little I realize that Application is not a wise solution for many reasons,
Session: I try to keep session very slim: I would never save an object with some 30-50 Keys if I can avoid.... unless I remove it at the end of the page itself (if it worth)?
Someone suggest to load translations into Application as "plain array" and then build Dictionary every time it is required, but while loading the sentences into the Dictionary I can test if current sentence is target sentence and extract the translation without using Dictionary..
therefore neither this is a wise solution
I read also about
Lookup Component from Microsoft
but couldn't find any docs
perhaps can use some .NET components, like HashTable?
Since I imagine that translations are a common issue, I expect there has to be a better solution, and that my approach is wrong:
Can pls suggest a better approach or some hints?
I use Application for caching of some objects in Classic ASP, usually as an array with the values retrieved from the database with GetRows().
Session isn't suitable as it is only available to one user, not all users like Application.
For your situation where you probably want to cache a LOT of data I have a suggestion.
When you retrieve your values from the database you could create an ASP script with the File System Object which contains the VBScript code to create your dictionary and populate with values. Then you could include this generated ASP page in all of your files.
For example, to build your cache file...
<%
datestamp = Year(Now()) & Month(Now()) & Day(Now()) & Hour(Now()) & Minute(Now()) & Second(Now())
set fs=Server.CreateObject("Scripting.FileSystemObject")
set tfile=fs.CreateTextFile(Server.MapPath("\cache\language_" & datestamp))
tfile.WriteLine("<%")
tfile.WriteLine("Set objDict=Server.CreateObject(""Scripting.Dictionary"")")
'...your database code here....
while not rst.eof
tfile.WriteLine("objDict.Add " & rst("txt") & ",rst(strTargetLanguage)")
rst.movenext()
wend
'...etc etc...
tfile.WriteLine("%>")
tfile.close
set tfile=nothing
set fs=nothing
Application("languagecache") = datestamp
%>
NB. The datestamp in the filename is there so that there is no issue when the cache is being built.
Then, in your ASP pages you could include the latest cache file using Server.Execute...
Server.Execute("\cache\language_" & Application("languagecache"))
This is all just an example. You should also add code to ensure that if a page is accessed before the first time the cache file is built that it gets content from an include file that is always going to be there. You would also add in some code to check when the last time the cache file was generated and generate a new one after a set time. You might do this is a scheduled task so that some poor user doesn't have to wait while the cache file is built (or just start it asynchronously).
A while ago I inherited a project from another developer and to date in Classic ASP I haven't found a more efficient way of handling localisation.
The basic premise is
Store key-value pairs in a database, the structure we used is a keys table containing the keys and a "section" (which denotes a particular grouping of keys). We then have our values table which contains language specific localisations associated to the key by a FK (1:M) relationship.
╔══════════════════════════════════════════════════════════════════╗
║ Keys Table ║
╠══════════════╦══════════════╦═════════════════╦══════════════════╣
║ id (int, PK) ║ key (string) ║ section (string)║ editpanel (bit) ║
╚══════════════╩══════════════╩═════════════════╩══════════════════╝
╔═════════════════════════════════════════════════════════════════════╗
║ Values Table ║
╠══════════════╦═════════════════╦═════════════════╦══════════════════╣
║ id (int, PK) ║ key_id (int, FK)║ lang (string) ║ value (string) ║
╚══════════════╩═════════════════╩═════════════════╩══════════════════╝
Build a ASP application to create the XML from the key-value pairs in the database. The application is basically two pages
Iterates through the supported languages and sections (defined in the keys table) in a nested loop, this is how we decide on logical separation of cache files. Within each iteration we pass the work to a another page via a WinHttpRequest object. The page returns an XML structure that is has built by looking in the database and pulling out all the key-value pairs that are related to the specific section being iterated.
As already a mentioned a page specifically built to be called by the WinHttpRequest object that after querying the database for the specific section key-value pairs returns them in a bespoke XML structure.
Store a file structure something like
\packs\ ┐
├ \de\
├ \es\
├ \fr\
... etc
that includes a sub directory for each language supported in the database. The directories need to be accessible by Web Application Identity (whether that's a IUSR or a pre-configured account) with at least Modify permission to allow the creation and modification of the cached XML files.
The bespoke XML files look something like this
<?xml version="1.0" encoding="utf-8" ?>
<language_pack>
<strings>
<string id="391" key="connect" editpanel="0" section="email">
<![CDATA[Connect]]>
</string>
<string id="9" key="uploadimage" editpanel="0" section="common">
<![CDATA[Upload Photo]]>
</string>
<string id="12" key="notes" editpanel="0" section="common">
<![CDATA[Notes]]>
</string>
</strings>
<error_messages>
<error id="1" key="pleasebepatient">
<![CDATA[\nThis action may take a little time!\nPlease be patient.\n]]>
</error>
</error_messages>
<info>
<langcode>gb</langcode>
<langname>English</langname>
<strings>194</strings>
<time>0</time>
</info>
</language_pack>
XML file truncated to keep things simple, actual files contain a lot more values
The main localisation is then powered by an #include file that get's added to each page that needs to support localisation (it just becomes part of the build process when working in localised web applications).
The #include we call locale.asp does something similar to what you describe in the question. It made up of various functions of which the main ones include;
init_langpack(langcode, admin, section) - Handles any initialisation of the XML cache file. The langcode is just the string representation of the language you want to load (collates to the directory names, de, es etc). admin determines whether we are in "Admin Mode" or not and behaves slightly differently if we are, setting page headers etc. and section is the XML file (which we separate into sections) we want to load into an XMLDOM object. It should always be called before attempting to access ml_string() as it loads the XML.
ml_string(id, showcontrol) - Used the ASP pages where we want to output a localisation the id is a key from the XML / Keys table (why both?, will explain in a bit), while the showcontrol Boolean is used to decide when the page is displayed in "Admin Mode" so we can show the localisation in an editable field or not. You might not always want to do this due to how localisations are placed in the page (allowing you to handle them differently, display a panel underneath etc).

How to read the Resource File using C#?

I have a web application which should be Localized to 3 languages. All the controls are taking the control text from the Resx file of that language. Now I have scenario like suppose if we have a messages,custom error messages to show for that particular culture. So for this I have created a seperate Foldere as "Resources" and created a resx as "DialogMessages.ar-IQ.resx".
How can I read the "DialogMessages.ar-IQ.resx" in C# ?
I have tried to read the file using ResxResourceReader class. Is this a correct process or any flaw exists ?
You can use ResXResourceReader and specifying the resource file location properly .
ResXResourceReader reader = new ResXResourceReader("Map path with resource file");
IDictionaryEnumerator iterator = reader.GetEnumerator();
while (iterator.MoveNext())
{
// process the collection of key value pair.
}

Isn't HTTP verb PUT used for updating and not creating content?

In CouchDB, to create a new document you send:
PUT /albums/70b50bfa0a4b3aed1f8aff9e92dc16a0
Isn't PUT used to update data and not creating it?
It's used for both. Quoth the RFC:
The PUT method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server. If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI.
The key term for PUT for me is always idempotent. While for POST you are always "adding another" item to the systems-state, with PUT the action is the same even if multiple times performed (because you are adressing an item).
Example:
doing 100-times POST /albums = you would end up with 100 different albums (but with same content)
doing 100-times PUT /albums/123 = you would end up with one single album with id 123 (with the content)
PUT is to create a new or replace entirely an existing resource when you know the existing URI or what the new URI will be. POST is for updating parts of an existing resource, or for creating a new resource when the server has to assign the new URI. It's that simple. Both PUT and POST are used for creates and updates, it's not about if you're creating or updating, it's about whether you already know the URI or you need the server to assign it for you.
PUT is indeed used for updating content, but if you already have an id, you're updating. In REST generally, you'd POST to /albums (or whatever) to create a new record with an as-yet unknown id. Since you have an id, you're updating the empty document with that id to the content you're providing.

Multiple file upload association in MVC

I have implemented the File Upload (upon reading Scott Hanselman's excellent post)
I have multiple files associated with various questions on the form though, and would like to associate each saved file with an internal ID.
How can I do this? For example, if question # 3 has a file uploaded abc.pdf, how can I associated that file with ID #3?
Any good ideas, or has someone done this before?
I would have an array or vector in one of files with a getter and setter. This way when question #3 has file abc.pdf uploaded you can send the information you want to save to the setter and save it at index 3. When you want to access it use the getter for index 3.
Depending what you want to save you create an array that holds what you want. I haven't used Asp.net but this site tells you how to sort an array, which we don't want, but it also shows how to make an array of structures. So if you want to save the name of the file only then you only need a string array. But if you need to save the name and something else then create the array of structures.
Private Structure FileInfo
Public Name As String
Public OtherInfo As String
End Structure
Then create the array with :
Dim FileInfoArray(NumOfPotentialUploadedFiles - 1) As FileInfo
Since it sounds like each of your input fields upload one file each you would just need to remember the id number of the fields and then you would easily "know which IDs the uploaded files were associated with" as if field 1 has an uploaded file then it would be in the array at the same position. You could create a boolean within the structure that is set to false when you first create the array. Then when you upload a file of index 1 you change the boolean to true. This way you easily know which files you have when you go through the array b/c only the positions with a true value have a file.
Ok, figured out an easy solution. I was struggling since the Request.Files[x] object did not have any reference to the fields, but the Request.Files (HttpFileCollectionWrapper) has an AllKeys property that holds the array of fields. My code now is:
for (int fileIndex = 0; fileIndex < Request.Files.Count; fileIndex++)
{
string fieldName = Request.Files.AllKeys[fileIndex]; <--- Here is where you can gleam an key to persist to the database, I have an ID in the fieldName
string savedFileName = Path.GetFileName(Request.Files[fileIndex].FileName);
var path = Path.Combine(<your server save path>, savedFileName);
Request.Files[fileIndex].SaveAs(path);
}
Easy enough!

asp.net multiligual website culture settings

In asp.net multilingual website in english Uk and swedish, i have three rsources file
1. en-GB.resx
2. sv-SE.resx
3. Culture neutral file.
I have create one base class and all pages is inherited from that class. There i write following lines to set UICULTURE and culture
1. Thread.CurrentThread.CurrentUICulture = CultureInfo.CurrentUICulture.Name;
2. Thread.CurrentThread.CurrentCulture = CultureInfo.CurrentCulture.Name;
Question: Suppose my browser language is Swedish(sv-SE) then this code will run because it find CurrentUICulture and CurrentCulture values as sv-SE.
Now if suppose browser language is Swedish(sv) only, in that case values will be set as
CurrentUICulture = sv; and CurrentCulture = sv-SE
Now the problem is that user can able to view all text in Culture neutral resource file that i kept as english while all decimal saperators, currency and other will be appear in swedish.
It looks confusing to usr.
What would be right approach. I am thinking following solution. Please correct me?
1. i can create extra resource file for sv also.
2. I check value of CurrentUICulture in base class and if it is sv then replace it with sv-SE
Please correct me which one is right approach or Is there any other good way of doing?
You can easily replace the value in the base class like you mentioned. I would stay away from creating an additional resource file that duplicates data since it will be harder to maintain.
if (Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName.ToLower() != "sv")
...replace with sv-SE
EDIT See this other question]1 for additional info. There's a good article referenced in the answer of that question

Resources