Changes between Version 3 and Version 4 of Dev/i18n

07/28/11 11:47:30 (3 years ago)



  • Dev/i18n

    v3 v4  
    11= Internationalization: Quick Start = 
    3 The purpose of this page is to explain you in a few easy steps how to do internationalization in Indico, without going into too much details ( for more information on how it is implemented you may ave a look [wiki:here] 
     3From v0.98 on, Indico relies on [ Babel] in order to manage language dictionaries and provide some basic date formatting and other locale-related functions. 
    5 == Step 1: Find strings in Indico that have to be internationalized == 
     5== Writing i18n-aware code == 
    7 Of course you could ask every developer to always put _("...") around every string, but as it is possible to forget it, and because it has not always been done, there is a tool available for searching for strings that have not yet _("...") around them. We will explain you how to use it. 
     7It's very hard to write/maintain an application that is 100% internationalized, since this requires a great deal of coordination not only with the translators, but between developers as well. Not only people usually forget to properly internationalize strings, but also when they remember to do so they choose to do it at the wrong place. Here are some examples of some good/bad practices. 
     9For example: 
    10 path_to_indico/code/code/MaKaC/po/ code_dir 
     13class UserContainer(object): 
     14    _userTypes = { 
     15        # wrong! 
     16        'admin': _('Administrator'), 
     17        'regular': _('Regular user') 
     18    } 
     20    # ... 
     22    def getUserType(self): 
     23        # can you spot the problem? 
     24        return UserContainer._userTypes[self.user_type] 
    13  * path_to_indico : Is the path where your checkout of the source code of Indico is 
    14  * code_dir : For example path_to_indico/code/code, but you can be more specific an threat only one subdirectory at a time 
     27Why is this wrong? Class attributes are initialized only once, '''at module load time'''. So, since modules are normally only loaded once '''per process''', a French user that is using this app will ''theoretically'' (see paragraph below) get the same translation as a Japanese one. The '''right way''' to do so would be delaying translation till the template is rendered: we could have a dictionary of "English" strings and then just call `_()` from the piece of template (or other i18n-safe code) that calls `getUserType`. 
    16 What this program will do is to scan code_dir, recursively to find all .py and .tpl files and it will ask you each time it finds a string without _() to know if you want it to be translated 
     29Fortunately our current i18n code supports Lazy translation (meaning that it replaces the result string with a proxy object that will only execute the translation when it is asked for its string value), and the situation above would not be problematic. However, it '''not at all good practice''' and should be '''avoided'''. 
    18 If you want to continue on another day just press on Ctrl + C to tell the program to stop. The changes on the file you are currently working on will be undone, but all other files that you processed before keep their changes. Moreover, for a file that has been completely processed you wont get any questions next time. ( If you want to change this behavior, just delete the corresponding .bak file ) 
     31Another classic mistake is: 
    20 Note that when you encounter a string that has been encountered in a previous line or file, the program keeps track of your previous answer and proposes it as the default answer. 
    22 Moreover if you let the program run again over a file you have already processed in the past, it will remember where you answered no and will do so next time without asking ( This works only if the word occurs at the same line number ). 
     36from persistent import Persistent 
    24 If these history features cause you some kind of problems you can always pass an option to the program, to disable this feature ( Use the --help option to get detailed information on the different options you can set ). 
     38# ... 
    26 If you want to simply reset these histories, have a look at the learning_dict.pkl and no_dict.pkl, that lie in the current directory. 
    28 '''Warning: If your tpl file or python file is malformed, for example if it is not used very often and there are syntax errors in it, this tool is very likely to produce strange output. So you should always have a look at the diff of the files that are transformed!''' 
    30 == Step 2: Find all strings marked by _("...") and generate a template file for the human translators == 
    32 In a console type: 
    33 {{{ 
    34   cd path_to_indico/code/code 
    35   MaKaC/po/ 
     40class User(Persistent): 
     41    def __init__(self, name, position): 
     42        self.position = _(position) 
    38 This program will write a file named message.pot in the current working directory. This file is a template that you can give to a human translator so that he can fill in the empty msgstr's 
    40 Moreover it will propose you to fill it with already known translations. To do this it will ask you to choose a language (eg. en_US) and it will use msgmerge to create a file called messages_en_US.pot for example. 
    42 If you need to to have a template for another language, just run the program again and choose another language 
    44 == Step 3: Translate the remaining strings == 
    46 Well there's not much to say about this. You basically just have to edit the .pot files, fill in the missing translations, correct those with the mention fuzzy. 
    48 It is easily possible to do this with a simple text editor like vim, nano or emacs but there are some tools out there that where constructed just for this purpose. [wiki:Dev/i18n/editors Here are a few notes about their respective advantages and weaknesses]. 
    50 == Step 4: Install the .pot file == 
    52 There is another utility available to install your translations. To use it type the following lines in a console: 
    54 {{{ 
    55   cd path_to_indico/code/code 
    56   MaKaC/po/ path_to_pot_file 
    57 }}} 
    59 You will be asked again for the language, an then you will have to wait for msgmerge to terminate ( this can take a while ). 
    61 '''Note''': If when you are asked for the language, you do not find yours. Please, cancel the execution of the program, create a folder inside MaKaC/po/locale/ with the name of your language (e.g. MaKaC/po/locale/ru_RU) and run again. 
    63 == Step 5: Compile the .po files to .mo files == 
    65 This is the last step before your translation is made visible on your server. 
    67 In a console type: 
    69 {{{ 
    70   cd path_to_indico/code/code 
    71   MaKaC/po/ 
    72 }}} 
    74 == Step 6: Add your language to the menu in the web interface == 
    76 In the official release of Indico, we do only support English and French for the time being. If you want to be able to choose your own language, you will need to modify the file MaKaC/ and add your language to the dictionary languagesNames. Therefore 
    77 you transform this: 
    79 {{{ 
    80 languageNames = { 
    81     'en_US': 'English', 
    82     'fr_FR': 'Français' 
    83     } 
    84 }}} 
    86 into this: 
    88 {{{ 
    89 languageNames = { 
    90     'en_US': 'English', 
    91     'fr_FR': 'Français', 
    92     'ru_RU': 'Russian' 
    93     } 
    94 }}} 
     45This one should be easier to spot, if you consider that `Persistent` objects are stored in the database. By translating `position`, we get it in the language that was in use at the time of object creation. The right way to do it is, once again, to translate "as late as possible".