Använd XML för språkoberoende hemsidor
Förord
Många pratar om språkhantering och i många lägen behövs det. Det finns faktiskt inbyggt i .net men varför inte testa att skriva ett eget sådant. Sagt å gjort, här är alltså ett alternativ till att bygga upp en site eller en applikation med ett eget sätt att sköta språkhantering på. Idéen är hämtad från Martin på dropit.se som byggde en liknande version.Innehåll
»»
»
»
»
»
»
Relaterade artiklar
» asp.net - bygg in språkberoende på din siteVi börjar helt enkelt med att skapa oss en xml-fil som till en början inte blir speciellt märkvärdig. Denna kan sedan byggas på mer och mer, med fler språk och du kan till och med separera dessa så en xml-fil tillhör respektive språk. I mitt syfte kör vi i alla fall två språk, men vi har allt data samlat på en och samma sida.
Nu har vi vår första xml-fil som du har skapat i notepad eller någon annan editor. Som du ser här så har vi starttaggen languages, där efter kommer language-taggen för svenska och därefter engelska. Som du förstår nu så kan du alltså bygga denna fil hierarkiskt hur djup som helst för att gruppera din information. Vi börjar med att fylla på några små objekt för såväl svenska som engelska och därefter skall vi plocka ut informationen och använda den i vår applikation.
Nu har vi lagt en rubrik som vi kallar page, och under page några objekt - typiska menyalternativ i toppen på pellesoft som du ser. Nu är det dags att bygga en funktion som hjälper oss att växla mellan språk. Enklast är att använda sig av ett sessionsobjekt och det börjar vi med genom att ha en link som vi trycker på om vart annat för att växla språket.
När vi klickar på denna länken så har vi en funktion kopplad till den i codebehind som ser ut som följer:
Först tittar vi lite på Response.Redirect här. Den används för att ladda om hela sidan igen då koden troligast ligger i en usercontrol som visar menyn. Genom att ladda om sidan kommer informationen att presenteras igen men eftersom vi bytt språk ser man även ändringarna. Det står Language.English och .Swedish. Dessa har jag lagt i min basklass för att enkelt nå den. Du kan göra samma sak genom att lägga till följande.
Nu behövs vår magiska kontroll för att hämta rätt språk för rätt inställning. Vi börjar med funktionen som hämtar rätt språk.
Rutinen returnerar helt enkelt en eller sv som vi sedan kommer använda oss av i vår språkfunktion när vi hämtar ut datat från vår xmlfil. Om nu anvädaren tittar på sidan och inte har påbörjat sin session än kan man också ta reda på vad webbläsaren har default för språk. Detta gör du exempelvis genom att kalla en rutin som nedan men endast en gång.
Denna rutinen kan du exempelvis kalla på initiellt för att sedan användas vidare i dina rutiner. Den skall alltså kallas första gången, såvida du inte alltid vill påtvinga språket som webbläsaren initiellt använder. Därefter behöver vi också få reda på var filen finns placerat rent fysiskt på disk i vår applikation.
Denna hämtar helt enkelt ut sökvägen där vi placerat vår xmlfil. Du kan antingen använda dig av mappath eller så lägger du det helt enkelt i web.config. Jag förordar web.config eftersom mappath faktiskt gör en findfile som i sin tur tar cpu och disk IO i onödan. Det kommer bli ganska många anrop när folk surfar på sidan så allt sådant som är tidsbesparande skall användas.
Sådär, nu har vi även våra sökvägar i web.config. Nästa moment är nu aktiskt att återanvända till vår linkbutton ovan, för den skall ju byta språk på texten varje gång man klickar på denna. Vi tar helt enkelt och på vår huvudsida, exempelvis default.aspx att lägga till denna funktion.
Vi är nu bara en liten bit kvar från vår färdiga språkfil och den sista funktionen är alltså GetLangMessage som vi använder för att få tag på vår xml-fils specifika post beroende på vilket språk vi valt. Den här rutinen är lite mer avancerad än dom tidigare och en kort förklaring krävs innan vi summerar allting.
XML-filen läses in i cacheobjektet rakt upp och ner. Så länge den finns kommer vi hämta den och plocka ut vår översättning. Men om den inte finns där, läses den upp igen och läggs in. Jag har valt att med 15 minuters intervall döda denna cache och ladda om den automatiskt. Anledningen till det är att om jag skickar in en ny språkfil exempelvis via FTP så vet jag att inom 15 minuter kommer den nya användas. Hur du gör får du bestämma själv förstås. men jag tycker det är ett smidigt sätt.
En sak till, jag har något som kallas fallback. Det innebär att om jag står på sidan på Engelska och skall visa namnet där och det inte finns, då går jag vidare till Svenska och visar det. Finns inte det heller så skriver jag helt enkelt [nyckeln saknas]. På detta sätt vet du att du kanske missat ett stavat fe l på något tagg i din xml-fil, eller helt enkelt glömt den när du hoppar mellan dom olika språken. Nu tar vi en titt på vår funktion.
Det som kan vara lite främmande här är ett scriptspråk som heter XPath och kan användas för att arbeta med just xml-filer. Funktionen SelectSingleNode söker den först matchande noden som kan finnas och vi skickar in data som säger att language skall vara SV/ därefter skall noden vara något som vi skickar med från vår funktion när vi skall skriva vår label. Det bör även tilläggas att Cache.Insert motsvarar gamla asp's Application("namn") men är nu mycket mer avancerad och effektivare.
Mer läsning om ämnet:
Och så var hela applikationen klar att användas och testas. Om ni kommer på bättre eller effektivare lösningar så hör gärna av er eller posta de i filarkivet. Lycka till!
[/include/language.xml]
Din xml-fil
Nu har vi vår första xml-fil som du har skapat i notepad eller någon annan editor. Som du ser här så har vi starttaggen languages, där efter kommer language-taggen för svenska och därefter engelska. Som du förstår nu så kan du alltså bygga denna fil hierarkiskt hur djup som helst för att gruppera din information. Vi börjar med att fylla på några små objekt för såväl svenska som engelska och därefter skall vi plocka ut informationen och använda den i vår applikation.
In English
På Svenska
Förbered textvisning
Nu har vi lagt en rubrik som vi kallar page, och under page några objekt - typiska menyalternativ i toppen på pellesoft som du ser. Nu är det dags att bygga en funktion som hjälper oss att växla mellan språk. Enklast är att använda sig av ett sessionsobjekt och det börjar vi med genom att ha en link som vi trycker på om vart annat för att växla språket.
LinkButton
När vi klickar på denna länken så har vi en funktion kopplad till den i codebehind som ser ut som följer:
Private Sub lnkLanguage_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkLanguage.Click
If Session("UserCurrentLanguage") = Language.English Then
Session("UserCurrentLanguage") = Language.Swedish
Else
Session("UserCurrentLanguage") = Language.English
End If
Response.Redirect(Request.Url.ToString, True)
End Sub
Ladda om sidan
Först tittar vi lite på Response.Redirect här. Den används för att ladda om hela sidan igen då koden troligast ligger i en usercontrol som visar menyn. Genom att ladda om sidan kommer informationen att presenteras igen men eftersom vi bytt språk ser man även ändringarna. Det står Language.English och .Swedish. Dessa har jag lagt i min basklass för att enkelt nå den. Du kan göra samma sak genom att lägga till följande.
Public Enum Language
Swedish = 1
English = 2
End Enum
Nu behövs vår magiska kontroll för att hämta rätt språk för rätt inställning. Vi börjar med funktionen som hämtar rätt språk.
Shared Function GetLanguage() As String
Select Case System.Web.HttpContext.Current.Session("UserCurrentLanguage")
Case Language.English : GetLanguage = "en"
Case Else : GetLanguage = "sv"
End Select
End Function
Plocka ut rätt datat med scriptspråket XPath
Rutinen returnerar helt enkelt en eller sv som vi sedan kommer använda oss av i vår språkfunktion när vi hämtar ut datat från vår xmlfil. Om nu anvädaren tittar på sidan och inte har påbörjat sin session än kan man också ta reda på vad webbläsaren har default för språk. Detta gör du exempelvis genom att kalla en rutin som nedan men endast en gång.
Shared Function GetLanguageId() As Integer
GetLanguageId = CInt(System.Web.HttpContext.Current.Session("UserCurrentLanguage"))
If GetLanguageId < 1 Then
' vilket språk är webbläsaren inställd på som default, visa då det först?
Dim webbrowserlanguage As String = System.Web.HttpContext.Current.Request.ServerVariables("HTTP_ACCEPT_LANGUAGE")
Select Case Left(LCase(webbrowserlanguage), Language.English)
Case "sv"
System.Web.HttpContext.Current.Session("UserCurrentLanguage") = Language.Swedish
Case "en"
System.Web.HttpContext.Current.Session("UserCurrentLanguage") = Language.English
Case Else
System.Web.HttpContext.Current.Session("UserCurrentLanguage") = Language.English
End Select
End If
End Function
Denna rutinen kan du exempelvis kalla på initiellt för att sedan användas vidare i dina rutiner. Den skall alltså kallas första gången, såvida du inte alltid vill påtvinga språket som webbläsaren initiellt använder. Därefter behöver vi också få reda på var filen finns placerat rent fysiskt på disk i vår applikation.
Shared Function GetLanguagePath() As String
If InStr(HttpContext.Current.Request.Url.ToString, "localhost") > 0 Then
GetLanguagePath = ConfigurationSettings.AppSettings("LanguageFileLocal")
Else
GetLanguagePath = ConfigurationSettings.AppSettings("LanguageFile")
End If
End Function
Sökvägen till din xml-fil
Denna hämtar helt enkelt ut sökvägen där vi placerat vår xmlfil. Du kan antingen använda dig av mappath eller så lägger du det helt enkelt i web.config. Jag förordar web.config eftersom mappath faktiskt gör en findfile som i sin tur tar cpu och disk IO i onödan. Det kommer bli ganska många anrop när folk surfar på sidan så allt sådant som är tidsbesparande skall användas.
Sådär, nu har vi även våra sökvägar i web.config. Nästa moment är nu aktiskt att återanvända till vår linkbutton ovan, för den skall ju byta språk på texten varje gång man klickar på denna. Vi tar helt enkelt och på vår huvudsida, exempelvis default.aspx att lägga till denna funktion.
lnkLanguage.Text = Namespace.Klassnamn.GetLangMessage("/page/language", True)
Vi är nu bara en liten bit kvar från vår färdiga språkfil och den sista funktionen är alltså GetLangMessage som vi använder för att få tag på vår xml-fils specifika post beroende på vilket språk vi valt. Den här rutinen är lite mer avancerad än dom tidigare och en kort förklaring krävs innan vi summerar allting.
XML-filen läses in i cacheobjektet rakt upp och ner. Så länge den finns kommer vi hämta den och plocka ut vår översättning. Men om den inte finns där, läses den upp igen och läggs in. Jag har valt att med 15 minuters intervall döda denna cache och ladda om den automatiskt. Anledningen till det är att om jag skickar in en ny språkfil exempelvis via FTP så vet jag att inom 15 minuter kommer den nya användas. Hur du gör får du bestämma själv förstås. men jag tycker det är ett smidigt sätt.
Om inte det du söker finns?
En sak till, jag har något som kallas fallback. Det innebär att om jag står på sidan på Engelska och skall visa namnet där och det inte finns, då går jag vidare till Svenska och visar det. Finns inte det heller så skriver jag helt enkelt [nyckeln saknas]. På detta sätt vet du att du kanske missat ett stavat fe l på något tagg i din xml-fil, eller helt enkelt glömt den när du hoppar mellan dom olika språken. Nu tar vi en titt på vår funktion.
Shared Function GetLangMessage(ByVal KeyName As String, Optional ByVal FixBlankingSpace As Boolean = False) As String
' // Hämtar ur translate.xml ett namn eller en text beroende
' på hur användaren ställt in sin webbläsare, eller via
' hemsidan. Dokumentet läggs även i en application-cache
' så den endast efterfrågas då vid av applikation/iis.
' ------------------------------------------------------
' KeyName är case-sensitive
' rooten anges ej, är
' ------------------------------------------------------
Dim XMLdoc As XmlDocument = New XmlDocument
Try
' // Om hela xmldokumentet är cachat så använder vi det, annars öppnar vi
' det bara igen och läser igenom våra värden
If Not HttpContext.Current.Cache("languagefile") Is Nothing Then
' läs ut xmlfilen från cache
XMLdoc = HttpContext.Current.Cache("languagefile")
Else
' skriv ner xmlfilen i cache, cacha i 15 minuter
XMLdoc.Load(GetLanguagePath)
HttpContext.Current.Cache.Insert("languagefile", XMLdoc, Nothing, DateTime.Now.AddMinutes(15), TimeSpan.Zero)
End If
' // Finns något data?
If XMLdoc.DocumentElement Is Nothing Then
Exit Function
End If
Catch ex As Exception
' vi fick något fel
System.Web.HttpContext.Current.Response.Write("Fel vid hämtning av xml-fil till siten. " & Err.Source & "
" & Err.Description)
HttpContext.Current.Response.End()
End Try
' // Söker ut rätt data baserat på frågan. En fallback finns vilket innebär
' att om inte något annat språks värde finns, söker den efter det svenska
' och finns inte det - returneras texten [nyckel saknas]
Dim returnvalue As String
Try
returnvalue = XMLdoc.SelectSingleNode("/languages/language[@id='" & UCase(pellesoft.Site.GetLanguage,) & "']/" & KeyName).InnerText
Catch ex As Exception
' fallback på språk
If pellesoft.Site.GetLanguageId >= 1 Then
Try
returnvalue = XMLdoc.SelectSingleNode("/languages/language[@id='SV']/" & KeyName).InnerText
Catch ex
returnvalue = "[nyckel saknas]"
End Try
Else
returnvalue = "[nyckel saknas]"
End If
End Try
' // rensar objektet
XMLdoc = Nothing
' // returnerar resultatet
If FixBlankingSpace = True Then
returnvalue = Replace(returnvalue, "-", "-")
Return Replace(returnvalue, " ", " ")
Else
Return returnvalue
End If
End Function
Avslutningsvis
Det som kan vara lite främmande här är ett scriptspråk som heter XPath och kan användas för att arbeta med just xml-filer. Funktionen SelectSingleNode söker den först matchande noden som kan finnas och vi skickar in data som säger att language skall vara SV/ därefter skall noden vara något som vi skickar med från vår funktion när vi skall skriva vår label. Det bör även tilläggas att Cache.Insert motsvarar gamla asp's Application("namn") men är nu mycket mer avancerad och effektivare.Mer läsning om ämnet:
- Se: http://www.w3schools.com/xpath/default.asp
Och så var hela applikationen klar att användas och testas. Om ni kommer på bättre eller effektivare lösningar så hör gärna av er eller posta de i filarkivet. Lycka till!
Johan Segolsson
Tycker du borde förklara varför du inte vill använda det inbyggda stödet för lokalisering i .net. Visst kan det tyckas enkelt att bara kunna skicka upp en xml fil men detta går att göra med det existerande ramverket också. En annan sak, hur mycket påverkas prestandan om man jämnför med det inbyggda stödet?
Pelle Johansson
Hej Johan. Man får bättre överblick på hela filen. Det är lättare att hantera helt enkelt för alla parter, speciellt om man ser till att kunder, användare, översättare som kanske skall ändra något snabbt istället för att behöva förlita sig på utvecklaren eller webbyrån. Ta ett exempel: Du skall ändra knappens caption från "Skicka kommentarer" till "Skicka nu". Du vet att du kan ändra xmlfilen på servern själv eller använda det inbyggda stödet. Vilket går fortast att fixa? När det gäller prestandan så tror jag inte det är någon större skillnad. XPath är snabbt och eftersom xmlfilen ligger cachad som om den hade legat kompilerad. Jag tror varken den är snabbare eller långsammare. Enligt min mening bara mer dynamisk och lättanvänd.
Johan Segolsson
Håller med om att det går mycket snabbare att ändra i en xml fil, men varför inte använda det inbyggda stödet och inte kompilera in det i en assembly. Du ladda in vanliga resx filer (vilka är xml filer), skillnaden mellan det här och ditt sätt är att du inte behöver bygga någon egen lokaliseringsmodul utan du kan .NET's. Det är dock lite svårare att använda resx filer som inte är inkompilerade i en assembly men inte i närheten av så kompliserat som att göra en egen lokaliseringsmodul. Det tycker jag iallafall :-P
Robert Bolocci
Hej Jag är en nybörjare ,din artikel verkar vara mycket intressant. jag vill verkligen använda den men jag försår inte hur. skulle du kunna strukturisera den? jag menar hur många filer använder man? var web.config ligger? tack så mycket
Pelle Johansson
web.config ligger i rooten på din virtuella sajt. Man använder 1 xml-fil och kan dela in den hur man vill när det gäller antal språk och innehåll. XML kan ju skrivas lite som man vill.
Robert Bolocci
varför jag kan inte hitta web.config under Inetpub, jag har sökt på alla mappar i denna mapp. allt annat kod som du har skrivit ska ligger i en och samma aspx fil eller ?