|
Internationalisation (i18n) with PHP XML
|
|
|
|
|
Wednesday, 16 July 2008
|
I. Introduction The principle of multi-languages is to amend the text content of all elements of an HTML page depending on the language chosen by the user. To do this we must store these values in external files, they will be in XML format and syntax controlled with a DTD (Document Type Definition). To be able to manipulate these files XML, PHP offers us a complete panel of tools: DOM functions. Using a database like MySQL here allows you to save the languages used and a variable to see if the language is open to users or not. To interact with the database I used a class that I myself created (SQLManager.php) will not be discussed in this article, but it is very well documented and you can learn to use it if you want with his comments. In summary, the class can connect to the server and a database, especially it generates SQL for us and we return results in tabular form. Do not confuse warning functions and functions DOM DOM XML, DOM XML functions are not implemented in PHP 5. II. The DTD (Document Type Definition) The DTD is used to create a model for XML documents, a strict hierarchy with respect to the interlocking elements and their attributes. When you create an XML document, it indicates the location of DTD to "validate" the document. It is necessary to use certain functions DOM and allows homogenise all documents. The DTD uses a language unique to him, but simple to understand. In the file will set the name tags needed and their attributes, specifying whether these attributes and tags are mandatory or optional. Code DTD (xmldoctype.dtd) <? xml version = "1.0" encoding = "UTF-8"?> <! ELEMENT language (xmldata *)> <! -- Definition of the first element 'language' (the root element of the file) in which one can place items (xmldata) *: The star indicates 0 or more elements --> <! ELEMENT xmldata (translation +)> <! ATTLIST xmldata id ID # REQUIRED> <! -- Defining the 'xmldata', we can put elements (translation) then attribute 'id' is defined and ID is required (mandatory), the '+' means It takes at least 1 element. --> <! ELEMENT translation (# PCDATA)> <! ATTLIST translation lang CDATA # REQUIRED> <! -- Finally here is the definition of a 'translation' which contains Data (# PCDATA), which means that it is a big chain Characters for simplicity. It is this element which contain our texts. It has an attribute 'lang' which is also required. --> III. The XML file XML files will be our database for texts of the site, following the definitions created before, it will be simple conçevoir 'database' (mode Design Eclipse is very convenient and simple). Code XML (for example website.xml) <? xml version = "1.0" encoding = "UTF-8"?> <! DOCTYPE language SYSTEM "xmldoctype.dtd"> <! - We must define the location of DTD and the element used as root -> <language> <xmldata id="webPageTitle"> <translation lang="fr"> demonstration XMLEngine </ translation> <translation lang="en"> XMLEngine demonstration </ translation> </ xmldata> </ language> IV. The SQL database For our database is very simple, we just need a table with two fields: one containing the tag of languages used, the other being a boolean that allows whether the language is open to users or not. | Table: Languages | | Field Name | Value | Description | | Language | VARCHAR (2) | String containing the tag of the language | | IsAvailable | BOOL (TinyInt (1)) | Variable control for the accessibility of language users | V. The class PHP: XMLEngine Here is the central part of our solution, it is this class that will allow us to manipulate XML data files. The class has several functions to help the hotel via a panel administrator. The warning types highlighted in this table are just as indicative. It should not copy them in the source file. Definition of the attributes of the class: | Attributes | | Name | Definition | | private DOMDocument domFile | Field in which lies the body of the DOMDocument class. | | private string currentLanguage | Field in which you save the language used when handling XML files. | | private string currentFile | Field where we store the path to the XML file open and in use. | | private fileID fileLock | Field to block access to the file when changes. | Definition of methods (functions) of the class: | Methods | | Name | Definition | public __construct ( string $ filename, $ string language) | Manufacturer of class, an argument the path to the XML file to open, $ filename, with the language $ language to use. | | public string getCurrentFile () | method to retrieve the file name under way. | public string getItemValue ( itemID $ string) | method to extract the text content of an ID tag with $ itemID argument is past. | | public string [] getItemNodeList () | method to retrieve all the IDs of tags xmldata XML file. | public changeFile ( $ newFile string) | method to load a new XML file. | public changeLanguage ( $ newLanguage string) | method to change the language used. | public bool addXmlElement ( $ itemID string, $ string Lang, itemDefaultValue $ string) | method for creating a new element xmldata whose ID will be $ itemID with a child in the language translation $ lang and with the text $ itemDefaultValue. | public bool modifyXmlElement ( $ itemID string, $ string Lang, $ newContent string) | method for modifying the text content of an element whose ID is $ itemID, in the language $ lang, and the new text will be $ newContent. | public bool removeXmlElement ( $ itemID string, $ string lang) | method to remove an element whose language translation is $ lang, in this element xmldata which has the ID $ itemID. | public bool completelyRemoveXmlElement ( itemID $ string) | method to remove an element xmldata whose ID is $ itemID. | public bool isEmptyXml ( $ itemID string, $ string lang) | method to verify that an item is full translation in the language $ lang. | public addLanguageToFile ( $ newLanguage string) | method for adding a new language $ newLanguage the file. This function creates a translation with the new language in each element xmldata if there is none. | public bool checkIntegrity ( $ string lang) | method to verify that all elements xmldata have elements in the language translation $ lang and they are met. | public string __get ( string $ itemID) | Method magic PHP to be executed when you want to read an attribute of the class. By using the IDs of elements xmldata as class attributes, we can return the contents of the text tags more easily. | | private saveFile () | method to save the XML file in a secure way. | VI. Using the class In warning the pages of code on ignore the HTML standards to go to the essentials and avoid getting lost, only the elements necessary for the proper functioning of the solution will be present. Accordingly, no CSS style has been defined, the styles are inserted directly into the STYLE attribute for demonstrations in this article. A. Extraction of data To begin with we will simply use the class to retrieve data: PHP Code (extract.php) <? PHP session_start (); / / It adds the class file. require_once 'include / XMLEngine.php'; / * It will use a variable session for the user's language. * If the variable is not on the design. * / if (! isset ($ _SESSION [ 'Language'])) ( $ _SESSION [ 'Language'] = 'en' / / French default. ) / / Create a forum for our class with the XML file to open. $ xml = new XMLEngine ( 'xml / website.xml', $ _SESSION [ 'Language']); / * With the function __get (), this line is sufficient to display the content * Element XMLDATA ID 'webPageName' French .* / echo $ xml-> webPageName; ?> There is that for extracting data, other functions are mainly designed to create the hotel through a web interface. B. Adding elements In this section we will see how to add elements, creating a first step in an HTML form to enter the necessary elements, then the party which will deal with PHP this form to add the element and display a message javascript, while utilizing whenever our XML object to retrieve the texts of the site: PHP Code (addForm.php) <? PHP / * * It takes the same early as before: * / session_start (); require_once 'include / XMLEngine.php'; if (! isset ($ _SESSION [ 'Language'])) ( $ _SESSION [ 'Language'] = 'en' / / French default. ) $ xml = new XMLEngine ( 'xml / website.xml', $ _SESSION [ 'Language']); / / It includes SQLManager class and create an object. require_once 'include / SQLManager.php'; $ sql = new SQLManager ( 'localhost', 'root','' 'Test'); / * Creation of the entry form: * /?> <form action="addForm.php" method="post"> <! - On a table in the form to make Shaped -> <table Border="1"> <! - Title -> <tr> <td Colspan="2"> <? Php echo $ xml-> webPageName;?> </ Td> </ Tr> <! - Entering the ID tag -> <tr> <td> <? Php echo $ xml-> inputID;?> </ Td> <td> <input Type="text" name="itemId" /> </ Td> </ Tr> <! - Enter text for the new content -> <tr> <td> <? Php echo $ xml-> inputData;?> </ Td> <td> <textarea cols="20" rows="5" name="itemData"> </ textarea> </ Td> </ Tr> <! - Creating a list of choice of languages -> <tr> <td> <? Php echo $ xml-> inputLang;?> </ Td> <td> <? PHP / / It gets all the languages of the SQL database $ langs = $ sql-> selectQuery ( '*', 'Languages', 0,'','','',''); / / Can we create the list?> <select name="itemLanguage" style="width:100%;"> <? php Foreach ($ langs as $ language) (?> <option value = "<? php echo $ language [ 'Language'];?>"><? php echo $ xml-> ($ language [' Language '].' Lang '};?></ option> < ? PHP )?> </ Select> </ Td> </ Tr> <! - Bouton d sending to create an XML tag -> <tr> <td Colspan="2"> <input style = "width: 100%; text-align: center;" type = "submit" name = "submitCreateItem" value = "<? php echo $ xml-> submitCreate;?>" /> </ Td> </ Tr> </ Table> </ form> <? php / * * Treatment of form when it is submitted * / if (isset ($ _POST [ 'submitCreateItem'])) ( / * * It is now call the function with addXmlElement * Variables of form, function as a reference booleen * L can be directly included in the condition of IF: * / if ($ xml-> addXmlElement ($ _POST [ 'itemId'], $ _POST [ 'itemLanguage'], $ _POST [ 'itemData'])) ( / * Successfully adding the element * It displays a small message javascript, always with our class. * /?> <script Type="text/javascript"> <! -- Alert ( "<? Php echo $ xml-> createXmlDone ;?>"); --> </ Script> <? Php ) Else (?> <script Type="text/javascript"> <! -- Alert ( "<? Php echo $ xml-> createXmlError ;?>"); --> </ Script> <? Php ) ) ?>
C. Updating elements (translation) With this we can already create elements in XML files. Now we will modify the elements in order to bring those who are not. We will therefore generate an HTML form tags to change depending on the language translation chosen: PHP Code (traduceForm.php) <? PHP / * * There is a new page, * It takes the same early as before: * / session_start (); require_once 'include / XMLEngine.php'; / / It includes SQLManager class and create an object. require_once 'include / SQLManager.php'; $ sql = new SQLManager ( 'localhost', 'root','' 'Test'); if (! isset ($ _SESSION [ 'Language'])) ( $ _SESSION [ 'Language'] = 'en' / / French default. ) $ xml = new XMLEngine ( 'xml / website.xml', $ _SESSION [ 'Language']); / * * It includes here the treatment of form that will be seen then. * We must see 'require_once' as copying and pasting, so the * File 'traduceTreatement.php It is only to be included. * / require_once 'traduceFormTreatment.php'; / * Beginning form hotel: * /?> <div align="center"> style="height:300px;overflow:scroll;" <? php / * * To begin with we need to create a form for choosing * Language in which translate, on display this form * If no other has been sent. * / if (! isset ($ _POST [ 'submitLangToTraduce']) & &! isset ($ _POST [ 'submitTraduceFile'])) (?> <form action="traduceForm.php" method="post"> <table Border="1"> <tr> <td> <? PHP Echo $ xml-> foreignLang;?> </ Td> </ Tr> <! - It creates a list of languages the ol -> <tr> <td Align="center"> <? PHP / / It gets all the languages of the SQL database $ langs = $ sql-> selectQuery ( '*', 'Languages', 0,'','','',''); / * Can we create the list * /?> <select name="langToTraduce" style="width:100%;"> <? php Foreach ($ langs as $ language) (?> <option value = "<? php echo $ language [ 'Language'];?>"><? php echo $ xml-> ($ language [' Language '].' Lang '};?></ option> < ? PHP )?> </ Select> </ Td> </ Tr> <tr> <td Colspan="2"> <input type = "submit" name = "submitLangToTraduce" value = "<? php echo $ xml-> submitChooseLang;?>" /> </ Td> </ Tr> </ Table> </ form> <? php ) / / End of form choice of languages / * * Once the language or treatment carried out on poster form * With the elements of file website.xml * / if (isset ($ _POST [ 'submitLangToTraduce']) | | isset ($ _POST [ 'submitTraduceFile'])) ( / * Create a new object xml for the language to translate $ xmlTraductor = new XMLEngine ( 'xml / website.xml', $ _POST [ 'langToTraduce']) * /?> <form Action="traduceForm.php" method="post"> <table Border="1"> <! - Title -> <tr> <td Colspan="3"> <? Php echo $ xml-> webPageName;?> </ Td> </ Tr> <! - Cells of a header -> <tr> <td> <? Php echo $ xml-> frLang;?> </ Td> <td> <? PHP / * * If you use the same syntax for IDs xml * Which represents the name of a language, you can create the variable name * Using the dynamic variables without knowing, we will create * The variable name in the form of string before * Use it. Here you will frLang or enLang. * / echo $ xml-> ($ _POST [ 'langToTraduce']. 'Lang');?> </ Td> <td> <? Php echo $ xml-> newContent;?> </ Td> </ Tr> <? Php / / It gets the IDs of tags: IdTab $ = $ xml-> getItemNodeList (); / * * On the trail tables identifiers in creating each * Times a line that contains the French version read-only (mother tongue) * And on the right language to translate, then all right, to change fields * A value if you like, or fill an empty field. * / Foreach ($ idTab as $ cell) (?> <tr> <td> <input type = "text" readonly = "readonly" value = "<? php echo $ xml-> $ cell;?>" style = "width: 250px" /> </ Td> <td> <input type = "text" readonly = "readonly" value = "<? php echo $ xmlTraductor-> $ cell;?>" style = "width: 250px;" /> </ Td> <! - Should be given a name for the field that we will fulfil, this title is that of ID XML tag -> <td> <textarea name = "<? php echo $ cell;?>" rows = "1" cols = "20"> </ textarea> </ Td> </ Tr> <? Php ) / * Then submit button to make the changes: * /?> <tr> <td Colspan="3"> <input type = "hidden" name = "langToTraduce" value = "<? php echo $ _POST [ 'langToTraduce'];?>" /> <input type = "submit" name = "submitTraduceFile" value = "<? php echo $ xml-> submitTraduce;?>" style = "width: 100%; text-align: center;" /> </ Td> </ Tr> </ Table> </ Form> <? Php ) / / End of form translation / / Here we check that everything went well to display a message JScript. if (isset ($ _POST [ 'submitTraduceFile'])) ( / / It verifies our control variable and displays the correct message. If ($ allSaveGood) ( / * * Success?> <script Type="text/javascript"> Alert ( "<? Php echo $ xml-> modificationDone ;?>"); </ Script> <? Php ) Else ( / * * Failure?> ?> <script Type="text/javascript"> alert ( "<? php echo $ xml-> modificationError ;?>"); </ Script> <? Php ) )?> </ div> This code will generate an HTML form that can view the contents of tags in both languages and desire to change, empty fields will not be processed. Caution warning: we must not give an identifier for XML SUBMIT button which is identical to attribute NAME, there would be conflicts in the $ _POST between the name of the button and scope TEXTAREA with the same name. Once this form submitted, it must be treated to modify XML elements which form fields are not empty, this file will be included in early traduceForm.php: PHP Code (traduceFormTreatment.php) <? PHP / * * It detects the launch of form translation for change * XML file before reuse. * / if (isset ($ _POST [ 'submitTraduceFile'])) ( / * * It eliminates the variable SUBMIT button in the $ _POST, for * Able to use the $ _POST that will not contain THAT IDs * XML elements. If you add other tags in the form, beware * To remove these variables table $ _POST here. * / Unset ($ _POST [ 'submitTraduceFile']); / / It gets the language before you remove it from the table $ _POST. LangToTraduce $ = $ _POST [ 'langToTraduce']; Unset ($ _POST [ 'langToTraduce']); / * * We launch function adding a language in the XML file. * This feature is intelligent and not create duplicate, if all * Elements already exist it will not alter the data. * / $ Xml-> addLanguageToFile ($ langToTraduce); / / Variable to know if everything went well. $ AllSaveGood = true; / / Now on the table and route changes loop: Foreach ($ _POST as $ xmlID => $ newValue) ( / * * If you have any difficulties with the foreach: * Here for each box in the table, get the name of Box * With $ xmlID (identifier XML), and its value with $ newValue (the new text) * * / / / If the field is not empty. If ($ newValue! ='') ( / * * On amends XML element using the return value of the * Function which is a boolean, if the latter is not true, then our * Control variable $ allSaveGood will also be false. * / if ($ xml-> modifyXMLElement ($ xmlID, $ langToTraduce, $ newValue)) ( $ AllSaveGood = false; ) ) ) / * * We must re-insert these two values in the $ _POST so thereafter * File takes place without problems. * / $ _POST [ 'LangToTraduce'] = $ langToTraduce / / Language to translate. $ _POST [ 'SubmitTraduceFile'] = true / / Like what form was sent. ) / / End of treatment form translation ?> D. Removing elements To remove what it has two functions, one removes the element xmldata, another element of a translation xmldata element in the chosen language. It will create a simple form containing two lists, one with all identifying elements xmldata, the other languages used: PHP Code (removeForm.php) <? PHP / * * There is a new page, * It takes the same early as before: * / session_start (); require_once 'include / XMLEngine.php'; / / It includes SQLManager class and create an object. require_once 'include / SQLManager.php'; $ sql = new SQLManager ( 'localhost', 'root','' 'Test'); if (! isset ($ _SESSION [ 'Language'])) ( $ _SESSION [ 'Language'] = 'en' / / French default. ) $ xml = new XMLEngine ( 'xml / website.xml', $ _SESSION [ 'Language']); / * * Inclusion of treatment form. * / require_once 'removeFormTreatment.php'; / / Start form hotel: ?> <div align="center"> style="height:500px;overflow:scroll;" <? php / * * It is preparing a JavaScript function to confirm the deletion * An element, this function return true or false, to send * Whether or not the form to be processed. * /?> <script Type="text/javascript"> <! -- / / Variable Javascript total for what submit button is pressed. Var button; ConfirmDelete function () ( If (button == "xml") ( return confirm ( "<? php echo $ xml-> confirmDelete;?>" document.removeForm.xmlDataId.value + + " '?"); ) Else if (button == "translation") ( return confirm ( "<? php echo $ xml-> confirmDelete;?> 'translation' language '" Document.removeForm.translationLangId.value + + " 'the'" + + Document.removeForm.xmlDataId.value '? " ); ) ) --> </ Script> <? PHP / * * It adds the attribute 'onsubmit' and 'name' to send form * only after confirmation of the removal with the function JS. * /?> action="removeForm.php" <form method="post" name="removeForm" onsubmit="return confirmDelete();"> <table Border="1"> <tr> <td> <? PHP Echo $ xml-> element. " <i> xmldata </ i >';?> </ Td> <td> <? PHP Echo $ xml-> element. " <i> translation </ i >';?> </ Td> </ Tr> <tr> <td> <select Name="xmlDataId"> <? PHP / * * On retrieves the list of elements and then * Display. * / $ ItemList = $ xml-> getItemNodeList (); Foreach ($ itemList as $ item) (?> <option value = "<? php echo $ item ;?>"><? php echo $ item ;?></ option> <? php )?> </ Select> </ Td> <td> <? PHP / / It gets all the languages of the SQL database $ langs = $ sql-> selectQuery ( '*', 'Languages', 0,'','','',''); / * Can we create the list * /?> <select name="translationLangId" style="width:100%;"> <? php Foreach ($ langs as $ language) (?> <option value = "<? php echo $ language [ 'Language'];?>"><? php echo $ xml-> ($ language [' Language '].' Lang '};?></ option> < ? PHP )?> </ Select> </ Td> </ Tr> <tr> <! - Bouton d dispatch to remove an element xmldata -> <td> <! - It uses the attribute ONCLICK in which we place JavaScript code, then are assigned to the variable button (variable JS) the word or XML TRANSLATION -> <input type = "submit" name = "submitDeleteXmlDataElement" value = "<? php echo $ xml-> deleteElement;?>" onclick = "button = 'xml';" /> </ Td> <! - Bouton d dispatch to remove a translation -> <td> <! - Ditto for the ONCLICK attribute that previously -> <input type = "submit" name = "submitDeleteTranslationElement" value = "<? php echo $ xml-> deleteElement;?>" onclick = "button = 'translation';"/> </ Td> </ Tr> </ Table> </ Form> </ div> Then the script treatment of the form, included just before the latter so that the list be updated. PHP Code (removeFormTreatment.php) <? PHP / * * File only to be included. * Treatment form removal of elements when it * Is sent. We must make two different treatments according to the * Submit button used. * / if (isset ($ _POST [ 'submitDeleteXmlDataElement'])) ( / * * To remove an element xmldata, using the method of * XMLEngine class, which return true or false. * / if ($ xml-> completelyRemoveXmlElement ($ _POST [ 'xmlDataId'])) (?> <script Type="text/javascript"> <! -- Alert ( "<? Php echo $ xml-> modificationDone ;?>"); --> </ Script> <? Php ) Else (?> <script Type="text/javascript"> <! -- alert ( "<? php echo $ xml-> modificationError ;?>"); --> </ Script> <? Php ) ) else if (isset ($ _POST [ 'submitDeleteTranslationElement'])) ( / * * To remove a translation, using the method of * XMLEngine class, which return true or false. * / if ($ xml-> removeXmlElement ($ _POST [ 'xmlDataId'], $ _POST [ 'translationLangId'])) (?> <script Type="text/javascript"> <! -- Alert ( "<? Php echo $ xml-> modificationDone ;?>"); --> </ Script> <? Php ) Else (?> <script Type="text/javascript"> <! -- alert ( "<? php echo $ xml-> modificationError ;?>"); --> </ Script> <? Php ) )?> E. Availability of languages Finally we will see how to verify that all our files have the elements necessary to make a language accessible to users. We need just a form verifying languages inaccesibles with the database and then begin the verification and modify the database if necessary. PHP Code (languageAvailability.php) <? PHP / * * There is a new page, * It takes the same early as before: * / session_start (); require_once 'include / XMLEngine.php'; / / It includes SQLManager class and create an object. require_once 'include / SQLManager.php'; $ sql = new SQLManager ( 'localhost', 'root','' 'Test'); if (! isset ($ _SESSION [ 'Language'])) ( $ _SESSION [ 'Language'] = 'en' / / French default. ) $ xml = new XMLEngine ( 'xml / website.xml', $ _SESSION [ 'Language']); / * * Treatment of verification form * / if (isset ($ _POST [ 'submitCheckLanguage'])) ( / * * To check the languages of all files, * Be put before XML files separately in a * Directory alone. Can you open this directory: * / $ I = 0; $ DirRes = opendir ( 'xml'); / / Then we read the names of the files one by one while (false! == ($ file = readdir ($ dirRes))) ( / / If we have an XML file If (strpos ($ file, '. Xml')) ( / / It saves the name in a table $ XmlTab [$ i + +] = $ file; ) ) / / Then if the table exists, we will check all files If (isset ($ xmlTab)) ( / / It saves the file used acutellement CFile $ = $ xml-> getCurrentFile (); / / Variable control over true default $ FileChecker = true; / / On the trail list file to verify Foreach ($ xmlTab as $ file) ( / / It changes the file covered in XMLEngine $ Xml-> changeFile ( 'xml /'. $ File); / * * Can we check its integrity with the function of the class * To return true or false. * / if ($ xml-> checkIntegrity ($ _POST [ 'languageToCheck'])) ( / * In case of failure on display a warning for each file * /?> <script Type="text/javascript"> <! -- alert ( "<? php echo $ xml-> fileCheckError. ':'. ;?>"); $ file //--> </ Script> <? Php $ fileChecker = false; / / Variable control over false ) ) / * * Then if the variable control permits, it updates the database * To bring the field IsAvailable '1 (or true) * / If ($ fileChecker) ( $ Fields [0] = 'IsAvailable' $ values [0] = true; $ sql-> updateQuery ( 'Languages', $ fields, $ values,' Language ',' = ', $ _POST [' languageToCheck ']); / * Can we display a message * /?> <script Type="text/javascript"> <! -- alert ( "<? php echo $ xml-> checkLanguageDone ;?>"); //--> </ Script> <? Php ) ) ) / * Beginning form hotel: * /?> <div align="center"> style="height:500px;overflow:scroll;" <form action="languageAvailability.php" method="post"> <table Border="1"> <tr> <td> <? PHP Echo $ xml-> languageToCheck;?> </ Td> </ Tr> <tr> <td> <? PHP / / It gets all the languages of the SQL database $ langs = $ sql-> selectQuery ( '*', 'Languages', 0,'','','',''); / * Can we create the list * /?> <select name="languageToCheck" style="width:100%;"> <? php Foreach ($ langs as $ language) (?> <option value = "<? php echo $ language [ 'Language'];?>"><? php echo $ xml-> ($ language [' Language '].' Lang '};?></ option> < ? PHP )?> </ Select> </ Td> </ Tr> <tr> <td> <input type = "submit" name = "submitCheckLanguage" value = "<? php echo $ xml-> checkIt;?>" /> </ Td> </ Tr> </ Table> </ Form> </ div>
|
|
|