3 * Display the admin/language menu and process strings translation.
8 * 1) Fixed bug 5745 reported by Harry Smith so now can edit en_utf8_local pack
9 * 2) More notification messages included
12 * Merged with version by Mitsuhiro Yoshida - display icon links instead of text links
13 * (I have changed the position of arrow icons to not to be so close each other)
16 * A lot of changes to support translation of *_utf8_local langugage packs. Needs testing
19 * 1) LANG_DEFAULT_FILE can be now set. moodle.php used to be opened automatically.
20 * As it was (and still is) one of the biggest files it usually took a long time to load the page
21 * even if you just want to choose the file to translate.
22 * 2) added links from "missing" to "compare" mode page
23 * 3) english strings are now key-sorted in "missing" mode
24 * 4) list of files with missing strings is now displayed at the top of "missing" page
26 * 2006/05/14 mudrd8mz Improvements of lang.php,v 1.65 2006/04/10 22:15:57 stronk7 Exp
27 * 1) "go to first missing string" link can be displayed (see LANG_DISPLAY_MISSING_LINKS)
28 * 2) "go to next missing" link can be displayed (see LANG_DISPLAY_MISSING_LINKS)
29 * 3) submit button may be repeated (see LANG_SUBMIT_REPEAT*)
30 * 4) added (empty) "summary" attribute for the <table>'s
31 * 5) added error_reporting(E_ALL ^ E_NOTICE); in "compare" mode (even in debug environment
32 * we know that missing keys are missing strings here, not bugs ;-)
35 require_once('../config.php');
37 define('LANG_SUBMIT_REPEAT', 1); // repeat displaying submit button?
38 define('LANG_SUBMIT_REPEAT_EVERY', 20); // if so, after how many lines?
39 define('LANG_DISPLAY_MISSING_LINKS', 1); // display "go to first/next missing string" links?
40 define('LANG_DEFAULT_FILE', ''); // default file to translate. Empty allowed
41 define('LANG_LINK_MISSING_STRINGS', 1); // create links from "missing" page to "compare" page?
42 define('LANG_DEFAULT_USELOCAL', 0); // should *_utf8_local be used by default?
44 $mode = optional_param('mode', '', PARAM_ALPHA
);
45 $currentfile = optional_param('currentfile', LANG_DEFAULT_FILE
, PARAM_FILE
);
46 $uselocal = optional_param('uselocal', -1, PARAM_INT
);
48 if ($uselocal == -1) {
49 if (isset($SESSION->langtranslateintolocal
)) {
50 $uselocal = $SESSION->langtranslateintolocal
;
52 $uselocal = LANG_DEFAULT_USELOCAL
;
55 $SESSION->langtranslateintolocal
= $uselocal;
61 error("You need to be admin to edit this page");
64 if (! $site = get_site()) {
65 error("Site not defined!");
68 $stradministration = get_string("administration");
69 $strconfiguration = get_string("configuration");
70 $strlanguage = get_string("language");
71 $strcurrentlanguage = get_string("currentlanguage");
72 $strmissingstrings = get_string("missingstrings");
73 $streditstrings = get_string("editstrings", 'admin');
74 $stredithelpdocs = get_string("edithelpdocs", 'admin');
75 $strthislanguage = get_string("thislanguage");
76 $strgotofirst = get_string('gotofirstmissing','admin');
77 $strfilestoredin = get_string('langstorein', 'admin');
78 $strfilestoredinhelp = get_string('langstoreinhelp', 'admin');
79 $strswitchlangdirbtn = get_string('langstoreswitch', 'admin');
80 $strchoosefiletoedit = get_string('langchoosefile', 'admin');
81 $streditennotallowed = get_string('langnoeditenglish', 'admin');
82 $strfilecreated = get_string('langfilecreated', 'admin');
85 // remove following lines after adding string into proper english lang pack
86 $strgotofirst = 'go to first missing string';
87 $strfilestoredin = 'Save file into folder :';
88 $strfilestoredinhelp = 'Where the file will be stored';
89 $strswitchlangdirbtn = 'switch';
90 $strchoosefiletoedit = 'Choose file to edit from the box above';
91 $streditennotallowed = 'Language en_utf8 cannot be edited with this page - switch to local';
92 $strfilecreated = 'New file created';
94 $currentlang = current_language();
98 // Missing array keys are not bugs here but missing strings
99 error_reporting(E_ALL ^ E_NOTICE
);
100 $navigation = "<a href=\"lang.php\">$strlanguage</a> -> $strmissingstrings";
101 $title = $strmissingstrings;
102 $button = '<form target="'.$CFG->framename
.'" method="get" action="'.$CFG->wwwroot
.'/'.$CFG->admin
.'/lang.php">'.
103 '<input type="hidden" name="mode" value="compare" />'.
104 '<input type="submit" value="'.$streditstrings.'" /></form>';
107 $navigation = "<a href=\"lang.php\">$strlanguage</a> -> $streditstrings";
108 $title = $streditstrings;
109 $button = '<form target="'.$CFG->framename
.'" method="get" action="'.$CFG->wwwroot
.'/'.$CFG->admin
.'/lang.php">'.
110 '<input type="hidden" name="mode" value="missing" />'.
111 '<input type="submit" value="'.$strmissingstrings.'" /></form>';
114 $title = $strlanguage;
115 $navigation = $strlanguage;
121 print_header("$site->shortname: $title", "$site->fullname",
122 "<a href=\"index.php\">$stradministration</a> -> ".
123 "<a href=\"configure.php\">$strconfiguration</a> -> $navigation",
124 '', '', true, $button);
127 print_simple_box_start('center','80%');
128 echo '<table summary="" align="center" width="100%"><tr><td width="50%" align="center">';
129 print_string('managelang','admin');
130 echo '</td><td align="center" width="50%">';
131 print_string('editlang','admin');
132 echo '</td></tr><tr><td>';
133 print_string('lang16notify','admin');
134 echo '<p /><a href="langimport.php">'.get_string('langimport','admin').'</a>';
136 $currlang = current_language();
137 $langs = get_list_of_languages();
138 echo "<table summary=\"\" align=\"center\"><tr><td align=\"right\">";
139 echo "<b>$strcurrentlanguage:</b>";
141 echo popup_form ("$CFG->wwwroot/$CFG->admin/lang.php?lang=", $langs, "chooselang", $currlang, "", "", "", true);
142 echo '</td></tr><tr><td colspan="2">';
143 $options["lang"] = $currentlang;
144 //print_single_button("http://moodle.org/download/lang/", $options, get_string("latestlanguagepack"));
145 echo "</td></tr></table>";
146 print_heading("<a href=\"lang.php?mode=missing\">$strmissingstrings</a>");
147 print_heading("<a href=\"lang.php?mode=compare\">$streditstrings</a>");
148 print_heading("<a href=\"langdoc.php\">$stredithelpdocs</a>");
149 echo '</td></tr></table>';
150 print_simple_box_end();
155 // Get a list of all the root files in the English directory
157 $enlangdir = "$CFG->dirroot/lang/en_utf8";
158 if ($currentlang == 'en_utf8') {
159 $langdir = $enlangdir;
161 $langdir = "$CFG->dataroot/lang/$currentlang";
163 $locallangdir = "$CFG->dataroot/lang/{$currentlang}_local";
165 if (! $stringfiles = get_directory_list($enlangdir, "", false)) {
166 error("Could not find English language pack!");
169 foreach ($stringfiles as $key => $file) {
170 if (substr($file, -4) != ".php") { //Avoid non php files to be showed
171 unset($stringfiles[$key]);
173 if ($file == "langconfig.php") { //Avoid langconfig.php to be showed
174 unset($stringfiles[$key]);
178 if ($mode == "missing") {
179 if (!file_exists($langdir)) {
180 error ('to edit this language pack, you need to put it in '.$CFG->dataroot
.'/lang');
183 // Following variables store the HTML output to be echo-ed
187 // For each file, check that a counterpart exists, then check all the strings
188 foreach ($stringfiles as $file) {
190 include("$enlangdir/$file");
197 if (file_exists("$langdir/$file")) {
198 include("$langdir/$file");
202 // notify(get_string("filemissing", "", "$langdir/$file"));
203 $o .= '<div class="notifyproblem" align="center">'.get_string("filemissing", "", "$langdir/$file").'</div><br />';
210 foreach ($enstring as $key => $value) {
211 if (empty($string[$key]) and $string[$key] != "0") { //bug fix 4735 mits
212 $value = htmlspecialchars($value);
213 $value = str_replace("$"."a", "\\$"."a", $value);
214 $value = str_replace("%%","%",$value);
216 $m .= "<a href=\"lang.php?mode=missing#$file\">$file";
217 $m .= $fileismissing ?
'*' : '';
218 $m .= '</a> ';
219 $o .= "<p><a name=\"$file\"></a><b>".get_string("stringsnotset","","$langdir/$file")."</b></p><pre>";
221 $somethingfound = true;
224 if (LANG_LINK_MISSING_STRINGS
) {
225 $missinglinkstart = "<a href=\"lang.php?mode=compare&currentfile=$file#missing$missingcounter\">";
226 $missinglinkend = '</a>';
228 $missinglinkstart = '';
229 $missinglinkend = '';
231 $o .= "$"."string['".$missinglinkstart.$key.$missinglinkend."'] = \"$value\";<br />";
235 $o .= '</pre><hr />';
241 print_simple_box_start("center", "80%");
242 echo '<center><font size="2">';
244 echo '</font></center>';
245 print_simple_box_end();
249 if (! $files = get_directory_list("$CFG->dirroot/lang/en_utf8/help", "CVS")) {
250 error("Could not find English language help files!");
253 foreach ($files as $filekey => $file) { // check all the help files.
254 if (!file_exists("$langdir/help/$file")) {
255 echo "<p><font color=\"red\">".get_string("filemissing", "", "$langdir/help/$file")."</font></p>";
256 $somethingfound = true;
261 if (! $files = get_directory_list("$CFG->dirroot/lang/en_utf8/docs", "CVS")) {
262 error("Could not find English language docs files!");
264 foreach ($files as $filekey => $file) { // check all the docs files.
265 if (!file_exists("$langdir/docs/$file")) {
266 echo "<p><font color=\"red\">".get_string("filemissing", "", "$langdir/docs/$file")."</font></p>";
267 $somethingfound = true;
272 if (!empty($somethingfound)) {
273 print_continue("lang.php");
275 notice(get_string("languagegood"), "lang.php");
278 } else if ($mode == "compare") {
280 if (!$uselocal && !file_exists($langdir)) {
281 if (!lang_make_directory($langdir)) {
282 error('ERROR: Could not create directory '.$langdir);
284 echo '<div class="notifysuccess" align="center">Created directory '.
285 $langdir .'</div>'."<br />\n";
288 if ($uselocal && !file_exists($locallangdir)) {
289 if (!lang_make_directory($locallangdir)) {
290 echo '<div class="notifyproblem" align="center">ERROR: Could not create directory '.
291 $locallangdir .'</div>'."<br />\n";
294 echo '<div class="notifysuccess" align="center">Created directory '.
295 $locallangdir .'</div>'."<br />\n";
299 if (isset($_POST['currentfile'])){ // Save a file
300 if (!confirm_sesskey()) {
301 error(get_string('confirmsesskeybad', 'error'));
304 $newstrings = array();
306 foreach ($_POST as $postkey => $postval) {
307 $stringkey = lang_file_string_key($postkey);
308 $newstrings[$stringkey] = $postval;
311 unset($newstrings['currentfile']);
314 include("$langdir/$currentfile");
315 if (isset($string)) {
316 $packstring = $string;
318 $packstring = array();
321 $saveinto = $locallangdir;
323 $packstring = array();
324 $saveinto = $langdir;
327 if (lang_save_file($saveinto, $currentfile, $newstrings, $uselocal, $packstring)) {
328 notify(get_string("changessaved")." ($saveinto/$currentfile)", "green");
330 error("Could not save the file '$saveinto/$currentfile'!", "lang.php?mode=compare&currentfile=$currentfile");
335 print_heading_with_help($streditstrings, "langedit");
337 print_simple_box_start("center", "80%");
338 echo '<center><font size="2">';
339 foreach ($stringfiles as $file) {
340 if ($file == $currentfile) {
341 echo "<b>$file</b> ";
343 echo "<a href=\"lang.php?mode=compare&currentfile=$file\">$file</a> ";
346 echo '</font></center>';
347 print_simple_box_end();
350 print_simple_box_start("center", "80%");
351 echo '<center>'.$strfilestoredin.' <strong>';
352 echo $uselocal ?
"{$currentlang}_local" : $currentlang;
354 helpbutton('langswitchstorage', $strfilestoredinhelp, 'moodle');
356 echo '<form target="'.$CFG->framename
.'" method="get" action="'.$CFG->wwwroot
.'/'.$CFG->admin
.'/lang.php">'.
357 '<input type="hidden" name="mode" value="compare" />'.
358 '<input type="hidden" name="currentfile" value="'.$currentfile.'" />'.
359 '<input type="hidden" name="uselocal" value="'.(1 - $uselocal %
2).'" />'.
360 '<input type="submit" value="'.$strswitchlangdirbtn.'" />'.
362 print_simple_box_end();
364 if ($currentfile <> '') {
365 $saveto = $uselocal ?
$locallangdir : $langdir;
367 if (!file_exists("$saveto/$currentfile")) {
368 if (!@touch
("$saveto/$currentfile")) {
369 print_heading(get_string("filemissing", "", "$saveto/$currentfile"), "center", 4, "error");
371 print_heading($strfilecreated, "center", 4, "notifysuccess");
374 if ($currentlang == "en_utf8" && !$uselocal) {
376 print_heading($streditennotallowed, 'center', 4);
377 } elseif ($f = fopen("$saveto/$currentfile","r+")) {
382 echo "<p><font size=\"1\">".get_string("makeeditable", "", "$saveto/$currentfile")."</font></p>";
384 error_reporting($CFG->debug
);
386 print_heading("$currentfile", "center", 4);
387 if (LANG_DISPLAY_MISSING_LINKS
&& $editable) {
388 print_heading('<a href="#missing1">'.$strgotofirst.'</a>', "center", 4);
392 include("$enlangdir/$currentfile");
394 if ($currentlang != 'en' and $currentfile == 'moodle.php') {
395 $enstring['thislanguage'] = "<< TRANSLATORS: Specify the name of your language here. If possible use Unicode Numeric Character References >>";
396 $enstring['thischarset'] = "<< TRANSLATORS: Specify the character set of your language here. Note that all text created while this language is active will be stored using this character set, so don't change it once you have set it. Example: iso-8859-1 >>";
397 $enstring['thisdirection'] = "<< TRANSLATORS: This string specifies the direction of your text, either left-to-right or right-to-left. Insert either 'ltr' or 'rtl' here. >>";
398 $enstring['parentlanguage'] = "<< TRANSLATORS: If your language has a Parent Language that Moodle should use when strings are missing from your language pack, then specify the code for it here. If you leave this blank then English will be used. Example: nl >>";
404 @include
("$locallangdir/$currentfile");
405 $localstring = isset($string) ?
$string : array();
408 @include
("$langdir/$currentfile");
411 echo "<form name=\"$currentfile\" action=\"lang.php\" method=\"post\">";
413 echo "<table summary=\"\" width=\"100%\" cellpadding=\"2\" cellspacing=\"3\" border=\"0\" class=\"generalbox\">";
416 foreach ($enstring as $key => $envalue) {
418 if (LANG_SUBMIT_REPEAT
&& $editable && $linescounter % LANG_SUBMIT_REPEAT_EVERY
== 0) {
419 echo '<tr><td colspan="2"> <td><br />';
420 echo ' <input type="submit" name="update" value="'.get_string('savechanges').': '.$currentfile.'" />';
421 echo '<br /> </td></tr>';
423 $envalue = nl2br(htmlspecialchars($envalue));
424 $envalue = preg_replace('/(\$a\-\>[a-zA-Z0-9]*|\$a)/', '<b>$0</b>', $envalue); // Make variables bold.
425 $envalue = str_replace("%%","%",$envalue);
426 $envalue = str_replace("\\","",$envalue); // Delete all slashes
429 echo '<td dir="ltr" lang="en" width="20%" nowrap="nowrap" valign="top">'.$key.'</td>'."\n";
430 echo '<td dir="ltr" lang="en" width="40%" valign="top">'.$envalue.'</td>'."\n";
432 // Missing array keys are not bugs here but missing strings
433 error_reporting(E_ALL ^ E_NOTICE
);
435 $value = lang_fix_value_from_file($localstring[$key]);
436 $value2 = lang_fix_value_from_file($string[$key]);
441 $value = lang_fix_value_from_file($string[$key]);
442 $value2 = lang_fix_value_from_file($localstring[$key]);
444 error_reporting($CFG->debug
);
446 // Color highlighting:
447 // red #ef6868 - translation missing in both system and local pack
448 // yellow #feff7f - translation missing in system pack but is translated in local
449 // green #AAFFAA - translation present in both system and local but is different
452 $cellcolour = 'style="background-color: #ef6868"'; // TODO: replace by CSS class
454 $cellcolour = 'style="background-color: #feff7f"'; // TODO: replace by CSS class
457 if (LANG_DISPLAY_MISSING_LINKS
) {
458 $missingtarget = '<a name="missing'.$missingcounter.'"></a>';
459 $missingnext = '<a href="#missing'.($missingcounter+
1).'">'.
460 '<img src="' . $CFG->pixpath
. '/t/down.gif" height="11" width="11" border="0" alt="" /></a>';
461 $missingprev = '<a href="#missing'.($missingcounter-1).'">'.
462 '<img src="' . $CFG->pixpath
. '/t/up.gif" height="11" width="11" border="0" alt="" /></a>';
469 if ($value <> $value2 && $value2 <> '') {
470 $cellcolour = 'style="background-color: #AAFFAA"'; // TODO: replace by CSS class
480 echo '<td width="40%" '.$cellcolour.' valign="top">'. $missingprev . $missingtarget."\n";
481 if (isset($string[$key])) {
482 $valuelen = strlen($value);
484 $valuelen = strlen($envalue);
487 if (strstr($value, "\r") or strstr($value, "\n") or $valuelen > $cols) {
488 $rows = ceil($valuelen / $cols);
489 echo '<textarea name="stringXXX'.lang_form_string_key($key).'" cols="'.$cols.'" rows="'.$rows.'">'.$value.'</textarea>'."\n";
492 $cols = $valuelen +
5;
494 echo '<input type="text" name="stringXXX'.lang_form_string_key($key).'" value="'.$value.'" size="'.$cols.'" />';
496 if ($value2 <> '' && $value <> $value2) {
497 echo '<br /><span style="font-size:small">'.$value2.'</span>';
499 echo $missingnext . '</td>';
502 echo '<td width="40%" bgcolor="'.$cellcolour.'" valign="top">'.$value.'</td>';
506 echo '<tr><td colspan="2"> <td><br />';
507 echo '<input type="hidden" name="sesskey" value="'.$USER->sesskey
.'" />';
508 echo ' <input type="hidden" name="currentfile" value="'.$currentfile.'" />';
509 echo ' <input type="hidden" name="mode" value="compare" />';
510 echo ' <input type="submit" name="update" value="'.get_string('savechanges').': '.$currentfile.'" />';
517 // no $currentfile specified
518 print_heading($strchoosefiletoedit, "center", 4);
524 //////////////////////////////////////////////////////////////////////
527 * Save language translation file.
529 * Thanks to Petri Asikainen for the original version of code
530 * used to save language files.
534 * @param string $path Full pathname to the directory to use
535 * @param string $file File to overwrite
536 * @param array $strings Array of strings to write
537 * @param bool $local Should *_local version be saved?
538 * @param array $packstrings Array of default langpack strings (needed if $local)
539 * @return bool Created successfully?
541 function lang_save_file($path, $file, $strings, $local, $packstrings) {
543 if (!$f = @fopen
("$path/$file","w")) {
547 fwrite($f, "<?PHP // \$Id\$ \n");
548 fwrite($f, " // $file - created with Moodle $CFG->release ($CFG->version)\n");
550 fwrite($f, " // local modifications from $CFG->wwwroot\n");
554 foreach ($strings as $key => $value) {
555 @list
($id, $stringname) = explode('XXX',$key);
556 if ($CFG->lang
!= "zh_hk" and $CFG->lang
!= "zh_tw") { // Some MB languages include backslash bytes
557 $value = str_replace("\\","",$value); // Delete all slashes
559 $value = str_replace("'", "\\'", $value); // Add slashes for '
560 $value = str_replace('"', "\\\"", $value); // Add slashes for "
561 $value = str_replace("%","%%",$value); // Escape % characters
562 $value = str_replace("\r", "",$value); // Remove linefeed characters
563 $value = trim($value); // Delete leading/trailing white space
564 if ($id == "string" and $value != ""){
565 if ((!$local) ||
(lang_fix_value_from_file($packstrings[$stringname]) <> lang_fix_value_from_file($value))) {
566 fwrite($f,"\$string['$stringname'] = '$value';\n");
576 * Fix value of string to translate.
578 * @param string $value Original string from the file
579 * @return string Fixed value
581 function lang_fix_value_from_file($value='') {
582 $value = str_replace("\r","",$value); // Bad character caused by Windows
583 $value = preg_replace("/\n{3,}/", "\n\n", $value); // Collapse runs of blank lines
584 $value = trim($value); // Delete leading/trailing white space
585 $value = str_replace("\\","",$value); // Delete all slashes
586 $value = str_replace("%%","%",$value);
587 $value = str_replace("<","<",$value);
588 $value = str_replace(">",">",$value);
589 $value = str_replace('"',""",$value);
594 * Try and create a new language directory.
597 * @param string $directory directory name under $CFG->dataroot/lang eg cs_utf8_local
598 * @return string|false Returns full path to directory if successful, false if not
600 function lang_make_directory($dir, $shownotices=true) {
603 if (! file_exists($dir)) {
604 if (! @mkdir
($dir, $CFG->directorypermissions
)) {
607 //@chmod($dir, $CFG->directorypermissions); // Just in case mkdir didn't do it
614 /// Following functions are required because '.' in form input names
615 /// get replaced by '_' by PHP.
617 function lang_form_string_key($keyfromfile) {
618 return str_replace('.', '##46#', $keyfromfile); /// Derived from ., the ascii value for a period.
621 function lang_file_string_key($keyfromform) {
622 return str_replace('##46#', '.', $keyfromform);