'\n',
"\r" => '\r',
"\t" => '\t',
'\\' => '\\\\',
'$' => '\$',
'"' => '\"', ];
function addphpslashes ($string) {
// Translate as in "Table 7-1 Escaped characters" in the PHP manual
// $string = str_replace ("\n", '\n', $string);
// $string = str_replace ("\r", '\r', $string);
// $string = str_replace ("\t", '\t', $string);
// $string = str_replace ('\\', '\\\\', $string);
// $string = str_replace ('$', '\$', $string);
// $string = str_replace ('"', '\"', $string);
// We skip the exotic regexps for octal an hexadecimal
// notation - \{0-7]{1,3} and \x[0-9A-Fa-f]{1,2} -
// since they should not apper in english strings.
// return $string;
global $addPHPslashes;
return strtr ($string, $addPHPslashes);
}
$removePHPslashes = ['\n' => "\n",
'\r' => "\r",
'\t' => "\t",
'\\\\' => '\\',
'\$' => '$',
'\"' => '"', ];
function removephpslashes ($string) {
// $string = str_replace ('\n', "\n", $string);
// $string = str_replace ('\r', "\r", $string);
// $string = str_replace ('\t', "\t", $string);
// $string = str_replace ('\\\\', '\\', $string);
// $string = str_replace ('\$', '$', $string);
// $string = str_replace ('\"', '"', $string);
// We skip the exotic regexps for octal an hexadecimal
// notation - \{0-7]{1,3} and \x[0-9A-Fa-f]{1,2} - since they
// should not apper in english strings.
if (preg_match ('/\{0-7]{1,3}|\x[0-9A-Fa-f]{1,2}/', $string, $match)) {
trigger_error ("Octal or hexadecimal string '".$match[1]."' not supported",
E_WARNING, );
}
// return $string;
global $removePHPslashes;
return strtr ($string, $removePHPslashes);
}
function hardwire_file ($file)
{
global $files;
$files[] = $file;
print "File (hardwired): $file
";
}
function collect_files ($dir)
{
global $files;
$handle = opendir ($dir);
while (false !== ($file = readdir ($handle))) {
// Skip current and parent directory
// also skip other directories which may contain source code
// that should not be translated (the directories normally contain
// temporary results etc.)
// Please note that these directories will be skipped on all levels
if ('.' == $file || '..' == $file ||
'lang' == $file || 'templates_c' == $file || 'dump' == $file ||
'util' == $file || 'gallery2' == $file || 'shopping' == $file ||
'temp' == $file || 'img' == $file || 'cache' == $file) {
continue;
}
$filepath = $dir . '/' . $file;
if (preg_match ("/.*\.(tpl|php)$/", $file)) {
print("File: $filepath
");
$files[] = $filepath;
}
else {
if (is_dir ($filepath)) {
collect_files ($filepath);
}
}
}
closedir ($handle);
}
function addToWordlist (&$wordlist, $sentence) {
global $spelling;
if ($spelling) {
// Perhapps regexphandling must be improved?!
// Spellcheckers seems to handle special chars quite OK however.
$words = preg_split ("/[\s]+/", $sentence);
foreach ($words as $dummy => $word) {
$wordlist[strtolower($word)] = 1;
}
}
}
function writeFile_and_User ($fd, $outstring) {
print (nl2br ($outstring));
fwrite ($fd, $outstring);
}
function writeTranslationPair ($fd, $key, $val) {
writeFile_and_User ($fd,
'"' . addphpslashes ($key) . '"' . " => " .
'"' . addphpslashes ($val) . '",', );
}
////////////////////////////////////////////////////////////////////////////
require_once '../kernel/includes/setup_inc.php';
if(!$gBitUser->isAdmin()) {
die("You need to be admin to run this script");
}
$comments = isset ($_REQUEST['comments']);
$close = isset ($_REQUEST['close']) || $comments;
$module = isset ($_REQUEST['module']) || $comments;
$patch = isset ($_REQUEST['patch']);
$spelling = isset ($_REQUEST['spelling']);
$group_w = isset ($_REQUEST['groupwrite']);
$nohelp = isset ($_REQUEST['nohelp']);
$nosections = isset ($_REQUEST['nosections']);
// Get the language(s)
$languages = [];
print "Languages: ";
if (isset ($_REQUEST["lang"])) {
$lang = $_REQUEST["lang"];
$languages[] = $lang;
print ("$lang");
}
else {
$handle=opendir ('lang');
while (false !== ($lang = readdir ($handle))) {
if($lang == '.' || $lang == '..')
continue;
print("$lang ");
$languages[] = $lang;
}
closedir ($handle);
}
print "
";
$files = [];
$wordlist = [];
## When collecting files we need to add a file since the directory which it
## is placed in is excluded. We should keep hardwiring to a minimum.
## In a normal case a file that should be translated shuold not exist in
## a (sub)directory that is excluded. In this (unfortunate) case it seems that
## the file is placed in a logical location.
collect_files ( BIT_ROOT_PATH );
hardwire_file ( LANGUAGES_PKG_INCLUDE_PATH.'lang_mapping_inc.php');
$oldEndMarker = '##end###';
$endMarker = '###end###';
foreach ($languages as $sel) {
unset ($lang);
unset ($to_translate);
unset ($translated);
unset ($modulename);
unset ($unused);
unset ($dictionary);
$to_translate = [];
$modulename = [];
$translated = [];
if ($patch) {
$origPatch = "lang/$sel/language.patch";
if (!file_exists ($origPatch)) {
die ("No patch file .../$origPatch exisits");
}
require $origPatch;
$patchLang = $lang;
unset ($lang);
}
require "lang/$sel/language.php";
if (isset ($lang[$oldEndMarker])) {
unset ($lang[$oldEndMarker]);
}
if (isset ($lang[$endMarker])) {
unset ($lang[$endMarker]);
}
$unused = $lang;
$dictionary = $lang;
if ($group_w) {
// We set umask to zero value to allow proper chmod later
// (Is this really nesserary? Does not chmod work independently of umask?)
$old_umask = umask (0);
}
$fw = fopen( LANGUAGES_PKG_PATH."lang/$sel/new_language.php",'w');
print "<";
fwrite($fw,"<");
writeFile_and_User ($fw, "?php");
// The comment coding:utf-8 is for the benefit of emacs
// and must be on the very first line in the file
// we leave this comment in even if comments are off since
// editing files with the wrong encoding causes commical effects at best.
writeFile_and_User ($fw, " // -*- coding:utf-8 -*-\n");
if (!$nohelp) {
// Good to have instructions for translators in the release file.
// The comments get filtered away by Smarty anyway
writeFile_and_User ($fw, "// parameters:\n");
writeFile_and_User ($fw, "// lang=xx : only tranlates language 'xx',\n");
writeFile_and_User ($fw, "// if not given all languages are translated\n");
writeFile_and_User ($fw, "// comments : generate all comments (equal to close&module)\n");
writeFile_and_User ($fw, "// close : look for similar strings that are allready translated and\n");
writeFile_and_User ($fw, "// generate a commet if a 'match' is made\n");
writeFile_and_User ($fw, "// module : generate comments that describes in which .php and/or .tpl\n");
writeFile_and_User ($fw, "// module(s) a certain string was found (useful for checking\n");
writeFile_and_User ($fw, "// translations in context)\n");
writeFile_and_User ($fw, "// patch : looks for the file 'language.patch' in the same directory\n");
writeFile_and_User ($fw, "// as the corresponding language.php and overrides any strings\n");
writeFile_and_User ($fw, "// in language.php - good if a user does not agree with\n");
writeFile_and_User ($fw, "// some translations or if only changes are sent to the maintaner\n");
writeFile_and_User ($fw, "// spelling : generates a file 'spellcheck_me.txt' that contains the\n");
writeFile_and_User ($fw, "// words used in the translation.It is then easy to check this\n");
writeFile_and_User ($fw, "// file for spelling errors (corrections must be done in\n ");
writeFile_and_User ($fw, "// 'language.php, however)\n");
writeFile_and_User ($fw, "// groupwrite : Sets the generated files permissions to allow the generated\n");
writeFile_and_User ($fw, "// language.php also be group writable. This is good for\n");
writeFile_and_User ($fw, "// translators if they do not have root access to tiki but\n");
writeFile_and_User ($fw, "// are in the same group as the webserver. Please remember\n");
writeFile_and_User ($fw, "// to have write access removed when translation is finished\n");
writeFile_and_User ($fw, "// for security reasons. (Run script again without this\n");
writeFile_and_User ($fw, "// parameter)\n");
writeFile_and_User ($fw, "// Examples:\n");
writeFile_and_User ($fw, "// http://www.neonchart.com/get_strings.php?lang=sv\n");
writeFile_and_User ($fw, "// Will translate langauage 'sv' and (almost) avoiding comment generation\n\n");
writeFile_and_User ($fw, "// http://www.neonchart.com/get_strings.php?lang=sv&comments\n");
writeFile_and_User ($fw, "// Will translate langauage 'sv' and generate all possible comments.\n");
writeFile_and_User ($fw, "// This is the most usefull mode when working on a translation.\n\n");
writeFile_and_User ($fw, "// http://www.neonchart.com/get_strings.php?lang=sv&nohelp&nosections\n");
writeFile_and_User ($fw, "// These options will only provide the minimal amout of comments.\n");
writeFile_and_User ($fw, "// Usefull mode when preparing a translation for distribution.\n\n");
writeFile_and_User ($fw, "// http://www.neonchart.com/get_strings.php?nohelp&nosections\n");
writeFile_and_User ($fw, "// Prepare all languages for release \n\n");
}
// Start generating the lang array
writeFile_and_User ($fw, "\n\$lang=Array(\n");
foreach ($files as $file) {
$fp = fopen ($file, "r");
$data = fread ($fp, filesize ($file));
fclose ($fp);
unset ($words); $words = [];
unset ($uqwords); $uqwords = [];
unset ($sqwords); $sqwords = [];
unset ($dqwords); $dqwords = [];
// PM for unusual regexps
// (?m) sets PCRE_MULTILINE which makes '^' and '$' ignore '\n'
// (?s) sets PCRE_DOTALL which makes that '.' also matches '\n'
// ?: below makes that the pharentesis is not extracted int the outarray
// +? below is the nongreedy version of the ? operator
if (preg_match ("/\.php$/", $file)) {
// Do not translate PHP comments (we only filter the "safe" cases)
// Calling php -w would take care of all comments,
// but that does not go well with safe-mode.
$data = preg_replace ("/(?s)\/\*.*?\*\//", "", $data); // C comments
$data = preg_replace ("/(?m)^\s*\/\/.*\$/", "", $data); // C++ comments
$data = preg_replace ("/(?m)^\s*\#.*\$/", "", $data); // shell comments
// Only extract tra () and hawtra () in .php-files
// Extract from SINGLE qouted strings
preg_match_all ('/(?s)[^a-zA-Z0-9_\x7f-\xff](?:haw)?tra\s*\(\s*\'(.+?)\'\s*\)/', $data, $sqwords);
// Extract from DOUBLE quoted strings
preg_match_all ('/(?s)[^a-zA-Z0-9_\x7f-\xff](?:haw)?tra\s*\(\s*"(.+?)"\s*\)/', $data, $dqwords);
}
if (preg_match ("/\.tpl$/", $file)) {
// Do not translate text in Smarty comments: {* Smarty comment *}
$data = preg_replace ('/(?s)\{\*.*?\*\}/', '', $data); // Smarty comment
// Strings of the type {$perms[user].type} need (should)
// not be translated
$data = preg_replace ('/(?s)\{tr\}\s*\{[$][^\}]*?\}\s*\{\/tr\}/','',$data);
// Only extract ... in .tpl-files
preg_match_all ('/(?s)\{tr\}(.+?)\{\/tr\}/', $data, $uqwords);
}
// Transfer UNqouted words (if any) to the words array
if (count ($uqwords) > 0) {
$words = $uqwords[1];
}
// Transfer SINGLEqouted words (if any) to the words array
if (count ($sqwords) > 0) {
foreach (array_unique ($sqwords[1]) as $sqword) {
// Strip the extracted strings from escapes
// (these will not be reinserted during generation, since ' need
// not be escaped when string delimeters are double quotes)
$word = preg_replace ("/\\'/", "'", $sqword);
$words[$word] = $word;
}
}
// Transfer DOUBLEqouted words (if any) to the words array
if (count ($dqwords) > 0) {
foreach (array_unique ($dqwords[1]) as $dqword) {
// Strip the extracted strings from escapes
// (these will be reinserted during generation)
$word = removephpslashes ($dqword);
$words["$word"] = "$word";
}
}
foreach (array_unique ($words) as $word) {
if (isset ($lang[$word])) {
if (!isset ($translated[$word])) {
$translated[$word] = $lang[$word];
}
unset ($unused[$word]);
}
else {
if (!isset ($to_translate[$word])) {
$to_translate[$word]=$word;
}
}
if (isset ($modulename[$word])) {
if (!strpos ($modulename[$word], $file)) {
$modulename[$word] = $modulename[$word] .', '. $file;
}
}
else {
$modulename[$word] = $file;
}
}
} // foreach ($files as $file)
//////////////////////////////////////////////
if ($patch) {
foreach ($unused as $key => $val) {
if (isset ($patchLang[$key])) {
$unused[$key] = $patchLang[$key];
}
}
foreach ($to_translate as $key => $val) {
if (isset ($patchLang[$key])) {
// $to_translate[$key] = $patchLang[$key];
// We are removing words from the to_translate list,
// since they are provided by the patch
unset ($to_translate[$key]);
}
}
foreach ($translated as $key=>$val) {
if (isset ($patchLang[$key])) {
$translated[$key] = $patchLang[$key];
}
}
}
unset ($unused['']);
if (count ($unused) > 0) {
writeFile_and_User ($fw, "// ### Start of unused words\n");
writeFile_and_User ($fw, "// ### Please remove manually!\n");
writeFile_and_User ($fw, "// ### N.B. Legitimate strings may be marked");
writeFile_and_User ($fw, "// ### as unused!\n");
writeFile_and_User ($fw, "// ### Please see https://bitweaver.org/wiki/index.php?page=UnusedWords for furhter info.\n");
foreach ($unused as $key => $val) {
writeTranslationPair ($fw, $key, $val);
addToWordlist ($wordlist, $val);
writeFile_and_User ($fw, "\n");
}
writeFile_and_User ($fw, "// ### end of unused words\n\n");
}
unset ($to_translate['']);
if (count ($to_translate) > 0) {
if ('en' != $sel && !$nosections) {
writeFile_and_User ($fw, "// ### start of untranslated words\n");
writeFile_and_User ($fw,
"// ### uncomment value pairs as you translate\n", );
}
foreach ($to_translate as $key => $val) {
writeFile_and_User ($fw, "// ");
writeTranslationPair ($fw, $key, $val);
addToWordlist ($wordlist, $val);
if ($module || $close) {
$closeText = "";
$moduleText = "";
if ($close) {
$dist = 256;
foreach ($dictionary as $english=>$trans) {
$d = levenshtein (strtolower (substr ($key, 0, 255)),
strtolower (substr ($english, 0, 255)), );
if ($d < $dist) {
$dist = $d;
$closeTrans = $trans;
$closeEnglish = $english;
}
}
if ($dist < 1 + strlen ($key)/5) {
$closeText = ' // ## CLOSE: "' . addphpslashes ($closeEnglish) .
'" => "' . addphpslashes ($closeTrans) . '"';
}
}
if ($module) {
$moduleText = " // ## MODULES " . $modulename[$key];
}
writeFile_and_User ($fw, "{$closeText}{$moduleText}");
}
writeFile_and_User ($fw, "\n");
}
if ('en' != $sel && !$nosections) {
writeFile_and_User ($fw, "// ### end of untranslated words\n");
writeFile_and_User ($fw, "// ###\n\n");
}
}
if ('en' != $sel && !$nosections) {
writeFile_and_User($fw, "// ###\n");
writeFile_and_User ($fw,"// ### start of possibly untranslated words\n");
writeFile_and_User($fw, "// ###\n\n");
}
foreach ($translated as $key => $val) {
if ($key == $val) {
writeTranslationPair ($fw, $key, $val);
addToWordlist ($wordlist, $val);
if ($module) {
writeFile_and_User ($fw, ' // '. $modulename[$key]);
}
writeFile_and_User ($fw, "\n");
}
}
if ('en' != $sel && !$nosections) {
writeFile_and_User($fw, "// ###\n");
writeFile_and_User($fw, "// ### end of possibly untranslated words\n");
writeFile_and_User($fw, "// ###\n\n");
}
foreach($translated as $key => $val) {
if ($key != $val) {
writeTranslationPair ($fw, $key, $val);
addToWordlist ($wordlist, $val);
if ($module) {
writeFile_and_User ($fw, ' // '. $modulename[$key]);
}
writeFile_and_User ($fw, "\n");
}
}
writeFile_and_User ($fw, '"'.$endMarker.'"=>"'.$endMarker.'");'."\n");
print "?>
\n";
fwrite ($fw, '?>'."\n");
fclose ($fw);
if ($spelling) {
$fw = fopen("lang/$sel/spellcheck_me.txt", 'w');
ksort ($wordlist);
reset ($wordlist);
foreach ($wordlist as $word => $dummy) {
fwrite ($fw, "$word\n");
}
fclose ($fw);
}
@unlink ("lang/$sel/old.php");
rename ("lang/$sel/language.php","lang/$sel/old.php");
rename ("lang/$sel/new_language.php","lang/$sel/language.php");
if ($group_w) {
// chmod the file to be writeable also by group for users that do not
// have root acces
chmod ("lang/$sel/language.php", 0664);
umask($old_umask); // Reset umask back to original value
}
}