User:Lvl/Devel
Distributed gettext fixing effort
/Distributed gettext fixing effort
See also /I18n TODO list
Questions awaiting answers on the dev. list
Mails
Things I would like to work on
I'm listing below various things I whould like to work on. Since our code squirrels have very limited time, I'm giving a list of these here so that they can pick up on which topics they have time to review effective code and accept the code fix.
Fix automatically found gettext markup errors
review the comments I made in the dev list on Coachmike's code, because I saw that some of these comments were ignored. e.g. dp-devel/pinc/Stage.inc echo "<input type='submit' value='". _("Submit Changes") . "'>\n"; should use attr_safe() - fix the 64 occurrences of similar constructs found by grep -r ' [a-z]\{1,\}=['\''"]\{2\} *\. *_(' dp-devel run a similar perl program to find deviations to the coding Guidelines: while (<>) { s/\t/ /g; if (/^ *\$(\w+) *=.*_\(/) { $tainted{$1} = $_; } print if (/ [a-z]+ *= *'[^\\']*" *\. *_\(/); while (/ [a-z]+ *= *'[^\\']*\$(\w+)/g) { print if (exists $tainted{$1}); } while (/ [a-z]+ *= *'[^\\']*" *\. *\$(\w+)/g) { print if (exists $tainted{$1}); } }
Organise a list of files to be checked for gettext markup update
I have created a list on the wiki: http://www.pgdp.net/wiki/User:Lvl/Devel/Distributed_gettext_fixing_effort This is now waiting for feedback from code squirrels.
Task:1344 translator center
Implement step 0 of the strategy described in the dev. list.
Fix projectID validation
- check cases of $_GET['project and $POST_['project which are not validated with the new function validate_projectID() - consider testing using is_string() that $value is a real string (and not an array) in validate_projectID()?
htmlspecialchars without ENT_QUOTES
look for cases of using htmlspecialchars without ENT_QUOTES.
Get rid of addslashes()
- Many cases of addslashes inside SQL queries should be replaced with mysql_real_escape_string. - In other cases, there is just a confusion. Document the variables that are encoded specifically using addslashes in the database, in a separate documentation file, and for any variable not documented as using a special format, remove these addslashes calls.
Task:749 disallow binary characters and tabs in txt files
in function _normalize_page_text, file pinc/DPage.inc, add a regexp to replace rows of illegal characters with a single space char. Add possibly other regexps that will convert Windows special chars into our markup. make that dependent upon whether the site is in utf-8. Also replace addslashes with mysql_real_escape_string in file_content_expr() => check code by Michael in tools/project_manager/add_files.php and pinc/DPage.inc
Task:1201 Illustration tag button should remove the colon and space if nothing highlighted
This is a duplicate of 1300; close [[Task:1300]]. insert in dp_proof.js: // standard tag selection function surroundSelectionOrInsert(wOT,wCT,text) { markRef.markBox.value=wOT; markRef.markBoxEnd.value=wCT; insertTags(wOT,wCT,text,false); } and in toolbox.inc, replace "top.surroundSelection('[Illustration: ',']')" with "top.surroundSelectionOrInsert('[Illustration: ',']','[Illustration]')"
Translate popup help
([[Task:594]] faq/pophelp should be localized and "theme-able") At present popups are stand-alone html files grouped in directory. The idea is to replace each directory with one php file containing a big switch, depending on $_GET['id'], to branch to code that displays the popup help, using _(...). I have written a shell script that converts the html files into the relevant php code, and I suggest to fix pinc/js_newpophelp.inc so that the generated javascript function no longer links to a standalone html file, but to that php file, i.e. replace return " function newHelpWin(wFile) { var newFeatures='toolbar=0,status=0,location=0,directories=0,menubar=0,scrollbars=1,resizable=1,width=400,height=300,top=$top,left=$left'; window.open('$popHelpDir'+wFile+'.html','popHelp',newFeatures); } "; with $pop_help_php = "$popHelpDir?id="; $pop_title = attr_safe(_("popHelp")); return " function newHelpWin(wFile) { var newFeatures='toolbar=0,status=0,location=0,directories=0,menubar=0,scrollbars=1,resizable=1,width=400,height=300,top=$top,left=$left'; window.open('$pop_help_php'+wFile,'$pop_title',newFeatures); } "; The various popup php files will include file pophelp.inc: <?php include_once("site_vars.php"); // $charset ? include_once("gettext_setup.inc"); function do_popup($title, $content) { global $charset; echo "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n"; echo "<html " . lang_html_header() . ">\n"; echo "<head>\n"; echo "<title>" . sprintf(_("%s Help"), $title) . "</title>"; echo "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=$charset\" />\n"; echo "</meta>"; echo "<body>"; echo "<h2 style='text-align: center;'>$title</h2>"; echo $content; echo "<p style='text-align: center;'><b><a href='Javascript:window.close();'>" . _("Close Window") . "</a></b></p>"; echo "</body></html>" } ?>
Fix missing quotes in html attributes
Things like <tag attribute=value> are not supported in XHTML, and cause display problems in some browsers. I suggest to convert them to use quotes, e.g. <tag attribute="value"> or <tag attribute='value'>
cleaner handling of backslashes in page text inner interfaces
DPage.inc, PPage.inc, LPage.inc and processtext.php: change the interface for saveAsInProgress() and saveAsDone() so that page text is passed unslashed, and mysql_real_escape_string is used directly close to the building of the SQL query.
minor optimisations in page_table.inc
in tools/project_manager/page_table.inc, - replace all $code_url/tools/project_manager/ with "" - replace style='text-align: right' with class='r' and add this in function echo_pagetable_stylesheet() table.pagedetail td.r { text-align: right; } - remove space in echo "<td $cell_class_attr> and put the space in $cell_class_attr - cache values of gettext for frequent links _("Edit"), _("no diff"), and so on. - href=$url => put quotes around it. - cache the value of the correct bad state for the current round, instead of // Bad Page $page_is_in_bad_state = page_state_is_a_bad_state($page_state); - cache the value of the cell describing the url to send message + number of pages proofed by a user in a round.
fix html ampersand in urls
tidy complains about things like &foo=... (should be &foo=) But beware of functions handling urls in misc.inc
translate dates
check calls to date() and replace them with calls to strftime()
deprecated functions
Deprecated functions in PHP 5.3.0: mysql_escape_string() POSIX regex => replace that with pcre regex. affected functions: ereg(), eregi(), ereg_replace(), eregi_replace() (we don't seem to call split, spliti or sql_regcase) dl() (used by the difference engine) and of course the magic quotes stuff.
get rid of \w and \W which locale dependant
- re PHP manual: "The definition of letters and digits is controlled by PCRE's character tables, and may vary if locale-specific matching is taking place. For example, in the "fr" (French) locale, some character codes greater than 128 are used for accented letters, and these are matched by \w." => find all occurrences of \W and \w and replace them with the actual meanings in the C locale. Theoretically \b and \B are affected too.
latin-1 upper/lowercase in javascript
javascript functions to convert to lowercase or uppercase, taking care of the latin-1 accented characters. function translit(left, right, str) { for ( var i = 0, l = right.length; i < l; i++ ) { var repl = new RegExp(left.charAt(i), "g") var subs = right.charAt(i); str = str.replace(repl, subs); } return str; } function uppercase(str) { return translit( "àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþ", "ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ", str.toupper()) . replace(/ÿ/g, "[\"Y]"); } function lowercase(str) { return translit( "ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ", "àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþ", str.replace(/\["Y\]/g, "ÿ").tolower()) ; }
minor fix
page_table.inc, line 369 $page_is_in_bad_state = page_state_is_a_bad_state($page_state); this calls a loop for each page; would be better to compute first the bad state for this round, out of the loop, and within the loop test if the page state is equal to that bad state.
fix to adapt to number of rounds > 2
automodify.php // In round 2, if it has any bad pages // and no available pages, it's bad. // if ($round->round_number == 2) put a number >= 2
Task:1181 Add a link to change the proofreading preferences
add the link in ctrl_frame.php, something like <?php $url = "..."; echo "<a style=\"color:#0000FF; text-decoration: underline;\" href='$url' target='_blank'>"; echo _('Proofreading Preferences')."</i>\n"; echo "</a>\n" ?>
htmlspecialchars missing
pinc/filter_project_list.inc in function _build_project_filter_select, protect the variables using htmlspecialchars(..., ENT_QUOTES) in these two lines: $return.="value='$value'"; $return.="value='$option'";
translatable genres
genre should be made an associative array, for translating purposes. pinc/filter_project_list.inc: 1) put near the top of file: include_once("genres.inc"); 2) replace around line 225 with if($field == "special_code" || $field=="difficulty" || $field=="genre") 3) in function _load_project_filter_field_values, put global $GENRES; and add the three lines below inside the while loop: else if ($field == "genre") $return[$a_res[0]] = array_key_exists($a_res[0], $GENRES)) ? $GENRES[$a_res[0]] : $a_res[0];
in tools/proofers/ctrl_frame.php, after .dropchars { background-color:#EEDFCC; insert the result of function css_for_font_pref() to be defined in PPage.inc: function css_for_font_pref() { $css = ""; if ( $userP['i_layout']==1 ) { // "vertical" $font_face_i = $userP['v_fntf']; $font_size_i = $userP['v_fnts']; } else { // "horizontal" $font_face_i = $userP['h_fntf']; $font_size_i = $userP['h_fnts']; } $font_face = $f_f[$font_face_i]; $font_size = $f_s[$font_size_i]; if ( $font_face != '' && $font_face != BROWSER_DEFAULT_STR ) $css .= "font-family: $font_face; "; if ( $font_size != '' && $font_size != BROWSER_DEFAULT_STR ) $css .= "font-size: $font_size;"; return $css }
Task:630 Suppress/fix Mentor Projects display when no projects available
pinc/mentorbanner.inc comment: "correctly display singular v. plural of days when age is one day (task #630)" replace printf(_("Oldest English %sMENTORS ONLY%s book in %s is %d days old."), "<a href='$wiki_url/Mentoring'>", "</a>", $round_id, $oldest); with printf( ngettext( "Oldest English <a href="%1\$s"MENTORS ONLY</a> book in %2\$s is %3\$d day old." "Oldest English <a href="%1\$s"MENTORS ONLY</a> book in %2\$s is %3\$d days old." $oldest), '$wiki_url/Mentoring', $round_id, $oldest); and implement a replacement for ngettext if not present, in pinc/gettext_setup.inc function ngettext($singular, $plural, $count) { // 0 days, 1 day, 2 days. return ($count == 1) ? $singular : $plural; }
Task:187 Allow to move a page status to avail even if it is not in save
=> suggested implementation is to use Page_Reclaim() instead of Page_clearRound() on these pages. => modify page_operations.inc, function page_clear(), and page_details.inc
Task:580 Glottal stop in team name not supported
Actually team stuff is pretty buggy. The [i] and [b] markup seems to be implemented rather inconsistently. I suggest to: - check the number of teams using [b] and [i], - remove that functionality in team names - use the proper escaping mechanism instead of stripAllString
Task:662 Count smoothreading uploads & icon for smoothreaders
Icon: I've drafted an icon representing a comfortable seat. Will open a topic on the general forum to allow other, more talented users, to agree on a specific icon. tallies: Do we want to count the number of projects, or to count the cumulative number of pages in these projects? I'm assuming we also want to keep the dates, and not only the cumulative number of pages/projects. If it's the number of pages, then we can reuse the page statistics stuff and also display neighbours, average page per day, and diagrams easily. But it looks a bit weird. So, I suggest rather to count the number of projects. Displaying the average number of projects smoothread per day would be a bit silly... So then I suggest to count statistics per months instead, reusing part of the page_tally.inc code that is still relevant (e.g. neighbours), and create new code to handle monthly statistics instead. (note that these new monthly project statistics could also later include PP and PPV statistics) There is no need to add a field in table smoothread to indicate if a project was already uploaded; we can determine that in the filesystem. In tools/upload_text.php, if the field is not set then update a book tally: if ($stage == 'smooth_done') { if (... not uploading the second version of the same project ...) { $users_SR_tallyboard = new TallyBoard( 'SR', 'U' ); $users_SR_tallyboard->add_to_tally( $userP['user_id'], 1 ); } then set the field to 1. } In stats/include/member.inc, function showMbrRoles(), add this to display a smoothreader icon (displayed when one book has been uploaded) $users_SR_tallyboard = new TallyBoard( 'SR', 'U' ); if ($users_SR_tallyboard->get_current_tally($curMbr['u_id']) > 0) { $roles[] = array( 'sr', _("Smooth reader"), 25 ); } Show the PP, PPV and SR statistics also, counted as number of pages or number of books? Change showMbrTallySelector() Add also a echo _("Display user stats for different activities:") . ' ' . $choices2; where choices2 contains also PP, PPV, SR?
Task:709 layout of diff page / links to images
Suggest adding checkboxes on top of page, showing possible rounds and displayed rounds; and to only display by default the current and previous round. That would save bandwidth and time for 95% of uses imho. I'm not sure suppressing the dates for completed rounds is a good idea, because often you're interested in finding out if a particular proofer has been click-throughing the pages. Implementation notes: move the $rounds_to_display = get_rounds_with_data( $projectid ); out of tools/project_manager/page_table.inc, and pass $rounds_to_display as argument to function echo_page_table(). In tools/project_manager/page_detail.php, add the checkbox stuff with a button that refreshes the display.
Task:728 Show number of books available for Smooth Reading on activity hub
Implementation suggestion: in pinc/Stage.inc, add an other parameter which is a SQL where clause on the project table. $project_where_clause // The optional SQL where clause on project table for projects // in this stage. (put NULL if not relevant) in the creators for Round and Pool, supply NULL for this parameter. in pinc/stages.inc, in the definition of stage "SR", put: "state = 'proj_post_first_checked_out' AND smoothread_deadline > UNIX_TIMESTAMP()" in activity_hub.php, function summarize_stage(), if the stage is neither a Pool nor a Round, and if $project_where_clause is set, run the SQL query, possibly with the filter, in order to calculate the total number of projects, instead of the code currently between // Calculate the total number of projects. and // Output the table row.
Task:839 Greek translit. box - rho misread as psi - small font
Trivial. tools/proofers/greek2ascii.php replace 'h' and 'r' with '<code>h</code>' and '<code>r</code>'; replace <emp> (which doesn't exist) with <em>.
Task:858 Monitor file sizes in add_files.php
(need a site setting, warn at a large size, prevent at a still larger size?) in tools/project_manager/add_files.php, replace if ( $action == 'error' ) { $this->n_errors++; $error_msgs .= "$error_msg\n"; } with if ( $action == 'error' ) { $this->n_errors++; } if ( $error_msg != '' ) { $error_msgs .= "$error_msg\n"; } and have the function _get_action report warning messages for big sizes. replace in _check_file() else { if ( $ext != ".txt" && (filesize($filename) < 100) ) { return _('image file is small and probably bad'); } } with: if ($ext != ".txt") { $filesize = filesize($filename); if ($filesize < 100) ... else if ($filesize > SOME_LIMIT) { return sprintf(_("Image file is too big (more than %d kb)"), SOME_LIMIT); } } in _get_action(), after else { assert( FALSE ); } put $warning = ''; if (filesize($filename) > SOME_LIMIT) { $warning = sprintf(_("Warning: Image file is very big (more than %d kb)"), SOME_LIMIT); } } ** consider also to whom whould a warning be issued? A mailing list?
Task:918 Typo in german interface textes
The task center is not the right place to address issues with translations in a particular language. Implementation suggestion: in pinc/languages.inc, and pinc/lang_data.inc, in addition to $lang_forum_data=array( ); add an array storing the forum topic to report bugs with the translation $lang_translation_issue_forum_data=array( ); in pinc/theme.inc, after the code to display "Report a bug", add code to display a _("Report an issue with the current translation") if that link exists for that language. Ideally the code for creating or editing a language in locale/translators should be able to edit these fields, so ideally the file lang_data.inc should be outside CVS?
Task:926 Show rounds in which pages have been proofed
Implementation suggestion, in project.php, at the end of function do_page_summary() $rounds_with_data = get_rounds_with_data( $projectid ); echo "<p>" . _("<p>The project gone through the following rounds:") . " " . implode(" ", $rounds_with_data) . "</p>"; (perhaps later add a summary of rounds and possibly user names?)
Task:1015 Repeat the "in progress" pages near the "start proofreading"
A simpler solution is to not display the "start proofreading" link on top if there are pages in progress. add this function before function recentlyproofed( $wlist ) function user_has_pages_in_progress() { global $project, $pguser; $round = get_Round_for_project_state($project->state); assert($round); $state_condition = "(state='{$round->page_temp_state}' OR state='{$round->page_out_state}')"; return mysql_query("SELECT 1 FROM $project->projectid WHERE {$round->user_column_name}='$pguser' AND $state_condition LIMIT 0"); } and add something like this test case before line 354 else if ( user_has_pages_in_progress() ) { // Even though we assume the user has read the new comments, // user is probably willing to finish pages in progress first. $top_blurb = _("You may wish to finish pages which are in progress before proofreading new pages."); . "<br>" . $please_scroll_down . "<br>" . $comments_last_modified_blurb . "<br>" . _("The 'Start Proofreading' link and the list of your recent pages in progress appear below the Project Comments");
Task:1106 Make "catalog" page pageable or remove it
Suggest to remove entire catalog/index and catalog/get_page_text. Unlinked from our CVS code, virtually useless, and bugged.
Task:1147 possibility of deleting some of "my projects"
(related to Task 1208). Implementation suggestion: store 0 in t_latest_home_visit when a user wants to "forget" about a project. A user forgets a project by clicking in a checkbox in his my projects page, and then a button _("Do not show selected projects any more"). Only do that when the user has no pages in progress or out for that project. add AND user_project_info.t_latest_home_visit <> 0 in the SQL query for "my project" (excluding the special PF/admin feature of "my projects").
Task:1207 Show how many people have already Returned Page to Round
(7 votes) If we add in page_events an index on (projectid, username, image, round, event_type), I don't see why the request should be extremely slow. It would also enable to avoid proposing the same page to the same user when already returned to the round Question: would the queries be faster if the user numerical ID were stored in table page_events instead of user name? On the other hand, we could implement a degraded version of the feature: when returning a page to the round, increase a counter stored in field b_user (that field is unused when the page is not marked as bad). Saving a page as done does reset this counter. The counter would then hold the number of times people (possibly the same user) have returned that page to the round. Run a cron job to fetch the pages having this counter set to a high value periodically?
Task:1208 Projects incorrectly added to My Projects
Implementation suggestion: in pinc/DPage.inc, in Page_returnToRound, and possibly also Page_reclaim and Page_markAsBad, if there are no other pages by this user in this project, then set t_latest_page_event to zero for that project, so that the project will not appear in the "My Projects" list for that user.
I considered doing it also for Page_clearRound, but I suppose the proofer might be interested in going back to that project when the PM has notified that pages have been cleared.
Task:1263 search/replace undo also undoes other formatting
Well, what is clear is that it is not possible in all cases to revert the changes made by the search/replace while keeping the other changes made. This is because the system does not keep a history of changes made, it just changes the entire text window. What can be implemented is to ask for confirmation if the text has been further edited (i.e. is different) after the replace took place.
Task:1282 Zipped image file creation fails when project has 2048+ image files
suggest replacing in file tools/download_images.php exec( "zip -n .png:.jpg -q -j $zipfile_path $projectpath/*.png $projectpath/*.jpg", $output, $return_code ); with exec( "find $projectpath -name '*.png' -or -name '*.jpg' | xargs zip -n .png:.jpg -q -j $zipfile_path", $output, $return_code );
Task:1304 Option of downloading just the proofing images for a project.
In project.php, add after if ( $discriminator == 'images' ) { ... } else if ( $discriminator == 'page_images' ) // Generate images zip on the fly, so it's not taking up space on the disk. $url = "$code_url/tools/download_images.php?projectid=$projectid" . "&pages_only=1" . "&dummy={$projectid}_page_images.zip"; // The 'dummy' parameter is for the benefit of download-software that // names the resulting file after the last component of the request URL. // To gain speed, images shall not be compressed in the zip, // so the sum of their individual filesizes is a fair approximation of // the size of the resulting zip. $filesize_b = 0; $result = mysql_query(" SELECT image FROM $projectid ORDER BY image "); while($row = mysql_fetch_row($result)) { filesize_b += filesize("$project->dir/$row[0]"); } and put this in download_images.php. if (isset($_GET['pages_only'])) { $result = mysql_query(" SELECT image FROM $projectid ORDER BY image "); $zip_cmd = "zip -n .png:.jpg -q -j $zipfile_path "; $zip_args = ""; $n_args = 0; while ($row = mysql_fetch_row($result)) { if ($nargs > 100) { exec("$zip_cmd$zip_args"); $n_args = 0; $zip_args = ""; } $zip_args .= " $projectpath/$row[0]"; $nargs ++; } if ($nargs != 0) exec("$zip_cmd$zip_args"); } else { ... }
Task:1318 Concatenated text download - only own pages
add in project.php: echo "Export only my pages? "; echo "<input type='radio' name='this_user_only' value='1' />"; echo "Yes "; echo "<input type='radio' name='this_user_only' value='0' CHECKED />"; echo "No<br />\n"; in tools/project_manager/generate_post_files.php, add $this_user_only = get enumerated parameter ... @$_REQUEST['this_user_only']; generate_interim_file( $projectid, $round_id, $which_text, $include_proofers, $this_user_only); in tools/project_manager/post_files.inc, add parameter $this_user_only to function generate_interim_file($project, $limit_round_id, $which_text, $include_proofers) replace $pages_res = page_info_query($project, $limit_round_id, $which_text); with $pages_res = page_info_query($project, $limit_round_id, $which_text, $this_user_only); replace function page_info_query( $projectid, $limit_round_id, $which_text ) with function page_info_query( $projectid, $limit_round_id, $which_text, $this_user_only = 0 ) and put if ($this_user_only != 0) { $where = "WHERE '$pguser' IN (" for ( $rn = 1; $rn <= $limit_round->round_number; $rn++ ) $where .= ($rn > 1? ", " : "") . $round->user_column_name; $where .= ")" } else $where = ""; $res = mysql_query(" SELECT $text_column_expr, image $user_fields FROM $projectid $where ORDER BY image "); at the end of page_info_query.
Task:1338 Allow PPer to use the skip recommendation tool on their projects
(I don't remember if this has been implemented yet) In noncvs/project_retread_skip_recommendations.php, replace // confirm the user can edit the project $ucep_result = user_can_edit_project($projectid); if(!($ucep_result == USER_CAN_EDIT_PROJECT || $pguser == 'piggy')) with // confirm the user may run the script $user_allowed = (user_can_edit_project($projectid) == USER_CAN_EDIT_PROJECT); // also allow the reserved PPer. That will also include PPVers when the project // is in PPV, but it doesn't matter. $user_allowed = $user_allowed || mysql_query("SELECT 1 FROM projects WHERE projectid = '$projectid' AND checkedoutby = '$pguser' LIMIT 0"); if(!($user_allowed || $pguser == 'piggy'))
pinc/SettingsClass.inc
Given this code: switch($settingCode) { default: return true; case 'sitemanager': case 'manager': case 'postprocessor': return false; } use an alternate logic so that sitemanager, manager and postprocessor can be set using the same function (using table users instead of table usersettings)
field postprocessor in users table
That field appears to be unused (superseded with PP.access in usersettings) Suggest to remove it in the next release.
Translate emails
In theory when a mail is being sent to someone else, we should proceed thus: $temp = mysql_query("SELECT email, u_intlang FROM users WHERE username = '...'"); $email = mysql_result($temp, 0, "email"); $temp_intlang = mysql_result($temp, 0, "u_intlang"); // temporarily change the current locale to the recipient's language. setlocale(LC_ALL, $temp_intlang); // build the mail subject and body, in the current localt $body = _("..."); $subject = _("..."); // Send the mail using $subject and $body // restore the current locale global $intlang; setlocale(LC_ALL, $intlang); So, check all use of function maybe_mail() and distinguish two cases: 1) mail is sent to a mailing list: then in conjunction with the email address global variable, add an email language global variable, e.g. in addition to $email_addr = $promotion_requests_email_addr; add $email_lang = $promotion_requests_intlang; and then use setlocale(LC_ALL, $email_lang) before building the body and subject. (that allows in theory another site to have its mailing lists in a different language, while sharing the same code) Or to simplify we could use the same intlang value for all administrative purposes, and define a global variable $site_admin_intlang; 2) mail is sent to an individual (a proofreader, project manager, etc.); use function $email = get_email_and_setlocale($username) before building the subject and body. Similarly one could define a function get_email_and_setlocale_project_manager($project) if needed. And do not forget to restore the language after maybe_mail().
gettext ersatz
Unless nobody objects, I intend to move the replacement for _() that is currently in pinc/site_vars.php into pinc/gettext_setup.inc, and also to add there a replacement for ngettext() in case the gettext extension is not installed.
userprefs and real name
- strip slashes on the user real name and user htmlspecialchars(, ENT_QUOTES) around $current_value in userprefs.php function _show_textfield
special colors
pinc/special_colors.inc actually dumps the entire special color database in an associative array, merely to check if a key exists in that array, and this for each books listed in the round pages. This array should be cached for efficiency.
tools/proofer/diff.php
creates a new Project for the same project ID several times. Should pass the Project object along for efficiency.