View Full Version : what is the best way to have multiple languages in ASP?
FabriceB
08-26-2010, 07:58 PM
Hi All!
Would you have any advice or links to pages about how to have multiple language asp pages?
Here is what i think should be known in order to help get a better answer.. please let me know if more details are needed.
- For now, only the support section of the website will be made bilingual. (the languages would be english and french)
- User language is preset in preferences and available to me in a session variable (session("language"))
- The page will mainly consist of dropdowns and textareas / input text box.
- There will be a minimum of text in the page. (i.e.: Please select system related to current issue )
My idea for now is to connect to the data base in order to pull out the text directly in the preferred language of the user. This would obviously mean that I would need to ensure not to reload the page for anything else... (thinking AJAX for anything else...)
That's all I can think of for now... Again, please let me know if you feel there could be more details to help me get started or to make it easier to refer me to any links!
Thanks in advance! :D
Fab
Old Pedant
08-26-2010, 08:04 PM
Well, probably the best answer is to *NOT* use ASP. Instead, learn to use ASP.NET, which has better support for this kind of stuff.
But, yes, if you must use ASP, then getting your text from a database is probably the best thing. The big question here is what your DB table(s) will look like for this.
Not sure why you think that " I would need to ensure not to reload the page for anything else"?? If you did reload the page, you'd still pay attention to the session variable and still get the right language.
If you meant that you would need your AJAX code to be aware of the session language, then yes, that's true. But that wouldn't be hard to do.
FabriceB
08-26-2010, 08:20 PM
Hi! Thanks for once again replying so quickly Old Pedant. We have not yet crossed over to ASP.NET here which limits me to staying with classic ASP.
The big question here is what your DB table(s) will look like for this.
As per my DB Tables, that is another thing I wasn't too sure on how to approach... I figured that having something similar to: field_id [tinyint], lan_id [bit], text_en [varchar], text_fr [varchar] and make the selection through the language_id (lan_id). This is of course for lack of having a better idea of approaching so if you have anything you could suggest please feel free to share with me!
Not sure why you think that " I would need to ensure not to reload the page for anything else"?? If you did reload the page, you'd still pay attention to the session variable and still get the right language.
this was more of the fact of having so many connections going in to grab the information from the database... Now that I think about it though, the group of users who will be using that page isn't that large anyways so I guess ultimately even if they do reload it's not the end of the world...
Hope this helps clarify!
Thanks again!
Fab
Old Pedant
08-26-2010, 08:45 PM
So I'd probably go with this for the DB table:
Table: Phrases
pageid int (or varchar)
phraseid varchar(32)
language char(2) ' EN or FR
phrase text
If a phrase is a common one used on several pages, then set the pageid to 0 (or null if you use varchar for pageid).
Using a varchar(32) for the phraseid will make your ASP code *MUCH* more readable.
And a char(2) takes up no more real DB space than BIT and makes the table much more readable.
So now your ASP pages look something like this:
<%
' at the top of the page (could be in an include file):
Set conn = Server.CreateObject("ADODB.Connection")
conn.Open "..."
SQL = "SELECT phraseid, phrase FROM phrases " _
& " WHERE pageid IS NULL OR pageid = '" & pageNameOfThisPage & "' " _
& " AND language = '" & Session("language") & "'"
Set RS = conn.Execute( SQL )
Set phrases = Server.CreateObject("Scripting.Dictionary")
Do Until RS.EOF
phrases.Add LCase(RS("phraseid")), RS("phrase")
RS.MoveNext
Loop
RS.Close
%>
And then, in the page, where ever you need a phrase, you do something like this:
<div class="greeting"><%=phrases("greeting")%></div>
...
<form>
<label><%=phrases("enter name")%><input name="name" /></label><br/>
<label><%=phrases("enter password")%><input name="password" /></label><br/>
...
<input type="submit" value="<%=phrases("submit form")%>" />
</form>
...
etc.
Old Pedant
08-26-2010, 08:47 PM
Notice that this design allows you to use natural names for your phrases and even to use the same name on multiple pages, either with the same phrase content (if the pageid is null) or with differing content (if the pageid values differ).
I think it's nearly as flexible as what ASP.NET allows. Not as efficient, but neither is it terrible.
Old Pedant
08-26-2010, 08:53 PM
Finally, you could *AUTOMATE* the generation of the DB table without too much difficulty.
You could create your pages all in English (or all in French) to begin with. And every place you have a phrase that needs to be translated, you put in just the phraseid and delimit it with some special characters. Maybe like this:
<div class="greeting">%greeting%</div>
...
<form>
<label>%enter name%<input name="name" /></label><br/>
<label>%enter password%<input name="password" /></label><br/>
...
<input type="submit" value="%submit form%" />
</form>
...
And then you run a simple script that read those pages, finds all the %xxxx% strings, creates the DB table (with blank text for the actual phrases, of course), and replaces the %xxx% with <%=phrases("xxx")%>.
Then all you have to do is go into the DB and fill in the text for the phrases. Presto.
FabriceB
08-26-2010, 08:58 PM
I will get started and let you know how it all turns out!
Thank you very much for your help!
Old Pedant
08-26-2010, 08:59 PM
And improvement on that:
<div class="greeting">%greeting%Hello, there!%</div>
So now your automation code reads that and grabs *both* the phraseid and the text for one of the languages. Creates the DB with the one language in place, making it easier to remember what text is needed when your translate it.
Naturally, you would need to provide for a way to input actual % signs. Easy: %% means a single % and is not treated as a delimiter.
Old Pedant
08-26-2010, 09:22 PM
You *COULD* even do:
<div class="greeting">%thanks%Thank you%Merci%</div>
And collect both languages into the DB at the same time. But I'm guessing that this would disturb the page appearance too much.
OH WAIT!
How about this:
<div class="greeting"><!--%thanks-->Thank you<!--Merci%--></div>
Or come up with your own variation.
(Doesn't quite work for button labels, but...)
FabriceB
08-30-2010, 06:53 PM
Hi!
Sorry for the delay! I started working on it this morning afterall! I'm having a bit of an issue right now and i'm not sure where i'm going wrong... Here is what I get as an error message:
ADODB.Field error '80020009'
Either BOF or EOF is True, or the current record has been deleted. Requested operation requires a current record.
/SMST/Default.asp, line 0
This is the query that is being executed:
SET NOCOUNT ON SELECT phraseid, phrase FROM Tbl_Form_SMST_Phrases WHERE pageid IS NULL OR pageid = 'default' AND language = 'FR'
I kept the architecture of the table as suggested until a few minutes ago.
it was like this:
Table: Phrases
pageid varchar(30)
phraseid varchar(32)
language char(2) ' EN or FR
phrase text
and since I noticed after googling the error msg that it might be related to the fact that "phrase" is text (http://classicasp.aspfaq.com/general/why-do-i-get-80020009-errors.html) I changed the type to varchar.
here is the code portion executed until it's failure point.
<!--#include file ="../common/asp/functions.asp"-->
<!--#INCLUDE VIRTUAL="common/classes/asp/ConnectionManager.asp"-->
<!--#INCLUDE VIRTUAL="common/classes/asp/HtmlTable.asp"-->
<%Session.Timeout = 60%>
<%
function IIf( expr, truepart, falsepart )
IIf = falsepart
If expr Then IIf = truepart
end function
dim conn, sp, pageNameOfThisPage, phrases,rsP
pageNameOfThisPage = "default"
set conn = new connectionManager
sp = "SET NOCOUNT ON SELECT phraseid, phrase FROM Tbl_Form_SMST_Phrases " _
& " WHERE pageid IS NULL OR pageid = '" & pageNameOfThisPage & "' " _
& " AND language = '" & Session("language") & "'"
conn.storedprocedure = sp
response.write(sp)
conn.connect
Set phrases = Server.CreateObject("Scripting.Dictionary")
if conn.hasErrorOccured then
response.write("<div class='error'>")
response.write(conn.messageString)
response.write("</div>")
else
set rsP = conn.recordset
Do Until rsP.EOF
phrases.Add LCase(rsP("phraseid")), rsP("phrase")
rsP.MoveNext
Loop
set rsP = nothing
end if
%>
<html>
<head>
<title>Outage Search Report</title>
<style type="text/css"> <!-- I removed this since there was no relation to the issue... --> </style>
<script language="javascript"> <!-- I removed this since there was no relation to the issue... --> </script>
</head>
<body>
<form id="form" name="form" action="default.asp" method="get" >
<div id="bannerDIV">
<p align="center"><img id="banner" name="banner" src="/common/images/Header_smst_small.jpg" width=600 height=50 /></p>
<ul class="left">
<li> <a href="##"><%=phrases("inline1")%></a></li>
<li> <a href="##">ITSC TICKETS</a></li>
<li> <a href="##">NOC NETWORK</a></li>
<li> <a href="##">OTHER ISSUES</a></li>
<li> <a href="##">REPORT ISSUES</a></li>
</ul>
</div>
This is the result of the query itself:
phraseid phrase
inline1 ACCEUIL
inline2 BILLETS ITSC
Please keep in mind that I am only trying to figure things out for now and this is not my final implementation of what you recommanded!
Thanks for your help!
Fab :)
Old Pedant
08-30-2010, 08:07 PM
Well, there is certainly no reason to use SET NOCOUNT ON and maybe every reason to get rid of it, so start by getting rid of it. You only need that when you execute a stored procedure that does a SELECT to put info into temp (SP-scope) variables and you don't want that SELECT to show up to your calling code. When you are doing a simple SELECT like this, trying to get the data, you don't need/want it.
I doubt that it is the culprit, but might as well start by getting rid of it.
I have no idea what you "connectionManager" is doing, but it's possible that it doesn't like using an ad hoc query when it's expecting a stored procedure invocation. Is there a reason you are using that instead of a simple ADODB.Connection??
The fact that your error message says the error is in "Line 0" leads me to believe that the problem is somewhere in that connectionManager code, but of course I can't know that.
FabriceB
08-30-2010, 08:44 PM
I was getting error msg 800a0e78 earlier and while troubleshooting i added the "SET NO COUNT ON". I removed it now. Thanks for the explanation.
As per expecting an sp, it is possible to use either an sp or just a query in this case. A team member came up with the connectionManager (which uses ADODB.Connection) and we've been using that here ever since. I strongly doubt it is the issue since the recordset is created... the issue seems to be occuring when using the EOF function!
(in order to help eliminate any remaining doubt about the connectionManager, here is the code for it:
<%
class ConnectionManager
private cls_db
private cls_sp
private cls_rs
private cls_CursorType
private cls_LockType
private cls_cmd
private cls_conn
private cls_CnErrors
private cls_CnErrorsCount
private cls_arrHeaders
private cls_msgString
private cls_hasErrorOccured
private cls_toString_Array
public property get Database
Database = cls_db
end property
public property let Database(dbName)
cls_db = dbName
end property
public property get StoredProcedure
StoredProcedure = cls_sp
end property
public property let StoredProcedure(spName)
cls_sp = spName
end property
public property let LockType(lt)
cls_LockType = lt
end property
public property let CursorType(ct)
cls_CursorType = ct
end property
public property get recordset
set recordset = cls_rs
end property
public property get nextrecordset
set cls_rs = cls_rs.nextRecordset
set nextrecordset = cls_rs
end property
public property get cloneRecordset
set cloneRecordset = CloneRs(cls_rs)
end property
public property get connection
set connection = me
end property
public property let setTimeOut(timeOut)
cls_conn.commandTimeout = timeOut
end property
public property get getRows
dim arr
if cls_rs.eof then
arr = null
else
arr = cls_rs.getRows()
end if
getRows = arr
end property
public property get getRowHeaders
dim i
dim maxCols
if cls_rs.eof then
cls_arrHeaders = null
else
maxCols = cls_rs.fields.count
redim cls_arrHeaders(maxCols)
For i = 0 to maxCols -1
cls_arrHeaders(i)=cls_rs(i).Name
Next
end if
getRowHeaders = cls_arrHeaders
end property
public property get connectionErrors
set connectionErrors = cls_CnErrors
end property
public property get connectionErrorCount
connectionErrorCount = cls_CnErrorsCount
end property
public property get hasErrorOccured
hasErrorOccured = cls_hasErrorOccured
end property
public property get messageString
messageString = cls_msgString
end property
public property get toString_Array
'Returns a string array in the form of
'[val1,val2],[val3,val4]...]
'with javascript, can convert it to array with eval(var)
dim res
dim cpt
cpt = 0
if not cls_rs is nothing then
res = res & "["
do while not cls_rs.eof
for each x in cls_rs.fields
if cpt mod 2 = 0 then
res = res & "["
res = res & x.value
res = res & ","
'p2
else
res = res & x.value
res = res & "]"
end if
cpt =cpt + 1
next
cpt = 0
res = res & ","
cls_rs.movenext
loop
res = mid(res,1,len(res)-1)
res = res & "]"
end if
cls_rs.movefirst
toString_Array = res
end property
sub connect
on error resume next
if cls_conn.state = 0 then
cls_conn.open session("udl")
end if
cls_conn.defaultdatabase = cls_db
'set cls_rs = cls_conn.execute(cls_sp)
if cls_rs.state = 1 then
cls_rs.close
end if
cls_rs.open cls_sp, cls_conn, cls_CursorType,cls_LockType
set cls_CnErrors = cls_conn.errors
cls_CnErrorsCount = cls_conn.errors.count
if cls_CnErrorsCount <> 0 then
cls_hasErrorOccured = true
cls_msgString = "<ul class='error'>"
for each objErr in cls_CnErrors
cls_msgString = cls_msgString & "<li class='error' >Error Number : " & objErr.Number & "</li>"
cls_msgString = cls_msgString & "<li class='error' >Error Description : " & objErr.Description & "</li>"
next
cls_msgString = cls_msgString & "</ul>"
' response.write(cls_msgString)
end if
end sub
private function CloneRs(rs)
dim oStream, tmpRs
set oStream = createobject("Adodb.Stream")
set tmpRs = createobject("Adodb.Recordset")
rs.Save oStream
' locktype unspecified or -1
tmpRs.Open oStream,,,3
set CloneRs = tmpRs
set tmpRs = nothing
end function
Private Sub Class_Initialize
cls_db = "Isol_Reporting"
cls_sp = "undefined"
set cls_rs = nothing
set cls_conn = nothing
set cls_CnErrors = nothing
cls_CnErrorsCount = -1
cls_msgString = "<p class='success'>Insertion / update was successfull</p>"
set cls_rs =server.createobject("ADODB.Recordset")
set cls_conn = Server.CreateObject("ADODB.Connection")
cls_hasErrorOccured = false
cls_toString_Array = "undefined"
cls_CursorType = 0
cls_LockType = 1
End Sub
Private Sub Class_Terminate
'on error resume next
'1 = open
if cls_conn.state = 1 then
cls_conn.close
end if
set cls_rs = nothing
set cls_conn = nothing
set cls_CnErrors = nothing
End Sub
end class
%>
)
any other suggestions?
thanks again with your help!
greatly appreciated!
Old Pedant
08-30-2010, 09:39 PM
Well, debug time, I guess.
else
set rsP = conn.recordset
If rsP.EOF Then Response.Write "NO RECORDS in rsP recordset<hr>"
Do Until rsP.EOF
pid = rsP("phraseid")
ph = rsP("phrase")
Response.Write pid & "::" & ph & "<br/>" & vbNewLine
phrases.Add LCase(pid), ph
rsP.MoveNext
Loop
set rsP = nothing
end if
What more does that DEBUG show you??
Old Pedant
08-30-2010, 09:40 PM
What you really need to do is figure out where that error is *really* coming from. It's clearly not really coming from Line 0.
FabriceB
08-30-2010, 09:46 PM
What you really need to do is figure out where that error is *really* coming from. It's clearly not really coming from Line 0.
Hi!
When going with the code you posted @ 4:39pm it works fine! so it could be the .Add which didn't like the fact of being given the rsP() values directly!
not sure what to say other then thank you for once again pointing me in the right direction! :)
Old Pedant
08-30-2010, 09:48 PM
Well, I certainly don't understand why that would be different, but hey! If it works, it works!
FabriceB
08-30-2010, 10:03 PM
If it works, it works!
I couldn't have said it better! lol
ketty20
09-01-2010, 08:26 AM
Best option is to use ASP.NET than Asp.
It reduces lots of burden and using database is also very simple.
It support multiple language but very less languages.This feature is included in newer version of .NET 2010:thumbsup:
Please Try it.
Old Pedant
09-01-2010, 06:52 PM
Yes, I mentioned that (ASP.NET) in my first answer. But he specifically wants and needs an ASP solution.
FabriceB
01-27-2011, 06:14 PM
Well, probably the best answer is to *NOT* use ASP. Instead, learn to use ASP.NET, which has better support for this kind of stuff.
Hi Old Pedant,
We started a migration to ASP.NET and for now I've kept the same approach in regards to translation but I'm now very curious about how I could've used ASP.NET...
Had anything in mind at that point?
Thanks
Old Pedant
01-28-2011, 02:25 AM
http://www.google.com/search?q=asp.net+localization
The very first link that produces is the best starting point:
http://msdn.microsoft.com/en-us/library/c6zyy3s9.aspx
vBulletin® v3.8.2, Copyright ©2000-2012, Jelsoft Enterprises Ltd.